diff --git a/dist/demo_static_test_gui b/dist/demo_static_test_gui index d8a1f47..4520dc8 100755 Binary files a/dist/demo_static_test_gui and b/dist/demo_static_test_gui differ diff --git a/dist/demo_static_test_gui.exe b/dist/demo_static_test_gui.exe index 727b2de..6a17cc1 100755 Binary files a/dist/demo_static_test_gui.exe and b/dist/demo_static_test_gui.exe differ diff --git a/dist/flight_gui b/dist/flight_gui index d1419f3..e3cd502 100755 Binary files a/dist/flight_gui and b/dist/flight_gui differ diff --git a/dist/flight_gui.exe b/dist/flight_gui.exe index 3b2beb9..185b8c9 100755 Binary files a/dist/flight_gui.exe and b/dist/flight_gui.exe differ diff --git a/dist/mk2_static_test_gui b/dist/mk2_static_test_gui index b59b833..b2e749e 100755 Binary files a/dist/mk2_static_test_gui and b/dist/mk2_static_test_gui differ diff --git a/dist/mk2_static_test_gui.exe b/dist/mk2_static_test_gui.exe index 742a507..77e90a1 100755 Binary files a/dist/mk2_static_test_gui.exe and b/dist/mk2_static_test_gui.exe differ diff --git a/drivers/flight_gui.py b/drivers/flight_gui.py index 634a255..c016ad0 100755 --- a/drivers/flight_gui.py +++ b/drivers/flight_gui.py @@ -4,10 +4,17 @@ sys.path.append("src") from tkinter import * +from tkinter.messagebox import showerror import manager import gui import plot +import time +import enum + +class Config(enum.Enum): + DEMO = 0 + FLIGHT = 2 def vector_DataType(name, *args, **kwd_args): data_types = [manager.DataType(d + "_" + name, *args, **kwd_args) for d in ['x', 'y', 'z']] @@ -19,21 +26,290 @@ def vector_Plot(x, y, name=None, *args, **kwd_args): name = y.replace("_", " ") return plot.Plot(x, [d + "_" + y for d in ['x', 'y', 'z']], name, *args, **kwd_args) -dts = ([manager.DataType('temperature', float, units='deg C', thresholds=(-20, 80))] + - vector_DataType('magnetometer', float, thresholds=(-100, 100)) + # TODO: units - vector_DataType('gyro', float, thresholds=(-100, 100)) + # TODO: units - vector_DataType('euler_angle', float, units='degrees', thresholds=(0, 360)) + - vector_DataType('acceleration', float, units='m/sec^2', thresholds=(-50, 50))) -plots = [vector_Plot('time', 'magnetometer', width=4), - vector_Plot('time', 'gyro', width=4), - vector_Plot('time', 'euler_angle', width=4), - vector_Plot('time', 'acceleration', width=4)] -dispatcher = manager.Dispatcher(*dts) -manager = manager.DataManager(dispatcher) -root = Tk() -app = gui.Application(dispatcher, manager, plots, master=root, - serial_console_height=10, - default_baud=38400) + +def init(config=Config.FLIGHT): + dts = ( + [ + manager.DataType('bmp_alt', float, units='m', thresholds=(-100, 80000)), + manager.DataType('gps_alt', float, units='m', thresholds=(-100, 80000)), + manager.DataType('gps_lat', float, units='deg', thresholds=(-91, 91)), + manager.DataType('gps_lon', float, units='deg', thresholds=(-181, 181)), + manager.DataType('vb1', float, units='V', thresholds=(-1, 55)), + manager.DataType('test', float, units='_', thresholds=(-1, 1)), + manager.DataType('hdp', float, units='m', thresholds=(0, 100)), + manager.DataType('sats', int, units='#', thresholds=(-10, 169)), + manager.DataType('heading', float, units='deg', thresholds=(-180, 180)), + manager.DataType('attitude', float, units='deg', thresholds=(-90, 90)), + manager.DataType('bank', float, units='deg', thresholds=(-180, 180)) + ] + + vector_DataType('euler_angle', float, units='degrees', thresholds=(-180, 360)) + + vector_DataType('magnetometer', float, units='mu T', thresholds=(-100, 100)) + + vector_DataType('gyro', float, units='rad/s', thresholds=(-100, 100)) + + vector_DataType('acceleration', float, units='m/sec^2', thresholds=(-50, 50)) + + [ + manager.DataType('temperature', float, units='deg C', thresholds=(-20, 80)), + manager.DataType('gps_vel', float, units='xy m/s', thresholds=(-20, 100)), + manager.DataType('gps_dir', float, units='xy deg', thresholds=(-20, 365)), + manager.DataType('x_from_launch', float, units='xy m', thresholds=(-10000, 100000)), + manager.DataType('y_from_launch', float, units='xy m', thresholds=(-10000, 100000)), + manager.DataType('dir_from_launch', float, units='xy deg', thresholds=(-20, 365)), + manager.DataType('P1_setting', bool), + manager.DataType('P2_setting', bool), + manager.DataType('P3_setting', bool), + manager.DataType('P4_setting', bool), + manager.DataType('P5_setting', bool), + + manager.DataType('ATST', float, units="m"), + manager.DataType('Launch_ALT', float, units="m"), + manager.DataType('BMPcf', float, units="Pa*100"), + manager.DataType('launch_lat', float), + manager.DataType('launch_lon', float), + #manager.DataType('land_lat', float), + #manager.DataType('land_lon', float), + #manager.DataType('gps_n', float, units="m"), + #manager.DataType('gps_e', float, units="m"), + + #manager.DataType('up', bool), + #manager.DataType('down', bool), + #manager.DataType('gps_d', bool), + #manager.DataType('bmp_d', bool), + #manager.DataType('bmp_d2', bool), + #manager.DataType('bno_d', bool), + + manager.DataType('run_time', int, units="ms"), + manager.DataType('status', str), #str), + manager.DataType('Apogee_Passed', bool), + manager.DataType('l2g', bool), + manager.DataType('ss', bool) #sensor_status , show=False + ] + ) + plots = [plot.Plot('time', ['bmp_alt', 'gps_alt'], "Altitude", width=2, show_x_label=False), + plot.Plot('time', ['heading', 'attitude','bank'], "q-Angles", width=4, show_x_label=False), + plot.Plot('time', 'gps_lat', width=1, show_x_label=False), + plot.Plot('time', 'gps_lon', width=1, show_x_label=False), + #plot.Plot('gps_e', 'gps_n',"xy from launch (m)", width=1, show_x_label=True), + vector_Plot('time', 'euler_angle', width=4, show_x_label=False), + vector_Plot('time', 'gyro', width=4, show_x_label=False), + vector_Plot('time', 'acceleration', width=4, show_x_label=False)] + dispatcher = manager.Dispatcher(*dts) + data_manager = manager.DataManager(dispatcher) + root = Tk() + root.configure(background='#69615e') + app = gui.Application( + dispatcher, data_manager, plots, master=root, + window_manager_title= + "Telemetry monitor - Demo" if config == Config.DEMO else + "Telemetry monitor - Flight" if config == Config.FLIGHT else + "Telemetry monitor", + show_send_value=False, + serial_console_height=1, + plots_size=(10,10), + plots_background='#69615e', + controls_background='#69615e', + default_baud=57600) + + running = False + def heartbeat(): + if running: #get link2ground status all the time, exept if I delete this line, the start/abort button doesn't work right for some reason + app.sendValue("c") #c stands for connection, i.e. heartbeat + app.after(500, heartbeat) + + def start_abort_handler(): + nonlocal running + if running: + app.sendValue("a") #a= abort + else: + app.reset() + if app.start(): + app.sendValue("s") #s=start + running = True + heartbeat() + + def check_stop(time, status): + nonlocal running + if status == 'STAND_BY': #was 'STAND_BY' + #status.config(bg='#0fe9f5') + app.stop() + running = False + if config == Config.DEMO: + countdown.config(text=" T-00:10:00") + else: + countdown.config(text=" T-01:00:00") + start_abort_button.config(text="Start", bg='lime green') + #I figured it out... having the below code included caused the unresponsiveness :( + + #elif status == 'TERMINAL_COUNT': + # status.config(bg='#e6d925') #status.config is bad specifically!!!! + #elif status == 'POWERED_ASCENT': + # status.config(bg='#e04122') + + #elif status == 'UNPOWERED_ASCENT': + # status.config(bg='#bd857b') + #elif status == 'FREEFALL': + # status.config(bg='#760e99') + #elif status == 'DROGUE_DESCENT': + # status.config(bg='#8b65ba') + #elif status == 'MAIN_DESCENT': + # status.config(bg='#402aa1') + #elif status == 'LANDED': + # status.config(bg='#4395d9') + + else: + start_abort_button.config(text="Abort", bg='red') + #if status == 'TERMINAL_COUNT': + # running = True + #qqq= 10 #causes issues also + + + def update_time(abs_time, relative_time): + if not running: + relative_time = -60000 if config != Config.DEMO else -10000 + sign = "+" if relative_time > 0 else "-" + mins = abs(relative_time) // 60000 + secs = (abs(relative_time) // 1000) % 60 + cs = (abs(relative_time) // 10) % 100 + text = " T{}{:02}:{:02}:{:02}".format(sign, mins, secs, cs) + countdown.config(text=text, fg = "green" if relative_time > 0 else "red") + + #def update_state(time, status): + # if status== 'STAND_BY': + # status.config(bg='#e6d925') + # if status == 'POWERED_ASCENT': + # status.config(bg='#e04122') + # if status == 'UNPOWERED_ASCENT': + # status.config(bg='#bd857b') + # if status == 'FREEFALL': + # status.config(bg='#760e99') + # if status == 'DROGUE_DESCENT': + # status.config(bg='#8b65ba') + # if status == 'MAIN_DESCENT': + # status.config(bg='#402aa1') + # if status == 'LANDED': + # status.config(bg='#4395d9') + + + + + def state_name(name): + lower_name = name[0] + name[1:].lower() + return lower_name.replace("_", " ") + + # Add custom gui controls + Label(app, text="\nControls", bg= '#69615e').pack(side=TOP) + + # Sensor controls + #Label(app, text="\nSensor Controls").pack() + controlsFrame = Frame(app, bg= '#69615e') + controlsFrame.pack(side=TOP) #expand=1 + sensorStatus = Label(controlsFrame, text="All sensors functional", fg='green', font=("Helvetica", 12), bg= '#c9c1be') #light grey c9c1be + sensorStatus.grid(row=0,column=0,columnspan=4) + #Button(controlsFrame, text="Zero force", command=lambda: app.sendValue("zero_force")).pack(side=LEFT) + #Button(controlsFrame, text="Zero pressure", command=lambda: app.sendValue("zero_pressure")).pack(side=LEFT) + #Button(controlsFrame, text="Reset board", command=lambda: app.sendValue("reset")).pack(side=LEFT) + #BMP_cf - pressure calibration factor input + #Launch_ALT + #ATST + #launch_lat + #launch_lon + #land_lat + #land_lon + u1= Entry(controlsFrame) #,width=20 + u1.grid(row=1,column=2,columnspan=2) + #u1.focus_set() #not sure if this is needed + #def sendVar(): + b1= Button(controlsFrame, text="set launch alt (m)",font=("Helvetica", 7), width=20, command=lambda: app.sendValue("Launch_ALT",float(u1.get()))) + b1.grid(row=1,column=0,padx=5,columnspan=2) + + u2= Entry(controlsFrame) + u2.grid(row=2,column=2,columnspan=2) + b2= Button(controlsFrame, text="set BMP calib. factor (HPA)", width=20,font=("Helvetica", 7) , command=lambda: app.sendValue("BMP_cf",u2.get())) + b2.grid(row=2,column=0,padx=5,columnspan=2) + + u3= Entry(controlsFrame) + u3.grid(row=3,column=2,columnspan=2) + b3= Button(controlsFrame, text="set ATST (m)", width=20,font=("Helvetica", 7), command=lambda: app.sendValue("ATST",u3.get())) + b3.grid(row=3,column=0,padx=5,columnspan=2) + + u4= Entry(controlsFrame,width=8) + u4.grid(row=4,column=1) + b4= Button(controlsFrame, text="set launch lat", width=10, font=("Helvetica", 7), command=lambda: app.sendValue("launch_lat",u4.get())) + b4.grid(row=4,column=0,padx=1) + + u5= Entry(controlsFrame,width=8) + u5.grid(row=4,column=3) + b5= Button(controlsFrame, text="set launch lon", font=("Helvetica", 7), width=10, command=lambda: app.sendValue("launch_lon",u5.get())) + b5.grid(row=4,column=2,padx=1) + + u6= Entry(controlsFrame,width=8) + u6.grid(row=5,column=1) + b6= Button(controlsFrame, text="set land lat", width=10, font=("Helvetica", 7), command=lambda: app.sendValue("land_lat",u6.get())) + b6.grid(row=5,column=0,padx=1) + + u7= Entry(controlsFrame,width=8) + u7.grid(row=5,column=3) + b7= Button(controlsFrame, text="set land lon", width=10, font=("Helvetica", 7), command=lambda: app.sendValue("land_lon",u7.get())) + b7.grid(row=5,column=2,padx=1) + + + # Igniter controls- can only switch one way... + #Label(app, text="\Igniter Controls").pack() + #igniterFrame = Frame(app) + #igniterFrame.pack() + #fireButton = Button(igniterFrame, text="Camera", background="orange", command=lambda: app.sendValue("cam")) + #fireButton.pack() + + # Throttle controls + #Label(app, text="\nThrottle Controls").pack() + throttleFrame = Frame(app, bg= '#69615e') #bg or background works + throttleFrame.pack() + Label(throttleFrame, text="Drogue", font=("Helvetica", 10), bg= '#69615e').grid(row=0, column=1, sticky= W) + Label(throttleFrame, text="Main", font=("Helvetica", 10), bg= '#69615e').grid(row=0, column=2, padx=15, sticky= W) + Label(throttleFrame, text="1", font=("Helvetica", 10), bg= '#69615e').grid(row=1, column=0, sticky=W, padx=5) + Label(throttleFrame, text="2", font=("Helvetica", 10), bg= '#69615e').grid(row=2, column=0, sticky=W, padx=5) + Label(throttleFrame, text="Cam", font=("Helvetica", 10), bg= '#69615e').grid(row=3, column=0, sticky=W, padx=5) + Button(throttleFrame, text="Reset board", font=("Helvetica", 8), command=lambda: app.sendValue("reset")).grid(row=3, column=2, sticky=W, padx=5) + + + valves = ['P1', 'P2', 'P3', 'P4', 'P5'] #indexing starts at 0 + valveSettings = {valve: False for valve in valves} + valveButtons = {} + for i, valve in enumerate(valves): + button = Button(throttleFrame, text="closed", background="red") + button.bind('', lambda _, valve=valve: app.sendValue(valve + "cmd", not valveSettings[valve])) + button.grid(row=1 + int(i / 2), column=1 + i % 2, sticky=W, padx=5) + valveButtons[valve] = button + + # Run controls + #Label(app, text="\nRun Controls").pack() + runFrame = Frame(app, bg= '#69615e') + runFrame.pack(side=TOP) + start_abort_button = Button(runFrame, text="Start", command=start_abort_handler, bg="lime green", height=2, width=8) + start_abort_button.pack(side=LEFT) + countdown = Label(runFrame, text=" T-01:00:00", width=10, fg="red", font=("Helvetica", 16, "bold"), bg= '#c9c1be') #b9b1ae + countdown.pack(side=TOP) + + status = Label(runFrame, text=" Stand by", width=16, font=("Helvetica", 10), bg= '#c9c1be') + status.pack(side=TOP) + + + + + + # Listeners + app.dispatcher.add_listener('status', lambda time, val: status.config(text=" " + state_name(val))) + app.dispatcher.add_listener('status', check_stop) + #app.dispatcher.add_listener('status', update_state) + app.dispatcher.add_listener('run_time', update_time) + app.dispatcher.add_listener('ss', lambda time, val: sensorStatus.config(text="All sensors functional" if val else "Sensor error encountered", + fg='green' if val else 'red')) + + for valve in valves: + def callback(time, val, valve=valve): + valveSettings[valve] = val + valveButtons[valve].config(text='open' if val else 'closed', background='green' if val else 'red') + app.dispatcher.add_listener(valve + '_setting', callback) + + return app if __name__ == '__main__': - app.mainloop() + init().mainloop() diff --git a/src/gui.py b/src/gui.py index b085b93..d1b8ac3 100644 --- a/src/gui.py +++ b/src/gui.py @@ -50,7 +50,8 @@ def __init__(self, 'serial_console_height': 15, 'default_baud': 9600, 'plots_size': (12,10), - 'plots_background': 'white'} + 'plots_background': 'white', + 'controls_background': None} new_flags.update(flags) self.flags = new_flags @@ -69,8 +70,8 @@ def __init__(self, self.flags['full_screen'] = args.full_screen # Init gui - Frame.__init__(self, master) - self.pack() + Frame.__init__(self, master, bg=self.flags['controls_background']) + self.pack(side=LEFT) #side=LEFT #master.iconbitmap('telemetry.png') master.attributes("-fullscreen", self.flags['full_screen']) master.bind('', self.unmaximize) @@ -118,32 +119,31 @@ def _createWidgets(self): """Initialize the various widgets in the main frame.""" self._setupPlots() - buttons = Frame(self) - self.controlButton = Button(buttons, text="Start", command=self.start, bg="lime green") + buttons = Frame(self, bg=self.flags['controls_background']) + self.controlButton = Button(buttons, text="Start", command=self.start, bg="lime green",font=("Helvetica", 8)) self.controlButton.pack(side=LEFT) - self.exitButton = Button(buttons, text="Quit", command=self.terminate) + self.exitButton = Button(buttons, text="Quit", command=self.terminate, font=("Helvetica", 8)) self.exitButton.pack(side=LEFT) - self.openButton = Button(buttons, text="Open...", command=self.openFile) + self.openButton = Button(buttons, text="Open", command=self.openFile, font=("Helvetica", 8), height=1, width=4) self.openButton.pack(side=LEFT) - self.saveButton = Button(buttons, text="Save as...", command=self.saveFile) + self.saveButton = Button(buttons, text="Save as", command=self.saveFile, font=("Helvetica", 8), height=1 ,width=6) self.saveButton.pack(side=LEFT) - self.exportButton = Button(buttons, text="Export csv...", command=self.exportCSV) + self.exportButton = Button(buttons, text="Export csv", command=self.exportCSV, font=("Helvetica", 8), height=1, width=8) self.exportButton.pack(side=LEFT) - self.thresholdButton = Button(buttons, text="Set thresholds...", command=self.configureThresholds) + self.thresholdButton = Button(buttons, text="Thresholds", command=self.configureThresholds, font=("Helvetica", 8), height=1, width=8) self.thresholdButton.pack(side=LEFT) - - buttons.pack() - serialLabel = Label(self, text="\nSerial console") + buttons.pack() + serialLabel = Label(self, text="\nSerial console", bg=self.flags['controls_background']) serialLabel.pack() - serial = Frame(self) - serialControls = Frame(serial) + serial = Frame(self, bg=self.flags['controls_background']) + serialControls = Frame(serial, bg=self.flags['controls_background']) self.serialPort = StringVar(self) self.serialSelect = OptionMenu(serialControls, self.serialPort, []) @@ -173,7 +173,7 @@ def _createWidgets(self): self.serialIn.bind('', self.sendSerial) self.serialIn.pack() - serialSendButtons = Frame(serial) + serialSendButtons = Frame(serial, bg=self.flags['controls_background']) self.sendButton = Button(serialSendButtons, text="Send", command=self.sendSerial) self.sendButton.pack(side=LEFT) @@ -182,7 +182,6 @@ def _createWidgets(self): self.sendNewlineButton.pack(side=LEFT) serialSendButtons.pack() - serialSendButtons = Frame(serial) serial.pack() @@ -191,7 +190,7 @@ def _createWidgets(self): if self.flags['show_send_value']: sendValuesLabel.pack() - sendValues = Frame(self) + sendValues = Frame(self, bg=self.flags['controls_background']) self.sendDataName = Entry(sendValues, width=10) self.sendDataName.bind('', self.sendValues) @@ -205,13 +204,13 @@ def _createWidgets(self): sendValues.pack() # Value readout widget - valuesLabel = Label(self, text="\nCurrent values") + valuesLabel = Label(self, text="\nCurrent values", bg=self.flags['controls_background'], height= 2) if self.flags['show_current_values']: - valuesLabel.pack() + valuesLabel.pack(side=TOP) + + valuesTableFrame = Frame(self, bg=self.flags['controls_background']) + self.valuesTable = Treeview(valuesTableFrame, columns=('value',), show='tree',height=6) - valuesTableFrame = Frame(self) - self.valuesTable = Treeview(valuesTableFrame, columns=('value',), show='tree') - self.valuesTable.insert('', 'end', 'abs time', text="abs time (ms)") self.dispatcher.add_listener( 'sys time', @@ -224,8 +223,8 @@ def _createWidgets(self): ty.name, lambda time, data, id=ty.name: self.valuesTable.item(id, values=(data,)), 100) - - valuesScrollbar = Scrollbar(valuesTableFrame, orient=VERTICAL) + + valuesScrollbar = Scrollbar(valuesTableFrame, orient=VERTICAL, width=15) valuesScrollbar.config(command=self.valuesTable.yview) self.valuesTable.configure(yscrollcommand=valuesScrollbar.set) @@ -241,7 +240,7 @@ def _setupPlots(self): self.fig = matplotlib.figure.Figure(figsize=self.flags['plots_size'], dpi=100) self.fig.set_facecolor(self.flags['plots_background']) self.canvas = matplotlib.backends.backend_tkagg.FigureCanvasTkAgg(self.fig, master=self) - + plot.setup(self.plots, self.fig, self.manager) def animate(i): @@ -287,18 +286,18 @@ def start(self): else: showerror("Error", "No serial port selected") return False - + def stop(self): """Stop the current run.""" self.manager.stop() self.controlButton.config(text="Reset", bg="grey", command=self.reset) - + def reset(self): """Reset for the next run.""" self.resetValuesTable() self.manager.reset() self.controlButton.config(text="Start", bg="lime green", command=self.start) - + def sendSerial(self, _=None): """Send a serial command.""" if self.serialManager: @@ -306,7 +305,7 @@ def sendSerial(self, _=None): self.serialIn.delete(0, 'end') else: showerror("Error", "No serial port selected") - + def sendSerialNewline(self, _=None): """Handler for sending a serial command with a newline appended.""" if self.serialManager: @@ -314,13 +313,13 @@ def sendSerialNewline(self, _=None): self.serialIn.delete(0, 'end') else: showerror("Error", "No serial port selected") - + def sendValues(self, _=None): """Handler for sending a formatted packet.""" self.sendValue(self.sendDataName.get(), self.sendDataIn.get()) if self.serialManager: self.sendDataIn.delete(0, 'end') - + def sendValue(self, name, value=""): """Send a formatted packet.""" if self.serialManager: @@ -329,20 +328,20 @@ def sendValue(self, name, value=""): else: showerror("Error", "No serial port selected") return False - + def unmaximize(self, _): """Disable full-screen mode.""" self.master.attributes("-fullscreen", False) - + def toggleFullScreen(self, _): """Toggle full-screen mode.""" self.master.attributes("-fullscreen", not self.master.attributes('-fullscreen')) - + def resetValuesTable(self): """Clear the values table after a reset or changing ports.""" for item in self.valuesTable.get_children(): self.valuesTable.item(item, values=()) - + def changeSerial(self, *args): """Handler for changing the serial port.""" # Try-catch needed b/c error messages in tracebacks on Windows are buggy @@ -357,20 +356,20 @@ def changeSerial(self, *args): except: if not sys.platform.startswith('win'): traceback.print_exc() - + def checkSerial(self): """Handler for checking the available serial ports.""" self.serialSelect['menu'].delete(0, 'end') for port in serialmanager.serial_ports(): self.serialSelect['menu'].add_command(label=port, command=lambda p=port: self.serialPort.set(p)) - + def _startListeners(self): """Begin updating the data manager listeners.""" if self.manager.update_all_listeners(): self.after(50, self._startListeners) else: self.after(100, self._startListeners) - + def startSerial(self): """Begin reading serial data.""" if self.serialManager: @@ -382,7 +381,7 @@ def startSerial(self): except OSError: self.serialManager = None self.checkSerial() - + _last_update_time = "" def saveBackup(self, times, values): """Write the current run data log to the backup file once per second @@ -391,10 +390,10 @@ def saveBackup(self, times, values): if len(values) > 0 and values[-1] != self._last_update_time: self._last_update_time = values[0] open(self.flags["backup_log"], 'w').write(self.manager.dump('json')) - + def openFile(self): """Handler to open a file.""" - filename = askopenfilename(filetypes=[('All files', '*.*'), + filename = askopenfilename(filetypes=[('All files', '*.*'), ('JSON data file', '*.json'), ('Log file', '*.log'), ('Comma-seperated values', '*.csv')]) @@ -415,7 +414,7 @@ def openFile(self): showerror("Error", "Invalid data file:\n" + str(e)) else: self.controlButton.config(text="Reset", bg="grey", command=self.reset) - + def saveFile(self): """Handler to save to a file.""" filename = asksaveasfilename(defaultextension='.json', @@ -450,7 +449,7 @@ def saveFile(self): self.serialManager.paused = False else: self.fig.savefig(filename) - + def exportCSV(self): """Handler to export as a CSV file.""" filename = asksaveasfilename() @@ -587,7 +586,7 @@ def configureThresholds(self): enabled_vars[data.name] = enabled_var lower_vars[data.name] = lower_var upper_vars[data.name] = upper_var - + ok = False def accept(): nonlocal ok diff --git a/src/plot.py b/src/plot.py index 183695d..3262085 100644 --- a/src/plot.py +++ b/src/plot.py @@ -37,7 +37,7 @@ def __init__(self, suffix = suffix.replace("_", " ") if " " in suffix: suffix = suffix[suffix.index(" "):] - + if ys_names: self.ys_names = ys_names elif suffix: @@ -133,7 +133,7 @@ def fn(x_data, y_data, y=y): j += 1 x_data = new_x_data y_data = new_y_data - + assert len(x_data) == len(y_data) # 'Prune' plotted data to avoid slow-down with large amounts of data indices = range(0, len(x_data), max(len(x_data) // max_points, 1)) @@ -167,7 +167,7 @@ def gen_layout(plots): available = {(i, j): True for i in range(width) for j in range(height)} - + # List of info for each plot layout = []