source: sasview/guiframe/local_perspectives/plotting/masking.py @ 2f6c260

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 2f6c260 was c5874f2, checked in by Jae Cho <jhjcho@…>, 15 years ago

mask interactor added

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