source: sasview/guiframe/local_perspectives/plotting/masking.py @ e5c102c

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 e5c102c was db72faf, checked in by Jae Cho <jhjcho@…>, 14 years ago

fixed minor bug

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