class Maskplotpanel(PlotPanel): """ PlotPanel for Quick plot and masking plot """ def __init__(self, parent, id=-1, dimension=2, color=None, dpi=None, **kwargs): """ """ PlotPanel.__init__(self, parent, id=id, color=color, dpi=dpi, **kwargs) # Keep track of the parent Frame self.parent = parent # Internal list of plottable names (because graph # doesn't have a dictionary of handles for the plottables) self.dimension = dimension self.plots = {} self.graph = Graph() # add axis labels self.graph.xaxis('\\rm{x} ', '') self.graph.yaxis('\\rm{y} ', '') def add_toolbar(self): """ Add toolbar """ # Not implemented pass def on_set_focus(self, event): """ send to the parenet the current panel on focus """ if self.dimension == 3: pass else: self.draw() def add_image(self, plot): """ Add Image """ self.plots[plot.name] = plot # init graph self.graph = Graph() # add plot self.graph.add(plot) # add axes if self.dimension == 1: self.xaxis_label = '\\rm{x} ' self.xaxis_unit = '' self.yaxis_label = '\\rm{y} ' self.yaxis_unit = '' # draw # message status_type = 'progress' msg = 'Plotting...' self._status_info(msg, status_type) status_type = 'stop' self.graph.render(self) self.subplot.figure.canvas.resizing = False if self.dimension < 3: self.graph.render(self) self.subplot.figure.canvas.draw() elif FONT_VARIANT: self.subplot.figure.canvas.draw() msg = 'Plotting Completed.' self._status_info(msg, status_type) def onMouseMotion(self, event): """ Disable dragging 2D image """ pass def onWheel(self, event): """ """ pass def onLeftDown(self, event): """ Disables LeftDown """ pass def onPick(self, event): """ Disables OnPick """ pass def draw(self): """ Draw """ # message status_type = 'progress' msg = 'Plotting...' self._status_info(msg, status_type) status_type = 'stop' if not self.dimension == 3: self.subplot.figure.canvas.draw_idle() msg = 'Plotting Completed.' self._status_info(msg, status_type) def onContextMenu(self, event): """ Default context menu for a plot panel """ # Selective Slicer plot popup menu slicerpop = wx.Menu() id = wx.NewId() slicerpop.Append(id, '&Print Image', 'Print image') wx.EVT_MENU(self, id, self.onPrint) id = wx.NewId() slicerpop.Append(id, '&Copy to Clipboard', 'Copy to the clipboard') wx.EVT_MENU(self, id, self.OnCopyFigureMenu) if self.dimension == 1: id = wx.NewId() slicerpop.Append(id, '&Change Scale') wx.EVT_MENU(self, id, self._onProperties) else: slicerpop.AppendSeparator() id_cm = wx.NewId() slicerpop.Append(id_cm, '&Toggle Linear/Log scale') wx.EVT_MENU(self, id_cm, self._onToggleScale) pos = event.GetPosition() pos = self.ScreenToClient(pos) self.PopupMenu(slicerpop, pos) def _status_info(self, msg='', type="update"): """ Status msg """ if self.parent.parent.parent is not None: wx.PostEvent(self.parent.parent.parent, StatusEvent(status=msg, type=type))
class SimplePlotPanel(PlotPanel): """ PlotPanel for 1d and 2d """ _window_caption = 'Simple Plot' def __init__(self, parent, id=-1, color=None, dpi=None, style=wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs): """ Init """ PlotPanel.__init__(self, parent, id=id, style=style, **kwargs) self.SetColor(wx.WHITE) self.toolbar = NavigationToolBar(parent=self, canvas=self.canvas) self.toolbar.Show(False) self.scale = parent.scale self.window_caption = self._window_caption def draw(self): """ """ self.resizing = False self.canvas.set_resizing(self.resizing) self.canvas.draw() def add_toolbar(self): """ """ pass def onContextMenu(self, event): """ 2D plot context menu :param event: wx context event """ slicerpop = PanelMenu() slicerpop.set_plots(self.plots) slicerpop.set_graph(self.graph) wx_id = wx.NewId() slicerpop.Append(wx_id, '&Save Image') wx.EVT_MENU(self, wx_id, self.onSaveImage) wx_id = wx.NewId() slicerpop.Append(wx_id, '&Print Image', 'Print image') wx.EVT_MENU(self, wx_id, self.onPrint) wx_id = wx.NewId() slicerpop.Append(wx_id, '&Copy to Clipboard', 'Copy to the clipboard') wx.EVT_MENU(self, wx_id, self.OnCopyFigureMenu) if self.dimension != 3: slicerpop.AppendSeparator() wx_id = wx.NewId() slicerpop.Append(wx_id, '&Toggle Grid On/Off', 'Toggle Grid On/Off') wx.EVT_MENU(self, wx_id, self.on_grid_onoff) if self.data.__class__.__name__ == 'Data1D': slicerpop.AppendSeparator() wx_id = wx.NewId() slicerpop.Append(wx_id, '&Change Scale') wx.EVT_MENU(self, wx_id, self._onProperties) elif self.data2D.__class__.__name__ == 'Data2D': slicerpop.AppendSeparator() wx_id = wx.NewId() slicerpop.Append(wx_id, '&Toggle Linear/Log Scale') wx.EVT_MENU(self, wx_id, self._onToggleScale) try: pos_evt = event.GetPosition() pos = self.ScreenToClient(pos_evt) except: pos_x, pos_y = self.toolbar.GetPositionTuple() pos = (pos_x, pos_y + 5) self.PopupMenu(slicerpop, pos) if self.scale is not None: self.parent.scale2d = self.scale def on_grid_onoff(self, event): """ On grid on/off """ switch = (not self.grid_on) self.onGridOnOff(switch) def onLeftDown(self, event): """ left button down and ready to drag """ # Check that the LEFT button was pressed if event.button == 1: self.leftdown = True ax = event.inaxes if ax is not None: self.xInit, self.yInit = event.xdata, event.ydata try: pos_x = float(event.xdata) # / size_x pos_y = float(event.ydata) # / size_y pos_x = "%8.3g" % pos_x pos_y = "%8.3g" % pos_y self.position = str(pos_x), str(pos_y) wx.PostEvent(self.parent, StatusEvent(status=self.position)) except: self.position = None def _OnReSize(self, event): """ On response of the resize of a panel, set axes_visiable False """ self.resizing = False if self.x_size is not None: if self.x_size == self.GetSize(): self.canvas.set_resizing(self.resizing) return self.x_size = self.GetSize() # Ready for another event # Do not remove this Skip. # Otherwise it will get runtime error on wx>=2.9. event.Skip() # set the resizing flag self.canvas.set_resizing(self.resizing) pos_x, pos_y = self.GetPositionTuple() if pos_x != 0 and pos_y != 0: self.size, _ = self.GetClientSizeTuple() self.SetSizer(self.sizer) def on_set_focus(self, event): """ By pass default boundary blue color drawing """ pass def on_kill_focus(self, event): """ Reset the panel color """ pass def show_plot(self, plot): """ Show the plot """ _yaxis, _yunit = plot.get_yaxis() _xaxis, _xunit = plot.get_xaxis() self.data = plot self.plots[plot.name] = plot # Axis scales if plot.xtransform is not None: self.xLabel = plot.xtransform if plot.ytransform is not None: self.yLabel = plot.ytransform # Init graph self.graph = Graph() # Add plot: Handles both 1D and 2D self.graph.add(self.data) self.canvas.set_resizing(False) if self.data.__class__.__name__ == 'Data2D': self.data2D = plot elif self.data.__class__.__name__ == 'Data1D': self._onEVT_FUNC_PROPERTY(show=False) # Axes self.graph.xaxis(_xaxis, _xunit) self.graph.yaxis(_yaxis, _yunit) self.xaxis(_xaxis, _xunit) self.yaxis(_yaxis, _yunit) self.set_xscale(self.xscale) self.set_yscale(self.yscale) self.graph.render(self)
class SimplePlotPanel(PlotPanel): """ PlotPanel for 1d and 2d """ _window_caption = 'Simple Plot' def __init__(self, parent, id=-1, color=None, dpi=None, style=wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs): """ Init """ PlotPanel.__init__(self, parent, id=id, style=style, **kwargs) self.SetColor(wx.WHITE) self.toolbar = NavigationToolBar(parent=self, canvas=self.canvas) self.toolbar.Show(False) self.scale = parent.scale self.window_caption = self._window_caption def draw(self): """ """ self.resizing = False self.canvas.set_resizing(self.resizing) self.canvas.draw() def add_toolbar(self): """ """ pass def onContextMenu(self, event): """ 2D plot context menu :param event: wx context event """ slicerpop = PanelMenu() slicerpop.set_plots(self.plots) slicerpop.set_graph(self.graph) wx_id = wx.NewId() slicerpop.Append(wx_id, '&Save Image') wx.EVT_MENU(self, wx_id, self.onSaveImage) wx_id = wx.NewId() slicerpop.Append(wx_id, '&Print Image', 'Print image') wx.EVT_MENU(self, wx_id, self.onPrint) wx_id = wx.NewId() slicerpop.Append(wx_id, '&Copy to Clipboard', 'Copy to the clipboard') wx.EVT_MENU(self, wx_id, self.OnCopyFigureMenu) if self.dimension != 3: slicerpop.AppendSeparator() wx_id = wx.NewId() slicerpop.Append(wx_id, '&Toggle Grid On/Off', 'Toggle Grid On/Off') wx.EVT_MENU(self, wx_id, self.on_grid_onoff) if self.data.__class__.__name__ == 'Data1D': slicerpop.AppendSeparator() wx_id = wx.NewId() slicerpop.Append(wx_id, '&Change Scale') wx.EVT_MENU(self, wx_id, self._onProperties) elif self.data2D.__class__.__name__ == 'Data2D': slicerpop.AppendSeparator() wx_id = wx.NewId() slicerpop.Append(wx_id, '&Toggle Linear/Log Scale') wx.EVT_MENU(self, wx_id, self._onToggleScale) try: pos_evt = event.GetPosition() pos = self.ScreenToClient(pos_evt) except: pos_x, pos_y = self.toolbar.GetPositionTuple() pos = (pos_x, pos_y + 5) self.PopupMenu(slicerpop, pos) if self.scale is not None: self.parent.scale2d = self.scale def on_grid_onoff(self, event): """ On grid on/off """ switch = (not self.grid_on) self.onGridOnOff(switch) def onLeftDown(self, event): """ left button down and ready to drag """ # Check that the LEFT button was pressed if event.button == 1: self.leftdown = True ax = event.inaxes if ax is not None: self.xInit, self.yInit = event.xdata, event.ydata try: pos_x = float(event.xdata) # / size_x pos_y = float(event.ydata) # / size_y pos_x = "%8.3g" % pos_x pos_y = "%8.3g" % pos_y self.position = str(pos_x), str(pos_y) wx.PostEvent(self.parent, StatusEvent(status=self.position)) except: self.position = None def _OnReSize(self, event): """ On response of the resize of a panel, set axes_visiable False """ self.resizing = False if self.x_size is not None: if self.x_size == self.GetSize(): self.canvas.set_resizing(self.resizing) return self.x_size = self.GetSize() # Ready for another event # Do not remove this Skip. # Otherwise it will get runtime error on wx>=2.9. event.Skip() # set the resizing flag self.canvas.set_resizing(self.resizing) pos_x, pos_y = self.GetPositionTuple() if pos_x != 0 and pos_y != 0: self.size, _ = self.GetClientSizeTuple() self.SetSizer(self.sizer) def on_set_focus(self, event): """ By pass default boundary blue color drawing """ pass def on_kill_focus(self, event): """ Reset the panel color """ pass def show_plot(self, plot): """ Show the plot """ _yaxis, _yunit = plot.get_yaxis() _xaxis, _xunit = plot.get_xaxis() self.data = plot self.plots[plot.name] = plot # Axis scales if plot.xtransform is not None: self.xLabel = plot.xtransform if plot.ytransform is not None: self.yLabel = plot.ytransform # Init graph self.graph = Graph() # Add plot: Handles both 1D and 2D self.graph.add(self.data) self.canvas.set_resizing(False) if self.data.__class__.__name__ == 'Data2D': self.data2D = plot elif self.data.__class__.__name__ == 'Data1D': self._onEVT_FUNC_PROPERTY(show=False) # Axes self.graph.xaxis(_xaxis, _xunit) self.graph.yaxis(_yaxis, _yunit) self.xaxis(_xaxis, _xunit) self.yaxis(_yaxis, _yunit) self.set_xscale(self.xscale) self.set_yscale(self.yscale) self.graph.render(self)
class OutputPlot(PlotPanel): """ Plot panel used to show the selected results as a function of D_max """ ## Title for plottools window_caption = "D Explorer" def __init__(self, d_min, d_max, parent, id= -1, color=None, \ dpi=None, style=wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs): """ Initialization. The parameters added to PlotPanel are: :param d_min: Minimum value of D_max to explore :param d_max: Maximum value of D_max to explore """ PlotPanel.__init__(self, parent, id=id, style=style, **kwargs) self.parent = parent self.min = d_min self.max = d_max self.npts = DEFAULT_NPTS step = (self.max - self.min) / (self.npts - 1) self.x = np.arange(self.min, self.max + step * 0.01, step) dx = np.zeros(len(self.x)) y = np.ones(len(self.x)) dy = np.zeros(len(self.x)) # Plot area self.plot = Model1D(self.x, y=y, dy=dy) self.plot.name = DEFAULT_OUTPUT self.plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM # Graph self.graph = Graph() self.graph.xaxis("\\rm{D_{max}}", 'A') self.graph.yaxis("\\rm{%s}" % DEFAULT_OUTPUT, "") self.graph.add(self.plot) self.graph.render(self) self.toolbar.DeleteToolByPos(0) self.toolbar.DeleteToolByPos(8) self.toolbar.Realize() def onContextMenu(self, event): """ Default context menu for the plot panel :TODO: Would be nice to add printing and log/linear scales. The current version of plottools no longer plays well with plots outside of guiframe. Guiframe team needs to fix this. """ # Slicer plot popup menu wx_id = wx.NewId() slicerpop = wx.Menu() slicerpop.Append(wx_id, '&Save image', 'Save image as PNG') wx.EVT_MENU(self, wx_id, self.onSaveImage) wx_id = wx.NewId() slicerpop.AppendSeparator() slicerpop.Append(wx_id, '&Reset Graph') wx.EVT_MENU(self, wx_id, self.onResetGraph) pos = event.GetPosition() pos = self.ScreenToClient(pos) self.PopupMenu(slicerpop, pos)
class SmallPanel(PlotPanel): """ PlotPanel for Quick plot and masking plot """ def __init__(self, parent, id= -1, is_number=False, content='?', **kwargs): """ """ PlotPanel.__init__(self, parent, id=id, **kwargs) self.is_number = is_number self.content = content self.point = None self.position = (0.4, 0.5) self.scale = 'linear' self.prevXtrans = "x" self.prevYtrans = "y" self.viewModel = "--" self.subplot.set_xticks([]) self.subplot.set_yticks([]) self.add_text() self.figure.subplots_adjust(left=0.1, bottom=0.1) def set_content(self, content=''): """ Set text content """ self.content = str(content) def add_toolbar(self): """ Add toolbar """ # Not implemented pass def on_set_focus(self, event): """ send to the parenet the current panel on focus """ pass def add_image(self, plot): """ Add Image """ self.content = '' self.textList = [] self.plots = {} self.clear() self.point = plot try: self.figure.delaxes(self.figure.axes[0]) self.subplot = self.figure.add_subplot(111) #self.figure.delaxes(self.figure.axes[1]) except: pass try: name = plot.name except: name = plot.filename self.plots[name] = plot #init graph self.graph = Graph() #add plot self.graph.add(plot) #draw self.graph.render(self) try: self.figure.delaxes(self.figure.axes[1]) except: pass self.subplot.figure.canvas.resizing = False self.subplot.tick_params(axis='both', labelsize=9) # Draw zero axis lines self.subplot.axhline(linewidth=1, color='r') self.subplot.axvline(linewidth=1, color='r') self.erase_legend() try: # mpl >= 1.1.0 self.figure.tight_layout() except: self.figure.subplots_adjust(left=0.1, bottom=0.1) self.subplot.figure.canvas.draw() def add_text(self): """ Text in the plot """ if not self.is_number: return self.clear() try: self.figure.delaxes(self.figure.axes[0]) self.subplot = self.figure.add_subplot(111) self.figure.delaxes(self.figure.axes[1]) except: pass self.subplot.set_xticks([]) self.subplot.set_yticks([]) label = self.content FONT = FontProperties() xpos, ypos = (0.4, 0.5) font = FONT.copy() font.set_size(14) self.textList = [] self.subplot.set_xlim((0, 1)) self.subplot.set_ylim((0, 1)) try: if self.content != '?': float(label) except: self.subplot.set_frame_on(False) try: # mpl >= 1.1.0 self.figure.tight_layout() except: self.figure.subplots_adjust(left=0.1, bottom=0.1) if len(label) > 0 and xpos > 0 and ypos > 0: new_text = self.subplot.text(str(xpos), str(ypos), str(label), fontproperties=font) self.textList.append(new_text) def erase_legend(self): """ Remove Legend """ #for ax in self.axes: self.remove_legend(self.subplot) def onMouseMotion(self, event): """ Disable dragging 2D image """ def onWheel(self, event): """ """ def onLeftDown(self, event): """ Disables LeftDown """ def onPick(self, event): """ Remove Legend """ for ax in self.axes: self.remove_legend(ax) def draw(self): """ Draw """ if self.dimension == 3: pass else: self.subplot.figure.canvas.resizing = False self.subplot.tick_params(axis='both', labelsize=9) self.erase_legend() self.subplot.figure.canvas.draw_idle() try: self.figure.delaxes(self.figure.axes[1]) except: pass def onContextMenu(self, event): """ Default context menu for a plot panel """ id = wx.NewId() slicerpop = wx.Menu() data = self.point if issubclass(data.__class__, Data1D): slicerpop.Append(id, '&Change Scale') wx.EVT_MENU(self, id, self._onProperties) else: slicerpop.Append(id, '&Toggle Linear/Log Scale') wx.EVT_MENU(self, id, self.ontogglescale) try: # mouse event pos_evt = event.GetPosition() pos = self.ScreenToClient(pos_evt) except: # toolbar event pos_x, pos_y = self.toolbar.GetPositionTuple() pos = (pos_x, pos_y + 5) self.PopupMenu(slicerpop, pos) def ontogglescale(self, event): """ On toggle 2d scale """ self._onToggleScale(event) try: # mpl >= 1.1.0 self.figure.tight_layout() except: self.figure.subplots_adjust(left=0.1, bottom=0.1) try: self.figure.delaxes(self.figure.axes[1]) except: pass def _onProperties(self, event): """ when clicking on Properties on context menu , The Property dialog is displayed The user selects a transformation for x or y value and a new plot is displayed """ list = [] list = self.graph.returnPlottable() if len(list.keys()) > 0: first_item = list.keys()[0] if first_item.x != []: from sas.sasgui.plottools.PropertyDialog import Properties dial = Properties(self, -1, 'Change Scale') # type of view or model used dial.xvalue.Clear() dial.yvalue.Clear() dial.view.Clear() dial.xvalue.Insert("x", 0) dial.xvalue.Insert("log10(x)", 1) dial.yvalue.Insert("y", 0) dial.yvalue.Insert("log10(y)", 1) dial.view.Insert("--", 0) dial.view.Insert("Linear y vs x", 1) dial.setValues(self.prevXtrans, self.prevYtrans, self.viewModel) dial.Update() if dial.ShowModal() == wx.ID_OK: self.xLabel, self.yLabel, self.viewModel = dial.getValues() if self.viewModel == "Linear y vs x": self.xLabel = "x" self.yLabel = "y" self.viewModel = "--" dial.setValues(self.xLabel, self.yLabel, self.viewModel) self._onEVT_FUNC_PROPERTY() dial.Destroy() def _onEVT_FUNC_PROPERTY(self, remove_fit=True): """ Receive the x and y transformation from myDialog, Transforms x and y in View and set the scale """ list = [] list = self.graph.returnPlottable() # Changing the scale might be incompatible with # currently displayed data (for instance, going # from ln to log when all plotted values have # negative natural logs). # Go linear and only change the scale at the end. self.set_xscale("linear") self.set_yscale("linear") _xscale = 'linear' _yscale = 'linear' for item in list: item.setLabel(self.xLabel, self.yLabel) # control axis labels from the panel itself yname, yunits = item.get_yaxis() xname, xunits = item.get_xaxis() # Goes through all possible scales # Goes through all possible scales if(self.xLabel == "x"): item.transformX(transform.toX, transform.errToX) self.graph._xaxis_transformed("%s" % xname, "%s" % xunits) if(self.xLabel == "log10(x)"): item.transformX(transform.toX_pos, transform.errToX_pos) _xscale = 'log' self.graph._xaxis_transformed("%s" % xname, "%s" % xunits) if(self.yLabel == "y"): item.transformY(transform.toX, transform.errToX) self.graph._yaxis_transformed("%s" % yname, "%s" % yunits) if(self.yLabel == "log10(y)"): item.transformY(transform.toX_pos, transform.errToX_pos) _yscale = 'log' self.graph._yaxis_transformed("%s" % yname, "%s" % yunits) item.transformView() self.prevXtrans = self.xLabel self.prevYtrans = self.yLabel self.set_xscale(_xscale) self.set_yscale(_yscale) self.draw()
class Maskplotpanel(PlotPanel): """ PlotPanel for Quick plot and masking plot """ def __init__(self, parent, id=-1, dimension=2, color=None, dpi=None, **kwargs): """ """ PlotPanel.__init__(self, parent, id=id, color=color, dpi=dpi, **kwargs) # Keep track of the parent Frame self.parent = parent # Internal list of plottable names (because graph # doesn't have a dictionary of handles for the plottables) self.dimension = dimension self.plots = {} self.graph = Graph() # add axis labels self.graph.xaxis('\\rm{x} ', '') self.graph.yaxis('\\rm{y} ', '') def add_toolbar(self): """ Add toolbar """ # Not implemented pass def on_set_focus(self, event): """ send to the parenet the current panel on focus """ if self.dimension == 3: pass else: self.draw() def add_image(self, plot): """ Add Image """ self.plots[plot.name] = plot # init graph self.graph = Graph() # add plot self.graph.add(plot) # add axes if self.dimension == 1: self.xaxis_label = '\\rm{x} ' self.xaxis_unit = '' self.yaxis_label = '\\rm{y} ' self.yaxis_unit = '' # draw # message status_type = 'progress' msg = 'Plotting...' self._status_info(msg, status_type) status_type = 'stop' self.graph.render(self) self.subplot.figure.canvas.resizing = False if self.dimension < 3: self.graph.render(self) self.subplot.figure.canvas.draw() elif FONT_VARIANT: self.subplot.figure.canvas.draw() msg = 'Plotting Completed.' self._status_info(msg, status_type) def onMouseMotion(self, event): """ Disable dragging 2D image """ pass def onWheel(self, event): """ """ pass def onLeftDown(self, event): """ Disables LeftDown """ pass def onPick(self, event): """ Disables OnPick """ pass def draw(self): """ Draw """ # message status_type = 'progress' msg = 'Plotting...' self._status_info(msg, status_type) status_type = 'stop' if not self.dimension == 3: self.subplot.figure.canvas.draw_idle() msg = 'Plotting Completed.' self._status_info(msg, status_type) def onContextMenu(self, event): """ Default context menu for a plot panel """ # Selective Slicer plot popup menu slicerpop = wx.Menu() id = wx.NewId() slicerpop.Append(id, '&Print Image', 'Print image') wx.EVT_MENU(self, id, self.onPrint) id = wx.NewId() slicerpop.Append(id, '&Copy to Clipboard', 'Copy to the clipboard') wx.EVT_MENU(self, id, self.OnCopyFigureMenu) if self.dimension == 1: id = wx.NewId() slicerpop.Append(id, '&Change Scale') wx.EVT_MENU(self, id, self._onProperties) else: slicerpop.AppendSeparator() id_cm = wx.NewId() slicerpop.Append(id_cm, '&Toggle Linear/Log scale') wx.EVT_MENU(self, id_cm, self._onToggleScale) pos = event.GetPosition() pos = self.ScreenToClient(pos) self.PopupMenu(slicerpop, pos) def _status_info(self, msg='', type="update"): """ Status msg """ if self.parent.parent.parent != None: wx.PostEvent(self.parent.parent.parent, StatusEvent(status=msg, type=type))
class OutputPlot(PlotPanel): """ Plot panel used to show the selected results as a function of D_max """ ## Title for plottools window_caption = "D Explorer" def __init__(self, d_min, d_max, parent, id= -1, color=None, \ dpi=None, style=wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs): """ Initialization. The parameters added to PlotPanel are: :param d_min: Minimum value of D_max to explore :param d_max: Maximum value of D_max to explore """ PlotPanel.__init__(self, parent, id=id, style=style, **kwargs) self.parent = parent self.min = d_min self.max = d_max self.npts = DEFAULT_NPTS step = (self.max - self.min) / (self.npts - 1) self.x = np.arange(self.min, self.max + step * 0.01, step) dx = np.zeros(len(self.x)) y = np.ones(len(self.x)) dy = np.zeros(len(self.x)) # Plot area self.plot = Model1D(self.x, y=y, dy=dy) self.plot.name = DEFAULT_OUTPUT self.plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM # Graph self.graph = Graph() self.graph.xaxis("\\rm{D_{max}}", 'A') self.graph.yaxis("\\rm{%s}" % DEFAULT_OUTPUT, "") self.graph.add(self.plot) self.graph.render(self) self.toolbar.DeleteToolByPos(0) self.toolbar.DeleteToolByPos(8) self.toolbar.Realize() def onContextMenu(self, event): """ Default context menu for the plot panel :TODO: Would be nice to add printing and log/linear scales. The current version of plottools no longer plays well with plots outside of guiframe. Guiframe team needs to fix this. """ # Slicer plot popup menu wx_id = wx.NewId() slicerpop = wx.Menu() slicerpop.Append(wx_id, '&Save image', 'Save image as PNG') wx.EVT_MENU(self, wx_id, self.onSaveImage) wx_id = wx.NewId() slicerpop.AppendSeparator() slicerpop.Append(wx_id, '&Reset Graph') wx.EVT_MENU(self, wx_id, self.onResetGraph) pos = event.GetPosition() pos = self.ScreenToClient(pos) self.PopupMenu(slicerpop, pos)
class ModelPanel2D(ModelPanel1D): """ Plot panel for use with the GUI manager """ ## Internal name for the AUI manager window_name = "plotpanel" ## Title to appear on top of the window window_caption = "Plot Panel" ## Flag to tell the GUI manager that this panel is not # tied to any perspective ALWAYS_ON = True ## Group ID group_id = None def __init__(self, parent, id=-1, data2d=None, color=None, dpi=None, style=wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs): """ Initialize the panel """ ModelPanel1D.__init__(self, parent, id=id, style=style, **kwargs) self.parent = parent ## Reference to the parent window if hasattr(parent, "parent"): self.parent = self.parent.parent ## Dictionary containing Plottables self.plots = {} ## Save reference of the current plotted self.data2D = data2d ## Unique ID (from gui_manager) self.uid = None ## Action IDs for internal call-backs self.action_ids = {} ## Create Artist and bind it self.connect = BindArtist(self.subplot.figure) ## Beam stop self.beamstop_radius = DEFAULT_BEAM ## to set the order of lines drawn first. self.slicer_z = 5 ## Reference to the current slicer self.slicer = None ## event to send slicer info self.Bind(EVT_INTERNAL, self._onEVT_INTERNAL) self.axes_frozen = False ## panel that contains result from slicer motion (ex: Boxsum info) self.panel_slicer = None self.title_label = None self.title_font = None self.title_color = 'black' ## Graph self.graph = Graph() self.graph.xaxis("\\rm{Q}", 'A^{-1}') self.graph.yaxis("\\rm{Intensity} ", "cm^{-1}") self.graph.render(self) ## store default value of zmin and zmax self.default_zmin_ctl = self.zmin_2D self.default_zmax_ctl = self.zmax_2D def on_plot_qrange(self, event=None): """ On Qmin Qmax vertical line event """ # Not implemented if event is None: return event.Skip() def onLeftDown(self, event): """ left button down and ready to drag """ # Check that the LEFT button was pressed PlotPanel.onLeftDown(self, event) ax = event.inaxes if ax is not None: # data coordinate position pos_x = "%8.3g" % event.xdata pos_y = "%8.3g" % event.ydata position = "x: %s y: %s" % (pos_x, pos_y) wx.PostEvent(self.parent, StatusEvent(status=position)) self.plottable_selected(self.data2D.id) self._manager.set_panel_on_focus(self) wx.PostEvent(self.parent, PanelOnFocusEvent(panel=self)) def add_toolbar(self): """ add toolbar """ self.enable_toolbar = True self.toolbar = NavigationToolBar2D(parent=self, canvas=self.canvas) self.toolbar.Realize() # On Windows platform, default window size is incorrect, so set # toolbar width to figure width. _, th = self.toolbar.GetSizeTuple() fw, _ = self.canvas.GetSizeTuple() self.toolbar.SetSize(wx.Size(fw, th)) self.sizer.Add(self.toolbar, 0, wx.LEFT | wx.EXPAND) # update the axes menu on the toolbar self.toolbar.update() def plot_data(self, data): """ Data is ready to be displayed :TODO: this name should be changed to something more appropriate Don't forget that changing this name will mean changing code in plotting.py :param event: data event """ xlo = None xhi = None ylo = None yhi = None if data.__class__.__name__ == 'Data1D': return ## Update self.data2d with the current plot self.data2D = data if data.id in self.plots.keys(): #replace xlo, xhi = self.subplot.get_xlim() ylo, yhi = self.subplot.get_ylim() self.graph.replace(data) self.plots[data.id] = data else: self.plots[data.id] = data self.graph.add(self.plots[data.id]) # update qmax with the new xmax of data plotted self.qmax = data.xmax self.slicer = None # Check axis labels #TODO: Should re-factor this ## render the graph with its new content #data2D: put 'Pixel (Number)' for axis title and unit in case of having no detector info and none in _units if len(data.detector) < 1: if len(data._xunit) < 1 and len(data._yunit) < 1: data._xaxis = '\\rm{x}' data._yaxis = '\\rm{y}' data._xunit = 'pixel' data._yunit = 'pixel' # graph properties self.graph.xaxis(data._xaxis, data._xunit) self.graph.yaxis(data._yaxis, data._yunit) if self._is_changed_legend_label: data.label = self.title_label if data.label is None: data.label = data.name if not self.title_font: self.graph.title(data.label) self.graph.render(self) # Set the axis labels on subplot self._set_axis_labels() self.draw_plot() else: self.graph.render(self) self.draw_plot() self.subplot.set_title(label=data.label, fontproperties=self.title_font, color=self.title_color) self.subplot.figure.canvas.draw_idle() ## store default value of zmin and zmax self.default_zmin_ctl = self.zmin_2D self.default_zmax_ctl = self.zmax_2D if not self.is_zoomed: return # Recover the x,y limits if xlo and xhi and ylo and yhi: if xlo > data.xmin and xhi < data.xmax and \ ylo > data.ymin and yhi < data.ymax: self.subplot.set_xlim((xlo, xhi)) self.subplot.set_ylim((ylo, yhi)) else: self.toolbar.update() self.is_zoomed = False def _set_axis_labels(self): """ Set axis labels """ data = self.data2D # control axis labels from the panel itself yname, yunits = data.get_yaxis() if self.yaxis_label is not None: yname = self.yaxis_label yunits = self.yaxis_unit else: self.yaxis_label = yname self.yaxis_unit = yunits xname, xunits = data.get_xaxis() if self.xaxis_label is not None: xname = self.xaxis_label xunits = self.xaxis_unit else: self.xaxis_label = xname self.xaxis_unit = xunits self.xaxis(xname, xunits, self.xaxis_font, self.xaxis_color, self.xaxis_tick) self.yaxis(yname, yunits, self.yaxis_font, self.yaxis_color, self.yaxis_tick) def onContextMenu(self, event): """ 2D plot context menu :param event: wx context event """ ids = iter(self._menu_ids) slicerpop = PanelMenu() slicerpop.set_plots(self.plots) slicerpop.set_graph(self.graph) wx_id = ids.next() slicerpop.Append(wx_id, '&Save Image', 'Save image as png') wx.EVT_MENU(self, wx_id, self.onSaveImage) wx_id = ids.next() slicerpop.Append(wx_id, '&Print Image', 'Print image') wx.EVT_MENU(self, wx_id, self.onPrint) wx_id = ids.next() slicerpop.Append(wx_id, '&Copy to Clipboard', 'Copy to the clipboard') wx.EVT_MENU(self, wx_id, self.OnCopyFigureMenu) slicerpop.AppendSeparator() # saving data plot = self.data2D wx_id = ids.next() slicerpop.Append(wx_id, "&Data Info") wx.EVT_MENU(self, wx_id, self._onDataShow) wx_id = ids.next() slicerpop.Append(wx_id, "&Save as a File (DAT)") self.action_ids[str(wx_id)] = plot wx.EVT_MENU(self, wx_id, self._onSave) slicerpop.AppendSeparator() if len(self.data2D.detector) <= 1: item_list = self.parent.get_current_context_menu(self) if ((item_list is not None) and len(item_list) and self.data2D.name.split(" ")[0] != 'Residuals'): for item, wx_id in zip(item_list, [ids.next() for i in range(len(item_list))]): try: slicerpop.Append(wx_id, item[0], item[1]) wx.EVT_MENU(self, wx_id, item[2]) except: msg = "ModelPanel1D.onContextMenu: " msg += "bad menu item %s" % sys.exc_value wx.PostEvent(self.parent, StatusEvent(status=msg)) slicerpop.AppendSeparator() wx_id = ids.next() slicerpop.Append(wx_id, '&Perform Circular Average') wx.EVT_MENU(self, wx_id, self.onCircular) \ # For Masked Data if not plot.mask.all(): wx_id = ids.next() slicerpop.Append(wx_id, '&Masked Circular Average') wx.EVT_MENU(self, wx_id, self.onMaskedCircular) wx_id = ids.next() slicerpop.Append(wx_id, '&Sector [Q View]') wx.EVT_MENU(self, wx_id, self.onSectorQ) wx_id = ids.next() slicerpop.Append(wx_id, '&Annulus [Phi View ]') wx.EVT_MENU(self, wx_id, self.onSectorPhi) wx_id = ids.next() slicerpop.Append(wx_id, '&Box Sum') wx.EVT_MENU(self, wx_id, self.onBoxSum) wx_id = ids.next() slicerpop.Append(wx_id, '&Box Averaging in Qx') wx.EVT_MENU(self, wx_id, self.onBoxavgX) wx_id = ids.next() slicerpop.Append(wx_id, '&Box Averaging in Qy') wx.EVT_MENU(self, wx_id, self.onBoxavgY) if self.slicer is not None: wx_id = ids.next() slicerpop.Append(wx_id, '&Clear Slicer') wx.EVT_MENU(self, wx_id, self.onClearSlicer) if self.slicer.__class__.__name__ != "BoxSum": wx_id = ids.next() slicerpop.Append(wx_id, '&Edit Slicer Parameters') wx.EVT_MENU(self, wx_id, self._onEditSlicer) slicerpop.AppendSeparator() wx_id = ids.next() slicerpop.Append(wx_id, '&Edit Graph Label', 'Edit Graph Label') wx.EVT_MENU(self, wx_id, self.onEditLabels) slicerpop.AppendSeparator() # ILL mod here wx_id = ids.next() slicerpop.Append(wx_id, '&Modify graph appearance', 'Modify graph appearance') wx.EVT_MENU(self, wx_id, self.modifyGraphAppearance) slicerpop.AppendSeparator() wx_id = ids.next() slicerpop.Append(wx_id, '&2D Color Map') wx.EVT_MENU(self, wx_id, self._onEditDetector) slicerpop.AppendSeparator() wx_id = ids.next() slicerpop.Append(wx_id, '&Toggle Linear/Log Scale') wx.EVT_MENU(self, wx_id, self._onToggleScale) slicerpop.AppendSeparator() wx_id = ids.next() slicerpop.Append(wx_id, '&Window Title') wx.EVT_MENU(self, wx_id, self.onChangeCaption) try: pos_evt = event.GetPosition() pos = self.ScreenToClient(pos_evt) except: pos_x, pos_y = self.toolbar.GetPositionTuple() pos = (pos_x, pos_y + 5) self.PopupMenu(slicerpop, pos) def onEditLabels(self, event): """ Edit legend label """ try: selected_plot = self.plots[self.graph.selected_plottable] except: selected_plot = self.plots[self.data2D.id] label = selected_plot.label dial = TextDialog(None, -1, 'Change Label', label) if dial.ShowModal() == wx.ID_OK: try: FONT = FontProperties() newlabel = dial.getText() font = FONT.copy() font.set_size(dial.getSize()) font.set_family(dial.getFamily()) font.set_style(dial.getStyle()) font.set_weight(dial.getWeight()) colour = dial.getColor() if len(newlabel) > 0: # update Label selected_plot.label = newlabel self.graph.title(newlabel) self.title_label = selected_plot.label self.title_font = font self.title_color = colour ## render the graph self.subplot.set_title(label=self.title_label, fontproperties=self.title_font, color=self.title_color) self._is_changed_legend_label = True self.subplot.figure.canvas.draw_idle() except: msg = "Add Text: Error. Check your property values..." logger.error(msg) if self.parent is not None: wx.PostEvent(self.parent, StatusEvent(status=msg)) dial.Destroy() def _onEditDetector(self, event): """ Allow to view and edits detector parameters :param event: wx.menu event """ import detector_dialog dialog = detector_dialog.DetectorDialog(self, -1, base=self.parent, reset_zmin_ctl=self.default_zmin_ctl, reset_zmax_ctl=self.default_zmax_ctl, cmap=self.cmap) ## info of current detector and data2D xnpts = len(self.data2D.x_bins) ynpts = len(self.data2D.y_bins) xmax = max(self.data2D.xmin, self.data2D.xmax) ymax = max(self.data2D.ymin, self.data2D.ymax) qmax = math.sqrt(math.pow(xmax, 2) + math.pow(ymax, 2)) ## set dialog window content dialog.setContent(xnpts=xnpts, ynpts=ynpts, qmax=qmax, beam=self.data2D.xmin, zmin=self.zmin_2D, zmax=self.zmax_2D) if dialog.ShowModal() == wx.ID_OK: evt = dialog.getContent() self.zmin_2D = evt.zmin self.zmax_2D = evt.zmax self.cmap = evt.cmap dialog.Destroy() ## Redraw the current image self.image(data=self.data2D.data, qx_data=self.data2D.qx_data, qy_data=self.data2D.qy_data, xmin=self.data2D.xmin, xmax=self.data2D.xmax, ymin=self.data2D.ymin, ymax=self.data2D.ymax, zmin=self.zmin_2D, zmax=self.zmax_2D, cmap=self.cmap, color=0, symbol=0, label=self.data2D.name) self.subplot.figure.canvas.draw_idle() def freeze_axes(self): """ """ self.axes_frozen = True def thaw_axes(self): """ """ self.axes_frozen = False def onMouseMotion(self, event): """ """ pass def onWheel(self, event): """ """ pass def update(self, draw=True): """ Respond to changes in the model by recalculating the profiles and resetting the widgets. """ self.draw_plot() def _getEmptySlicerEvent(self): """ create an empty slicervent """ return SlicerEvent(type=None, params=None, obj_class=None) def _onEVT_INTERNAL(self, event): """ Draw the slicer :param event: wx.lib.newevent (SlicerEvent) containing slicer parameter """ self._setSlicer(event.slicer) def _setSlicer(self, slicer): """ Clear the previous slicer and create a new one.Post an internal event. :param slicer: slicer class to create """ ## Clear current slicer if self.slicer is not None: self.slicer.clear() ## Create a new slicer self.slicer_z += 1 self.slicer = slicer(self, self.subplot, zorder=self.slicer_z) self.subplot.set_ylim(self.data2D.ymin, self.data2D.ymax) self.subplot.set_xlim(self.data2D.xmin, self.data2D.xmax) ## Draw slicer self.update() self.slicer.update() msg = "Plotter2D._setSlicer %s" % self.slicer.__class__.__name__ wx.PostEvent(self.parent, StatusEvent(status=msg)) # Post slicer event event = self._getEmptySlicerEvent() event.type = self.slicer.__class__.__name__ event.obj_class = self.slicer.__class__ event.params = self.slicer.get_params() wx.PostEvent(self, event) def onMaskedCircular(self, event): """ perform circular averaging on Data2D with mask if it exists :param event: wx.menu event """ self.onCircular(event, True) def onCircular(self, event, ismask=False): """ perform circular averaging on Data2D :param event: wx.menu event """ # Find the best number of bins npt = math.sqrt(len(self.data2D.data[np.isfinite(self.data2D.data)])) npt = math.floor(npt) from sas.sascalc.dataloader.manipulations import CircularAverage ## compute the maximum radius of data2D self.qmax = max(math.fabs(self.data2D.xmax), math.fabs(self.data2D.xmin)) self.ymax = max(math.fabs(self.data2D.ymax), math.fabs(self.data2D.ymin)) self.radius = math.sqrt(math.pow(self.qmax, 2) + math.pow(self.ymax, 2)) ##Compute beam width bin_width = (self.qmax + self.qmax) / npt ## Create data1D circular average of data2D Circle = CircularAverage(r_min=0, r_max=self.radius, bin_width=bin_width) circ = Circle(self.data2D, ismask=ismask) from sas.sasgui.guiframe.dataFitting import Data1D if hasattr(circ, "dxl"): dxl = circ.dxl else: dxl = None if hasattr(circ, "dxw"): dxw = circ.dxw else: dxw = None new_plot = Data1D(x=circ.x, y=circ.y, dy=circ.dy, dx=circ.dx) new_plot.dxl = dxl new_plot.dxw = dxw new_plot.name = "Circ avg " + self.data2D.name new_plot.source = self.data2D.source #new_plot.info = self.data2D.info new_plot.interactive = True new_plot.detector = self.data2D.detector ## If the data file does not tell us what the axes are, just assume... new_plot.xaxis("\\rm{Q}", "A^{-1}") if hasattr(self.data2D, "scale") and \ self.data2D.scale == 'linear': new_plot.ytransform = 'y' new_plot.yaxis("\\rm{Residuals} ", "normalized") else: new_plot.yaxis("\\rm{Intensity} ", "cm^{-1}") new_plot.group_id = "2daverage" + self.data2D.name new_plot.id = "Circ avg " + self.data2D.name new_plot.is_data = True self.parent.update_theory(data_id=self.data2D.id, \ theory=new_plot) wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title=new_plot.name)) def _onEditSlicer(self, event): """ Is available only when a slicer is drawn.Create a dialog window where the user can enter value to reset slicer parameters. :param event: wx.menu event """ if self.slicer is not None: from SlicerParameters import SlicerParameterPanel dialog = SlicerParameterPanel(self, -1, "Slicer Parameters") dialog.set_slicer(self.slicer.__class__.__name__, self.slicer.get_params()) if dialog.ShowModal() == wx.ID_OK: dialog.Destroy() def onSectorQ(self, event): """ Perform sector averaging on Q and draw sector slicer """ from SectorSlicer import SectorInteractor self.onClearSlicer(event) wx.PostEvent(self, InternalEvent(slicer=SectorInteractor)) def onSectorPhi(self, event): """ Perform sector averaging on Phi and draw annulus slicer """ from AnnulusSlicer import AnnulusInteractor self.onClearSlicer(event) wx.PostEvent(self, InternalEvent(slicer=AnnulusInteractor)) def onBoxSum(self, event): """ """ from sas.sasgui.guiframe.gui_manager import MDIFrame from boxSum import BoxSum self.onClearSlicer(event) self.slicer_z += 1 self.slicer = BoxSum(self, self.subplot, zorder=self.slicer_z) self.subplot.set_ylim(self.data2D.ymin, self.data2D.ymax) self.subplot.set_xlim(self.data2D.xmin, self.data2D.xmax) self.update() self.slicer.update() ## Value used to initially set the slicer panel params = self.slicer.get_params() ## Create a new panel to display results of summation of Data2D from slicerpanel import SlicerPanel win = MDIFrame(self.parent, None, 'None', (100, 200)) new_panel = SlicerPanel(parent=win, id=-1, base=self, type=self.slicer.__class__.__name__, params=params, style=wx.RAISED_BORDER) new_panel.window_caption = self.slicer.__class__.__name__ + " " + \ str(self.data2D.name) new_panel.window_name = self.slicer.__class__.__name__ + " " + \ str(self.data2D.name) ## Store a reference of the new created panel ## save the window_caption of the new panel in the current slicer self.slicer.set_panel_name(name=new_panel.window_caption) ## post slicer panel to guiframe to display it from sas.sasgui.guiframe.events import SlicerPanelEvent win.set_panel(new_panel) new_panel.frame = win wx.PostEvent(self.parent, SlicerPanelEvent(panel=new_panel, main_panel=self)) wx.CallAfter(new_panel.frame.Show) self.panel_slicer = new_panel def onBoxavgX(self, event): """ Perform 2D data averaging on Qx Create a new slicer . :param event: wx.menu event """ from boxSlicer import BoxInteractorX self.onClearSlicer(event) wx.PostEvent(self, InternalEvent(slicer=BoxInteractorX)) def onBoxavgY(self, event): """ Perform 2D data averaging on Qy Create a new slicer . :param event: wx.menu event """ from boxSlicer import BoxInteractorY self.onClearSlicer(event) wx.PostEvent(self, InternalEvent(slicer=BoxInteractorY)) def onClearSlicer(self, event): """ Clear the slicer on the plot """ if self.slicer is not None: self.slicer.clear() self.subplot.figure.canvas.draw() self.slicer = None # Post slicer None event event = self._getEmptySlicerEvent() wx.PostEvent(self, event) def _onSave(self, evt): """ Save a data set to a dat(text) file :param evt: Menu event """ event_id = str(evt.GetId()) if self.parent is not None: self._default_save_location = self.parent._default_save_location default_name = self.plots[self.graph.selected_plottable].label if default_name.count('.') > 0: default_name = default_name.split('.')[0] default_name += "_out" if event_id in self.action_ids: self.parent.save_data2d(self.data2D, default_name) def _onDataShow(self, evt): """ Show the data set in text :param evt: Menu event """ menu = evt.GetEventObject() event_id = evt.GetId() self.set_selected_from_menu(menu, event_id) data = self.plots[self.graph.selected_plottable] default_name = data.label if default_name.count('.') > 0: default_name = default_name.split('.')[0] #default_name += "_out" if self.parent is not None: self.parent.show_data2d(data, default_name) def modifyGraphAppearance(self, e): self.graphApp = graphAppearance(self, 'Modify graph appearance', legend=False) self.graphApp.setDefaults(self.grid_on, self.legend_on, self.xaxis_label, self.yaxis_label, self.xaxis_unit, self.yaxis_unit, self.xaxis_font, self.yaxis_font, find_key(self.get_loc_label(), self.legendLoc), self.xcolor, self.ycolor, self.is_xtick, self.is_ytick) self.graphApp.Bind(wx.EVT_CLOSE, self.on_graphApp_close) def on_graphApp_close(self, e): """ Gets values from graph appearance dialog and sends them off to modify the plot """ self.onGridOnOff(self.graphApp.get_togglegrid()) self.xaxis_label = self.graphApp.get_xlab() self.yaxis_label = self.graphApp.get_ylab() self.xaxis_unit = self.graphApp.get_xunit() self.yaxis_unit = self.graphApp.get_yunit() self.xaxis_font = self.graphApp.get_xfont() self.yaxis_font = self.graphApp.get_yfont() self.is_xtick = self.graphApp.get_xtick_check() self.is_ytick = self.graphApp.get_ytick_check() if self.is_xtick: self.xaxis_tick = self.xaxis_font if self.is_ytick: self.yaxis_tick = self.yaxis_font self.xaxis(self.xaxis_label, self.xaxis_unit, self.graphApp.get_xfont(), self.graphApp.get_xcolor(), self.xaxis_tick) self.yaxis(self.yaxis_label, self.yaxis_unit, self.graphApp.get_yfont(), self.graphApp.get_ycolor(), self.yaxis_tick) self.graphApp.Destroy()