def __init__(self, wxparent=None, window=1, _larch=None, size=None, **kws): PlotFrame.__init__(self, parent=None, size=size, output_title='plot2d', exit_callback=self.onExit, **kws) self.Show() self.Raise() self.panel.cursor_callback = self.onCursor self.panel.cursor_mode = 'zoom' self.window = int(window) self._larch = _larch self._xylims = {} self.cursor_hist = [] self.symname = '%s.plot%i' % (MODNAME, self.window) symtable = ensuremod(self._larch, MODNAME) self.panel.canvas.figure.set_facecolor('#FDFDFB') if symtable is not None: symtable.set_symbol(self.symname, self) if not hasattr(symtable, '%s.cursor_maxhistory' % MODNAME): symtable.set_symbol('%s.cursor_maxhistory' % MODNAME, MAX_CURSHIST) if window not in PLOT_DISPLAYS: PLOT_DISPLAYS[window] = self
def OnClick(self, event): p = self.dos_panel.GetParams() if p.step == 1234: awx.tetris_game() try: edos = self.bands.get_dos(step=p.step, width=p.width) except: awx.showErrorMessage(self) return tot_dos, tot_idos = edos.dos_idos() label = "$\sigma = %s, step = %s$" % (p.width, p.step) plotframe = None if self.replot_checkbox.GetValue() and len(self._pframes): plotframe = self._pframes[-1] if plotframe is None: plotframe = PlotFrame(parent=self) plotframe.plot(tot_dos.mesh, tot_dos.values, label=label, draw_legend=True) plotframe.Show() self._pframes.append(plotframe) else: plotframe.oplot(tot_dos.mesh, tot_dos.values, label=label, draw_legend=True)
def onProject(self, event=None, mode='y'): wid = event.GetId() if mode=='x': x = self.tomo_frame[0].panel.ydata y = self.tomo_frame[0].panel.conf.data.sum(axis=1) x = self.tomo_frame[1].panel.ydata y = self.tomo_frame[1].panel.conf.data.sum(axis=1) axname = 'horizontal' if x is None: x = np.arange(y.shape[0]) else: x = self.tomo_frame[0].panel.xdata y = self.tomo_frame[0].panel.conf.data.sum(axis=0) x = self.tomo_frame[1].panel.xdata y = self.tomo_frame[1].panel.conf.data.sum(axis=0) if x is None: x = np.arange(y.shape[0]) axname = 'vertical' title = '%s: sum along %s axis' % (self.GetTitle(), axname) pf = PlotFrame(title=title, parent=self, size=(500, 250)) colors = RGB_COLORS if len(y.shape) == 2 and y.shape[1] == 3: pf.plot(x, y[:,0], color=colors[0]) pf.oplot(x, y[:,1], color=colors[1]) pf.oplot(x, y[:,2], color=colors[2]) else: pf.plot(x, y) pf.Raise() pf.Show()
def raise_plotframe(self): if self.plotframe is not None: try: self.plotframe.Show() except: self.plotframe = None if self.plotframe is None: self.plotframe = PlotFrame(None, size=(650, 400)) self.plotframe.Show()
def onStartProjections(self, event=None): try: self.xplot.Raise() except: self.xplot = PlotFrame(parent=self) try: self.yplot.Raise() except: self.yplot = PlotFrame(parent=self) self.proj_start = True self.proj_timer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.onShowProjections, self.proj_timer) self.proj_timer.Start(500)
def OnCompare(self, event): """ Callback triggered by the `compare` option in the popup menu of the panels Plote the netcdf variable/dimension as function of the file index. """ # Get the name of the variable. name, obj = event.name, event.obj # Define the function to use the get the value of the dimension/variable. if isinstance(obj, netCDF4.Variable): def extract(dataset, name): return dataset.variables[name][:] elif isinstance(obj, netCDF4.Dimension): def extract(dataset, name): return np.array(len(dataset.dimensions[name])) else: raise ValueError("Don't know how to handle %s" % repr(obj)) # Extract data. Make sure all the shapes are equal. data, shape = [], None for dataset in self.datasets: v = extract(dataset, name) data.append(v) if shape is None: shape = v.shape else: assert shape == v.shape # Plot data. Two Branches for scalars and arrays. opts = self.GetPlotOptions() is_scalar = not shape or (len(shape) == 1 and shape[0] == 1) #print(shape) from wxmplot import PlotFrame if is_scalar: frame = PlotFrame(parent=self) xx = range(len(self.datasets)) if opts.plot_mode == "line": frame.plot(xx, data) else: frame.scatterplot(xx, data) frame.set_xlabel("File index") frame.set_ylabel(name) frame.Show() else: # Open new frame to allow the user to specify how to handle the array. ArrayComparisonFrame(self, name, data).Show()
def show_PlotFrame(self, do_raise=True, clear=True): "make sure plot frame is enabled, and visible" if self.plotframe is None: self.plotframe = PlotFrame(self, title='XRF Spectra') try: self.plotframe.Show() except wx.PyDeadObjectError: self.plotframe = PlotFrame(self, title='XRF Spectra') self.plotframe.Show() if do_raise: self.plotframe.Raise() if clear: self.plotframe.panel.clear() self.plotframe.reset_config()
def __init__(self, wxparent=None, window=1, _larch=None, **kws): PlotFrame.__init__(self, parent=wxparent, exit_callback=self.onExit, **kws) self.Show() self.Raise() self.cursor_pos = None self.panel.cursor_callback = self.onCursor self.window = int(window) self._larch = _larch self.symname = '%s.plot%i' % (MODNAME, self.window) symtable = ensuremod(self._larch) if symtable is not None: symtable.set_symbol(self.symname, self) if window not in PLOT_DISPLAYS: PLOT_DISPLAYS[window] = self
def OnClick(self, event): method = self.method_choice.GetStringSelection() try: from wxmplot import PlotFrame except ImportError: #warnings.warn("Error while importing wxmplot. Some features won't be available") raise plotframe = None if self.replot_checkbox.GetValue() and len(self._pframes): plotframe = self._pframes[-1] try: if method == "None": g = self.func1d else: g = getattr(self.func1d, method)() if plotframe is None: plotframe = PlotFrame(self) self._pframes.append(plotframe) plotframe.plot(g.mesh, g.values, label=method, draw_legend=True) plotframe.Show() else: plotframe.oplot(g.mesh, g.values, label=method, draw_legend=True) except: showErrorMessage(self)
def OnClick(self, event): method = self.method_choice.GetStringSelection() plotframe = None if self.replot_checkbox.GetValue() and len(self._pframes): plotframe = self._pframes[-1] try: if method == "None": g = self.func1d else: g = getattr(self.func1d, method)() if plotframe is None: plotframe = PlotFrame(self) self._pframes.append(plotframe) plotframe.plot(g.mesh, g.values, label=method, draw_legend=True) plotframe.Show() else: plotframe.oplot(g.mesh, g.values, label=method, draw_legend=True) except: showErrorMessage(self)
def OnClick(self, event): p = self.dos_panel.GetParams() if p.step == 1234: awx.tetris_game() try: edos = self.bands.get_edos(step=p.step, width=p.width) except: awx.showErrorMessage(self) return tot_dos, tot_idos = edos.dos_idos() label = "$\sigma = %s, step = %s$" % (p.width, p.step) plotframe = None if self.replot_checkbox.GetValue() and len(self._pframes): plotframe = self._pframes[-1] if plotframe is None: plotframe = PlotFrame(parent=self) plotframe.plot(tot_dos.mesh, tot_dos.values, label=label, draw_legend=True) plotframe.Show() self._pframes.append(plotframe) else: plotframe.oplot(tot_dos.mesh, tot_dos.values, label=label, draw_legend=True)
def __init__(self, wxparent=None, window=1, _larch=None, size=None, **kws): PlotFrame.__init__(self, parent=None, size=size, output_title='plot2d', exit_callback=self.onExit, **kws) self.Show() self.Raise() self.panel.cursor_callback = self.onCursor self.panel.cursor_mode = 'zoom' self.window = int(window) self._larch = _larch self._xylims = {} self.symname = '%s.plot%i' % (MODNAME, self.window) symtable = ensuremod(self._larch, MODNAME) if symtable is not None: symtable.set_symbol(self.symname, self) if window not in PLOT_DISPLAYS: PLOT_DISPLAYS[window] = self
def get_plotwindow(self, new=False, **kws): pframe = None if not new: while pframe is None: try: pframe = self.plotters.pop() pframe.Show() pframe.Raise() except IndexError: pframe = None break except PyDeadObjectError: pframe = None if pframe is None: pframe = PlotFrame() pframe.Show() pframe.Raise() self.plotters.append(pframe) return pframe
def __init__(self, wxparent=None, window=1, _larch=None, size=None, **kws): PlotFrame.__init__(self, parent=None, size=size, output_title='plot2d', exit_callback=self.onExit, **kws) self.Show() self.Raise() self.panel.cursor_callback = self.onCursor self.panel.cursor_mode = 'zoom' self.window = int(window) self._larch = _larch self._xylims = {} self.cursor_hist = [] self.symname = '%s.plot%i' % (_larch_name, self.window) symtable = ensuremod(self._larch, _larch_name) self.panel.canvas.figure.set_facecolor('#FDFDFB') if symtable is not None: symtable.set_symbol(self.symname, self) if not hasattr(symtable, '%s.cursor_maxhistory' % _larch_name): symtable.set_symbol('%s.cursor_maxhistory' % _larch_name, MAX_CURSHIST) if window not in PLOT_DISPLAYS: PLOT_DISPLAYS[window] = self
def onPlotData(self, event): operators = self.panel.GetSelectedOperators() from wxmplot import PlotFrame frame = PlotFrame(parent=self) xx = range(len(self.data)) for oname, op in operators.items(): #print(oname) values = [op(arr) for arr in self.data] frame.oplot(xx, values, label=oname) #if opts.plot_mode == "line": # frame.oplot(xx, values) #else: # frame.scatterplot(xx, values) frame.set_xlabel("File index") frame.set_ylabel(self.name) frame.Show()
def onPlotData(self, event): operators = self.panel.GetSelectedOperators() frame = PlotFrame(parent=self) xx = range(len(self.data)) for oname, op in operators.items(): #print(oname) values = [op(arr) for arr in self.data] frame.oplot(xx, values, label=oname) #if opts.plot_mode == "line": # frame.oplot(xx, values) #else: # frame.scatterplot(xx, values) frame.set_xlabel("File index") frame.set_ylabel(self.name) frame.Show()
def plot_variable(self, var_name, var, dataset): """ Use `wxmplot` to plot the selected variables. Args: var_name: Name of the variable var: Netcdf4 `Variable`. dataset: Netcdf4 `Dataset`. """ # Remove fake dimensions. shape, dimensions = [], [] for num, name in zip(var.shape, var.dimensions): if num > 1: shape.append(num) dimensions.append(name) # Get data to plot. data = np.reshape(var[:], shape) opts = self.GetPlotOptions() cplx_mode = opts.cplx_mode if cplx_mode != "None": if shape[-1] != 2: err_msg = "cplx_mode: %s. Expecting 2 as last dimensions but got %d" % ( cplx_mode, shape[-1]) raise ValueError(err_msg) # Convert to complex then change shape and dimensions data = data[..., 0] + 1j*data[..., 1] shape = shape[:-1] dimensions = dimensions[:-1] if cplx_mode == "Abs": data = np.abs(data) elif cplx_mode == "Real": data = data.real elif cplx_mode == "Imag": data = data.imag else: raise ValueError("Wrong value for cplx_mode %s" % cplx_mode) # Plotting a scalar? if not shape: return ndim = len(shape) if ndim == 1: # Vector dim_name = dimensions[0] xx = range(len(dataset.dimensions[dim_name])) frame = PlotFrame(parent=self) if opts.plot_mode == "line": frame.plot(xx, data) else: frame.scatterplot(xx, data) frame.set_xlabel(dim_name) frame.set_ylabel(var_name) frame.Show() elif ndim == 2: # Two dimensional array. dim_namex, dim_namey = dimensions xx, yy = range(len(dataset.dimensions[dim_namex])), range(len(dataset.dimensions[dim_namey])) mode = opts.image_mode if False: # 3d plot import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D fig = plt.figure() ax = Axes3D(fig) X, Y = np.meshgrid(xx, yy, sparse=False, indexing='ij') print(X.shape, Y.shape, data.shape) ax.plot_surface(X, Y, data) # rstride=8, cstride=8, alpha=0.3) plt.show() frame = ImageFrame(parent=self) frame.display(data, title=var_name, style=mode, x=xx, y=yy, xlabel=dim_namex, ylabel=dim_namey) frame.Show() else: raise NotImplementedError()
class MapImageFrame(ImageFrame): """ MatPlotlib Image Display on a wx.Frame, using ImagePanel """ def __init__(self, parent=None, size=None, mode='intensity', lasso_callback=None, move_callback=None, save_callback=None, show_xsections=False, cursor_labels=None, output_title='Image', **kws): # instdb=None, inst_name=None, self.det = None self.xrmfile = None self.map = None self.move_callback = move_callback self.save_callback = save_callback ImageFrame.__init__(self, parent=parent, size=size, lasso_callback=lasso_callback, cursor_labels=cursor_labels, mode=mode, output_title=output_title, **kws) self.panel.add_cursor_mode('prof', motion = self.prof_motion, leftdown = self.prof_leftdown, leftup = self.prof_leftup) self.panel.report_leftdown = self.report_leftdown self.panel.report_motion = self.report_motion self.prof_plotter = None self.zoom_ini = None self.lastpoint = [None, None] self.this_point = None self.rbbox = None def display(self, map, det=None, xrmfile=None, xoff=0, yoff=0, **kws): self.xoff = xoff self.yoff = yoff self.det = det self.xrmfile = xrmfile self.map = map self.title = '' if 'title' in kws: self.title = kws['title'] ImageFrame.display(self, map, **kws) if 'x' in kws: self.panel.xdata = kws['x'] if 'y' in kws: self.panel.ydata = kws['y'] if self.panel.conf.auto_contrast: self.set_contrast_levels() def prof_motion(self, event=None): if not event.inaxes or self.zoom_ini is None: return try: xmax, ymax = event.x, event.y except: return xmin, ymin, xd, yd = self.zoom_ini if event.xdata is not None: self.lastpoint[0] = event.xdata if event.ydata is not None: self.lastpoint[1] = event.ydata yoff = self.panel.canvas.figure.bbox.height ymin, ymax = yoff - ymin, yoff - ymax zdc = wx.ClientDC(self.panel.canvas) zdc.SetLogicalFunction(wx.XOR) zdc.SetBrush(wx.TRANSPARENT_BRUSH) zdc.SetPen(wx.Pen('White', 2, wx.SOLID)) zdc.ResetBoundingBox() if not is_wxPhoenix: zdc.BeginDrawing() # erase previous box if self.rbbox is not None: zdc.DrawLine(*self.rbbox) self.rbbox = (xmin, ymin, xmax, ymax) zdc.DrawLine(*self.rbbox) if not is_wxPhoenix: zdc.EndDrawing() def prof_leftdown(self, event=None): self.report_leftdown(event=event) if event.inaxes and len(self.map.shape) == 2: self.lastpoint = [None, None] self.zoom_ini = [event.x, event.y, event.xdata, event.ydata] def prof_leftup(self, event=None): if len(self.map.shape) != 2: return if self.rbbox is not None: zdc = wx.ClientDC(self.panel.canvas) zdc.SetLogicalFunction(wx.XOR) zdc.SetBrush(wx.TRANSPARENT_BRUSH) zdc.SetPen(wx.Pen('White', 2, wx.SOLID)) zdc.ResetBoundingBox() if not is_wxPhoenix: zdc.BeginDrawing() zdc.DrawLine(*self.rbbox) if not is_wxPhoenix: zdc.EndDrawing() self.rbbox = None if self.zoom_ini is None or self.lastpoint[0] is None: return x0 = int(self.zoom_ini[2]) x1 = int(self.lastpoint[0]) y0 = int(self.zoom_ini[3]) y1 = int(self.lastpoint[1]) dx, dy = abs(x1-x0), abs(y1-y0) self.lastpoint, self.zoom_ini = [None, None], None if dx < 2 and dy < 2: self.zoom_ini = None return outdat = [] if dy > dx: _y0 = min(int(y0), int(y1+0.5)) _y1 = max(int(y0), int(y1+0.5)) for iy in range(_y0, _y1): ix = int(x0 + (iy-int(y0))*(x1-x0)/(y1-y0)) outdat.append((ix, iy)) else: _x0 = min(int(x0), int(x1+0.5)) _x1 = max(int(x0), int(x1+0.5)) for ix in range(_x0, _x1): iy = int(y0 + (ix-int(x0))*(y1-y0)/(x1-x0)) outdat.append((ix, iy)) x, y, z = [], [], [] for ix, iy in outdat: x.append(ix) y.append(iy) z.append(self.panel.conf.data[iy,ix]) self.prof_dat = dy>dx, outdat if self.prof_plotter is not None: try: self.prof_plotter.Raise() self.prof_plotter.clear() except (AttributeError, PyDeadObjectError): self.prof_plotter = None if self.prof_plotter is None: self.prof_plotter = PlotFrame(self, title='Profile') self.prof_plotter.panel.report_leftdown = self.prof_report_coords xlabel, y2label = 'Pixel (x)', 'Pixel (y)' if dy > dx: x, y = y, x xlabel, y2label = y2label, xlabel self.prof_plotter.panel.clear() # reset_config() if len(self.title) < 1: self.title = os.path.split(self.xrmfile.filename)[1] opts = dict(linewidth=2, marker='+', markersize=3, show_legend=True, xlabel=xlabel) self.prof_plotter.plot(x, z, title=self.title, color='blue', zorder=20, xmin=min(x)-3, xmax=max(x)+3, ylabel='counts', label='counts', **opts) self.prof_plotter.oplot(x, y, y2label=y2label, label=y2label, zorder=3, side='right', color='#771111', **opts) self.prof_plotter.panel.unzoom_all() self.prof_plotter.Show() self.zoom_ini = None self.zoom_mode.SetSelection(0) self.panel.cursor_mode = 'zoom' def prof_report_coords(self, event=None): """override report leftdown for profile plotter""" if event is None: return ex, ey = event.x, event.y msg = '' plotpanel = self.prof_plotter.panel axes = plotpanel.fig.get_axes()[0] write = plotpanel.write_message try: x, y = axes.transData.inverted().transform((ex, ey)) except: x, y = event.xdata, event.ydata if x is None or y is None: return _point = 0, 0, 0, 0, 0 for ix, iy in self.prof_dat[1]: if (int(x) == ix and not self.prof_dat[0] or int(x) == iy and self.prof_dat[0]): _point = (ix, iy, self.panel.xdata[ix], self.panel.ydata[iy], self.panel.conf.data[iy, ix]) msg = "Pixel [%i, %i], X, Y = [%.4f, %.4f], Intensity= %g" % _point write(msg, panel=0) def onCursorMode(self, event=None, mode='zoom'): self.panel.cursor_mode = mode if event is not None: if 1 == event.GetInt(): self.panel.cursor_mode = 'lasso' elif 2 == event.GetInt(): self.panel.cursor_mode = 'prof' def report_leftdown(self, event=None): if event is None: return if event.xdata is None or event.ydata is None: return ix, iy = round(event.xdata), round(event.ydata) conf = self.panel.conf if conf.flip_ud: iy = conf.data.shape[0] - iy if conf.flip_lr: ix = conf.data.shape[1] - ix self.this_point = None msg = '' if (ix >= 0 and ix < conf.data.shape[1] and iy >= 0 and iy < conf.data.shape[0]): pos = '' pan = self.panel # print( 'has xdata? ', pan.xdata is not None, pan.ydata is not None) labs, vals = [], [] if pan.xdata is not None: labs.append(pan.xlab) vals.append(pan.xdata[ix]) if pan.ydata is not None: labs.append(pan.ylab) vals.append(pan.ydata[iy]) pos = ', '.join(labs) vals =', '.join(['%.4g' % v for v in vals]) pos = '%s = [%s]' % (pos, vals) dval = conf.data[iy, ix] if len(pan.data_shape) == 3: dval = "%.4g, %.4g, %.4g" % tuple(dval) else: dval = "%.4g" % dval if pan.xdata is not None and pan.ydata is not None: self.this_point = (ix, iy) msg = "Pixel [%i, %i], %s, Intensity=%s " % (ix, iy, pos, dval) self.panel.write_message(msg, panel=0) def report_motion(self, event=None): return def onLasso(self, data=None, selected=None, mask=None, **kws): if hasattr(self.lasso_callback , '__call__'): self.lasso_callback(data=data, selected=selected, mask=mask, xoff=self.xoff, yoff=self.yoff, det=self.det, xrmfile=self.xrmfile, **kws) self.zoom_mode.SetSelection(0) self.panel.cursor_mode = 'zoom' def CustomConfig(self, panel, sizer, irow): """config panel for left-hand-side of frame""" conf = self.panel.conf lpanel = panel lsizer = sizer labstyle = wx.ALIGN_LEFT|wx.LEFT|wx.TOP|wx.EXPAND self.zoom_mode = wx.RadioBox(panel, -1, "Cursor Mode:", wx.DefaultPosition, wx.DefaultSize, ('Zoom to Rectangle', 'Pick Area for XRF Spectrum', 'Show Line Profile'), 1, wx.RA_SPECIFY_COLS) self.zoom_mode.Bind(wx.EVT_RADIOBOX, self.onCursorMode) sizer.Add(self.zoom_mode, (irow, 0), (1, 4), labstyle, 3) if self.save_callback is not None: self.pos_name = wx.TextCtrl(panel, -1, '', size=(175, -1), style=wx.TE_PROCESS_ENTER) self.pos_name.Bind(wx.EVT_TEXT_ENTER, self.onSavePixel) label = SimpleText(panel, label='Save Position:', size=(-1, -1)) # sbutton = Button(panel, 'Save Position', size=(100, -1), # action=self.onSavePixel) sizer.Add(label, (irow+1, 0), (1, 2), labstyle, 3) sizer.Add(self.pos_name, (irow+1, 2), (1, 2), labstyle, 3) # sizer.Add(sbutton, (irow+2, 0), (1, 2), labstyle, 3) if self.move_callback is not None: mbutton = Button(panel, 'Move to Position', size=(100, -1), action=self.onMoveToPixel) irow = irow + 2 sizer.Add(mbutton, (irow+1, 0), (1, 2), labstyle, 3) def onMoveToPixel(self, event=None): if self.this_point is not None and self.move_callback is not None: p1 = float(self.panel.xdata[self.this_point[0]]) p2 = float(self.panel.ydata[self.this_point[1]]) self.move_callback(p1, p2) def onSavePixel(self, event=None): if self.this_point is not None and self.save_callback is not None: name = str(event.GetString().strip()) # name = str(self.pos_name.GetValue().strip()) ix, iy = self.this_point x = float(self.panel.xdata[ix]) y = float(self.panel.ydata[iy]) self.save_callback(name, ix, iy, x=x, y=y, title=self.title, datafile=self.xrmfile)
def prof_leftup(self, event=None): if len(self.map.shape) != 2: return if self.rbbox is not None: zdc = wx.ClientDC(self.panel.canvas) zdc.SetLogicalFunction(wx.XOR) zdc.SetBrush(wx.TRANSPARENT_BRUSH) zdc.SetPen(wx.Pen('White', 2, wx.SOLID)) zdc.ResetBoundingBox() if not is_wxPhoenix: zdc.BeginDrawing() zdc.DrawLine(*self.rbbox) if not is_wxPhoenix: zdc.EndDrawing() self.rbbox = None if self.zoom_ini is None or self.lastpoint[0] is None: return x0 = int(self.zoom_ini[2]) x1 = int(self.lastpoint[0]) y0 = int(self.zoom_ini[3]) y1 = int(self.lastpoint[1]) dx, dy = abs(x1-x0), abs(y1-y0) self.lastpoint, self.zoom_ini = [None, None], None if dx < 2 and dy < 2: self.zoom_ini = None return outdat = [] if dy > dx: _y0 = min(int(y0), int(y1+0.5)) _y1 = max(int(y0), int(y1+0.5)) for iy in range(_y0, _y1): ix = int(x0 + (iy-int(y0))*(x1-x0)/(y1-y0)) outdat.append((ix, iy)) else: _x0 = min(int(x0), int(x1+0.5)) _x1 = max(int(x0), int(x1+0.5)) for ix in range(_x0, _x1): iy = int(y0 + (ix-int(x0))*(y1-y0)/(x1-x0)) outdat.append((ix, iy)) x, y, z = [], [], [] for ix, iy in outdat: x.append(ix) y.append(iy) z.append(self.panel.conf.data[iy,ix]) self.prof_dat = dy>dx, outdat if self.prof_plotter is not None: try: self.prof_plotter.Raise() self.prof_plotter.clear() except (AttributeError, PyDeadObjectError): self.prof_plotter = None if self.prof_plotter is None: self.prof_plotter = PlotFrame(self, title='Profile') self.prof_plotter.panel.report_leftdown = self.prof_report_coords xlabel, y2label = 'Pixel (x)', 'Pixel (y)' if dy > dx: x, y = y, x xlabel, y2label = y2label, xlabel self.prof_plotter.panel.clear() # reset_config() if len(self.title) < 1: self.title = os.path.split(self.xrmfile.filename)[1] opts = dict(linewidth=2, marker='+', markersize=3, show_legend=True, xlabel=xlabel) self.prof_plotter.plot(x, z, title=self.title, color='blue', zorder=20, xmin=min(x)-3, xmax=max(x)+3, ylabel='counts', label='counts', **opts) self.prof_plotter.oplot(x, y, y2label=y2label, label=y2label, zorder=3, side='right', color='#771111', **opts) self.prof_plotter.panel.unzoom_all() self.prof_plotter.Show() self.zoom_ini = None self.zoom_mode.SetSelection(0) self.panel.cursor_mode = 'zoom'
class XRD2D_DisplayFrame(ImageFrame): """ MatPlotlib Image Display on a wx.Frame, using ImagePanel """ def __init__(self, _larch=None, parent=None, size=None, mode='intensity', move_callback=None, save_callback=None, show_xsections=False, cursor_labels=None, output_title='Image', **kws): self.xrmfile = None self.map = None self.move_callback = move_callback self.save_callback = save_callback self.larch = _larch if self.larch is None: self.init_larch() ImageFrame.__init__(self, parent=parent, size=size, cursor_labels=cursor_labels, mode=mode, output_title=output_title, **kws) self.panel.cursor_mode = 'zoom' self.panel.xaxis = 'q' self.panel.report_leftdown = self.report_leftdown self.panel.report_motion = self.report_motion self.prof_plotter = None self.zoom_ini = None self.lastpoint = [None, None] self.this_point = None self.rbbox = None def display(self, map, xrmfile=None, ai=None, mask=None, **kws): self.xrmfile = xrmfile self.map = map self.title = '' if 'title' in kws: self.title = kws['title'] ImageFrame.display(self, map, **kws) if self.panel.conf.auto_contrast: self.set_contrast_levels() self.ai = ai self.mask = mask if np.shape(self.mask) == np.shape(map): self.masked_map = map * (np.ones(np.shape(self.mask))-mask.value) self.panel.xdata = np.arange(map.shape[0]) self.panel.ydata = np.arange(map.shape[0]) def init_larch(self): if self.larch is None: self.larch = Interpreter() def prof_motion(self, event=None): if not event.inaxes or self.zoom_ini is None: return try: xmax, ymax = event.x, event.y except: return xmin, ymin, xd, yd = self.zoom_ini if event.xdata is not None: self.lastpoint[0] = event.xdata if event.ydata is not None: self.lastpoint[1] = event.ydata yoff = self.panel.canvas.figure.bbox.height ymin, ymax = yoff - ymin, yoff - ymax zdc = wx.ClientDC(self.panel.canvas) zdc.SetLogicalFunction(wx.XOR) zdc.SetBrush(wx.TRANSPARENT_BRUSH) zdc.SetPen(wx.Pen('White', 2, wx.SOLID)) zdc.ResetBoundingBox() if not is_wxPhoenix: zdc.BeginDrawing() # erase previous box if self.rbbox is not None: zdc.DrawLine(*self.rbbox) self.rbbox = (xmin, ymin, xmax, ymax) zdc.DrawLine(*self.rbbox) if not is_wxPhoenix: zdc.EndDrawing() def prof_leftdown(self, event=None): self.report_leftdown(event=event) if event.inaxes and len(self.map.shape) == 2: self.lastpoint = [None, None] self.zoom_ini = [event.x, event.y, event.xdata, event.ydata] def prof_leftup(self, event=None): if len(self.map.shape) != 2: return if self.rbbox is not None: zdc = wx.ClientDC(self.panel.canvas) zdc.SetLogicalFunction(wx.XOR) zdc.SetBrush(wx.TRANSPARENT_BRUSH) zdc.SetPen(wx.Pen('White', 2, wx.SOLID)) zdc.ResetBoundingBox() if not is_wxPhoenix: zdc.BeginDrawing() zdc.DrawLine(*self.rbbox) if not is_wxPhoenix: zdc.EndDrawing() self.rbbox = None if self.zoom_ini is None or self.lastpoint[0] is None: return x0 = int(self.zoom_ini[2]) x1 = int(self.lastpoint[0]) y0 = int(self.zoom_ini[3]) y1 = int(self.lastpoint[1]) dx, dy = abs(x1-x0), abs(y1-y0) self.lastpoint, self.zoom_ini = [None, None], None if dx < 2 and dy < 2: self.zoom_ini = None return outdat = [] if dy > dx: _y0 = min(int(y0), int(y1+0.5)) _y1 = max(int(y0), int(y1+0.5)) for iy in range(_y0, _y1): ix = int(x0 + (iy-int(y0))*(x1-x0)/(y1-y0)) outdat.append((ix, iy)) else: _x0 = min(int(x0), int(x1+0.5)) _x1 = max(int(x0), int(x1+0.5)) for ix in range(_x0, _x1): iy = int(y0 + (ix-int(x0))*(y1-y0)/(x1-x0)) outdat.append((ix, iy)) x, y, z = [], [], [] for ix, iy in outdat: x.append(ix) y.append(iy) z.append(self.panel.conf.data[iy,ix]) self.prof_dat = dy>dx, outdat if self.prof_plotter is not None: try: self.prof_plotter.Raise() self.prof_plotter.clear() except (AttributeError, PyDeadObjectError): self.prof_plotter = None if self.prof_plotter is None: self.prof_plotter = PlotFrame(self, title='Profile') self.prof_plotter.panel.report_leftdown = self.prof_report_coords xlabel, y2label = 'Pixel (x)', 'Pixel (y)' if dy > dx: x, y = y, x xlabel, y2label = y2label, xlabel self.prof_plotter.panel.clear() # reset_config() if len(self.title) < 1: self.title = os.path.split(self.xrmfile.filename)[1] opts = dict(linewidth=2, marker='+', markersize=3, show_legend=True, xlabel=xlabel) self.prof_plotter.plot(x, z, title=self.title, color='blue', zorder=20, xmin=min(x)-3, xmax=max(x)+3, ylabel='counts', label='counts', **opts) self.prof_plotter.oplot(x, y, y2label=y2label, label=y2label, zorder=3, side='right', color='#771111', **opts) self.prof_plotter.panel.unzoom_all() self.prof_plotter.Show() self.zoom_ini = None self.panel.cursor_mode = 'zoom' def prof_report_coords(self, event=None): """override report leftdown for profile plotter""" if event is None: return ex, ey = event.x, event.y msg = '' plotpanel = self.prof_plotter.panel axes = plotpanel.fig.get_axes()[0] write = plotpanel.write_message try: x, y = axes.transData.inverted().transform((ex, ey)) except: x, y = event.xdata, event.ydata if x is None or y is None: return if self.ai is None: _point = 0, 0, 0 for ix, iy in self.prof_dat[1]: if (int(x) == ix and not self.prof_dat[0] or int(x) == iy and self.prof_dat[0]): _point = (ix, iy, self.panel.conf.data[iy, ix]) msg = "Pixel [%i, %i], Intensity= %g" % _point else: ai = self.ai xcenter = ai._poni2/ai.detector.pixel2 ## units pixels ycenter = ai._poni1/ai.detector.pixel1 ## units pixels _point = 0, 0, 0, 0, 0 for ix, iy in self.prof_dat[1]: if (int(x) == ix and not self.prof_dat[0] or int(x) == iy and self.prof_dat[0]): x_pix = ix - xcenter ## units pixels y_pix = iy - ycenter ## units pixels x_m = x_pix * ai.detector.pixel2 ## units m y_m = y_pix * ai.detector.pixel1 ## units m twth = np.arctan2(math.sqrt(x_m**2 + y_m**2),ai._dist) ## radians twth = np.degrees(twth) ## units degrees eta = np.arctan2(y_m,x_m) ## units radians eta = np.degrees(eta) ## units degrees _point = (ix, iy, twth, eta, self.panel.conf.data[iy, ix]) msg = 'Pixel [%i, %i], 2TH=%.2f, ETA=%.1f, Intensity= %g' % _point write(msg, panel=0) def report_leftdown(self, event=None): if event is None: return if event.xdata is None or event.ydata is None: return ix, iy = int(round(event.xdata)), int(round(event.ydata)) conf = self.panel.conf if conf.flip_ud: iy = conf.data.shape[0] - iy if conf.flip_lr: ix = conf.data.shape[1] - ix self.this_point = None msg = '' if (ix >= 0 and ix < conf.data.shape[1] and iy >= 0 and iy < conf.data.shape[0]): pos = '' pan = self.panel labs, vals = [], [] if pan.xdata is not None: labs.append(pan.xlab) vals.append(pan.xdata[ix]) if pan.ydata is not None: labs.append(pan.ylab) vals.append(pan.ydata[iy]) dval = conf.data[iy, ix] if len(pan.data_shape) == 3: dval = "%.4g, %.4g, %.4g" % tuple(dval) else: dval = "%.4g" % dval if pan.xdata is not None and pan.ydata is not None: self.this_point = (ix, iy) if self.ai is None: msg = "Pixel [%i, %i], Intensity=%s " % (ix, iy, dval) else: ai = self.ai xcenter = ai._poni2/ai.detector.pixel2 ## units pixels ycenter = ai._poni1/ai.detector.pixel1 ## units pixels x_pix = ix - xcenter ## units pixels y_pix = iy - ycenter ## units pixels x_m = x_pix * ai.detector.pixel2 ## units m y_m = y_pix * ai.detector.pixel1 ## units m twth = np.arctan2(math.sqrt(x_m**2 + y_m**2),ai._dist) ## radians twth = np.degrees(twth) ## units degrees eta = np.arctan2(y_m,x_m) ## units radians eta = np.degrees(eta) ## units degrees msg = 'Pixel [%i, %i], 2TH=%.2f deg., ETA=%.1f deg., Intensity= %s' % (ix, iy, twth, eta, dval) self.panel.write_message(msg, panel=0) def report_motion(self, event=None): return def CustomConfig(self, panel, sizer, irow): """config panel for left-hand-side of frame""" conf = self.panel.conf lpanel = panel lsizer = sizer labstyle = wx.ALIGN_LEFT|wx.LEFT|wx.TOP|wx.EXPAND self.MskCkBx = wx.CheckBox(panel, label='Apply mask?') self.MskCkBx.Bind(wx.EVT_CHECKBOX, self.onApplyMask) sizer.Add(self.MskCkBx, (irow+1,0), (1,4), labstyle, 3) self.LoadBtn = wx.Button(panel, label='Load New Mask') self.LoadBtn.Bind(wx.EVT_BUTTON, self.onLoadMask) sizer.Add(self.LoadBtn, (irow+2,0), (1,4), labstyle, 3) self.ReCalc1D = wx.Button(panel, label='Replot 1DXRD') self.ReCalc1D.Bind(wx.EVT_BUTTON, self.onReplot1DXRD) sizer.Add(self.ReCalc1D, (irow+3,0), (1,4), labstyle, 3) def onApplyMask(self, event): ''' Applies mask to 2DXRD map mkak 2016.09.29 ''' if event.GetEventObject().GetValue(): if self.masked_map is None: print('Mask file not defined.') question = 'No mask found in map file. Would you like to load a new file now?' caption = 'Load mask file?' dlg = wx.MessageDialog(self, question, caption, wx.YES_NO | wx.ICON_QUESTION) print( 'answer:', dlg.ShowModal()) # == wx.ID_YES read = dlg.ShowModal() dlg.Destroy() if read == wx.ID_YES: self.onLoadMask() self.MskCkBx.SetValue(False) else: ImageFrame.display(self, self.masked_map) else: ImageFrame.display(self, self.map) def onLoadMask(self, evt=None): wildcards = 'pyFAI mask (*.edf)|*.edf|All files (*.*)|*.*' dlg = wx.FileDialog(self, message='Choose XRD mask file', defaultDir=os.getcwd(), wildcard=wildcards, style=wx.FD_OPEN) edffile, read = None, False if dlg.ShowModal() == wx.ID_OK: read = True edffile = dlg.GetPath().replace('\\', '/') dlg.Destroy() if read: print('Reading mask file: %s' % edffile) try: import fabio self.mask = fabio.open(edffile).data self.masked_map = self.map * (np.ones(np.shape(self.mask))-self.mask) self.MskCkBx.SetValue(True) ImageFrame.display(self, self.masked_map) except: print('File must be .edf format; user must have fabio installed.') ## Can this be called here? #readEDFfile(self,name='mask',keyword='maskfile') #add_calibration() def onReplot1DXRD(self, evt=None): print('Not yet implemented.')
class StageFrame(wx.Frame): htmllog = 'SampleStage.html' html_header = """<html><head><title>Sample Stage Log</title></head> <meta http-equiv='Pragma' content='no-cache'> <meta http-equiv='Refresh' content='300'> <body> """ def __init__(self, inifile='SampleStage.ini', size=(1600, 800), ask_workdir=True, orientation='landscape'): super(StageFrame, self).__init__(None, wx.ID_ANY, style=wx.DEFAULT_FRAME_STYLE, size=size) self.SetFont(wx.Font(10, wx.SWISS, wx.NORMAL, wx.BOLD, False)) self.read_config(configfile=inifile, get_dir=ask_workdir) self.overlay_frame = None self.last_pixel = None self.xhair_pixel = None self.create_frame(size=size, orientation=orientation) self.xplot = None self.yplot = None self.imgpanel.Start() def create_frame(self, size=(1600, 800), orientation='landscape'): "build main frame" self.statusbar = self.CreateStatusBar(2, wx.CAPTION) self.statusbar.SetStatusWidths([-4, -1]) for index in range(2): self.statusbar.SetStatusText('', index) config = self.config opts = dict(writer=self.write_framerate, leftdown_cb=self.onSelectPixel, motion_cb=self.onPixelMotion, xhair_cb=self.onShowCrosshair, center_cb=self.onMoveToCenter, autosave_file=self.autosave_file) if self.cam_type.startswith('fly2'): opts['camera_id'] = int(self.cam_fly2id) opts['output_pv'] = config['camera'].get('output_pv', None) ImagePanel, ConfPanel = ImagePanel_Fly2, ConfPanel_Fly2 elif self.cam_type.startswith('area'): opts['prefix'] = self.cam_adpref ImagePanel, ConfPanel = ImagePanel_EpicsAD, ConfPanel_EpicsAD elif self.cam_type.startswith('webcam'): opts['url'] = self.cam_weburl ImagePanel, ConfPanel = ImagePanel_URL, ConfPanel_URL self.imgpanel = ImagePanel(self, **opts) self.imgpanel.SetMinSize((285, 250)) if orientation.lower().startswith('land'): size = (1600, 800) self.cpanel = wx.CollapsiblePane(self, label='Show Controls', style=wx.CP_DEFAULT_STYLE|wx.CP_NO_TLW_RESIZE) self.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED, self.OnPaneChanged, self.cpanel) ppanel = wx.Panel(self.cpanel.GetPane()) self.pospanel = PositionPanel(ppanel, self, config=config['scandb']) self.pospanel.SetMinSize((250, 700)) self.ctrlpanel = ControlPanel(ppanel, groups=config['stage_groups'], config=config['stages'], autofocus=self.onAutoFocus) self.confpanel = ConfPanel(ppanel, image_panel=self.imgpanel, **opts) msizer = wx.GridBagSizer(2, 2) msizer.Add(self.ctrlpanel, (0, 0), (1, 1), ALL_EXP|LEFT_TOP, 1) msizer.Add(self.confpanel, (1, 0), (1, 1), ALL_EXP|LEFT_TOP, 1) msizer.Add(self.pospanel, (0, 1), (2, 1), ALL_EXP|LEFT_TOP, 2) pack(ppanel, msizer) sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.AddMany([(self.imgpanel, 5, ALL_EXP|LEFT_CEN, 0), (self.cpanel, 1, ALL_EXP|LEFT_CEN|wx.GROW, 1)]) pack(self, sizer) self.cpanel.Collapse(False) self.cpanel.SetLabel('Hide Controls') else: # portrait mode size = (900, 1500) ppanel = wx.Panel(self) self.pospanel = PositionPanel(ppanel, self, config=config['scandb']) self.pospanel.SetMinSize((250, 450)) self.ctrlpanel = ControlPanel(ppanel, groups=config['stage_groups'], config=config['stages'], autofocus=self.onAutoFocus) self.confpanel = ConfPanel(ppanel, image_panel=self.imgpanel, **opts) msizer = wx.GridBagSizer(3, 3) msizer.Add(self.ctrlpanel, (0, 0), (1, 1), ALL_EXP|LEFT_TOP, 1) msizer.Add(self.pospanel, (0, 1), (2, 1), ALL_EXP|LEFT_TOP, 2) msizer.Add(self.confpanel, (0, 2), (1, 1), ALL_EXP|LEFT_TOP, 1) pack(ppanel, msizer) sizer = wx.BoxSizer(wx.VERTICAL) sizer.AddMany([(self.imgpanel, 5, ALL_EXP|LEFT_CEN, 0), (ppanel, 1, ALL_EXP|LEFT_CEN|wx.GROW, 1)]) pack(self, sizer) self.imgpanel.confpanel = self.confpanel self.SetSize(size) if len(self.iconfile) > 0: self.SetIcon(wx.Icon(self.iconfile, wx.BITMAP_TYPE_ICO)) ex = [{'shape':'circle', 'color': (255, 0, 0), 'width': 1.5, 'args': (0.5, 0.5, 0.007)}, {'shape':'line', 'color': (200, 100, 0), 'width': 2.0, 'args': (0.7, 0.97, 0.97, 0.97)}] self.create_menus() self.Bind(wx.EVT_CLOSE, self.onClose) self.init_timer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.onInitTimer, self.init_timer) self.init_timer.Start(1000) def OnPaneChanged(self, evt=None): self.Layout() if self.cpanel.IsExpanded(): self.cpanel.SetLabel('Hide Controls') else: self.cpanel.SetLabel('Show Controls') self.imgpanel.Refresh() def onInitTimer(self, event=None, **kws): if self.imgpanel.full_size is not None: if 'overlays' in self.config: olays = self.config['overlays'] sbar = [float(x) for x in olays['scalebar'].split()] circ = [float(x) for x in olays['circle'].split()] img_x, img_y = self.imgpanel.full_size pix_x = float(self.config['camera']['calib_x']) iscale = 0.5/abs(pix_x * img_x) ssiz, sx, sy, swid, scolr, scolg, scolb = sbar csiz, cx, cy, cwid, ccolr, ccolg, ccolb = circ cargs = [cx, cy, csiz*iscale] sargs = [sx - ssiz*iscale, sy, sx + ssiz*iscale, sy] scol = wx.Colour(int(scolr), int(scolg), int(scolb)) ccol = wx.Colour(int(ccolr), int(ccolg), int(ccolb)) dobjs = [dict(shape='Line', width=swid, style=wx.SOLID, color=scol, args=sargs), dict(shape='Circle', width=cwid, style=wx.SOLID, color=ccol, args=cargs)] if self.xhair_pixel is not None: xwid, xcolr, xcolg, xcolb = swid, scolr, scolg, scolb xcol = wx.Colour(int(xcolr), int(xcolg), int(xcolb)) xcol = wx.Colour(int(20), int(300), int(250)) hx = self.xhair_pixel['x'] hy = self.xhair_pixel['y'] xargs = [hx - ssiz*iscale, hy - ssiz*iscale, hx + ssiz*iscale, hy + ssiz*iscale] dobjs.append(dict(shape='Line', width=2, style=wx.SOLID, color=xcol, args=xargs)) #print "Showing xhair: ", xargs # print 'Draw Objects ', dobjs self.imgpanel.draw_objects = dobjs self.init_timer.Stop() def onChangeCamera(self, evt=None): if not self.cam_type.startswith('area'): print 'How did that happen?' return name = self.cam_adpref prefix = None dlg = wx.TextEntryDialog(self, 'Enter PV for Area Detector', caption='Enter PV for Area Detector', defaultValue=name) dlg.Raise() if dlg.ShowModal() == wx.ID_OK: prefix = dlg.GetValue() dlg.Destroy() if prefix is not None: self.imgpanel.set_prefix(prefix) self.confpanel.set_prefix(prefix) self.cam_adpref = prefix def create_menus(self): "Create the menubar" mbar = wx.MenuBar() fmenu = wx.Menu() pmenu = wx.Menu() omenu = wx.Menu() add_menu(self, fmenu, label="&Read Config", text="Read Configuration", action = self.onReadConfig) add_menu(self, fmenu, label="&Save Config", text="Save Configuration", action = self.onSaveConfig) add_menu(self, fmenu, label="Show Projections\tCtrl+G", text="Start Projection Plots", action = self.onStartProjections) add_menu(self, fmenu, label="Print Blurriness\tCtrl+B", text="print blurriness", action = self.onReportBlurry) add_menu(self, fmenu, label="Stop Projection\tCtrl+C", text="Stop Projection Plots", action = self.onStopProjections) add_menu(self, fmenu, label="Select &Working Directory\tCtrl+W", text="change Working Folder", action = self.onChangeWorkdir) if self.cam_type.startswith('area'): add_menu(self, fmenu, label="Change AreaDetector", text="Change Camera to different AreaDetector", action = self.onChangeCamera) fmenu.AppendSeparator() add_menu(self, fmenu, label="E&xit\tCtrl+x", text="Quit Program", action = self.onClose) add_menu(self, pmenu, label="Export Positions", text="Export Positions", action = self.onExportPositions) add_menu(self, pmenu, label="Import Positions", text="Import Positions", action = self.onImportPositions) add_menu(self, pmenu, label="Erase Many Positions\tCtrl+E", text="Select Multiple Positions to Erase", action = self.onEraseMany) add_menu(self, omenu, label="Image Overlays", text="Setup Image Overlays", action = self.onConfigOverlays) vmove = wx.NewId() verase = wx.NewId() vreplace = wx.NewId() cenfine = wx.NewId() self.menu_opts = {vmove: 'v_move', verase: 'v_erase', vreplace: 'v_replace', cenfine: 'center_with_fine_stages'} mitem = omenu.Append(vmove, "Verify Go To ", "Prompt to Verify Moving with 'Go To'", wx.ITEM_CHECK) mitem.Check() self.Bind(wx.EVT_MENU, self.onMenuOption, mitem) mitem = omenu.Append(verase, "Verify Erase", "Prompt to Verify Erasing Positions", wx.ITEM_CHECK) mitem.Check() self.Bind(wx.EVT_MENU, self.onMenuOption, mitem) mitem = omenu.Append(vreplace, "Verify Overwrite", "Prompt to Verify Overwriting Positions", wx.ITEM_CHECK) mitem.Check() self.Bind(wx.EVT_MENU, self.onMenuOption, mitem) mitem = omenu.Append(cenfine, "Center With Fine Stages", "Bring to Center will move the Fine Stages", wx.ITEM_CHECK) mitem.Check(0) self.Bind(wx.EVT_MENU, self.onMenuOption, mitem) omenu.AppendSeparator() # print 'Create Menus ', self.ctrlpanel.subpanels # for key, val in self.config['stages'].items(): # print key, val for name, panel in self.ctrlpanel.subpanels.items(): show = 0 label = 'Enable %s' % name mid = wx.NewId() self.menu_opts[mid] = label for mname, data in self.config['stages'].items(): if data['group'] == name: show = show + data['show'] mitem = omenu.Append(mid, label, label, wx.ITEM_CHECK) if show > 0 : mitem.Check() self.Bind(wx.EVT_MENU, Closure(self.onShowHide, name=name, panel=panel), mitem) mbar.Append(fmenu, '&File') mbar.Append(omenu, '&Options') mbar.Append(pmenu, 'Positions') if len(self.config['scandb'].get('offline', '')): cmenu = wx.Menu() # add_menu(self, cmenu, label="Calibrate Microscope", # text="Calibrate to Offline Microscope", # action = self.pospanel.onMicroscopeCalibrate) add_menu(self, cmenu, label="Copy Positions from Offline Microscope", text="Copy Positions from Offline Microscope", action = self.pospanel.onMicroscopeTransfer) mbar.Append(cmenu, 'Offline Microscope') self.SetMenuBar(mbar) def onShowHide(self, event=None, panel=None, name='---'): showval = {True:1, False:0}[event.Checked()] if showval: panel.Enable() else: panel.Disable() for mname, data in self.config['stages'].items(): if data['group'] == name: data['show'] = showval def onEraseMany(self, evt=None, **kws): self.pospanel.onEraseMany(event=evt) evt.Skip() def onConfigOverlays(self, evt=None, **kws): shown = False if self.overlay_frame is not None: try: self.overlay_frame.Raise() shown = True except: del self.overlay_frame if not shown: self.overlayframe = OverlayFrame(image_panel=self.imgpanel, config=self.config) def onMenuOption(self, evt=None): """events for options menu: move, erase, overwrite """ setattr(self, self.menu_opts[evt.GetId()], evt.Checked()) def read_config(self, configfile=None, get_dir=False): "open/read ini config file" if get_dir: ret = SelectWorkdir(self) if ret is None: self.Destroy() os.chdir(ret) self.cnf = StageConfig(configfile) self.config = self.cnf.config gui = self.config['gui'] self.workdir_file = gui.get('workdir_file', 'sampleviewer_workdir.txt') self.iconfile = gui.get('icon_file', '') self.autosave_file = gui.get('autosave_file', 'SampleStage_autosave.ini') self.v_move = gui.get('verify_move', True) self.v_erase = gui.get('verify_erase', True) self.v_replace = gui.get('verify_overwrite', True) self.center_with_fine_stages = gui.get('center_with_fine_stages', False) self.SetTitle(gui.get('title', 'Microscope')) cam = self.config['camera'] self.imgdir = cam.get('image_folder', 'Sample_Images') self.cam_type = cam.get('type', 'fly2').lower() self.cam_fly2id = cam.get('fly2_id', 0) self.cam_adpref = cam.get('ad_prefix', '') self.cam_adform = cam.get('ad_format', 'JPEG') self.cam_weburl = cam.get('web_url', 'http://164.54.160.115/jpg/2/image.jpg') self.get_cam_calib() try: pref = self.imgdir.split('_')[0] except: pref = 'Sample' self.htmllog = '%sStage.html' % pref if not os.path.exists(self.imgdir): os.makedirs(self.imgdir) if not os.path.exists(self.htmllog): self.begin_htmllog() self.config = self.cnf.config self.stages = OrderedDict() for mname, data in self.config.get('stages', {}).items(): mot = Motor(name=mname) if data['prec'] is None: data['prec'] = mot.precision if data['desc'] is None: data['desc'] = mot.description if data['maxstep'] is None: data['maxstep'] = (mot.high_limit - mot.low_limit)/2.10 self.stages[mname] = data def get_cam_calib(self): cam = self.config['camera'] cx = self.cam_calibx = float(cam.get('calib_x', 0.001)) cy = self.cam_caliby = float(cam.get('calib_y', 0.001)) return cx, cy def begin_htmllog(self): "initialize log file" fout = open(self.htmllog, 'w') fout.write(self.html_header) fout.close() def save_image(self, fname): "save image to file" imgdata = self.imgpanel.SaveImage(fname) if imgdata is None: self.write_message('could not save image to %s' % fname) else: self.write_message('saved image to %s' % fname) return imgdata def autosave(self, positions=None): self.cnf.Save(self.autosave_file, positions=positions) def write_htmllog(self, name, thispos): stages = self.config['stages'] img_folder = self.config['camera']['image_folder'] junk, img_file = os.path.split(thispos['image']) imgfile = os.path.join(img_folder, img_file) txt = [] html_fmt ="""<hr> <table><tr><td><a href='%s'> <img src='%s' width=350></a></td> <td><table><tr><td>Position:</td><td>%s</td><td>%s</td></tr> <tr><td>Motor Name</td><td>PV Name</td><td>Value</td></tr> %s </table></td></tr></table>""" pos_fmt =" <tr><td> %s </td><td> %s </td><td> %f</td></tr>" for pvname, value in thispos['position'].items(): txt.append(pos_fmt % (stages[pvname]['desc'], pvname, value)) fout = open(self.htmllog, 'a') fout.write(html_fmt % (imgfile, imgfile, name, thispos['timestamp'], '\n'.join(txt))) fout.close() def write_message(self, msg='', index=0): "write to status bar" self.statusbar.SetStatusText(msg, index) def write_framerate(self, msg): "write to status bar" self.statusbar.SetStatusText(msg, 1) def onShowCrosshair(self, event=None, show=True, **kws): self.xhair_pixel = None if show: self.xhair_pixel = self.last_pixel print "Set XHAIR ", self.xhair_pixel def onAFTimer(self, event=None, **kws): if self.af_done: self.af_thread.join() self.af_timer.Stop() if self.ctrlpanel.af_message is not None: self.ctrlpanel.af_message.SetLabel('') self.ctrlpanel.af_button.Enable() def onAutoFocus(self, event=None, **kws): self.af_done = False self.ctrlpanel.af_button.Disable() self.af_thread = Thread(target=self.do_autofocus) self.af_timer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.onAFTimer, self.af_timer) self.af_timer.Start(2000) self.af_thread.start() def do_autofocus(self): report = None if self.ctrlpanel.af_message is not None: report = self.ctrlpanel.af_message.SetLabel if report is not None: report('Auto-setting exposure') self.imgpanel.AutoSetExposureTime() report('Auto-focussing start') zstage = self.ctrlpanel.motors['z']._pvs['VAL'] start_pos = zstage.get() min_pos = start_pos - 2.50 max_pos = start_pos + 2.50 step, min_step = 0.004*(81), 0.002 # start trying both directions: score_start = image_blurriness(self.imgpanel) zstage.put(start_pos+step/2.0, wait=True) time.sleep(0.15) score_plus = image_blurriness(self.imgpanel) direction = 1 if score_start < score_plus: direction = -1 print("Start Scores: ", score_start, score_plus, direction) best_score = score_start best_pos = start_pos zstage.put(start_pos, wait=True) count = 0 report('Auto-focussing finding focus') posvals = [] scores = [] while step >= min_step and count < 32: self.imgpanel.Refresh() count += 1 pos = zstage.get() + step * direction if pos < min_pos or pos > max_pos: break zstage.put(pos, wait=True, timeout=3.0) time.sleep(0.15) score = image_blurriness(self.imgpanel) # print(" step : ", step, direction, score_plus) report('Auto-focussing step=%.3f' % step) # print 'Focus %i: %.3f %.3f %.3f %.3f %.3f %.1f' % ( # count, pos, score, best_pos, best_score, step, direction) posvals.append(pos) scores.append(score) if score < best_score: best_score = score best_pos = pos else: # best_score = score step = step / 3.0 if step < min_step: break direction = -direction zstage.put(best_pos, wait=True, timeout=3.0) time.sleep(0.15) last_score = score zstage.put(best_pos) self.af_done = True report('Auto-focussing done.') def onMoveToCenter(self, event=None, **kws): "bring last pixel to image center" p = self.last_pixel if p is None: return cal_x, cal_y = self.get_cam_calib() dx = 0.001*cal_x*(p['x']-p['xmax']/2.0) dy = 0.001*cal_y*(p['y']-p['ymax']/2.0) mots = self.ctrlpanel.motors xmotor, ymotor = 'x', 'y' if self.center_with_fine_stages and 'finex' in mots: xmotor, ymotor = 'finex', 'finey' xscale, yscale = 1.0, 1.0 for stage_info in self.stages.values(): if stage_info['desc'].lower() == xmotor: xscale = stage_info['scale'] if stage_info['desc'].lower() == ymotor: yscale = stage_info['scale'] mots[xmotor].VAL += dx*xscale mots[ymotor].VAL += dy*yscale self.onSelectPixel(p['xmax']/2.0, p['ymax']/2.0, xmax=p['xmax'], ymax=p['ymax']) def onSelectPixel(self, x, y, xmax=100, ymax=100): " select a pixel from image " self.last_pixel = dict(x=x, y=y, xmax=xmax, ymax=ymax) cal_x, cal_y = self.get_cam_calib() self.confpanel.on_selected_pixel(x, y, xmax, ymax, cam_calibx=cal_x, cam_caliby=cal_y) def onPixelMotion(self, x, y, xmax=100, ymax=100): " select a pixel from image " fmt = """Pixel=(%i, %i) (%.1f, %.1f)um from center, (%.1f, %.1f)um from selected""" if x > 0 and x < xmax and y > 0 and y < ymax: dx = abs(self.cam_calibx*(x-xmax/2.0)) dy = abs(self.cam_caliby*(y-ymax/2.0)) ux, uy = 0, 0 if self.last_pixel is not None: lastx = self.last_pixel['x'] lasty = self.last_pixel['y'] ux = abs(self.cam_calibx*(x-lastx)) uy = abs(self.cam_caliby*(y-lasty)) pix_msg = fmt % (x, y, dx, dy, ux, uy) self.write_message(pix_msg) if not self.confpanel.img_size_shown: self.confpanel.img_size.SetLabel("(%i, %i)" % (xmax, ymax)) self.confpanel.img_size_shown = True def onClose(self, event=None): if wx.ID_YES == popup(self, "Really Quit?", "Exit Sample Stage?", style=wx.YES_NO|wx.NO_DEFAULT|wx.ICON_QUESTION): fout = open(self.workdir_file, 'w') fout.write("%s\n" % os.path.abspath(os.curdir)) fout.close() self.imgpanel.Stop() try: self.overlay_frame.Destroy() except: pass self.Destroy() def onExportPositions(self, event=None): fname = FileSave(self, 'Export Positions File', wildcard='Position Files (*.pos)|*.pos|All files (*.*)|*.*', default_file='Save.pos') if fname is not None: self.pospanel.SavePositions(fname) self.write_message('Saved Positions File %s' % fname) def onImportPositions(self, event=None): fname = FileOpen(self, 'Import Positions File', wildcard='Position Files (*.pos)|*.pos|All files (*.*)|*.*', default_file='Save.pos') if fname is not None: self.pospanel.LoadPositions(fname) self.write_message('Loaded Positions from File %s' % fname) def onChangeWorkdir(self, event=None): ret = SelectWorkdir(self) if ret is None: return os.chdir(ret) cam = self.config['camera'] self.imgdir = cam.get('image_folder', 'Sample_Images') if not os.path.exists(self.imgdir): os.makedirs(self.imgdir) if not os.path.exists(self.htmllog): self.begin_htmllog() def onReportBlurry(self, event=None): score = image_blurriness(self.imgpanel, full=True) tscore = -(5*score[0] - score[1]) print(" blurriness: %.3f %.3f -> %.3f " % (score[0], score[1], tscore)) def onStartProjections(self, event=None): try: self.xplot.Raise() except: self.xplot = PlotFrame(parent=self) try: self.yplot.Raise() except: self.yplot = PlotFrame(parent=self) self.proj_start = True self.proj_timer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.onShowProjections, self.proj_timer) self.proj_timer.Start(500) def onStopProjections(self, event=None): self.proj_timer.Stop() def onShowProjections(self, event=None): dat = self.imgpanel.grab_data() shape = dat.shape if len(shape) == 3: dat = dat.sum(axis=2) cx, cy = self.get_cam_calib() kws = dict(ylabel='intensity', xlabel='distance ($\mu\mathrm{m}$)', marker='+', markersize=4) _y = (np.arange(shape[0]) - shape[0]/2.0) *abs(cx) _x = (np.arange(shape[1]) - shape[1]/2.0) *abs(cy) _xi = dat.sum(axis=0) _yi = dat.sum(axis=1) ymin = min((min(_xi), min(_yi))) ymax = max((max(_xi), max(_yi))) if self.proj_start: self.xplot.plot(_x, _xi, title='X projection', **kws) self.yplot.plot(_y, _yi, title='Y projection', **kws) self.xplot.Show() self.yplot.Show() self.proj_start = False else: self.xplot.panel.update_line(0, _x, _xi, draw=True, update_limits=True) self.yplot.panel.update_line(0, _y, _yi, draw=True, update_limits=True) self.xplot.panel.axes.set_ylim((ymin, ymax), emit=True) self.yplot.panel.axes.set_ylim((ymin, ymax), emit=True) # print 'X lims: ', self.xplot.panel.conf.zoom_lims, xlims # self.xplot.panel.set_xylims(xlims) # self.yplot.panel.set_xylims(ylims) def onSaveConfig(self, event=None): fname = FileSave(self, 'Save Configuration File', wildcard='INI (*.ini)|*.ini|All files (*.*)|*.*', default_file='SampleStage.ini') if fname is not None: self.cnf.Save(fname) self.write_message('Saved Configuration File %s' % fname) def onReadConfig(self, event=None): fname = FileOpen(self, 'Read Configuration File', wildcard='INI (*.ini)|*.ini|All files (*.*)|*.*', default_file='SampleStage.ini') if fname is not None: self.read_config(fname) self.connect_motors() self.pospanel.set_positions(self.config['positions']) self.write_message('Read Configuration File %s' % fname)
def plot_variable(self, var_name, var, dataset): """ Use `wxmplot` to plot the selected variables. Args: var_name: Name of the variable var: Netcdf4 `Variable`. dataset: Netcdf4 `Dataset`. """ # Remove fake dimensions. shape, dimensions = [], [] for num, name in zip(var.shape, var.dimensions): if num > 1: shape.append(num) dimensions.append(name) # Get data to plot. data = np.reshape(var[:], shape) opts = self.GetPlotOptions() cplx_mode = opts.cplx_mode if cplx_mode != "None": if shape[-1] != 2: err_msg = "cplx_mode: %s. Expecting 2 as last dimensions but got %d" % ( cplx_mode, shape[-1]) raise ValueError(err_msg) # Convert to complex then change shape and dimensions data = data[..., 0] + 1j * data[..., 1] shape = shape[:-1] dimensions = dimensions[:-1] if cplx_mode == "Abs": data = np.abs(data) elif cplx_mode == "Real": data = data.real elif cplx_mode == "Imag": data = data.imag else: raise ValueError("Wrong value for cplx_mode %s" % cplx_mode) # Plotting a scalar? if not shape: return ndim = len(shape) if ndim == 1: # Vector dim_name = dimensions[0] xx = range(len(dataset.dimensions[dim_name])) from wxmplot import PlotFrame frame = PlotFrame(parent=self) if opts.plot_mode == "line": frame.plot(xx, data) else: frame.scatterplot(xx, data) frame.set_xlabel(dim_name) frame.set_ylabel(var_name) frame.Show() elif ndim == 2: # Two dimensional array. dim_namex, dim_namey = dimensions xx, yy = range(len(dataset.dimensions[dim_namex])), range( len(dataset.dimensions[dim_namey])) mode = opts.image_mode if False: # 3d plot import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D fig = plt.figure() ax = Axes3D(fig) X, Y = np.meshgrid(xx, yy, sparse=False, indexing='ij') print(X.shape, Y.shape, data.shape) ax.plot_surface(X, Y, data) # rstride=8, cstride=8, alpha=0.3) plt.show() from wxmplot import ImageFrame frame = ImageFrame(parent=self) frame.display(data, title=var_name, style=mode, x=xx, y=yy, xlabel=dim_namex, ylabel=dim_namey) frame.Show() else: raise NotImplementedError()
class StageFrame(wx.Frame): htmllog = 'SampleStage.html' html_header = """<html><head><title>Sample Stage Log</title></head> <meta http-equiv='Pragma' content='no-cache'> <meta http-equiv='Refresh' content='300'> <body> """ def __init__(self, inifile='SampleStage.ini', size=(1600, 800), ask_workdir=True, orientation='landscape'): super(StageFrame, self).__init__(None, wx.ID_ANY, style=wx.DEFAULT_FRAME_STYLE, size=size) self.SetFont(wx.Font(10, wx.SWISS, wx.NORMAL, wx.BOLD, False)) self.read_config(configfile=inifile, get_dir=ask_workdir) self.overlay_frame = None self.last_pixel = None self.xhair_pixel = None self.create_frame(size=size, orientation=orientation) self.xplot = None self.yplot = None self.imgpanel.Start() def create_frame(self, size=(1600, 800), orientation='landscape'): "build main frame" self.statusbar = self.CreateStatusBar(2, wx.CAPTION) self.statusbar.SetStatusWidths([-4, -1]) for index in range(2): self.statusbar.SetStatusText('', index) config = self.config opts = dict(writer=self.write_framerate, leftdown_cb=self.onSelectPixel, motion_cb=self.onPixelMotion, xhair_cb=self.onShowCrosshair, center_cb=self.onMoveToCenter, autosave_file=self.autosave_file) autofocus_cb = self.onAutoFocus if self.cam_type.startswith('fly2'): opts['camera_id'] = int(self.cam_fly2id) opts['output_pv'] = config['camera'].get('output_pv', None) ImagePanel, ConfPanel = ImagePanel_Fly2, ConfPanel_Fly2 elif self.cam_type.startswith('adfly'): opts['prefix'] = self.cam_adpref ImagePanel, ConfPanel = ImagePanel_Fly2AD, ConfPanel_Fly2AD autofocus_cb = None elif self.cam_type.startswith('area'): opts['prefix'] = self.cam_adpref ImagePanel, ConfPanel = ImagePanel_EpicsAD, ConfPanel_EpicsAD elif self.cam_type.startswith('webcam'): opts['url'] = self.cam_weburl ImagePanel, ConfPanel = ImagePanel_URL, ConfPanel_URL self.imgpanel = ImagePanel(self, **opts) self.imgpanel.SetMinSize((285, 250)) if orientation.lower().startswith('land'): size = (1600, 800) self.cpanel = wx.CollapsiblePane(self, label='Show Controls', style=wx.CP_DEFAULT_STYLE | wx.CP_NO_TLW_RESIZE) self.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED, self.OnPaneChanged, self.cpanel) ppanel = wx.Panel(self.cpanel.GetPane()) self.pospanel = PositionPanel(ppanel, self, config=config['scandb']) self.pospanel.SetMinSize((250, 700)) self.ctrlpanel = ControlPanel(ppanel, groups=config['stage_groups'], config=config['stages'], autofocus=autofocus_cb) self.confpanel = ConfPanel(ppanel, image_panel=self.imgpanel, **opts) msizer = wx.GridBagSizer(2, 2) msizer.Add(self.ctrlpanel, (0, 0), (1, 1), ALL_EXP | LEFT_TOP, 1) msizer.Add(self.confpanel, (1, 0), (1, 1), ALL_EXP | LEFT_TOP, 1) msizer.Add(self.pospanel, (0, 1), (2, 1), ALL_EXP | LEFT_TOP, 2) pack(ppanel, msizer) sizer = wx.BoxSizer(wx.HORIZONTAL) sizer.AddMany([(self.imgpanel, 5, ALL_EXP | LEFT_CEN, 0), (self.cpanel, 1, ALL_EXP | LEFT_CEN | wx.GROW, 1)]) pack(self, sizer) self.cpanel.Collapse(False) self.cpanel.SetLabel('Hide Controls') else: # portrait mode size = (900, 1500) ppanel = wx.Panel(self) self.pospanel = PositionPanel(ppanel, self, config=config['scandb']) self.pospanel.SetMinSize((250, 450)) self.ctrlpanel = ControlPanel(ppanel, groups=config['stage_groups'], config=config['stages'], autofocus=autofocus_cb) self.confpanel = ConfPanel(ppanel, image_panel=self.imgpanel, **opts) msizer = wx.GridBagSizer(3, 3) msizer.Add(self.ctrlpanel, (0, 0), (1, 1), ALL_EXP | LEFT_TOP, 1) msizer.Add(self.pospanel, (0, 1), (2, 1), ALL_EXP | LEFT_TOP, 2) msizer.Add(self.confpanel, (0, 2), (1, 1), ALL_EXP | LEFT_TOP, 1) pack(ppanel, msizer) sizer = wx.BoxSizer(wx.VERTICAL) sizer.AddMany([(self.imgpanel, 5, ALL_EXP | LEFT_CEN, 0), (ppanel, 1, ALL_EXP | LEFT_CEN | wx.GROW, 1)]) pack(self, sizer) self.imgpanel.confpanel = self.confpanel self.SetSize(size) if len(self.iconfile) > 0: self.SetIcon(wx.Icon(self.iconfile, wx.BITMAP_TYPE_ICO)) ex = [{ 'shape': 'circle', 'color': (255, 0, 0), 'width': 1.5, 'args': (0.5, 0.5, 0.007) }, { 'shape': 'line', 'color': (200, 100, 0), 'width': 2.0, 'args': (0.7, 0.97, 0.97, 0.97) }] self.create_menus() self.Bind(wx.EVT_CLOSE, self.onClose) self.init_timer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.onInitTimer, self.init_timer) self.init_timer.Start(1000) def OnPaneChanged(self, evt=None): self.Layout() if self.cpanel.IsExpanded(): self.cpanel.SetLabel('Hide Controls') else: self.cpanel.SetLabel('Show Controls') self.imgpanel.Refresh() def onInitTimer(self, event=None, **kws): if self.imgpanel.full_size is not None: if 'overlays' in self.config: olays = self.config['overlays'] sbar = [float(x) for x in olays['scalebar'].split()] circ = [float(x) for x in olays['circle'].split()] img_x, img_y = self.imgpanel.full_size pix_x = float(self.config['camera']['calib_x']) iscale = 0.5 / abs(pix_x * img_x) ssiz, sx, sy, swid, scolr, scolg, scolb = sbar csiz, cx, cy, cwid, ccolr, ccolg, ccolb = circ cargs = [cx, cy, csiz * iscale] sargs = [sx - ssiz * iscale, sy, sx + ssiz * iscale, sy] scol = wx.Colour(int(scolr), int(scolg), int(scolb)) ccol = wx.Colour(int(ccolr), int(ccolg), int(ccolb)) dobjs = [ dict(shape='Line', width=swid, style=wx.SOLID, color=scol, args=sargs), dict(shape='Circle', width=cwid, style=wx.SOLID, color=ccol, args=cargs) ] if self.xhair_pixel is not None: xwid, xcolr, xcolg, xcolb = swid, scolr, scolg, scolb xcol = wx.Colour(int(xcolr), int(xcolg), int(xcolb)) xcol = wx.Colour(int(20), int(300), int(250)) hx = self.xhair_pixel['x'] hy = self.xhair_pixel['y'] xargs = [ hx - ssiz * iscale, hy - ssiz * iscale, hx + ssiz * iscale, hy + ssiz * iscale ] dobjs.append( dict(shape='Line', width=2, style=wx.SOLID, color=xcol, args=xargs)) #print( "Showing xhair: ", xargs) # print('Draw Objects ', dobjs) self.imgpanel.draw_objects = dobjs self.init_timer.Stop() def onChangeCamera(self, evt=None): if not self.cam_type.startswith('area'): print('How did that happen?') return name = self.cam_adpref prefix = None dlg = wx.TextEntryDialog(self, 'Enter PV for Area Detector', caption='Enter PV for Area Detector', defaultValue=name) dlg.Raise() if dlg.ShowModal() == wx.ID_OK: prefix = dlg.GetValue() dlg.Destroy() if prefix is not None: self.imgpanel.set_prefix(prefix) self.confpanel.set_prefix(prefix) self.cam_adpref = prefix def create_menus(self): "Create the menubar" mbar = wx.MenuBar() fmenu = wx.Menu() pmenu = wx.Menu() omenu = wx.Menu() add_menu(self, fmenu, label="&Read Config", text="Read Configuration", action=self.onReadConfig) add_menu(self, fmenu, label="&Save Config", text="Save Configuration", action=self.onSaveConfig) add_menu(self, fmenu, label="Show Projections\tCtrl+G", text="Start Projection Plots", action=self.onStartProjections) add_menu(self, fmenu, label="Print Blurriness\tCtrl+B", text="print blurriness", action=self.onReportBlurry) add_menu(self, fmenu, label="Stop Projection\tCtrl+C", text="Stop Projection Plots", action=self.onStopProjections) add_menu(self, fmenu, label="Select &Working Directory\tCtrl+W", text="change Working Folder", action=self.onChangeWorkdir) if self.cam_type.startswith('area'): add_menu(self, fmenu, label="Change AreaDetector", text="Change Camera to different AreaDetector", action=self.onChangeCamera) fmenu.AppendSeparator() add_menu(self, fmenu, label="E&xit\tCtrl+x", text="Quit Program", action=self.onClose) add_menu(self, pmenu, label="Export Positions", text="Export Positions", action=self.onExportPositions) add_menu(self, pmenu, label="Import Positions", text="Import Positions", action=self.onImportPositions) add_menu(self, pmenu, label="Erase Many Positions\tCtrl+E", text="Select Multiple Positions to Erase", action=self.onEraseMany) add_menu(self, omenu, label="Image Overlays", text="Setup Image Overlays", action=self.onConfigOverlays) vmove = wx.NewId() verase = wx.NewId() vreplace = wx.NewId() cenfine = wx.NewId() self.menu_opts = { vmove: 'v_move', verase: 'v_erase', vreplace: 'v_replace', cenfine: 'center_with_fine_stages' } mitem = omenu.Append(vmove, "Verify Go To ", "Prompt to Verify Moving with 'Go To'", wx.ITEM_CHECK) mitem.Check() self.Bind(wx.EVT_MENU, self.onMenuOption, mitem) mitem = omenu.Append(verase, "Verify Erase", "Prompt to Verify Erasing Positions", wx.ITEM_CHECK) mitem.Check() self.Bind(wx.EVT_MENU, self.onMenuOption, mitem) mitem = omenu.Append(vreplace, "Verify Overwrite", "Prompt to Verify Overwriting Positions", wx.ITEM_CHECK) mitem.Check() self.Bind(wx.EVT_MENU, self.onMenuOption, mitem) mitem = omenu.Append(cenfine, "Center With Fine Stages", "Bring to Center will move the Fine Stages", wx.ITEM_CHECK) mitem.Check(0) self.Bind(wx.EVT_MENU, self.onMenuOption, mitem) omenu.AppendSeparator() # print( 'Create Menus ', self.ctrlpanel.subpanels) # for key, val in self.config['stages'].items(): # print( key, val) for name, panel in self.ctrlpanel.subpanels.items(): show = 0 label = 'Enable %s' % name mid = wx.NewId() self.menu_opts[mid] = label for mname, data in self.config['stages'].items(): if data['group'] == name: show = show + data['show'] mitem = omenu.Append(mid, label, label, wx.ITEM_CHECK) if show > 0: mitem.Check() self.Bind(wx.EVT_MENU, Closure(self.onShowHide, name=name, panel=panel), mitem) mbar.Append(fmenu, '&File') mbar.Append(omenu, '&Options') mbar.Append(pmenu, 'Positions') if len(self.config['scandb'].get('offline', '')): cmenu = wx.Menu() # add_menu(self, cmenu, label="Calibrate Microscope", # text="Calibrate to Offline Microscope", # action = self.pospanel.onMicroscopeCalibrate) add_menu(self, cmenu, label="Copy Positions from Offline Microscope", text="Copy Positions from Offline Microscope", action=self.pospanel.onMicroscopeTransfer) mbar.Append(cmenu, 'Offline Microscope') self.SetMenuBar(mbar) def onShowHide(self, event=None, panel=None, name='---'): showval = {True: 1, False: 0}[event.IsChecked()] if showval: panel.Enable() else: panel.Disable() for mname, data in self.config['stages'].items(): if data['group'] == name: data['show'] = showval def onEraseMany(self, evt=None, **kws): self.pospanel.onEraseMany(event=evt) evt.Skip() def onConfigOverlays(self, evt=None, **kws): shown = False if self.overlay_frame is not None: try: self.overlay_frame.Raise() shown = True except: del self.overlay_frame if not shown: self.overlayframe = OverlayFrame(image_panel=self.imgpanel, config=self.config) def onMenuOption(self, evt=None): """events for options menu: move, erase, overwrite """ setattr(self, self.menu_opts[evt.GetId()], evt.IsChecked()) def read_config(self, configfile=None, get_dir=False): "open/read ini config file" if get_dir: ret = SelectWorkdir(self) if ret is None: self.Destroy() os.chdir(ret) self.cnf = StageConfig(configfile) self.config = self.cnf.config gui = self.config['gui'] self.workdir_file = gui.get('workdir_file', 'sampleviewer_workdir.txt') self.iconfile = gui.get('icon_file', '') self.autosave_file = gui.get('autosave_file', 'SampleStage_autosave.ini') self.v_move = gui.get('verify_move', True) self.v_erase = gui.get('verify_erase', True) self.v_replace = gui.get('verify_overwrite', True) self.center_with_fine_stages = gui.get('center_with_fine_stages', False) self.SetTitle(gui.get('title', 'Microscope')) cam = self.config['camera'] self.imgdir = cam.get('image_folder', 'Sample_Images') self.cam_type = cam.get('type', 'fly2').lower() self.cam_fly2id = cam.get('fly2_id', 0) self.cam_adpref = cam.get('ad_prefix', '') self.cam_adform = cam.get('ad_format', 'JPEG') self.cam_weburl = cam.get('web_url', 'http://164.54.160.115/jpg/2/image.jpg') self.get_cam_calib() try: pref = self.imgdir.split('_')[0] except: pref = 'Sample' self.htmllog = '%sStage.html' % pref if not os.path.exists(self.imgdir): os.makedirs(self.imgdir) if not os.path.exists(self.htmllog): self.begin_htmllog() self.config = self.cnf.config self.stages = OrderedDict() for mname, data in self.config.get('stages', {}).items(): mot = Motor(name=mname) if data['prec'] is None: data['prec'] = mot.precision if data['desc'] is None: data['desc'] = mot.description if data['maxstep'] is None: data['maxstep'] = (mot.high_limit - mot.low_limit) / 2.10 self.stages[mname] = data def get_cam_calib(self): cam = self.config['camera'] cx = self.cam_calibx = float(cam.get('calib_x', 0.001)) cy = self.cam_caliby = float(cam.get('calib_y', 0.001)) return cx, cy def begin_htmllog(self): "initialize log file" fout = open(self.htmllog, 'w') fout.write(self.html_header) fout.close() def save_image(self, fname): "save image to file" imgdata = self.imgpanel.SaveImage(fname) if imgdata is None: self.write_message('could not save image to %s' % fname) else: self.write_message('saved image to %s' % fname) return imgdata def autosave(self, positions=None): self.cnf.Save(self.autosave_file, positions=positions) def write_htmllog(self, name, thispos): stages = self.config['stages'] img_folder = self.config['camera']['image_folder'] junk, img_file = os.path.split(thispos['image']) imgfile = os.path.join(img_folder, img_file) txt = [] html_fmt = """<hr> <table><tr><td><a href='%s'> <img src='%s' width=350></a></td> <td><table><tr><td>Position:</td><td>%s</td><td>%s</td></tr> <tr><td>Motor Name</td><td>PV Name</td><td>Value</td></tr> %s </table></td></tr></table>""" pos_fmt = " <tr><td> %s </td><td> %s </td><td> %f</td></tr>" for pvname, value in thispos['position'].items(): txt.append(pos_fmt % (stages[pvname]['desc'], pvname, value)) fout = open(self.htmllog, 'a') fout.write( html_fmt % (imgfile, imgfile, name, thispos['timestamp'], '\n'.join(txt))) fout.close() def write_message(self, msg='', index=0): "write to status bar" self.statusbar.SetStatusText(msg, index) def write_framerate(self, msg): "write to status bar" self.statusbar.SetStatusText(msg, 1) def onShowCrosshair(self, event=None, show=True, **kws): self.xhair_pixel = None if show: self.xhair_pixel = self.last_pixel def onAFTimer(self, event=None, **kws): if self.af_done: self.af_thread.join() self.af_timer.Stop() if self.ctrlpanel.af_message is not None: self.ctrlpanel.af_message.SetLabel('') self.ctrlpanel.af_button.Enable() def onAutoFocus(self, event=None, **kws): self.af_done = False self.ctrlpanel.af_button.Disable() self.af_thread = Thread(target=self.do_autofocus) self.af_timer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.onAFTimer, self.af_timer) self.af_timer.Start(2000) self.af_thread.start() def do_autofocus(self): report = None if self.ctrlpanel.af_message is not None: report = self.ctrlpanel.af_message.SetLabel if report is not None: report('Auto-setting exposure') self.imgpanel.AutoSetExposureTime() report('Auto-focussing start') def make_fibs(max=3000): f = [1., 1.] i = 0 while True: val = f[i] + f[i + 1] if val > max: break f.append(val) i += 1 return f zstage = self.ctrlpanel.motors['z']._pvs['VAL'] start_pos = zstage.get() focus_data = [] def get_score(pos): zpos = start_pos + pos * 0.001 zstage.put(zpos, wait=True) time.sleep(0.2) score = image_blurriness(self.imgpanel) dat = (pos, zstage.get(), score) focus_data.append(dat) return score # step 1: take up to 15 steps of 250 microns # while score is still improving report('Auto-focussing finding rough focus') scores = [] NMAX = 15 step = 250.0 score0 = get_score(0) scorep = get_score(step) scorem = get_score(-step) sign = None best = 0.0 if scorem < score0: score0 = scorem sign = -1.0 elif scorep < score0: score0 = scorep sign = 1.0 if sign is not None: i = 1 while i < NMAX: i = i + 1 tmp = sign * step * (i + 1) score = get_score(tmp) if score > score0: break score0, best = score, tmp zstage.put(start_pos + best * 0.001, wait=True) # now refine start_pos = zstage.get() report('Auto-focussing refining focus') start, stop = -300, 300 fibs = make_fibs(max=abs(stop - start)) nfibs = len(fibs) step = fibs[nfibs - 3] / fibs[nfibs - 1] best = (start + stop) / 2 z1, z2 = int(start + step * (stop - start)), int(stop - step * (stop - start)) score1, score2 = get_score(z1), get_score(z2) for i in range(nfibs - 2): step = fibs[nfibs - i - 3] / fibs[nfibs - i - 1] report("Auto-focussing refining focus %i " % (i + 1)) if score1 > score2: start = z1 best = int(stop - step * (stop - start)) z1, z2 = z2, best score1, score2 = score2, get_score(best) else: stop = z2 best = int(start + step * (stop - start)) z1, z2 = best, z1 score1, score2 = get_score(best), score1 if abs(z1 - z2) < 2: break get_score(best) report('Auto-focussing done. ') self.ctrlpanel.af_button.Enable() def onMoveToCenter(self, event=None, **kws): "bring last pixel to image center" p = self.last_pixel if p is None: return cal_x, cal_y = self.get_cam_calib() dx = 0.001 * cal_x * (p['x'] - p['xmax'] / 2.0) dy = 0.001 * cal_y * (p['y'] - p['ymax'] / 2.0) mots = self.ctrlpanel.motors xmotor, ymotor = 'x', 'y' if self.center_with_fine_stages and 'finex' in mots: xmotor, ymotor = 'finex', 'finey' xscale, yscale = 1.0, 1.0 for stage_info in self.stages.values(): if stage_info['desc'].lower() == xmotor: xscale = stage_info['scale'] if stage_info['desc'].lower() == ymotor: yscale = stage_info['scale'] mots[xmotor].VAL += dx * xscale mots[ymotor].VAL += dy * yscale self.onSelectPixel(p['xmax'] / 2.0, p['ymax'] / 2.0, xmax=p['xmax'], ymax=p['ymax']) def onSelectPixel(self, x, y, xmax=100, ymax=100): " select a pixel from image " self.last_pixel = dict(x=x, y=y, xmax=xmax, ymax=ymax) cal_x, cal_y = self.get_cam_calib() self.confpanel.on_selected_pixel(x, y, xmax, ymax, cam_calibx=cal_x, cam_caliby=cal_y) def onPixelMotion(self, x, y, xmax=100, ymax=100): " select a pixel from image " fmt = """Pixel=(%i, %i) (%.1f, %.1f)um from center, (%.1f, %.1f)um from selected""" if x > 0 and x < xmax and y > 0 and y < ymax: dx = abs(self.cam_calibx * (x - xmax / 2.0)) dy = abs(self.cam_caliby * (y - ymax / 2.0)) ux, uy = 0, 0 if self.last_pixel is not None: lastx = self.last_pixel['x'] lasty = self.last_pixel['y'] ux = abs(self.cam_calibx * (x - lastx)) uy = abs(self.cam_caliby * (y - lasty)) pix_msg = fmt % (x, y, dx, dy, ux, uy) self.write_message(pix_msg) if not self.confpanel.img_size_shown: self.confpanel.img_size.SetLabel("(%i, %i)" % (xmax, ymax)) self.confpanel.img_size_shown = True def onClose(self, event=None): if wx.ID_YES == popup(self, "Really Quit?", "Exit Sample Stage?", style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION): fout = open(self.workdir_file, 'w') fout.write("%s\n" % os.path.abspath(os.curdir)) fout.close() self.imgpanel.Stop() try: self.overlay_frame.Destroy() except: pass self.Destroy() def onExportPositions(self, event=None): curpath = os.getcwd() fname = FileSave( self, 'Export Positions File', wildcard='Position Files (*.pos)|*.pos|All files (*.*)|*.*', default_file='Save.pos') if fname is not None: self.pospanel.SavePositions(fname) self.write_message('Saved Positions File %s' % fname) os.chdir(curpath) def onImportPositions(self, event=None): curpath = os.getcwd() fname = FileOpen( self, 'Import Positions File', wildcard='Position Files (*.pos)|*.pos|All files (*.*)|*.*', default_file='Save.pos') if fname is not None: self.pospanel.LoadPositions(fname) self.write_message('Loaded Positions from File %s' % fname) os.chdir(curpath) def onChangeWorkdir(self, event=None): ret = SelectWorkdir(self) if ret is None: return os.chdir(ret) cam = self.config['camera'] self.imgdir = cam.get('image_folder', 'Sample_Images') if not os.path.exists(self.imgdir): os.makedirs(self.imgdir) if not os.path.exists(self.htmllog): self.begin_htmllog() def onReportBlurry(self, event=None): score = image_blurriness(self.imgpanel) print(" blurriness = %.3f" % (score)) def onStartProjections(self, event=None): try: self.xplot.Raise() except: self.xplot = PlotFrame(parent=self) try: self.yplot.Raise() except: self.yplot = PlotFrame(parent=self) self.proj_start = True self.proj_timer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.onShowProjections, self.proj_timer) self.proj_timer.Start(500) def onStopProjections(self, event=None): self.proj_timer.Stop() def onShowProjections(self, event=None): dat = self.imgpanel.grab_data() shape = dat.shape if len(shape) == 3: dat = dat.sum(axis=2) cx, cy = self.get_cam_calib() kws = dict(ylabel='intensity', xlabel='distance ($\mu\mathrm{m}$)', marker='+', markersize=4) _y = (np.arange(shape[0]) - shape[0] / 2.0) * abs(cx) _x = (np.arange(shape[1]) - shape[1] / 2.0) * abs(cy) _xi = dat.sum(axis=0) _yi = dat.sum(axis=1) ymin = min((min(_xi), min(_yi))) ymax = max((max(_xi), max(_yi))) if self.proj_start: self.xplot.plot(_x, _xi, title='X projection', **kws) self.yplot.plot(_y, _yi, title='Y projection', **kws) self.xplot.Show() self.yplot.Show() self.proj_start = False else: self.xplot.panel.update_line(0, _x, _xi, draw=True, update_limits=True) self.yplot.panel.update_line(0, _y, _yi, draw=True, update_limits=True) self.xplot.panel.axes.set_ylim((ymin, ymax), emit=True) self.yplot.panel.axes.set_ylim((ymin, ymax), emit=True) # print('X lims: ', self.xplot.panel.conf.zoom_lims, xlims) # self.xplot.panel.set_xylims(xlims) # self.yplot.panel.set_xylims(ylims) def onSaveConfig(self, event=None): fname = FileSave(self, 'Save Configuration File', wildcard='INI (*.ini)|*.ini|All files (*.*)|*.*', default_file='SampleStage.ini') if fname is not None: self.cnf.Save(fname) self.write_message('Saved Configuration File %s' % fname) def onReadConfig(self, event=None): curpath = os.getcwd() fname = FileOpen(self, 'Read Configuration File', wildcard='INI (*.ini)|*.ini|All files (*.*)|*.*', default_file='SampleStage.ini') if fname is not None: self.read_config(fname) self.connect_motors() self.pospanel.set_positions(self.config['positions']) self.write_message('Read Configuration File %s' % fname) os.chdir(curpath)
class XRD2D_DisplayFrame(ImageFrame): """ MatPlotlib Image Display on a wx.Frame, using ImagePanel """ def __init__(self, _larch=None, parent=None, size=None, mode='intensity', move_callback=None, save_callback=None, show_xsections=False, cursor_labels=None, output_title='Image', **kws): self.xrmfile = None self.map = None self.move_callback = move_callback self.save_callback = save_callback self.larch = _larch if self.larch is None: self.init_larch() ImageFrame.__init__(self, parent=parent, size=size, cursor_labels=cursor_labels, mode=mode, output_title=output_title, **kws) self.panel.cursor_mode = 'zoom' self.panel.xaxis = 'q' self.panel.report_leftdown = self.report_leftdown self.panel.report_motion = self.report_motion self.prof_plotter = None self.zoom_ini = None self.lastpoint = [None, None] self.this_point = None self.rbbox = None def display(self, map, xrmfile=None, ai=None, mask=None, **kws): self.xrmfile = xrmfile self.map = map self.title = '' if 'title' in kws: self.title = kws['title'] ImageFrame.display(self, map, **kws) if self.panel.conf.auto_contrast: self.set_contrast_levels() self.ai = ai self.mask = mask if np.shape(self.mask) == np.shape(map): self.masked_map = map * (np.ones(np.shape(self.mask))-mask.value) self.panel.xdata = np.arange(map.shape[0]) self.panel.ydata = np.arange(map.shape[0]) def init_larch(self): if self.larch is None: self.larch = Interpreter() def prof_motion(self, event=None): if not event.inaxes or self.zoom_ini is None: return try: xmax, ymax = event.x, event.y except: return xmin, ymin, xd, yd = self.zoom_ini if event.xdata is not None: self.lastpoint[0] = event.xdata if event.ydata is not None: self.lastpoint[1] = event.ydata yoff = self.panel.canvas.figure.bbox.height ymin, ymax = yoff - ymin, yoff - ymax zdc = wx.ClientDC(self.panel.canvas) zdc.SetLogicalFunction(wx.XOR) zdc.SetBrush(wx.TRANSPARENT_BRUSH) zdc.SetPen(wx.Pen('White', 2, wx.SOLID)) zdc.ResetBoundingBox() if not is_wxPhoenix: zdc.BeginDrawing() # erase previous box if self.rbbox is not None: zdc.DrawLine(*self.rbbox) self.rbbox = (xmin, ymin, xmax, ymax) zdc.DrawLine(*self.rbbox) if not is_wxPhoenix: zdc.EndDrawing() def prof_leftdown(self, event=None): self.report_leftdown(event=event) if event.inaxes and len(self.map.shape) == 2: self.lastpoint = [None, None] self.zoom_ini = [event.x, event.y, event.xdata, event.ydata] def prof_leftup(self, event=None): if len(self.map.shape) != 2: return if self.rbbox is not None: zdc = wx.ClientDC(self.panel.canvas) zdc.SetLogicalFunction(wx.XOR) zdc.SetBrush(wx.TRANSPARENT_BRUSH) zdc.SetPen(wx.Pen('White', 2, wx.SOLID)) zdc.ResetBoundingBox() if not is_wxPhoenix: zdc.BeginDrawing() zdc.DrawLine(*self.rbbox) if not is_wxPhoenix: zdc.EndDrawing() self.rbbox = None if self.zoom_ini is None or self.lastpoint[0] is None: return x0 = int(self.zoom_ini[2]) x1 = int(self.lastpoint[0]) y0 = int(self.zoom_ini[3]) y1 = int(self.lastpoint[1]) dx, dy = abs(x1-x0), abs(y1-y0) self.lastpoint, self.zoom_ini = [None, None], None if dx < 2 and dy < 2: self.zoom_ini = None return outdat = [] if dy > dx: _y0 = min(int(y0), int(y1+0.5)) _y1 = max(int(y0), int(y1+0.5)) for iy in range(_y0, _y1): ix = int(x0 + (iy-int(y0))*(x1-x0)/(y1-y0)) outdat.append((ix, iy)) else: _x0 = min(int(x0), int(x1+0.5)) _x1 = max(int(x0), int(x1+0.5)) for ix in range(_x0, _x1): iy = int(y0 + (ix-int(x0))*(y1-y0)/(x1-x0)) outdat.append((ix, iy)) x, y, z = [], [], [] for ix, iy in outdat: x.append(ix) y.append(iy) z.append(self.panel.conf.data[iy,ix]) self.prof_dat = dy>dx, outdat if self.prof_plotter is not None: try: self.prof_plotter.Raise() self.prof_plotter.clear() except (AttributeError, PyDeadObjectError): self.prof_plotter = None if self.prof_plotter is None: self.prof_plotter = PlotFrame(self, title='Profile') self.prof_plotter.panel.report_leftdown = self.prof_report_coords xlabel, y2label = 'Pixel (x)', 'Pixel (y)' if dy > dx: x, y = y, x xlabel, y2label = y2label, xlabel self.prof_plotter.panel.clear() # reset_config() if len(self.title) < 1: self.title = os.path.split(self.xrmfile.filename)[1] opts = dict(linewidth=2, marker='+', markersize=3, show_legend=True, xlabel=xlabel) self.prof_plotter.plot(x, z, title=self.title, color='blue', zorder=20, xmin=min(x)-3, xmax=max(x)+3, ylabel='counts', label='counts', **opts) self.prof_plotter.oplot(x, y, y2label=y2label, label=y2label, zorder=3, side='right', color='#771111', **opts) self.prof_plotter.panel.unzoom_all() self.prof_plotter.Show() self.zoom_ini = None self.panel.cursor_mode = 'zoom' def prof_report_coords(self, event=None): """override report leftdown for profile plotter""" if event is None: return ex, ey = event.x, event.y msg = '' plotpanel = self.prof_plotter.panel axes = plotpanel.fig.get_axes()[0] write = plotpanel.write_message try: x, y = axes.transData.inverted().transform((ex, ey)) except: x, y = event.xdata, event.ydata if x is None or y is None: return if self.ai is None: _point = 0, 0, 0 for ix, iy in self.prof_dat[1]: if (int(x) == ix and not self.prof_dat[0] or int(x) == iy and self.prof_dat[0]): _point = (ix, iy, self.panel.conf.data[iy, ix]) msg = "Pixel [%i, %i], Intensity= %g" % _point else: ai = self.ai xcenter = ai._poni2/ai.detector.pixel2 ## units pixels ycenter = ai._poni1/ai.detector.pixel1 ## units pixels _point = 0, 0, 0, 0, 0 for ix, iy in self.prof_dat[1]: if (int(x) == ix and not self.prof_dat[0] or int(x) == iy and self.prof_dat[0]): x_pix = ix - xcenter ## units pixels y_pix = iy - ycenter ## units pixels x_m = x_pix * ai.detector.pixel2 ## units m y_m = y_pix * ai.detector.pixel1 ## units m twth = np.arctan2(math.sqrt(x_m**2 + y_m**2),ai._dist) ## radians twth = np.degrees(twth) ## units degrees eta = np.arctan2(y_m,x_m) ## units radians eta = np.degrees(eta) ## units degrees _point = (ix, iy, twth, eta, self.panel.conf.data[iy, ix]) msg = 'Pixel [%i, %i], 2TH=%.2f, ETA=%.1f, Intensity= %g' % _point write(msg, panel=0) def report_leftdown(self, event=None): if event is None: return if event.xdata is None or event.ydata is None: return ix, iy = int(round(event.xdata)), int(round(event.ydata)) conf = self.panel.conf if conf.flip_ud: iy = conf.data.shape[0] - iy if conf.flip_lr: ix = conf.data.shape[1] - ix self.this_point = None msg = '' if (ix >= 0 and ix < conf.data.shape[1] and iy >= 0 and iy < conf.data.shape[0]): pos = '' pan = self.panel labs, vals = [], [] if pan.xdata is not None: labs.append(pan.xlab) vals.append(pan.xdata[ix]) if pan.ydata is not None: labs.append(pan.ylab) vals.append(pan.ydata[iy]) dval = conf.data[iy, ix] if len(pan.data_shape) == 3: dval = "%.4g, %.4g, %.4g" % tuple(dval) else: dval = "%.4g" % dval if pan.xdata is not None and pan.ydata is not None: self.this_point = (ix, iy) if self.ai is None: msg = "Pixel [%i, %i], Intensity=%s " % (ix, iy, dval) else: ai = self.ai xcenter = ai._poni2/ai.detector.pixel2 ## units pixels ycenter = ai._poni1/ai.detector.pixel1 ## units pixels x_pix = ix - xcenter ## units pixels y_pix = iy - ycenter ## units pixels x_m = x_pix * ai.detector.pixel2 ## units m y_m = y_pix * ai.detector.pixel1 ## units m twth = np.arctan2(math.sqrt(x_m**2 + y_m**2),ai._dist) ## radians twth = np.degrees(twth) ## units degrees eta = np.arctan2(y_m,x_m) ## units radians eta = np.degrees(eta) ## units degrees msg = 'Pixel [%i, %i], 2TH=%.2f deg., ETA=%.1f deg., Intensity= %s' % (ix, iy, twth, eta, dval) self.panel.write_message(msg, panel=0) def report_motion(self, event=None): return def CustomConfig(self, panel, sizer, irow): """config panel for left-hand-side of frame""" conf = self.panel.conf lpanel = panel lsizer = sizer labstyle = wx.ALIGN_LEFT|wx.LEFT|wx.TOP|wx.EXPAND self.MskCkBx = wx.CheckBox(panel, label='Apply mask?') self.MskCkBx.Bind(wx.EVT_CHECKBOX, self.onApplyMask) sizer.Add(self.MskCkBx, (irow+1,0), (1,4), labstyle, 3) self.LoadBtn = wx.Button(panel, label='Load New Mask') self.LoadBtn.Bind(wx.EVT_BUTTON, self.onLoadMask) sizer.Add(self.LoadBtn, (irow+2,0), (1,4), labstyle, 3) self.ReCalc1D = wx.Button(panel, label='Replot 1DXRD') self.ReCalc1D.Bind(wx.EVT_BUTTON, self.onReplot1DXRD) sizer.Add(self.ReCalc1D, (irow+3,0), (1,4), labstyle, 3) def onApplyMask(self, event): ''' Applies mask to 2DXRD map mkak 2016.09.29 ''' if event.GetEventObject().GetValue(): if self.masked_map is None: print('Mask file not defined.') question = 'No mask found in map file. Would you like to load a new file now?' caption = 'Load mask file?' dlg = wx.MessageDialog(self, question, caption, wx.YES_NO | wx.ICON_QUESTION) print 'answer:', dlg.ShowModal() # == wx.ID_YES read = dlg.ShowModal() dlg.Destroy() if read == wx.ID_YES: self.onLoadMask() self.MskCkBx.SetValue(False) else: ImageFrame.display(self, self.masked_map) else: ImageFrame.display(self, self.map) def onLoadMask(self, evt=None): wildcards = 'pyFAI mask (*.edf)|*.edf|All files (*.*)|*.*' dlg = wx.FileDialog(self, message='Choose XRD mask file', defaultDir=os.getcwd(), wildcard=wildcards, style=wx.FD_OPEN) edffile, read = None, False if dlg.ShowModal() == wx.ID_OK: read = True edffile = dlg.GetPath().replace('\\', '/') dlg.Destroy() if read: print('Reading mask file: %s' % edffile) try: import fabio self.mask = fabio.open(edffile).data self.masked_map = self.map * (np.ones(np.shape(self.mask))-self.mask) self.MskCkBx.SetValue(True) ImageFrame.display(self, self.masked_map) except: print('File must be .edf format; user must have fabio installed.') ## Can this be called here? #readEDFfile(self,name='mask',keyword='maskfile') #add_calibration() def onReplot1DXRD(self, evt=None): print('Not yet implemented.')
class EditColumnFrame(wx.Frame): """Set Column Labels for a file""" def __init__(self, parent, group=None, last_array_sel=None, read_ok_cb=None, edit_groupname=True): self.parent = parent self.dgroup = group if not hasattr(self.dgroup, 'is_xas'): try: self.dgroup.is_xas = 'energ' in self.dgroup.array_labels[ 0].lower() except: self.dgroup.is_xas = False self.read_ok_cb = read_ok_cb self.array_sel = { 'xpop': '', 'xarr': None, 'ypop': '', 'yop': '/', 'yarr1': None, 'yarr2': None, 'use_deriv': False } if last_array_sel is not None: self.array_sel.update(last_array_sel) if self.array_sel['yarr2'] is None and 'i0' in self.dgroup.array_labels: self.array_sel['yarr2'] = 'i0' if self.array_sel['yarr1'] is None: if 'itrans' in self.dgroup.array_labels: self.array_sel['yarr1'] = 'itrans' elif 'i1' in self.dgroup.array_labels: self.array_sel['yarr1'] = 'i1' message = "Build Arrys from Data Columns for %s" % self.dgroup.filename wx.Frame.__init__(self, None, -1, 'Build Arrays from Data Columns for %s' % self.dgroup.filename, style=FRAMESTYLE) self.SetFont(Font(10)) panel = scrolled.ScrolledPanel(self) self.SetMinSize((600, 600)) self.colors = GUIColors() self.plotframe = None # title row title = SimpleText(panel, message, font=Font(13), colour=self.colors.title, style=LCEN) opts = dict(action=self.onColumnChoice, size=(120, -1)) arr_labels = self.dgroup.array_labels yarr_labels = arr_labels + ['1.0', '0.0', ''] xarr_labels = arr_labels + ['<index>'] self.xarr = Choice(panel, choices=xarr_labels, **opts) self.yarr1 = Choice(panel, choices=arr_labels, **opts) self.yarr2 = Choice(panel, choices=yarr_labels, **opts) opts['size'] = (90, -1) self.xpop = Choice(panel, choices=XPRE_OPS, **opts) self.ypop = Choice(panel, choices=YPRE_OPS, **opts) opts['size'] = (50, -1) self.yop = Choice(panel, choices=ARR_OPS, **opts) ylab = SimpleText(panel, 'Y = ') xlab = SimpleText(panel, 'X = ') self.xsuf = SimpleText(panel, '') self.ysuf = SimpleText(panel, '') self.xpop.SetStringSelection(self.array_sel['xpop']) self.ypop.SetStringSelection(self.array_sel['ypop']) self.yop.SetStringSelection(self.array_sel['yop']) if '(' in self.array_sel['ypop']: self.ysuf.SetLabel(')') ixsel, iysel, iy2sel = 0, 1, len(yarr_labels) - 1 if self.array_sel['xarr'] in xarr_labels: ixsel = xarr_labels.index(self.array_sel['xarr']) if self.array_sel['yarr1'] in arr_labels: iysel = arr_labels.index(self.array_sel['yarr1']) if self.array_sel['yarr2'] in yarr_labels: iy2sel = yarr_labels.index(self.array_sel['yarr2']) self.xarr.SetSelection(ixsel) self.yarr1.SetSelection(iysel) self.yarr2.SetSelection(iy2sel) opts['size'] = (150, -1) self.use_deriv = Check(panel, label='use derivative', default=self.array_sel['use_deriv'], **opts) self.is_xas = Check(panel, label='use as XAS data', default=self.dgroup.is_xas, **opts) bpanel = wx.Panel(panel) bsizer = wx.BoxSizer(wx.HORIZONTAL) bsizer.Add(Button(bpanel, 'Preview', action=self.onColumnChoice), 4) bsizer.Add(Button(bpanel, 'OK', action=self.onOK), 4) bsizer.Add(Button(bpanel, 'Cancel', action=self.onCancel), 4) pack(bpanel, bsizer) sizer = wx.GridBagSizer(4, 8) sizer.Add(title, (0, 0), (1, 7), LCEN, 5) ir = 1 sizer.Add(xlab, (ir, 0), (1, 1), CEN, 0) sizer.Add(self.xpop, (ir, 1), (1, 1), CEN, 0) sizer.Add(self.xarr, (ir, 2), (1, 1), CEN, 0) sizer.Add(self.xsuf, (ir, 3), (1, 1), CEN, 0) ir += 1 sizer.Add(ylab, (ir, 0), (1, 1), CEN, 0) sizer.Add(self.ypop, (ir, 1), (1, 1), CEN, 0) sizer.Add(self.yarr1, (ir, 2), (1, 1), CEN, 0) sizer.Add(self.yop, (ir, 3), (1, 1), CEN, 0) sizer.Add(self.yarr2, (ir, 4), (1, 1), CEN, 0) sizer.Add(self.ysuf, (ir, 5), (1, 1), CEN, 0) ir += 1 sizer.Add(self.use_deriv, (ir, 0), (1, 3), LCEN, 0) ir += 1 sizer.Add(self.is_xas, (ir, 0), (1, 3), LCEN, 0) self.wid_groupname = None if edit_groupname: wid_grouplab = SimpleText(panel, 'Use Group Name: ') self.wid_groupname = wx.TextCtrl(panel, value=self.dgroup._groupname, size=(100, -1)) ir += 1 sizer.Add(wid_grouplab, (ir, 0), (1, 2), LCEN, 3) sizer.Add(self.wid_groupname, (ir, 2), (1, 3), LCEN, 3) ir += 1 sizer.Add(bpanel, (ir, 0), (1, 5), LCEN, 3) pack(panel, sizer) ftext = wx.TextCtrl(self, style=wx.TE_MULTILINE | wx.TE_READONLY, size=(-1, 150)) try: m = open(self.dgroup.filename, 'r') text = m.read() m.close() except: text = "The file '%s'\n was not found" % self.dgroup.filename ftext.SetValue(text) ftext.SetFont(Font(9)) mainsizer = wx.BoxSizer(wx.VERTICAL) mainsizer.Add(panel, 0, wx.GROW | wx.ALL, 2) mainsizer.Add(ftext, 1, LCEN | wx.GROW, 2) pack(self, mainsizer) self.Show() self.Raise() def onOK(self, event=None): """ build arrays according to selection """ if not hasattr(self.dgroup, '_xdat'): self.onColumnChoice() if self.wid_groupname is not None: self.dgroup._groupname = fix_varname(self.wid_groupname.GetValue()) if self.plotframe is not None: try: self.plotframe.Destroy() except: pass if self.read_ok_cb is not None: self.read_ok_cb(self.dgroup, self.array_sel) self.Destroy() def onCancel(self, event=None): self.dgroup.import_ok = False if self.plotframe is not None: self.plotframe.Destroy() self.Destroy() def onColumnChoice(self, evt=None): """column selections changed calc _xdat and _ydat""" # dtcorr = self.dtcorr.IsChecked() dtcorr = False use_deriv = self.use_deriv.IsChecked() dgroup = self.dgroup ix = self.xarr.GetSelection() xname = self.xarr.GetStringSelection() if xname == '<index>': xname = self.dgroup.array_labels[0] dgroup._index = 1.0 * np.arange(len(getattr(dgroup, xname))) xname = '_index' dgroup.is_xas = self.is_xas.IsChecked() dgroup._xdat = getattr(dgroup, xname) def do_preop(opwid, arr): opstr = opwid.GetStringSelection().strip() suf = '' if opstr in ('-log(', 'log('): suf = ')' if opstr == 'log(': arr = np.log(arr) elif opstr == '-log(': arr = -np.log(arr) return suf, opstr, arr xsuf, xpop, dgroup._xdat = do_preop(self.xpop, dgroup._xdat) self.xsuf.SetLabel(xsuf) try: xunits = dgroup.array_units[ix].strip() xlabel = '%s (%s)' % (xname, xunits) except: xlabel = xname def get_data(group, arrayname, correct=False): if hasattr(group, 'get_data'): return group.get_data(arrayname, correct=correct) return getattr(group, arrayname, None) yname1 = self.yarr1.GetStringSelection().strip() yname2 = self.yarr2.GetStringSelection().strip() yop = self.yop.GetStringSelection().strip() ylabel = yname1 if len(yname2) == 0: yname2, yop = '1.0', '*' else: ylabel = "%s%s%s" % (ylabel, yop, yname2) yarr1 = get_data(dgroup, yname1, correct=dtcorr) if yname2 in ('0.0', '1.0'): yarr2 = float(yname2) if yop == '/': yarr2 = 1.0 else: yarr2 = get_data(dgroup, yname2, correct=dtcorr) dgroup._ydat = yarr1 if yop == '+': dgroup._ydat = yarr1.__add__(yarr2) elif yop == '-': dgroup._ydat = yarr1.__sub__(yarr2) elif yop == '*': dgroup._ydat = yarr1.__mul__(yarr2) elif yop == '/': dgroup._ydat = yarr1.__truediv__(yarr2) ysuf, ypop, dgroup._ydat = do_preop(self.ypop, dgroup._ydat) self.ysuf.SetLabel(ysuf) if use_deriv: try: dgroup._ydat = np.gradient(dgroup._ydat) / np.gradient( dgroup._xdat) except: pass self.array_sel = { 'xpop': xpop, 'xarr': xname, 'ypop': ypop, 'yop': yop, 'yarr1': yname1, 'yarr2': yname2, 'use_deriv': use_deriv } try: npts = min(len(dgroup._xdat), len(dgroup._ydat)) except AttributeError: print('Error calculating arrays (npts not correct)') return dgroup._npts = npts dgroup.plot_xlabel = xlabel dgroup.plot_ylabel = ylabel dgroup._xdat = np.array(dgroup._xdat[:npts]) dgroup._ydat = np.array(dgroup._ydat[:npts]) if dgroup.is_xas: dgroup.energy = dgroup._xdat dgroup.mu = dgroup._ydat self.raise_plotframe() ppanel = self.plotframe.panel popts = {} path, fname = os.path.split(dgroup.filename) popts['label'] = "%s: %s" % (fname, dgroup.plot_ylabel) popts['title'] = fname popts['ylabel'] = dgroup.plot_ylabel popts['xlabel'] = dgroup.plot_xlabel ppanel.plot(dgroup._xdat, dgroup._ydat, **popts) def raise_plotframe(self): if self.plotframe is not None: try: self.plotframe.Show() except: self.plotframe = None if self.plotframe is None: self.plotframe = PlotFrame(None, size=(650, 400)) self.plotframe.Show()
class MapImageFrame(ImageFrame): """ MatPlotlib Image Display on a wx.Frame, using ImagePanel """ def __init__(self, parent=None, size=(650, 650), mode='intensity', lasso_callback=None, move_callback=None, save_callback=None, show_xsections=False, cursor_labels=None, with_savepos=True,output_title='Image', **kws): # instdb=None, inst_name=None, self.det = None self.xrmfile = None self.map = None self.move_callback = move_callback self.save_callback = save_callback self.with_savepos = with_savepos ImageFrame.__init__(self, parent=parent, size=size, lasso_callback=lasso_callback, cursor_labels=cursor_labels, mode=mode, output_title=output_title, **kws) self.panel.add_cursor_mode('prof', motion = self.prof_motion, leftdown = self.prof_leftdown, leftup = self.prof_leftup) self.panel.report_leftdown = self.report_leftdown self.panel.report_motion = self.report_motion w0, h0 = self.GetSize() w1, h1 = self.GetBestSize() self.SetSize((max(w0, w1)+5, max(h0, h1)+5)) self.SetMinSize((500, 500)) self.prof_plotter = None self.zoom_ini = None self.lastpoint = [None, None] self.this_point = None self.rbbox = None def display(self, map, det=None, xrmfile=None, xoff=0, yoff=0, with_savepos=True, **kws): self.xoff = xoff self.yoff = yoff self.det = det self.xrmfile = xrmfile self.map = map self.title = '' if 'title' in kws: self.title = kws['title'] ImageFrame.display(self, map, **kws) if 'x' in kws: self.panel.xdata = kws['x'] if 'y' in kws: self.panel.ydata = kws['y'] self.set_contrast_levels() if self.save_callback is not None and hasattr(self, 'pos_name'): self.pos_name.Enable(with_savepos) sd = kws.get('subtitles', None) if sd is not None: for i, name in enumerate(('red', 'green', 'blue')): sub = sd.get(name, None) if sub is not None: self.cmap_panels[i].title.SetLabel(sub) def prof_motion(self, event=None): if not event.inaxes or self.zoom_ini is None: return try: xmax, ymax = event.x, event.y except: return xmin, ymin, xd, yd = self.zoom_ini if event.xdata is not None: self.lastpoint[0] = event.xdata if event.ydata is not None: self.lastpoint[1] = event.ydata yoff = self.panel.canvas.figure.bbox.height ymin, ymax = yoff - ymin, yoff - ymax zdc = wx.ClientDC(self.panel.canvas) zdc.SetLogicalFunction(wx.XOR) zdc.SetBrush(wx.TRANSPARENT_BRUSH) zdc.SetPen(wx.Pen('White', 2, wx.SOLID)) zdc.ResetBoundingBox() if not is_wxPhoenix: zdc.BeginDrawing() # erase previous box if self.rbbox is not None: zdc.DrawLine(*self.rbbox) self.rbbox = (xmin, ymin, xmax, ymax) zdc.DrawLine(*self.rbbox) if not is_wxPhoenix: zdc.EndDrawing() def prof_leftdown(self, event=None): self.report_leftdown(event=event) if event.inaxes: # and len(self.map.shape) == 2: self.lastpoint = [None, None] self.zoom_ini = [event.x, event.y, event.xdata, event.ydata] def prof_leftup(self, event=None): # print("Profile Left up ", self.map.shape, self.rbbox) if self.rbbox is not None: zdc = wx.ClientDC(self.panel.canvas) zdc.SetLogicalFunction(wx.XOR) zdc.SetBrush(wx.TRANSPARENT_BRUSH) zdc.SetPen(wx.Pen('White', 2, wx.SOLID)) zdc.ResetBoundingBox() if not is_wxPhoenix: zdc.BeginDrawing() zdc.DrawLine(*self.rbbox) if not is_wxPhoenix: zdc.EndDrawing() self.rbbox = None if self.zoom_ini is None or self.lastpoint[0] is None: return x0 = int(self.zoom_ini[2]) x1 = int(self.lastpoint[0]) y0 = int(self.zoom_ini[3]) y1 = int(self.lastpoint[1]) dx, dy = abs(x1-x0), abs(y1-y0) self.lastpoint, self.zoom_ini = [None, None], None if dx < 2 and dy < 2: self.zoom_ini = None return outdat = [] if dy > dx: _y0 = min(int(y0), int(y1+0.5)) _y1 = max(int(y0), int(y1+0.5)) for iy in range(_y0, _y1): ix = int(x0 + (iy-int(y0))*(x1-x0)/(y1-y0)) outdat.append((ix, iy)) else: _x0 = min(int(x0), int(x1+0.5)) _x1 = max(int(x0), int(x1+0.5)) for ix in range(_x0, _x1): iy = int(y0 + (ix-int(x0))*(y1-y0)/(x1-x0)) outdat.append((ix, iy)) x, y, z = [], [], [] for ix, iy in outdat: x.append(ix) y.append(iy) z.append(self.panel.conf.data[iy, ix]) self.prof_dat = dy>dx, outdat if self.prof_plotter is not None: try: self.prof_plotter.Raise() self.prof_plotter.clear() except (AttributeError, PyDeadObjectError): self.prof_plotter = None if self.prof_plotter is None: self.prof_plotter = PlotFrame(self, title='Profile') self.prof_plotter.panel.report_leftdown = self.prof_report_coords xlabel, y2label = 'Pixel (x)', 'Pixel (y)' x = np.array(x) y = np.array(y) z = np.array(z) if dy > dx: x, y = y, x xlabel, y2label = y2label, xlabel self.prof_plotter.panel.clear() if len(self.title) < 1: self.title = os.path.split(self.xrmfile.filename)[1] opts = dict(linewidth=2, marker='+', markersize=3, show_legend=True, xlabel=xlabel) if isinstance(z[0], np.ndarray) and len(z[0]) == 3: # color plot rlab = self.subtitles['red'] glab = self.subtitles['green'] blab = self.subtitles['blue'] self.prof_plotter.plot(x, z[:, 0], title=self.title, color='red', zorder=20, xmin=min(x)-3, xmax=max(x)+3, ylabel='counts', label=rlab, **opts) self.prof_plotter.oplot(x, z[:, 1], title=self.title, color='darkgreen', zorder=20, xmin=min(x)-3, xmax=max(x)+3, ylabel='counts', label=glab, **opts) self.prof_plotter.oplot(x, z[:, 2], title=self.title, color='blue', zorder=20, xmin=min(x)-3, xmax=max(x)+3, ylabel='counts', label=blab, **opts) else: self.prof_plotter.plot(x, z, title=self.title, color='blue', zorder=20, xmin=min(x)-3, xmax=max(x)+3, ylabel='counts', label='counts', **opts) self.prof_plotter.oplot(x, y, y2label=y2label, label=y2label, zorder=3, side='right', color='black', **opts) self.prof_plotter.panel.unzoom_all() self.prof_plotter.Show() self.zoom_ini = None self.zoom_mode.SetSelection(0) self.panel.cursor_mode = 'zoom' def prof_report_coords(self, event=None): """override report leftdown for profile plotter""" if event is None: return ex, ey = event.x, event.y msg = '' plotpanel = self.prof_plotter.panel axes = plotpanel.fig.properties()['axes'][0] write = plotpanel.write_message try: x, y = axes.transData.inverted().transform((ex, ey)) except: x, y = event.xdata, event.ydata if x is None or y is None: return _point = 0, 0, 0, 0, 0 for ix, iy in self.prof_dat[1]: if (int(x) == ix and not self.prof_dat[0] or int(x) == iy and self.prof_dat[0]): _point = (ix, iy, self.panel.xdata[ix], self.panel.ydata[iy], self.panel.conf.data[iy, ix]) msg = "Pixel [%i, %i], X, OME = [%.4f mm, %.4f deg], Intensity= %g" % _point write(msg, panel=0) def onCursorMode(self, event=None, mode='zoom'): self.panel.cursor_mode = mode choice = self.zoom_mode.GetString(self.zoom_mode.GetSelection()) if event is not None: if choice.startswith('Pick Area'): #if 1 == event.GetInt(): self.panel.cursor_mode = 'lasso' elif choice.startswith('Show Line'): #elif 2 == event.GetInt(): self.panel.cursor_mode = 'prof' def report_leftdown(self, event=None): if event is None: return if event.xdata is None or event.ydata is None: return ix, iy = int(round(event.xdata)), int(round(event.ydata)) conf = self.panel.conf if conf.flip_ud: iy = conf.data.shape[0] - iy if conf.flip_lr: ix = conf.data.shape[1] - ix self.this_point = None msg = '' if (ix >= 0 and ix < conf.data.shape[1] and iy >= 0 and iy < conf.data.shape[0]): pos = '' pan = self.panel labs, vals = [], [] if pan.xdata is not None: labs.append(pan.xlab) vals.append(pan.xdata[ix]) if pan.ydata is not None: labs.append(pan.ylab) vals.append(pan.ydata[iy]) pos = ', '.join(labs) vals =', '.join(['%.4g' % v for v in vals]) pos = '%s = [%s]' % (pos, vals) dval = conf.data[iy, ix] if len(pan.data_shape) == 3: dval = "%.4g, %.4g, %.4g" % tuple(dval) else: dval = "%.4g" % dval if pan.xdata is not None and pan.ydata is not None: self.this_point = (ix, iy) msg = "Pixel [%i, %i], %s, Intensity=%s " % (ix, iy, pos, dval) self.panel.write_message(msg, panel=0) def report_motion(self, event=None): return def onLasso(self, data=None, selected=None, mask=None, **kws): if hasattr(self.lasso_callback , '__call__'): self.lasso_callback(data=data, selected=selected, mask=mask, xoff=self.xoff, yoff=self.yoff, det=self.det, xrmfile=self.xrmfile, **kws) self.zoom_mode.SetSelection(0) self.panel.cursor_mode = 'zoom' def CustomConfig(self, panel, sizer=None, irow=0): """config panel for left-hand-side of frame""" labstyle = wx.ALIGN_LEFT|wx.LEFT|wx.TOP|wx.EXPAND if self.lasso_callback is None: zoom_opts = ('Zoom to Rectangle', 'Show Line Profile') else: zoom_opts = ('Zoom to Rectangle', 'Pick Area for XRF Spectrum', 'Show Line Profile') cpanel = wx.Panel(panel) if sizer is None: sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(SimpleText(cpanel, label='Cursor Modes', style=labstyle), 0, labstyle, 3) self.zoom_mode = wx.RadioBox(cpanel, -1, "", wx.DefaultPosition, wx.DefaultSize, zoom_opts, 1, wx.RA_SPECIFY_COLS) self.zoom_mode.Bind(wx.EVT_RADIOBOX, self.onCursorMode) sizer.Add(self.zoom_mode, 1, labstyle, 4) if self.save_callback is not None: sizer.Add(SimpleText(cpanel, label='Save Position:', style=labstyle), 0, labstyle, 3) self.pos_name = wx.TextCtrl(cpanel, -1, '', size=(155, -1), style=wx.TE_PROCESS_ENTER) self.pos_name.Bind(wx.EVT_TEXT_ENTER, self.onSavePixel) sizer.Add(self.pos_name, 0, labstyle, 3) pack(cpanel, sizer) return cpanel def onMoveToPixel(self, event=None): pass def onSavePixel(self, event=None): if self.this_point is not None and self.save_callback is not None: name = str(event.GetString().strip()) # name = str(self.pos_name.GetValue().strip()) ix, iy = self.this_point x = float(self.panel.xdata[int(ix)]) y = float(self.panel.ydata[int(iy)]) self.save_callback(name, ix, iy, x=x, y=y, title=self.title, xrmfile=self.xrmfile)
class TomographyFrame(BaseFrame): ### COPY OF ImageFrame(BaseFrame) with portions of ImageMatrixFrame(BaseFrame) ''' MatPlotlib Image Display ons a wx.Frame, using ImagePanel ''' help_msg = '''Quick help: Left-Click: to display X,Y coordinates and Intensity Left-Drag: to zoom in on region Right-Click: display popup menu with choices: Zoom out 1 level Zoom all the way out -------------------- Rotate Image Save Image Keyboard Shortcuts: (For Mac OSX, replace 'Ctrl' with 'Apple') Saving Images: Ctrl-S: Save image to file Ctrl-C: Copy image to clipboard Ctrl-P: Print Image Zooming: Ctrl-Z: Zoom all the way out Rotating/Flipping: Ctrl-R: Rotate Clockwise Ctrl-T: Flip Top/Bottom Ctrl-F: Flip Left/Right Image Enhancement: Ctrl-L: Log-Scale Intensity Ctrl-E: Enhance Contrast ''' def __init__(self, parent=None, size=None, mode='intensity', lasso_callback=None, output_title='Tomography Display Frame', subtitles=None, user_menus=None, **kws): if size is None: size = (1500, 600) self.lasso_callback = lasso_callback self.user_menus = user_menus self.cursor_menulabels = {} self.cursor_menulabels.update(CURSOR_MENULABELS) self.title = output_title self.det = None self.xrmfile = None self.wxmplot_version = get_wxmplot_version() BaseFrame.__init__(self, parent=parent, title = output_title, output_title=output_title, size=size, **kws) self.cmap_panels = {} self.subtitles = {} self.config_mode = None if subtitles is not None: self.subtitles = subtitles sbar_widths = [-2, -1, -1] sbar = self.CreateStatusBar(len(sbar_widths), wx.CAPTION) sfont = sbar.GetFont() sfont.SetWeight(wx.BOLD) sfont.SetPointSize(10) sbar.SetFont(sfont) self.SetStatusWidths(sbar_widths) self.optional_menus = [] self.bgcol = rgb2hex(self.GetBackgroundColour()[:3]) splitter = wx.SplitterWindow(self, style=wx.SP_LIVE_UPDATE) splitter.SetMinimumPaneSize(225) self.config_panel = wx.Panel(splitter) self.main_panel = wx.Panel(splitter) img_opts = dict(size = (700, 525), dpi = 100, data_callback = self.onDataChange, lasso_callback = self.onLasso, output_title = self.output_title) self.tomo_frame = [TomoFrameClass(panel=ImagePanel(self.main_panel, **img_opts), label='Sinogram'), TomoFrameClass(panel=ImagePanel(self.main_panel, **img_opts), label='Tomograph')] for iframe in self.tomo_frame: iframe.panel.nstatusbar = sbar.GetFieldsCount() self.BuildMenu() self.SetBackgroundColour('#F8F8F4') self.imin_val = {} self.imax_val = {} self.islider_range = {} self.config_mode = 'int' if mode.lower().startswith('rgb'): self.config_mode = 'rgb' self.Build_ConfigPanel() for iframe in self.tomo_frame: kwargs = {'frame':iframe} iframe.panel.add_cursor_mode('prof', motion = partial(self.prof_motion, **kwargs), leftdown = partial(self.prof_leftdown, **kwargs), leftup = partial(self.prof_leftup, **kwargs)) iframe.panel.report_leftdown = partial(self.report_leftdown, **kwargs) iframe.panel.messenger = self.write_message self.prof_plotter = None self.zoom_ini = None self.lastpoint = [None, None] self.this_point = None self.rbbox = None lsty = wx.ALIGN_LEFT|wx.LEFT|wx.TOP|wx.EXPAND gsizer = wx.GridSizer(1, 2, 2, 2) lsty |= wx.GROW|wx.ALL|wx.EXPAND|wx.ALIGN_CENTER|wx.ALIGN_CENTER_VERTICAL gsizer.Add(self.tomo_frame[0].panel, 1, lsty, 2) gsizer.Add(self.tomo_frame[1].panel, 1, lsty, 2) pack(self.main_panel, gsizer) splitter.SplitVertically(self.config_panel, self.main_panel, 1) mainsizer = wx.BoxSizer(wx.VERTICAL) mainsizer.Add(splitter, 1, wx.GROW|wx.ALL, 5) pack(self, mainsizer) def display(self, map1, map2, title=None, colormap=None, style='image', subtitles=None, name1='Sinogram', name2='Tomograph', xlabel='x', ylabel='y', rotlabel='theta', x=None, y=None, rot=None, **kws): '''plot after clearing current plot ''' map1 = np.array(map1) map2 = np.array(map2) if len(map1.shape) != len(map2.shape): return self.tomo_frame[0].label, self.tomo_frame[1].label = name1, name2 self.xdata, self.ydata, self.rotdata = x, y, rot self.xlabel, self.ylabel, self.rotlabel = xlabel, ylabel, rotlabel if title is not None: self.SetTitle(title) if subtitles is not None: self.subtitles = subtitles cmode = self.config_mode.lower()[:3] # make sure 3d image is shaped (NY, NX, 3) if len(map1.shape) == 3: ishape = map1.shape if ishape[2] != 3: if ishape[0] == 3: map1 = map1.swapaxes(0, 1).swapaxes(1, 2) elif ishape[1] == 3: map1 = map1.swapaxes(1, 2) if len(map2.shape) == 3: ishape = map2.shape if ishape[2] != 3: if ishape[0] == 3: map2 = map2.swapaxes(0, 1).swapaxes(1, 2) elif ishape[1] == 3: map2 = map2.swapaxes(1, 2) self.xzoom = self.yzoom = slice(0, map1.shape[1]+1) self.rotzoom = slice(0, map1.shape[0]+1) ## sets config_mode to single or tri color and builds panel accordingly if len(map1.shape) == 3 and len(map2.shape) == 3: if cmode != 'rgb': for comp in self.config_panel.Children: comp.Destroy() self.config_mode = 'rgb' self.tomo_frame[0].panel.conf.tricolor_mode = 'rgb' self.Build_ConfigPanel() else: ##if len(map1.shape) == 2 and len(map2.shape) == 2: if cmode != 'int': for comp in self.config_panel.Children: comp.Destroy() self.config_mode = 'int' self.Build_ConfigPanel() for map,iframe in zip([map1,map2],self.tomo_frame): iframe.map = map iframe.panel.display(iframe.map, style=style, **kws) iframe.panel.conf.title = iframe.label if colormap is not None and self.config_mode == 'int': self.cmap_panels[0].set_colormap(name=colormap) if subtitles is not None: if isinstance(subtitles, dict): self.set_subtitles(**subtitles) elif self.config_mode == 'int': self.set_subtitles(red=subtitles) contour_value = 0 if style == 'contour': contour_value = 1 self.set_contrast_levels() for iframe in self.tomo_frame: iframe.panel.redraw() self.config_panel.Refresh() self.SendSizeEvent() wx.CallAfter(self.EnableMenus) def prof_motion(self, event=None, frame=None): if not event.inaxes or self.zoom_ini is None: return try: xmax, ymax = event.x, event.y except: return if frame is None: frame = self.tomo_frame[0] xmin, ymin, xd, yd = self.zoom_ini if event.xdata is not None: self.lastpoint[0] = event.xdata if event.ydata is not None: self.lastpoint[1] = event.ydata yoff = frame.panel.canvas.figure.bbox.height ymin, ymax = yoff - ymin, yoff - ymax zdc = wx.ClientDC(frame.panel.canvas) zdc.SetLogicalFunction(wx.XOR) zdc.SetBrush(wx.TRANSPARENT_BRUSH) zdc.SetPen(wx.Pen('White', 2, wx.SOLID)) zdc.ResetBoundingBox() if not is_wxPhoenix: zdc.BeginDrawing() # erase previous box if self.rbbox is not None: zdc.DrawLine(*self.rbbox) self.rbbox = (xmin, ymin, xmax, ymax) zdc.DrawLine(*self.rbbox) if not is_wxPhoenix: zdc.EndDrawing() def prof_leftdown(self, event=None, frame=None): if frame is None: frame = self.tomo_frame[0] self.report_leftdown(event=event,frame=frame) if event.inaxes: # and len(self.map.shape) == 2: self.lastpoint = [None, None] self.zoom_ini = [event.x, event.y, event.xdata, event.ydata] def prof_leftup(self, event=None, frame=None): # print("Profile Left up ", self.map.shape, self.rbbox) if frame is None: frame = self.tomo_frame[0] if self.rbbox is not None: zdc = wx.ClientDC(frame.panel.canvas) zdc.SetLogicalFunction(wx.XOR) zdc.SetBrush(wx.TRANSPARENT_BRUSH) zdc.SetPen(wx.Pen('White', 2, wx.SOLID)) zdc.ResetBoundingBox() if not is_wxPhoenix: zdc.BeginDrawing() zdc.DrawLine(*self.rbbox) if not is_wxPhoenix: zdc.EndDrawing() self.rbbox = None if self.zoom_ini is None or self.lastpoint[0] is None: return x0 = int(self.zoom_ini[2]) x1 = int(self.lastpoint[0]) y0 = int(self.zoom_ini[3]) y1 = int(self.lastpoint[1]) dx, dy = abs(x1-x0), abs(y1-y0) self.lastpoint, self.zoom_ini = [None, None], None if dx < 2 and dy < 2: self.zoom_ini = None return outdat = [] if dy > dx: _y0 = min(int(y0), int(y1+0.5)) _y1 = max(int(y0), int(y1+0.5)) for iy in range(_y0, _y1): ix = int(x0 + (iy-int(y0))*(x1-x0)/(y1-y0)) outdat.append((ix, iy)) else: _x0 = min(int(x0), int(x1+0.5)) _x1 = max(int(x0), int(x1+0.5)) for ix in range(_x0, _x1): iy = int(y0 + (ix-int(x0))*(y1-y0)/(x1-x0)) outdat.append((ix, iy)) x, y, z = [], [], [] for ix, iy in outdat: x.append(ix) y.append(iy) z.append(frame.panel.conf.data[iy, ix]) self.prof_dat = dy>dx, outdat if self.prof_plotter is not None: try: self.prof_plotter.Raise() self.prof_plotter.clear() except (AttributeError, PyDeadObjectError): self.prof_plotter = None if self.prof_plotter is None: self.prof_plotter = PlotFrame(self, title='Profile') self.prof_plotter.panel.report_leftdown = self.prof_report_coords xlabel, y2label = 'Pixel (x)', 'Pixel (y)' x = np.array(x) y = np.array(y) z = np.array(z) if dy > dx: x, y = y, x xlabel, y2label = y2label, xlabel self.prof_plotter.panel.clear() if len(self.title) < 1: self.title = os.path.split(self.xrmfile.filename)[1] opts = dict(linewidth=2, marker='+', markersize=3, show_legend=True, xlabel=xlabel) if isinstance(z[0], np.ndarray) and len(z[0]) == 3: # color plot rlab = self.subtitles['red'] glab = self.subtitles['green'] blab = self.subtitles['blue'] self.prof_plotter.plot(x, z[:, 0], title=self.title, color='red', zorder=20, xmin=min(x)-3, xmax=max(x)+3, ylabel='counts', label=rlab, **opts) self.prof_plotter.oplot(x, z[:, 1], title=self.title, color='darkgreen', zorder=20, xmin=min(x)-3, xmax=max(x)+3, ylabel='counts', label=glab, **opts) self.prof_plotter.oplot(x, z[:, 2], title=self.title, color='blue', zorder=20, xmin=min(x)-3, xmax=max(x)+3, ylabel='counts', label=blab, **opts) else: self.prof_plotter.plot(x, z, color='blue', title=self.title.split(':')[-1], #title=self.title, zorder=20, xmin=min(x)-3, xmax=max(x)+3, ylabel='counts', label='counts', **opts) try: self.prof_plotter.oplot(x, y, y2label=y2label, label=y2label, zorder=3, side='right', color='black', **opts) except: pass self.prof_plotter.panel.unzoom_all() self.prof_plotter.Show() self.zoom_ini = None self.zoom_mode.SetSelection(0) frame.panel.cursor_mode = 'zoom' def prof_report_coords(self, event=None, frame=None): """override report leftdown for profile plotter""" if event is None: return ex, ey = event.x, event.y msg = '' if frame is None: frame = self.tomo_frame[0] plotpanel = self.prof_plotter.panel axes = plotpanel.fig.properties()['axes'][0] write = plotpanel.write_message try: x, y = axes.transData.inverted().transform((ex, ey)) except: x, y = event.xdata, event.ydata if x is None or y is None: return _point = 0, 0, 0, 0, 0 for ix, iy in self.prof_dat[1]: if (int(x) == ix and not self.prof_dat[0] or int(x) == iy and self.prof_dat[0]): _point = (ix, iy, frame.panel.xdata[ix], frame.panel.ydata[iy], frame.panel.conf.data[iy, ix]) msg = "Pixel [%i, %i], X, OME = [%.4f mm, %.4f deg], Intensity= %g" % _point write(msg, panel=0) def report_leftdown(self,event=None, frame=None): if event is None: return if event.xdata is None or event.ydata is None: return if frame is None: frame = self.tomo_frame[0] ix, iy = int(round(event.xdata)), int(round(event.ydata)) if (ix >= 0 and ix < frame.map.shape[1] and iy >= 0 and iy < frame.map.shape[0]): pos = '' if self.xdata is not None: pos = ' %s=%.4g,' % (self.xlabel, self.xdata[ix]) if self.ydata is not None: pos = '%s %s=%.4g,' % (pos, self.ylabel, self.ydata[iy]) if len(frame.map.shape) > 2: msg = 'Pixel [%i, %i],%s %s=(%.4g, %.4g, %.4g)' % (ix, iy, pos, frame.label, frame.map[iy,ix,0], frame.map[iy,ix,1], frame.map[iy,ix,2]) else: msg = 'Pixel [%i, %i],%s %s=%.4g' % (ix, iy, pos, frame.label, frame.map[iy, ix]) self.write_message(msg, panel=0) #if callable(self.cursor_callback): # self.cursor_callback(x=event.xdata, y=event.ydata) def set_subtitles(self, red=None, green=None, blue=None, **kws): if self.config_mode.startswith('int') and red is not None: self.cmap_panels[0].title.SetLabel(red) if self.config_mode.startswith('rgb') and red is not None: self.cmap_panels[0].title.SetLabel(red) if self.config_mode.startswith('rgb') and green is not None: self.cmap_panels[1].title.SetLabel(green) if self.config_mode.startswith('rgb') and blue is not None: self.cmap_panels[2].title.SetLabel(blue) def EnableMenus(self, evt=None): is_3color = len(self.tomo_frame[0].panel.conf.data.shape) > 2 for menu, on_3color in self.optional_menus: menu.Enable(is_3color==on_3color) def unzoom_all(self): self.tomo_frame[0].panel.unzoom() self.tomo_frame[1].panel.unzoom() def BuildMenu(self): # file menu mfile = self.Build_FileMenu(extras=(('Save Image of Colormap', 'Save Image of Colormap', self.onCMapSave),)) # options menu mview = self.view_menu = wx.Menu() MenuItem(self, mview, 'Zoom Out\tCtrl+Z', 'Zoom out to full data range', self.unzoom_all) m = MenuItem(self, mview, 'Toggle Background Color (Black/White)\tCtrl+W', 'Toggle background color for 3-color images', self.onTriColorBG, kind=wx.ITEM_CHECK) self.optional_menus.append((m, True)) mview.AppendSeparator() MenuItem(self, mview, 'Rotate clockwise\tCtrl+R', '', partial(self.onFlip, mode='rot_cw')) MenuItem(self, mview, 'Flip Top/Bottom\tCtrl+T', '', partial(self.onFlip, mode='flip_ud')) MenuItem(self, mview, 'Flip Left/Right\tCtrl+F', '', partial(self.onFlip, mode='flip_lr')) mview.AppendSeparator() MenuItem(self, mview, 'Projet Horizontally\tCtrl+X', '', partial(self.onProject, mode='x')) MenuItem(self, mview, 'Projet Vertically\tCtrl+Y', '', partial(self.onProject, mode='y')) mview.AppendSeparator() m = MenuItem(self, mview, 'As Contour', 'Shown as contour map', self.onContourToggle, kind=wx.ITEM_CHECK) m.Check(False) self.optional_menus.append((m, False)) m = MenuItem(self, mview, 'Configure Contours', 'Configure Contours', self.onContourConfig) self.optional_menus.append((m, False)) # intensity contrast mint =self.intensity_menu = wx.Menu() # MenuItem(self, mint, 'Log Scale Intensity\tCtrl+L', # 'use logarithm to set intensity scale', # self.onLogScale, kind=wx.ITEM_CHECK) MenuItem(self, mint, 'Toggle Contrast Enhancement\tCtrl+E', 'Toggle contrast between auto-scale and full-scale', self.onEnhanceContrast, kind=wx.ITEM_CHECK) MenuItem(self, mint, 'Set Auto-Contrast Level', 'Set auto-contrast scale', self.onContrastConfig) # smoothing msmoo = wx.Menu() for itype in Interp_List: wid = wx.NewId() msmoo.AppendRadioItem(wid, itype, itype) self.Bind(wx.EVT_MENU, partial(self.onInterp, name=itype), id=wid) # help mhelp = wx.Menu() MenuItem(self, mhelp, 'Quick Reference', 'Quick Reference for WXMPlot', self.onHelp) MenuItem(self, mhelp, 'About', 'About WXMPlot', self.onAbout) # add all sub-menus, including user-added submenus = [('File', mfile), ('Image', mview), ('Contrast', mint), ('Smoothing', msmoo)] if self.user_menus is not None: submenus.extend(self.user_menus) submenus.append(('&Help', mhelp)) mbar = wx.MenuBar() for title, menu in submenus: mbar.Append(menu, title) self.SetMenuBar(mbar) self.Bind(wx.EVT_CLOSE,self.onExit) def onInterp(self, evt=None, name=None): if name not in Interp_List: name = Interp_List[0] for iframe in self.tomo_frame: iframe.panel.conf.interp = name iframe.panel.redraw() def onCursorMode(self, event=None, mode='zoom'): choice = self.zoom_mode.GetString(self.zoom_mode.GetSelection()) for iframe in self.tomo_frame: iframe.panel.cursor_mode = mode if event is not None: if choice.startswith('Pick Area'): iframe.panel.cursor_mode = 'lasso' elif choice.startswith('Show Line'): iframe.panel.cursor_mode = 'prof' def onProject(self, event=None, mode='y'): wid = event.GetId() if mode=='x': x = self.tomo_frame[0].panel.ydata y = self.tomo_frame[0].panel.conf.data.sum(axis=1) x = self.tomo_frame[1].panel.ydata y = self.tomo_frame[1].panel.conf.data.sum(axis=1) axname = 'horizontal' if x is None: x = np.arange(y.shape[0]) else: x = self.tomo_frame[0].panel.xdata y = self.tomo_frame[0].panel.conf.data.sum(axis=0) x = self.tomo_frame[1].panel.xdata y = self.tomo_frame[1].panel.conf.data.sum(axis=0) if x is None: x = np.arange(y.shape[0]) axname = 'vertical' title = '%s: sum along %s axis' % (self.GetTitle(), axname) pf = PlotFrame(title=title, parent=self, size=(500, 250)) colors = RGB_COLORS if len(y.shape) == 2 and y.shape[1] == 3: pf.plot(x, y[:,0], color=colors[0]) pf.oplot(x, y[:,1], color=colors[1]) pf.oplot(x, y[:,2], color=colors[2]) else: pf.plot(x, y) pf.Raise() pf.Show() def onFlip(self, event=None, mode=None): for iframe in self.tomo_frame: conf = iframe.panel.conf if mode == 'flip_lr': conf.flip_lr = not conf.flip_lr elif mode == 'flip_ud': conf.flip_ud = not conf.flip_ud elif mode == 'flip_orig': conf.flip_lr, conf.flip_ud = False, False elif mode == 'rot_cw': conf.rot = True iframe.panel.unzoom_all() def Build_ConfigPanel(self): '''config panel for left-hand-side of frame: RGB Maps''' csizer = wx.BoxSizer(wx.VERTICAL) lsty = wx.ALIGN_LEFT|wx.LEFT|wx.TOP|wx.EXPAND icol = 0 if self.config_mode == 'rgb': for iframe in self.tomo_frame: for ic,col in enumerate(RGB_COLORS): self.cmap_panels[icol] = ColorMapPanel(self.config_panel, iframe.panel, title='%s - %s: ' % (iframe.label,col.title()), color=ic, default=col, colormap_list=None) csizer.Add(self.cmap_panels[icol], 0, lsty, 2) csizer.Add(wx.StaticLine(self.config_panel, size=(100, 2), style=wx.LI_HORIZONTAL), 0, lsty, 2) icol += 1 else: for iframe in self.tomo_frame: self.cmap_panels[icol] = ColorMapPanel(self.config_panel, iframe.panel, title='%s: ' % iframe.label, default='gray', colormap_list=ColorMap_List) csizer.Add(self.cmap_panels[icol], 0, lsty, 1) csizer.Add(wx.StaticLine(self.config_panel, size=(100, 2), style=wx.LI_HORIZONTAL), 0, lsty, 2) icol += 1 cust = self.CustomConfig(self.config_panel, None, 0) if cust is not None: csizer.Add(cust, 0, lsty, 1) pack(self.config_panel, csizer) def clear_highlight_area(self): for iframe in self.tomo_frame: for area in iframe.panel.conf.highlight_areas: for w in area.collections + area.labelTexts: w.remove() iframe.panel.conf.highlight_areas = [] iframe.panel.redraw() def add_highlight_area(self, mask0, label=None, col=0): """add a highlighted area -- outline an arbitrarily shape -- as if drawn from a Lasso event. This takes a mask, which should be a boolean array of the same shape as the image. """ panel = None for iframe in self.tomo_frame: imap_size = iframe.map.shape[:2] for imask in (mask0,np.swapaxes(mask0,0,1)): if imap_size == imask.shape: panel = iframe.panel mask = imask if panel is not None: patch = mask * np.ones(mask.shape) * 0.9 cmap = panel.conf.cmap[col] area = panel.axes.contour(patch, cmap=cmap, levels=[0, 1]) panel.conf.highlight_areas.append(area) col = None if hasattr(cmap, '_lut'): rgb = [int(i*240)^255 for i in cmap._lut[0][:3]] col = '#%02x%02x%02x' % (rgb[0], rgb[1], rgb[2]) if label is not None: def fmt(*args, **kws): return label panel.axes.clabel(area, fontsize=9, fmt=fmt, colors=col, rightside_up=True) if col is not None: for l in area.collections: l.set_color(col) panel.canvas.draw() def CustomConfig(self, panel, sizer=None, irow=0): ''' override to add custom config panel items to bottom of config panel ''' labstyle = wx.ALIGN_LEFT|wx.LEFT|wx.TOP|wx.EXPAND if self.lasso_callback is None: zoom_opts = ('Zoom to Rectangle', 'Show Line Profile') else: zoom_opts = ('Zoom to Rectangle', 'Pick Area for XRM Spectra', 'Show Line Profile') if self.wxmplot_version > 0.921: cpanel = wx.Panel(panel) if sizer is None: sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(SimpleText(cpanel, label='Cursor Modes', style=labstyle), 0, labstyle, 3) self.zoom_mode = wx.RadioBox(cpanel, -1, '', wx.DefaultPosition, wx.DefaultSize, zoom_opts, 1, wx.RA_SPECIFY_COLS) self.zoom_mode.Bind(wx.EVT_RADIOBOX, self.onCursorMode) sizer.Add(self.zoom_mode, 1, labstyle, 4) pack(cpanel, sizer) return cpanel else: # support older versions of wxmplot, will be able to deprecate conf = self.tomo_frame[0].panel.conf # self.panel.conf lpanel = panel lsizer = sizer self.zoom_mode = wx.RadioBox(panel, -1, 'Cursor Mode:', wx.DefaultPosition, wx.DefaultSize, zoom_opts, 1, wx.RA_SPECIFY_COLS) self.zoom_mode.Bind(wx.EVT_RADIOBOX, self.onCursorMode) sizer.Add(self.zoom_mode, (irow, 0), (1, 4), labstyle, 3) def onContrastConfig(self, event=None): for iframe in self.tomo_frame: dlg = AutoContrastDialog(parent=self, conf=iframe.panel.conf) dlg.CenterOnScreen() val = dlg.ShowModal() if val == wx.ID_OK: pass dlg.Destroy() def onContourConfig(self, event=None): for icol,iframe in enumerate(self.tomo_frame): conf = iframe.panel.conf dlg = ContourDialog(parent=self, conf=conf) dlg.CenterOnScreen() val = dlg.ShowModal() if val == wx.ID_OK: pass dlg.Destroy() if conf.style != 'contour': return if self.config_mode == 'int': self.cmap_panels[icol].set_colormap() iframe.panel.axes.cla() iframe.panel.display(conf.data, x=iframe.panel.xdata, y = iframe.panel.ydata, xlabel=iframe.panel.xlab, ylabel=iframe.panel.ylab, contour_labels=conf.contour_labels, nlevels=conf.ncontour_levels, style='contour') iframe.panel.redraw() def onContourToggle(self, event=None): for icol,iframe in enumerate(self.tomo_frame): if len(iframe.panel.conf.data.shape) > 2: return conf = iframe.panel.conf conf.style = 'image' if event.IsChecked(): conf.style = 'contour' nlevels = int(conf.ncontour_levels) if self.config_mode == 'int': self.cmap_panels[0].set_colormap() iframe.panel.axes.cla() iframe.panel.display(conf.data, x=iframe.panel.xdata, y = iframe.panel.ydata, nlevels=nlevels, contour_labels=conf.contour_labels, xlabel=iframe.panel.xlab, ylabel=iframe.panel.ylab, style=conf.style) iframe.panel.redraw() def onTriColorBG(self, event=None): bgcol = {True:'white', False:'black'}[event.IsChecked()] icol = 0 for iframe in self.tomo_frame: conf = iframe.panel.conf if bgcol == conf.tricolor_bg: return conf.tricolor_bg = bgcol cmaps = colors = RGB_COLORS if bgcol.startswith('wh'): cmaps = ('Reds', 'Greens', 'Blues') for i in range(3): self.cmap_panels[icol].set_colormap(name=cmaps[i]) icol += 1 iframe.panel.redraw() def onLasso(self, data=None, selected=None, mask=None, **kws): ## orients mask correctly to match with raw data shape ## mkak 2018.01.24 mask = np.swapaxes(mask,0,1) if hasattr(self.lasso_callback , '__call__'): self.lasso_callback(data=data, selected=selected, mask=mask, **kws) def onDataChange(self, data, x=None, y=None, col='int', **kw): icol = 0 for iframe in self.tomo_frame: conf = iframe.panel.conf if len(data.shape) == 2: # intensity map imin, imax = data.min(), data.max() conf.int_lo[0] = imin conf.int_hi[0] = imax cpan = self.cmap_panels[0] cpan.cmap_lo.SetValue(imin) cpan.cmap_hi.SetValue(imax) cpan.imin_val.SetValue('%.4g' % imin) cpan.imax_val.SetValue('%.4g' % imax) cpan.imin_val.Enable() cpan.imax_val.Enable() else: for ix in range(3): imin, imax = data[:,:,ix].min(), data[:,:,ix].max() conf.int_lo[ix] = imin conf.int_hi[ix] = imax self.cmap_panels[icol].imin_val.SetValue('%.4g' % imin) self.cmap_panels[icol].imax_val.SetValue('%.4g' % imax) self.cmap_panels[icol].imin_val.Enable() self.cmap_panels[icol].imax_val.Enable() icol += 1 def onEnhanceContrast(self, event=None): '''change image contrast, using scikit-image exposure routines''' for iframe in self.tomo_frame: iframe.panel.conf.auto_contrast = event.IsChecked() self.set_contrast_levels() for iframe in self.tomo_frame: iframe.panel.redraw() def set_contrast_levels(self): '''enhance contrast levels, or use full data range according to value of iframe.panel.conf.auto_contrast ''' def set_panel_contrast(icol,conf,ix=-1): if ix < 0: ix = icol img = conf.data else: img = conf.data[:,:,ix] jmin = imin = img.min() jmax = imax = img.max() self.cmap_panels[icol].imin_val.SetValue('%.4g' % imin) self.cmap_panels[icol].imax_val.SetValue('%.4g' % imax) conf.int_lo[ix] = imin conf.int_hi[ix] = imax if conf.auto_contrast: jmin, jmax = np.percentile(img, [ conf.auto_contrast_level, 100.0-conf.auto_contrast_level]) if imax == imin: imax = imin + 0.5 conf.cmap_lo[ix] = xlo = (jmin-imin)*conf.cmap_range/(imax-imin) conf.cmap_hi[ix] = xhi = (jmax-imin)*conf.cmap_range/(imax-imin) self.cmap_panels[icol].cmap_hi.SetValue(xhi) self.cmap_panels[icol].cmap_lo.SetValue(xlo) str = 'Shown: [ %.4g : %.4g ]' % (jmin, jmax) self.cmap_panels[icol].islider_range.SetLabel(str) self.cmap_panels[icol].redraw_cmap() icol = 0 for iframe in self.tomo_frame: conf = iframe.panel.conf if len(conf.data.shape) == 2: # intensity map set_panel_contrast(icol,conf) icol += 1 elif len(conf.data.shape) == 3: # rgb map for ix in range(3): set_panel_contrast(icol,conf,ix=ix) icol += 1 iframe.panel.redraw() def onLogScale(self, event=None): for iframe in self.tomo_frame: iframe.panel.conf.log_scale = not iframe.panel.conf.log_scale iframe.panel.redraw() def onCMapSave(self, event=None, col='int'): '''save color table image''' file_choices = 'PNG (*.png)|*.png' ofile = 'Colormap.png' dlg = wx.FileDialog(self, message='Save Colormap as...', defaultDir=os.getcwd(), defaultFile=ofile, wildcard=file_choices, style=wx.FD_SAVE|wx.FD_CHANGE_DIR) if dlg.ShowModal() == wx.ID_OK: self.cmap_panels[0].cmap_canvas.print_figure(dlg.GetPath(), dpi=600) def save_figure(self,event=None, transparent=True, dpi=600): ''' save figure image to file''' for iframe in self.tomo_frame: if iframe.panel is not None: iframe.panel.save_figure(event=event, transparent=transparent, dpi=dpi)
class with_profile_mode: def prof_motion(self, event=None): if not event.inaxes or self.zoom_ini is None: return try: xmax, ymax = event.x, event.y except: return xmin, ymin, xd, yd = self.zoom_ini if event.xdata is not None: self.lastpoint[0] = event.xdata if event.ydata is not None: self.lastpoint[1] = event.ydata yoff = self.panel.canvas.figure.bbox.height ymin, ymax = yoff - ymin, yoff - ymax zdc = wx.ClientDC(self.panel.canvas) zdc.SetLogicalFunction(wx.XOR) zdc.SetBrush(wx.TRANSPARENT_BRUSH) zdc.SetPen(wx.Pen('White', 2, wx.SOLID)) zdc.ResetBoundingBox() if not is_wxPhoenix: zdc.BeginDrawing() # erase previous box if self.rbbox is not None: zdc.DrawLine(*self.rbbox) self.rbbox = (xmin, ymin, xmax, ymax) zdc.DrawLine(*self.rbbox) if not is_wxPhoenix: zdc.EndDrawing() def prof_leftdown(self, event=None): self.report_leftdown(event=event) if event.inaxes: # and len(self.map.shape) == 2: self.lastpoint = [None, None] self.zoom_ini = [event.x, event.y, event.xdata, event.ydata] def prof_leftup(self, event=None): # print("Profile Left up ", self.map.shape, self.rbbox) if self.rbbox is not None: zdc = wx.ClientDC(self.panel.canvas) zdc.SetLogicalFunction(wx.XOR) zdc.SetBrush(wx.TRANSPARENT_BRUSH) zdc.SetPen(wx.Pen('White', 2, wx.SOLID)) zdc.ResetBoundingBox() if not is_wxPhoenix: zdc.BeginDrawing() zdc.DrawLine(*self.rbbox) if not is_wxPhoenix: zdc.EndDrawing() self.rbbox = None if self.zoom_ini is None or self.lastpoint[0] is None: return x0 = int(self.zoom_ini[2]) x1 = int(self.lastpoint[0]) y0 = int(self.zoom_ini[3]) y1 = int(self.lastpoint[1]) dx, dy = abs(x1-x0), abs(y1-y0) self.lastpoint, self.zoom_ini = [None, None], None if dx < 2 and dy < 2: self.zoom_ini = None return outdat = [] if dy > dx: _y0 = min(int(y0), int(y1+0.5)) _y1 = max(int(y0), int(y1+0.5)) for iy in range(_y0, _y1): ix = int(x0 + (iy-int(y0))*(x1-x0)/(y1-y0)) outdat.append((ix, iy)) else: _x0 = min(int(x0), int(x1+0.5)) _x1 = max(int(x0), int(x1+0.5)) for ix in range(_x0, _x1): iy = int(y0 + (ix-int(x0))*(y1-y0)/(x1-x0)) outdat.append((ix, iy)) x, y, z = [], [], [] for ix, iy in outdat: x.append(ix) y.append(iy) z.append(self.panel.conf.data[iy, ix]) self.prof_dat = dy>dx, outdat if self.prof_plotter is not None: try: self.prof_plotter.Raise() self.prof_plotter.clear() except (AttributeError, PyDeadObjectError): self.prof_plotter = None if self.prof_plotter is None: self.prof_plotter = PlotFrame(self, title='Profile') self.prof_plotter.panel.report_leftdown = self.prof_report_coords xlabel, y2label = 'Pixel (x)', 'Pixel (y)' x = np.array(x) y = np.array(y) z = np.array(z) if dy > dx: x, y = y, x xlabel, y2label = y2label, xlabel self.prof_plotter.panel.clear() if len(self.title) < 1: self.title = os.path.split(self.xrmfile.filename)[1] opts = dict(linewidth=2, marker='+', markersize=3, show_legend=True, xlabel=xlabel) if isinstance(z[0], np.ndarray) and len(z[0]) == 3: # color plot rlab = self.subtitles['red'] glab = self.subtitles['green'] blab = self.subtitles['blue'] self.prof_plotter.plot(x, z[:, 0], title=self.title, color='red', zorder=20, xmin=min(x)-3, xmax=max(x)+3, ylabel='counts', label=rlab, **opts) self.prof_plotter.oplot(x, z[:, 1], title=self.title, color='darkgreen', zorder=20, xmin=min(x)-3, xmax=max(x)+3, ylabel='counts', label=glab, **opts) self.prof_plotter.oplot(x, z[:, 2], title=self.title, color='blue', zorder=20, xmin=min(x)-3, xmax=max(x)+3, ylabel='counts', label=blab, **opts) else: self.prof_plotter.plot(x, z, title=self.title, color='blue', zorder=20, xmin=min(x)-3, xmax=max(x)+3, ylabel='counts', label='counts', **opts) self.prof_plotter.oplot(x, y, y2label=y2label, label=y2label, zorder=3, side='right', color='black', **opts) self.prof_plotter.panel.unzoom_all() self.prof_plotter.Show() self.zoom_ini = None self.zoom_mode.SetSelection(0) self.panel.cursor_mode = 'zoom' def prof_report_coords(self, event=None): """override report leftdown for profile plotter""" if event is None: return ex, ey = event.x, event.y msg = '' plotpanel = self.prof_plotter.panel axes = plotpanel.fig.properties()['axes'][0] write = plotpanel.write_message try: x, y = axes.transData.inverted().transform((ex, ey)) except: x, y = event.xdata, event.ydata if x is None or y is None: return _point = 0, 0, 0, 0, 0 for ix, iy in self.prof_dat[1]: if (int(x) == ix and not self.prof_dat[0] or int(x) == iy and self.prof_dat[0]): _point = (ix, iy, self.panel.xdata[ix], self.panel.ydata[iy], self.panel.conf.data[iy, ix]) msg = "Pixel [%i, %i], X, OME = [%.4f mm, %.4f deg], Intensity= %g" % _point write(msg, panel=0)
class MapImageFrame(ImageFrame): """ MatPlotlib Image Display on a wx.Frame, using ImagePanel """ def __init__(self, parent=None, size=None, mode='intensity', lasso_callback=None, move_callback=None, save_callback=None, show_xsections=False, cursor_labels=None, output_title='Image', **kws): # instdb=None, inst_name=None, self.det = None self.xrmfile = None self.map = None self.move_callback = move_callback self.save_callback = save_callback ImageFrame.__init__(self, parent=parent, size=size, lasso_callback=lasso_callback, cursor_labels=cursor_labels, mode=mode, output_title=output_title, **kws) self.panel.add_cursor_mode('prof', motion = self.prof_motion, leftdown = self.prof_leftdown, leftup = self.prof_leftup) self.panel.report_leftdown = self.report_leftdown self.panel.report_motion = self.report_motion self.prof_plotter = None self.zoom_ini = None self.lastpoint = [None, None] self.this_point = None self.rbbox = None def display(self, map, det=None, xrmfile=None, xoff=0, yoff=0, **kws): self.xoff = xoff self.yoff = yoff self.det = det self.xrmfile = xrmfile self.map = map self.title = '' if 'title' in kws: self.title = kws['title'] ImageFrame.display(self, map, **kws) if 'x' in kws: self.panel.xdata = kws['x'] if 'y' in kws: self.panel.ydata = kws['y'] if self.panel.conf.auto_contrast: self.set_contrast_levels() def prof_motion(self, event=None): if not event.inaxes or self.zoom_ini is None: return try: xmax, ymax = event.x, event.y except: return xmin, ymin, xd, yd = self.zoom_ini if event.xdata is not None: self.lastpoint[0] = event.xdata if event.ydata is not None: self.lastpoint[1] = event.ydata yoff = self.panel.canvas.figure.bbox.height ymin, ymax = yoff - ymin, yoff - ymax zdc = wx.ClientDC(self.panel.canvas) zdc.SetLogicalFunction(wx.XOR) zdc.SetBrush(wx.TRANSPARENT_BRUSH) zdc.SetPen(wx.Pen('White', 2, wx.SOLID)) zdc.ResetBoundingBox() if not is_wxPhoenix: zdc.BeginDrawing() # erase previous box if self.rbbox is not None: zdc.DrawLine(*self.rbbox) self.rbbox = (xmin, ymin, xmax, ymax) zdc.DrawLine(*self.rbbox) if not is_wxPhoenix: zdc.EndDrawing() def prof_leftdown(self, event=None): self.report_leftdown(event=event) if event.inaxes and len(self.map.shape) == 2: self.lastpoint = [None, None] self.zoom_ini = [event.x, event.y, event.xdata, event.ydata] def prof_leftup(self, event=None): if len(self.map.shape) != 2: return if self.rbbox is not None: zdc = wx.ClientDC(self.panel.canvas) zdc.SetLogicalFunction(wx.XOR) zdc.SetBrush(wx.TRANSPARENT_BRUSH) zdc.SetPen(wx.Pen('White', 2, wx.SOLID)) zdc.ResetBoundingBox() if not is_wxPhoenix: zdc.BeginDrawing() zdc.DrawLine(*self.rbbox) if not is_wxPhoenix: zdc.EndDrawing() self.rbbox = None if self.zoom_ini is None or self.lastpoint[0] is None: return x0 = int(self.zoom_ini[2]) x1 = int(self.lastpoint[0]) y0 = int(self.zoom_ini[3]) y1 = int(self.lastpoint[1]) dx, dy = abs(x1-x0), abs(y1-y0) self.lastpoint, self.zoom_ini = [None, None], None if dx < 2 and dy < 2: self.zoom_ini = None return outdat = [] if dy > dx: _y0 = min(int(y0), int(y1+0.5)) _y1 = max(int(y0), int(y1+0.5)) for iy in range(_y0, _y1): ix = int(x0 + (iy-int(y0))*(x1-x0)/(y1-y0)) outdat.append((ix, iy)) else: _x0 = min(int(x0), int(x1+0.5)) _x1 = max(int(x0), int(x1+0.5)) for ix in range(_x0, _x1): iy = int(y0 + (ix-int(x0))*(y1-y0)/(x1-x0)) outdat.append((ix, iy)) x, y, z = [], [], [] for ix, iy in outdat: x.append(ix) y.append(iy) z.append(self.panel.conf.data[iy,ix]) self.prof_dat = dy>dx, outdat if self.prof_plotter is not None: try: self.prof_plotter.Raise() self.prof_plotter.clear() except (AttributeError, PyDeadObjectError): self.prof_plotter = None if self.prof_plotter is None: self.prof_plotter = PlotFrame(self, title='Profile') self.prof_plotter.panel.report_leftdown = self.prof_report_coords xlabel, y2label = 'Pixel (x)', 'Pixel (y)' if dy > dx: x, y = y, x xlabel, y2label = y2label, xlabel self.prof_plotter.panel.clear() # reset_config() if len(self.title) < 1: self.title = os.path.split(self.xrmfile.filename)[1] opts = dict(linewidth=2, marker='+', markersize=3, show_legend=True, xlabel=xlabel) self.prof_plotter.plot(x, z, title=self.title, color='blue', zorder=20, xmin=min(x)-3, xmax=max(x)+3, ylabel='counts', label='counts', **opts) self.prof_plotter.oplot(x, y, y2label=y2label, label=y2label, zorder=3, side='right', color='#771111', **opts) self.prof_plotter.panel.unzoom_all() self.prof_plotter.Show() self.zoom_ini = None self.zoom_mode.SetSelection(0) self.panel.cursor_mode = 'zoom' def prof_report_coords(self, event=None): """override report leftdown for profile plotter""" if event is None: return ex, ey = event.x, event.y msg = '' plotpanel = self.prof_plotter.panel axes = plotpanel.fig.get_axes()[0] write = plotpanel.write_message try: x, y = axes.transData.inverted().transform((ex, ey)) except: x, y = event.xdata, event.ydata if x is None or y is None: return _point = 0, 0, 0, 0, 0 for ix, iy in self.prof_dat[1]: if (int(x) == ix and not self.prof_dat[0] or int(x) == iy and self.prof_dat[0]): _point = (ix, iy, self.panel.xdata[ix], self.panel.ydata[iy], self.panel.conf.data[iy, ix]) msg = "Pixel [%i, %i], X, Y = [%.4f, %.4f], Intensity= %g" % _point write(msg, panel=0) def onCursorMode(self, event=None, mode='zoom'): self.panel.cursor_mode = mode if event is not None: if 1 == event.GetInt(): self.panel.cursor_mode = 'lasso' elif 2 == event.GetInt(): self.panel.cursor_mode = 'prof' def report_leftdown(self, event=None): if event is None: return if event.xdata is None or event.ydata is None: return ix, iy = int(round(event.xdata)), int(round(event.ydata)) conf = self.panel.conf if conf.flip_ud: iy = conf.data.shape[0] - iy if conf.flip_lr: ix = conf.data.shape[1] - ix self.this_point = None msg = '' if (ix >= 0 and ix < conf.data.shape[1] and iy >= 0 and iy < conf.data.shape[0]): pos = '' pan = self.panel # print( 'has xdata? ', pan.xdata is not None, pan.ydata is not None) labs, vals = [], [] if pan.xdata is not None: labs.append(pan.xlab) vals.append(pan.xdata[ix]) if pan.ydata is not None: labs.append(pan.ylab) vals.append(pan.ydata[iy]) pos = ', '.join(labs) vals =', '.join(['%.4g' % v for v in vals]) pos = '%s = [%s]' % (pos, vals) dval = conf.data[iy, ix] if len(pan.data_shape) == 3: dval = "%.4g, %.4g, %.4g" % tuple(dval) else: dval = "%.4g" % dval if pan.xdata is not None and pan.ydata is not None: self.this_point = (ix, iy) msg = "Pixel [%i, %i], %s, Intensity=%s " % (ix, iy, pos, dval) self.panel.write_message(msg, panel=0) def report_motion(self, event=None): return def onLasso(self, data=None, selected=None, mask=None, **kws): if hasattr(self.lasso_callback , '__call__'): self.lasso_callback(data=data, selected=selected, mask=mask, xoff=self.xoff, yoff=self.yoff, det=self.det, xrmfile=self.xrmfile, **kws) self.zoom_mode.SetSelection(0) self.panel.cursor_mode = 'zoom' def CustomConfig(self, panel, sizer, irow): """config panel for left-hand-side of frame""" conf = self.panel.conf lpanel = panel lsizer = sizer labstyle = wx.ALIGN_LEFT|wx.LEFT|wx.TOP|wx.EXPAND self.zoom_mode = wx.RadioBox(panel, -1, "Cursor Mode:", wx.DefaultPosition, wx.DefaultSize, ('Zoom to Rectangle', 'Pick Area for XRF Spectrum', 'Show Line Profile'), 1, wx.RA_SPECIFY_COLS) self.zoom_mode.Bind(wx.EVT_RADIOBOX, self.onCursorMode) sizer.Add(self.zoom_mode, (irow, 0), (1, 4), labstyle, 3) if self.save_callback is not None: self.pos_name = wx.TextCtrl(panel, -1, '', size=(175, -1), style=wx.TE_PROCESS_ENTER) self.pos_name.Bind(wx.EVT_TEXT_ENTER, self.onSavePixel) label = SimpleText(panel, label='Save Position:', size=(-1, -1)) # sbutton = Button(panel, 'Save Position', size=(100, -1), # action=self.onSavePixel) sizer.Add(label, (irow+1, 0), (1, 2), labstyle, 3) sizer.Add(self.pos_name, (irow+1, 2), (1, 2), labstyle, 3) # sizer.Add(sbutton, (irow+2, 0), (1, 2), labstyle, 3) if self.move_callback is not None: mbutton = Button(panel, 'Move to Position', size=(100, -1), action=self.onMoveToPixel) irow = irow + 2 sizer.Add(mbutton, (irow+1, 0), (1, 2), labstyle, 3) def onMoveToPixel(self, event=None): if self.this_point is not None and self.move_callback is not None: p1 = float(self.panel.xdata[self.this_point[0]]) p2 = float(self.panel.ydata[self.this_point[1]]) self.move_callback(p1, p2) def onSavePixel(self, event=None): if self.this_point is not None and self.save_callback is not None: name = str(event.GetString().strip()) # name = str(self.pos_name.GetValue().strip()) ix, iy = self.this_point x = float(self.panel.xdata[int(ix)]) y = float(self.panel.ydata[int(iy)]) self.save_callback(name, ix, iy, x=x, y=y, title=self.title, datafile=self.xrmfile)
class ScanViewerFrame(wx.Frame): _about = """Scan 2D Plotter Matt Newville <newville @ cars.uchicago.edu> """ def __init__(self, _larch=None, **kws): wx.Frame.__init__(self, None, -1, style=FRAMESTYLE) self.file_groups = {} self.file_paths = [] title = "Column Data File Viewer" self.larch = _larch self.larch_buffer = None self.subframes = {} self.plotframe = None self.groupname = None self.SetTitle(title) self.SetSize((850, 650)) self.SetFont(Font(10)) self.config = {'chdir_on_fileopen': True} 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) read_workdir('scanviewer.dat') def createMainPanel(self): splitter = wx.SplitterWindow(self, style=wx.SP_LIVE_UPDATE) splitter.SetMinimumPaneSize(225) self.filelist = wx.ListBox(splitter) self.filelist.SetBackgroundColour(wx.Colour(255, 255, 255)) self.filelist.Bind(wx.EVT_LISTBOX, self.ShowFile) self.detailspanel = self.createDetailsPanel(splitter) splitter.SplitVertically(self.filelist, self.detailspanel, 1) wx.CallAfter(self.init_larch) def createDetailsPanel(self, parent): mainpanel = wx.Panel(parent) mainsizer = wx.BoxSizer(wx.VERTICAL) panel = wx.Panel(mainpanel) sizer = wx.GridBagSizer(8, 7) self.title = SimpleText(panel, 'initializing...') ir = 0 sizer.Add(self.title, (ir, 0), (1, 6), LCEN, 2) # x-axis self.xarr = Choice(panel, choices=[], action=self.onColumnChoices, size=(120, -1)) self.xop = Choice(panel, choices=('', 'log'), action=self.onColumnChoices, size=(90, -1)) ir += 1 sizer.Add(SimpleText(panel, 'X = '), (ir, 0), (1, 1), CEN, 0) sizer.Add(self.xop, (ir, 1), (1, 1), CEN, 0) sizer.Add(SimpleText(panel, '('), (ir, 2), (1, 1), CEN, 0) sizer.Add(self.xarr, (ir, 3), (1, 1), RCEN, 0) sizer.Add(SimpleText(panel, ')'), (ir, 4), (1, 1), CEN, 0) self.yops = [] self.yarr = [] opts = { 'choices': [], 'size': (120, -1), 'action': self.onColumnChoices } for i in range(3): self.yarr.append(Choice(panel, **opts)) for opts, sel, siz in ((PRE_OPS, 0, 90), (ARR_OPS, 3, 50), (ARR_OPS, 3, 50)): w1 = Choice(panel, choices=opts, action=self.onColumnChoices, size=(siz, -1)) w1.SetSelection(sel) self.yops.append(w1) ir += 1 label = 'Y = ' sizer.Add(SimpleText(panel, label), (ir, 0), (1, 1), CEN, 0) sizer.Add(self.yops[0], (ir, 1), (1, 1), CEN, 0) sizer.Add(SimpleText(panel, '[('), (ir, 2), (1, 1), CEN, 0) sizer.Add(self.yarr[0], (ir, 3), (1, 1), CEN, 0) sizer.Add(self.yops[1], (ir, 4), (1, 1), CEN, 0) sizer.Add(self.yarr[1], (ir, 5), (1, 1), CEN, 0) sizer.Add(SimpleText(panel, ')'), (ir, 6), (1, 1), LCEN, 0) ir += 1 sizer.Add(self.yops[2], (ir, 4), (1, 1), CEN, 0) sizer.Add(self.yarr[2], (ir, 5), (1, 1), CEN, 0) sizer.Add(SimpleText(panel, ']'), (ir, 6), (1, 1), LCEN, 0) self.use_deriv = Check(panel, default=False, label='Use Derivative?', action=self.onColumnChoices) self.dtcorr = Check(panel, default=True, label='correct deadtime?', action=self.onColumnChoices) ir += 1 sizer.Add(self.use_deriv, (ir, 0), (1, 3), LCEN, 0) sizer.Add(self.dtcorr, (ir, 3), (1, 3), LCEN, 0) pack(panel, sizer) self.nb = flat_nb.FlatNotebook(mainpanel, -1, agwStyle=FNB_STYLE) self.nb.SetTabAreaColour(wx.Colour(248, 248, 240)) self.nb.SetActiveTabColour(wx.Colour(254, 254, 195)) self.nb.SetNonActiveTabTextColour(wx.Colour(40, 40, 180)) self.nb.SetActiveTabTextColour(wx.Colour(80, 0, 0)) self.xas_panel = self.CreateXASPanel(self.nb) # mainpanel) self.fit_panel = self.CreateFitPanel(self.nb) # mainpanel) self.nb.AddPage(self.fit_panel, ' General Analysis ', True) self.nb.AddPage(self.xas_panel, ' XAS Processing ', True) mainsizer.Add(panel, 0, LCEN | wx.EXPAND, 2) btnbox = wx.Panel(mainpanel) btnsizer = wx.BoxSizer(wx.HORIZONTAL) for ttl, opt in (('New Plot', 'new'), ('Over Plot (left)', 'left'), ('Over Plot (right)', 'right')): btnsizer.Add( Button(btnbox, ttl, size=(135, -1), action=partial(self.onPlot, opt=opt)), LCEN, 1) pack(btnbox, btnsizer) mainsizer.Add(btnbox, 0, LCEN, 2) mainsizer.Add(self.nb, 1, LCEN | wx.EXPAND, 2) pack(mainpanel, mainsizer) return mainpanel def CreateFitPanel(self, parent): panel = wx.Panel(parent) tpan = wx.Panel(panel) self.fit_model = Choice(tpan, size=(100, -1), choices=('Gaussian', 'Lorentzian', 'Voigt', 'Linear', 'Quadratic', 'Step', 'Rectangle', 'Exponential')) self.fit_bkg = Choice(tpan, size=(100, -1), choices=('None', 'constant', 'linear', 'quadratic')) self.fit_step = Choice(tpan, size=(100, -1), choices=('linear', 'error function', 'arctan')) tsizer = wx.GridBagSizer(10, 4) tsizer.Add(SimpleText(tpan, 'Fit Model: '), (0, 0), (1, 1), LCEN) tsizer.Add(self.fit_model, (0, 1), (1, 1), LCEN) tsizer.Add(SimpleText(tpan, 'Background: '), (0, 2), (1, 1), LCEN) tsizer.Add(self.fit_bkg, (0, 3), (1, 1), LCEN) tsizer.Add( Button(tpan, 'Show Fit', size=(100, -1), action=self.onFitPeak), (1, 1), (1, 1), LCEN) tsizer.Add(SimpleText(tpan, 'Step Form: '), (1, 2), (1, 1), LCEN) tsizer.Add(self.fit_step, (1, 3), (1, 1), LCEN) pack(tpan, tsizer) self.fit_report = RichTextCtrl(panel, size=(525, 250), style=wx.VSCROLL | wx.NO_BORDER) self.fit_report.SetEditable(False) self.fit_report.SetFont(Font(9)) sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(tpan, 0, wx.GROW | wx.ALL, 2) sizer.Add(self.fit_report, 1, LCEN | wx.GROW, 2) pack(panel, sizer) return panel def InitializeXASPanel(self): if self.groupname is None: lgroup = None lgroup = getattr(self.larch.symtable, self.groupname) self.xas_e0.SetValue(getattr(lgroup, 'e0', 0)) self.xas_step.SetValue(getattr(lgroup, 'edge_step', 0)) self.xas_pre1.SetValue(getattr(lgroup, 'pre1', -200)) self.xas_pre2.SetValue(getattr(lgroup, 'pre2', -30)) self.xas_nor1.SetValue(getattr(lgroup, 'norm1', 50)) self.xas_nor2.SetValue(getattr(lgroup, 'norm2', -10)) self.xas_vict.SetSelection(getattr(lgroup, 'nvict', 1)) self.xas_nnor.SetSelection(getattr(lgroup, 'nnorm', 2)) def CreateXASPanel(self, parent): p = panel = wx.Panel(parent) self.xas_autoe0 = Check(panel, default=True, label='auto?') self.xas_showe0 = Check(panel, default=True, label='show?') self.xas_autostep = Check(panel, default=True, label='auto?') self.xas_op = Choice( panel, size=(225, -1), choices=('Raw Data', 'Normalized', 'Derivative', 'Normalized + Derivative', 'Pre-edge subtracted', 'Raw Data With Pre-edge/Post-edge Curves'), action=self.onXASChoice) opts = { 'size': (95, -1), 'precision': 3 } # , 'action': self.onXASChoice} self.xas_e0 = FloatCtrl(panel, value=0, **opts) self.xas_step = FloatCtrl(panel, value=0, **opts) opts['precision'] = 1 self.xas_pre1 = FloatCtrl(panel, value=-200, **opts) self.xas_pre2 = FloatCtrl(panel, value=-30, **opts) self.xas_nor1 = FloatCtrl(panel, value=50, **opts) self.xas_nor2 = FloatCtrl(panel, value=-50, **opts) opts = { 'size': (50, -1), 'choices': ('0', '1', '2', '3'), 'action': self.onXASChoice } self.xas_vict = Choice(panel, **opts) self.xas_nnor = Choice(panel, **opts) 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, 3), 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_showe0, (1, 4), (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 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) try: lgroup = getattr(self.larch.symtable, gname) x = lgroup._xdat_ y = lgroup._ydat_ except AttributeError: self.write_message('need data to fit!') return if step.startswith('error'): step = 'erf' elif step.startswith('arctan'): step = 'atan' pgroup = fit_peak(x, y, model, background=bkg, step=step, _larch=self.larch) dtext = '\n'.join(dtext) dtext = '%s\n%s\n' % ( dtext, fit_report( pgroup.params, min_correl=0.25, _larch=self.larch)) self.fit_report.SetEditable(True) self.fit_report.SetValue(dtext) self.fit_report.SetEditable(False) popts1 = dict(style='solid', linewidth=3, marker='None', markersize=4) popts2 = dict(style='short dashed', linewidth=2, marker='None', markersize=4) lgroup.plot_yarrays = [(lgroup._ydat_, popts1, lgroup.plot_ylabel)] if bkg is None: lgroup._fit = pgroup.fit[:] lgroup.plot_yarrays.append((lgroup._fit, popts2, 'fit')) else: lgroup._fit = pgroup.fit[:] lgroup._fit_bgr = pgroup.bkg[:] lgroup.plot_yarrays.append((lgroup._fit, popts2, 'fit')) lgroup.plot_yarrays.append((lgroup._fit_bgr, popts2, 'background')) self.onPlot() def xas_process(self, gname, new_mu=False, **kws): """ process (pre-edge/normalize) XAS data from XAS form, overwriting larch group '_y1_' attribute to be plotted """ out = self.xas_op.GetStringSelection().lower() # raw, pre, norm, flat preopts = {'group': gname, 'e0': None} lgroup = getattr(self.larch.symtable, gname) dtcorr = self.dtcorr.IsChecked() if new_mu: try: del lgroup.e0, lgroup.edge_step except: pass if not self.xas_autoe0.IsChecked(): e0 = self.xas_e0.GetValue() if e0 < max(lgroup._xdat_) and e0 > min(lgroup._xdat_): preopts['e0'] = e0 if not self.xas_autostep.IsChecked(): preopts['step'] = self.xas_step.GetValue() dt = debugtime() 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['nvict'] = self.xas_vict.GetSelection() preopts['nnorm'] = self.xas_nnor.GetSelection() preopts['make_flat'] = 'False' preopts['group'] = gname preopts = ", ".join(["%s=%s" % (k, v) for k, v in preopts.items()]) preedge_cmd = "pre_edge(%s._xdat_, %s._ydat_, %s)" self.larch(preedge_cmd % (gname, gname, preopts)) if self.xas_autoe0.IsChecked(): self.xas_e0.SetValue(lgroup.e0) if self.xas_autostep.IsChecked(): self.xas_step.SetValue(lgroup.edge_step) details_group = lgroup try: details_group = lgroup.pre_edge_details except: pass self.xas_pre1.SetValue(details_group.pre1) self.xas_pre2.SetValue(details_group.pre2) self.xas_nor1.SetValue(details_group.norm1) self.xas_nor2.SetValue(details_group.norm2) popts1 = dict(style='solid', linewidth=3, marker='None', markersize=4) popts2 = dict(style='short dashed', linewidth=2, zorder=-5, marker='None', markersize=4) poptsd = dict(style='solid', linewidth=2, zorder=-5, side='right', y2label='derivative', marker='None', markersize=4) lgroup.plot_yarrays = [(lgroup._ydat_, popts1, lgroup.plot_ylabel)] y4e0 = lgroup._ydat_ if out.startswith('raw data with'): lgroup.plot_yarrays = [(lgroup._ydat_, popts1, lgroup.plot_ylabel), (lgroup.pre_edge, popts2, 'pre edge'), (lgroup.post_edge, popts2, 'post edge')] elif out.startswith('pre'): self.larch('%s.pre_edge_sub = %s.norm * %s.edge_step' % (gname, gname, gname)) lgroup.plot_yarrays = [(lgroup.pre_edge_sub, popts1, 'pre edge subtracted XAFS')] y4e0 = lgroup.pre_edge_sub elif 'norm' in out and 'deriv' in out: lgroup.plot_yarrays = [(lgroup.norm, popts1, 'normalized XAFS'), (lgroup.dmude, poptsd, 'derivative')] y4e0 = lgroup.norm elif out.startswith('norm'): lgroup.plot_yarrays = [(lgroup.norm, popts1, 'normalized XAFS')] y4e0 = lgroup.norm elif out.startswith('deriv'): lgroup.plot_yarrays = [(lgroup.dmude, popts1, 'derivative')] y4e0 = lgroup.dmude lgroup.plot_ymarkers = [] if self.xas_showe0.IsChecked(): ie0 = index_of(lgroup._xdat_, lgroup.e0) lgroup.plot_ymarkers = [(lgroup.e0, y4e0[ie0], {'label': 'e0'})] return def init_larch(self): t0 = time.time() if self.larch is None: self.larch = Interpreter() self.larch.symtable.set_symbol('_sys.wx.wxapp', wx.GetApp()) self.larch.symtable.set_symbol('_sys.wx.parent', self) self.SetStatusText('ready') self.datagroups = self.larch.symtable self.title.SetLabel('') def write_message(self, s, panel=0): """write a message to the Status Bar""" self.SetStatusText(s, panel) def get_data(self, group, arrayname, correct=False): if hasattr(group, 'get_data'): return group.get_data(arrayname, correct=correct) return getattr(group, arrayname, None) def onXASChoice(self, evt=None, **kws): if self.groupname is None: return self.xas_process(self.groupname, **kws) self.onPlot() def onColumnChoices(self, evt=None): """column selections changed .. recalculate _xdat_ and _ydat_ arrays for this larch group""" dtcorr = self.dtcorr.IsChecked() use_deriv = self.use_deriv.IsChecked() ix = self.xarr.GetSelection() x = self.xarr.GetStringSelection() xop = self.xop.GetStringSelection() op1 = self.yops[0].GetStringSelection() op2 = self.yops[1].GetStringSelection() op3 = self.yops[2].GetStringSelection() y1 = self.yarr[0].GetStringSelection() y2 = self.yarr[1].GetStringSelection() y3 = self.yarr[2].GetStringSelection() array_sel = { 'xop': xop, 'xarr': x, 'op1': op1, 'op2': op2, 'op3': op3, 'y1': y1, 'y2': y2, 'y3': y3, 'dtcorr': dtcorr, 'use_deriv': use_deriv } try: gname = self.groupname lgroup = getattr(self.larch.symtable, gname) except: gname = SCANGROUP lgroup = getattr(self.larch.symtable, gname) xlabel = x try: xunits = lgroup.array_units[ix] except: xunits = '' if xop != '': xlabel = "%s(%s)" % (xop, xlabel) if xunits != '': xlabel = '%s (%s)' % (xlabel, xunits) ylabel = y1 if y2 == '': y2, op2 = '1.0', '*' else: ylabel = "%s%s%s" % (ylabel, op2, y2) if y3 == '': y3, op3 = '1.0', '*' else: ylabel = "(%s)%s%s" % (ylabel, op3, y3) if op1 != '': ylabel = "%s(%s)" % (op1, ylabel) if y1 in ('0.0', '1.0'): y1 = float(yl1) else: y1 = self.get_data(lgroup, y1, correct=dtcorr) if y2 in ('0.0', '1.0'): y2 = float(y2) if op2 == '/': y2 = 1.0 else: y2 = self.get_data(lgroup, y2, correct=dtcorr) if y3 in ('0.0', '1.0'): y3 = float(y3) if op3 == '/': y3 = 1.0 else: y3 = self.get_data(lgroup, y3, correct=dtcorr) if x not in ('0', '1'): x = self.get_data(lgroup, x) lgroup._x = x lgroup._y1 = y1 lgroup._y2 = y2 lgroup._y3 = y3 self.larch("%s._xdat_ = %s(%s._x)" % (gname, xop, gname)) try: yexpr = "%s._ydat_ = %s((%s._y1 %s %s._y2) %s %s._y3)" % ( gname, op1, gname, op2, gname, op3, gname) self.larch(yexpr) except RuntimeWarning: self.larch("%s._ydat_ = %s._y1") try: if use_deriv: d_calc = "%s._ydat_ = gradient(%s._ydat_)/gradient(%s._xdat_)" self.larch(d_calc % (gname, gname, gname)) except: pass try: npts = min(len(lgroup._xdat_), len(lgroup._ydat_)) except AttributeError: print('Error calculating arrays (npts not correct)') return del lgroup._x, lgroup._y1, lgroup._y2, lgroup._y3 lgroup.array_sel = array_sel lgroup.plot_xlabel = xlabel lgroup.plot_ylabel = ylabel lgroup._xdat_ = np.array(lgroup._xdat_[:npts]) lgroup._ydat_ = np.array(lgroup._ydat_[:npts]) if (self.nb.GetCurrentPage() == self.xas_panel): self.xas_process(self.groupname, new_mu=True) else: lgroup.plot_yarrays = [(lgroup._ydat_, {}, None)] def onPlot(self, evt=None, opt='new', npts=None, reprocess=False): try: self.plotframe.Show() except: # wx.PyDeadObjectError self.plotframe = PlotFrame(None, size=(650, 400)) self.plotframe.Show() self.plotpanel = self.plotframe.panel if reprocess: if (self.nb.GetCurrentPage() == self.xas_panel): self.xas_process(self.groupname, new_mu=True) side = 'left' update = False plotcmd = self.plotpanel.plot if opt in ('left', 'right'): side = opt plotcmd = self.plotpanel.oplot elif opt == 'update' and npts > 4: plotcmd = self.plotpanel.update_line update = True if 'new' in opt: self.plotpanel.clear() popts = {'side': side} try: gname = self.groupname lgroup = getattr(self.larch.symtable, gname) except: gname = SCANGROUP lgroup = getattr(self.larch.symtable, gname) return if not hasattr(lgroup, '_xdat_'): self.onColumnChoices() lgroup._xdat_ = np.array(lgroup._xdat_[:npts]) plot_yarrays = [(lgroup._ydat_, {}, None)] if hasattr(lgroup, 'plot_yarrays'): plot_yarrays = lgroup.plot_yarrays #for yarr in plot_yarrays: # yarr = np.array(yarr[:npts]) path, fname = os.path.split(lgroup.filename) popts['label'] = "%s: %s" % (fname, lgroup.plot_ylabel) if side == 'right': popts['y2label'] = lgroup.plot_ylabel else: popts['ylabel'] = lgroup.plot_ylabel if plotcmd == self.plotpanel.plot: popts['title'] = fname if update: self.plotpanel.set_xlabel(lgroup.plot_xlabel) self.plotpanel.set_ylabel(lgroup.plot_ylabel) for itrace, yarr, label in enumerate(plot_yarrays): plotcmd(itrace, lgroup._xdat_, yarr[0], draw=True, update_limits=((npts < 5) or (npts % 5 == 0)), **yarr[1]) self.plotpanel.set_xylims( (min(lgroup._xdat_), max(lgroup._xdat_), min(yarr), max(yarr))) else: for yarr in plot_yarrays: popts.update(yarr[1]) if yarr[2] is not None: popts['label'] = yarr[2] plotcmd(lgroup._xdat_, yarr[0], **popts) plotcmd = self.plotpanel.oplot if hasattr(lgroup, 'plot_ymarkers'): for x, y, opts in lgroup.plot_ymarkers: popts = {'marker': 'o', 'markersize': 4} popts.update(opts) self.plotpanel.oplot([x], [y], **popts) self.plotpanel.canvas.draw() def onShowLarchBuffer(self, evt=None): if self.larch_buffer is None: self.larch_buffer = larchframe.LarchFrame(_larch=self.larch) self.larch_buffer.Show() self.larch_buffer.Raise() def ShowFile(self, evt=None, groupname=None, **kws): if groupname is None and evt is not None: fpath = self.file_paths[evt.GetInt()] groupname = self.file_groups[fpath] if not hasattr(self.datagroups, groupname): print('Error reading file ', groupname) return self.groupname = groupname self.lgroup = getattr(self.datagroups, groupname, None) if groupname == SCANGROUP: self.lgroup.filename = filename elif self.lgroup is not None: if hasattr(self.lgroup, 'array_labels'): array_labels = self.lgroup.array_labels[:] elif hasattr(self.lgroup, 'column_labels'): array_labels = self.lgroup.column_labels[:] else: array_labels = [] for attr in dir(self.lgroup): if isinstance(getattr(self.lgroup, attr), np.ndarray): array_labels.append(attr) self.lgroup.array_labels = array_labels self.set_array_labels() if hasattr(self.lgroup, 'array_sel'): sel = self.lgroup.array_sel try: self.xarr.SetStringSelection(sel['xarr']) self.xop.SetStringSelection(sel['xop']) self.yops[0].SetStringSelection(sel['op1']) self.yops[1].SetStringSelection(sel['op2']) self.yops[2].SetStringSelection(sel['op3']) self.yarr[0].SetStringSelection(sel['y1']) self.yarr[1].SetStringSelection(sel['y2']) self.yarr[2].SetStringSelection(sel['y3']) self.dtcorr.SetValue({True: 1, False: 0}[sel['dtcorr']]) self.use_deriv.SetValue({ True: 1, False: 0 }[sel['use_deriv']]) except: pass def set_array_labels(self, labels=None): """set choices for array dropdowns from array labels""" array_labels = self.lgroup.array_labels xcols = array_labels[:] ycols = array_labels[:] y2cols = array_labels[:] + ['1.0', '0.0', ''] ncols = len(xcols) self.title.SetLabel(self.lgroup.filename) _xarr = self.xarr.GetStringSelection() if len(_xarr) < 1 or _xarr not in xcols: _xarr = xcols[0] _yarr = [[], [], []] for j in range(3): _yarr[j] = self.yarr[j].GetStringSelection() if _yarr[j] not in ycols: _yarr[j] = '' self.xarr.SetItems(xcols) self.xarr.SetStringSelection(_xarr) for j in range(3): if j == 0: self.yarr[j].SetItems(ycols) if _yarr[j] in ycols and len(_yarr[j]) > 0: self.yarr[j].SetStringSelection(_yarr[j]) elif ycols[0] == _xarr and len(ycols) > 1: self.yarr[j].SetStringSelection(ycols[1]) else: self.yarr[j].SetItems(y2cols) self.yarr[j].SetStringSelection(_yarr[j]) inb = 0 for colname in xcols: if 'energ' in colname.lower(): inb = 1 self.nb.SetSelection(inb) if inb == 1: self.InitializeXASPanel() def createMenus(self): # ppnl = self.plotpanel self.menubar = wx.MenuBar() # fmenu = wx.Menu() MenuItem(self, fmenu, "&Open Data File\tCtrl+O", "Read Scan File", self.onReadScan) MenuItem(self, fmenu, "Show Larch Buffer", "Show Larch Programming Buffer", self.onShowLarchBuffer) fmenu.AppendSeparator() MenuItem(self, fmenu, "&Quit\tCtrl+Q", "Quit program", self.onClose) self.menubar.Append(fmenu, "&File") omenu = wx.Menu() MenuItem(self, omenu, "Edit Column Labels\tCtrl+E", "Edit Column Labels", self.onEditColumnLabels) self.menubar.Append(omenu, "Options") # fmenu.AppendSeparator() # MenuItem(self, fmenu, "&Copy\tCtrl+C", # "Copy Figure to Clipboard", self.onClipboard) # MenuItem(self, fmenu, "&Save\tCtrl+S", "Save Figure", self.onSaveFig) # MenuItem(self, fmenu, "&Print\tCtrl+P", "Print Figure", self.onPrint) # MenuItem(self, fmenu, "Page Setup", "Print Page Setup", self.onPrintSetup) # MenuItem(self, fmenu, "Preview", "Print Preview", self.onPrintPreview) # #MenuItem(self, pmenu, "Unzoom\tCtrl+Z", "Unzoom Plot", self.onUnzoom) ##pmenu.AppendSeparator() #MenuItem(self, pmenu, "Toggle Legend\tCtrl+L", # "Toggle Legend on Plot", self.onToggleLegend) #MenuItem(self, pmenu, "Toggle Grid\tCtrl+G", # "Toggle Grid on Plot", self.onToggleGrid) # self.menubar.Append(pmenu, "Plot Options") self.SetMenuBar(self.menubar) self.Bind(wx.EVT_CLOSE, self.onClose) 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): save_workdir('scanviewer.dat') try: self.plotframe.Destroy() except: pass if self.larch_buffer is not None: try: self.larch_buffer.onClose() except: pass for nam in dir(self.larch.symtable._sys.wx): obj = getattr(self.larch.symtable._sys.wx, nam) del obj self.Destroy() def show_subframe(self, name, frameclass): shown = False if name in self.subframes: try: self.subframes[name].Raise() shown = True except: del self.subframes[name] if not shown: self.subframes[name] = frameclass(self) def onEditColumnLabels(self, evt=None): self.show_subframe('coledit', EditColumnFrame) def onReadScan(self, evt=None): dlg = wx.FileDialog(self, message="Load Column Data File", defaultDir=os.getcwd(), wildcard=FILE_WILDCARDS, style=wx.FD_OPEN) if dlg.ShowModal() == wx.ID_OK: path = dlg.GetPath() path = path.replace('\\', '/') if path in self.file_groups: if wx.ID_YES != popup(self, "Re-read file '%s'?" % path, 'Re-read file?'): return gname = '_sview0001' count, maxcount = 1, 9999 while hasattr(self.datagroups, gname) and count < maxcount: count += 1 gname = '_sview%4.4i' % count if hasattr(self.datagroups, gname): gname = randname() parent, fname = os.path.split(path) if self.config['chdir_on_fileopen']: os.chdir(parent) fh = open(path, 'r') line1 = fh.readline().lower() fh.close() reader = 'read_ascii' if 'epics scan' in line1: reader = 'read_gsescan' elif 'xdi' in line1: reader = 'read_xdi' if 'epics stepscan file' in line1: reader = 'read_gsexdi' self.larch("%s = %s('%s')" % (gname, reader, path)) self.larch("%s.path = '%s'" % (gname, path)) self.filelist.Append(fname) self.file_paths.append(path) self.file_groups[path] = gname self.ShowFile(groupname=gname) dlg.Destroy()
def onPlot(self, evt=None, opt='new', npts=None, reprocess=False): try: self.plotframe.Show() except: # wx.PyDeadObjectError self.plotframe = PlotFrame(None, size=(650, 400)) self.plotframe.Show() self.plotpanel = self.plotframe.panel if reprocess: if (self.nb.GetCurrentPage() == self.xas_panel): self.xas_process(self.groupname, new_mu=True) side = 'left' update = False plotcmd = self.plotpanel.plot if opt in ('left', 'right'): side = opt plotcmd = self.plotpanel.oplot elif opt == 'update' and npts > 4: plotcmd = self.plotpanel.update_line update = True if 'new' in opt: self.plotpanel.clear() popts = {'side': side} try: gname = self.groupname lgroup = getattr(self.larch.symtable, gname) except: gname = SCANGROUP lgroup = getattr(self.larch.symtable, gname) return if not hasattr(lgroup, '_xdat_'): self.onColumnChoices() lgroup._xdat_ = np.array(lgroup._xdat_[:npts]) plot_yarrays = [(lgroup._ydat_, {}, None)] if hasattr(lgroup, 'plot_yarrays'): plot_yarrays = lgroup.plot_yarrays #for yarr in plot_yarrays: # yarr = np.array(yarr[:npts]) path, fname = os.path.split(lgroup.filename) popts['label'] = "%s: %s" % (fname, lgroup.plot_ylabel) if side == 'right': popts['y2label'] = lgroup.plot_ylabel else: popts['ylabel'] = lgroup.plot_ylabel if plotcmd == self.plotpanel.plot: popts['title'] = fname if update: self.plotpanel.set_xlabel(lgroup.plot_xlabel) self.plotpanel.set_ylabel(lgroup.plot_ylabel) for itrace, yarr, label in enumerate(plot_yarrays): plotcmd(itrace, lgroup._xdat_, yarr[0], draw=True, update_limits=((npts < 5) or (npts % 5 == 0)), **yarr[1]) self.plotpanel.set_xylims( (min(lgroup._xdat_), max(lgroup._xdat_), min(yarr), max(yarr))) else: for yarr in plot_yarrays: popts.update(yarr[1]) if yarr[2] is not None: popts['label'] = yarr[2] plotcmd(lgroup._xdat_, yarr[0], **popts) plotcmd = self.plotpanel.oplot if hasattr(lgroup, 'plot_ymarkers'): for x, y, opts in lgroup.plot_ymarkers: popts = {'marker': 'o', 'markersize': 4} popts.update(opts) self.plotpanel.oplot([x], [y], **popts) self.plotpanel.canvas.draw()
class MapViewerFrame(wx.Frame): _about = """XRF Map Viewer Matt Newville <newville @ cars.uchicago.edu> """ def __init__(self, conffile=None, **kwds): kwds["style"] = wx.DEFAULT_FRAME_STYLE wx.Frame.__init__(self, None, -1, size=(700, 400), **kwds) self.data = None self.filemap = {} self.im_displays = [] self.larch = None self.plotframe = None self.Font14=wx.Font(14, wx.SWISS, wx.NORMAL, wx.BOLD, 0, "") self.Font12=wx.Font(12, wx.SWISS, wx.NORMAL, wx.BOLD, 0, "") self.Font11=wx.Font(11, wx.SWISS, wx.NORMAL, wx.BOLD, 0, "") self.Font10=wx.Font(10, wx.SWISS, wx.NORMAL, wx.BOLD, 0, "") self.Font9 =wx.Font(9, wx.SWISS, wx.NORMAL, wx.BOLD, 0, "") self.SetTitle("GSE XRM MapViewer") self.SetFont(self.Font9) 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) def createMainPanel(self): sizer = wx.BoxSizer(wx.VERTICAL) splitter = wx.SplitterWindow(self, style=wx.SP_LIVE_UPDATE) splitter.SetMinimumPaneSize(175) self.filelist = EditableListBox(splitter, self.ShowFile) # self.detailspanel = self.createViewOptsPanel(splitter) dpanel = self.detailspanel = wx.Panel(splitter) dpanel.SetMinSize((575, 350)) self.createNBPanels(dpanel) splitter.SplitVertically(self.filelist, self.detailspanel, 1) sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(splitter, 1, wx.GROW|wx.ALL, 5) wx.CallAfter(self.init_larch) pack(self, sizer) def createNBPanels(self, parent): self.title = SimpleText(parent, 'initializing...') sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(self.title, 0, ALL_CEN) self.nb = flat_nb.FlatNotebook(parent, wx.ID_ANY, agwStyle=FNB_STYLE) self.nb.SetBackgroundColour('#FCFCFA') self.SetBackgroundColour('#F0F0E8') self.nbpanels = {} for name, key, creator in (('Simple ROI Map', 'roimap', SimpleMapPanel), ('3-Color ROI Map', '3color', TriColorMapPanel)): # ('2x2 Grid', self.MapGridPanel)): # print 'panel ' , name, parent, creator p = creator(parent, owner=self) self.nb.AddPage(p, name, True) self.nbpanels[key] = p self.nb.SetSelection(0) sizer.Add(self.nb, 1, wx.ALL|wx.EXPAND) pack(parent, sizer) def lassoHandler(self, data=None, selected=None, det=None, mask=None, **kws): mask.shape = data.shape energy = self.current_file.xrfmap['detsum/energy'].value spectra = self.current_file.xrfmap['detsum/data'].value spectra = spectra.swapaxes(0, 1)[mask].sum(axis=0) self.show_PlotFrame() spectra[np.where(spectra<1)] = 1 self.plotframe.plot(energy, spectra, ylog_scale=True) def show_PlotFrame(self, do_raise=True, clear=True): "make sure plot frame is enabled, and visible" if self.plotframe is None: self.plotframe = PlotFrame(self, title='XRF Spectra') try: self.plotframe.Show() except wx.PyDeadObjectError: self.plotframe = PlotFrame(self, title='XRF Spectra') self.plotframe.Show() if do_raise: self.plotframe.Raise() if clear: self.plotframe.panel.clear() self.plotframe.reset_config() def add_imdisplay(self, title, det=None, config_on_frame=True): on_lasso = Closure(self.lassoHandler, det=det) self.im_displays.append(ImageFrame(output_title=title, lasso_callback=on_lasso, config_on_frame=config_on_frame)) def display_map(self, map, title='', info='', x=None, y=None, det=None, with_config=True): """display a map in an available image display""" displayed = False while not displayed: try: imd = self.im_displays.pop() imd.display(map, title=title, x=x, y=y) displayed = True except IndexError: on_lasso = Closure(self.lassoHandler, det=det) imd = ImageFrame(output_title=title, lasso_callback=on_lasso, config_on_frame=with_config) imd.display(map, title=title, x=x, y=y) displayed = True except PyDeadObjectError: displayed = False self.im_displays.append(imd) imd.SetStatusText(info, 1) imd.Show() imd.Raise() def init_larch(self): t0 = time.time() from larch import Interpreter 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.SetStatusText('ready') self.datagroups = self.larch.symtable self.title.SetLabel('') def onPlot(self, evt): self.do_plot(newplot=True) def onOPlot(self, evt): self.do_plot(newplot=False) def do_plot(self, newplot=False): ix = self.x_choice.GetSelection() x = self.x_choice.GetStringSelection() if self.data is None and ix > -1: self.SetStatusText( 'cannot plot - no valid data') xop = self.x_op.GetStringSelection() yop1 = self.y_op1.GetStringSelection() yop2 = self.y_op2.GetStringSelection() yop3 = self.y_op3.GetStringSelection() y1 = self.y1_choice.GetStringSelection() y2 = self.y2_choice.GetStringSelection() y3 = self.y3_choice.GetStringSelection() if y1 == '': y1 = '1' if y2 == '': y2 = '1' if y3 == '': y3 = '1' gname = self.groupname lgroup = getattr(self.larch.symtable, gname) xlabel_ = xlabel = x xunits = lgroup.column_units[ix] if xunits != '': xlabel = '%s (%s)' % (xlabel, xunits) x = "%s.get_data('%s')" % (gname, x) if xop == 'log': x = "log(%s)" % x ylabel = "[%s%s%s]%s%s" % (y1, yop2, y2, yop3, y3) if y2 == '1' and yop2 in ('*', '/') or y2 == '0' and yop2 in ('+', '-'): ylabel = "(%s%s%s" % (y1, yop3, y3) if y3 == '1' and yop3 in ('*', '/') or y3 == '0' and yop3 in ('+', '-'): ylabel = "%s" % (y1) elif y3 == '1' and yop3 in ('*', '/') or y3 == '0' and yop3 in ('+', '-'): ylabel = "%s%s%s" % (y1, yop2, y2) if yop1 != '': yoplab = yop1.replace('deriv', 'd') ylabel = '%s(%s)' % (yoplab, ylabel) if '(' in yop1: ylabel = "%s)" % ylabel y1 = y1 if y1 in ('0, 1') else "%s.get_data('%s')" % (gname, y1) y2 = y2 if y2 in ('0, 1') else "%s.get_data('%s')" % (gname, y2) y3 = y3 if y3 in ('0, 1') else "%s.get_data('%s')" % (gname, y3) y = "%s((%s %s %s) %s (%s))" % (yop1, y1, yop2, y2, yop3, y3) if '(' in yop1: y = "%s)" % y if 'deriv' in yop1: y = "%s/deriv(%s)" % (y, x) ylabel = '%s/d(%s)' % (ylabel, xlabel_) fmt = "plot(%s, %s, label='%s', xlabel='%s', ylabel='%s', new=%s)" cmd = fmt % (x, y, self.data.fname, xlabel, ylabel, repr(newplot)) self.larch(cmd) def ShowFile(self, evt=None, filename=None, **kws): if filename is None and evt is not None: filename = evt.GetString() if self.check_ownership(filename): self.filemap[filename].process() self.current_file = self.filemap[filename] self.title.SetLabel("%s" % filename) rois = list(self.filemap[filename].xrfmap['roimap/sum_name']) rois_extra = [''] + rois set_choices(self.nbpanels['roimap'].roi1, rois) set_choices(self.nbpanels['roimap'].roi2, rois_extra) set_choices(self.nbpanels['3color'].rchoice, rois) set_choices(self.nbpanels['3color'].gchoice, rois) set_choices(self.nbpanels['3color'].bchoice, rois) def createMenus(self): self.menubar = wx.MenuBar() fmenu = wx.Menu() add_menu(self, fmenu, "&Open Map File\tCtrl+O", "Read Map File", self.onReadFile) add_menu(self, fmenu, "&Open Map Folder\tCtrl+F", "Read Map Folder", self.onReadFolder) fmenu.AppendSeparator() add_menu(self, fmenu, "&Quit\tCtrl+Q", "Quit program", self.onClose) self.menubar.Append(fmenu, "&File") self.SetMenuBar(self.menubar) def onAbout(self,evt): dlg = wx.MessageDialog(self, self._about,"About GSEXRM MapViewer", wx.OK | wx.ICON_INFORMATION) dlg.ShowModal() dlg.Destroy() def onClose(self,evt): for xrmfile in self.filemap.values(): xrmfile.close() for imd in self.im_displays: try: imd.Destroy() except: pass for nam in dir(self.larch.symtable._plotter): obj = getattr(self.larch.symtable._plotter, nam) 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() def onReadFolder(self, evt=None): dlg = wx.DirDialog(self, message="Read Map Folder", defaultPath=os.getcwd(), style=wx.OPEN) path, read = None, False if dlg.ShowModal() == wx.ID_OK: read = True path = dlg.GetPath().replace('\\', '/') dlg.Destroy() if read: try: xrmfile = GSEXRM_MapFile(folder=str(path)) except: popup(self, NOT_GSEXRM_FOLDER % fname, "Not a Map folder") return fname = xrmfile.filename if fname not in self.filemap: self.filemap[fname] = xrmfile if fname not in self.filelist.GetItems(): self.filelist.Append(fname) if self.check_ownership(fname): self.process_file(fname) self.ShowFile(filename=fname) def onReadFile(self, evt=None): dlg = wx.FileDialog(self, message="Read Map File", defaultDir=os.getcwd(), wildcard=FILE_WILDCARDS, style=wx.OPEN) path, read = None, False if dlg.ShowModal() == wx.ID_OK: read = True path = dlg.GetPath().replace('\\', '/') if path in self.filemap: read = popup(self, "Re-read file '%s'?" % path, 'Re-read file?', style=wx.YES_NO) dlg.Destroy() if read: try: parent, fname = os.path.split(path) xrmfile = GSEXRM_MapFile(fname) except: popup(self, NOT_GSEXRM_FILE % fname, "Not a Map file!") return if fname not in self.filemap: self.filemap[fname] = xrmfile if fname not in self.filelist.GetItems(): self.filelist.Append(fname) if self.check_ownership(fname): self.process_file(fname) self.ShowFile(filename=fname) def onGSEXRM_Data(self, **kws): print 'Saw GSEXRM_Data ', kws def process_file(self, filename): """Request processing of map file. This can take awhile, so is done in a separate thread, with updates displayed in message bar """ xrm_map = self.filemap[filename] if xrm_map.status == GSEXRM_FileStatus.created: xrm_map.initialize_xrfmap() if xrm_map.dimension is None and isGSEXRM_MapFolder(self.folder): xrm_map.read_master() if self.filemap[filename].folder_has_newdata(): print 'PROCESS ', filename, xrm_map.folder_has_newdata() dthread = Thread(target=self.new_mapdata, args=(filename,)) dthread.start() dthread.join() def new_mapdata(self, filename): xrm_map = self.filemap[filename] nrows = len(xrm_map.rowdata) if xrm_map.folder_has_newdata(): irow = xrm_map.last_row + 1 while irow < nrows: row = xrm_map.read_rowdata(irow) if row is not None: xrm_map.add_rowdata(row) irow = irow + 1 time.sleep(.001) wx.Yield() xrm_map.resize_arrays(xrm_map.last_row+1) xrm_map.h5root.flush() def OLDprocess_file(self, filename): """Request processing of map file. This can take awhile, so is done in a separate thread, with updates displayed in message bar """ xrm_map = self.filemap[filename] def on_process(row=0, maxrow=0, filename=None, status='unknown'): print 'on process ', row, maxrow, filename, status if maxrow < 1 or filename is None: return #self.SetStatusText('processing row=%i / %i for %s [%s]' % # (row, maxrow, fname, status)) if xrm_map.folder_has_newdata(): print 'PROCESS ', filename, xrm_map.folder_has_newdata() dthread = Thread(target=self.filemap[filename].process, kwargs={'callback': on_process}, name='process_thread') dthread.start() dthread.join() def check_ownership(self, fname): """ check whether we're currently owner of the file. this is important!! HDF5 files can be corrupted. """ if not self.filemap[fname].check_hostid(): if popup(self, NOT_OWNER_MSG % fname, 'Not Owner of HDF5 File', style=wx.YES_NO): self.filemap[fname].claim_hostid() return self.filemap[fname].check_hostid()