class MaskPanel(wx.Dialog): """ Provides the Mask Editor GUI. """ # # Internal nickname for the window, used by the AUI manager window_name = "Mask Editor" # # Name to appear on the window title bar window_caption = "Mask Editor" # # Flag to tell the AUI manager to put this panel in the center pane CENTER_PANE = True def __init__(self, parent=None, base=None, data=None, id=-1, *args, **kwds): kwds["style"] = wx.DEFAULT_DIALOG_STYLE kwds["size"] = wx.Size(_STATICBOX_WIDTH * 0.8, PANEL_SIZE) wx.Dialog.__init__(self, parent, id=id, *args, **kwds) if data != None: # Font size kwds = [] self.SetWindowVariant(variant=FONT_VARIANT) self.SetTitle("Mask Editor for " + data.name) self.parent = base self.data = data self.str = self.data.__str__() # # mask for 2D self.mask = data.mask self.default_mask = copy.deepcopy(data.mask) # # masked data from GUI self.slicer_mask = None self.slicer = None self.slicer_z = 5 self.data.interactive = True # # when 2 data have the same id override the 1 st plotted self.name = self.data.name # Panel for 2D plot self.plotpanel = Maskplotpanel(self, -1, style=wx.TRANSPARENT_WINDOW) self.cmap = DEFAULT_CMAP # # Create Artist and bind it self.subplot = self.plotpanel.subplot self.connect = BindArtist(self.subplot.figure) self._setup_layout() self.newplot = Data2D(image=self.data.data) self.newplot.setValues(self.data) self.plotpanel.add_image(self.newplot) self._update_mask(self.mask) self.Centre() self.Layout() # bind evt_close to _draw in fitpage self.Bind(wx.EVT_CLOSE, self.OnClose) def ShowMessage(self, msg=''): """ Show error message when mask covers whole data area """ mssg = 'Erase, redraw or clear the mask. \n\r' mssg += 'The data range can not be completely masked... \n\r' mssg += msg wx.MessageBox(mssg, 'Error', wx.OK | wx.ICON_ERROR) def _setup_layout(self): """ Set up the layout """ note = "Note: This masking applies\n only to %s." % self.data.name note_txt = wx.StaticText(self, -1, note) note_txt.SetForegroundColour(wx.RED) shape = "Select a Shape for Masking:" # panel sizer = wx.GridBagSizer(10, 10) #---------inputs---------------- shape_txt = wx.StaticText(self, -1, shape) sizer.Add(shape_txt, (1, 1), flag=wx.TOP | wx.LEFT | wx.BOTTOM, border=5) self.innersector_rb = wx.RadioButton(self, -1, "Double Wings") self.Bind(wx.EVT_RADIOBUTTON, partial(self._on_mask, slicer=SectorMask, inside=True), id=self.innersector_rb.GetId()) sizer.Add(self.innersector_rb, (2, 1), flag=wx.RIGHT | wx.BOTTOM, border=5) self.innercircle_rb = wx.RadioButton(self, -1, "Circular Disk") self.Bind(wx.EVT_RADIOBUTTON, partial(self._on_mask, slicer=CircularMask, inside=True), id=self.innercircle_rb.GetId()) sizer.Add(self.innercircle_rb, (3, 1), flag=wx.RIGHT | wx.BOTTOM, border=5) self.innerbox_rb = wx.RadioButton(self, -1, "Rectangular Disk") self.Bind(wx.EVT_RADIOBUTTON, partial(self._on_mask, slicer=BoxMask, inside=True), id=self.innerbox_rb.GetId()) sizer.Add(self.innerbox_rb, (4, 1), flag=wx.RIGHT | wx.BOTTOM, border=5) self.outersector_rb = wx.RadioButton(self, -1, "Double Wing Window") self.Bind(wx.EVT_RADIOBUTTON, partial(self._on_mask, slicer=SectorMask, inside=False), id=self.outersector_rb.GetId()) sizer.Add(self.outersector_rb, (5, 1), flag=wx.RIGHT | wx.BOTTOM, border=5) self.outercircle_rb = wx.RadioButton(self, -1, "Circular Window") self.Bind(wx.EVT_RADIOBUTTON, partial(self._on_mask, slicer=CircularMask, inside=False), id=self.outercircle_rb.GetId()) sizer.Add(self.outercircle_rb, (6, 1), flag=wx.RIGHT | wx.BOTTOM, border=5) self.outerbox_rb = wx.RadioButton(self, -1, "Rectangular Window") self.Bind(wx.EVT_RADIOBUTTON, partial(self._on_mask, slicer=BoxMask, inside=False), id=self.outerbox_rb.GetId()) sizer.Add(self.outerbox_rb, (7, 1), flag=wx.RIGHT | wx.BOTTOM, border=5) sizer.Add(note_txt, (8, 1), flag=wx.RIGHT | wx.BOTTOM, border=5) self.innercircle_rb.SetValue(False) self.outercircle_rb.SetValue(False) self.innerbox_rb.SetValue(False) self.outerbox_rb.SetValue(False) self.innersector_rb.SetValue(False) self.outersector_rb.SetValue(False) sizer.Add(self.plotpanel, (0, 2), (13, 13), wx.EXPAND | wx.LEFT | wx.RIGHT, 15) #-----Buttons------------1 id_button = wx.NewId() button_add = wx.Button(self, id_button, "Add") button_add.SetToolTipString("Add the mask drawn.") button_add.Bind(wx.EVT_BUTTON, self._on_add_mask, id=button_add.GetId()) sizer.Add(button_add, (13, 7)) id_button = wx.NewId() button_erase = wx.Button(self, id_button, "Erase") button_erase.SetToolTipString("Erase the mask drawn.") button_erase.Bind(wx.EVT_BUTTON, self._on_erase_mask, id=button_erase.GetId()) sizer.Add(button_erase, (13, 8)) id_button = wx.NewId() button_reset = wx.Button(self, id_button, "Reset") button_reset.SetToolTipString("Reset the mask.") button_reset.Bind(wx.EVT_BUTTON, self._on_reset_mask, id=button_reset.GetId()) sizer.Add(button_reset, (13, 9), flag=wx.RIGHT | wx.BOTTOM, border=15) id_button = wx.NewId() button_reset = wx.Button(self, id_button, "Clear") button_reset.SetToolTipString("Clear all mask.") button_reset.Bind(wx.EVT_BUTTON, self._on_clear_mask, id=button_reset.GetId()) sizer.Add(button_reset, (13, 10), flag=wx.RIGHT | wx.BOTTOM, border=15) sizer.AddGrowableCol(3) sizer.AddGrowableRow(2) self.SetSizerAndFit(sizer) self.Centre() self.Show(True) def _on_mask(self, event=None, slicer=BoxMask, inside=True): """ Draw a slicer and use it as mask :param event: wx event :param slicer: Slicer class to use :param inside: whether we mask what's inside or outside the slicer """ # get ready for next evt event.Skip() # from boxMask import BoxMask if event != None: self._on_clear_slicer(event) self.slicer_z += 1 self.slicer = slicer(self, self.subplot, zorder=self.slicer_z, side=inside) self.subplot.set_ylim(self.data.ymin, self.data.ymax) self.subplot.set_xlim(self.data.xmin, self.data.xmax) self.update() self.slicer_mask = self.slicer.update() def _on_add_mask(self, event): """ Add new mask to old mask """ if not self.slicer == None: data = Data2D() data = self.data self.slicer_mask = self.slicer.update() data.mask = self.data.mask & self.slicer_mask self._check_display_mask(data.mask, event) def _check_display_mask(self, mask, event): """ check if the mask valid and update the plot :param mask: mask data """ # # Redraw the current image self._update_mask(mask) def _on_erase_mask(self, event): """ Erase new mask from old mask """ if not self.slicer == None: self.slicer_mask = self.slicer.update() mask = self.data.mask mask[self.slicer_mask == False] = True self._check_display_mask(mask, event) def _on_reset_mask(self, event): """ Reset mask to the original mask """ self.slicer_z += 1 self.slicer = BoxMask(self, self.subplot, zorder=self.slicer_z, side=True) self.subplot.set_ylim(self.data.ymin, self.data.ymax) self.subplot.set_xlim(self.data.xmin, self.data.xmax) mask = copy.deepcopy(self.default_mask) self.data.mask = mask # update mask plot self._check_display_mask(mask, event) def _on_clear_mask(self, event): """ Clear mask """ self.slicer_z += 1 self.slicer = BoxMask(self, self.subplot, zorder=self.slicer_z, side=True) self.subplot.set_ylim(self.data.ymin, self.data.ymax) self.subplot.set_xlim(self.data.xmin, self.data.xmax) mask = numpy.ones(len(self.data.mask), dtype=bool) self.data.mask = mask # update mask plot self._check_display_mask(mask, event) def _on_clear_slicer(self, event): """ Clear the slicer on the plot """ if not self.slicer == None: self.slicer.clear() self.subplot.figure.canvas.draw() self.slicer = None def update(self, draw=True): """ Respond to changes in the model by recalculating the profiles and resetting the widgets. """ self.plotpanel.draw() def _set_mask(self, mask): """ Set mask """ self.data.mask = mask def set_plot_unfocus(self): """ Not implemented """ pass def _update_mask(self, mask): """ Respond to changes in masking """ # the case of liitle numbers of True points if len(mask[mask]) < 10 and self.data != None: self.ShowMessage() mask = copy.deepcopy(self.mask) self.data.mask = mask else: self.mask = mask # make temperary data to plot temp_mask = numpy.zeros(len(mask)) temp_data = copy.deepcopy(self.data) # temp_data default is None # This method is to distinguish between masked point and data point = 0. temp_mask = temp_mask / temp_mask temp_mask[mask] = temp_data.data[mask] # set temp_data value for self.mask==True, else still None # temp_mask[mask] = temp_data[mask] # TODO: refactor this horrible logic temp_data.data[mask == False] = temp_mask[mask == False] self.plotpanel.clear() if self.slicer != None: self.slicer.clear() self.slicer = None # Post slicer None event event = self._getEmptySlicerEvent() wx.PostEvent(self, event) # #use this method # set zmax and zmin to plot: Fix it w/ data. if self.plotpanel.scale == 'log_{10}': zmax = math.log10(max(self.data.data[self.data.data > 0])) zmin = math.log10(min(self.data.data[self.data.data > 0])) else: zmax = max(self.data.data[self.data.data > 0]) zmin = min(self.data.data[self.data.data > 0]) # plot self.plotpanel.image(data=temp_mask, qx_data=self.data.qx_data, qy_data=self.data.qy_data, xmin=self.data.xmin, xmax=self.data.xmax, ymin=self.data.ymin, ymax=self.data.ymax, zmin=zmin, zmax=zmax, cmap=self.cmap, color=0, symbol=0, label=self.data.name) # axis labels self.plotpanel.axes[0].set_xlabel('$\\rm{Q}_{x}(A^{-1})$') self.plotpanel.axes[0].set_ylabel('$\\rm{Q}_{y}(A^{-1})$') self.plotpanel.render() self.plotpanel.subplot.figure.canvas.draw_idle() def _getEmptySlicerEvent(self): """ create an empty slicervent """ self.innerbox_rb.SetValue(False) self.outerbox_rb.SetValue(False) self.innersector_rb.SetValue(False) self.outersector_rb.SetValue(False) self.innercircle_rb.SetValue(False) self.outercircle_rb.SetValue(False) return SlicerEvent(type=None, params=None, obj_class=None) def _draw_model(self, event): """ on_close, update the model2d plot """ pass def freeze_axes(self): """ freeze axes """ self.plotpanel.axes_frozen = True def thaw_axes(self): """ thaw axes """ self.plotpanel.axes_frozen = False def onMouseMotion(self, event): """ onMotion event """ pass def onWheel(self, event): """ on wheel event """ pass def OnClose(self, event): """ Processing close event """ try: self.parent._draw_masked_model(event) except: # when called by data panel event.Skip() pass