class ScanViewerFrame(wx.Frame): _about = """Scan Viewer, Matt Newville <newville @ cars.uchicago.edu> """ TIME_MSG = 'Point %i/%i, Time Remaining ~ %s ' def __init__(self, parent, dbname=None, server='sqlite', host=None, port=None, user=None, password=None, create=True, **kws): wx.Frame.__init__(self, None, -1, style=FRAMESTYLE) title = "Epics Step Scan Viewer" self.parent = parent self.scandb = getattr(parent, 'scandb', None) if self.scandb is None and dbname is not None: self.scandb = ScanDB(dbname=dbname, server=server, host=host, user=user, password=password, port=port, create=create) self.larch = None self.lgroup = None self.SetTitle(title) self.SetSize((750, 750)) self.SetFont(Font(9)) self.createMainPanel() self.createMenus() self.statusbar = self.CreateStatusBar(2, 0) self.statusbar.SetStatusWidths([-3, -1]) statusbar_fields = ["Initializing....", " "] for i in range(len(statusbar_fields)): self.statusbar.SetStatusText(statusbar_fields[i], i) if self.scandb is not None: self.get_info = self.scandb.get_info self.scandb_server = self.scandb.server self.live_scanfile = None self.live_cpt = -1 self.total_npts = 1 self.scantimer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.onScanTimer, self.scantimer) self.scantimer.Start(50) self.Show() self.Raise() def onScanTimer(self, evt=None, **kws): if self.lgroup is None: return curfile = fix_filename(self.get_info('filename')) sdata = self.scandb.get_scandata() npts = len(sdata[-1].data) if (npts > 2 and npts == self.live_cpt and curfile == self.live_scanfile): # no new data return # filename changed -- scan starting, so update # list of positioners, detectors, etc force_newplot = False if curfile != self.live_scanfile: force_newplot = True self.live_scanfile = curfile self.title.SetLabel(curfile) self.set_column_names(sdata) if npts == self.live_cpt: return time_est = hms(self.get_info('scan_time_estimate', as_int=True)) msg = self.TIME_MSG % (npts, self.total_npts, time_est) self.SetStatusText(msg) self.live_cpt = npts for row in sdata: dat = row.data if self.scandb_server == 'sqlite': dat = json.loads(dat.replace('{', '[').replace('}', ']')) setattr(self.lgroup, fix_varname(row.name), np.array(dat)) if npts > 1: self.onPlot(npts=npts, force_newplot=force_newplot) def set_column_names(self, sdata): """set column names from values read from scandata table""" self.lgroup.array_units = [fix_varname(s.units) for s in sdata] self.total_npts = self.get_info('scan_total_points', as_int=True) self.live_cpt = -1 xcols, ycols, y2cols = [], [], [] for s in sdata: nam = fix_varname(s.name) ycols.append(nam) if s.notes.lower().startswith('pos'): xcols.append(nam) y2cols = ycols[:] + ['1.0', '0.0', ''] xarr_old = self.xarr.GetStringSelection() self.xarr.SetItems(xcols) ix = xcols.index(xarr_old) if xarr_old in xcols else 0 self.xarr.SetSelection(ix) for i in range(2): for j in range(3): yold = self.yarr[i][j].GetStringSelection() idef, cols = 0, y2cols if i == 0 and j == 0: idef, cols = 1, ycols self.yarr[i][j].SetItems(cols) iy = cols.index(yold) if yold in cols else idef self.yarr[i][j].SetSelection(iy) def createMainPanel(self): wx.CallAfter(self.init_larch) mainpanel = wx.Panel(self) mainsizer = wx.BoxSizer(wx.VERTICAL) panel = wx.Panel(mainpanel) self.yops = [[],[]] self.yarr = [[],[]] arr_kws= {'choices':[], 'size':(120, -1), 'action':self.onPlot} self.title = SimpleText(panel, 'initializing...', font=Font(13), colour='#880000') self.xarr = add_choice(panel, **arr_kws) for i in range(3): self.yarr[0].append(add_choice(panel, **arr_kws)) self.yarr[1].append(add_choice(panel, **arr_kws)) for opts, sel, wid in ((PRE_OPS, 0, 100), (ARR_OPS, 3, 60), (ARR_OPS, 3, 60)): arr_kws['choices'] = opts arr_kws['size'] = (wid, -1) self.yops[0].append(add_choice(panel, default=sel, **arr_kws)) self.yops[1].append(add_choice(panel, default=sel, **arr_kws)) # place widgets sizer = wx.GridBagSizer(5, 10) sizer.Add(self.title, (0, 1), (1, 6), LCEN, 2) sizer.Add(SimpleText(panel, ' X ='), (1, 0), (1, 1), CEN, 0) sizer.Add(self.xarr, (1, 3), (1, 1), RCEN, 0) ir = 1 for i in range(2): ir += 1 label = ' Y%i =' % (i+1) sizer.Add(SimpleText(panel, label), (ir, 0), (1, 1), CEN, 0) sizer.Add(self.yops[i][0], (ir, 1), (1, 1), CEN, 0) sizer.Add(SimpleText(panel, '[('), (ir, 2), (1, 1), CEN, 0) sizer.Add(self.yarr[i][0], (ir, 3), (1, 1), CEN, 0) sizer.Add(self.yops[i][1], (ir, 4), (1, 1), CEN, 0) sizer.Add(self.yarr[i][1], (ir, 5), (1, 1), CEN, 0) sizer.Add(SimpleText(panel, ')'), (ir, 6), (1, 1), LCEN, 0) sizer.Add(self.yops[i][2], (ir, 7), (1, 1), CEN, 0) sizer.Add(self.yarr[i][2], (ir, 8), (1, 1), CEN, 0) sizer.Add(SimpleText(panel, ']'), (ir, 9), (1, 1), LCEN, 0) ir += 1 sizer.Add(hline(panel), (ir, 0), (1, 12), CEN|wx.GROW|wx.ALL, 0) pack(panel, sizer) self.plotpanel = PlotPanel(mainpanel, size=(520, 550), axissize=(0.18, 0.18, 0.70, 0.70), fontsize=8) self.plotpanel.messenger = self.write_message self.plotpanel.canvas.figure.set_facecolor((0.98,0.98,0.97)) btnsizer = wx.StdDialogButtonSizer() btnpanel = wx.Panel(mainpanel) btnsizer.Add(add_button(btnpanel, 'Pause', action=self.onPause)) btnsizer.Add(add_button(btnpanel, 'Resume', action=self.onResume)) btnsizer.Add(add_button(btnpanel, 'Abort', action=self.onAbort)) pack(btnpanel, btnsizer) mainsizer.Add(panel, 0, LCEN|wx.EXPAND, 2) mainsizer.Add(self.plotpanel, 1, wx.GROW|wx.ALL, 1) mainsizer.Add(btnpanel, 0, wx.GROW|wx.ALL, 1) pack(mainpanel, mainsizer) return mainpanel def onPause(self, evt=None): self.scandb.set_info('request_command_pause', 1) def onResume(self, evt=None): self.scandb.set_info('request_command_pause', 0) def onAbort(self, evt=None): self.scandb.set_info('request_command_abort', 1) def CreateFitPanel(self, parent): p = panel = wx.Panel(parent) self.fit_model = add_choice(panel, size=(100, -1), choices=('Gaussian', 'Lorentzian', 'Voigt', 'Linear', 'Quadratic', 'Step', 'Rectangle', 'Exponential')) self.fit_bkg = add_choice(panel, size=(100, -1), choices=('None', 'constant', 'linear', 'quadtratic')) self.fit_step = add_choice(panel, size=(100, -1), choices=('linear', 'error function', 'arctan')) self.fit_report = wx.StaticText(panel, -1, "", (180, 200)) sizer = wx.GridBagSizer(10, 4) sizer.Add(SimpleText(p, 'Fit Model: '), (0, 0), (1, 1), LCEN) sizer.Add(self.fit_model, (0, 1), (1, 1), LCEN) sizer.Add(SimpleText(p, 'Background: '), (1, 0), (1, 1), LCEN) sizer.Add(self.fit_bkg, (1, 1), (1, 1), LCEN) sizer.Add(SimpleText(p, 'Step Function Form: '), (2, 0), (1, 1), LCEN) sizer.Add(self.fit_step, (2, 1), (1, 1), LCEN) sizer.Add(add_button(panel, 'Show Fit', size=(100, -1), action=self.onFitPeak), (3, 0), (1, 1), LCEN) sizer.Add(self.fit_report, (0, 2), (4, 2), LCEN, 3) pack(panel, sizer) return panel def CreateXASPanel(self, parent): p = panel = wx.Panel(parent) self.xas_autoe0 = check(panel, default=True, label='auto?') self.xas_autostep = check(panel, default=True, label='auto?') self.xas_op = add_choice(panel, size=(95, -1), choices=('Raw Data', 'Pre-edged', 'Normalized', 'Flattened')) self.xas_e0 = FloatCtrl(panel, value = 0, precision=3, size=(95, -1)) self.xas_step = FloatCtrl(panel, value = 0, precision=3, size=(95, -1)) self.xas_pre1 = FloatCtrl(panel, value=-200, precision=1, size=(95, -1)) self.xas_pre2 = FloatCtrl(panel, value= -30, precision=1, size=(95, -1)) self.xas_nor1 = FloatCtrl(panel, value= 30, precision=1, size=(95, -1)) self.xas_nor2 = FloatCtrl(panel, value= 300, precision=1, size=(95, -1)) self.xas_vict = add_choice(panel, size=(50, -1), choices=('0', '1', '2', '3')) self.xas_nnor = add_choice(panel, size=(50, -1), choices=('0', '1', '2', '3')) self.xas_vict.SetSelection(1) self.xas_nnor.SetSelection(2) sizer = wx.GridBagSizer(10, 4) sizer.Add(SimpleText(p, 'Plot XAS as: '), (0, 0), (1, 1), LCEN) sizer.Add(SimpleText(p, 'E0 : '), (1, 0), (1, 1), LCEN) sizer.Add(SimpleText(p, 'Edge Step: '), (2, 0), (1, 1), LCEN) sizer.Add(SimpleText(p, 'Pre-edge range: '), (3, 0), (1, 1), LCEN) sizer.Add(SimpleText(p, 'Normalization range: '), (4, 0), (1, 1), LCEN) sizer.Add(self.xas_op, (0, 1), (1, 1), LCEN) sizer.Add(self.xas_e0, (1, 1), (1, 1), LCEN) sizer.Add(self.xas_step, (2, 1), (1, 1), LCEN) sizer.Add(self.xas_pre1, (3, 1), (1, 1), LCEN) sizer.Add(SimpleText(p, ':'), (3, 2), (1, 1), LCEN) sizer.Add(self.xas_pre2, (3, 3), (1, 1), LCEN) sizer.Add(self.xas_nor1, (4, 1), (1, 1), LCEN) sizer.Add(SimpleText(p, ':'), (4, 2), (1, 1), LCEN) sizer.Add(self.xas_nor2, (4, 3), (1, 1), LCEN) sizer.Add(self.xas_autoe0, (1, 2), (1, 2), LCEN) sizer.Add(self.xas_autostep, (2, 2), (1, 2), LCEN) sizer.Add(SimpleText(p, 'Victoreen:'), (3, 4), (1, 1), LCEN) sizer.Add(self.xas_vict, (3, 5), (1, 1), LCEN) sizer.Add(SimpleText(p, 'PolyOrder:'), (4, 4), (1, 1), LCEN) sizer.Add(self.xas_nnor, (4, 5), (1, 1), LCEN) pack(panel, sizer) return panel def onFitPeak(self, evt=None): gname = self.groupname if self.dtcorr.IsChecked(): print 'fit needs to dt correct!' dtext = [] model = self.fit_model.GetStringSelection().lower() dtext.append('Fit Model: %s' % model) bkg = self.fit_bkg.GetStringSelection() if bkg == 'None': bkg = None if bkg is None: dtext.append('No Background') else: dtext.append('Background: %s' % bkg) step = self.fit_step.GetStringSelection().lower() if model in ('step', 'rectangle'): dtext.append('Step form: %s' % step) lgroup = getattr(self.larch.symtable, gname) x = lgroup.arr_x y = lgroup.arr_y1 pgroup = fit_peak(x, y, model, background=bkg, step=step, _larch=self.larch) text = fit_report(pgroup.params, _larch=self.larch) dtext.append('Parameters: ') for pname in dir(pgroup.params): par = getattr(pgroup.params, pname) if isParameter(par): ptxt = " %s= %.4f" % (par.name, par.value) if (hasattr(par, 'stderr') and par.stderr is not None): ptxt = "%s(%.4f)" % (ptxt, par.stderr) dtext.append(ptxt) dtext = '\n'.join(dtext) text = fit_report(pgroup.params, _larch=self.larch) self.fit_report.SetLabel(dtext) def xas_process(self, gname, plotopts): """ process (pre-edge/normalize) XAS data from XAS form, overwriting larch group '_y1_' attribute to be plotted """ print 'Process XAS ', gname out = self.xas_op.GetStringSelection().lower() # raw, pre, norm, flat if out.startswith('raw'): return plotopts preopts = {'group': gname, 'e0': None, 'step': None} lgroup = getattr(self.larch.symtable, gname) if self.dtcorr.IsChecked(): print 'need to dt correct!' if not self.xas_autoe0.IsChecked(): xmin, xmax = min(lgroup.arr_x), max(lgroup.arr_x) e0 = self.xas_e0.GetValue() if e0 < xmax and e0 > xmin: preopts['e0'] = e0 if not self.xas_autostep.IsChecked(): preopts['step'] = self.xas_step.GetValue() preopts['pre1'] = self.xas_pre1.GetValue() preopts['pre2'] = self.xas_pre2.GetValue() preopts['norm1'] = self.xas_nor1.GetValue() preopts['norm2'] = self.xas_nor2.GetValue() preopts['nvict'] = self.xas_vict.GetSelection() preopts['nnorm'] = self.xas_nnor.GetSelection() preopts = ", ".join(["%s=%s" %(k, v) for k,v in preopts.items()]) preedge_cmd = "pre_edge(%s._x1_, %s._y1_, %s)" % (gname, gname, preopts) self.larch(preedge_cmd) self.xas_e0.SetValue(lgroup.e0) self.xas_step.SetValue(lgroup.edge_step) if out.startswith('pre'): self.larch('%s._y1_ = %s.norm * %s.edge_step' % (gname, gname, gname)) elif out.startswith('norm'): self.larch('%s._y1_ = %s.norm' % (gname, gname)) elif out.startswith('flat'): self.larch('%s._y1_ = %s.flat' % (gname, gname)) return plotopts def init_larch(self): t0 = time.time() from larch.wxlib import inputhook self.larch = Interpreter() self.larch.symtable.set_symbol('_sys.wx.wxapp', wx.GetApp()) self.larch.symtable.set_symbol('_sys.wx.parent', self) self.larch('%s = group(filename="%s")' % (SCANGROUP, CURSCAN)) self.larch('_sys.localGroup = %s)' % (SCANGROUP)) self.lgroup = getattr(self.larch.symtable, SCANGROUP) self.SetStatusText('ready') self.title.SetLabel('') def write_message(self, s, panel=0): """write a message to the Status Bar""" self.SetStatusText(s, panel) def onPlot(self, evt=None, npts=None, force_newplot=False): """drow plot of newest data""" new_plot = force_newplot or npts < 3 lgroup, gname = self.lgroup, SCANGROUP ix = self.xarr.GetSelection() x = self.xarr.GetStringSelection() xlabel = x popts = {'labelfontsize': 8, 'xlabel': x} try: xunits = lgroup.array_units[ix] xlabel = '%s (%s)' % (xlabel, xunits) except: pass def make_array(wids, iy): gn = SCANGROUP op1 = self.yops[iy][0].GetStringSelection() op2 = self.yops[iy][1].GetStringSelection() op3 = self.yops[iy][2].GetStringSelection() yy1 = self.yarr[iy][0].GetStringSelection() yy2 = self.yarr[iy][1].GetStringSelection() yy3 = self.yarr[iy][2].GetStringSelection() if yy1 in ('0', '1', '', None) or len(yy1) < 0: return '', '' label = yy1 expr = "%s.%s" % (gn, yy1) if yy2 != '': label = "%s%s%s" % (label, op2, yy2) expr = "%s%s%s.%s" % (expr, op2, gn, yy2) if yy3 != '': label = "(%s)%s%s" % (label, op3, yy3) expr = "(%s)%s%s.%s" % (expr, op3, gn, yy3) if op1 != '': label = "%s(%s)" % (op1, label) expr = "%s(%s)" % (op1, expr) return label, expr ylabel, yexpr = make_array(self.yops, 0) if yexpr == '': return self.larch("%s.arr_x = %s.%s" % (gname, gname, x)) self.larch("%s.arr_y1 = %s" % (gname, yexpr)) # print 'onPlot Show Groups ', lgroup # print ' : ', dir(lgroup) # print ' X -> ', x, lgroup.arr_x # print ' Y -> ', yexpr, lgroup.arr_y1 try: npts = min(len(lgroup.arr_x), len(lgroup.arr_y1)) except AttributeError: return y2label, y2expr = make_array(self.yops, 1) if y2expr != '': self.larch("%s.arr_y2 = %s" % (gname, y2expr)) n2pts = npts try: n2pts = min(len(lgroup.arr_x), len(lgroup.arr_y1), len(lgroup.arr_y2)) lgroup.arr_y2 = np.array( lgroup.arr_y2[:n2pts]) except: y2expr = '' npts = n2pts lgroup.arr_y1 = np.array( lgroup.arr_y1[:npts]) lgroup.arr_x = np.array( lgroup.arr_x[:npts]) path, fname = os.path.split(self.live_scanfile) popts.update({'title': fname, 'xlabel': xlabel, 'ylabel': ylabel, 'y2label': y2label}) ppnl = self.plotpanel if new_plot: ppnl.plot(lgroup.arr_x, lgroup.arr_y1, label= "%s: %s" % (fname, ylabel), **popts) if y2expr != '': ppnl.oplot(lgroup.arr_x, lgroup.arr_y2, side='right', label= "%s: %s" % (fname, y2label), **popts) ppnl.canvas.draw() else: ppnl.set_xlabel(xlabel) ppnl.set_ylabel(ylabel) ppnl.update_line(0, lgroup.arr_x, lgroup.arr_y1, draw=True, update_limits=True) ppnl.set_xylims((min(lgroup.arr_x), max(lgroup.arr_x), min(lgroup.arr_y1), max(lgroup.arr_y1))) if y2expr != '': ppnl.set_y2label(y2label) ppnl.update_line(1, lgroup.arr_x, lgroup.arr_y2, side='right', draw=True, update_limits=True) ppnl.set_xylims((min(lgroup.arr_x), max(lgroup.arr_x), min(lgroup.arr_y2), max(lgroup.arr_y2)), side='right') def createMenus(self): self.menubar = wx.MenuBar() # fmenu = wx.Menu() pmenu = wx.Menu() fmenu.AppendSeparator() add_menu(self, fmenu, "&Quit\tCtrl+Q", "Quit program", self.onClose) self.menubar.Append(fmenu, "&File") fmenu.AppendSeparator() add_menu(self, fmenu, "&Copy\tCtrl+C", "Copy to Clipboard", self.onClipboard) add_menu(self, fmenu, "&Save\tCtrl+S", "Save Figure", self.onSaveFig) add_menu(self, fmenu, "&Print\tCtrl+P", "Print Figure", self.onPrint) add_menu(self, fmenu, "Page Setup", "Print Page Setup", self.onPrintSetup) add_menu(self, fmenu, "Preview", "Print Preview", self.onPrintPreview) # add_menu(self, pmenu, "Configure\tCtrl+K", "Configure Plot", self.onConfigurePlot) add_menu(self, pmenu, "Unzoom\tCtrl+Z", "Unzoom Plot", self.onUnzoom) pmenu.AppendSeparator() add_menu(self, pmenu, "Toggle Legend\tCtrl+L", "Toggle Legend on Plot", self.onToggleLegend) add_menu(self, pmenu, "Toggle Grid\tCtrl+G", "Toggle Grid on Plot", self.onToggleGrid) self.menubar.Append(pmenu, "Plot Options") self.SetMenuBar(self.menubar) def onClipboard(self, evt=None): self.plotpanel.canvas.Copy_to_Clipboard(evt) def onSaveFig(self, evt=None): self.plotpanel.save_figure(event=evt, transparent=True, dpi=300) def onPrint(self, evt=None): self.plotpanel.Print(evet) def onPrintSetup(self, evt=None): self.plotpanel.PrintSetup(evt) def onPrintPreview(self, evt=None): self.plotpanel.PrintPreview(evt) def onConfigurePlot(self, evt=None): self.plotpanel.configure(evt) def onUnzoom(self, evt=None): self.plotpanel.unzoom(evt) def onToggleLegend(self, evt=None): self.plotpanel.toggle_legend(evt) def onToggleGrid(self, evt=None): self.plotpanel.toggle_grid(evt) def onAbout(self,evt): dlg = wx.MessageDialog(self, self._about,"About Epics StepScan", wx.OK | wx.ICON_INFORMATION) dlg.ShowModal() dlg.Destroy() def onClose(self,evt): for obj in self.plotters: try: obj.Destroy() except: pass for nam in dir(self.larch.symtable._sys.wx): obj = getattr(self.larch.symtable._sys.wx, nam) del obj self.Destroy()