source: sasview/sansguiframe/src/sans/guiframe/local_perspectives/plotting/masking.py @ 079072e

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 079072e was 3858e0f, checked in by Jae Cho <jhjcho@…>, 13 years ago

minor fix in saving as data

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