source: sasview/guiframe/local_perspectives/plotting/masking.py @ 4a2b054

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 4a2b054 was 0aee80d, checked in by Jae Cho <jhjcho@…>, 14 years ago

made labels consistant w/ other 2d plots

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