source: sasview/guiframe/local_perspectives/plotting/masking.py @ 9d9fd94

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 9d9fd94 was 9d9fd94, checked in by Jae Cho <jhjcho@…>, 13 years ago

try to fix focusing

  • Property mode set to 100644
File size: 21.1 KB
Line 
1
2################################################################################
3#This software was developed by the University of Tennessee as part of the
4#Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
5#project funded by the US National Science Foundation.
6#
7#If you use DANSE applications to do scientific research that leads to
8#publication, we ask that you acknowledge the use of the software with the
9#following sentence:
10#
11#This work benefited from DANSE software developed under NSF award DMR-0520547.
12#
13#copyright 2008, University of Tennessee
14################################################################################
15
16
17##Todo: cleaning up, improving the maskplotpanel initialization, and testing.
18import wx
19import sys
20import pylab
21import math
22import copy
23import numpy
24from danse.common.plottools.PlotPanel import PlotPanel
25from danse.common.plottools.plottables import Graph
26from binder import BindArtist
27from sans.guiframe.dataFitting import Data2D
28from boxMask import BoxMask
29from sectorMask import SectorMask
30from sans.guiframe.events import SlicerEvent
31from sans.guiframe.events import StatusEvent
32(InternalEvent, EVT_INTERNAL) = wx.lib.newevent.NewEvent()
33
34DEFAULT_CMAP = pylab.cm.jet
35_BOX_WIDTH = 76
36_STATICBOX_WIDTH = 400
37_SCALE = 1e-6
38
39_STATICBOX_WIDTH = 380
40PANEL_SIZE = 420
41#SLD panel size
42if sys.platform.count("win32") > 0:
43    FONT_VARIANT = 0
44else:
45    FONT_VARIANT = 1
46   
47
48class MaskPanel(wx.Dialog):
49    """
50    Provides the Mask Editor GUI.
51    """
52    ## Internal nickname for the window, used by the AUI manager
53    window_name = "Mask Editor"
54    ## Name to appear on the window title bar
55    window_caption = "Mask Editor"
56    ## Flag to tell the AUI manager to put this panel in the center pane
57    CENTER_PANE = True
58    def __init__(self, parent=None, base=None, 
59                 data=None, id=-1, *args, **kwds):
60        kwds["style"] = wx.DEFAULT_DIALOG_STYLE
61        kwds["size"] = wx.Size(_STATICBOX_WIDTH * 2, PANEL_SIZE) 
62        wx.Dialog.__init__(self, parent, id=id,  *args, **kwds)
63       
64        if data != None:
65            #Font size
66            kwds = []
67            self.SetWindowVariant(variant=FONT_VARIANT)
68            self.SetTitle("Mask Editor for " + data.name)
69            self.parent = base
70            self.data = data
71            self.str = self.data.__str__()
72            ## mask for 2D
73            self.mask = data.mask
74            self.default_mask = copy.deepcopy(data.mask)
75            ## masked data from GUI
76            self.slicer_mask = None
77            self.slicer = None
78            self.slicer_z = 5
79            self.data.interactive = True
80            ## when 2 data have the same id override the 1 st plotted
81            self.name = self.data.name
82            # Panel for 2D plot
83            self.plotpanel = Maskplotpanel(self, -1,
84                                           style=wx.TRANSPARENT_WINDOW)
85            self.cmap = DEFAULT_CMAP
86            ## Create Artist and bind it
87            self.subplot = self.plotpanel.subplot
88            self.connect = BindArtist(self.subplot.figure)
89            self._setup_layout()
90            self.newplot = Data2D(image=self.data.data)
91            self.newplot.setValues(self.data)
92            self.plotpanel.add_image(self.newplot) 
93            self._update_mask(self.mask)
94            self.Centre()
95            self.Layout()
96            # bind evt_close to _draw in fitpage
97            self.Bind(wx.EVT_CLOSE, self._draw_model)
98           
99    def ShowMessage(self, msg=''):
100        """
101        Show error message when mask covers whole data area
102        """
103        mssg = 'Erase, redraw or clear the mask. \n\r'
104        mssg += 'The data range can not be completely masked... \n\r'
105        mssg += msg
106        wx.MessageBox(mssg, 'Error', wx.OK | wx.ICON_ERROR)
107   
108    def _setup_layout(self):
109        """
110        Set up the layout
111        """
112        shape = "Select a Shape for Masking:"
113        #  panel
114        sizer = wx.GridBagSizer(10, 10)
115        #---------inputs----------------
116        shape_txt = wx.StaticText(self, -1, shape) 
117        sizer.Add(shape_txt, (1, 1), flag=wx.TOP|wx.LEFT|wx.BOTTOM, border=5)
118        self.innersector_rb = wx.RadioButton(self, -1, "Double Wings")
119        self.Bind(wx.EVT_RADIOBUTTON, self.onInnerSectorMask,
120                  id=self.innersector_rb.GetId())
121        sizer.Add(self.innersector_rb, (2, 1), 
122                  flag=wx.RIGHT|wx.BOTTOM, border=5)
123        self.innercircle_rb = wx.RadioButton(self, -1, "Circular Disk")
124        self.Bind(wx.EVT_RADIOBUTTON, self.onInnerRingMask,
125                  id=self.innercircle_rb.GetId())
126        sizer.Add(self.innercircle_rb, (3, 1),
127                   flag=wx.RIGHT|wx.BOTTOM, border=5)
128       
129        self.innerbox_rb = wx.RadioButton(self, -1, "Rectangular Disk")
130        self.Bind(wx.EVT_RADIOBUTTON, self.onInnerBoxMask,
131                  id=self.innerbox_rb.GetId())
132        sizer.Add(self.innerbox_rb, (4, 1), flag=wx.RIGHT|wx.BOTTOM, border=5)
133
134        self.outersector_rb = wx.RadioButton(self, -1, "Double Wing Window")
135        self.Bind(wx.EVT_RADIOBUTTON, self.onOuterSectorMask, 
136                  id=self.outersector_rb.GetId())
137        sizer.Add(self.outersector_rb, (5, 1),
138                  flag=wx.RIGHT|wx.BOTTOM, border=5)
139       
140        #outersector_y_txt = wx.StaticText(self, -1, 'Outer Sector')
141        self.outercircle_rb = wx.RadioButton(self, -1, "Circular Window")
142        self.Bind(wx.EVT_RADIOBUTTON, self.onOuterRingMask,
143                  id=self.outercircle_rb.GetId())
144        sizer.Add(self.outercircle_rb, (6, 1), 
145                  flag=wx.RIGHT|wx.BOTTOM, border=5)
146        #outerbox_txt = wx.StaticText(self, -1, 'Outer Box')
147        self.outerbox_rb = wx.RadioButton(self, -1, "Rectangular Window")
148        self.Bind(wx.EVT_RADIOBUTTON, self.onOuterBoxMask, 
149                  id=self.outerbox_rb.GetId())
150        sizer.Add(self.outerbox_rb, (7, 1), flag=wx.RIGHT|wx.BOTTOM, border=5)
151        self.innercircle_rb.SetValue(False)
152        self.outercircle_rb.SetValue(False)       
153        self.innerbox_rb.SetValue(False)
154        self.outerbox_rb.SetValue(False)
155        self.innersector_rb.SetValue(False)
156        self.outersector_rb.SetValue(False)
157        sizer.Add(self.plotpanel, (0, 2), (13, 13), 
158                  wx.EXPAND|wx.LEFT|wx.RIGHT, 15)
159
160        #-----Buttons------------1
161        id_button = wx.NewId()
162        button_add = wx.Button(self, id_button, "Add")
163        button_add.SetToolTipString("Add the mask drawn.")
164        button_add.Bind(wx.EVT_BUTTON, self.onAddMask, id=button_add.GetId()) 
165        sizer.Add(button_add, (13, 7))
166        id_button = wx.NewId()
167        button_erase = wx.Button(self, id_button, "Erase")
168        button_erase.SetToolTipString("Erase the mask drawn.")
169        button_erase.Bind(wx.EVT_BUTTON, self.onEraseMask,
170                          id=button_erase.GetId()) 
171        sizer.Add(button_erase, (13, 8))
172        id_button = wx.NewId()
173        button_reset = wx.Button(self, id_button, "Reset")
174        button_reset.SetToolTipString("Reset the mask.")
175        button_reset.Bind(wx.EVT_BUTTON, self.onResetMask,
176                          id=button_reset.GetId()) 
177        sizer.Add(button_reset, (13, 9), flag=wx.RIGHT|wx.BOTTOM, border=15)
178        id_button = wx.NewId()
179        button_reset = wx.Button(self, id_button, "Clear")
180        button_reset.SetToolTipString("Clear all mask.")
181        button_reset.Bind(wx.EVT_BUTTON, self.onClearMask,
182                          id=button_reset.GetId()) 
183        sizer.Add(button_reset, (13, 10), flag=wx.RIGHT|wx.BOTTOM, border=15)
184        sizer.AddGrowableCol(3)
185        sizer.AddGrowableRow(2)
186        self.SetSizerAndFit(sizer)
187        self.Centre()
188        self.Show(True)
189
190    def onInnerBoxMask(self, event=None):
191        """
192        Call Draw Box Slicer and get mask inside of the box
193        """
194        #get ready for next evt
195        event.Skip()       
196        #from boxMask import BoxMask
197        if event != None:
198            self.onClearSlicer(event)         
199        self.slicer_z += 1
200        self.slicer =  BoxMask(self, self.subplot,
201                               zorder=self.slicer_z, side=True)
202        self.subplot.set_ylim(self.data.ymin, self.data.ymax)
203        self.subplot.set_xlim(self.data.xmin, self.data.xmax)
204        self.update()
205        self.slicer_mask = self.slicer.update()
206       
207    def onOuterBoxMask(self, event=None):
208        """
209        Call Draw Box Slicer and get mask outside of the box
210        """
211        event.Skip()       
212        #from boxMask import BoxMask
213        if event != None:
214            self.onClearSlicer(event)     
215        self.slicer_z += 1
216        self.slicer =  BoxMask(self, self.subplot,
217                               zorder=self.slicer_z, side=False)
218        self.subplot.set_ylim(self.data.ymin, self.data.ymax)
219        self.subplot.set_xlim(self.data.xmin, self.data.xmax)
220        self.update()
221        self.slicer_mask = self.slicer.update()
222
223    def onInnerSectorMask(self, event=None):
224        """
225        Call Draw Sector Slicer and get mask inside of the sector
226        """
227        event.Skip()
228        from sectorMask import SectorMask
229        if event != None:
230            self.onClearSlicer(event)
231        self.slicer_z += 1
232        self.slicer =  SectorMask(self, self.subplot,
233                                  zorder=self.slicer_z, side=True)
234        self.subplot.set_ylim(self.data.ymin, self.data.ymax)
235        self.subplot.set_xlim(self.data.xmin, self.data.xmax)   
236        self.update()
237        self.slicer_mask = self.slicer.update() 
238
239    def onOuterSectorMask(self,event=None):
240        """
241        Call Draw Sector Slicer and get mask outside of the sector
242        """
243        event.Skip()
244        from sectorMask import SectorMask
245        if event != None:
246            self.onClearSlicer(event)
247        self.slicer_z += 1
248        self.slicer =  SectorMask(self, self.subplot,
249                                  zorder=self.slicer_z, side=False)
250        self.subplot.set_ylim(self.data.ymin, self.data.ymax)
251        self.subplot.set_xlim(self.data.xmin, self.data.xmax)   
252        self.update()     
253        self.slicer_mask = self.slicer.update()   
254
255    def onInnerRingMask(self, event=None):
256        """
257        Perform inner circular cut on Phi and draw circular slicer
258        """
259        event.Skip()
260        from AnnulusSlicer import CircularMask
261        if event != None:
262            self.onClearSlicer(event)
263        self.slicer_z += 1
264        self.slicer = CircularMask(self, self.subplot,
265                                   zorder=self.slicer_z, side=True)
266        self.subplot.set_ylim(self.data.ymin, self.data.ymax)
267        self.subplot.set_xlim(self.data.xmin, self.data.xmax)   
268        self.update()
269        self.slicer_mask = self.slicer.update() 
270
271    def onOuterRingMask(self, event=None):
272        """
273        Perform outer circular cut on Phi and draw circular slicer
274        """
275        event.Skip()
276        from AnnulusSlicer import CircularMask
277        if event != None:
278            self.onClearSlicer(event)
279        self.slicer_z += 1
280        self.slicer = CircularMask(self,self.subplot,
281                                   zorder=self.slicer_z, side=False)   
282        self.subplot.set_ylim(self.data.ymin, self.data.ymax)
283        self.subplot.set_xlim(self.data.xmin, self.data.xmax)
284        self.update()
285        self.slicer_mask = self.slicer.update()     
286       
287    def onAddMask(self, event):
288        """
289        Add new mask to old mask
290        """
291        if not self.slicer == None:
292            data = Data2D()
293            data = self.data
294            self.slicer_mask = self.slicer.update()
295            data.mask = self.data.mask & self.slicer_mask
296            self._check_display_mask(data.mask, event)
297           
298    def _check_display_mask(self, mask, event):
299        """
300        check if the mask valid and update the plot
301       
302        :param mask: mask data
303        """
304        ## Redraw the current image
305        self._update_mask(mask)
306
307    def onEraseMask(self, event):
308        """
309        Erase new mask from old mask
310        """
311        if not self.slicer==None:
312            self.slicer_mask = self.slicer.update()
313            mask = self.data.mask
314            mask[self.slicer_mask==False] = True
315            self._check_display_mask(mask, event)
316           
317    def onResetMask(self, event):
318        """
319        Reset mask to the original mask
320        """       
321        self.slicer_z += 1
322        self.slicer =  BoxMask(self, self.subplot, 
323                               zorder=self.slicer_z, side=True)
324        self.subplot.set_ylim(self.data.ymin, self.data.ymax)
325        self.subplot.set_xlim(self.data.xmin, self.data.xmax)   
326        mask = copy.deepcopy(self.default_mask)
327        self.data.mask = mask
328        # update mask plot
329        self._check_display_mask(mask, event)
330       
331    def onClearMask(self, event):
332        """
333        Clear mask
334        """           
335        self.slicer_z += 1
336        self.slicer =  BoxMask(self, self.subplot,
337                               zorder=self.slicer_z, side=True)
338        self.subplot.set_ylim(self.data.ymin, self.data.ymax)
339        self.subplot.set_xlim(self.data.xmin, self.data.xmax)   
340        #mask = copy.deepcopy(self.default_mask)
341        mask = numpy.ones(len(self.data.mask), dtype=bool)
342        self.data.mask = mask
343        # update mask plot
344        self._check_display_mask(mask, event)
345       
346    def onClearSlicer(self, event):
347        """
348        Clear the slicer on the plot
349        """
350        if not self.slicer == None:
351            self.slicer.clear()
352            self.subplot.figure.canvas.draw()
353            self.slicer = None
354
355    def _setSlicer(self):
356        """
357        Clear the previous slicer and create a new one.Post an internal
358        event.
359       
360        :param slicer: slicer class to create
361        """
362        ## Clear current slicer
363        if not self.slicer == None: 
364            self.slicer.clear()           
365        ## Create a new slicer   
366        self.slicer_z += 1
367        self.slicer = slicer(self, self.subplot, zorder=self.slicer_z)
368        self.subplot.set_ylim(self.data2D.ymin, self.data2D.ymax)
369        self.subplot.set_xlim(self.data2D.xmin, self.data2D.xmax)
370        ## Draw slicer
371        self.update()
372        self.slicer.update()
373        msg = "Plotter2D._setSlicer  %s"%self.slicer.__class__.__name__
374        wx.PostEvent(self.parent, StatusEvent(status=msg))
375        # Post slicer event
376        event = self._getEmptySlicerEvent()
377        event.type = self.slicer.__class__.__name__
378        event.obj_class = self.slicer.__class__
379        event.params = self.slicer.get_params()
380        wx.PostEvent(self, event)
381   
382    def update(self, draw=True):
383        """
384        Respond to changes in the model by recalculating the
385        profiles and resetting the widgets.
386        """
387        self.plotpanel.draw()
388       
389    def _set_mask(self, mask):
390        """
391        Set mask
392        """
393        self.data.mask = mask
394       
395    def _update_mask(self,mask):
396        """
397        Respond to changes in masking
398        """ 
399        # the case of liitle numbers of True points
400        if (len(mask[mask]) < 10 and self.data != None):
401            self.ShowMessage()
402            mask = copy.deepcopy(self.mask)
403            self.data.mask = mask
404        else:
405            self.mask = mask
406        # make temperary data to plot
407        temp_mask = numpy.zeros(len(mask))
408        temp_data = copy.deepcopy(self.data)
409        # temp_data default is None
410        # This method is to distinguish between masked point and data point = 0.
411        temp_mask = temp_mask/temp_mask
412        temp_mask[mask] = temp_data.data[mask]
413        # set temp_data value for self.mask==True, else still None
414        #temp_mask[mask] = temp_data[mask]
415        temp_data.data[mask==False] = temp_mask[mask==False]
416        self.plotpanel.clear()
417        if self.slicer != None:
418            self.slicer.clear()
419            self.slicer = None
420        # Post slicer None event
421        event = self._getEmptySlicerEvent()
422        wx.PostEvent(self, event)
423       
424        ##use this method
425        #set zmax and zmin to plot: Fix it w/ data.
426        if self.plotpanel.scale == 'log':
427            zmax = math.log(max(self.data.data[self.data.data>0]))
428            zmin = math.log(min(self.data.data[self.data.data>0]))
429        else:
430            zmax = max(self.data.data[self.data.data>0])
431            zmin = min(self.data.data[self.data.data>0])
432        #plot   
433        plot = self.plotpanel.image(data=temp_mask,
434                       qx_data=self.data.qx_data,
435                       qy_data=self.data.qy_data,
436                       xmin=self.data.xmin,
437                       xmax=self.data.xmax,
438                       ymin=self.data.ymin,
439                       ymax=self.data.ymax,
440                       zmin=zmin,
441                       zmax=zmax,
442                       cmap=self.cmap,
443                       color=0, symbol=0, label=self.data.name)
444        # axis labels
445        self.plotpanel.axes[0].set_xlabel('$\\rm{Q}_{x}(A^{-1})$')
446        self.plotpanel.axes[0].set_ylabel('$\\rm{Q}_{y}(A^{-1})$')
447        self.plotpanel.render()
448        self.plotpanel.subplot.figure.canvas.draw_idle()
449       
450    def _getEmptySlicerEvent(self):
451        """
452        create an empty slicervent
453        """
454        self.innerbox_rb.SetValue(False)
455        self.outerbox_rb.SetValue(False)
456        self.innersector_rb.SetValue(False)
457        self.outersector_rb.SetValue(False)
458        self.innercircle_rb.SetValue(False)
459        self.outercircle_rb.SetValue(False)
460        return SlicerEvent(type=None,
461                           params=None,
462                           obj_class=None) 
463             
464    def _draw_model(self, event):
465        """
466         on_close, update the model2d plot
467        """
468        pass
469       
470    def freeze_axes(self):
471        """
472        freeze axes
473        """
474        self.plotpanel.axes_frozen = True
475       
476    def thaw_axes(self):
477        """
478        thaw axes
479        """
480        self.plotpanel.axes_frozen = False       
481         
482    def onMouseMotion(self, event):
483        """
484        onMotion event
485        """
486        pass
487   
488    def onWheel(self, event):
489        """
490        on wheel event
491        """
492        pass 
493   
494    def OnClose(self, event):
495        """
496        """
497        event.Skip()
498        self.Destroy()
499           
500class Maskplotpanel(PlotPanel):
501    """
502    """
503    def __init__(self, parent, id=-1, color=None, dpi=None, **kwargs):
504        """
505        """
506        PlotPanel.__init__(self, parent, id=id, color=color, dpi=dpi, **kwargs)
507       
508        # Keep track of the parent Frame
509        self.parent = parent
510        # Internal list of plottable names (because graph
511        # doesn't have a dictionary of handles for the plottables)
512        self.plots = {}
513        self.graph = Graph()
514       
515    def add_toolbar(self):
516        """
517        Add toolbar
518        """
519        # Not implemented
520        pass
521    def on_set_focus(self, event):
522        """
523        send to the parenet the current panel on focus
524        """
525        #change the panel background
526        #self.SetColor((170, 202, 255))
527        self.draw()   
528         
529    def add_image(self, plot):
530        """
531        Add Image
532        """
533        self.plots[plot.name] = plot
534        #init graph
535        self.gaph = Graph()
536        #add plot
537        self.graph.add(plot)
538        #add axes
539        self.graph.xaxis('\\rm{Q}_{x} ', 'A^{-1}')
540        self.graph.yaxis('\\rm{Q}_{y} ', 'A^{-1}')
541        #draw
542        self.graph.render(self)
543        self.subplot.figure.canvas.draw_idle()
544       
545    def onMouseMotion(self, event):
546        """
547        Disable dragging 2D image
548        """
549        pass
550   
551    def onContextMenu(self, event):
552        """
553        Default context menu for a plot panel
554        """
555        # Slicer plot popup menu
556        slicerpop = wx.Menu()
557       
558        id_cm = wx.NewId()
559        slicerpop.Append(id_cm, '&Toggle Linear/Log scale')
560        wx.EVT_MENU(self, id_cm, self._onToggleScale)
561               
562        pos = event.GetPosition()
563        pos = self.ScreenToClient(pos)
564        self.PopupMenu(slicerpop, pos)
565
566
567"""
568class ViewerFrame(wx.Frame):
569    def __init__(self, parent, id, title):
570        #comment
571        #:param parent: parent panel/container
572        # Initialize the Frame object
573        wx.Frame.__init__(self, parent, id, title,
574                          wx.DefaultPosition, wx.Size(950, 850))
575        # Panel for 1D plot
576        self.plotpanel = Maskplotpanel(self, -1, style=wx.RAISED_BORDER)
577
578class ViewApp(wx.App):
579    def OnInit(self):
580        frame = ViewerFrame(None, -1, 'testView')   
581        frame.Show(True)
582        #self.SetTopWindow(frame)
583       
584        return True
585               
586if __name__ == "__main__":
587    app = ViewApp(0)
588    app.MainLoop()     
589"""
Note: See TracBrowser for help on using the repository browser.