class TelemetrySoftware(wx.Frame): def __init__(self): super(TelemetrySoftware, self).__init__(None, title="URSS Telemetry") # initial data values self.voltage = MAX_VOLTAGE self.amperage = MIN_AMPERAGE self.rpm = MIN_RPM self.controllerTemp = MIN_CONTROLLER_TEMP # plot data self.voltage_values = [] self.amperage_values = [] self.timestamps = [] self.goalVoltageLineDisplayed = False # fake telemetry flag self.fake_telemetry = True self.fake_telemetry_counter = 0 self.Maximize(True) self.InitUI() self.InitTelemetryThread() self.Centre() self.Show() def InitUI(self): panelRoot = wx.Panel(self) panelRootSizer = wx.BoxSizer(wx.VERTICAL) topSplitter = wx.SplitterWindow(panelRoot, style=wx.SP_LIVE_UPDATE | wx.SP_3DSASH) topVerticalSplitter = wx.SplitterWindow(topSplitter, style=wx.SP_LIVE_UPDATE | wx.SP_3DSASH) ############################################# # TOP-LEFT PANEL (STATUS AND CONTROLS) # ############################################# panelTopLeft = wx.Panel(topVerticalSplitter) panelTopLeftSizer = wx.BoxSizer(wx.VERTICAL) # create gauges and set initial values self.voltageGauge = wx.Gauge(panelTopLeft, -1, MAX_VOLTAGE - MIN_VOLTAGE, (0, 0), (250, 25)) self.voltageGauge.SetValue(self.voltage - MIN_VOLTAGE) self.amperageGauge = wx.Gauge(panelTopLeft, -1, MAX_AMPERAGE - MIN_AMPERAGE, (0, 0), (250, 25)) self.amperageGauge.SetValue(self.amperage - MIN_AMPERAGE) self.rpmGauge = wx.Gauge(panelTopLeft, -1, MAX_RPM - MIN_RPM, (0, 0), (250, 25)) self.rpmGauge.SetValue(self.rpm - MIN_RPM) self.controllerTempGauge = wx.Gauge( panelTopLeft, -1, MAX_CONTROLLER_TEMP - MIN_CONTROLLER_TEMP, (0, 0), (250, 25)) self.controllerTempGauge.SetValue(self.controllerTemp - MIN_CONTROLLER_TEMP) # create labels self.voltageLabel = wx.StaticText( panelTopLeft, -1, "Voltage (" + str(self.voltage) + ")") self.amperagLabel = wx.StaticText( panelTopLeft, -1, "Amperage (" + str(self.amperage) + ")") self.rpmLabel = wx.StaticText(panelTopLeft, -1, "RPM (" + str(self.rpm) + ")") self.controllerTempLabel = wx.StaticText( panelTopLeft, -1, "Controller Temperature (" + str(self.controllerTemp) + ")") # Add voltage Gauge and label to BoxSizer panelTopLeftSizer.Add(self.voltageLabel, 0, wx.ALIGN_CENTRE_HORIZONTAL) panelTopLeftSizer.Add(self.voltageGauge, 1, wx.ALIGN_CENTRE_HORIZONTAL) # Add amperage Gauge and label to BoxSizer panelTopLeftSizer.Add(self.amperagLabel, 0, wx.ALIGN_CENTRE_HORIZONTAL) panelTopLeftSizer.Add(self.amperageGauge, 1, wx.ALIGN_CENTRE_HORIZONTAL) # Add RPM Gauge and label to BoxSizer panelTopLeftSizer.Add(self.rpmLabel, 0, wx.ALIGN_CENTRE_HORIZONTAL) panelTopLeftSizer.Add(self.rpmGauge, 1, wx.ALIGN_CENTRE_HORIZONTAL) # Add controller temp Gauge and label to BoxSizer panelTopLeftSizer.Add(self.controllerTempLabel, 0, wx.ALIGN_CENTRE_HORIZONTAL) panelTopLeftSizer.Add(self.controllerTempGauge, 1, wx.ALIGN_CENTRE_HORIZONTAL) # Add BoxSizer to panel panelTopLeft.SetSizer(panelTopLeftSizer) ################################ # TOP-RIGHT PANEL (GRAPH) # ################################ panelTopRight = wx.Panel(topVerticalSplitter) panelTopRightSizer = wx.BoxSizer(wx.VERTICAL) # create top button bar topButtonPanel = wx.Panel(panelTopRight, -1) topButtonSizer = wx.BoxSizer(wx.HORIZONTAL) goalVoltageLabel = wx.StaticText( topButtonPanel, -1, ' End Goal Voltage Value (V): ') self.goalEndVoltage = FloatCtrl(topButtonPanel, size=(100, -1), value=34.5, precision=1) endTimeLabel = wx.StaticText(topButtonPanel, -1, ' End Time (min): ') self.endTime = FloatCtrl(topButtonPanel, size=(100, -1), value=120, precision=0) plotGoalVoltageButton = wx.Button(topButtonPanel, -1, 'Plot Goal Voltage', size=(250, -1)) plotGoalVoltageButton.Bind(wx.EVT_BUTTON, self.OnPlotGoalVoltage) topButtonSizer.Add(goalVoltageLabel) topButtonSizer.Add(self.goalEndVoltage) topButtonSizer.Add(endTimeLabel) topButtonSizer.Add(self.endTime) topButtonSizer.Add(plotGoalVoltageButton) topButtonPanel.SetSizer(topButtonSizer) topButtonSizer.Fit(topButtonPanel) # create plot panel self.plotPanel = PlotPanel(panelTopRight) # create bottom button bar bottomButtonPanel = wx.Panel(panelTopRight, -1) bottomButtonSizer = wx.BoxSizer(wx.HORIZONTAL) exportButton = wx.Button(bottomButtonPanel, -1, 'Export Plot to CSV', size=(250, -1)) exportButton.Bind(wx.EVT_BUTTON, self.ExportPlotDataToCSV) resetButton = wx.Button(bottomButtonPanel, -1, 'Reset Graph', size=(250, -1)) resetButton.Bind(wx.EVT_BUTTON, self.ResetGraph) bottomButtonSizer.Add(exportButton, 1) bottomButtonSizer.Add(resetButton, 1) bottomButtonPanel.SetSizer(bottomButtonSizer) bottomButtonSizer.Fit(bottomButtonPanel) # Add panels to top right sizer panelTopRightSizer.Add(topButtonPanel, 0, wx.EXPAND | wx.ALL) panelTopRightSizer.Add(self.plotPanel, 1, wx.EXPAND | wx.ALL) panelTopRightSizer.Add(bottomButtonPanel, 0) # Add BoxSizer to panel panelTopRight.SetSizer(panelTopRightSizer) # add top panels to vertical splitter topVerticalSplitter.SplitVertically(panelTopLeft, panelTopRight) topVerticalSplitter.SetSashGravity(0.25) ############################################# # BOTTOM PANEL (LOG & TELEMETRY INPUT) # ############################################# panelBottom = wx.Panel(topSplitter) panelBottomSizer = wx.BoxSizer(wx.VERTICAL) self.logPane = LogPane(panelBottom) logPaneLabel = wx.StaticText( panelBottom, label="Telemetry Message Log (Last 100 messages shown):") logPaneLabel.SetFont( wx.Font(14, wx.FONTFAMILY_TELETYPE, wx.NORMAL, wx.BOLD)) panelBottomSizer.Add(logPaneLabel, 0, wx.ALIGN_TOP) panelBottomSizer.Add(self.logPane, 1, wx.EXPAND | wx.ALL) panelBottom.SetSizer(panelBottomSizer) topSplitter.SplitHorizontally(topVerticalSplitter, panelBottom) topSplitter.SetSashGravity(0.75) panelRootSizer.Add(topSplitter, 1, wx.EXPAND | wx.ALL) panelRoot.SetSizer(panelRootSizer) def InitTelemetryThread(self): print("Initializing telemetry thread...") if not self.fake_telemetry: success = False try: self.serial = serial.Serial("/dev/cu.usbserial-DN01236H", 57600) success = True if not self.serial.is_open: success = False except Exception: print("Could not open serial radio!") if not success: # If we fail to connect to serial, display error and then quitself. dial = wx.MessageDialog( None, 'Could not connect to serial radio. Please plug in the serial radio adapter and restart your computer!', 'Error', wx.OK | wx.ICON_ERROR) dial.ShowModal() exit(0) thread = threading.Thread(target=self.TelemetryThread) thread.start() print("Done.") def TelemetryCallback(self, message): timestamp = int(round(time.time())) if len(self.timestamps) == 0: self.t0 = timestamp self.logPane.AddLine(message) if not self.fake_telemetry: # parse JSON message m = json.loads(message) split_message = m['message'].split(',') if split_message[0] == 'BATTERY': self.voltage = float(split_message[2][2:]) / 1000 self.amperage = float(split_message[4][2:]) / 1000 * -1 # add values to lists self.voltage_values.append(self.voltage) self.amperage_values.append(self.amperage) self.timestamps.append(timestamp - self.t0) # update gauges and plot self.UpdateGauges() self.UpdatePlot() def UpdateGauges(self): self.voltageGauge.SetValue(self.voltage - MIN_VOLTAGE) self.voltageLabel.SetLabel("Voltage (" + str(self.voltage) + "/" + str(MAX_VOLTAGE) + ")") self.amperageGauge.SetValue(self.amperage - MIN_AMPERAGE) self.amperagLabel.SetLabel("Amperage (" + str(self.amperage) + "/" + str(MAX_AMPERAGE) + ")") self.rpmGauge.SetValue(self.rpm - MIN_RPM) self.rpmLabel.SetLabelText("RPM (" + str(self.rpm) + "/" + str(MAX_RPM) + ")") self.controllerTempGauge.SetValue(self.controllerTemp - MIN_CONTROLLER_TEMP) self.controllerTempLabel.SetLabelText("Controller Temperature (" + str(self.controllerTemp) + ")") def UpdatePlot(self): v_n = len(self.voltage_values) tdat = np.array(self.timestamps) vdat = np.array(self.voltage_values) adat = np.array(self.amperage_values) if v_n <= 2: self.plotPanel.plot(tdat, vdat, xlabel='Time (s from start)', ylabel='Voltage (V)', label='Voltage', style='solid') self.plotPanel.oplot(tdat, adat, y2label='Amperage (A)', side='right', label='Amperage', style='long dashed', show_legend=True) else: self.plotPanel.update_line(0, tdat, vdat, draw=True) self.plotPanel.update_line(1, tdat, adat, draw=True) def OnPlotGoalVoltage(self, event=None): v = float(self.goalEndVoltage.GetValue()) t = int(self.endTime.GetValue()) * 60 if len(self.voltage_values) > 2 and v <= self.voltage and t >= 0: tdat = np.linspace(0, t, t) vdat = np.linspace(self.voltage_values[0], v, t) if not self.goalVoltageLineDisplayed: self.goalVoltageLineDisplayed = True self.plotPanel.oplot(tdat, vdat, label='Goal Voltage', style='short dashed') else: self.plotPanel.update_line(2, tdat, vdat, draw=True) def ResetGraph(self, event=None): del self.timestamps[:] del self.voltage_values[:] del self.amperage_values[:] self.goalVoltageLineDisplayed = False def ExportPlotDataToCSV(self, event=None): filename = 'exported_data_' + str(int(round( time.time() * 1000))) + '.csv' with open(filename, 'wb') as datafile: w = csv.writer(datafile) w.writerow(['Time (s)', 'Voltage (V)', 'Amperage (A)']) for i in range(len(self.timestamps)): w.writerow([ str(self.timestamps[i]), str(self.voltage_values[i]), str(self.amperage_values[i]) ]) def TelemetryThread(self): while True: if self.fake_telemetry: self.fake_telemetry_counter += 1 wx.CallAfter( self.TelemetryCallback, "Fake Telemetry Element " + str(self.fake_telemetry_counter) + "\n") time.sleep(0.5) else: line = self.serial.readline() wx.CallAfter(self.TelemetryCallback, line)
class StripChartFrame(wx.Frame): def __init__(self, parent, ID, **kws): kws["style"] = wx.DEFAULT_FRAME_STYLE | wx.RESIZE_BORDER | wx.TAB_TRAVERSAL wx.Frame.__init__(self, parent, ID, '', wx.DefaultPosition, wx.Size(-1, -1), **kws) self.SetTitle("wxmplot StripChart Demo") self.tmin = 15.0 self.SetFont(wx.Font(12, wx.SWISS, wx.NORMAL, wx.BOLD, False)) menu = wx.Menu() ID_EXIT = wx.NewId() ID_TIMER = wx.NewId() menu_exit = menu.Append(ID_EXIT, "E&xit", "Terminate the program") menuBar = wx.MenuBar() menuBar.Append(menu, "&File") self.SetMenuBar(menuBar) self.Bind(wx.EVT_MENU, self.OnExit, menu_exit) self.Bind(wx.EVT_CLOSE, self.OnExit) sbar = self.CreateStatusBar(2, wx.CAPTION) sfont = sbar.GetFont() sfont.SetWeight(wx.BOLD) sfont.SetPointSize(11) sbar.SetFont(sfont) self.SetStatusWidths([-3, -1]) self.SetStatusText('', 0) mainsizer = wx.BoxSizer(wx.VERTICAL) btnpanel = wx.Panel(self, -1) btnsizer = wx.BoxSizer(wx.HORIZONTAL) b_on = wx.Button(btnpanel, -1, 'Start', size=(-1, -1)) b_off = wx.Button(btnpanel, -1, 'Stop', size=(-1, -1)) b_on.Bind(wx.EVT_BUTTON, self.onStartTimer) b_off.Bind(wx.EVT_BUTTON, self.onStopTimer) tlabel = wx.StaticText(btnpanel, -1, ' Time range:') self.time_range = FloatCtrl(btnpanel, size=(100, -1), value=abs(self.tmin), precision=1) btnsizer.Add(b_on, 0, wx.ALIGN_LEFT | wx.ALIGN_CENTER | wx.LEFT, 0) btnsizer.Add(b_off, 0, wx.ALIGN_LEFT | wx.ALIGN_CENTER | wx.LEFT, 0) btnsizer.Add( tlabel, 1, wx.GROW | wx.ALL | wx.ALIGN_LEFT | wx.ALIGN_CENTER | wx.LEFT, 0) btnsizer.Add(self.time_range, 0, wx.ALIGN_LEFT | wx.ALIGN_CENTER | wx.LEFT, 0) btnpanel.SetSizer(btnsizer) btnsizer.Fit(btnpanel) self.plotpanel = PlotPanel(self, messenger=self.write_message) self.plotpanel.BuildPanel() self.plotpanel.set_xlabel('Time from Present (s)') mainsizer.Add(btnpanel, 0, wx.GROW | wx.ALIGN_LEFT | wx.ALIGN_CENTER | wx.LEFT, 0) mainsizer.Add( self.plotpanel, 1, wx.GROW | wx.ALL | wx.ALIGN_LEFT | wx.ALIGN_CENTER | wx.LEFT, 0) self.SetSizer(mainsizer) mainsizer.Fit(self) self.Bind(wx.EVT_TIMER, self.onTimer) self.timer = wx.Timer(self) self.count = 0 self.Refresh() wx.CallAfter(self.onStartTimer) def write_message(self, msg, panel=0): """write a message to the Status Bar""" self.SetStatusText(msg, panel) def onStartTimer(self, event=None): self.count = 0 t0, y0 = next_data() self.ylist = [y0] self.tlist = [t0] self.tmin_last = -10000 self.time0 = time.time() self.timer.Start(50) def onStopTimer(self, event=None): self.timer.Stop() def onTimer(self, event): self.count += 1 etime = time.time() - self.time0 self.tmin = float(self.time_range.GetValue()) t1, y1 = next_data() self.tlist.append(t1) self.ylist.append(y1) tdat = np.array(self.tlist) - t1 mask = np.where(tdat > -abs(self.tmin)) ydat = np.array(self.ylist) n = len(self.ylist) if n <= 2: self.plotpanel.plot(tdat, ydat) else: self.plotpanel.update_line(0, tdat, ydat, draw=True) self.write_message(" %i points in %8.4f s" % (n, etime)) lims = self.plotpanel.get_viewlimits() try: ymin, ymax = ydat[mask].min(), ydat[mask].max() except: ymin, ymax = ydat.min(), ydat.max() tmin = max(int(min(tdat)) - 1.0, -self.tmin) if (ymin < lims[2] or ymax > lims[3] or tmin != self.tmin_last or time.time() - self.last_update > 2): self.tmin_last = tmin self.last_update = time.time() self.plotpanel.set_xylims((tmin, 0, ymin, ymax)) def OnAbout(self, event): dlg = wx.MessageDialog(self, "wxmplot example: stripchart app", "About WXMPlot test", wx.OK | wx.ICON_INFORMATION) dlg.ShowModal() dlg.Destroy() def OnExit(self, event): self.Destroy()
class Viewer1DXRD(wx.Panel): ''' Frame for housing all 1D XRD viewer widgets ''' label='Viewer' def __init__(self,parent,owner=None,_larch=None): wx.Panel.__init__(self, parent) self.parent = parent self.owner = owner ## Default information self.data_name = [] self.xy_data = [] self.xy_plot = [] self.plotted_data = [] self.xy_scale = [] self.wavelength = None self.xlabel = 'q (A^-1)' self.xunits = ['q','d'] self.cif_name = [] self.cif_data = [] self.cif_plot = [] self.plotted_cif = [] self.x_for_zoom = None self.Panel1DViewer() ############################################## #### PANEL DEFINITIONS def Panel1DViewer(self): ''' Frame for housing all 1D XRD viewer widgets ''' leftside = self.LeftSidePanel(self) rightside = self.RightSidePanel(self) panel1D = wx.BoxSizer(wx.HORIZONTAL) panel1D.Add(leftside,flag=wx.ALL,border=10) panel1D.Add(rightside,proportion=1,flag=wx.EXPAND|wx.ALL,border=10) self.SetSizer(panel1D) def Toolbox(self,panel): ''' Frame for visual toolbox ''' tlbx = wx.StaticBox(self,label='PLOT TOOLBOX') vbox = wx.StaticBoxSizer(tlbx,wx.VERTICAL) ########################### ## X-Scale hbox_xaxis = wx.BoxSizer(wx.HORIZONTAL) ttl_xaxis = wx.StaticText(self, label='X-SCALE') self.ch_xaxis = wx.Choice(self,choices=self.xunits) self.ch_xaxis.Bind(wx.EVT_CHOICE, self.checkXaxis) hbox_xaxis.Add(ttl_xaxis, flag=wx.RIGHT, border=8) hbox_xaxis.Add(self.ch_xaxis, flag=wx.EXPAND, border=8) vbox.Add(hbox_xaxis, flag=wx.ALL, border=10) ########################### ## Y-Scale hbox_yaxis = wx.BoxSizer(wx.HORIZONTAL) ttl_yaxis = wx.StaticText(self, label='Y-SCALE') yscales = ['linear','log'] self.ch_yaxis = wx.Choice(self,choices=yscales) self.ch_yaxis.Bind(wx.EVT_CHOICE, None) hbox_yaxis.Add(ttl_yaxis, flag=wx.RIGHT, border=8) hbox_yaxis.Add(self.ch_yaxis, flag=wx.EXPAND, border=8) vbox.Add(hbox_yaxis, flag=wx.ALL, border=10) return vbox def DataBox(self,panel): ''' Frame for data toolbox ''' tlbx = wx.StaticBox(self,label='DATA TOOLBOX') vbox = wx.StaticBoxSizer(tlbx,wx.VERTICAL) ########################### ## DATA CHOICE self.ch_data = wx.Choice(self,choices=self.data_name) self.ch_data.Bind(wx.EVT_CHOICE, self.onSELECT) vbox.Add(self.ch_data, flag=wx.EXPAND|wx.ALL, border=8) #self.ttl_data = wx.StaticText(self, label='') #vbox.Add(self.ttl_data, flag=wx.EXPAND|wx.ALL, border=8) ########################### # self.ck_bkgd = wx.CheckBox(self,label='BACKGROUND') # self.ck_smth = wx.CheckBox(self,label='SMOOTHING') # # self.ck_bkgd.Bind(wx.EVT_CHECKBOX, None) # self.ck_smth.Bind(wx.EVT_CHECKBOX, None) # # vbox.Add(self.ck_bkgd, flag=wx.ALL, border=8) # vbox.Add(self.ck_smth, flag=wx.ALL, border=8) ########################### ## Scale hbox_scl = wx.BoxSizer(wx.HORIZONTAL) ttl_scl = wx.StaticText(self, label='SCALE Y TO:') self.entr_scale = wx.TextCtrl(self,wx.TE_PROCESS_ENTER) btn_scale = wx.Button(self,label='set') btn_scale.Bind(wx.EVT_BUTTON, self.normalize1Ddata) hbox_scl.Add(ttl_scl, flag=wx.RIGHT, border=8) hbox_scl.Add(self.entr_scale, flag=wx.RIGHT, border=8) hbox_scl.Add(btn_scale, flag=wx.RIGHT, border=8) vbox.Add(hbox_scl, flag=wx.BOTTOM|wx.TOP, border=8) ########################### ## Hide/show and reset hbox_btns = wx.BoxSizer(wx.HORIZONTAL) btn_hide = wx.Button(self,label='hide') btn_reset = wx.Button(self,label='reset') btn_rmv = wx.Button(self,label='remove') btn_hide.Bind(wx.EVT_BUTTON, self.hide1Ddata) btn_reset.Bind(wx.EVT_BUTTON, self.reset1Dscale) btn_rmv.Bind(wx.EVT_BUTTON, self.remove1Ddata) btn_hide.Disable() btn_rmv.Disable() hbox_btns.Add(btn_reset, flag=wx.ALL, border=10) hbox_btns.Add(btn_hide, flag=wx.ALL, border=10) hbox_btns.Add(btn_rmv, flag=wx.ALL, border=10) vbox.Add(hbox_btns, flag=wx.ALL, border=10) return vbox def AddPanel(self,panel): hbox = wx.BoxSizer(wx.HORIZONTAL) btn_data = wx.Button(panel,label='ADD NEW DATA SET') btn_data.Bind(wx.EVT_BUTTON, self.loadXYFILE) btn_cif = wx.Button(panel,label='ADD NEW CIF') btn_cif.Bind(wx.EVT_BUTTON, self.loadCIF) hbox.Add(btn_data, flag=wx.ALL, border=8) hbox.Add(btn_cif, flag=wx.ALL, border=8) return hbox def LeftSidePanel(self,panel): vbox = wx.BoxSizer(wx.VERTICAL) plttools = self.Toolbox(self) addbtns = self.AddPanel(self) dattools = self.DataBox(self) vbox.Add(plttools,flag=wx.ALL,border=10) vbox.Add(addbtns,flag=wx.ALL,border=10) vbox.Add(dattools,flag=wx.ALL,border=10) return vbox def RightSidePanel(self,panel): vbox = wx.BoxSizer(wx.VERTICAL) self.plot1DXRD(panel) btnbox = self.QuickButtons(panel) vbox.Add(self.plot1D,proportion=1,flag=wx.ALL|wx.EXPAND,border = 10) vbox.Add(btnbox,flag=wx.ALL|wx.ALIGN_RIGHT,border = 10) return vbox def QuickButtons(self,panel): buttonbox = wx.BoxSizer(wx.HORIZONTAL) btn_img = wx.Button(panel,label='SAVE FIGURE') btn_calib = wx.Button(panel,label='PLOT SETTINGS') btn_integ = wx.Button(panel,label='RESET PLOT') btn_img.Bind(wx.EVT_BUTTON, self.onSAVEfig) btn_calib.Bind(wx.EVT_BUTTON, self.onPLOTset) btn_integ.Bind(wx.EVT_BUTTON, self.onRESETplot) buttonbox.Add(btn_img, flag=wx.ALL, border=8) buttonbox.Add(btn_calib, flag=wx.ALL, border=8) buttonbox.Add(btn_integ, flag=wx.ALL, border=8) return buttonbox ############################################## #### PLOTPANEL FUNCTIONS def plot1DXRD(self,panel): self.plot1D = PlotPanel(panel,size=(1000, 500)) self.plot1D.messenger = self.owner.write_message ## Set defaults for plotting self.plot1D.set_ylabel('Intensity (a.u.)') self.plot1D.cursor_mode = 'zoom' ## trying to get this functionality into our gui ## mkak 2016.11.10 # interactive_legend().show() def onSAVEfig(self,event): self.plot1D.save_figure() def onPLOTset(self,event): self.plot1D.configure() def onRESETplot(self,event): self.plot1D.reset_config() ############################################## #### XRD PLOTTING FUNCTIONS def add1Ddata(self,x,y,name=None,cif=False,wavelength=None): plt_no = len(self.data_name) if cif: if name is None: name = 'cif %i' % plt_no else: name = 'cif: %s' % name else: if name is None: name = 'dataset %i' % plt_no else: name = 'data: %s' % name if wavelength is not None: self.addLAMBDA(wavelength) ## Add to data array lists self.data_name.append(name) self.xy_scale.append(max(y)) self.xy_data.extend([x,y]) ## redefine x,y based on scales self.xy_plot.extend([x,y]) ## Add to plot self.plotted_data.append(self.plot1D.oplot(x,y,label=name,show_legend=True))#,xlabel=self.xlabel)) ## Use correct x-axis units self.checkXaxis(None) self.ch_data.Set(self.data_name) self.ch_data.SetStringSelection(name) ## Update toolbox panel, scale all cif to 1000 if cif is True: self.entr_scale.SetValue('1000') self.normalize1Ddata(None) else: self.entr_scale.SetValue(str(self.xy_scale[plt_no])) def addLAMBDA(self,wavelength,units='m'): ## convert to units A if units == 'm': self.wavelength = wavelength*1e10 elif units == 'cm': self.wavelength = wavelength*1e8 elif units == 'mm': self.wavelength = wavelength*1e7 elif units == 'um': self.wavelength = wavelength*1e4 elif units == 'nm': self.wavelength = wavelength*1e1 else: ## units 'A' self.wavelength = wavelength self.xunits.append(u'2\u03B8') self.ch_xaxis.Set(self.xunits) def normalize1Ddata(self,event): plt_no = self.ch_data.GetSelection() self.xy_scale[plt_no] = float(self.entr_scale.GetValue()) if self.xy_scale[plt_no] <= 0: self.xy_scale[plt_no] = max(self.xy_data[(plt_no*2+1)]) self.entr_scale.SetValue(str(self.xy_scale[plt_no])) y = self.xy_data[(plt_no*2+1)] self.xy_plot[(plt_no*2+1)] = y/np.max(y) * self.xy_scale[plt_no] self.updatePLOT() def remove1Ddata(self,event): ## Needs pop up warning: "Do you really want to delete this data set from plotter? ## Current settings will not be saved." ## mkak 2016.11.10 plt_no = self.ch_data.GetSelection() print('EVENTUALLY, button will remove plot: %s' % self.data_name[plt_no]) ## removing name from list works... do not activate till rest is working ## mkak 2016.11.10 # self.data_name.remove(self.data_name[plt_no]) # self.ch_data.Set(self.data_name) def hide1Ddata(self,event): plt_no = self.ch_data.GetSelection() print('EVENTUALLY, button will hide plot: %s' % self.data_name[plt_no]) def onSELECT(self,event): data_str = self.ch_data.GetString(self.ch_data.GetSelection()) # self.ttl_data.SetLabel('SELECTED: %s' % data_str) plt_no = self.ch_data.GetSelection() self.entr_scale.SetValue(str(self.xy_scale[plt_no])) def checkXaxis(self, event): if self.ch_xaxis.GetSelection() == 2: for plt_no in range(len(self.plotted_data)): self.xy_plot[plt_no*2] = calc_q_to_2th(self.xy_data[plt_no*2],self.wavelength) elif self.ch_xaxis.GetSelection() == 1: for plt_no in range(len(self.plotted_data)): self.xy_plot[plt_no*2] = calc_q_to_d(self.xy_data[plt_no*2]) else: for plt_no in range(len(self.plotted_data)): self.xy_plot[plt_no*2] = self.xy_data[plt_no*2] if self.ch_xaxis.GetSelection() == 2: self.xlabel = r'$2\Theta$'+r' $(^\circ)$' elif self.ch_xaxis.GetSelection() == 1: self.xlabel = 'd ($\AA$)' else: self.xlabel = 'q (1/$\AA$)' self.plot1D.set_xlabel(self.xlabel) self.updatePLOT() def updatePLOT(self): xmax,xmin,ymax,ymin = None,0,None,0 if len(self.plotted_data) > 0: for plt_no in range(len(self.plotted_data)): i = plt_no*2 j = i+1 x = self.xy_plot[i] y = self.xy_plot[j] if xmax is None or xmax < max(x): xmax = max(x) if xmin > min(x): xmin = min(x) if ymax is None or ymax < max(y): ymax = max(y) if ymin > min(y): ymin = min(y) self.plot1D.update_line(plt_no,x,y) self.unzoom_all() self.plot1D.canvas.draw() if self.ch_xaxis.GetSelection() == 1: xmax = 5 self.plot1D.set_xylims([xmin, xmax, ymin, ymax]) def reset1Dscale(self,event): plt_no = self.ch_data.GetSelection() self.xy_plot[(plt_no*2+1)] = self.xy_data[(plt_no*2+1)] self.plot1D.update_line(plt_no,self.xy_plot[(plt_no*2)], self.xy_plot[(plt_no*2+1)]) self.plot1D.canvas.draw() self.unzoom_all() self.updatePLOT() self.xy_scale[plt_no] = max(self.xy_data[(plt_no*2+1)]) self.entr_scale.SetValue(str(self.xy_scale[plt_no])) ####### BEGIN ####### ## THIS IS DIRECTLY FROM XRDDISPLAY.PY ## mkak 2016.11.11 def unzoom_all(self, event=None): xmid, xrange, xmin, xmax = self._get1Dlims() self._set_xview(xmin, xmax) self.xview_range = None def _get1Dlims(self): xmin, xmax = self.plot1D.axes.get_xlim() xrange = xmax-xmin xmid = (xmax+xmin)/2.0 if self.x_for_zoom is not None: xmid = self.x_for_zoom return (xmid, xrange, xmin, xmax) def _set_xview(self, x1, x2, keep_zoom=False, pan=False): xmin,xmax = self.abs_limits() xrange = x2-x1 x1 = max(xmin,x1) x2 = min(xmax,x2) if pan: if x2 == xmax: x1 = x2-xrange elif x1 == xmin: x2 = x1+xrange if not keep_zoom: self.x_for_zoom = (x1+x2)/2.0 self.plot1D.axes.set_xlim((x1, x2)) self.xview_range = [x1, x2] self.plot1D.canvas.draw() def abs_limits(self): if len(self.data_name) > 0: xmin, xmax = self.xy_plot[0].min(), self.xy_plot[0].max() return xmin,xmax ####### END ####### ############################################## #### XRD FILE OPENING/SAVING def loadXYFILE(self,event): wildcards = 'XRD data file (*.xy)|*.xy|All files (*.*)|*.*' dlg = wx.FileDialog(self, message='Choose 1D XRD data file', defaultDir=os.getcwd(), wildcard=wildcards, style=wx.FD_OPEN) path, read = None, False if dlg.ShowModal() == wx.ID_OK: read = True path = dlg.GetPath().replace('\\', '/') dlg.Destroy() if read: try: x,y = xy_file_reader(path) self.add1Ddata(x,y,name=os.path.split(path)[-1]) except: print('incorrect xy file format: %s' % os.path.split(path)[-1]) def saveXYFILE(self,event): wildcards = 'XRD data file (*.xy)|*.xy|All files (*.*)|*.*' dlg = wx.FileDialog(self, 'Save data as...', defaultDir=os.getcwd(), wildcard=wildcards, style=wx.SAVE|wx.OVERWRITE_PROMPT) path, save = None, False if dlg.ShowModal() == wx.ID_OK: save = True path = dlg.GetPath().replace('\\', '/') dlg.Destroy() if save: ## mkak 2016.11.16 print('need to write something to save data - like pyFAI does?') def loadCIF(self,event): wildcards = 'XRD cifile (*.cif)|*.cif|All files (*.*)|*.*' dlg = wx.FileDialog(self, message='Choose CIF', defaultDir=os.getcwd(), wildcard=wildcards, style=wx.FD_OPEN) path, read = None, False if dlg.ShowModal() == wx.ID_OK: read = True path = dlg.GetPath().replace('\\', '/') dlg.Destroy() if read: cifile = os.path.split(path)[-1] try: cif = xu.materials.Crystal.fromCIF(path) except: print('incorrect file format: %s' % os.path.split(path)[-1]) return ## generate hkl list hkllist = [] maxhkl = 8 for i in range(-maxhkl,maxhkl+1): for j in range(-maxhkl,maxhkl+1): for k in range(-maxhkl,maxhkl+1): if i+j+k > 0: # as long as h,k,l all positive, eliminates 0,0,0 hkllist.append([i,j,k]) hc = constants.value(u'Planck constant in eV s') * \ constants.value(u'speed of light in vacuum') * 1e-3 ## units: keV-m if self.wavelength is not None: qlist = cif.Q(hkllist) Flist = cif.StructureFactorForQ(qlist,(hc/(self.wavelength*(1e-10))*1e3)) Fall = [] qall = [] hklall = [] for i,hkl in enumerate(hkllist): if np.abs(Flist[i]) > 0.01: Fadd = np.abs(Flist[i]) qadd = np.linalg.norm(qlist[i]) if qadd not in qall and qadd < 6: Fall.extend((0,Fadd,0)) qall.extend((qadd,qadd,qadd)) if np.shape(Fall)[0] > 0: Fall = np.array(Fall) qall = np.array(qall) self.add1Ddata(qall,Fall,name=os.path.split(path)[-1],cif=True) else: print('Could not calculate real structure factors.') else: print('Wavelength/energy must be specified for structure factor calculations.') def openPONI(self,event): wildcards = 'pyFAI calibration file (*.poni)|*.poni|All files (*.*)|*.*' dlg = wx.FileDialog(self, message='Choose pyFAI calibration file', defaultDir=os.getcwd(), wildcard=wildcards, style=wx.FD_OPEN) path, read = None, False if dlg.ShowModal() == wx.ID_OK: read = True path = dlg.GetPath().replace('\\', '/') dlg.Destroy() if read: try: print('Loading calibration file: %s' % path) ai = pyFAI.load(path) except: print('Not recognized as a pyFAI calibration file.') return self.addLAMBDA(ai._wavelength,units='m') def setLAMBDA(self,event): dlg = SetLambdaDialog() path, okay = None, False if dlg.ShowModal() == wx.ID_OK: okay = True wavelength = dlg.wavelength dlg.Destroy() if okay: self.addLAMBDA(wavelength,units='A')
class Fitting1DXRD(wx.Panel): ''' Frame for housing all 1D XRD fitting widgets ''' label='Fitting' def __init__(self,parent,owner=None,_larch=None): wx.Panel.__init__(self, parent) self.parent = parent self.owner = owner ## Default information self.xlabel = 'q (A^-1)' self.xunits = ['q','d'] self.Panel1DFitting() ############################################## #### PANEL DEFINITIONS def Panel1DFitting(self): ''' Frame for housing all 1D XRD viewer widgets ''' leftside = self.LeftSidePanel(self) rightside = self.RightSidePanel(self) panel1D = wx.BoxSizer(wx.HORIZONTAL) panel1D.Add(leftside,flag=wx.ALL,border=10) panel1D.Add(rightside,proportion=1,flag=wx.EXPAND|wx.ALL,border=10) self.SetSizer(panel1D) def Toolbox(self,panel): ''' Frame for visual toolbox ''' tlbx = wx.StaticBox(self,label='PLOT TOOLBOX') vbox = wx.StaticBoxSizer(tlbx,wx.VERTICAL) ########################### ## X-Scale hbox_xaxis = wx.BoxSizer(wx.HORIZONTAL) ttl_xaxis = wx.StaticText(self, label='X-SCALE') self.ch_xaxis = wx.Choice(self,choices=self.xunits) self.ch_xaxis.Bind(wx.EVT_CHOICE, self.checkXaxis) hbox_xaxis.Add(ttl_xaxis, flag=wx.RIGHT, border=8) hbox_xaxis.Add(self.ch_xaxis, flag=wx.EXPAND, border=8) vbox.Add(hbox_xaxis, flag=wx.ALL, border=10) ########################### ## Y-Scale hbox_yaxis = wx.BoxSizer(wx.HORIZONTAL) ttl_yaxis = wx.StaticText(self, label='Y-SCALE') yscales = ['linear','log'] self.ch_yaxis = wx.Choice(self,choices=yscales) self.ch_yaxis.Bind(wx.EVT_CHOICE, None) hbox_yaxis.Add(ttl_yaxis, flag=wx.RIGHT, border=8) hbox_yaxis.Add(self.ch_yaxis, flag=wx.EXPAND, border=8) vbox.Add(hbox_yaxis, flag=wx.ALL, border=10) return vbox def LeftSidePanel(self,panel): vbox = wx.BoxSizer(wx.VERTICAL) plttools = self.Toolbox(self) vbox.Add(plttools,flag=wx.ALL,border=10) return vbox def RightSidePanel(self,panel): vbox = wx.BoxSizer(wx.VERTICAL) self.plot1DXRD(panel) btnbox = self.QuickButtons(panel) vbox.Add(self.plot1D,proportion=1,flag=wx.ALL|wx.EXPAND,border = 10) vbox.Add(btnbox,flag=wx.ALL|wx.ALIGN_RIGHT,border = 10) return vbox def QuickButtons(self,panel): buttonbox = wx.BoxSizer(wx.HORIZONTAL) btn_img = wx.Button(panel,label='SAVE FIGURE') btn_calib = wx.Button(panel,label='PLOT SETTINGS') btn_integ = wx.Button(panel,label='RESET PLOT') btn_img.Bind(wx.EVT_BUTTON, self.onSAVEfig) btn_calib.Bind(wx.EVT_BUTTON, self.onPLOTset) btn_integ.Bind(wx.EVT_BUTTON, self.onRESETplot) buttonbox.Add(btn_img, flag=wx.ALL, border=8) buttonbox.Add(btn_calib, flag=wx.ALL, border=8) buttonbox.Add(btn_integ, flag=wx.ALL, border=8) return buttonbox ############################################## #### PLOTPANEL FUNCTIONS def plot1DXRD(self,panel): self.plot1D = PlotPanel(panel,size=(1000, 500)) self.plot1D.messenger = self.owner.write_message ## Set defaults for plotting self.plot1D.set_ylabel('Intensity (a.u.)') self.plot1D.cursor_mode = 'zoom' ## trying to get this functionality into our gui ## mkak 2016.11.10 # interactive_legend().show() def onSAVEfig(self,event): self.plot1D.save_figure() def onPLOTset(self,event): self.plot1D.configure() def onRESETplot(self,event): self.plot1D.reset_config() def checkXaxis(self, event): if self.ch_xaxis.GetSelection() == 2: for plt_no in range(len(self.plotted_data)): self.xy_plot[plt_no*2] = calc_q_to_2th(self.xy_data[plt_no*2],self.wavelength) elif self.ch_xaxis.GetSelection() == 1: for plt_no in range(len(self.plotted_data)): self.xy_plot[plt_no*2] = calc_q_to_d(self.xy_data[plt_no*2]) else: for plt_no in range(len(self.plotted_data)): self.xy_plot[plt_no*2] = self.xy_data[plt_no*2] if self.ch_xaxis.GetSelection() == 2: self.xlabel = r'$2\Theta$'+r' $(^\circ)$' elif self.ch_xaxis.GetSelection() == 1: self.xlabel = 'd ($\AA$)' else: self.xlabel = 'q (1/$\AA$)' self.plot1D.set_xlabel(self.xlabel) self.updatePLOT() def updatePLOT(self): xmax,xmin,ymax,ymin = None,0,None,0 if len(self.plotted_data) > 0: for plt_no in range(len(self.plotted_data)): i = plt_no*2 j = i+1 x = self.xy_plot[i] y = self.xy_plot[j] if xmax is None or xmax < max(x): xmax = max(x) if xmin > min(x): xmin = min(x) if ymax is None or ymax < max(y): ymax = max(y) if ymin > min(y): ymin = min(y) self.plot1D.update_line(plt_no,x,y) self.unzoom_all() self.plot1D.canvas.draw() if self.ch_xaxis.GetSelection() == 1: xmax = 5 self.plot1D.set_xylims([xmin, xmax, ymin, ymax]) def reset1Dscale(self,event): plt_no = self.ch_data.GetSelection() self.xy_plot[(plt_no*2+1)] = self.xy_data[(plt_no*2+1)] self.plot1D.update_line(plt_no,self.xy_plot[(plt_no*2)], self.xy_plot[(plt_no*2+1)]) self.plot1D.canvas.draw() self.unzoom_all() self.updatePLOT() self.xy_scale[plt_no] = max(self.xy_data[(plt_no*2+1)]) self.entr_scale.SetValue(str(self.xy_scale[plt_no])) ####### BEGIN ####### ## THIS IS DIRECTLY FROM XRDDISPLAY.PY ## mkak 2016.11.11 def unzoom_all(self, event=None): xmid, xrange, xmin, xmax = self._get1Dlims() self._set_xview(xmin, xmax) self.xview_range = None def _get1Dlims(self): xmin, xmax = self.plot1D.axes.get_xlim() xrange = xmax-xmin xmid = (xmax+xmin)/2.0 if self.x_for_zoom is not None: xmid = self.x_for_zoom return (xmid, xrange, xmin, xmax) def _set_xview(self, x1, x2, keep_zoom=False, pan=False): xmin,xmax = self.abs_limits() xrange = x2-x1 x1 = max(xmin,x1) x2 = min(xmax,x2) if pan: if x2 == xmax: x1 = x2-xrange elif x1 == xmin: x2 = x1+xrange if not keep_zoom: self.x_for_zoom = (x1+x2)/2.0 self.plot1D.axes.set_xlim((x1, x2)) self.xview_range = [x1, x2] self.plot1D.canvas.draw() def abs_limits(self): if len(self.data_name) > 0: xmin, xmax = self.xy_plot[0].min(), self.xy_plot[0].max() return xmin,xmax
class StripChartFrame(wx.Frame): def __init__(self, parent, ID, **kws): kws["style"] = wx.DEFAULT_FRAME_STYLE|wx.RESIZE_BORDER|wx.TAB_TRAVERSAL wx.Frame.__init__(self, parent, ID, '', wx.DefaultPosition, wx.Size(-1,-1), **kws) self.SetTitle("wxmplot StripChart Demo") self.tmin = 15.0 self.SetFont(wx.Font(12,wx.SWISS,wx.NORMAL,wx.BOLD,False)) menu = wx.Menu() ID_EXIT = wx.NewId() ID_TIMER = wx.NewId() menu_exit = menu.Append(ID_EXIT, "E&xit", "Terminate the program") menuBar = wx.MenuBar() menuBar.Append(menu, "&File"); self.SetMenuBar(menuBar) self.Bind(wx.EVT_MENU, self.OnExit, menu_exit) self.Bind(wx.EVT_CLOSE, self.OnExit) sbar = self.CreateStatusBar(2,wx.CAPTION) sfont = sbar.GetFont() sfont.SetWeight(wx.BOLD) sfont.SetPointSize(11) sbar.SetFont(sfont) self.SetStatusWidths([-3,-1]) self.SetStatusText('',0) mainsizer = wx.BoxSizer(wx.VERTICAL) btnpanel = wx.Panel(self, -1) btnsizer = wx.BoxSizer(wx.HORIZONTAL) b_on = wx.Button(btnpanel, -1, 'Start', size=(-1,-1)) b_off = wx.Button(btnpanel, -1, 'Stop', size=(-1,-1)) b_on.Bind(wx.EVT_BUTTON, self.onStartTimer) b_off.Bind(wx.EVT_BUTTON, self.onStopTimer) tlabel = wx.StaticText(btnpanel, -1, ' Time range:') self.time_range = FloatCtrl(btnpanel, size=(100, -1), value=abs(self.tmin), precision=1) btnsizer.Add(b_on, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER|wx.LEFT, 0) btnsizer.Add(b_off, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER|wx.LEFT, 0) btnsizer.Add(tlabel, 1, wx.GROW|wx.ALL|wx.ALIGN_LEFT|wx.ALIGN_CENTER|wx.LEFT, 0) btnsizer.Add(self.time_range, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER|wx.LEFT, 0) btnpanel.SetSizer(btnsizer) btnsizer.Fit(btnpanel) self.plotpanel = PlotPanel(self, messenger=self.write_message) self.plotpanel.BuildPanel() self.plotpanel.set_xlabel('Time from Present (s)') mainsizer.Add(btnpanel, 0, wx.GROW|wx.ALIGN_LEFT|wx.ALIGN_CENTER|wx.LEFT, 0) mainsizer.Add(self.plotpanel, 1, wx.GROW|wx.ALL|wx.ALIGN_LEFT|wx.ALIGN_CENTER|wx.LEFT, 0) self.SetSizer(mainsizer) mainsizer.Fit(self) self.Bind(wx.EVT_TIMER, self.onTimer) self.timer = wx.Timer(self) self.count = 0 self.Refresh() wx.CallAfter(self.onStartTimer) def write_message(self, msg, panel=0): """write a message to the Status Bar""" self.SetStatusText(msg, panel) def onStartTimer(self,event=None): self.count = 0 t0,y0 = next_data() self.ylist = [y0] self.tlist = [t0] self.tmin_last = -10000 self.time0 = time.time() self.timer.Start(50) def onStopTimer(self,event=None): self.timer.Stop() def onTimer(self, event): self.count += 1 etime = time.time() - self.time0 self.tmin = float(self.time_range.GetValue()) t1, y1 = next_data() self.tlist.append(t1) self.ylist.append(y1) tdat = np.array(self.tlist) - t1 mask = np.where(tdat > -abs(self.tmin)) ydat = np.array(self.ylist) n = len(self.ylist) if n <= 2: self.plotpanel.plot(tdat, ydat) else: self.plotpanel.update_line(0, tdat, ydat, draw=True) self.write_message(" %i points in %8.4f s" % (n,etime)) lims = self.plotpanel.get_viewlimits() try: ymin, ymax = ydat[mask].min(), ydat[mask].max() except: ymin, ymax = ydat.min(), ydat.max() tmin = max(int(min(tdat)) - 1.0, -self.tmin) if (ymin < lims[2] or ymax > lims[3] or tmin != self.tmin_last or time.time()-self.last_update > 2): self.tmin_last = tmin self.last_update = time.time() self.plotpanel.set_xylims((tmin, 0, ymin, ymax)) def OnAbout(self, event): dlg = wx.MessageDialog(self, "wxmplot example: stripchart app", "About WXMPlot test", wx.OK | wx.ICON_INFORMATION) dlg.ShowModal() dlg.Destroy() def OnExit(self, event): self.Destroy()
class Runner(noname.MyFrame1): power_rendered = False data = data.Data def __init__(self, parent): noname.MyFrame1.__init__(self, parent) def render_input_sound(self, event): if wx.Event.GetEventType(event) == 10084 or wx.Event.GetEventType( event) == 10161: self.m_panel2.Refresh() self.canvas1 = PlotPanel(self.m_panel2, size=(self.m_panel2.GetSize())) if hasattr(self.data, 'time') and hasattr(self.data, 'input_sound'): self.canvas1.plot(self.data.time, self.data.input_sound) return else: self.canvas1.update_line(0, self.data.time, self.data.input_sound, draw=True) return def render_power(self, event): if wx.Event.GetEventType(event) == 10084 or wx.Event.GetEventType( event) == 10161: if (self.power_rendered): self.canvas2.update_line(0, self.data.truncated_frequency, self.data.truncated_power, draw=True) else: self.canvas2 = PlotPanel(self.m_panel3, size=(self.m_panel3.GetSize())) if hasattr(self.data, 'truncated_frequency') and hasattr( self.data, 'truncated_power'): self.canvas2.plot(self.data.truncated_frequency, self.data.truncated_power, 'r') self.canvas2.oplot( self.data.truncated_frequency, np.full((len(self.data.truncated_frequency)), self.data.min)) self.power_rendered = True return # For slider else: p = backend.get_cutoff_value(self.data.min, self.data.slope, self.m_slider1.GetValue()) if (self.power_rendered): self.canvas2.update_line( 1, self.data.truncated_frequency, np.full((len(self.data.truncated_frequency)), p), draw=True) else: self.canvas2 = PlotPanel(self.m_panel3, size=(self.m_panel3.GetSize())) self.canvas2.plot(self.data.truncated_frequency, self.data.truncated_power, 'r') self.canvas2.oplot( self.data.truncated_frequency, np.full((len(self.data.truncated_frequency)), self.data.min)) self.power_rendered = True return def render_output(self, event): if wx.Event.GetEventType(event) == 10084 or wx.Event.GetEventType( event) == 10161: self.m_panel4.Refresh() self.canvas3 = PlotPanel(self.m_panel4, size=(self.m_panel4.GetSize())) if hasattr(self.data, 'time') and hasattr(self.data, 'ifft'): self.canvas3.plot(self.data.time, self.data.ifft) return else: self.canvas3.update_line(0, self.data.time, self.data.ifft, draw=True) return def loadAudio(self, event): ''' Loads audio from path and calls render functions.\n wx event: 10161 ''' self.data.load_sound(location=self.m_filePicker1.GetPath()) self.data.load_power_graph() # Fire screen refresh event self.render_input_sound(event) self.render_power(event) self.render_output(event) return def slider_move(self, event): self.render_power(event) self.data.load_output( backend.get_cutoff_value(self.data.min, self.data.slope, self.m_slider1.GetValue())) self.render_output(event) def export(self, event): self.data.export() def play(self, event): sd.play(self.data.ifft, self.data.fs) time.sleep(len(self.data.input_sound) / self.data.fs) sd.stop() def add_noise(self, event): self.data.add_noise() self.render_input_sound(event) self.render_power(event) self.render_output(event)