WORK IN PROGRESS
It is of course personal preference, but I generally like to structure my Python programs in much the same way a C program is structure, in three parts.
1. Import & Definitions,
2. Setup
3. Main
This program is no different, but instead of a simple “While True” loop, we will use the GUI instance as a condition of running the program. That way there is no background activity when the GUI is closed.
The very first step of any Python program is to import the required libraries and define any functions, classes, and variables. We will need...
- dearpygui.dearpygui as dpg
DearPyGUI is a framework built on top of Dear IMGui which provides a highly customizable interface, but is at the same time very fast compared to other libraries that are built with more of a focus on creating nice-looking plots for export to a PDF.
- numpy as np
NumPy is a mathematical library used in almost all Python programs that perform calculations on vectors or matrices.
- socket
The built-in socket library provides the interface for communicating with UDP (or TCP if desired) sockets over WLAN.
- select
The select library is used here to provide a robust method of limiting the amount of time our program will wait for incoming data before resuming the rest of the program.
import dearpygui.dearpygui as dpg import numpy as np import socket import select import random
After importing the required libraries, our first order of business is setting up the communication between our PC and the remote device. We do this by creating a UDP send/receive class with a pair of addresses and a listening timeout as parameters. It them creates a pair of UDP sockets, one for sending data, and the other for receiving. The receiving socket is set to non-bocking.
TX_addr = the address we are transmitting to
RX_addr = the address we are receiving on (our address)
The send/receive class has two methods, one to send and one to receive.
The TX method is straightforward. It takes a single argument, the message, and attempts to send it to the target socket over UDP.
The RX method is more complex as it uses the select library to create a non-blocking socket. The method creates an array [of one] sockets we expect to receive inputs on, looks to see if there is available data for any of them [just the one], and for any readable information it will...
- read the data into an input variable
- decode the information into a string
- split that string into an array using a comma as a delimiter
- remove the current [just the one] input from the monitored array
- return the message as an array
Using the select library in this way two-fold.
1. This is a more robust method then simply setting the socket to non-blocking
2. This allows for us to easily adapt this class to multiple receiving sockets in the future, which would allow one base-station to monitor several field devices. This would be done by passing an array of receive-addresses, creating a socket for each, and adding/removing them from the monitored array as data is received.
class UDP_TXRX(): def __init__(self, TX_addr, RX_addr, timeout): # TX Socket self.TX_addr = TX_addr self.TX_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # RX Socket self.timeout = timeout self.RX_addr = RX_addr self.RX_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.RX_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.RX_sock.setblocking(False) self.RX_sock.bind(self.RX_addr) def TX(self, message): try: self.TX_sock.sendto(message.encode(), self.TX_addr) except: pass def RX(self): try: inputs = [self.RX_sock] readable, _, _ = select.select(inputs, [], [], self.timeout) for r in readable: data, addr = r.recvfrom(256) message = data.decode().split(',') inputs.remove(r) return message break except: pass
This function performs a simple maneuver where it removes the first element in array and then appends a new value to the end. In this was we effectively have a FIFO array where the oldest entry is removed to make room for a new one.
def FIFO_my_array(i_array, i_newvalue): i_array = np.delete(np.asarray(i_array), 0) i_array = np.append(i_array, i_newvalue) return i_array
The next step is to create tuples to hold the addresses. The first is the IP address and port that we will be transmitting to, and the second are the address and port we will be receiving on. In the event the PC this program will run on has a single IP address there are programmatic ways to configure it, but here it is hard-coded.
Then we create an instance of the UDP_TXRX class we explain above.
TX_addr = ('10.0.0.16', 9090) RX_addr = ('10.0.0.20', 9090) basestation = UDP_TXRX(TX_addr, RX_addr, 0.005)
This is the final step before we begin setting up or visualization dashboard where we define all of the variables we will be monitoring. All of the variables are arrays of equal length (for reasons we will see below).
The time array [currently] begins as an array listed from 1 to 100 and an array for each variable to be plotted is set to an array of zeros of the same size.
t_data = np.arange(0, 100) x_data = 0*t_data y_data = 0*t_data z_data = 0*t_data p_data = 0*t_data r_data = 0*t_data h_data = 0*t_data ax_data = 0*t_data ay_data = 0*t_data az_data = 0*t_data gp_data = 0*t_data gr_data = 0*t_data gh_data = 0*t_data temp_data = 0*t_data state_vals = [0]*13 r = [0]*12
With this the IMPORT & DEFINE section is complete and we begin the SETUP portion. The very first step to begin setting up our visualization. The first step is to create what is called a "context" which is essentially an independant instance of the framework. In general, "context" = "one GUI". It is possible to create multiple contexts, but here we are creating only one.
Everything between dpg.create_context() and dpg.destroy_context() will apply to this GUI.
Here it would be useful to define "window" and "viewport" in the context of DearPy GUI. For our purposes, a "viewport" is what most people would consider a window in Microsoft Windows, Linux, etc. A "window" then, is a window within that viewport, or what most might consider a sub-window". Again for our purposes, as you will see later, we will create a single primary window within our viewport so that they are for purposes one and the same.
"Viewport" = window
"Window" = sub-window
dpg.create_context()
As mentioned, although it is possible to have multiple windows [sub-windows] within our viewport [window], we will only have a single primary one. Adding & configuring the widgets within the window is done using the "with dpg.window(*args) function. The arguments are used to give it a
- label name
- name tag for reference elsewhere in the context. For simplicity, we set the tag equal to the label
- size via width & height
Each widget is added and configured under this function the way you would a conditional.
note: some configurations are done when initially creating each widget while are some are done later outside of the "with window" scope.
with dpg.window(label='Primary Window', tag='Primary Window', width=1900, height=1000):
Here we begin adding each of our widgets. For each plot the steps are the same.
1. Call the "with plot" function in a similar manner to how we called the "with window" fuction.
2. Add an x-axis with its type, label, tag, and other customizations
3. Add a y-axis with its type, label, tag, and other customizations
4. Add one or more line_series that...
- must have x and y data
- a label and a tag
- a parent y-axis (two y-axes are possible, but not used here)
with dpg.plot(label="Position", height=400, width=400, tag='plot1'): dpg.add_plot_legend() dpg.add_plot_axis(dpg.mvXAxis, label="t", tag="x_axis1", no_tick_labels=True) dpg.add_plot_axis(dpg.mvYAxis, label="cm", tag="y_axis1") dpg.add_line_series(t_data.tolist(), x_data.tolist(), label='x', tag='x_data', parent='y_axis1') dpg.add_line_series(t_data.tolist(), y_data.tolist(), label='y', tag='y_data', parent='y_axis1') dpg.add_line_series(t_data.tolist(), z_data.tolist(), label='z', tag='z_data', parent='y_axis1') with dpg.plot(label="Orientation", height=400, width=400, tag='plot2'): dpg.add_plot_legend() dpg.add_plot_axis(dpg.mvXAxis, label="t", tag="x_axis2", no_tick_labels=True) dpg.add_plot_axis(dpg.mvYAxis, label="degrees", tag="y_axis2") dpg.add_line_series(t_data.tolist(), p_data.tolist(), label='pitch', tag='p_data', parent='y_axis2') dpg.add_line_series(t_data.tolist(), r_data.tolist(), label='roll', tag='r_data', parent='y_axis2') with dpg.plot(label="Heading", height=400, width=400, tag='plot3'): dpg.add_plot_legend() dpg.add_plot_axis(dpg.mvXAxis, label=" ", tag="x_axis3", no_tick_labels=True) dpg.add_plot_axis(dpg.mvYAxis, label=" ", tag="y_axis3") dpg.add_line_series(t_data.tolist(), h_data.tolist(), label='heading', tag='h_data', parent='y_axis3') with dpg.plot(label="Accelerometer", height=400, width=400, tag='plot4'): dpg.add_plot_legend() dpg.add_plot_axis(dpg.mvXAxis, label="t", tag="x_axis4", no_tick_labels=True) dpg.add_plot_axis(dpg.mvYAxis, label="g's", tag="y_axis4") dpg.add_line_series(t_data.tolist(), ax_data.tolist(), label='acc_x', tag='ax_data', parent='y_axis4') dpg.add_line_series(t_data.tolist(), ay_data.tolist(), label='acc_y', tag='ay_data', parent='y_axis4') dpg.add_line_series(t_data.tolist(), az_data.tolist(), label='acc_z', tag='az_data', parent='y_axis4') with dpg.plot(label="Gyroscope", height=400, width=400, tag='plot5'): dpg.add_plot_legend() dpg.add_plot_axis(dpg.mvXAxis, label="t", tag="x_axis5", no_tick_labels=True) dpg.add_plot_axis(dpg.mvYAxis, label="dps", tag="y_axis5") dpg.add_line_series(t_data.tolist(), gp_data.tolist(), label='gyr_p', tag='gp_data', parent='y_axis5') dpg.add_line_series(t_data.tolist(), gr_data.tolist(), label='gyr_r', tag='gr_data', parent='y_axis5') dpg.add_line_series(t_data.tolist(), gh_data.tolist(), label='gyr_h', tag='gh_data', parent='y_axis5') with dpg.plot(label="Temperature", height=400, width=400, tag='plot6'): dpg.add_plot_legend() dpg.add_plot_axis(dpg.mvXAxis, label="t", tag="x_axis6", no_tick_labels=True) dpg.add_plot_axis(dpg.mvYAxis, label="°C", tag="y_axis6") dpg.add_line_series(t_data.tolist(), temp_data.tolist(), label='temp', tag='temp_data', parent='y_axis6')
With our plots created, we then assign other widgets such as the buttons and displays.
button_border = 2 dpg.add_button(label='NEW', height=50-button_border, width=200-button_border, tag='new') dpg.add_button(label='CURRENT', height=50-button_border, width=200-button_border, tag='current') dpg.add_button(label='x', height=50-button_border, width=200-button_border, tag='x_new') dpg.add_button(label='y', height=50-button_border, width=200-button_border, tag='y_new') dpg.add_button(label='z', height=50-button_border, width=200-button_border, tag='z_new') dpg.add_button(label='p', height=50-button_border, width=200-button_border, tag='p_new') dpg.add_button(label='r', height=50-button_border, width=200-button_border, tag='r_new') dpg.add_button(label='h', height=50-button_border, width=200-button_border, tag='h_new') dpg.add_button(label='x', height=50-button_border, width=200-button_border, tag='x_curr') dpg.add_button(label='y', height=50-button_border, width=200-button_border, tag='y_curr') dpg.add_button(label='z', height=50-button_border, width=200-button_border, tag='z_curr') dpg.add_button(label='p', height=50-button_border, width=200-button_border, tag='p_curr') dpg.add_button(label='r', height=50-button_border, width=200-button_border, tag='r_curr') dpg.add_button(label='h', height=50-button_border, width=200-button_border, tag='h_curr') dpg.add_button(label='send', height=50-button_border, width=400-button_border, tag='send')
With all of our widgets created, assigned, and configured, we use the below function calls on each one (according to their tag) to assign them positions.
note: the position corresponds to the UPPER-LEFT corner of each widget.
dpg.set_item_pos(item='new', pos=[100, 100]) dpg.set_item_pos(item='current', pos=[300, 100]) dpg.set_item_pos(item='x_new', pos=[100, 150]) dpg.set_item_pos(item='y_new', pos=[100, 200]) dpg.set_item_pos(item='z_new', pos=[100, 250]) dpg.set_item_pos(item='p_new', pos=[100, 300]) dpg.set_item_pos(item='r_new', pos=[100, 350]) dpg.set_item_pos(item='h_new', pos=[100, 400]) dpg.set_item_pos(item='x_curr', pos=[300, 150]) dpg.set_item_pos(item='y_curr', pos=[300, 200]) dpg.set_item_pos(item='z_curr', pos=[300, 250]) dpg.set_item_pos(item='p_curr', pos=[300, 300]) dpg.set_item_pos(item='r_curr', pos=[300, 350]) dpg.set_item_pos(item='h_curr', pos=[300, 400]) dpg.set_item_pos(item='send', pos=[100, 450]) dpg.set_item_pos(item='plot1', pos=[600, 100]) dpg.set_item_pos(item='plot2', pos=[1000, 100]) dpg.set_item_pos(item='plot3', pos=[1400, 100]) dpg.set_item_pos(item='plot4', pos=[600, 500]) dpg.set_item_pos(item='plot5', pos=[1000, 500]) dpg.set_item_pos(item='plot6', pos=[1400, 500])
With all of our widgets now placed as well, the next step is to assign limits to our axes. We are not assigning limits to our horizontal axes because it represents a FIFO of time, but we do scale each of our y-axis based on the type of data to be displayed.
dpg.set_axis_limits(axis='y_axis1', ymin = -2.00, ymax = 2.00) # x, y, z dpg.set_axis_limits(axis='y_axis2', ymin = -6.00, ymax = 6.00) # p, r dpg.set_axis_limits(axis='y_axis3', ymin = -200.00, ymax = 200.00) # h dpg.set_axis_limits(axis='y_axis4', ymin = -2.00, ymax = 2.00) # acc_x, acc_y, acc_z dpg.set_axis_limits(axis='y_axis5', ymin = -31.45, ymax = 31.45) # gyr_p, gyr_r, gyrh dpg.set_axis_limits(axis='y_axis6', ymin = 0.00, ymax = 50.00) # temp
With everything in our window configured, we proceed to configure the viewport. As mentioned, we use a single primary window within our viewport, so here we...
- set the viewport size equal to the window size
- set a global font scale (adjusted based on the viewport/window resolution)
- run the DearPyGUI setup command
- maximize the viewport to occupy the whole screen
- set our single window as primary
- render the initial frame
dpg.create_viewport(title='Primary Viewport', width=1900, height=1000) dpg.set_global_font_scale(1.2) dpg.setup_dearpygui() dpg.show_viewport() dpg.maximize_viewport() dpg.set_primary_window("Primary Window", True) dpg.render_dearpygui_frame()
With all of this done, we conclude the SETUP portion of the code and move onto the "MAIN / WHILE-TRUE" portion that will execute so long as the program itself is running. Here we use the standard "While True" loop, but with the condition "dpg.is_dearpyguirunning() as the condition to continue executing. This way the code will execute as long as the GUI is up, but close as soon as the GUI is closed.
while dpg.is_dearpygui_running():
The first portion of our infinite-loop is the transmission & reception of data from the remote device. Here as an example we send gibberish data, but realistically it could be used to send any relevant information such as updated commands, reference inputs, telemetry, etc. to the remote device.
r[0] = random.randint(0, 100) r[1] = random.randint(0, 100) r[2] = random.randint(0, 100) r[3] = random.randint(0, 100) r[4] = random.randint(0, 100) r[5] = random.randint(0, 100) r[6] = random.randint(0, 100) r[7] = random.randint(0, 100) r[8] = random.randint(0, 100) r[9] = random.randint(0, 100) r[10] = random.randint(0, 100) r[11] = random.randint(0, 100) TX_msg = str(r) basestation.TX(TX_msg) RX_msg = basestation.RX() if RX_msg: string_array = RX_msg.split(',') string_array.pop(-1) state_vals = list(map(float, string_array)) print(state_vals)
Here we begin the block of code where each array in turn has:
1 - a new value received and/or calculated
2 - the new value is FIFO'd to the end of the array
Afterwards, each line_series in the plots defined above is updated with the new data and the axes are re-fitted to the new data. The effect is an infinitely scrolling FIFO array showing the previous 100 measurements.
After each line-series is updated, the new frame is rendered and the loop repeats.
t_new = max(t_data) + 1 x_new = state_vals[0] y_new = state_vals[1] z_new = state_vals[2] p_new = state_vals[3] r_new = state_vals[4] h_new = state_vals[5] ax_new = state_vals[6] ay_new = state_vals[7] az_new = state_vals[8] gp_new = state_vals[9] gr_new = state_vals[10] gh_new = state_vals[11] temp_new = state_vals[12] t_data = FIFO_my_array(t_data, t_new) x_data = FIFO_my_array(x_data, x_new) y_data = FIFO_my_array(y_data, y_new) z_data = FIFO_my_array(z_data, z_new) p_data = FIFO_my_array(p_data, p_new) r_data = FIFO_my_array(r_data, r_new) h_data = FIFO_my_array(h_data, h_new) ax_data = FIFO_my_array(ax_data, ax_new) ay_data = FIFO_my_array(ay_data, ay_new) az_data = FIFO_my_array(az_data, az_new) gp_data = FIFO_my_array(gp_data, gp_new) gr_data = FIFO_my_array(gr_data, gr_new) gh_data = FIFO_my_array(gh_data, gh_new) temp_data = FIFO_my_array(temp_data, temp_new) dpg.set_value('x_data', [t_data.tolist(), x_data.tolist()]) dpg.set_value('y_data', [t_data.tolist(), y_data.tolist()]) dpg.set_value('z_data', [t_data.tolist(), z_data.tolist()]) dpg.set_value('p_data', [t_data.tolist(), p_data.tolist()]) dpg.set_value('r_data', [t_data.tolist(), r_data.tolist()]) dpg.set_value('h_data', [t_data.tolist(), h_data.tolist()]) dpg.set_value('ax_data', [t_data.tolist(), ax_data.tolist()]) dpg.set_value('ay_data', [t_data.tolist(), ay_data.tolist()]) dpg.set_value('az_data', [t_data.tolist(), az_data.tolist()]) dpg.set_value('gp_data', [t_data.tolist(), gp_data.tolist()]) dpg.set_value('gr_data', [t_data.tolist(), gr_data.tolist()]) dpg.set_value('gh_data', [t_data.tolist(), gh_data.tolist()]) dpg.set_value('temp_data', [t_data.tolist(), temp_data.tolist()]) dpg.fit_axis_data('x_axis1') dpg.fit_axis_data('x_axis2') dpg.fit_axis_data('x_axis3') dpg.fit_axis_data('x_axis4') dpg.fit_axis_data('x_axis5') dpg.fit_axis_data('x_axis6') dpg.render_dearpygui_frame() dpg.destroy_context()
FUTURE IMPMROVEMENTS
- replacement of integer timesteps with actual time via time library
- use of Python library 'queue' to facilitate FIFO effect on arrays
- use of Python library 'logging' to log transmitted & received data from each flight
- use of Python library 'threads" to separate frame rendering, socket transmission, etc.
- simplify large codeblocks of repeated actions using a function
After all of this, the results should look similar to the below video!
FULL CODE BELOW:
import dearpygui.dearpygui as dpg import numpy as np import socket import select import random class UDP_TXRX(): def __init__(self, TX_addr, RX_addr, timeout): # TX Socket self.TX_addr = TX_addr self.TX_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # RX Socket self.timeout = timeout self.RX_addr = RX_addr self.RX_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.RX_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.RX_sock.setblocking(False) self.RX_sock.bind(self.RX_addr) def TX(self, message): try: self.TX_sock.sendto(message.encode(), self.TX_addr) except: pass def RX(self): try: inputs = [self.RX_sock] readable, _, _ = select.select(inputs, [], [], self.timeout) for r in readable: data, addr = r.recvfrom(256) message = data.decode().split(',') inputs.remove(r) return message break except: pass def FIFO_my_array(i_array, i_newvalue): i_array = np.delete(np.asarray(i_array), 0) i_array = np.append(i_array, i_newvalue) return i_array TX_addr = ('10.0.0.16', 9090) RX_addr = ('10.0.0.20', 9090) basestation = UDP_TXRX(TX_addr, RX_addr, 0.005) t_data = np.arange(0, 100) x_data = 0*t_data y_data = 0*t_data z_data = 0*t_data p_data = 0*t_data r_data = 0*t_data h_data = 0*t_data ax_data = 0*t_data ay_data = 0*t_data az_data = 0*t_data gp_data = 0*t_data gr_data = 0*t_data gh_data = 0*t_data temp_data = 0*t_data state_vals = [0]*13 r = [0]*12 dpg.create_context() with dpg.window(label='Primary Window', tag='Primary Window', width=1900, height=1000): with dpg.plot(label="Position", height=400, width=400, tag='plot1'): dpg.add_plot_legend() dpg.add_plot_axis(dpg.mvXAxis, label="t", tag="x_axis1", no_tick_labels=True) dpg.add_plot_axis(dpg.mvYAxis, label="cm", tag="y_axis1") dpg.add_line_series(t_data.tolist(), x_data.tolist(), label='x', tag='x_data', parent='y_axis1') dpg.add_line_series(t_data.tolist(), y_data.tolist(), label='y', tag='y_data', parent='y_axis1') dpg.add_line_series(t_data.tolist(), z_data.tolist(), label='z', tag='z_data', parent='y_axis1') with dpg.plot(label="Orientation", height=400, width=400, tag='plot2'): dpg.add_plot_legend() dpg.add_plot_axis(dpg.mvXAxis, label="t", tag="x_axis2", no_tick_labels=True) dpg.add_plot_axis(dpg.mvYAxis, label="degrees", tag="y_axis2") dpg.add_line_series(t_data.tolist(), p_data.tolist(), label='pitch', tag='p_data', parent='y_axis2') dpg.add_line_series(t_data.tolist(), r_data.tolist(), label='roll', tag='r_data', parent='y_axis2') with dpg.plot(label="Heading", height=400, width=400, tag='plot3'): dpg.add_plot_legend() dpg.add_plot_axis(dpg.mvXAxis, label=" ", tag="x_axis3", no_tick_labels=True) dpg.add_plot_axis(dpg.mvYAxis, label=" ", tag="y_axis3") dpg.add_line_series(t_data.tolist(), h_data.tolist(), label='heading', tag='h_data', parent='y_axis3') with dpg.plot(label="Accelerometer", height=400, width=400, tag='plot4'): dpg.add_plot_legend() dpg.add_plot_axis(dpg.mvXAxis, label="t", tag="x_axis4", no_tick_labels=True) dpg.add_plot_axis(dpg.mvYAxis, label="g's", tag="y_axis4") dpg.add_line_series(t_data.tolist(), ax_data.tolist(), label='acc_x', tag='ax_data', parent='y_axis4') dpg.add_line_series(t_data.tolist(), ay_data.tolist(), label='acc_y', tag='ay_data', parent='y_axis4') dpg.add_line_series(t_data.tolist(), az_data.tolist(), label='acc_z', tag='az_data', parent='y_axis4') with dpg.plot(label="Gyroscope", height=400, width=400, tag='plot5'): dpg.add_plot_legend() dpg.add_plot_axis(dpg.mvXAxis, label="t", tag="x_axis5", no_tick_labels=True) dpg.add_plot_axis(dpg.mvYAxis, label="dps", tag="y_axis5") dpg.add_line_series(t_data.tolist(), gp_data.tolist(), label='gyr_p', tag='gp_data', parent='y_axis5') dpg.add_line_series(t_data.tolist(), gr_data.tolist(), label='gyr_r', tag='gr_data', parent='y_axis5') dpg.add_line_series(t_data.tolist(), gh_data.tolist(), label='gyr_h', tag='gh_data', parent='y_axis5') with dpg.plot(label="Temperature", height=400, width=400, tag='plot6'): dpg.add_plot_legend() dpg.add_plot_axis(dpg.mvXAxis, label="t", tag="x_axis6", no_tick_labels=True) dpg.add_plot_axis(dpg.mvYAxis, label="°C", tag="y_axis6") dpg.add_line_series(t_data.tolist(), temp_data.tolist(), label='temp', tag='temp_data', parent='y_axis6') button_border = 2 dpg.add_button(label='NEW', height=50-button_border, width=200-button_border, tag='new') dpg.add_button(label='CURRENT', height=50-button_border, width=200-button_border, tag='current') dpg.add_button(label='x', height=50-button_border, width=200-button_border, tag='x_new') dpg.add_button(label='y', height=50-button_border, width=200-button_border, tag='y_new') dpg.add_button(label='z', height=50-button_border, width=200-button_border, tag='z_new') dpg.add_button(label='p', height=50-button_border, width=200-button_border, tag='p_new') dpg.add_button(label='r', height=50-button_border, width=200-button_border, tag='r_new') dpg.add_button(label='h', height=50-button_border, width=200-button_border, tag='h_new') dpg.add_button(label='x', height=50-button_border, width=200-button_border, tag='x_curr') dpg.add_button(label='y', height=50-button_border, width=200-button_border, tag='y_curr') dpg.add_button(label='z', height=50-button_border, width=200-button_border, tag='z_curr') dpg.add_button(label='p', height=50-button_border, width=200-button_border, tag='p_curr') dpg.add_button(label='r', height=50-button_border, width=200-button_border, tag='r_curr') dpg.add_button(label='h', height=50-button_border, width=200-button_border, tag='h_curr') dpg.add_button(label='send', height=50-button_border, width=400-button_border, tag='send') dpg.set_item_pos(item='new', pos=[100, 100]) dpg.set_item_pos(item='current', pos=[300, 100]) dpg.set_item_pos(item='x_new', pos=[100, 150]) dpg.set_item_pos(item='y_new', pos=[100, 200]) dpg.set_item_pos(item='z_new', pos=[100, 250]) dpg.set_item_pos(item='p_new', pos=[100, 300]) dpg.set_item_pos(item='r_new', pos=[100, 350]) dpg.set_item_pos(item='h_new', pos=[100, 400]) dpg.set_item_pos(item='x_curr', pos=[300, 150]) dpg.set_item_pos(item='y_curr', pos=[300, 200]) dpg.set_item_pos(item='z_curr', pos=[300, 250]) dpg.set_item_pos(item='p_curr', pos=[300, 300]) dpg.set_item_pos(item='r_curr', pos=[300, 350]) dpg.set_item_pos(item='h_curr', pos=[300, 400]) dpg.set_item_pos(item='send', pos=[100, 450]) dpg.set_item_pos(item='plot1', pos=[600, 100]) dpg.set_item_pos(item='plot2', pos=[1000, 100]) dpg.set_item_pos(item='plot3', pos=[1400, 100]) dpg.set_item_pos(item='plot4', pos=[600, 500]) dpg.set_item_pos(item='plot5', pos=[1000, 500]) dpg.set_item_pos(item='plot6', pos=[1400, 500]) dpg.set_axis_limits(axis='y_axis1', ymin = -2.00, ymax = 2.00) # x, y, z dpg.set_axis_limits(axis='y_axis2', ymin = -6.00, ymax = 6.00) # p, r dpg.set_axis_limits(axis='y_axis3', ymin = -200.00, ymax = 200.00) # h dpg.set_axis_limits(axis='y_axis4', ymin = -2.00, ymax = 2.00) # acc_x, acc_y, acc_z dpg.set_axis_limits(axis='y_axis5', ymin = -31.45, ymax = 31.45) # gyr_p, gyr_r, gyrh dpg.set_axis_limits(axis='y_axis6', ymin = 0.00, ymax = 50.00) # temp dpg.create_viewport(title='Primary Viewport', width=1900, height=1000) dpg.set_global_font_scale(1.2) dpg.setup_dearpygui() dpg.show_viewport() dpg.maximize_viewport() dpg.set_primary_window("Primary Window", True) dpg.render_dearpygui_frame() while dpg.is_dearpygui_running(): r[0] = random.randint(0, 100) r[1] = random.randint(0, 100) r[2] = random.randint(0, 100) r[3] = random.randint(0, 100) r[4] = random.randint(0, 100) r[5] = random.randint(0, 100) r[6] = random.randint(0, 100) r[7] = random.randint(0, 100) r[8] = random.randint(0, 100) r[9] = random.randint(0, 100) r[10] = random.randint(0, 100) r[11] = random.randint(0, 100) TX_msg = str(r) basestation.TX(TX_msg) RX_msg = basestation.RX() if RX_msg: string_array = RX_msg.split(',') string_array.pop(-1) state_vals = list(map(float, string_array)) print(state_vals) t_new = max(t_data) + 1 x_new = state_vals[0] y_new = state_vals[1] z_new = state_vals[2] p_new = state_vals[3] r_new = state_vals[4] h_new = state_vals[5] ax_new = state_vals[6] ay_new = state_vals[7] az_new = state_vals[8] gp_new = state_vals[9] gr_new = state_vals[10] gh_new = state_vals[11] temp_new = state_vals[12] t_data = FIFO_my_array(t_data, t_new) x_data = FIFO_my_array(x_data, x_new) y_data = FIFO_my_array(y_data, y_new) z_data = FIFO_my_array(z_data, z_new) p_data = FIFO_my_array(p_data, p_new) r_data = FIFO_my_array(r_data, r_new) h_data = FIFO_my_array(h_data, h_new) ax_data = FIFO_my_array(ax_data, ax_new) ay_data = FIFO_my_array(ay_data, ay_new) az_data = FIFO_my_array(az_data, az_new) gp_data = FIFO_my_array(gp_data, gp_new) gr_data = FIFO_my_array(gr_data, gr_new) gh_data = FIFO_my_array(gh_data, gh_new) temp_data = FIFO_my_array(temp_data, temp_new) dpg.set_value('x_data', [t_data.tolist(), x_data.tolist()]) dpg.set_value('y_data', [t_data.tolist(), y_data.tolist()]) dpg.set_value('z_data', [t_data.tolist(), z_data.tolist()]) dpg.set_value('p_data', [t_data.tolist(), p_data.tolist()]) dpg.set_value('r_data', [t_data.tolist(), r_data.tolist()]) dpg.set_value('h_data', [t_data.tolist(), h_data.tolist()]) dpg.set_value('ax_data', [t_data.tolist(), ax_data.tolist()]) dpg.set_value('ay_data', [t_data.tolist(), ay_data.tolist()]) dpg.set_value('az_data', [t_data.tolist(), az_data.tolist()]) dpg.set_value('gp_data', [t_data.tolist(), gp_data.tolist()]) dpg.set_value('gr_data', [t_data.tolist(), gr_data.tolist()]) dpg.set_value('gh_data', [t_data.tolist(), gh_data.tolist()]) dpg.set_value('temp_data', [t_data.tolist(), temp_data.tolist()]) dpg.fit_axis_data('x_axis1') dpg.fit_axis_data('x_axis2') dpg.fit_axis_data('x_axis3') dpg.fit_axis_data('x_axis4') dpg.fit_axis_data('x_axis5') dpg.fit_axis_data('x_axis6') dpg.render_dearpygui_frame() dpg.destroy_context()