Index: guiframe/local_perspectives/plotting/masking.py
===================================================================
--- guiframe/local_perspectives/plotting/masking.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
+++ guiframe/local_perspectives/plotting/masking.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
@@ -0,0 +1,588 @@
+
+################################################################################
+#This software was developed by the University of Tennessee as part of the
+#Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
+#project funded by the US National Science Foundation. 
+#
+#If you use DANSE applications to do scientific research that leads to 
+#publication, we ask that you acknowledge the use of the software with the 
+#following sentence:
+#
+#This work benefited from DANSE software developed under NSF award DMR-0520547. 
+#
+#copyright 2008, University of Tennessee
+################################################################################
+
+
+##Todo: cleaning up, improving the maskplotpanel initialization, and testing.
+import wx
+import sys
+import pylab
+import math
+import copy
+import numpy
+from danse.common.plottools.PlotPanel import PlotPanel
+from danse.common.plottools.plottables import Graph
+from binder import BindArtist
+from sans.guiframe.dataFitting import Data2D
+from boxMask import BoxMask
+from sectorMask import SectorMask
+from sans.guiframe.events import SlicerEvent
+from sans.guiframe.events import StatusEvent
+(InternalEvent, EVT_INTERNAL) = wx.lib.newevent.NewEvent()
+
+DEFAULT_CMAP = pylab.cm.jet
+_BOX_WIDTH = 76
+_SCALE = 1e-6
+_STATICBOX_WIDTH = 380
+PANEL_SIZE = 420
+#SLD panel size 
+if sys.platform.count("win32") > 0:
+    FONT_VARIANT = 0
+else:
+    FONT_VARIANT = 1
+    
+
+class MaskPanel(wx.Dialog):
+    """
+    Provides the Mask Editor GUI.
+    """
+    ## Internal nickname for the window, used by the AUI manager
+    window_name = "Mask Editor"
+    ## Name to appear on the window title bar
+    window_caption = "Mask Editor"
+    ## Flag to tell the AUI manager to put this panel in the center pane
+    CENTER_PANE = True
+    def __init__(self, parent=None, base=None, 
+                 data=None, id=-1, *args, **kwds):
+        kwds["style"] = wx.DEFAULT_DIALOG_STYLE
+        kwds["size"] = wx.Size(_STATICBOX_WIDTH * 2, PANEL_SIZE) 
+        wx.Dialog.__init__(self, parent, id=id,  *args, **kwds)
+        
+        if data != None:
+            #Font size 
+            kwds = []
+            self.SetWindowVariant(variant=FONT_VARIANT)
+            self.SetTitle("Mask Editor for " + data.name)
+            self.parent = base
+            self.data = data
+            self.str = self.data.__str__()
+            ## mask for 2D
+            self.mask = data.mask
+            self.default_mask = copy.deepcopy(data.mask)
+            ## masked data from GUI
+            self.slicer_mask = None
+            self.slicer = None
+            self.slicer_z = 5
+            self.data.interactive = True
+            ## when 2 data have the same id override the 1 st plotted
+            self.name = self.data.name
+            # Panel for 2D plot
+            self.plotpanel = Maskplotpanel(self, -1,
+                                           style=wx.TRANSPARENT_WINDOW)
+            self.cmap = DEFAULT_CMAP
+            ## Create Artist and bind it
+            self.subplot = self.plotpanel.subplot
+            self.connect = BindArtist(self.subplot.figure)
+            self._setup_layout()
+            self.newplot = Data2D(image=self.data.data)
+            self.newplot.setValues(self.data)
+            self.plotpanel.add_image(self.newplot) 
+            self._update_mask(self.mask)
+            self.Centre()
+            self.Layout()
+            # bind evt_close to _draw in fitpage
+            self.Bind(wx.EVT_CLOSE, self.OnClose)
+            
+    def ShowMessage(self, msg=''):
+        """
+        Show error message when mask covers whole data area
+        """
+        mssg = 'Erase, redraw or clear the mask. \n\r'
+        mssg += 'The data range can not be completely masked... \n\r'
+        mssg += msg
+        wx.MessageBox(mssg, 'Error', wx.OK | wx.ICON_ERROR)
+    
+    def _setup_layout(self):
+        """
+        Set up the layout
+        """
+        shape = "Select a Shape for Masking:"
+        #  panel
+        sizer = wx.GridBagSizer(10, 10)
+        #---------inputs----------------
+        shape_txt = wx.StaticText(self, -1, shape)  
+        sizer.Add(shape_txt, (1, 1), flag=wx.TOP|wx.LEFT|wx.BOTTOM, border=5)
+        self.innersector_rb = wx.RadioButton(self, -1, "Double Wings")
+        self.Bind(wx.EVT_RADIOBUTTON, self.onInnerSectorMask,
+                  id=self.innersector_rb.GetId())
+        sizer.Add(self.innersector_rb, (2, 1), 
+                  flag=wx.RIGHT|wx.BOTTOM, border=5)
+        self.innercircle_rb = wx.RadioButton(self, -1, "Circular Disk")
+        self.Bind(wx.EVT_RADIOBUTTON, self.onInnerRingMask,
+                  id=self.innercircle_rb.GetId())
+        sizer.Add(self.innercircle_rb, (3, 1),
+                   flag=wx.RIGHT|wx.BOTTOM, border=5)
+        
+        self.innerbox_rb = wx.RadioButton(self, -1, "Rectangular Disk")
+        self.Bind(wx.EVT_RADIOBUTTON, self.onInnerBoxMask,
+                  id=self.innerbox_rb.GetId())
+        sizer.Add(self.innerbox_rb, (4, 1), flag=wx.RIGHT|wx.BOTTOM, border=5)
+
+        self.outersector_rb = wx.RadioButton(self, -1, "Double Wing Window")
+        self.Bind(wx.EVT_RADIOBUTTON, self.onOuterSectorMask, 
+                  id=self.outersector_rb.GetId())
+        sizer.Add(self.outersector_rb, (5, 1),
+                  flag=wx.RIGHT|wx.BOTTOM, border=5)
+        
+        #outersector_y_txt = wx.StaticText(self, -1, 'Outer Sector')
+        self.outercircle_rb = wx.RadioButton(self, -1, "Circular Window")
+        self.Bind(wx.EVT_RADIOBUTTON, self.onOuterRingMask,
+                  id=self.outercircle_rb.GetId())
+        sizer.Add(self.outercircle_rb, (6, 1), 
+                  flag=wx.RIGHT|wx.BOTTOM, border=5)
+        #outerbox_txt = wx.StaticText(self, -1, 'Outer Box')
+        self.outerbox_rb = wx.RadioButton(self, -1, "Rectangular Window")
+        self.Bind(wx.EVT_RADIOBUTTON, self.onOuterBoxMask, 
+                  id=self.outerbox_rb.GetId())
+        sizer.Add(self.outerbox_rb, (7, 1), flag=wx.RIGHT|wx.BOTTOM, border=5)
+        self.innercircle_rb.SetValue(False)
+        self.outercircle_rb.SetValue(False)        
+        self.innerbox_rb.SetValue(False)
+        self.outerbox_rb.SetValue(False)
+        self.innersector_rb.SetValue(False)
+        self.outersector_rb.SetValue(False)
+        sizer.Add(self.plotpanel, (0, 2), (13, 13), 
+                  wx.EXPAND|wx.LEFT|wx.RIGHT, 15)
+
+        #-----Buttons------------1
+        id_button = wx.NewId()
+        button_add = wx.Button(self, id_button, "Add")
+        button_add.SetToolTipString("Add the mask drawn.")
+        button_add.Bind(wx.EVT_BUTTON, self.onAddMask, id=button_add.GetId()) 
+        sizer.Add(button_add, (13, 7))
+        id_button = wx.NewId()
+        button_erase = wx.Button(self, id_button, "Erase")
+        button_erase.SetToolTipString("Erase the mask drawn.")
+        button_erase.Bind(wx.EVT_BUTTON, self.onEraseMask,
+                          id=button_erase.GetId()) 
+        sizer.Add(button_erase, (13, 8))
+        id_button = wx.NewId()
+        button_reset = wx.Button(self, id_button, "Reset")
+        button_reset.SetToolTipString("Reset the mask.")
+        button_reset.Bind(wx.EVT_BUTTON, self.onResetMask,
+                          id=button_reset.GetId()) 
+        sizer.Add(button_reset, (13, 9), flag=wx.RIGHT|wx.BOTTOM, border=15)
+        id_button = wx.NewId()
+        button_reset = wx.Button(self, id_button, "Clear")
+        button_reset.SetToolTipString("Clear all mask.")
+        button_reset.Bind(wx.EVT_BUTTON, self.onClearMask,
+                          id=button_reset.GetId()) 
+        sizer.Add(button_reset, (13, 10), flag=wx.RIGHT|wx.BOTTOM, border=15)
+        sizer.AddGrowableCol(3)
+        sizer.AddGrowableRow(2)
+        self.SetSizerAndFit(sizer)
+        self.Centre()
+        self.Show(True)
+
+    def onInnerBoxMask(self, event=None):
+        """
+        Call Draw Box Slicer and get mask inside of the box
+        """
+        #get ready for next evt
+        event.Skip()        
+        #from boxMask import BoxMask
+        if event != None:
+            self.onClearSlicer(event)         
+        self.slicer_z += 1
+        self.slicer =  BoxMask(self, self.subplot,
+                               zorder=self.slicer_z, side=True)
+        self.subplot.set_ylim(self.data.ymin, self.data.ymax)
+        self.subplot.set_xlim(self.data.xmin, self.data.xmax)
+        self.update()
+        self.slicer_mask = self.slicer.update()
+        
+    def onOuterBoxMask(self, event=None):
+        """
+        Call Draw Box Slicer and get mask outside of the box
+        """
+        event.Skip()        
+        #from boxMask import BoxMask
+        if event != None:
+            self.onClearSlicer(event)      
+        self.slicer_z += 1
+        self.slicer =  BoxMask(self, self.subplot,
+                               zorder=self.slicer_z, side=False)
+        self.subplot.set_ylim(self.data.ymin, self.data.ymax)
+        self.subplot.set_xlim(self.data.xmin, self.data.xmax)
+        self.update()
+        self.slicer_mask = self.slicer.update()
+
+    def onInnerSectorMask(self, event=None):
+        """
+        Call Draw Sector Slicer and get mask inside of the sector
+        """
+        event.Skip()
+        from sectorMask import SectorMask
+        if event != None:
+            self.onClearSlicer(event)
+        self.slicer_z += 1
+        self.slicer =  SectorMask(self, self.subplot,
+                                  zorder=self.slicer_z, side=True)
+        self.subplot.set_ylim(self.data.ymin, self.data.ymax)
+        self.subplot.set_xlim(self.data.xmin, self.data.xmax)   
+        self.update()
+        self.slicer_mask = self.slicer.update() 
+
+    def onOuterSectorMask(self,event=None):
+        """
+        Call Draw Sector Slicer and get mask outside of the sector
+        """
+        event.Skip()
+        from sectorMask import SectorMask
+        if event != None:
+            self.onClearSlicer(event)
+        self.slicer_z += 1
+        self.slicer =  SectorMask(self, self.subplot,
+                                  zorder=self.slicer_z, side=False)
+        self.subplot.set_ylim(self.data.ymin, self.data.ymax)
+        self.subplot.set_xlim(self.data.xmin, self.data.xmax)    
+        self.update()     
+        self.slicer_mask = self.slicer.update()   
+
+    def onInnerRingMask(self, event=None):
+        """
+        Perform inner circular cut on Phi and draw circular slicer
+        """
+        event.Skip()
+        from AnnulusSlicer import CircularMask
+        if event != None:
+            self.onClearSlicer(event)
+        self.slicer_z += 1
+        self.slicer = CircularMask(self, self.subplot,
+                                   zorder=self.slicer_z, side=True)
+        self.subplot.set_ylim(self.data.ymin, self.data.ymax)
+        self.subplot.set_xlim(self.data.xmin, self.data.xmax)   
+        self.update()
+        self.slicer_mask = self.slicer.update() 
+
+    def onOuterRingMask(self, event=None):
+        """
+        Perform outer circular cut on Phi and draw circular slicer
+        """
+        event.Skip()
+        from AnnulusSlicer import CircularMask
+        if event != None:
+            self.onClearSlicer(event)
+        self.slicer_z += 1
+        self.slicer = CircularMask(self,self.subplot,
+                                   zorder=self.slicer_z, side=False)   
+        self.subplot.set_ylim(self.data.ymin, self.data.ymax)
+        self.subplot.set_xlim(self.data.xmin, self.data.xmax)
+        self.update()
+        self.slicer_mask = self.slicer.update()     
+        
+    def onAddMask(self, event):
+        """
+        Add new mask to old mask 
+        """
+        if not self.slicer == None:
+            data = Data2D()
+            data = self.data
+            self.slicer_mask = self.slicer.update()
+            data.mask = self.data.mask & self.slicer_mask
+            self._check_display_mask(data.mask, event)
+            
+    def _check_display_mask(self, mask, event):
+        """
+        check if the mask valid and update the plot
+        
+        :param mask: mask data
+        """
+        ## Redraw the current image
+        self._update_mask(mask)
+
+    def onEraseMask(self, event):
+        """
+        Erase new mask from old mask
+        """
+        if not self.slicer==None:
+            self.slicer_mask = self.slicer.update()
+            mask = self.data.mask
+            mask[self.slicer_mask==False] = True
+            self._check_display_mask(mask, event)
+            
+    def onResetMask(self, event):
+        """
+        Reset mask to the original mask 
+        """        
+        self.slicer_z += 1
+        self.slicer =  BoxMask(self, self.subplot, 
+                               zorder=self.slicer_z, side=True)
+        self.subplot.set_ylim(self.data.ymin, self.data.ymax)
+        self.subplot.set_xlim(self.data.xmin, self.data.xmax)   
+        mask = copy.deepcopy(self.default_mask)
+        self.data.mask = mask
+        # update mask plot
+        self._check_display_mask(mask, event)
+        
+    def onClearMask(self, event):
+        """
+        Clear mask
+        """            
+        self.slicer_z += 1
+        self.slicer =  BoxMask(self, self.subplot,
+                               zorder=self.slicer_z, side=True)
+        self.subplot.set_ylim(self.data.ymin, self.data.ymax)
+        self.subplot.set_xlim(self.data.xmin, self.data.xmax)   
+        #mask = copy.deepcopy(self.default_mask)
+        mask = numpy.ones(len(self.data.mask), dtype=bool)
+        self.data.mask = mask
+        # update mask plot
+        self._check_display_mask(mask, event)
+        
+    def onClearSlicer(self, event):
+        """
+        Clear the slicer on the plot
+        """
+        if not self.slicer == None:
+            self.slicer.clear()
+            self.subplot.figure.canvas.draw()
+            self.slicer = None
+
+    def _setSlicer(self):
+        """
+        Clear the previous slicer and create a new one.Post an internal
+        event.
+        
+        :param slicer: slicer class to create
+        """
+        ## Clear current slicer
+        if not self.slicer == None:  
+            self.slicer.clear()            
+        ## Create a new slicer    
+        self.slicer_z += 1
+        self.slicer = slicer(self, self.subplot, zorder=self.slicer_z)
+        self.subplot.set_ylim(self.data2D.ymin, self.data2D.ymax)
+        self.subplot.set_xlim(self.data2D.xmin, self.data2D.xmax)
+        ## Draw slicer
+        self.update()
+        self.slicer.update()
+        msg = "Plotter2D._setSlicer  %s"%self.slicer.__class__.__name__
+        wx.PostEvent(self.parent, StatusEvent(status=msg))
+        # Post slicer event
+        event = self._getEmptySlicerEvent()
+        event.type = self.slicer.__class__.__name__
+        event.obj_class = self.slicer.__class__
+        event.params = self.slicer.get_params()
+        wx.PostEvent(self, event)
+   
+    def update(self, draw=True):
+        """
+        Respond to changes in the model by recalculating the 
+        profiles and resetting the widgets.
+        """
+        self.plotpanel.draw()
+        
+    def _set_mask(self, mask):
+        """
+        Set mask
+        """
+        self.data.mask = mask
+        
+    def _update_mask(self,mask):
+        """
+        Respond to changes in masking
+        """ 
+        # the case of liitle numbers of True points
+        if (len(mask[mask]) < 10 and self.data != None):
+            self.ShowMessage()
+            mask = copy.deepcopy(self.mask)
+            self.data.mask = mask
+        else:
+            self.mask = mask
+        # make temperary data to plot
+        temp_mask = numpy.zeros(len(mask))
+        temp_data = copy.deepcopy(self.data)
+        # temp_data default is None
+        # This method is to distinguish between masked point and data point = 0.
+        temp_mask = temp_mask/temp_mask
+        temp_mask[mask] = temp_data.data[mask]
+        # set temp_data value for self.mask==True, else still None 
+        #temp_mask[mask] = temp_data[mask]
+        temp_data.data[mask==False] = temp_mask[mask==False]
+        self.plotpanel.clear()
+        if self.slicer != None:
+            self.slicer.clear()
+            self.slicer = None
+        # Post slicer None event
+        event = self._getEmptySlicerEvent()
+        wx.PostEvent(self, event)
+       
+        ##use this method
+        #set zmax and zmin to plot: Fix it w/ data.
+        if self.plotpanel.scale == 'log':
+            zmax = math.log(max(self.data.data[self.data.data>0]))
+            zmin = math.log(min(self.data.data[self.data.data>0]))
+        else:
+            zmax = max(self.data.data[self.data.data>0])
+            zmin = min(self.data.data[self.data.data>0])
+        #plot    
+        plot = self.plotpanel.image(data=temp_mask,
+                       qx_data=self.data.qx_data,
+                       qy_data=self.data.qy_data,
+                       xmin=self.data.xmin,
+                       xmax=self.data.xmax,
+                       ymin=self.data.ymin,
+                       ymax=self.data.ymax,
+                       zmin=zmin,
+                       zmax=zmax,
+                       cmap=self.cmap,
+                       color=0, symbol=0, label=self.data.name)
+        # axis labels
+        self.plotpanel.axes[0].set_xlabel('$\\rm{Q}_{x}(A^{-1})$')
+        self.plotpanel.axes[0].set_ylabel('$\\rm{Q}_{y}(A^{-1})$')
+        self.plotpanel.render()
+        self.plotpanel.subplot.figure.canvas.draw_idle()
+        
+    def _getEmptySlicerEvent(self):
+        """
+        create an empty slicervent 
+        """
+        self.innerbox_rb.SetValue(False)
+        self.outerbox_rb.SetValue(False)
+        self.innersector_rb.SetValue(False)
+        self.outersector_rb.SetValue(False)
+        self.innercircle_rb.SetValue(False)
+        self.outercircle_rb.SetValue(False)
+        return SlicerEvent(type=None,
+                           params=None,
+                           obj_class=None) 
+             
+    def _draw_model(self, event):
+        """
+         on_close, update the model2d plot
+        """
+        pass
+        
+    def freeze_axes(self):
+        """
+        freeze axes
+        """
+        self.plotpanel.axes_frozen = True
+        
+    def thaw_axes(self):
+        """
+        thaw axes
+        """
+        self.plotpanel.axes_frozen = False       
+         
+    def onMouseMotion(self, event):
+        """
+        onMotion event
+        """
+        pass
+    
+    def onWheel(self, event):
+        """
+        on wheel event
+        """
+        pass  
+    
+    def OnClose(self, event):
+        """
+        """
+        self.parent._draw_masked_model(event)
+        
+class Maskplotpanel(PlotPanel):
+    """
+    """
+    def __init__(self, parent, id=-1, color=None, dpi=None, **kwargs):
+        """
+        """
+        PlotPanel.__init__(self, parent, id=id, color=color, dpi=dpi, **kwargs)
+        
+        # Keep track of the parent Frame
+        self.parent = parent
+        # Internal list of plottable names (because graph 
+        # doesn't have a dictionary of handles for the plottables)
+        self.plots = {}
+        self.graph = Graph()
+        
+    def add_toolbar(self):
+        """ 
+        Add toolbar
+        """
+        # Not implemented
+        pass
+    def on_set_focus(self, event):
+        """
+        send to the parenet the current panel on focus
+        """
+        #change the panel background
+        #self.SetColor((170, 202, 255))
+        self.draw()   
+         
+    def add_image(self, plot):
+        """
+        Add Image
+        """
+        self.plots[plot.name] = plot
+        #init graph
+        self.gaph = Graph()
+        #add plot
+        self.graph.add(plot)
+        #add axes
+        self.graph.xaxis('\\rm{Q}_{x} ', 'A^{-1}')
+        self.graph.yaxis('\\rm{Q}_{y} ', 'A^{-1}')
+        #draw
+        self.graph.render(self)
+        self.subplot.figure.canvas.draw_idle()
+        
+    def onMouseMotion(self, event):
+        """
+        Disable dragging 2D image
+        """
+        pass
+   
+    def onContextMenu(self, event):
+        """
+        Default context menu for a plot panel
+        """
+        # Slicer plot popup menu
+        slicerpop = wx.Menu()
+        
+        id_cm = wx.NewId()
+        slicerpop.Append(id_cm, '&Toggle Linear/Log scale')
+        wx.EVT_MENU(self, id_cm, self._onToggleScale)
+               
+        pos = event.GetPosition()
+        pos = self.ScreenToClient(pos)
+        self.PopupMenu(slicerpop, pos)
+
+class ViewerFrame(wx.Frame):
+    """
+    Add comment
+    """
+    def __init__(self, parent, id, title):
+        """
+        comment
+        :param parent: parent panel/container
+        """
+        # Initialize the Frame object
+        wx.Frame.__init__(self, parent, id, title,
+                          wx.DefaultPosition, wx.Size(950, 850))
+        # Panel for 1D plot
+        self.plotpanel = Maskplotpanel(self, -1, style=wx.RAISED_BORDER)
+
+class ViewApp(wx.App):
+    def OnInit(self):
+        frame = ViewerFrame(None, -1, 'testView')    
+        frame.Show(True)
+        #self.SetTopWindow(frame)
+        
+        return True
+               
+if __name__ == "__main__": 
+    app = ViewApp(0)
+    app.MainLoop()     
