source: sasview/sansguiframe/src/sans/guiframe/local_perspectives/plotting/masking.py @ 9a3d433

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 9a3d433 was 8c347a6, checked in by Gervaise Alina <gervyh@…>, 13 years ago

moving guiframe under sansguiframe

  • Property mode set to 100644
File size: 21.1 KB
Line 
1
2################################################################################
3#This software was developed by the University of Tennessee as part of the
4#Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
5#project funded by the US National Science Foundation.
6#
7#If you use DANSE applications to do scientific research that leads to
8#publication, we ask that you acknowledge the use of the software with the
9#following sentence:
10#
11#This work benefited from DANSE software developed under NSF award DMR-0520547.
12#
13#copyright 2008, University of Tennessee
14################################################################################
15
16
17##Todo: cleaning up, improving the maskplotpanel initialization, and testing.
18import wx
19import sys
20import pylab
21import math
22import copy
23import numpy
24from danse.common.plottools.PlotPanel import PlotPanel
25from danse.common.plottools.plottables import Graph
26from binder import BindArtist
27from sans.guiframe.dataFitting import Data2D
28from boxMask import BoxMask
29from sectorMask import SectorMask
30from sans.guiframe.events import SlicerEvent
31from sans.guiframe.events import StatusEvent
32(InternalEvent, EVT_INTERNAL) = wx.lib.newevent.NewEvent()
33
34DEFAULT_CMAP = pylab.cm.jet
35_BOX_WIDTH = 76
36_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 _update_mask(self,mask):
394        """
395        Respond to changes in masking
396        """ 
397        # the case of liitle numbers of True points
398        if (len(mask[mask]) < 10 and self.data != None):
399            self.ShowMessage()
400            mask = copy.deepcopy(self.mask)
401            self.data.mask = mask
402        else:
403            self.mask = mask
404        # make temperary data to plot
405        temp_mask = numpy.zeros(len(mask))
406        temp_data = copy.deepcopy(self.data)
407        # temp_data default is None
408        # This method is to distinguish between masked point and data point = 0.
409        temp_mask = temp_mask/temp_mask
410        temp_mask[mask] = temp_data.data[mask]
411        # set temp_data value for self.mask==True, else still None
412        #temp_mask[mask] = temp_data[mask]
413        temp_data.data[mask==False] = temp_mask[mask==False]
414        self.plotpanel.clear()
415        if self.slicer != None:
416            self.slicer.clear()
417            self.slicer = None
418        # Post slicer None event
419        event = self._getEmptySlicerEvent()
420        wx.PostEvent(self, event)
421       
422        ##use this method
423        #set zmax and zmin to plot: Fix it w/ data.
424        if self.plotpanel.scale == 'log':
425            zmax = math.log(max(self.data.data[self.data.data>0]))
426            zmin = math.log(min(self.data.data[self.data.data>0]))
427        else:
428            zmax = max(self.data.data[self.data.data>0])
429            zmin = min(self.data.data[self.data.data>0])
430        #plot   
431        plot = self.plotpanel.image(data=temp_mask,
432                       qx_data=self.data.qx_data,
433                       qy_data=self.data.qy_data,
434                       xmin=self.data.xmin,
435                       xmax=self.data.xmax,
436                       ymin=self.data.ymin,
437                       ymax=self.data.ymax,
438                       zmin=zmin,
439                       zmax=zmax,
440                       cmap=self.cmap,
441                       color=0, symbol=0, label=self.data.name)
442        # axis labels
443        self.plotpanel.axes[0].set_xlabel('$\\rm{Q}_{x}(A^{-1})$')
444        self.plotpanel.axes[0].set_ylabel('$\\rm{Q}_{y}(A^{-1})$')
445        self.plotpanel.render()
446        self.plotpanel.subplot.figure.canvas.draw_idle()
447       
448    def _getEmptySlicerEvent(self):
449        """
450        create an empty slicervent
451        """
452        self.innerbox_rb.SetValue(False)
453        self.outerbox_rb.SetValue(False)
454        self.innersector_rb.SetValue(False)
455        self.outersector_rb.SetValue(False)
456        self.innercircle_rb.SetValue(False)
457        self.outercircle_rb.SetValue(False)
458        return SlicerEvent(type=None,
459                           params=None,
460                           obj_class=None) 
461             
462    def _draw_model(self, event):
463        """
464         on_close, update the model2d plot
465        """
466        pass
467       
468    def freeze_axes(self):
469        """
470        freeze axes
471        """
472        self.plotpanel.axes_frozen = True
473       
474    def thaw_axes(self):
475        """
476        thaw axes
477        """
478        self.plotpanel.axes_frozen = False       
479         
480    def onMouseMotion(self, event):
481        """
482        onMotion event
483        """
484        pass
485   
486    def onWheel(self, event):
487        """
488        on wheel event
489        """
490        pass 
491   
492    def OnClose(self, event):
493        """
494        """
495        self.parent._draw_masked_model(event)
496       
497class Maskplotpanel(PlotPanel):
498    """
499    """
500    def __init__(self, parent, id=-1, color=None, dpi=None, **kwargs):
501        """
502        """
503        PlotPanel.__init__(self, parent, id=id, color=color, dpi=dpi, **kwargs)
504       
505        # Keep track of the parent Frame
506        self.parent = parent
507        # Internal list of plottable names (because graph
508        # doesn't have a dictionary of handles for the plottables)
509        self.plots = {}
510        self.graph = Graph()
511       
512    def add_toolbar(self):
513        """
514        Add toolbar
515        """
516        # Not implemented
517        pass
518    def on_set_focus(self, event):
519        """
520        send to the parenet the current panel on focus
521        """
522        #change the panel background
523        #self.SetColor((170, 202, 255))
524        self.draw()   
525         
526    def add_image(self, plot):
527        """
528        Add Image
529        """
530        self.plots[plot.name] = plot
531        #init graph
532        self.gaph = Graph()
533        #add plot
534        self.graph.add(plot)
535        #add axes
536        self.graph.xaxis('\\rm{Q}_{x} ', 'A^{-1}')
537        self.graph.yaxis('\\rm{Q}_{y} ', 'A^{-1}')
538        #draw
539        self.graph.render(self)
540        self.subplot.figure.canvas.draw_idle()
541       
542    def onMouseMotion(self, event):
543        """
544        Disable dragging 2D image
545        """
546        pass
547   
548    def onContextMenu(self, event):
549        """
550        Default context menu for a plot panel
551        """
552        # Slicer plot popup menu
553        slicerpop = wx.Menu()
554       
555        id_cm = wx.NewId()
556        slicerpop.Append(id_cm, '&Toggle Linear/Log scale')
557        wx.EVT_MENU(self, id_cm, self._onToggleScale)
558               
559        pos = event.GetPosition()
560        pos = self.ScreenToClient(pos)
561        self.PopupMenu(slicerpop, pos)
562
563class ViewerFrame(wx.Frame):
564    """
565    Add comment
566    """
567    def __init__(self, parent, id, title):
568        """
569        comment
570        :param parent: parent panel/container
571        """
572        # Initialize the Frame object
573        wx.Frame.__init__(self, parent, id, title,
574                          wx.DefaultPosition, wx.Size(950, 850))
575        # Panel for 1D plot
576        self.plotpanel = Maskplotpanel(self, -1, style=wx.RAISED_BORDER)
577
578class ViewApp(wx.App):
579    def OnInit(self):
580        frame = ViewerFrame(None, -1, 'testView')   
581        frame.Show(True)
582        #self.SetTopWindow(frame)
583       
584        return True
585               
586if __name__ == "__main__": 
587    app = ViewApp(0)
588    app.MainLoop()     
Note: See TracBrowser for help on using the repository browser.