Index: guiframe/local_perspectives/plotting/AnnulusSlicer.py
===================================================================
--- guiframe/local_perspectives/plotting/AnnulusSlicer.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
+++ guiframe/local_perspectives/plotting/AnnulusSlicer.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
@@ -0,0 +1,612 @@
+#TODO: the line slicer should listen to all 2DREFRESH events, get the data and slice it
+#      before pushing a new 1D data update.
+
+#
+#TODO: NEED MAJOR REFACTOR
+#
+
+import math
+import wx
+#from copy import deepcopy 
+# Debug printout
+from sans.guiframe.events import NewPlotEvent
+from sans.guiframe.events import StatusEvent
+from sans.guiframe.events import SlicerParameterEvent
+from sans.guiframe.events import EVT_SLICER_PARS
+from BaseInteractor import _BaseInteractor
+from sans.guiframe.dataFitting import Data1D
+
+class AnnulusInteractor(_BaseInteractor):
+    """
+    Select an annulus through a 2D plot.
+    This interactor is used to average 2D data  with the region 
+    defined by 2 radius.
+    this class is defined by 2 Ringinterators.
+    """
+    def __init__(self, base, axes, color='black', zorder=3):
+        
+        _BaseInteractor.__init__(self, base, axes, color=color)
+        self.markers = []
+        self.axes = axes
+        self.base = base
+        self.qmax = min(math.fabs(self.base.data2D.xmax),
+                        math.fabs(self.base.data2D.xmin))  #must be positive
+        self.connect = self.base.connect
+    
+        ## Number of points on the plot
+        self.nbins = 20
+        #Cursor position of Rings (Left(-1) or Right(1))
+        self.xmaxd = self.base.data2D.xmax
+        self.xmind = self.base.data2D.xmin
+
+        if (self.xmaxd + self.xmind) > 0:
+            self.sign = 1
+        else:
+            self.sign = -1
+        # Inner circle
+        self.inner_circle = RingInteractor(self, self.base.subplot,
+                                            zorder=zorder,
+                                            r=self.qmax/2.0, sign=self.sign)
+        self.inner_circle.qmax = self.qmax
+        self.outer_circle = RingInteractor(self, self.base.subplot,
+                                           zorder=zorder+1, r=self.qmax/1.8,
+                                           sign=self.sign)
+        self.outer_circle.qmax = self.qmax * 1.2
+        self.update()
+        self._post_data()
+        
+        # Bind to slice parameter events
+        self.base.Bind(EVT_SLICER_PARS, self._onEVT_SLICER_PARS)
+        
+    def _onEVT_SLICER_PARS(self, event):
+        """
+        receive an event containing parameters values to reset the slicer
+        
+        :param event: event of type SlicerParameterEvent with params as 
+            attribute
+            
+        """
+        wx.PostEvent(self.base,
+                     StatusEvent(status="AnnulusSlicer._onEVT_SLICER_PARS"))
+        event.Skip()
+        if event.type == self.__class__.__name__:
+            self.set_params(event.params)
+            self.base.update()
+
+    def set_layer(self, n):
+        """
+        Allow adding plot to the same panel
+        
+        :param n: the number of layer
+        
+        """
+        self.layernum = n
+        self.update()
+        
+    def clear(self):
+        """
+        Clear the slicer and all connected events related to this slicer
+        """
+        self.clear_markers()
+        self.outer_circle.clear()
+        self.inner_circle.clear()
+        self.base.connect.clearall()
+        self.base.Unbind(EVT_SLICER_PARS)
+        
+    def update(self):
+        """
+        Respond to changes in the model by recalculating the profiles and
+        resetting the widgets.
+        """
+        # Update locations        
+        self.inner_circle.update()
+        self.outer_circle.update()
+        
+    def save(self, ev):
+        """
+        Remember the roughness for this layer and the next so that we
+        can restore on Esc.
+        """
+        self.base.freeze_axes()
+        self.inner_circle.save(ev)
+        self.outer_circle.save(ev)
+
+    def _post_data(self, nbins=None):
+        """
+        Uses annulus parameters to plot averaged data into 1D data.
+        
+        :param nbins: the number of points to plot 
+        
+        """
+        #Data to average
+        data = self.base.data2D
+        # If we have no data, just return
+        if data == None:
+            return
+        
+        from DataLoader.manipulations import Ring
+        rmin = min(math.fabs(self.inner_circle.get_radius()),
+                  math.fabs(self.outer_circle.get_radius()))
+        rmax = max(math.fabs(self.inner_circle.get_radius()),
+                   math.fabs(self.outer_circle.get_radius()))
+        #if the user does not specify the numbers of points to plot 
+        # the default number will be nbins= 20
+        if nbins == None:
+            self.nbins = 20
+        else:
+            self.nbins = nbins
+        ## create the data1D Q average of data2D    
+        sect = Ring(r_min=rmin, r_max=rmax, nbins=self.nbins)
+        sector = sect(self.base.data2D)
+    
+        if hasattr(sector, "dxl"):
+            dxl = sector.dxl
+        else:
+            dxl = None
+        if hasattr(sector, "dxw"):
+            dxw = sector.dxw
+        else:
+            dxw = None
+        new_plot = Data1D(x=(sector.x - math.pi) * 180/math.pi,
+                          y=sector.y, dy=sector.dy)
+        new_plot.dxl = dxl
+        new_plot.dxw = dxw
+        new_plot.name = "AnnulusPhi" +"("+ self.base.data2D.name+")"
+        
+        new_plot.source = self.base.data2D.source
+        #new_plot.info=self.base.data2D.info
+        new_plot.interactive = True
+        new_plot.detector = self.base.data2D.detector
+        # If the data file does not tell us what the axes are, just assume...
+        new_plot.xaxis("\\rm{\phi}", 'degrees')
+        new_plot.yaxis("\\rm{Intensity} ", "cm^{-1}")
+        if hasattr(data, "scale") and data.scale == 'linear' and \
+                self.base.data2D.name.count("Residuals") > 0:
+            new_plot.ytransform = 'y'
+            new_plot.yaxis("\\rm{Residuals} ", "/")
+
+        new_plot.group_id = "AnnulusPhi" + self.base.data2D.name
+        new_plot.id = None
+        #new_plot.is_data= True
+        new_plot.xtransform = "x"
+        new_plot.ytransform = "y"
+        wx.PostEvent(self.base.parent, NewPlotEvent(plot=new_plot,
+                                                 title="AnnulusPhi"))
+        
+    def moveend(self, ev):
+        """
+        Called when any dragging motion ends.
+        Post an event (type =SlicerParameterEvent)
+        to plotter 2D with a copy  slicer parameters
+        Call  _post_data method
+        """
+        self.base.thaw_axes()
+        # Post parameters to plotter 2D
+        event = SlicerParameterEvent()
+        event.type = self.__class__.__name__
+        event.params = self.get_params()
+        wx.PostEvent(self.base, event)
+        # create a 1D data plot
+        #self._post_data()
+            
+    def restore(self):
+        """
+        Restore the roughness for this layer.
+        """
+        self.inner_circle.restore()
+        self.outer_circle.restore()
+
+    def move(self, x, y, ev):
+        """
+        Process move to a new position, making sure that the move is allowed.
+        """
+        pass
+        
+    def set_cursor(self, x, y):
+        pass
+        
+    def get_params(self):
+        """
+        Store a copy of values of parameters of the slicer into a dictionary.
+        
+        :return params: the dictionary created
+        
+        """
+        params = {}
+        params["inner_radius"] = math.fabs(self.inner_circle._inner_mouse_x)
+        params["outer_radius"] = math.fabs(self.outer_circle._inner_mouse_x)
+        params["nbins"] = self.nbins
+        return params
+    
+    def set_params(self, params):
+        """
+        Receive a dictionary and reset the slicer with values contained 
+        in the values of the dictionary.
+        
+        :param params: a dictionary containing name of slicer parameters and 
+            values the user assigned to the slicer.
+            
+        """
+        inner = math.fabs(params["inner_radius"])
+        outer = math.fabs(params["outer_radius"])
+        self.nbins = int(params["nbins"])
+        ## Update the picture
+        self.inner_circle.set_cursor(inner, self.inner_circle._inner_mouse_y)
+        self.outer_circle.set_cursor(outer, self.outer_circle._inner_mouse_y)
+        ## Post the data given the nbins entered by the user 
+        self._post_data(self.nbins)
+        
+    def freeze_axes(self):
+        """
+        """
+        self.base.freeze_axes()
+        
+    def thaw_axes(self):
+        """
+        """
+        self.base.thaw_axes()
+
+    def draw(self):
+        """
+        """
+        self.base.draw()
+
+        
+class RingInteractor(_BaseInteractor):
+    """
+     Draw a ring Given a radius 
+    """
+    def __init__(self, base, axes, color='black', zorder=5, r=1.0, sign=1):
+        """
+        :param: the color of the line that defined the ring
+        :param r: the radius of the ring
+        :param sign: the direction of motion the the marker 
+        
+        """
+        _BaseInteractor.__init__(self, base, axes, color=color)
+        self.markers = []
+        self.axes = axes
+        # Current radius of the ring
+        self._inner_mouse_x = r
+        #Value of the center of the ring
+        self._inner_mouse_y = 0
+        # previous value of that radius
+        self._inner_save_x  = r
+        #Save value of the center of the ring
+        self._inner_save_y  = 0
+        #Class instantiating RingIterator class
+        self.base = base
+        #the direction of the motion of the marker
+        self.sign = sign
+        ## Create a marker 
+        try:
+            # Inner circle marker
+            x_value = [self.sign * math.fabs(self._inner_mouse_x)]
+            self.inner_marker = self.axes.plot(x_value,
+                                               [0],
+                                                linestyle='',
+                                          marker='s', markersize=10,
+                                          color=self.color, alpha=0.6,
+                                          pickradius=5, label="pick", 
+                                          zorder=zorder,
+                                          visible=True)[0]
+        except:
+            x_value = [self.sign * math.fabs(self._inner_mouse_x)]
+            self.inner_marker = self.axes.plot(x_value,
+                                               [0], 
+                                               linestyle='',
+                                          marker='s', markersize=10,
+                                          color=self.color, alpha=0.6,
+                                          label="pick", 
+                                          visible=True)[0]
+            message  = "\nTHIS PROTOTYPE NEEDS THE LATEST"
+            message += " VERSION OF MATPLOTLIB\n"
+            message += "Get the SVN version that is at "
+            message += " least as recent as June 1, 2007"
+            
+            owner = self.base.base.parent
+            wx.PostEvent(owner, 
+                         StatusEvent(status="AnnulusSlicer: %s" % message))
+            
+        # Draw a circle 
+        [self.inner_circle] = self.axes.plot([], [],
+                                      linestyle='-', marker='',
+                                      color=self.color)
+        # the number of points that make the ring line
+        self.npts = 40
+            
+        self.connect_markers([self.inner_marker])
+        self.update()
+
+    def set_layer(self, n):
+        """
+        Allow adding plot to the same panel
+         
+        :param n: the number of layer
+        
+        """
+        self.layernum = n
+        self.update()
+        
+    def clear(self):
+        """
+        Clear the slicer and all connected events related to this slicer
+        """
+        self.clear_markers()
+        try:
+            self.inner_marker.remove()
+            self.inner_circle.remove()
+        except:
+            # Old version of matplotlib
+            for item in range(len(self.axes.lines)):
+                del self.axes.lines[0]
+        
+    def get_radius(self):
+        """
+        :return self._inner_mouse_x: the current radius of the ring
+        """
+        return self._inner_mouse_x
+        
+    def update(self):
+        """
+        Draw the new roughness on the graph.
+        """
+        # Plot inner circle
+        x = []
+        y = []
+        for i in range(self.npts):
+            phi = 2.0 * math.pi / (self.npts - 1) * i
+            
+            xval = 1.0 * self._inner_mouse_x * math.cos(phi) 
+            yval = 1.0 * self._inner_mouse_x * math.sin(phi) 
+            
+            x.append(xval)
+            y.append(yval)
+            
+        self.inner_marker.set(xdata=[self.sign*math.fabs(self._inner_mouse_x)],
+                              ydata=[0])
+        self.inner_circle.set_data(x, y)        
+
+    def save(self, ev):
+        """
+        Remember the roughness for this layer and the next so that we
+        can restore on Esc.
+        """
+        self._inner_save_x = self._inner_mouse_x
+        self._inner_save_y = self._inner_mouse_y
+        self.base.freeze_axes()
+
+    def moveend(self, ev):
+        """
+        Called after a dragging motion
+        """
+        self.base.moveend(ev)
+            
+    def restore(self):
+        """
+        Restore the roughness for this layer.
+        """
+        self._inner_mouse_x = self._inner_save_x
+        self._inner_mouse_y = self._inner_save_y
+
+    def move(self, x, y, ev):
+        """
+        Process move to a new position, making sure that the move is allowed.
+        """
+        self._inner_mouse_x = x
+        self._inner_mouse_y = y
+        self.base.base.update()
+        
+    def set_cursor(self, x, y):
+        """
+        draw the ring given x, y value 
+        """
+        self.move(x, y, None)
+        self.update()
+        
+        
+    def get_params(self):
+        """
+        Store a copy of values of parameters of the slicer into a dictionary.
+        
+        :return params: the dictionary created
+        
+        """
+        params = {}
+        params["radius"] = math.fabs(self._inner_mouse_x)
+        return params
+    
+    def set_params(self, params):
+        """
+        Receive a dictionary and reset the slicer with values contained 
+        in the values of the dictionary.
+        
+        :param params: a dictionary containing name of slicer parameters and 
+            values the user assigned to the slicer.
+            
+        """
+        x = params["radius"] 
+        self.set_cursor(x, self._inner_mouse_y)
+        
+class CircularMask(_BaseInteractor):
+    """
+     Draw a ring Given a radius 
+    """
+    def __init__(self, base, axes, color='grey', zorder=3, side=None):
+        """
+        
+        :param: the color of the line that defined the ring
+        :param r: the radius of the ring
+        :param sign: the direction of motion the the marker 
+        
+        """
+        _BaseInteractor.__init__(self, base, axes, color=color)
+        self.markers = []
+        self.axes = axes
+        self.base = base
+        self.is_inside = side
+        self.qmax = min(math.fabs(self.base.data.xmax),
+                        math.fabs(self.base.data.xmin))  #must be positive
+        self.connect = self.base.connect
+        
+        #Cursor position of Rings (Left(-1) or Right(1))
+        self.xmaxd = self.base.data.xmax
+        self.xmind = self.base.data.xmin
+
+        if (self.xmaxd + self.xmind) > 0:
+            self.sign = 1
+        else:
+            self.sign = -1
+        # Inner circle
+        self.outer_circle = RingInteractor(self, self.base.subplot, 'blue',
+                                            zorder=zorder+1, r=self.qmax/1.8,
+                                            sign=self.sign)
+        self.outer_circle.qmax = self.qmax * 1.2
+        self.update()
+        self._post_data()
+        
+        # Bind to slice parameter events
+        #self.base.Bind(EVT_SLICER_PARS, self._onEVT_SLICER_PARS)
+        
+    def _onEVT_SLICER_PARS(self, event):
+        """
+        receive an event containing parameters values to reset the slicer
+        
+        :param event: event of type SlicerParameterEvent with params as 
+            attribute
+        """
+        wx.PostEvent(self.base,
+                     StatusEvent(status="AnnulusSlicer._onEVT_SLICER_PARS"))
+        event.Skip()
+        if event.type == self.__class__.__name__:
+            self.set_params(event.params)
+            self.base.update()
+
+    def set_layer(self, n):
+        """
+        Allow adding plot to the same panel
+         
+        :param n: the number of layer
+        
+        """
+        self.layernum = n
+        self.update()
+        
+    def clear(self):
+        """
+        Clear the slicer and all connected events related to this slicer
+        """
+        self.clear_markers()
+        self.outer_circle.clear()
+        self.base.connect.clearall()
+        #self.base.Unbind(EVT_SLICER_PARS)
+        
+    def update(self):
+        """
+        Respond to changes in the model by recalculating the profiles and
+        resetting the widgets.
+        """
+        # Update locations        
+        self.outer_circle.update()
+        #if self.is_inside != None:
+        out = self._post_data()
+        return out
+
+    def save(self, ev):
+        """
+        Remember the roughness for this layer and the next so that we
+        can restore on Esc.
+        """
+        self.base.freeze_axes()
+        self.outer_circle.save(ev)
+
+    def _post_data(self):
+        """
+        Uses annulus parameters to plot averaged data into 1D data.
+        
+        :param nbins: the number of points to plot 
+        
+        """
+        #Data to average
+        data = self.base.data
+              
+        # If we have no data, just return
+        if data == None:
+            return
+        mask = data.mask  
+        from DataLoader.manipulations import Ringcut
+    
+        rmin = 0
+        rmax = math.fabs(self.outer_circle.get_radius())
+
+        ## create the data1D Q average of data2D    
+        mask = Ringcut(r_min=rmin, r_max= rmax)
+
+        if self.is_inside:
+            out = (mask(data) == False)
+        else:
+            out = (mask(data))
+        #self.base.data.mask=out
+        return out                    
+
+         
+    def moveend(self, ev):
+        """
+        Called when any dragging motion ends.
+        Post an event (type =SlicerParameterEvent)
+        to plotter 2D with a copy  slicer parameters
+        Call  _post_data method
+        """
+        self.base.thaw_axes()
+        # create a 1D data plot
+        self._post_data()
+            
+    def restore(self):
+        """
+        Restore the roughness for this layer.
+        """
+        self.outer_circle.restore()
+
+    def move(self, x, y, ev):
+        """
+        Process move to a new position, making sure that the move is allowed.
+        """
+        pass
+        
+    def set_cursor(self, x, y):
+        pass
+        
+    def get_params(self):
+        """
+        Store a copy of values of parameters of the slicer into a dictionary.
+        
+        :return params: the dictionary created
+        
+        """
+        params = {}
+        params["outer_radius"] = math.fabs(self.outer_circle._inner_mouse_x)
+        return params
+    
+    def set_params(self, params):
+        """
+        Receive a dictionary and reset the slicer with values contained 
+        in the values of the dictionary.
+        
+        :param params: a dictionary containing name of slicer parameters and 
+            values the user assigned to the slicer.
+        """
+        outer = math.fabs(params["outer_radius"] )
+        ## Update the picture
+        self.outer_circle.set_cursor(outer, self.outer_circle._inner_mouse_y)
+        ## Post the data given the nbins entered by the user 
+        self._post_data()
+        
+    def freeze_axes(self):
+        self.base.freeze_axes()
+        
+    def thaw_axes(self):
+        self.base.thaw_axes()
+
+    def draw(self):
+        self.base.update()
+              
Index: guiframe/local_perspectives/plotting/Arc.py
===================================================================
--- guiframe/local_perspectives/plotting/Arc.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
+++ guiframe/local_perspectives/plotting/Arc.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
@@ -0,0 +1,162 @@
+
+import math
+import wx 
+from copy import deepcopy
+
+from BaseInteractor import _BaseInteractor
+from sans.guiframe.events import NewPlotEvent
+from sans.guiframe.events import StatusEvent
+from sans.guiframe.events import SlicerParameterEvent
+from sans.guiframe.events import EVT_SLICER_PARS
+  
+ 
+class ArcInteractor(_BaseInteractor):
+    """
+    Select an annulus through a 2D plot
+    """
+    def __init__(self, base, axes, color='black', zorder=5, r=1.0, 
+                 theta1=math.pi/8, theta2=math.pi/4):
+        
+        _BaseInteractor.__init__(self, base, axes, color=color)
+        self.markers = []
+        self.axes = axes
+        self._mouse_x = r
+        self._mouse_y = 0
+        
+        self._save_x  = r
+        self._save_y  = 0
+        
+        self.scale = 10.0
+        
+        self.theta1 = theta1
+        self.theta2 = theta2
+        self.radius = r
+        [self.arc] = self.axes.plot([], [],
+                                      linestyle='-', marker='',
+                                      color=self.color)
+        self.npts = 20
+        self.has_move = False    
+        self.connect_markers([self.arc])
+        self.update()
+
+    def set_layer(self, n):
+        """
+        """
+        self.layernum = n
+        self.update()
+        
+    def clear(self):
+        """
+        """
+        self.clear_markers()
+        try:
+            self.marker.remove()
+            self.arc.remove()
+        except:
+            # Old version of matplotlib
+            for item in range(len(self.axes.lines)):
+                del self.axes.lines[0]
+        
+    def get_radius(self):
+        """
+        """
+        radius = math.sqrt(math.pow(self._mouse_x, 2) + \
+                           math.pow(self._mouse_y, 2))
+        return radius
+        
+    def update(self, theta1=None, theta2=None, nbins=None, r=None):
+        """
+        """
+        # Plot inner circle
+        x = []
+        y = []
+        if theta1 != None:
+            self.theta1 = theta1
+        if theta2 != None:
+            self.theta2 = theta2
+        #print "ring update theta1 theta2", math.degrees(self.theta1), 
+        #math.degrees(self.theta2)
+        while self.theta2 < self.theta1:
+            self.theta2 += (2 * math.pi)
+        while self.theta2 >= (self.theta1 + 2 * math.pi):
+            self.theta2 -= (2 * math.pi)
+        npts = int((self.theta2 - self.theta1)/(math.pi/120))  
+             
+        if r == None:
+            self.radius =  math.sqrt(math.pow(self._mouse_x, 2) + \
+                                     math.pow(self._mouse_y, 2))
+        else:
+            self.radius = r
+        for i in range(self.npts):
+            phi = (self.theta2 - self.theta1)/(self.npts - 1) * i + self.theta1
+            xval = 1.0 * self.radius * math.cos(phi) 
+            yval = 1.0 * self.radius * math.sin(phi) 
+            
+            x.append(xval)
+            y.append(yval)
+        #self.marker.set(xdata=[self._mouse_x],ydata=[0])
+        self.arc.set_data(x, y) 
+        
+    def save(self, ev):
+        """
+        Remember the roughness for this layer and the next so that we
+        can restore on Esc.
+        """
+        self._save_x = self._mouse_x
+        self._save_y = self._mouse_y
+        #self._save_x = ev.xdata
+        #self._save_y = ev.ydata
+        self.base.freeze_axes()
+
+    def moveend(self, ev):
+        """
+        """
+        self.has_move = False
+        
+        event = SlicerParameterEvent()
+        event.type = self.__class__.__name__
+        event.params = self.get_params()
+        #print "in arc moveend params",self.get_params()
+        #wx.PostEvent(self.base.base.parent, event)
+        self.base.moveend(ev)
+            
+    def restore(self):
+        """
+        Restore the roughness for this layer.
+        """
+        self._mouse_x = self._save_x
+        self._mouse_y = self._save_y
+       
+    def move(self, x, y, ev):
+        """
+        Process move to a new position, making sure that the move is allowed.
+        """
+        #print "ring move x, y", x,y
+        self._mouse_x = x
+        self._mouse_y = y
+        self.has_move = True
+        self.base.base.update()
+        
+    def set_cursor(self, radius, phi_min, phi_max, nbins):
+        """
+        """
+        self.theta1 = phi_min
+        self.theta2 = phi_max
+        self.update(nbins=nbins, r=radius)
+
+    def get_params(self):
+        """
+        """
+        params = {}
+        params["radius"] = self.radius
+        params["theta1"] = self.theta1
+        params["theta2"] = self.theta2
+        return params
+    
+    def set_params(self, params):
+        """
+        """
+        x = params["radius"] 
+        self.set_cursor(x, self._mouse_y)
+        
+    
Index: guiframe/local_perspectives/plotting/AzimutSlicer.py
===================================================================
--- guiframe/local_perspectives/plotting/AzimutSlicer.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
+++ guiframe/local_perspectives/plotting/AzimutSlicer.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
@@ -0,0 +1,312 @@
+#TODO: the line slicer should listen to all 2DREFRESH events, get the data and slice it
+#      before pushing a new 1D data update.
+
+#
+#TODO: NEED MAJOR REFACTOR
+#
+
+
+# Debug printout
+import math
+import wx
+from copy import deepcopy
+from BaseInteractor import _BaseInteractor
+from sans.guiframe.events import NewPlotEvent
+from sans.guiframe.events import StatusEvent
+from sans.guiframe.events import SlicerParameterEvent
+from sans.guiframe.events import EVT_SLICER_PARS
+
+
+class SectorInteractor(_BaseInteractor):
+    """
+    Select an annulus through a 2D plot
+    """
+    def __init__(self, base, axes, color='black', zorder=3):
+        """
+        """
+        _BaseInteractor.__init__(self, base, axes, color=color)
+        self.markers = []
+        self.axes = axes
+        self.qmax = self.base.data2D.xmax
+        self.connect = self.base.connect
+        
+        ## Number of points on the plot
+        self.nbins = 20
+        theta1 = math.pi/8
+        theta2 = math.pi/2
+        theta1 = 2 * math.pi/3
+        theta2 = -2 * math.pi/3
+        r1 = self.qmax/2.0
+        r2 = self.qmax/1.8
+       
+        # Inner circle
+        from Arc import ArcInteractor
+        self.inner_circle = ArcInteractor(self, self.base.subplot,
+                                           zorder=zorder,
+                                           r=self.qmax/2.0,
+                                           theta1=theta1,
+                                           theta2=theta2)
+        self.inner_circle.qmax = self.qmax
+        self.outer_circle = ArcInteractor(self, self.base.subplot,
+                                          zorder=zorder+1,
+                                          r=self.qmax/1.8,
+                                          theta1=theta1,
+                                           theta2=theta2)
+        self.outer_circle.qmax = self.qmax * 1.2
+        #self.outer_circle.set_cursor(self.base.qmax/1.8, 0)
+        from Edge import RadiusInteractor
+        self.right_edge= RadiusInteractor(self, self.base.subplot,
+                                          zorder=zorder+1,
+                                             arc1=self.inner_circle,
+                                             arc2=self.outer_circle,
+                                            theta=theta1)
+        self.left_edge= RadiusInteractor(self, self.base.subplot,
+                                         zorder=zorder+1,
+                                             arc1=self.inner_circle,
+                                             arc2=self.outer_circle,
+                                            theta=theta2)
+        self.update()
+        self._post_data()
+        # Bind to slice parameter events
+        self.base.parent.Bind(EVT_SLICER_PARS, self._onEVT_SLICER_PARS)
+
+    def _onEVT_SLICER_PARS(self, event):
+        """
+        """
+        #printEVT("AnnulusSlicer._onEVT_SLICER_PARS")
+        event.Skip()
+        if event.type == self.__class__.__name__:
+            self.set_params(event.params)
+            self.base.update()
+
+    def save_data(self, path, image, x, y):
+        """
+        """
+        output = open(path, 'w')
+        data_x, data_y = self.get_data(image, x, y)
+        
+        output.write("<phi>  <average>\n")
+        for i in range(len(data_x)):
+            output.write("%g  %g\n" % (data_x[i], data_y[i]))
+        output.close()
+
+    def set_layer(self, n):
+        """
+        """
+        self.layernum = n
+        self.update()
+        
+    def clear(self):
+        """
+        """
+        self.clear_markers()
+        self.outer_circle.clear()
+        self.inner_circle.clear()
+        self.right_edge.clear()
+        self.left_edge.clear()
+        #self.base.connect.disconnect()
+        self.base.parent.Unbind(EVT_SLICER_PARS)
+        
+    def update(self):
+        """
+        Respond to changes in the model by recalculating the profiles and
+        resetting the widgets.
+        """
+        # Update locations   
+        if self.inner_circle.has_move:    
+            #print "inner circle has moved" 
+            self.inner_circle.update()
+            r1 = self.inner_circle.get_radius()
+            r2 = self.outer_circle.get_radius()
+            self.right_edge.update(r1, r2)
+            self.left_edge.update(r1, r2)
+        if self.outer_circle.has_move:    
+            #print "outer circle has moved" 
+            self.outer_circle.update()
+            r1 = self.inner_circle.get_radius()
+            r2 = self.outer_circle.get_radius()
+            self.left_edge.update(r1, r2)
+            self.right_edge.update(r1, r2)
+        if self.right_edge.has_move:
+            #print "right edge has moved"
+            self.right_edge.update()
+            self.inner_circle.update(theta1=self.right_edge.get_angle(),
+                                     theta2=None)
+            self.outer_circle.update(theta1=self.right_edge.get_angle(),
+                                     theta2=None)
+        if  self.left_edge.has_move:
+            #print "left Edge has moved"
+            self.left_edge.update()
+            self.inner_circle.update(theta1=None,
+                                     theta2=self.left_edge.get_angle())
+            self.outer_circle.update(theta1=None,
+                                     theta2=self.left_edge.get_angle())
+             
+    def save(self, ev):
+        """
+        Remember the roughness for this layer and the next so that we
+        can restore on Esc.
+        """
+        self.base.freeze_axes()
+        self.inner_circle.save(ev)
+        self.outer_circle.save(ev)
+        self.right_edge.save(ev)
+        self.left_edge.save(ev)
+        
+    def _post_data(self):
+        pass
+    
+    def post_data(self,new_sector ):
+        """ post data averaging in Q"""
+        if self.inner_circle.get_radius() < self.outer_circle.get_radius():
+            rmin = self.inner_circle.get_radius()
+            rmax = self.outer_circle.get_radius()
+        else:
+            rmin = self.outer_circle.get_radius()
+            rmax = self.inner_circle.get_radius()
+        if self.right_edge.get_angle() < self.left_edge.get_angle():
+            phimin = self.right_edge.get_angle()
+            phimax = self.left_edge.get_angle()
+        else:
+            phimin = self.left_edge.get_angle()
+            phimax = self.right_edge.get_angle()   
+        #print "phimin, phimax, rmin ,rmax",math.degrees(phimin),
+        # math.degrees(phimax), rmin ,rmax
+        #from DataLoader.manipulations import SectorQ
+        
+        sect = new_sector(r_min=rmin, r_max=rmax,
+                          phi_min=phimin, phi_max=phimax)
+        sector = sect(self.base.data2D)
+        
+        from sans.guiframe.dataFitting import Data1D
+        if hasattr(sector, "dxl"):
+            dxl = sector.dxl
+        else:
+            dxl = None
+        if hasattr(sector, "dxw"):
+            dxw = sector.dxw
+        else:
+            dxw = None
+        new_plot = Data1D(x=sector.x, y=sector.y, dy=sector.dy,
+                          dxl=dxl, dxw=dxw)
+        new_plot.name = str(new_sector.__name__) + \
+                        "("+ self.base.data2D.name+")"
+        new_plot.source = self.base.data2D.source
+        new_plot.interactive = True
+        #print "loader output.detector",output.source
+        new_plot.detector = self.base.data2D.detector
+        # If the data file does not tell us what the axes are, just assume...
+        new_plot.xaxis("\\rm{Q}", 'rad')
+        new_plot.yaxis("\\rm{Intensity} ", "cm^{-1}")
+        new_plot.group_id = str(new_sector.__name__) + self.base.data2D.name
+        wx.PostEvent(self.base.parent,
+                    NewPlotEvent(plot=new_plot, title=str(new_sector.__name__)))
+        
+    def moveend(self, ev):
+        #self.base.thaw_axes()
+        
+        # Post paramters
+        #event = SlicerParameterEvent()
+        #event.type = self.__class__.__name__
+        #event.params = self.get_params()
+        #print "main moveend ", event.params
+        #wx.PostEvent(self.base.parent, event)
+        #self._post_data()
+        pass
+            
+    def restore(self):
+        """
+        Restore the roughness for this layer.
+        """
+        self.inner_circle.restore()
+        self.outer_circle.restore()
+        self.right_edge.restore()
+        self.left_edge.restore()
+
+    def move(self, x, y, ev):
+        """
+        Process move to a new position, making sure that the move is allowed.
+        """
+        pass
+        
+    def set_cursor(self, x, y):
+        """
+        """
+        pass
+        
+    def get_params(self):
+        """
+        """
+        params = {}
+        params["r_min"] = self.inner_circle.get_radius()
+        params["r_max"] = self.outer_circle.get_radius()
+        params["phi_min"] = self.right_edge.get_angle()
+        params["phi_max"] = self.left_edge.get_angle()
+        params["nbins"] = self.nbins
+        return params
+    
+    def set_params(self, params):
+        """
+        """
+        #print "setparams on main slicer ",params
+        inner = params["r_min"] 
+        outer = params["r_max"] 
+        phi_min= params["phi_min"]
+        phi_max=params["phi_max"]
+        self.nbins = int(params["nbins"])
+        
+        self.inner_circle.set_cursor(inner, phi_min, phi_max,self.nbins)
+        self.outer_circle.set_cursor(outer,  phi_min, phi_max, self.nbins)
+        self.right_edge.set_cursor(inner, outer, phi_min)
+        self.left_edge.set_cursor(inner, outer, phi_max)
+        self._post_data()
+        
+    def freeze_axes(self):
+        """
+        """
+        self.base.freeze_axes()
+        
+    def thaw_axes(self):
+        """
+        """
+        self.base.thaw_axes()
+
+    def draw(self):
+        """
+        """
+        self.base.draw()
+
+class SectorInteractorQ(SectorInteractor):
+    """
+    """
+    def __init__(self, base, axes, color='black', zorder=3):
+        """
+        """
+        SectorInteractor.__init__(self, base, axes, color=color)
+        self.base=base
+        self._post_data()
+        
+    def _post_data(self):
+        """
+        """
+        from DataLoader.manipulations import SectorQ
+        self.post_data(SectorQ)   
+        
+
+class SectorInteractorPhi(SectorInteractor):
+    """
+    """
+    def __init__(self, base, axes, color='black', zorder=3):
+        """
+        """
+        SectorInteractor.__init__(self, base, axes, color=color)
+        self.base=base
+        self._post_data()
+        
+    def _post_data(self):
+        """
+        """
+        from DataLoader.manipulations import SectorPhi
+        self.post_data(SectorPhi )   
+        
+        
Index: guiframe/local_perspectives/plotting/BaseInteractor.py
===================================================================
--- guiframe/local_perspectives/plotting/BaseInteractor.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
+++ guiframe/local_perspectives/plotting/BaseInteractor.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
@@ -0,0 +1,180 @@
+interface_color = 'black'
+disable_color = 'gray'
+active_color = 'red'
+rho_color = 'black'
+mu_color = 'green'
+P_color = 'blue'
+theta_color = 'orange'
+profile_colors = [rho_color, mu_color, P_color, theta_color]
+
+class _BaseInteractor:
+    """
+    Share some functions between the interface interactor and various layer
+    interactors.
+    
+    Individual interactors need the following functions:
+    
+        save(ev)  - save the current state for later restore
+        restore() - restore the old state
+        move(x,y,ev) - move the interactor to position x,y
+        moveend(ev) - end the drag event
+        update() - draw the interactors
+        
+    The following are provided by the base class:
+    
+        connect_markers(markers) - register callbacks for all markers
+        clear_markers() - remove all items in self.markers
+        onHilite(ev) - enter/leave event processing
+        onLeave(ev) - enter/leave event processing
+        onClick(ev) - mouse click: calls save()
+        onRelease(ev) - mouse click ends: calls moveend()
+        onDrag(ev) - mouse move: calls move() or restore()
+        onKey(ev) - keyboard move: calls move() or restore()
+        
+    Interactor attributes:
+    
+        base  - model we are operating on
+        axes  - axes holding the interactor
+        color - color of the interactor in non-active state
+        markers - list of handles for the interactor
+        
+    """
+    def __init__(self, base, axes, color='black'):
+        """
+        """
+        self.base = base
+        self.axes = axes
+        self.color = color
+        self.clickx = None
+        self.clicky = None
+        self.markers = []
+        
+    def clear_markers(self):
+        """
+        Clear old markers and interfaces.
+        """
+        for h in self.markers: h.remove()
+        if self.markers:
+            self.base.connect.clear(*self.markers)
+        self.markers = []
+
+    def save(self, ev):
+        """
+        """
+        pass
+    
+    def restore(self, ev):
+        """
+        """
+        pass
+    
+    def move(self, x, y, ev):
+        """
+        """
+        pass
+    
+    def moveend(self, ev):
+        """
+        """
+        pass
+
+    def connect_markers(self, markers):
+        """
+        Connect markers to callbacks
+        """
+       
+        for h in markers:
+            connect = self.base.connect
+            connect('enter', h, self.onHilite)
+            connect('leave', h, self.onLeave)
+            connect('click', h, self.onClick)
+            connect('release', h, self.onRelease)
+            connect('drag', h, self.onDrag)
+            connect('key', h, self.onKey)
+
+    def onHilite(self, ev):
+        """
+        Hilite the artist reporting the event, indicating that it is
+        ready to receive a click.
+        """
+        ev.artist.set_color(active_color)
+        self.base.draw()
+        return True
+
+    def onLeave(self, ev):
+        """
+        Restore the artist to the original colour when the cursor leaves.
+        """
+        ev.artist.set_color(self.color)
+        self.base.draw()
+        return True
+            
+    def onClick(self, ev):
+        """
+        Prepare to move the artist.  Calls save() to preserve the state for
+        later restore().
+        """
+        self.clickx, self.clicky = ev.xdata, ev.ydata
+        self.save(ev)
+        return True
+
+    def onRelease(self, ev):
+        """
+        """
+        self.moveend(ev)
+        return True
+
+    def onDrag(self, ev):
+        """
+        Move the artist.  Calls move() to update the state, or restore() if
+        the mouse leaves the window.
+        """
+        inside, _ = self.axes.contains(ev)
+        if inside:
+            self.clickx, self.clicky = ev.xdata, ev.ydata
+            self.move(ev.xdata, ev.ydata, ev)
+        else:
+            self.restore()
+        self.base.update()
+        return True
+    
+    def onKey(self, ev):
+        """
+        Respond to keyboard events.  Arrow keys move the widget.  Escape
+        restores it to the position before the last click.
+        
+        Calls move() to update the state.  Calls restore() on escape.
+        """
+        if ev.key == 'escape':
+            self.restore()
+        elif ev.key in ['up', 'down', 'right', 'left']:
+            dx, dy = self.dpixel(self.clickx, self.clicky, nudge=ev.control)
+            if ev.key == 'up':
+                self.clicky += dy
+            elif ev.key == 'down':
+                self.clicky -= dy
+            elif ev.key == 'right':
+                self.clickx += dx
+            else: self.clickx -= dx
+            self.move(self.clickx, self.clicky, ev)
+        else:
+            return False
+        self.base.update()
+        return True
+
+    def dpixel(self, x, y, nudge=False):
+        """
+        Return the step size in data coordinates for a small
+        step in screen coordinates.  If nudge is False (default)
+        the step size is one pixel.  If nudge is True, the step
+        size is 0.2 pixels.
+        """
+        ax = self.axes
+        px, py = ax.transData.inverse_xy_tup((x, y))
+        if nudge:
+            nx, ny = ax.transData.xy_tup((px+0.2, py+0.2))
+        else:
+            nx, ny = ax.transData.xy_tup((px+1.0, py+1.0))
+        dx, dy = nx-x, ny-y
+        return dx, dy
+
Index: guiframe/local_perspectives/plotting/Edge.py
===================================================================
--- guiframe/local_perspectives/plotting/Edge.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
+++ guiframe/local_perspectives/plotting/Edge.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
@@ -0,0 +1,143 @@
+
+import math
+#import wx  
+#from copy import deepcopy
+from BaseInteractor import _BaseInteractor
+#from sans.guiframe.events import NewPlotEvent
+#from sans.guiframe.events import StatusEvent
+#from sans.guiframe.events import SlicerParameterEvent
+#from sans.guiframe.events import EVT_SLICER_PARS
+   
+
+class RadiusInteractor(_BaseInteractor):
+    """
+    Select an annulus through a 2D plot
+    """
+    def __init__(self, base, axes, color='black', zorder=5, arc1=None,
+                 arc2=None, theta=math.pi/8):
+        """
+        """
+        _BaseInteractor.__init__(self, base, axes, color=color)
+        self.markers = []
+        self.axes = axes
+        self.r1 = arc1.get_radius()
+        self.r2 = arc2.get_radius()
+        #print "radius init", self.r1, self.r2
+        self.theta = theta
+        self.save_theta = theta
+        #self.scale = 10.0
+        self.move_stop = False
+        self.theta_left = None
+        self.theta_right = None
+        self.arc1 = arc1
+        self.arc2 = arc2
+        #raise "Version error", message
+        x1 = self.r1 * math.cos(self.theta)
+        y1 = self.r1 * math.sin(self.theta)
+        x2 = self.r2 * math.cos(self.theta)
+        y2 = self.r2 * math.sin(self.theta)
+        self.line = self.axes.plot([x1, x2], [y1, y2],
+                                      linestyle='-', marker='',
+                                      color=self.color,
+                                      visible=True)[0]
+        self.phi = theta
+        self.npts = 20
+        self.has_move = False
+        self.connect_markers([self.line])
+        self.update()
+        
+    def set_layer(self, n):
+        """
+        """
+        self.layernum = n
+        self.update()
+        
+    def clear(self):
+        """
+        """
+        self.clear_markers()
+        try:
+            self.line.remove()
+        except:
+            # Old version of matplotlib
+            for item in range(len(self.axes.lines)):
+                del self.axes.lines[0]
+    
+    def get_angle(self):
+        """
+        """
+        return self.theta
+        
+    def update(self, r1=None, r2=None, theta=None):
+        """
+        Draw the new roughness on the graph.
+        """
+        if r1 != None:
+            self.r1 = r1
+        if r2 != None:
+            self.r2 = r2
+        if theta != None:
+            self.theta = theta
+        #print "in the edge r1, r2",self.r1,self.r2,math.degrees(self.theta)
+        x1 = self.r1 * math.cos(self.theta)
+        y1 = self.r1 * math.sin(self.theta)
+        x2 = self.r2 * math.cos(self.theta)
+        y2 = self.r2 * math.sin(self.theta)  
+        self.line.set(xdata=[x1, x2], ydata=[y1, y2])
+        
+    def save(self, ev):
+        """
+        Remember the roughness for this layer and the next so that we
+        can restore on Esc.
+        """
+        self.save_theta = math.atan2(ev.y,ev.x)
+        #self.save_theta= self.theta
+        self.base.freeze_axes()
+   
+    def moveend(self, ev):
+        """
+        """
+        self.has_move = False
+        self.base.moveend(ev)
+            
+    def restore(self, ev):
+        """
+        Restore the roughness for this layer.
+        """
+        self.theta = self.save_theta
+
+    def move(self, x, y, ev):
+        """
+        Process move to a new position, making sure that the move is allowed.
+        """
+        self.theta = math.atan2(y, x)
+        self.has_move = True
+        self.base.base.update()
+        
+    def set_cursor(self, r_min, r_max, theta):
+        """
+        """
+        self.theta = theta
+        self.r1 = r_min
+        self.r2 = r_max
+        self.update()
+        
+    def get_params(self):
+        """
+        """
+        params = {}
+        params["radius1"] = self.r1
+        params["radius2"] = self.r2
+        params["theta"] = self.theta
+        return params
+    
+    def set_params(self, params):
+        """
+        """
+        #print "when here set curcor arc"
+        x1 = params["radius1"] 
+        x2 = params["radius2"] 
+        theta= params["theta"]
+        self.set_cursor(x1, x2, theta)
+        
+    
Index: guiframe/local_perspectives/plotting/Plotter1D.py
===================================================================
--- guiframe/local_perspectives/plotting/Plotter1D.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
+++ guiframe/local_perspectives/plotting/Plotter1D.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
@@ -0,0 +1,518 @@
+
+################################################################################
+#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. 
+#
+#See the license text in license.txt
+#
+#copyright 2008, University of Tennessee
+################################################################################
+
+
+import wx
+import sys
+import os
+import pylab
+import math
+import numpy
+import time
+
+from danse.common.plottools.PlotPanel import PlotPanel
+#from danse.common.plottools.plottables import Graph
+from sans.guiframe import dataFitting 
+from sans.guiframe.events import EVT_NEW_PLOT
+from sans.guiframe.events import StatusEvent 
+from sans.guiframe.events import NewPlotEvent
+from sans.guiframe.events import SlicerEvent
+from sans.guiframe.events import PanelOnFocusEvent
+from sans.guiframe.events import EVT_NEW_LOADED_DATA
+from sans.guiframe.utils import PanelMenu
+from sans.guiframe.dataFitting import Data1D
+from sans.guiframe.panel_base import PanelBase
+from binder import BindArtist
+
+DEFAULT_QMAX = 0.05
+DEFAULT_QSTEP = 0.001
+DEFAULT_BEAM = 0.005
+BIN_WIDTH = 1
+
+
+class ModelPanel1D(PlotPanel, PanelBase):
+    """
+    Plot panel for use with the GUI manager
+    """
+    
+    ## Internal name for the AUI manager
+    window_name = "plotpanel"
+    ## Title to appear on top of the window
+    window_caption = "Plot Panel"
+    ## Flag to tell the GUI manager that this panel is not
+    #  tied to any perspective
+    ALWAYS_ON = True
+    ## Group ID
+    group_id = None
+    
+    def __init__(self, parent, id=-1, color = None,
+                 dpi=None, style=wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs):
+        PlotPanel.__init__(self, parent, id=id, style=style, **kwargs)
+        PanelBase.__init__(self, parent)
+        ## Reference to the parent window
+        self.parent = parent
+        ## Plottables
+        self.plots = {}
+        #context menu
+        self._slicerpop = None
+        
+        self._available_data = []
+        self._menu_add_ids = []
+        self._symbol_labels = self.get_symbol_label()
+      
+        self.hide_menu = None
+        ## Unique ID (from gui_manager)
+        self.uid = None
+        self.x_size = None
+        ## Default locations
+        self._default_save_location = os.getcwd() 
+        self.size = None       
+        ## Graph        
+        #self.graph = Graph()
+        self.graph.xaxis("\\rm{Q}", 'A^{-1}')
+        self.graph.yaxis("\\rm{Intensity} ", "cm^{-1}")
+        self.graph.render(self)
+        
+        # In resizing event
+        self.resizing = False
+        self.canvas.set_resizing(self.resizing)
+        self.Bind(wx.EVT_SIZE, self._OnReSize)
+        self._add_more_tool()
+       
+    def get_symbol_label(self):
+        """
+        Associates label to symbol
+        """
+        _labels = {}
+        i = 0
+        _labels['Circle'] = i
+        i += 1
+        _labels['Cross X '] = i
+        i += 1
+        _labels['Triangle Down'] = i
+        i += 1
+        _labels['Triangle Up'] = i
+        i += 1
+        _labels['Triangle Left'] = i
+        i += 1
+        _labels['Triangle Right'] = i
+        i += 1
+        _labels['Cross +'] = i
+        i += 1
+        _labels['Square'] = i
+        i += 1
+        _labels['Diamond'] = i
+        i += 1
+        _labels['Diamond'] = i
+        i += 1
+        _labels['Hexagon1'] = i
+        i += 1
+        _labels['Hexagon2'] = i
+        i += 1
+        _labels['Pentagon'] = i
+        i += 1
+        _labels['Line'] = i
+        return _labels
+
+    
+    def set_data(self, list=None):
+        """
+        """
+        pass
+    
+    def _reset(self):
+        """
+        Resets internal data and graph
+        """    
+        self.graph.reset()
+        self.plots      = {}
+        
+    def _OnReSize(self, event):   
+        """
+        On response of the resize of a panel, set axes_visiable False
+        """
+        # It was found that wx >= 2.9.3 sends an event even if no size changed.
+        # So manually recode the size (=x_size) and compare here.
+        if self.x_size != None:
+            if self.x_size == self.GetSize():
+                self.resizing = False
+                self.canvas.set_resizing(self.resizing)
+                return
+        self.x_size = self.GetSize()
+
+        # Ready for another event
+        # Do not remove this Skip. Otherwise it will get runtime error on wx>=2.9.
+        event.Skip() 
+        # set the resizing flag
+        self.resizing = True
+        self.canvas.set_resizing(self.resizing)
+        self.parent.set_schedule(True)
+        pos_x, pos_y = self.GetPositionTuple()
+        if pos_x != 0 and pos_y != 0:
+            self.size, _ = self.GetClientSizeTuple()
+        
+    def set_resizing(self, resizing=False):
+        """
+        Set the resizing (True/False)
+        """
+        self.resizing = resizing
+        #self.canvas.set_resizing(resizing)
+    
+    def schedule_full_draw(self, func='append'):    
+        """
+        Put self in schedule to full redraw list
+        """
+        # append/del this panel in the schedule list
+        self.parent.set_schedule_full_draw(self, func)
+        
+
+    def remove_data_by_id(self, id):
+        """'
+        remove data from plot
+        """
+        if id in self.plots.keys():
+            data =  self.plots[id]
+            self.graph.delete(data)
+            data_manager = self._manager.parent.get_data_manager()
+            data_list, theory_list = data_manager.get_by_id(id_list=[id])
+            
+            if id in data_list.keys():
+                data = data_list[id]
+            if id in theory_list.keys():
+                data = theory_list[id]
+           
+            del self.plots[id]
+            self.graph.render(self)
+            self.subplot.figure.canvas.draw_idle()    
+            if len(self.graph.plottables) == 0:
+                #onRemove: graph is empty must be the panel must be destroyed
+                self.parent.delete_panel(self.uid)
+        
+    def plot_data(self, data):
+        """
+        Data is ready to be displayed
+        
+        :param event: data event
+        """
+        if data.id in self.plots.keys():
+            #replace
+            self.graph.replace(data)
+            self.plots[data.id] = data
+        else:
+            self.plots[data.id] = data
+            self.graph.add(self.plots[data.id]) 
+
+        ## Set the view scale for all plots
+        self._onEVT_FUNC_PROPERTY()
+        ## render the graph<=No need this done in canvas
+        #self.graph.render(self)
+        #self.subplot.figure.canvas.draw_idle()
+    
+    def draw_plot(self):
+        """
+        Draw plot
+        """
+        self.draw()  
+
+
+       
+    def onLeftDown(self,event): 
+        """ 
+        left button down and ready to drag
+        Display the position of the mouse on the statusbar
+        """
+        PlotPanel.onLeftDown(self, event)
+        ax = event.inaxes
+        if ax != None:
+            position = "x: %8.3g    y: %8.3g" % (event.xdata, event.ydata)
+            wx.PostEvent(self.parent, StatusEvent(status=position))
+        # unfocus all
+        self.parent.set_plot_unfocus()  
+        #post nd event to notify guiframe that this panel is on focus
+        wx.PostEvent(self.parent, PanelOnFocusEvent(panel=self))
+
+        
+    def _ontoggle_hide_error(self, event):
+        """
+        Toggle error display to hide or show
+        """
+        
+        selected_plot = self.plots[self.graph.selected_plottable]
+        if self.hide_menu.GetText() == "Hide Error":
+            selected_plot.hide_error = True
+        else:
+            selected_plot.hide_error = False
+        ## increment graph color
+        self.graph.render(self)
+        self.subplot.figure.canvas.draw_idle()  
+          
+    def _onRemove(self, event):
+        """
+        Remove a plottable from the graph and render the graph 
+        
+        :param event: Menu event
+        
+        """
+        ## Check if there is a selected graph to remove
+        if self.graph.selected_plottable in self.plots.keys():
+            selected_plot = self.plots[self.graph.selected_plottable]
+            id = self.graph.selected_plottable
+            self.remove_data_by_id(id)
+            
+    def onContextMenu(self, event):
+        """
+        1D plot context menu
+        
+        :param event: wx context event
+        
+        """
+        self._slicerpop = PanelMenu()
+        self._slicerpop.set_plots(self.plots)
+        self._slicerpop.set_graph(self.graph)     
+        # Various plot options
+        id = wx.NewId()
+        self._slicerpop.Append(id, '&Save Image', 'Save image as PNG')
+        wx.EVT_MENU(self, id, self.onSaveImage)
+        id = wx.NewId()
+        self._slicerpop.Append(id, '&Print Image', 'Print image ')
+        wx.EVT_MENU(self, id, self.onPrint)
+        id = wx.NewId()
+        self._slicerpop.Append(id, '&Print Preview', 'Print preview')
+        wx.EVT_MENU(self, id, self.onPrinterPreview)
+        
+        id = wx.NewId()
+        self._slicerpop.Append(id, '&Copy to Clipboard', 'Copy to the clipboard')
+        wx.EVT_MENU(self, id, self.OnCopyFigureMenu)
+        
+        self._slicerpop.AppendSeparator()
+
+        #add menu of other plugins
+        item_list = self.parent.get_context_menu(self)
+
+        if (not item_list == None) and (not len(item_list) == 0):
+            for item in item_list:
+                try:
+                    id = wx.NewId()
+                    self._slicerpop.Append(id, item[0], item[1])
+                    wx.EVT_MENU(self, id, item[2])
+                except:
+                    msg = "ModelPanel1D.onContextMenu: "
+                    msg += "bad menu item  %s" % sys.exc_value
+                    wx.PostEvent(self.parent, StatusEvent(status=msg))
+                    pass
+            self._slicerpop.AppendSeparator()
+        #id = wx.NewId()
+        #self._slicerpop.Append(id, '&Print image', 'Print image')
+        if self.graph.selected_plottable in self.plots:
+            plot = self.plots[self.graph.selected_plottable]
+            
+            id = wx.NewId()
+            name = plot.name
+            self._slicerpop.Append(id, "&Save Points as a File")
+            self._slicerpop.AppendSeparator()
+            if plot.name != 'SLD':
+                wx.EVT_MENU(self, id, self._onSave)
+                id = wx.NewId()
+                self._slicerpop.Append(id, '&Linear Fit')
+                wx.EVT_MENU(self, id, self.onFitting)
+                self._slicerpop.AppendSeparator()
+    
+                id = wx.NewId()
+                self._slicerpop.Append(id, "Remove %s Curve" % name)
+                wx.EVT_MENU(self, id, self._onRemove)
+                if not plot.is_data:
+                    id = wx.NewId()
+                    self._slicerpop.Append(id, '&Freeze', 'Freeze')
+                    wx.EVT_MENU(self, id, self.onFreeze)
+                symbol_menu = wx.Menu()
+                for label in self._symbol_labels:
+                    id = wx.NewId()
+                    symbol_menu.Append(id, str(label), str(label))
+                    wx.EVT_MENU(self, id, self.onChangeSymbol)
+                id = wx.NewId()
+                self._slicerpop.AppendMenu(id,'&Modify Symbol',  symbol_menu)
+                self._slicerpop.AppendSeparator()
+    
+                id = wx.NewId()
+                self.hide_menu = self._slicerpop.Append(id, "Hide Error")
+    
+                if plot.dy is not None and plot.dy != []:
+                    if plot.hide_error :
+                        self.hide_menu.SetText('Show Error')
+                    else:
+                        self.hide_menu.SetText('Hide Error')
+                else:
+                    self.hide_menu.Enable(False)
+                wx.EVT_MENU(self, id, self._ontoggle_hide_error)
+                
+                self._slicerpop.AppendSeparator()
+                # Option to hide
+                #TODO: implement functionality to hide a plottable (legend click)
+        
+        
+        id = wx.NewId()
+        self._slicerpop.Append(id, '&Change scale')
+        wx.EVT_MENU(self, id, self._onProperties)
+        id = wx.NewId()
+        self._slicerpop.Append(id, '&Reset Graph')
+        wx.EVT_MENU(self, id, self.onResetGraph)  
+        pos = event.GetPosition()
+        pos = self.ScreenToClient(pos)
+        self.PopupMenu(self._slicerpop, pos)
+     
+    def onFreeze(self, event):
+        """
+        """
+        plot = self.plots[self.graph.selected_plottable]
+        self.parent.onfreeze([plot.id])
+        
+    def onChangeSymbol(self, event):
+        """
+        """
+        menu = event.GetEventObject()
+        id = event.GetId()
+        label =  menu.GetLabel(id)
+        selected_plot = self.plots[self.graph.selected_plottable]
+        selected_plot.symbol = self._symbol_labels[label]
+        ## Set the view scale for all plots
+        self._onEVT_FUNC_PROPERTY()
+        ## render the graph
+        #self.graph.render(self)
+        #self.subplot.figure.canvas.draw_idle()
+        
+    def _onsaveTXT(self, path):
+        """
+        Save file as txt
+            
+        :TODO: Refactor and remove this method. See TODO in _onSave.
+        
+        """
+        data = self.plots[self.graph.selected_plottable]
+       
+        if not path == None:
+            out = open(path, 'w')
+            has_errors = True
+            if data.dy == None or data.dy == []:
+                has_errors = False
+            # Sanity check
+            if has_errors:
+                try:
+                    if len(data.y) != len(data.dy):
+                        has_errors = False
+                except:
+                    has_errors = False
+            if has_errors:
+                if data.dx != None:
+                    out.write("<X>   <Y>   <dY>   <dX>\n")
+                else:
+                    out.write("<X>   <Y>   <dY>\n")
+            else:
+                out.write("<X>   <Y>\n")
+                
+            for i in range(len(data.x)):
+                if has_errors:
+                    if data.dx != None:
+                        out.write("%g  %g  %g  %g\n" % (data.x[i], 
+                                                    data.y[i],
+                                                    data.dy[i],
+                                                    data.dx[i]))
+                    else:
+                        out.write("%g  %g  %g\n" % (data.x[i], 
+                                                    data.y[i],
+                                                    data.dy[i]))
+                else:
+                    out.write("%g  %g\n" % (data.x[i], 
+                                            data.y[i]))
+            out.close()                 
+            try:
+                self._default_save_location = os.path.dirname(path)
+            except:
+                pass    
+                
+    def _onSave(self, evt):
+        """
+        Save a data set to a text file
+        
+        :param evt: Menu event
+        
+        """
+       
+        path = None
+        wildcard = "Text files (*.txt)|*.txt|"\
+        "CanSAS 1D files(*.xml)|*.xml" 
+        dlg = wx.FileDialog(self, "Choose a file",
+                            self._default_save_location,
+                             "", wildcard , wx.SAVE)
+       
+        if dlg.ShowModal() == wx.ID_OK:
+            path = dlg.GetPath()
+            # ext_num = 0 for .txt, ext_num = 1 for .xml
+            # This is MAC Fix
+            ext_num = dlg.GetFilterIndex()
+            if ext_num == 0:
+                format = '.txt'
+            else:
+                format = '.xml'
+            path = os.path.splitext(path)[0] + format
+            mypath = os.path.basename(path)
+            
+            #TODO: This is bad design. The DataLoader is designed 
+            #to recognize extensions.
+            # It should be a simple matter of calling the .
+            #save(file, data, '.xml') method
+            # of the DataLoader.loader.Loader class.
+            from DataLoader.loader import  Loader
+            #Instantiate a loader 
+            loader = Loader() 
+            data = self.plots[self.graph.selected_plottable]
+            format = ".txt"
+            if os.path.splitext(mypath)[1].lower() == format:
+                # Make sure the ext included in the file name
+                # especially on MAC
+                fName = os.path.splitext(path)[0] + format
+                self._onsaveTXT(fName)
+            format = ".xml"
+            if os.path.splitext(mypath)[1].lower() == format:
+                # Make sure the ext included in the file name
+                # especially on MAC
+                fName = os.path.splitext(path)[0] + format
+                loader.save(fName, data, format)
+            try:
+                self._default_save_location = os.path.dirname(path)
+            except:
+                pass    
+        dlg.Destroy()
+
+    def _add_more_tool(self):
+        """
+        Add refresh button in the tool bar
+        """
+        if self.parent.__class__.__name__ != 'ViewerFrame':
+            return
+        self.toolbar.AddSeparator()
+        id_delete = wx.NewId()
+        delete =  wx.ArtProvider.GetBitmap(wx.ART_DELETE, wx.ART_TOOLBAR)
+        self.toolbar.AddSimpleTool(id_delete, delete,
+                           'Delete', 'permanently Delete')
+
+        self.toolbar.Realize()
+        wx.EVT_TOOL(self, id_delete,  self._on_delete)
+
+    def _on_delete(self, event): 
+        """
+        Refreshes the plotpanel on refresh tollbar button
+        """
+        
+        if self.parent is not None:
+            wx.PostEvent(self.parent, 
+                         NewPlotEvent(group_id=self.group_id,
+                                      action="delete"))
+            
Index: guiframe/local_perspectives/plotting/Plotter2D.py
===================================================================
--- guiframe/local_perspectives/plotting/Plotter2D.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
+++ guiframe/local_perspectives/plotting/Plotter2D.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
@@ -0,0 +1,627 @@
+
+################################################################################
+#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. 
+#
+#See the license text in license.txt
+#
+#copyright 2008, University of Tennessee
+################################################################################
+
+
+import wx
+import sys
+import os
+import math
+import numpy
+import pylab
+import danse.common.plottools
+from danse.common.plottools.PlotPanel import PlotPanel
+from danse.common.plottools.plottables import Graph
+from sans.guiframe.events import EVT_NEW_PLOT
+from sans.guiframe.events import EVT_SLICER_PARS
+from sans.guiframe.events import StatusEvent 
+from sans.guiframe.events import NewPlotEvent
+from sans.guiframe.events import PanelOnFocusEvent
+from sans.guiframe.events import SlicerEvent
+from sans.guiframe.utils import PanelMenu
+from binder import BindArtist
+from Plotter1D import ModelPanel1D
+from danse.common.plottools.toolbar import NavigationToolBar 
+from sans.guiframe.dataFitting import Data1D
+(InternalEvent, EVT_INTERNAL) = wx.lib.newevent.NewEvent()
+
+DEFAULT_QMAX = 0.05
+DEFAULT_QSTEP = 0.001
+DEFAULT_BEAM = 0.005
+BIN_WIDTH = 1.0
+
+
+class NavigationToolBar2D(NavigationToolBar):
+    """
+    """
+    def __init__(self, canvas, parent=None):
+        NavigationToolBar.__init__(self, canvas=canvas, parent=parent)
+        
+    def delete_option(self):
+        """
+        remove default toolbar item
+        """
+        #delete reset button
+        self.DeleteToolByPos(0) 
+        #delete dragging
+        self.DeleteToolByPos(2) 
+        #delete unwanted button that configures subplot parameters
+        self.DeleteToolByPos(4)
+        
+    def add_option(self):
+        """
+        add item to the toolbar
+        """
+        #add print button
+        id_print = wx.NewId()
+        print_bmp =  wx.ArtProvider.GetBitmap(wx.ART_PRINT, wx.ART_TOOLBAR)
+        self.AddSimpleTool(id_print, print_bmp,
+                           'Print', 'Activate printing')
+        wx.EVT_TOOL(self, id_print, self.on_print)
+        
+        
+class ModelPanel2D(ModelPanel1D):
+    """
+    Plot panel for use with the GUI manager
+    """
+    
+    ## Internal name for the AUI manager
+    window_name = "plotpanel"
+    ## Title to appear on top of the window
+    window_caption = "Plot Panel"
+    ## Flag to tell the GUI manager that this panel is not
+    #  tied to any perspective
+    ALWAYS_ON = True
+    ## Group ID
+    group_id = None
+    
+    
+    def __init__(self, parent, id=-1, data2d=None, color = None,
+                 dpi=None, style=wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs):
+        """
+        Initialize the panel
+        """
+        ModelPanel1D.__init__(self, parent, id=id, style=style, **kwargs)
+        
+        ## Reference to the parent window
+        self.parent = parent
+        ## Dictionary containing Plottables
+        self.plots = {}
+        ## Save reference of the current plotted 
+        self.data2D = data2d
+        ## Unique ID (from gui_manager)
+        self.uid = None
+        ## Action IDs for internal call-backs
+        self.action_ids = {}
+        ## Create Artist and bind it
+        self.connect = BindArtist(self.subplot.figure)
+        ## Beam stop
+        self.beamstop_radius = DEFAULT_BEAM
+        ## to set the order of lines drawn first.
+        self.slicer_z = 5
+        ## Reference to the current slicer
+        self.slicer = None
+        ## event to send slicer info 
+        self.Bind(EVT_INTERNAL, self._onEVT_INTERNAL)
+        
+        self.axes_frozen = False
+        ## panel that contains result from slicer motion (ex: Boxsum info)
+        self.panel_slicer = None
+        ## Graph        
+        self.graph = Graph()
+        self.graph.xaxis("\\rm{Q}", 'A^{-1}')
+        self.graph.yaxis("\\rm{Intensity} ", "cm^{-1}")
+        self.graph.render(self)
+        ## store default value of zmin and zmax 
+        self.default_zmin_ctl = self.zmin_2D
+        self.default_zmax_ctl = self.zmax_2D
+       
+    def onLeftDown(self, event): 
+        """
+        left button down and ready to drag
+        
+        """
+        # Check that the LEFT button was pressed
+        if event.button == 1:
+            self.leftdown = True
+            ax = event.inaxes
+            if ax != None:
+                self.xInit, self.yInit = event.xdata, event.ydata
+        self.plottable_selected(self.data2D.id)
+       
+        self._manager.set_panel_on_focus(self)
+        wx.PostEvent(self.parent, PanelOnFocusEvent(panel=self))
+        
+    def add_toolbar(self):
+        """
+        add toolbar
+        """
+        self.enable_toolbar = True
+        self.toolbar = NavigationToolBar2D(parent=self, canvas=self.canvas)
+        self.toolbar.Realize()
+        # On Windows platform, default window size is incorrect, so set
+        # toolbar width to figure width.
+        tw, th = self.toolbar.GetSizeTuple()
+        fw, fh = self.canvas.GetSizeTuple()
+        self.toolbar.SetSize(wx.Size(fw, th))
+        self.sizer.Add(self.toolbar, 0, wx.LEFT|wx.EXPAND)
+        # update the axes menu on the toolbar
+        self.toolbar.update()
+         
+    def plot_data(self, data):
+        """
+        Data is ready to be displayed
+        
+        :TODO: this name should be changed to something more appropriate
+             Don't forget that changing this name will mean changing code
+             in plotting.py
+         
+        :param event: data event
+        """
+        ## Update self.data2d with the current plot
+        self.data2D = data
+        if data.id in self.plots.keys():
+            #replace
+            self.graph.replace(data)
+            self.plots[data.id] = data
+        else:
+            self.plots[data.id] = data
+            self.graph.add(self.plots[data.id]) 
+            # update qmax with the new xmax of data plotted
+            self.qmax = data.xmax
+            
+        self.slicer = None
+        # Check axis labels
+        #TODO: Should re-factor this
+        ## render the graph with its new content
+                
+        #data2D: put 'Pixel (Number)' for axis title and unit in case of having no detector info and none in _units
+        if len(self.data2D.detector) < 1: 
+            if len(data._xunit) < 1 and len(data._yunit) < 1:
+                data._xaxis = '\\rm{x}'
+                data._yaxis = '\\rm{y}'
+                data._xunit = 'pixel'
+                data._yunit = 'pixel'
+        self.graph.xaxis(data._xaxis, data._xunit)
+        self.graph.yaxis(data._yaxis, data._yunit)
+        self.graph.title(self.data2D.name)
+        self.graph.render(self)
+        self.draw_plot()
+        #self.subplot.figure.canvas.draw_idle()
+        ## store default value of zmin and zmax 
+        self.default_zmin_ctl = self.zmin_2D
+        self.default_zmax_ctl = self.zmax_2D
+
+    def onContextMenu(self, event):
+        """
+        2D plot context menu
+        
+        :param event: wx context event
+        
+        """
+        slicerpop = PanelMenu()
+        slicerpop.set_plots(self.plots)
+        slicerpop.set_graph(self.graph)
+             
+        id = wx.NewId()
+        slicerpop.Append(id, '&Save Image')
+        wx.EVT_MENU(self, id, self.onSaveImage)
+        
+        id = wx.NewId()
+        slicerpop.Append(id,'&Print Image', 'Print image')
+        wx.EVT_MENU(self, id, self.onPrint)
+        
+        id = wx.NewId()
+        slicerpop.Append(id,'&Print Preview', 'Print preview')
+        wx.EVT_MENU(self, id, self.onPrinterPreview)
+
+        id = wx.NewId()
+        slicerpop.Append(id, '&Copy to Clipboard', 'Copy to the clipboard')
+        wx.EVT_MENU(self, id, self.OnCopyFigureMenu)
+        slicerpop.AppendSeparator()
+        # saving data
+        plot = self.data2D
+        id = wx.NewId()
+        name = plot.name
+        slicerpop.Append(id, "&Save as a file (DAT)" )
+        self.action_ids[str(id)] = plot
+        wx.EVT_MENU(self, id, self._onSave)
+
+        slicerpop.AppendSeparator()
+        if len(self.data2D.detector) == 1:        
+            
+            item_list = self.parent.get_context_menu(self)
+            if (not item_list == None) and (not len(item_list) == 0) and\
+                self.data2D.name.split(" ")[0] != 'Residuals':  
+                # The line above; Not for trunk
+                for item in item_list:
+                    try:
+                        id = wx.NewId()
+                        slicerpop.Append(id, item[0], item[1])
+                        wx.EVT_MENU(self, id, item[2])
+                    except:
+                        msg = "ModelPanel1D.onContextMenu: "
+                        msg += "bad menu item  %s"%sys.exc_value
+                        wx.PostEvent(self.parent, StatusEvent(status=msg))
+                        pass
+                slicerpop.AppendSeparator()
+            
+            id = wx.NewId()
+            slicerpop.Append(id, '&Perform circular average')
+            wx.EVT_MENU(self, id, self.onCircular) \
+            # For Masked Data
+            if not plot.mask.all():
+                id = wx.NewId()
+                slicerpop.Append(id, '&Masked circular average')
+                wx.EVT_MENU(self, id, self.onMaskedCircular) 
+            id = wx.NewId()
+            slicerpop.Append(id, '&Sector [Q view]')
+            wx.EVT_MENU(self, id, self.onSectorQ) 
+            id = wx.NewId()
+            slicerpop.Append(id, '&Annulus [Phi view ]')
+            wx.EVT_MENU(self, id, self.onSectorPhi) 
+            id = wx.NewId()
+            slicerpop.Append(id, '&Box Sum')
+            wx.EVT_MENU(self, id, self.onBoxSum) 
+            id = wx.NewId()
+            slicerpop.Append(id, '&Box averaging in Qx')
+            wx.EVT_MENU(self, id, self.onBoxavgX) 
+            id = wx.NewId()
+            slicerpop.Append(id, '&Box averaging in Qy')
+            wx.EVT_MENU(self, id, self.onBoxavgY) 
+            if self.slicer != None:
+                id = wx.NewId()
+                slicerpop.Append(id, '&Clear slicer')
+                wx.EVT_MENU(self, id,  self.onClearSlicer) 
+                if self.slicer.__class__.__name__  != "BoxSum":
+                    id = wx.NewId()
+                    slicerpop.Append(id, '&Edit Slicer Parameters')
+                    wx.EVT_MENU(self, id, self._onEditSlicer) 
+            slicerpop.AppendSeparator() 
+        id = wx.NewId()
+        slicerpop.Append(id, '&2D Color Map')
+        wx.EVT_MENU(self, id, self._onEditDetector)
+        id = wx.NewId()
+        slicerpop.Append(id, '&Toggle Linear/Log scale')
+        wx.EVT_MENU(self, id, self._onToggleScale) 
+        pos = event.GetPosition()
+        pos = self.ScreenToClient(pos)
+        self.PopupMenu(slicerpop, pos)
+   
+    def _onEditDetector(self, event):
+        """
+        Allow to view and edits  detector parameters
+        
+        :param event: wx.menu event
+        
+        """
+        import detector_dialog
+        dialog = detector_dialog.DetectorDialog(self, -1,base=self.parent,
+                       reset_zmin_ctl =self.default_zmin_ctl,
+                       reset_zmax_ctl = self.default_zmax_ctl,cmap=self.cmap)
+        ## info of current detector and data2D
+        xnpts = len(self.data2D.x_bins)
+        ynpts = len(self.data2D.y_bins)
+        xmax = max(self.data2D.xmin, self.data2D.xmax)
+        ymax = max(self.data2D.ymin, self.data2D.ymax)
+        qmax = math.sqrt(math.pow(xmax, 2) + math.pow(ymax, 2))
+        beam = self.data2D.xmin
+        ## set dialog window content
+        dialog.setContent(xnpts=xnpts,ynpts=ynpts,qmax=qmax,
+                           beam=self.data2D.xmin,
+                           zmin = self.zmin_2D,
+                          zmax = self.zmax_2D)
+        if dialog.ShowModal() == wx.ID_OK:
+            evt = dialog.getContent()
+            self.zmin_2D = evt.zmin
+            self.zmax_2D = evt.zmax
+            self.cmap = evt.cmap
+        dialog.Destroy()
+        ## Redraw the current image
+        self.image(data=self.data2D.data,
+                   qx_data=self.data2D.qx_data,
+                   qy_data=self.data2D.qy_data,
+                   xmin= self.data2D.xmin,
+                   xmax= self.data2D.xmax,
+                   ymin= self.data2D.ymin,
+                   ymax= self.data2D.ymax,
+                   zmin= self.zmin_2D,
+                   zmax= self.zmax_2D,
+                   cmap= self.cmap,
+                   color=0, symbol=0, label=self.data2D.name)
+        self.subplot.figure.canvas.draw_idle()
+        
+    def freeze_axes(self):
+        """
+        """
+        self.axes_frozen = True
+        
+    def thaw_axes(self):
+        """
+        """
+        self.axes_frozen = False
+        
+    def onMouseMotion(self,event):
+        """
+        """
+        pass
+    
+    def onWheel(self, event):
+        """
+        """
+        pass  
+      
+    def update(self, draw=True):
+        """
+        Respond to changes in the model by recalculating the 
+        profiles and resetting the widgets.
+        """
+        self.draw_plot()
+        
+    def _getEmptySlicerEvent(self):
+        """
+        create an empty slicervent 
+        """
+        return SlicerEvent(type=None, params=None, obj_class=None)
+        
+    def _onEVT_INTERNAL(self, event):
+        """
+        Draw the slicer
+        
+        :param event: wx.lib.newevent (SlicerEvent) containing slicer
+            parameter
+            
+        """
+        self._setSlicer(event.slicer)
+            
+    def _setSlicer(self, slicer):
+        """
+        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 onMaskedCircular(self, event):
+        """
+        perform circular averaging on Data2D with mask if it exists
+        
+        :param event: wx.menu event
+        
+        """
+        self.onCircular(event, True)
+        
+    def onCircular(self, event, ismask=False):
+        """
+        perform circular averaging on Data2D
+        
+        :param event: wx.menu event
+        
+        """
+        # Find the best number of bins
+        npt = math.sqrt(len(self.data2D.data[numpy.isfinite(self.data2D.data)]))
+        npt = math.floor(npt)
+        from DataLoader.manipulations import CircularAverage
+        ## compute the maximum radius of data2D
+        self.qmax = max(math.fabs(self.data2D.xmax), 
+                        math.fabs(self.data2D.xmin))
+        self.ymax = max(math.fabs(self.data2D.ymax),
+                        math.fabs(self.data2D.ymin))
+        self.radius = math.sqrt(math.pow(self.qmax, 2)+ math.pow(self.ymax, 2)) 
+        ##Compute beam width
+        bin_width = (self.qmax + self.qmax)/npt
+        ## Create data1D circular average of data2D
+        Circle = CircularAverage(r_min=0, r_max=self.radius, 
+                                 bin_width=bin_width)
+        circ = Circle(self.data2D, ismask=ismask)
+        from sans.guiframe.dataFitting import Data1D
+        if hasattr(circ, "dxl"):
+            dxl = circ.dxl
+        else:
+            dxl = None
+        if hasattr(circ, "dxw"):
+            dxw = circ.dxw
+        else:
+            dxw = None
+
+        new_plot = Data1D(x=circ.x, y=circ.y, dy=circ.dy, dx=circ.dx)
+        new_plot.dxl = dxl
+        new_plot.dxw = dxw
+        new_plot.name = "Circ avg " + self.data2D.name
+        new_plot.source = self.data2D.source
+        #new_plot.info = self.data2D.info
+        new_plot.interactive = True
+        new_plot.detector = self.data2D.detector
+        
+        ## If the data file does not tell us what the axes are, just assume...
+        new_plot.xaxis("\\rm{Q}", "A^{-1}")
+        if hasattr(self.data2D, "scale") and \
+                    self.data2D.scale == 'linear':
+            new_plot.ytransform = 'y'
+            new_plot.yaxis("\\rm{Residuals} ", "normalized")
+        else:
+            new_plot.yaxis("\\rm{Intensity} ", "cm^{-1}")
+
+        new_plot.group_id = "Circ avg " + self.data2D.name
+        new_plot.id = "Circ avg " + self.data2D.name
+        new_plot.is_data = True
+        wx.PostEvent(self.parent, 
+                     NewPlotEvent(plot=new_plot, title=new_plot.name))
+       
+    def _onEditSlicer(self, event):
+        """
+        Is available only when a slicer is drawn.Create a dialog 
+        window where the user can enter value to reset slicer
+        parameters.
+        
+        :param event: wx.menu event
+        
+        """
+        if self.slicer != None:
+            from SlicerParameters import SlicerParameterPanel
+            dialog = SlicerParameterPanel(self, -1, "Slicer Parameters")
+            dialog.set_slicer(self.slicer.__class__.__name__,
+                            self.slicer.get_params())
+            if dialog.ShowModal() == wx.ID_OK:
+                dialog.Destroy() 
+        
+    def onSectorQ(self, event):
+        """
+        Perform sector averaging on Q and draw sector slicer
+        """
+        from SectorSlicer import SectorInteractor
+        self.onClearSlicer(event)
+        wx.PostEvent(self, InternalEvent(slicer=SectorInteractor))
+        
+    def onSectorPhi(self, event):
+        """
+        Perform sector averaging on Phi and draw annulus slicer
+        """
+        from AnnulusSlicer import AnnulusInteractor
+        self.onClearSlicer(event)
+        wx.PostEvent(self, InternalEvent(slicer=AnnulusInteractor))
+        
+    def onBoxSum(self, event):
+        """
+        """
+        from boxSum import BoxSum
+        self.onClearSlicer(event)
+        self.slicer_z += 1
+        self.slicer =  BoxSum(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)
+        self.update()
+        self.slicer.update()
+        ## Value used to initially set the slicer panel
+        type = self.slicer.__class__.__name__
+        params = self.slicer.get_params()
+        ## Create a new panel to display results of summation of Data2D
+        from slicerpanel import SlicerPanel
+        new_panel = SlicerPanel(parent=self.parent, id=-1,
+                                    base=self, type=type,
+                                    params=params, style=wx.RAISED_BORDER)
+        
+        new_panel.window_caption = self.slicer.__class__.__name__ + " " + \
+                                    str(self.data2D.name)
+        new_panel.window_name = self.slicer.__class__.__name__+ " " + \
+                                    str(self.data2D.name)
+        ## Store a reference of the new created panel
+        self.panel_slicer = new_panel
+        ## save the window_caption of the new panel in the current slicer
+        self.slicer.set_panel_name(name=new_panel.window_caption)
+        ## post slicer panel to guiframe to display it 
+        from sans.guiframe.events import SlicerPanelEvent
+        wx.PostEvent(self.parent, SlicerPanelEvent(panel=self.panel_slicer,
+                                                    main_panel=self))
+
+    def onBoxavgX(self,event):
+        """
+        Perform 2D data averaging on Qx
+        Create a new slicer .
+        
+        :param event: wx.menu event
+        """
+        from boxSlicer import BoxInteractorX
+        self.onClearSlicer(event)
+        wx.PostEvent(self, InternalEvent(slicer=BoxInteractorX))
+       
+    def onBoxavgY(self,event):
+        """
+        Perform 2D data averaging on Qy
+        Create a new slicer .
+        
+        :param event: wx.menu event
+        
+        """
+        from boxSlicer import BoxInteractorY
+        self.onClearSlicer(event)
+        wx.PostEvent(self, InternalEvent(slicer=BoxInteractorY))
+        
+    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
+            # Post slicer None event
+            event = self._getEmptySlicerEvent()
+            wx.PostEvent(self, event)
+            
+    def _onSave(self, evt):
+        """
+        Save a data set to a dat(text) file
+        
+        :param evt: Menu event
+        
+        """
+        id = str(evt.GetId())
+        if id in self.action_ids:         
+            
+            path = None
+            wildcard = "IGOR/DAT 2D file in Q_map (*.dat)|*.DAT"
+            dlg = wx.FileDialog(self, "Choose a file",
+                                self._default_save_location,
+                                 "", wildcard , wx.SAVE)
+           
+            if dlg.ShowModal() == wx.ID_OK:
+                path = dlg.GetPath()
+                # ext_num = 0 for .txt, ext_num = 1 for .xml
+                # This is MAC Fix
+                ext_num = dlg.GetFilterIndex()
+                if ext_num == 0:
+                    format = '.dat'
+                else:
+                    format = ''
+                path = os.path.splitext(path)[0] + format
+                mypath = os.path.basename(path)
+                
+                #TODO: This is bad design. The DataLoader is designed 
+                #to recognize extensions.
+                # It should be a simple matter of calling the .
+                #save(file, data, '.xml') method
+                # of the DataLoader.loader.Loader class.
+                from DataLoader.loader import  Loader
+                #Instantiate a loader 
+                loader = Loader() 
+                data = self.data2D
+
+                format = ".dat"
+                if os.path.splitext(mypath)[1].lower() == format:
+                    # Make sure the ext included in the file name
+                    # especially on MAC
+                    fName = os.path.splitext(path)[0] + format
+                    loader.save(fName, data, format)
+                try:
+                    self._default_save_location = os.path.dirname(path)
+                except:
+                    pass    
+            dlg.Destroy()
Index: guiframe/local_perspectives/plotting/SectorSlicer.py
===================================================================
--- guiframe/local_perspectives/plotting/SectorSlicer.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
+++ guiframe/local_perspectives/plotting/SectorSlicer.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
@@ -0,0 +1,626 @@
+
+import math
+import wx
+#from copy import deepcopy
+from BaseInteractor import _BaseInteractor
+from sans.guiframe.events import NewPlotEvent
+from sans.guiframe.events import StatusEvent
+from sans.guiframe.events import SlicerParameterEvent
+from sans.guiframe.events import EVT_SLICER_PARS
+from sans.guiframe.dataFitting import Data1D
+
+
+class SectorInteractor(_BaseInteractor):
+    """
+    Draw a sector slicer.Allow to performQ averaging on data 2D
+    """
+    def __init__(self, base, axes, color='black', zorder=3):
+        
+        _BaseInteractor.__init__(self, base, axes, color=color)
+        ## Class initialization
+        self.markers = []
+        self.axes = axes   
+        ## connect the plot to event 
+        self.connect = self.base.connect
+        
+        ## compute qmax limit to reset the graph     
+        x = math.pow(max(self.base.data2D.xmax, 
+                         math.fabs(self.base.data2D.xmin)), 2)
+        y = math.pow(max(self.base.data2D.ymax, 
+                         math.fabs(self.base.data2D.ymin)), 2)
+        self.qmax = math.sqrt(x + y)
+        ## Number of points on the plot
+        self.nbins = 20
+        ## Angle of the middle line
+        self.theta2 = math.pi/3
+        ## Absolute value of the Angle between the middle line and any side line
+        self.phi = math.pi/12
+        ## Middle line
+        self.main_line = LineInteractor(self, self.base.subplot, color='blue', 
+                                        zorder=zorder, r=self.qmax,
+                                           theta= self.theta2)
+        self.main_line.qmax = self.qmax
+        ## Right Side line
+        self.right_line = SideInteractor(self, self.base.subplot, color='black',
+                                          zorder=zorder, r=self.qmax,
+                                          phi=-1*self.phi, theta2=self.theta2)
+        self.right_line.qmax = self.qmax
+        ## Left Side line 
+        self.left_line = SideInteractor(self, self.base.subplot, color='black',
+                                        zorder=zorder, r=self.qmax,
+                                           phi=self.phi, theta2=self.theta2)
+        self.left_line.qmax = self.qmax
+        ## draw the sector               
+        self.update()
+        self._post_data()
+        ## Bind to slice parameter events
+        self.base.Bind(EVT_SLICER_PARS, self._onEVT_SLICER_PARS)
+
+    def _onEVT_SLICER_PARS(self, event):
+        """
+        receive an event containing parameters values to reset the slicer
+        
+        :param event: event of type SlicerParameterEvent with params as 
+        attribute
+        
+        """
+        wx.PostEvent(self.base.parent,
+                     StatusEvent(status="SectorSlicer._onEVT_SLICER_PARS"))
+        event.Skip()
+        if event.type == self.__class__.__name__:
+            self.set_params(event.params)
+            self.base.update()
+            
+    def set_layer(self, n):
+        """
+         Allow adding plot to the same panel
+         
+        :param n: the number of layer
+        
+        """
+        self.layernum = n
+        self.update()
+        
+    def clear(self):
+        """
+        Clear the slicer and all connected events related to this slicer
+        """
+        self.clear_markers()
+        self.main_line.clear()
+        self.left_line.clear()
+        self.right_line.clear()
+        self.base.connect.clearall()
+        self.base.Unbind(EVT_SLICER_PARS)
+        
+    def update(self):
+        """
+        Respond to changes in the model by recalculating the profiles and
+        resetting the widgets.
+        """
+        # Update locations  
+        ## Check if the middle line was dragged and 
+        #update the picture accordingly     
+        if self.main_line.has_move:
+            self.main_line.update()
+            self.right_line.update(delta=-self.left_line.phi/2,
+                                    mline=self.main_line.theta)
+            self.left_line.update(delta=self.left_line.phi/2,
+                                   mline=self.main_line.theta)
+        ## Check if the left side has moved and update the slicer accordingly  
+        if self.left_line.has_move:
+            self.main_line.update()
+            self.left_line.update(phi=None, delta=None, mline=self.main_line,
+                                  side=True, left=True)
+            self.right_line.update(phi=self.left_line.phi, delta=None,
+                                     mline=self.main_line, side=True,
+                                     left=False, right=True)
+        ## Check if the right side line has moved and 
+        #update the slicer accordingly
+        if self.right_line.has_move:
+            self.main_line.update()
+            self.right_line.update(phi=None, delta=None, mline=self.main_line,
+                                   side=True, left=False, right=True)
+            self.left_line.update(phi=self.right_line.phi, delta=None,
+                                    mline=self.main_line, side=True, left=False)
+            
+    def save(self, ev):
+        """
+        Remember the roughness for this layer and the next so that we
+        can restore on Esc.
+        """
+        self.base.freeze_axes()
+        self.main_line.save(ev)
+        self.right_line.save(ev)
+        self.left_line.save(ev)
+
+    def _post_data(self, nbins=None):
+        """
+        compute sector averaging of data2D into data1D
+        
+        :param nbins: the number of point to plot for the average 1D data
+        """
+        ## get the data2D to average
+        data = self.base.data2D
+        # If we have no data, just return
+        if data == None:
+            return
+        ## Averaging
+        from DataLoader.manipulations import SectorQ
+        radius = self.qmax 
+        phimin =  -self.left_line.phi + self.main_line.theta
+        phimax = self.left_line.phi + self.main_line.theta
+        if nbins == None:
+            nbins = 20
+        sect = SectorQ(r_min=0.0, r_max=radius,
+                        phi_min=phimin + math.pi,
+                        phi_max=phimax + math.pi, nbins=nbins)
+     
+        sector = sect(self.base.data2D)
+        ##Create 1D data resulting from average
+       
+        if hasattr(sector, "dxl"):
+            dxl = sector.dxl
+        else:
+            dxl = None
+        if hasattr(sector, "dxw"):
+            dxw = sector.dxw
+        else:
+            dxw = None
+        new_plot = Data1D(x=sector.x, y=sector.y, dy=sector.dy, dx=sector.dx)
+        new_plot.dxl = dxl
+        new_plot.dxw = dxw
+        new_plot.name = "SectorQ" + "(" + self.base.data2D.name + ")"
+        new_plot.source = self.base.data2D.source
+        #new_plot.info=self.base.data2D.info
+        new_plot.interactive = True
+        new_plot.detector = self.base.data2D.detector
+        ## If the data file does not tell us what the axes are, just assume...
+        new_plot.xaxis("\\rm{Q}", "A^{-1}")
+        new_plot.yaxis("\\rm{Intensity}", "cm^{-1}")
+        if hasattr(data, "scale") and data.scale == 'linear' and \
+                self.base.data2D.name.count("Residuals") > 0:
+            new_plot.ytransform = 'y'
+            new_plot.yaxis("\\rm{Residuals} ", "/")
+
+        new_plot.group_id = "SectorQ" + self.base.data2D.name
+        new_plot.id = None
+        new_plot.is_data = True
+
+        wx.PostEvent(self.base.parent, NewPlotEvent(plot=new_plot,
+                                    title="SectorQ" + self.base.data2D.name))
+        
+    def moveend(self, ev):
+        """
+        Called a dragging motion ends.Get slicer event 
+        """
+        self.base.thaw_axes()
+        ## Post parameters
+        event = SlicerParameterEvent()
+        event.type = self.__class__.__name__
+        event.params = self.get_params()
+        ## Send slicer paramers to plotter2D
+        wx.PostEvent(self.base, event)
+        
+    def restore(self):
+        """
+        Restore the roughness for this layer.
+        """
+        self.main_line.restore()
+        self.left_line.restore()
+        self.right_line.restore()
+
+    def move(self, x, y, ev):
+        """
+        Process move to a new position, making sure that the move is allowed.
+        """
+        pass
+        
+    def set_cursor(self, x, y):
+        """
+        """
+        pass
+        
+    def get_params(self):
+        """
+        Store a copy of values of parameters of the slicer into a dictionary.
+        
+        :return params: the dictionary created
+        
+        """
+        params = {}
+        ## Always make sure that the left and the right line are at phi 
+        ## angle of the middle line
+        if math.fabs(self.left_line.phi) != math.fabs(self.right_line.phi):
+            msg = "Phi left and phi right are different"
+            msg += " %f, %f" % (self.left_line.phi, self.right_line.phi)
+            raise ValueError, msg
+        params["Phi"] = self.main_line.theta
+        params["Delta_Phi"] = math.fabs(self.left_line.phi)
+        params["nbins"] = self.nbins
+        return params
+    
+    def set_params(self, params):
+        """
+        Receive a dictionary and reset the slicer with values contained 
+        in the values of the dictionary.
+        
+        :param params: a dictionary containing name of slicer parameters and 
+            values the user assigned to the slicer.
+        """
+        main = params["Phi"] 
+        phi = math.fabs(params["Delta_Phi"])
+        self.nbins = int(params["nbins"])
+        self.main_line.theta = main
+        ## Reset the slicer parameters
+        self.main_line.update()
+        self.right_line.update(phi=phi, delta=None, mline=self.main_line,
+                               side=True, right=True)
+        self.left_line.update(phi=phi, delta=None, 
+                              mline=self.main_line, side=True)
+        ## post the new corresponding data
+        self._post_data(nbins=self.nbins)
+        
+    def freeze_axes(self):
+        """
+        """
+        self.base.freeze_axes()
+    
+    def thaw_axes(self):
+        """
+        """
+        self.base.thaw_axes()
+
+    def draw(self):
+        """
+        """
+        self.base.draw()
+
+        
+class SideInteractor(_BaseInteractor):
+    """
+    Draw an oblique line
+    
+    :param phi: the phase between the middle line and one side line
+    :param theta2: the angle between the middle line and x- axis
+    
+    """
+    def __init__(self, base, axes, color='black', zorder=5, r=1.0,
+                 phi=math.pi/4, theta2= math.pi/3):
+        """
+        """
+        _BaseInteractor.__init__(self, base, axes, color=color)
+        ## Initialize the class
+        self.markers = []
+        self.axes = axes
+        ## compute the value of the angle between the current line and
+        ## the x-axis  
+        self.save_theta = theta2 + phi
+        self.theta = theta2 + phi
+        ## the value of the middle line angle with respect to the x-axis
+        self.theta2 = theta2
+        ## Radius to find polar coordinates this line's endpoints
+        self.radius = r
+        ## phi is the phase between the current line and the middle line
+        self.phi = phi
+        ## End points polar coordinates
+        x1 = self.radius * math.cos(self.theta)
+        y1 = self.radius * math.sin(self.theta)
+        x2 = -1 * self.radius * math.cos(self.theta)
+        y2 = -1 * self.radius * math.sin(self.theta)
+        ## defining a new marker 
+        try:
+            self.inner_marker = self.axes.plot([x1/2.5], [y1/2.5], linestyle='',
+                                          marker='s', markersize=10,
+                                          color=self.color, alpha=0.6,
+                                          pickradius=5, label="pick", 
+                                          # Prefer this to other lines
+                                          zorder=zorder, visible=True)[0]
+        except:
+            self.inner_marker = self.axes.plot([x1/2.5],[y1/2.5], linestyle='',
+                                          marker='s', markersize=10,
+                                          color=self.color, alpha=0.6,
+                                          label="pick", visible=True)[0]
+            message  = "\nTHIS PROTOTYPE NEEDS THE LATEST"
+            message += " VERSION OF MATPLOTLIB\n Get the SVN version that"
+            message += " is at least as recent as June 1, 2007"
+            owner = self.base.base.parent
+            wx.PostEvent(owner, 
+                         StatusEvent(status="sectorSlicer: %s" % message))
+        
+        ## Defining the current line
+        self.line = self.axes.plot([x1, x2], [y1, y2],
+                                      linestyle='-', marker='',
+                                      color=self.color, visible=True)[0]
+        ## Flag to differentiate the left line from the right line motion
+        self.left_moving = False
+        ## Flag to define a motion
+        self.has_move = False
+        ## connecting markers and draw the picture
+        self.connect_markers([self.inner_marker, self.line])
+       
+    def set_layer(self, n):
+        """
+        Allow adding plot to the same panel
+         
+        :param n: the number of layer
+        
+        """
+        self.layernum = n
+        self.update()
+        
+    def clear(self):
+        """
+        Clear the slicer and all connected events related to this slicer
+        """
+        self.clear_markers()
+        try:
+            self.line.remove()
+            self.inner_marker.remove()
+        except:
+            # Old version of matplotlib
+            for item in range(len(self.axes.lines)):
+                del self.axes.lines[0]
+    
+    def update(self, phi=None, delta=None, mline=None,
+               side=False, left= False, right=False):
+        """
+        Draw oblique line
+        
+        :param phi: the phase between the middle line and the current line
+        :param delta: phi/2 applied only when the mline was moved
+        
+        """
+        #print "update left or right ", self.has_move
+        self.left_moving = left
+        theta3 = 0
+        if phi != None:
+            self.phi = phi
+        if delta == None:
+            delta = 0
+        if  right:
+            self.phi = -1 * math.fabs(self.phi)
+            #delta=-delta
+        else:
+            self.phi = math.fabs(self.phi)
+        if side:
+            self.theta = mline.theta + self.phi
+                    
+        if mline != None :
+            if delta != 0:
+                self.theta2 = mline + delta
+            else:
+                self.theta2 = mline.theta
+        if delta == 0:
+            theta3 = self.theta + delta
+        else:
+            theta3 = self.theta2 + delta
+        x1 = self.radius * math.cos(theta3)
+        y1 = self.radius * math.sin(theta3)
+        x2 = -1 * self.radius * math.cos(theta3)
+        y2 = -1 * self.radius * math.sin(theta3)
+        self.inner_marker.set(xdata=[x1/2.5], ydata=[y1/2.5])
+        self.line.set(xdata=[x1, x2], ydata=[y1, y2])  
+        
+    def save(self, ev):
+        """
+        Remember the roughness for this layer and the next so that we
+        can restore on Esc.
+        """
+        self.save_theta = self.theta
+        self.base.freeze_axes()
+
+    def moveend(self, ev):
+        """
+        """
+        self.has_move = False
+        self.base.moveend(ev)
+            
+    def restore(self):
+        """
+        Restore the roughness for this layer.
+        """
+        self.theta = self.save_theta
+
+    def move(self, x, y, ev):
+        """
+        Process move to a new position, making sure that the move is allowed.
+        """
+        self.theta = math.atan2(y, x)
+        self.has_move = True
+        #ToDo: Simplify below
+        if not self.left_moving:
+            if  self.theta2 - self.theta <= 0 and self.theta2 > 0:
+                self.restore()
+                return 
+            elif self.theta2 < 0 and self.theta < 0 and \
+                self.theta-self.theta2 >= 0:
+                self.restore()
+                return                             
+            elif  self.theta2 < 0 and self.theta > 0 and \
+                (self.theta2 + 2 * math.pi - self.theta) >= math.pi/2:
+                #print "my theta", self.theta
+                self.restore()
+                return 
+            elif  self.theta2 < 0 and self.theta < 0 and \
+                (self.theta2 - self.theta) >= math.pi/2:
+                #print "my theta", self.theta
+                self.restore()
+                return 
+            elif self.theta2 > 0 and (self.theta2-self.theta >= math.pi/2 or \
+                (self.theta2-self.theta >= math.pi/2)):
+                #print "self theta encore"
+                self.restore()
+                return 
+        else:
+            #print "left move"
+            if  self.theta < 0 and (self.theta + math.pi*2-self.theta2) <= 0:
+                self.restore()
+                return 
+            elif self.theta2 < 0 and (self.theta-self.theta2) <= 0:
+                self.restore()
+                return                             
+            elif  self.theta > 0 and self.theta-self.theta2 <= 0:
+                #print "my theta", self.theta
+                self.restore()
+                return 
+            elif self.theta-self.theta2 >= math.pi/2 or  \
+                ((self.theta + math.pi * 2 - self.theta2) >= math.pi/2 and \
+                 self.theta < 0 and self.theta2 > 0):
+                #print "self theta encore"
+                self.restore()
+                return 
+            
+        self.phi = math.fabs(self.theta2 - self.theta)
+        if self.phi > math.pi:
+            self.phi = 2 * math.pi - math.fabs(self.theta2 - self.theta)
+        self.base.base.update()
+        
+    def set_cursor(self, x, y):
+        """
+        """
+        self.move(x, y, None)
+        self.update()
+    
+    def get_params(self):
+        """
+        """
+        params = {}
+        params["radius"] = self.radius
+        params["theta"] = self.theta
+        return params
+    
+    def set_params(self, params):
+        """
+        """
+        x = params["radius"] 
+        self.set_cursor(x, self._inner_mouse_y)
+        
+
+class LineInteractor(_BaseInteractor):
+    """
+    Select an annulus through a 2D plot
+    """
+    def __init__(self, base, axes, color='black',
+                 zorder=5, r=1.0, theta=math.pi/4):
+        """
+        """
+        _BaseInteractor.__init__(self, base, axes, color=color)
+        
+        self.markers = []
+        self.axes = axes
+        self.save_theta = theta 
+        self.theta= theta
+        self.radius = r
+        self.scale = 10.0
+        # Inner circle
+        x1 = self.radius * math.cos(self.theta)
+        y1 = self.radius * math.sin(self.theta)
+        x2 = -1*self.radius * math.cos(self.theta)
+        y2 = -1*self.radius * math.sin(self.theta)
+        try:
+            # Inner circle marker
+            self.inner_marker = self.axes.plot([x1/2.5], [y1/2.5], linestyle='',
+                                          marker='s', markersize=10,
+                                          color=self.color, alpha=0.6,
+                                          pickradius=5, label="pick", 
+                                          # Prefer this to other lines
+                                          zorder=zorder, 
+                                          visible=True)[0]
+        except:
+            self.inner_marker = self.axes.plot([x1/2.5], [y1/2.5], linestyle='',
+                                          marker='s', markersize=10,
+                                          color=self.color, alpha=0.6,
+                                          label="pick", 
+                                          visible=True)[0]
+            message  = "\nTHIS PROTOTYPE NEEDS THE LATEST VERSION"
+            message += " OF MATPLOTLIB\n Get the SVN version that is at"
+            message += " least as recent as June 1, 2007"
+        self.line = self.axes.plot([x1, x2], [y1, y2],
+                                      linestyle='-', marker='',
+                                      color=self.color, visible=True)[0]
+        self.npts = 20
+        self.has_move = False
+        self.connect_markers([self.inner_marker, self.line])
+        self.update()
+
+    def set_layer(self, n):
+        """
+        """
+        self.layernum = n
+        self.update()
+        
+    def clear(self):
+        """
+        """
+        self.clear_markers()
+        try:
+            self.inner_marker.remove()
+            self.line.remove()
+        except:
+            # Old version of matplotlib
+            for item in range(len(self.axes.lines)):
+                del self.axes.lines[0]
+  
+    def update(self, theta=None):
+        """
+        Draw the new roughness on the graph.
+        """
+       
+        if theta != None:
+            self.theta = theta
+        x1 = self.radius * math.cos(self.theta)
+        y1 = self.radius * math.sin(self.theta)
+        x2 = -1 * self.radius * math.cos(self.theta)
+        y2 = -1 * self.radius * math.sin(self.theta)
+        
+        self.inner_marker.set(xdata=[x1/2.5], ydata=[y1/2.5])
+        self.line.set(xdata=[x1, x2], ydata=[y1, y2])  
+    
+    def save(self, ev):
+        """
+        Remember the roughness for this layer and the next so that we
+        can restore on Esc.
+        """
+        self.save_theta= self.theta
+        self.base.freeze_axes()
+
+    def moveend(self, ev):
+        """
+        """
+        self.has_move = False
+        self.base.moveend(ev)
+            
+    def restore(self):
+        """
+        Restore the roughness for this layer.
+        """
+        self.theta = self.save_theta
+
+    def move(self, x, y, ev):
+        """
+        Process move to a new position, making sure that the move is allowed.
+        """
+        self.theta = math.atan2(y, x)
+        self.has_move = True
+        self.base.base.update()
+        
+    def set_cursor(self, x, y):
+        """
+        """
+        self.move(x, y, None)
+        self.update()
+        
+    def get_params(self):
+        """
+        """
+        params = {}
+        params["radius"] = self.radius
+        params["theta"] = self.theta
+        return params
+    
+    def set_params(self, params):
+        """
+        """
+        x = params["radius"] 
+        self.set_cursor(x, self._inner_mouse_y)
+        
Index: guiframe/local_perspectives/plotting/SlicerParameters.py
===================================================================
--- guiframe/local_perspectives/plotting/SlicerParameters.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
+++ guiframe/local_perspectives/plotting/SlicerParameters.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
@@ -0,0 +1,149 @@
+
+
+import wx
+import wx.lib.newevent
+#from copy import deepcopy
+from sans.guiframe.events import EVT_SLICER_PARS
+from sans.guiframe.utils import format_number
+from sans.guiframe.events import EVT_SLICER
+from sans.guiframe.events import SlicerParameterEvent
+
+
+class SlicerParameterPanel(wx.Dialog):
+    """
+    Panel class to show the slicer parameters 
+    """
+    #TODO: show units
+    #TODO: order parameters properly
+    
+    def __init__(self, parent, *args, **kwargs):
+        wx.Dialog.__init__(self, parent, *args, **kwargs)
+        """
+        Dialog window that allow to edit parameters slicer 
+        by entering new values
+        """
+        self.params = {}
+        self.parent = parent
+        self.type = None
+        self.listeners = []
+        self.parameters = []
+        self.bck = wx.GridBagSizer(5, 5)
+        self.SetSizer(self.bck)
+        label = "Right-click on 2D plot for slicer options"
+        title = wx.StaticText(self, -1, label, style=wx.ALIGN_LEFT)
+        self.bck.Add(title, (0, 0), (1, 2),
+                     flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border=15)
+        # Bindings
+        self.parent.Bind(EVT_SLICER, self.onEVT_SLICER)
+        self.parent.Bind(EVT_SLICER_PARS, self.onParamChange)
+
+    def onEVT_SLICER(self, event):
+        """
+        Process EVT_SLICER events
+        When the slicer changes, update the panel
+        
+        :param event: EVT_SLICER event
+        """
+        event.Skip()
+        if event.obj_class == None:
+            self.set_slicer(None, None)
+        else:
+            self.set_slicer(event.type, event.params)
+        
+    def set_slicer(self, type, params):
+        """
+        Rebuild the panel
+        """
+        self.bck.Clear(True)  
+        self.type = type  
+        if type == None:
+            label = "Right-click on 2D plot for slicer options"
+            title = wx.StaticText(self, -1, label, style=wx.ALIGN_LEFT)
+            self.bck.Add(title, (0, 0), (1, 2), 
+                         flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border=15)
+        else:
+            title = wx.StaticText(self, -1, 
+                                  "Slicer Parameters", style=wx.ALIGN_LEFT)
+            self.bck.Add(title, (0, 0), (1, 2),
+                         flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border=15)
+            ix = 0
+            iy = 0
+            self.parameters = []
+            keys = params.keys()
+            keys.sort()
+            for item in keys:
+                iy += 1
+                ix = 0
+                if not item in ["count", "errors"]:
+                    text = wx.StaticText(self, -1, item, style=wx.ALIGN_LEFT)
+                    self.bck.Add(text, (iy, ix), (1, 1), 
+                                 wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
+                    ctl = wx.TextCtrl(self, -1, size=(80, 20),
+                                      style=wx.TE_PROCESS_ENTER)
+                    hint_msg = "Modify the value of %s to change" % item
+                    hint_msg += " the 2D slicer"
+                    ctl.SetToolTipString(hint_msg)
+                    ix = 1
+                    ctl.SetValue(format_number(str(params[item])))
+                    self.Bind(wx.EVT_TEXT_ENTER, self.onTextEnter)
+                    ctl.Bind(wx.EVT_KILL_FOCUS, self.onTextEnter)
+                    self.parameters.append([item, ctl])
+                    self.bck.Add(ctl, (iy, ix), (1, 1), 
+                                 wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+                    ix = 3
+                    self.bck.Add((20, 20), (iy, ix), (1, 1), 
+                                 wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+                else:
+                    text = wx.StaticText(self, -1, item + " : ", 
+                                         style=wx.ALIGN_LEFT)
+                    self.bck.Add(text, (iy, ix), (1, 1), 
+                                 wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
+                    ctl = wx.StaticText(self, -1, 
+                                    format_number(str(params[item])), 
+                                    style=wx.ALIGN_LEFT)
+                    ix = 1
+                    self.bck.Add(ctl, (iy, ix), (1, 1), 
+                                 wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+            iy += 1
+            ix = 1
+            self.bck.Add((20, 20), (iy, ix), (1, 1), 
+                         wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
+        self.bck.Layout()
+        self.bck.Fit(self)
+        self.parent.GetSizer().Layout()
+
+    def onParamChange(self, evt):
+        """
+        receive an event end reset value text fields
+        inside self.parameters
+        """
+        evt.Skip()
+        if evt.type == "UPDATE":
+            for item in self.parameters:              
+                if item[0] in evt.params:
+                    item[1].SetValue("%-5.3g" % evt.params[item[0]])
+                    item[1].Refresh()
+        
+    def onTextEnter(self, evt): 
+        """
+        Parameters have changed
+        """ 
+        params = {}
+        has_error = False
+        for item in self.parameters:
+            try:
+                params[item[0]] = float(item[1].GetValue())
+                item[1].SetBackgroundColour(
+                        wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
+                item[1].Refresh()
+            except:
+                has_error = True
+                item[1].SetBackgroundColour("pink")
+                item[1].Refresh()
+
+        if has_error == False:
+            # Post parameter event
+            ##parent hier is plotter2D
+            event = SlicerParameterEvent(type=self.type, params=params)
+            wx.PostEvent(self.parent, event)
+        
Index: guiframe/local_perspectives/plotting/__init__.py
===================================================================
--- guiframe/local_perspectives/plotting/__init__.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
+++ guiframe/local_perspectives/plotting/__init__.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
@@ -0,0 +1,2 @@
+PLUGIN_ID = "Plotting plug-in 1.0"
+from plotting import *
Index: guiframe/local_perspectives/plotting/binder.py
===================================================================
--- guiframe/local_perspectives/plotting/binder.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
+++ guiframe/local_perspectives/plotting/binder.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
@@ -0,0 +1,388 @@
+"""
+Extension to MPL to support the binding of artists to key/mouse events.
+"""
+
+class Selection:
+    """
+    Store and compare selections.
+    """
+    # TODO: We need some way to check in prop matches, preferably
+    # TODO: without imposing structure on prop.
+        
+    artist = None
+    prop = {}
+    def __init__(self, artist=None, prop={}):
+        self.artist, self.prop = artist, self.prop
+        
+    def __eq__(self, other):
+        return self.artist is other.artist
+    
+    def __ne__(self, other):
+        return self.artist is not other.artist
+    
+    def __nonzero__(self):
+        return self.artist is not None
+
+class BindArtist:
+
+    # Track keyboard modifiers for events.
+    # TODO: Move keyboard modifier support into the backend.  We cannot
+    # TODO: properly support it from outside the windowing system since there
+    # TODO: is no way to recognized whether shift is held down when the mouse
+    # TODO: first clicks on the the application window.
+    control, shift, alt, meta = False, False, False, False
+
+    # Track doubleclick
+    dclick_threshhold = 0.25
+    _last_button, _last_time = None, 0
+    
+    # Mouse/keyboard events we can bind to
+    events = ['enter', 'leave', 'motion', 'click', 'dclick', 'drag', 'release',
+             'scroll', 'key', 'keyup']
+    # TODO: Need our own event structure
+    def __init__(self, figure):
+        canvas = figure.canvas
+        # Link to keyboard/mouse
+        try:
+            self._connections = [
+                canvas.mpl_connect('motion_notify_event', self._onMotion),
+                canvas.mpl_connect('button_press_event', self._onClick),
+                canvas.mpl_connect('button_release_event', self._onRelease),
+                canvas.mpl_connect('key_press_event', self._onKey),
+                canvas.mpl_connect('key_release_event', self._onKeyRelease),
+                canvas.mpl_connect('scroll_event', self._onScroll)
+            ]
+        except:
+            #print "bypassing scroll_event: wrong matplotlib version"
+            self._connections = [
+                canvas.mpl_connect('motion_notify_event', self._onMotion),
+                canvas.mpl_connect('button_press_event', self._onClick),
+                canvas.mpl_connect('button_release_event', self._onRelease),
+                canvas.mpl_connect('key_press_event', self._onKey),
+                canvas.mpl_connect('key_release_event', self._onKeyRelease),
+            ]
+        # Turn off picker if it hasn't already been done
+        try:
+            canvas.mpl_disconnect(canvas.button_pick_id)
+            canvas.mpl_disconnect(canvas.scroll_pick_id)
+        except: 
+            pass
+        self.canvas = canvas
+        self.figure = figure
+        self.clearall()
+        
+    def clear(self, *artists):
+        """
+        self.clear(h1,h2,...)
+            Remove connections for artists h1, h2, ...
+            
+        Use clearall() to reset all connections.
+        
+        """
+        for h in artists:
+            for a in self.events:
+                if h in self._actions[a]:
+                    del self._actions[a][h]
+            if h in self._artists:
+                self._artists.remove(h)
+        if self._current.artist in artists:
+            self._current = Selection()
+        if self._hasclick.artist in artists:
+            self._hasclick = Selection()
+        if self._haskey.artist in artists:
+            self._haskey = Selection()
+        
+    def clearall(self):
+        """
+        Clear connections to all artists.
+        
+        Use clear(h1,h2,...) to reset specific artists.
+        """
+        # Don't monitor any actions
+        self._actions = {}
+        for action in self.events:
+            self._actions[action] = {}
+        # Need activity state
+        self._artists = []
+        self._current = Selection()
+        self._hasclick = Selection()
+        self._haskey = Selection()
+
+    def disconnect(self):
+        """
+        In case we need to disconnect from the canvas...
+        """
+        try: 
+            for cid in self._connections:
+                self.canvas.mpl_disconnect(cid)
+        except: 
+            pass
+        self._connections = []
+
+    def __del__(self):
+        self.disconnect()
+
+    def __call__(self, trigger, artist, action):
+        """Register a callback for an artist to a particular trigger event.
+        
+        usage:
+            self.connect(eventname,artist,action)
+    
+        where:
+            eventname is a string
+            artist is the particular graph object to respond to the event
+            action(event,**kw) is called when the event is triggered
+
+        The action callback is associated with particular artists.
+        Different artists will have different kwargs.  See documentation
+        on the contains() method for each artist.  One common properties
+        are ind for the index of the item under the cursor, which is
+        returned by Line2D and by collections.
+
+        The following events are supported:
+            enter: mouse cursor moves into the artist or to a new index
+            leave: mouse cursor leaves the artist
+            click: mouse button pressed on the artist
+            drag: mouse button pressed on the artist and cursor moves
+            release: mouse button released for the artist
+            key: key pressed when mouse is on the artist
+            keyrelease: key released for the artist
+    
+        The event received by action has a number of attributes:
+            name is the event name which was triggered
+            artist is the object which triggered the event
+            x,y are the screen coordinates of the mouse
+            xdata,ydata are the graph coordinates of the mouse
+            button is the mouse button being pressed/released
+            key is the key being pressed/released
+            shift,control,alt,meta are flags which are true if the
+                corresponding key is pressed at the time of the event.
+            details is a dictionary of artist specific details, such as the 
+                id(s) of the point that were clicked.
+                
+        When receiving an event, first check the modifier state to be
+        sure it applies.  E.g., the callback for 'press' might be:
+            if event.button == 1 and event.shift: process Shift-click
+
+        :TODO: Only receive events with the correct modifiers (e.g., S-click,
+        :TODO:   or *-click for any modifiers).
+        :TODO: Only receive button events for the correct button (e.g., click1
+        :TODO:   release3, or dclick* for any button)
+        :TODO: Support virtual artist, so that and artist can be flagged as
+        :TODO:   having a tag list and receive the correct events
+        :TODO: Support virtual events for binding to button-3 vs shift button-1
+        :TODO:   without changing callback code
+        :TODO: Attach multiple callbacks to the same event?
+        :TODO: Clean up interaction with toolbar modes
+        :TODO: push/pushclear/pop context so that binding changes 
+            for the duration
+        :TODO:   e.g., to support ? context sensitive help
+        
+        """
+        # Check that the trigger is valid
+        if trigger not in self._actions:
+            raise ValueError, "%s invalid --- valid triggers are %s" \
+                % (trigger, ", ".join(self.events))
+        # Register the trigger callback
+        self._actions[trigger][artist] = action
+        #print "==> added",artist,[artist],"to",trigger,":",
+        #self._actions[trigger].keys()
+        # Maintain a list of all artists
+        if artist not in self._artists: 
+            self._artists.append(artist)
+
+    def trigger(self, actor, action, ev):
+        """
+        Trigger a particular event for the artist.  Fallback to axes,
+        to figure, and to 'all' if the event is not processed.
+        """
+        if action not in self.events:
+            raise ValueError, "Trigger expects " + ", ".join(self.events)
+        # Tag the event with modifiers
+        for mod in ('alt', 'control', 'shift', 'meta'):
+            setattr(ev, mod, getattr(self, mod))
+        setattr(ev, 'artist', None)
+        setattr(ev, 'action', action)
+        setattr(ev, 'prop', {})
+        # Fallback scheme. If the event does not return false, pass to parent.
+        processed = False
+        artist, prop = actor.artist, actor.prop
+        if artist in self._actions[action]:
+            ev.artist, ev.prop = artist, prop
+            processed = self._actions[action][artist](ev)
+        if not processed and ev.inaxes in self._actions[action]:
+            ev.artist, ev.prop = ev.inaxes, {}
+            processed = self._actions[action][ev.inaxes](ev)
+        if not processed and self.figure in self._actions[action]:
+            ev.artist, ev.prop = self.figure, {}
+            processed = self._actions[action][self.figure](ev)
+        if not processed and 'all' in self._actions[action]:
+            ev.artist, ev.prop = None, {}
+            processed = self._actions[action]['all'](ev)
+        return processed
+
+    def _find_current(self, event):
+        """
+        Find the artist who will receive the event.  Only search
+        registered artists.  All others are invisible to the mouse.
+        """
+        # TODO: sort by zorder of axes then by zorder within axes
+        self._artists.sort(cmp=lambda x, y: cmp(y.zorder, x.zorder))
+        # print "search"," ".join([str(h) for h in self._artists])
+        found = Selection()
+        #print "searching in",self._artists
+        for artist in self._artists:
+            # TODO: should contains() return false if invisible?
+            if not artist.get_visible(): 
+                continue
+            # TODO: optimization - exclude artists not inaxes
+            try:
+                inside,prop = artist.contains(event)
+            except:
+                # Probably an old version of matplotlib
+                inside = False
+            if inside:
+                found.artist, found.prop = artist, prop
+                break
+        #print "found",found.artist
+        
+        # TODO: how to check if prop is equal?
+        if found != self._current:
+            self.trigger(self._current, 'leave', event)
+            self.trigger(found, 'enter', event)
+        self._current = found
+        return found
+        
+    def _onMotion(self, event):
+        """
+        Track enter/leave/motion through registered artists; all
+        other artists are invisible.
+        """
+        ## Can't kill double-click on motion since Windows produces
+        ## spurious motion events.
+        #self._last_button = None
+        
+        # Dibs on the motion event for the clicked artist
+        if self._hasclick:
+            # Make sure the x,y data use the coordinate system of the
+            # artist rather than the default axes coordinates.
+            
+            transform = self._hasclick.artist.get_transform()
+            #x,y = event.xdata,event.ydata
+            x, y = event.x, event.y
+            try:
+                x, y = transform.inverted().transform_point((x, y))
+
+            except:
+                x, y = transform.inverse_xy_tup((x, y))
+            event.xdata, event.ydata = x, y
+            self.trigger(self._hasclick, 'drag', event)
+        else:
+            found = self._find_current(event)
+            #print "found",found.artist
+            self.trigger(found, 'motion', event)
+
+    def _onClick(self, event):
+        """
+        Process button click
+        """
+        import time
+        
+        # Check for double-click
+        event_time = time.time()
+        #print event_time,self._last_time,self.dclick_threshhold
+        #print (event_time > self._last_time + self.dclick_threshhold)
+        #print event.button,self._last_button
+        if (event.button != self._last_button) or \
+                (event_time > self._last_time + self.dclick_threshhold):
+            action = 'click'
+        else:
+            action = 'dclick'
+        self._last_button = event.button
+        self._last_time = event_time
+        
+        # If an artist is already dragging, feed any additional button
+        # presses to that artist.
+        # TODO: do we want to force a single button model on the user?
+        # TODO: that is, once a button is pressed, no other buttons
+        # TODO: can come through?  I think this belongs in canvas, not here.
+        if self._hasclick:
+            found = self._hasclick
+        else:
+            found = self._find_current(event)
+        #print "button %d pressed"%event.button
+        # Note: it seems like if "click" returns False then hasclick should
+        # not be set.  The problem is that there are two reasons it can
+        # return false: because there is no click action for this artist
+        # or because the click action returned false.  A related problem
+        # is that click actions will go to the canvas if there is no click
+        # action for the artist, even if the artist has a drag. I'll leave
+        # it to future maintainers to sort out this problem.  For now the
+        # recommendation is that users should define click if they have
+        # drag or release on the artist.
+        self.trigger(found, action, event)
+        self._hasclick = found
+
+    def _onDClick(self, event):
+        """
+        Process button double click
+        """
+        # If an artist is already dragging, feed any additional button
+        # presses to that artist.
+        # TODO: do we want to force a single button model on the user?
+        # TODO: that is, once a button is pressed, no other buttons
+        # TODO: can come through?  I think this belongs in canvas, not here.
+        if self._hasclick:
+            found = self._hasclick
+        else:
+            found = self._find_current(event)
+        self.trigger(found, 'dclick', event)
+        self._hasclick = found
+
+    def _onRelease(self, event):
+        """
+        Process release release
+        """
+        self.trigger(self._hasclick, 'release', event)
+        self._hasclick = Selection()
+            
+    def _onKey(self, event):
+        """
+        Process key click
+        """
+        # TODO: Do we really want keyboard focus separate from mouse focus?
+        # TODO: Do we need an explicit focus command for keyboard?
+        # TODO: Can we tab between items?
+        # TODO: How do unhandled events get propogated to axes, figure and
+        # TODO: finally to application?  Do we need to implement a full tags 
+        # TODO: architecture a la Tk?
+        # TODO: Do modifiers cause a grab?  Does the artist see the modifiers?
+        if event.key in ('alt', 'meta', 'control', 'shift'):
+            setattr(self, event.key, True)
+            return
+
+        if self._haskey:
+            found = self._haskey
+        else:
+            found = self._find_current(event)
+        self.trigger(found, 'key', event)
+        self._haskey = found
+    
+    def _onKeyRelease(self, event):
+        """
+        Process key release
+        """
+        if event.key in ('alt', 'meta', 'control', 'shift'):
+            setattr(self, event.key, False)
+            return
+        if self._haskey:
+            self.trigger(self._haskey, 'keyup', event)
+        self._haskey = Selection()
+
+    def _onScroll(self, event):
+        """
+        Process scroll event
+        """
+        found = self._find_current(event)
+        self.trigger(found, 'scroll', event)
+
Index: guiframe/local_perspectives/plotting/boxMask.py
===================================================================
--- guiframe/local_perspectives/plotting/boxMask.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
+++ guiframe/local_perspectives/plotting/boxMask.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
@@ -0,0 +1,252 @@
+
+
+
+import math
+#import wx
+#from copy import deepcopy
+from BaseInteractor import _BaseInteractor
+from boxSum import PointInteractor
+from boxSum import VerticalDoubleLine
+from boxSum import HorizontalDoubleLine
+#from sans.guiframe.events import SlicerParamUpdateEvent
+
+
+class BoxMask(_BaseInteractor):
+    """
+    BoxMask Class: determine 2 rectangular area to find the pixel of
+    a Data inside of box. 
+    
+    Uses PointerInteractor , VerticalDoubleLine,HorizontalDoubleLine.
+    
+    :param zorder:  Artists with lower zorder values are drawn first.
+    :param x_min: the minimum value of the x coordinate
+    :param x_max: the maximum value of the x coordinate
+    :param y_min: the minimum value of the y coordinate
+    :param y_max: the maximum value of the y coordinate
+
+    """
+    def __init__(self, base, axes, color='black', zorder=3, side=None, 
+                   x_min=0.008, x_max=0.008, y_min=0.0025, y_max=0.0025):
+        """
+        """
+        _BaseInteractor.__init__(self, base, axes, color=color)
+        ## class initialization
+        ## list of Boxmask markers
+        self.markers = []
+        self.axes = axes
+        self.is_inside = side
+        ## connect the artist for the motion
+        self.connect = self.base.connect
+        ## when qmax is reached the selected line is reset 
+        #the its previous value
+        self.qmax = min(self.base.data.xmax, self.base.data.xmin)
+        ## Define the box limits
+        self.xmin = -1 * 0.5 * min(math.fabs(self.base.data.xmax),
+                                 math.fabs(self.base.data.xmin))
+        self.ymin = -1 * 0.5 * min(math.fabs(self.base.data.xmax),
+                                   math.fabs(self.base.data.xmin))
+        self.xmax = 0.5 * min(math.fabs(self.base.data.xmax),
+                              math.fabs(self.base.data.xmin))
+        self.ymax =  0.5 * min(math.fabs(self.base.data.xmax),
+                               math.fabs(self.base.data.xmin))
+        ## center of the box
+        self.center_x = 0.0002
+        self.center_y = 0.0003
+        ## Number of points on the plot
+        self.nbins = 20
+        ## Define initial result the summation 
+        self.count = 0
+        self.error = 0
+        self.data = self.base.data
+        ## Flag to determine if the current figure has moved
+        ## set to False == no motion , set to True== motion
+        self.has_move = False
+        ## Create Box edges 
+        self.horizontal_lines = HorizontalDoubleLine(self,
+                                                     self.base.subplot,
+                                                     color='blue',
+                                                      zorder=zorder,
+                                                      y=self.ymax,
+                                                      x=self.xmax,
+                                                      center_x=self.center_x,
+                                                      center_y=self.center_y)
+        self.horizontal_lines.qmax = self.qmax
+        
+        self.vertical_lines = VerticalDoubleLine(self,
+                                                self.base.subplot,
+                                                color='grey',
+                                                zorder=zorder,
+                                                y=self.ymax,
+                                                x=self.xmax,
+                                                center_x=self.center_x,
+                                                center_y=self.center_y)
+        self.vertical_lines.qmax = self.qmax
+        
+        self.center = PointInteractor(self,
+                                      self.base.subplot,color='grey',
+                                    zorder=zorder,
+                                    center_x=self.center_x,
+                                    center_y=self.center_y)
+        ## Save the name of the slicer panel associate with this slicer 
+        self.panel_name = ""    
+        ## Update and post slicer parameters  
+        self.update()
+        self._post_data()
+    
+    def clear(self):
+        """
+        Clear the slicer and all connected events related to this slicer
+        """
+        self.clear_markers()
+        self.horizontal_lines.clear()
+        self.vertical_lines.clear()
+        self.center.clear()
+        self.base.connect.clearall()
+        #self.base.Unbind(EVT_SLICER_PARS)
+    
+    def update(self):
+        """
+        Respond to changes in the model by recalculating the profiles and
+        resetting the widgets.
+        """
+        # check if the center point has moved and update the figure accordingly 
+        if self.center.has_move:
+            self.center.update()
+            self.horizontal_lines.update(center=self.center)
+            self.vertical_lines.update(center=self.center)
+        ## check if the horizontal lines have moved and update
+        # the figure accordingly    
+        if self.horizontal_lines.has_move:
+            self.horizontal_lines.update()
+            self.vertical_lines.update(y1=self.horizontal_lines.y1,
+                                       y2=self.horizontal_lines.y2,
+                                       height=self.horizontal_lines.half_height)
+        ## check if the vertical lines have moved and update
+        # the figure accordingly    
+        if self.vertical_lines.has_move:
+            self.vertical_lines.update()
+            self.horizontal_lines.update(x1=self.vertical_lines.x1,
+                                         x2=self.vertical_lines.x2,
+                                         width=self.vertical_lines.half_width)
+        #if self.is_inside != None:
+        out = self._post_data()
+        return out
+        
+    def save(self, ev):
+        """
+        Remember the roughness for this layer and the next so that we
+        can restore on Esc.
+        """
+        self.base.freeze_axes()
+        self.horizontal_lines.save(ev)
+        self.vertical_lines.save(ev)
+        self.center.save(ev)
+        
+    def _post_data(self):
+        """
+        Get the limits of the boxsum and compute the sum of the pixel
+        contained in that region and the error on that sum
+        """
+        from DataLoader.manipulations import Boxcut
+        ## Data 2D for which the pixel will be summed
+        data = self.base.data
+        mask = data.mask
+        ## the region of the summation
+        x_min = self.horizontal_lines.x2 
+        x_max = self.horizontal_lines.x1 
+        y_min = self.vertical_lines.y2
+        y_max = self.vertical_lines.y1
+        mask = Boxcut(x_min=x_min, x_max=x_max, y_min=y_min, y_max=y_max)
+
+        if self.is_inside:
+            out = (mask(data) == False)
+        else:
+            out = (mask(data))
+        #self.base.data.mask=out
+        return out  
+              
+    def moveend(self, ev):
+        """
+        After a dragging motion this function is called to compute
+        the error and the sum of pixel of a given data 2D 
+        """
+        self.base.thaw_axes()
+        ## post
+        self._post_data()
+            
+    def restore(self):
+        """
+        Restore the roughness for this layer.
+        """
+        self.horizontal_lines.restore()
+        self.vertical_lines.restore()
+        self.center.restore()
+        
+    def move(self, x, y, ev):
+        """
+        Process move to a new position, making sure that the move is allowed.
+        """
+        pass
+    
+    def set_cursor(self, x, y):
+        pass
+        
+    def get_params(self):
+        """
+        Store a copy of values of parameters of the slicer into a dictionary.
+        
+        :return params: the dictionary created
+        
+        """
+        params = {}
+        params["Width"] = math.fabs(self.vertical_lines.half_width) * 2
+        params["Height"] = math.fabs(self.horizontal_lines.half_height) * 2 
+        params["center_x"] = self.center.x
+        params["center_y"] = self.center.y
+        return params
+    
+    def get_mask(self):
+        """
+        return mask as a result of boxcut
+        """
+        mask = self.mask
+        return mask
+    
+    def set_params(self, params):
+        """
+        Receive a dictionary and reset the slicer with values contained 
+        in the values of the dictionary.
+        
+        :param params: a dictionary containing name of slicer parameters and 
+           values the user assigned to the slicer.
+        """
+        x_max = math.fabs(params["Width"] )/2
+        y_max = math.fabs(params["Height"])/2
+        
+        self.center_x = params["center_x"] 
+        self.center_y = params["center_y"]
+        #update the slicer given values of params
+        self.center.update(center_x=self.center_x, center_y=self.center_y)
+        self.horizontal_lines.update(center=self.center,
+                                     width=x_max,
+                                     height=y_max)
+        self.vertical_lines.update(center=self.center,
+                                    width=x_max,
+                                    height=y_max)
+        #compute the new error and sum given values of params
+        self._post_data()
+               
+    def freeze_axes(self):
+        self.base.freeze_axes()
+        
+    def thaw_axes(self):
+        self.base.thaw_axes()
+
+    def draw(self):
+        self.base.update()
+
+class inner_BoxMask(BoxMask):   
+    def __call__(self):     
+        self.base.data.mask = (self._post_data()==False)
+      
+   
Index: guiframe/local_perspectives/plotting/boxSlicer.py
===================================================================
--- guiframe/local_perspectives/plotting/boxSlicer.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
+++ guiframe/local_perspectives/plotting/boxSlicer.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
@@ -0,0 +1,560 @@
+
+
+import wx
+#import copy
+#from copy import deepcopy
+import math
+import numpy
+from sans.guiframe.events import NewPlotEvent
+from sans.guiframe.events import StatusEvent
+from sans.guiframe.events import SlicerParameterEvent
+from sans.guiframe.events import EVT_SLICER_PARS
+from BaseInteractor import _BaseInteractor
+from sans.guiframe.dataFitting import Data1D
+#import SlicerParameters
+
+
+class BoxInteractor(_BaseInteractor):
+    """
+    BoxInteractor define a rectangle that return data1D average of Data2D
+    in a rectangle area defined by -x, x ,y, -y
+    """
+    def __init__(self, base, axes, color='black', zorder=3):
+        _BaseInteractor.__init__(self, base, axes, color=color)
+        ## Class initialization
+        self.markers = []
+        self.axes = axes
+        ##connecting artist
+        self.connect = self.base.connect
+        ## determine x y  values
+        self.x = 0.5 * min(math.fabs(self.base.data2D.xmax),
+                           math.fabs(self.base.data2D.xmin))
+        self.y = 0.5 * min(math.fabs(self.base.data2D.xmax),
+                           math.fabs(self.base.data2D.xmin))        
+        ## when reach qmax reset the graph
+        self.qmax = max(self.base.data2D.xmax, self.base.data2D.xmin,
+                        self.base.data2D.ymax, self.base.data2D.ymin)   
+        ## Number of points on the plot
+        self.nbins = 30
+        ## If True, I(|Q|) will be return, otherwise,
+        #negative q-values are allowed
+        self.fold = True        
+        ## reference of the current  Slab averaging
+        self.averager = None
+        ## Create vertical and horizaontal lines for the rectangle
+        self.vertical_lines = VerticalLines(self,
+                                            self.base.subplot, 
+                                            color='blue', 
+                                            zorder=zorder,
+                                            y=self.y ,
+                                            x=self.x)
+        self.vertical_lines.qmax = self.qmax
+       
+        self.horizontal_lines = HorizontalLines(self,
+                                               self.base.subplot,
+                                               color='green', 
+                                               zorder=zorder,
+                                               x=self.x,
+                                               y=self.y)
+        self.horizontal_lines.qmax = self.qmax
+        ## draw the rectangle and plost the data 1D resulting
+        ## of averaging data2D
+        self.update()
+        self._post_data()
+        ## Bind to slice parameter events
+        self.base.Bind(EVT_SLICER_PARS, self._onEVT_SLICER_PARS)
+
+    def _onEVT_SLICER_PARS(self, event):
+        """
+        receive an event containing parameters values to reset the slicer
+        
+        :param event: event of type SlicerParameterEvent with params as 
+            attribute
+        """
+        wx.PostEvent(self.base.parent,
+                     StatusEvent(status="BoxSlicer._onEVT_SLICER_PARS"))
+        event.Skip()
+        if event.type == self.__class__.__name__:
+            self.set_params(event.params)
+            self.base.update()
+            
+    def update_and_post(self):
+        """
+        Update the slicer and plot the resulting data
+        """
+        self.update()
+        self._post_data()
+        
+    def set_layer(self, n):
+        """
+        Allow adding plot to the same panel
+        
+        :param n: the number of layer
+        
+        """
+        self.layernum = n
+        self.update()
+        
+    def clear(self):
+        """
+        Clear the slicer and all connected events related to this slicer
+        """
+        self.averager = None
+        self.clear_markers()
+        self.horizontal_lines.clear()
+        self.vertical_lines.clear()
+        self.base.connect.clearall()
+        self.base.Unbind(EVT_SLICER_PARS)
+        
+    def update(self):
+        """
+        Respond to changes in the model by recalculating the profiles and
+        resetting the widgets.
+        """
+        ##Update the slicer if an horizontal line is dragged   
+        if self.horizontal_lines.has_move:
+            self.horizontal_lines.update()
+            self.vertical_lines.update(y=self.horizontal_lines.y)
+        ##Update the slicer if a vertical line is dragged    
+        if self.vertical_lines.has_move:
+            self.vertical_lines.update()
+            self.horizontal_lines.update(x=self.vertical_lines.x)
+                 
+    def save(self, ev):
+        """
+        Remember the roughness for this layer and the next so that we
+        can restore on Esc.
+        """
+        self.base.freeze_axes()
+        self.vertical_lines.save(ev)
+        self.horizontal_lines.save(ev)
+    
+    def _post_data(self):
+        pass
+        
+    def post_data(self, new_slab=None, nbins=None, direction=None):
+        """
+        post data averaging in Qx or Qy given new_slab type
+        
+        :param new_slab: slicer that determine with direction to average
+        :param nbins: the number of points plotted when averaging
+        :param direction: the direction of averaging
+        
+        """
+        x_min = -1 * math.fabs(self.vertical_lines.x)
+        x_max = math.fabs(self.vertical_lines.x)
+        y_min = -1 * math.fabs(self.horizontal_lines.y)
+        y_max = math.fabs(self.horizontal_lines.y)
+        
+        if nbins != None:
+            self.nbins = nbins
+        if self.averager == None:
+            if new_slab == None:
+                msg = "post data:cannot average , averager is empty"
+                raise ValueError, msg
+            self.averager = new_slab
+        if direction == "X":
+            if self.fold:
+                x_low = 0
+            else:
+                x_low = math.fabs(x_min)
+            bin_width = (x_max + x_low)/self.nbins
+        else:
+            if self.fold:
+                y_low = 0
+            else:
+                y_low = math.fabs(y_min)
+            bin_width = (y_max + y_low)/self.nbins
+        ## Average data2D given Qx or Qy
+        box = self.averager(x_min=x_min, x_max=x_max, y_min=y_min, y_max=y_max,
+                         bin_width=bin_width)
+        box.fold = self.fold
+        boxavg = box(self.base.data2D)
+        #3 Create Data1D to plot
+        if hasattr(boxavg, "dxl"):
+            dxl = boxavg.dxl
+        else:
+            dxl = None
+        if hasattr(boxavg, "dxw"):
+            dxw = boxavg.dxw
+        else:
+            dxw = None
+        new_plot = Data1D(x=boxavg.x, y=boxavg.y, dy=boxavg.dy)
+        new_plot.dxl  = dxl
+        new_plot.dxw  = dxw
+        new_plot.name = str(self.averager.__name__) + \
+                        "(" + self.base.data2D.name + ")"
+        new_plot.source = self.base.data2D.source
+        new_plot.interactive = True
+        new_plot.detector = self.base.data2D.detector
+        ## If the data file does not tell us what the axes are, just assume...
+        new_plot.xaxis("\\rm{Q}", "A^{-1}")
+        new_plot.yaxis("\\rm{Intensity} ", "cm^{-1}")
+
+        data = self.base.data2D
+        if hasattr(data, "scale") and data.scale == 'linear' and \
+                self.base.data2D.name.count("Residuals") > 0:
+            new_plot.ytransform = 'y'
+            new_plot.yaxis("\\rm{Residuals} ", "/")
+            
+        new_plot.group_id = str(self.averager.__name__) + self.base.data2D.name
+        #new_plot.id = str(self.averager.__name__)
+        #new_plot.is_data= True
+        wx.PostEvent(self.base.parent, NewPlotEvent(plot=new_plot,
+                                title=str(self.averager.__name__)))
+          
+    def moveend(self, ev):
+        """
+        Called after a dragging event.
+        Post the slicer new parameters and creates a new Data1D 
+        corresponding to the new average
+        """
+        self.base.thaw_axes()
+        # Post paramters
+        event = SlicerParameterEvent()
+        event.type = self.__class__.__name__
+        event.params = self.get_params()
+        wx.PostEvent(self.base.parent, event)
+        # create the new data1D
+        self._post_data()
+            
+    def restore(self):
+        """
+        Restore the roughness for this layer.
+        """
+        self.horizontal_lines.restore()
+        self.vertical_lines.restore()
+       
+    def move(self, x, y, ev):
+        """
+        Process move to a new position, making sure that the move is allowed.
+        """
+        pass
+        
+    def set_cursor(self, x, y):
+        pass
+        
+    def get_params(self):
+        """
+        Store a copy of values of parameters of the slicer into a dictionary.
+        
+        :return params: the dictionary created
+        
+        """
+        params = {}
+        params["x_max"] = math.fabs(self.vertical_lines.x)
+        params["y_max"] = math.fabs(self.horizontal_lines.y)
+        params["nbins"] = self.nbins
+        return params
+    
+    def set_params(self, params):
+        """
+        Receive a dictionary and reset the slicer with values contained 
+        in the values of the dictionary.
+        
+        :param params: a dictionary containing name of slicer parameters and 
+            values the user assigned to the slicer.
+        """
+        self.x = float(math.fabs(params["x_max"]))
+        self.y = float(math.fabs(params["y_max"] ))
+        self.nbins = params["nbins"]
+        
+        self.horizontal_lines.update(x=self.x, y=self.y)
+        self.vertical_lines.update(x=self.x, y=self.y)
+        self.post_data(nbins=None)
+        
+    def freeze_axes(self):
+        """
+        """
+        self.base.freeze_axes()
+        
+    def thaw_axes(self):
+        """
+        """
+        self.base.thaw_axes()
+
+    def draw(self):
+        """
+        """
+        self.base.draw()
+
+
+class HorizontalLines(_BaseInteractor):
+    """
+    Draw 2 Horizontal lines centered on (0,0) that can move 
+    on the x- direction and in opposite direction
+    """
+    def __init__(self, base, axes, color='black', zorder=5, x=0.5, y=0.5):
+        """
+        """
+        _BaseInteractor.__init__(self, base, axes, color=color)
+        ##Class initialization
+        self.markers = []
+        self.axes = axes
+        ## Saving the end points of two lines
+        self.x = x
+        self.save_x = x
+        
+        self.y = y
+        self.save_y = y
+        ## Creating a marker
+        try:
+            # Inner circle marker
+            self.inner_marker = self.axes.plot([0], [self.y], linestyle='',
+                                          marker='s', markersize=10,
+                                          color=self.color, alpha=0.6,
+                                          pickradius=5, label="pick", 
+                                          # Prefer this to other lines
+                                          zorder=zorder, 
+                                          visible=True)[0]
+        except:
+            self.inner_marker = self.axes.plot([0], [self.y], linestyle='',
+                                          marker='s', markersize=10,
+                                          color=self.color, alpha=0.6,
+                                          label="pick", visible=True)[0]
+            message  = "\nTHIS PROTOTYPE NEEDS THE LATEST VERSION "
+            message += "OF MATPLOTLIB\n Get the SVN version that"
+            message += " is at least as recent as June 1, 2007"
+            owner = self.base.base.parent
+            wx.PostEvent(owner,
+                         StatusEvent(status="AnnulusSlicer: %s" % message))
+        ## Define 2 horizontal lines
+        self.top_line = self.axes.plot([self.x, -self.x], [self.y, self.y],
+                                      linestyle='-', marker='',
+                                      color=self.color, visible=True)[0]
+        self.bottom_line = self.axes.plot([self.x, -self.x], [-self.y, -self.y],
+                                      linestyle='-', marker='',
+                                      color=self.color, visible=True)[0]
+        ## Flag to check the motion of the lines
+        self.has_move = False
+        ## Connecting markers to mouse events and draw
+        self.connect_markers([self.top_line, self.inner_marker])
+        self.update()
+
+    def set_layer(self, n):
+        """
+        Allow adding plot to the same panel
+        
+        :param n: the number of layer
+        
+        """
+        self.layernum = n
+        self.update()
+        
+    def clear(self):
+        """
+        Clear this slicer  and its markers
+        """
+        self.clear_markers()
+        try:
+            self.inner_marker.remove()
+            self.top_line.remove() 
+            self.bottom_line.remove()
+        except:
+            # Old version of matplotlib
+            for item in range(len(self.axes.lines)):
+                del self.axes.lines[0]
+   
+    def update(self, x=None, y=None):
+        """
+        Draw the new roughness on the graph.
+        
+        :param x: x-coordinates to reset current class x
+        :param y: y-coordinates to reset current class y
+        
+        """
+        ## Reset x, y- coordinates if send as parameters
+        if x != None:
+            self.x = numpy.sign(self.x) * math.fabs(x)
+        if y != None:
+            self.y = numpy.sign(self.y) * math.fabs(y)
+        ## Draw lines and markers
+        self.inner_marker.set(xdata=[0], ydata=[self.y])
+        self.top_line.set(xdata=[self.x, -self.x], ydata=[self.y, self.y])
+        self.bottom_line.set(xdata=[self.x,-self.x], ydata=[-self.y, -self.y])
+        
+    def save(self, ev):
+        """
+        Remember the roughness for this layer and the next so that we
+        can restore on Esc.
+        """
+        self.save_x = self.x
+        self.save_y = self.y
+        self.base.freeze_axes()
+
+    def moveend(self, ev):
+        """
+        Called after a dragging this edge and set self.has_move to False
+        to specify the end of dragging motion
+        """
+        self.has_move = False
+        self.base.moveend(ev)
+             
+    def restore(self):
+        """
+        Restore the roughness for this layer.
+        """
+        self.x = self.save_x
+        self.y = self.save_y
+        
+    def move(self, x, y, ev):
+        """
+        Process move to a new position, making sure that the move is allowed.
+        """
+        self.y = y
+        self.has_move = True
+        self.base.base.update()
+        
+  
+class VerticalLines(_BaseInteractor):
+    """
+    Select an annulus through a 2D plot
+    """
+    def __init__(self, base, axes, color='black', zorder=5, x=0.5, y=0.5):
+        """
+        """
+        _BaseInteractor.__init__(self, base, axes, color=color)
+        self.markers = []
+        self.axes = axes
+        self.x = math.fabs(x)
+        self.save_x = self.x
+        self.y = math.fabs(y)
+        self.save_y = y
+        try:
+            # Inner circle marker
+            self.inner_marker = self.axes.plot([self.x], [0], linestyle='',
+                                          marker='s', markersize=10,
+                                          color=self.color, alpha=0.6,
+                                          pickradius=5, label="pick", 
+                                          # Prefer this to other lines
+                                          zorder=zorder, visible=True)[0]
+        except:
+            self.inner_marker = self.axes.plot([self.x], [0], linestyle='',
+                                          marker='s', markersize=10,
+                                          color=self.color, alpha=0.6,
+                                          label="pick", visible=True)[0]
+            message  = "\nTHIS PROTOTYPE NEEDS THE LATEST VERSION"
+            message += " OF MATPLOTLIB\n Get the SVN version that is"
+            message += " at least as recent as June 1, 2007"
+            
+        self.right_line = self.axes.plot([self.x, self.x],
+                                         [self.y , -self.y],
+                                      linestyle='-', marker='',
+                                      color=self.color, visible=True)[0]
+        self.left_line = self.axes.plot([-self.x, -self.x],
+                                        [self.y, -self.y],
+                                      linestyle='-', marker='',
+                                      color=self.color, visible=True)[0]
+        self.has_move = False
+        self.connect_markers([self.right_line, self.inner_marker])
+        self.update()
+
+    def set_layer(self, n):
+        """
+        Allow adding plot to the same panel
+        
+        :param n: the number of layer
+        
+        """
+        self.layernum = n
+        self.update()
+        
+    def clear(self):
+        """
+        Clear this slicer  and its markers
+        """
+        self.clear_markers()
+        try:
+            self.inner_marker.remove()
+            self.left_line.remove()
+            self.right_line.remove()
+        except:
+            # Old version of matplotlib
+            for item in range(len(self.axes.lines)):
+                del self.axes.lines[0]
+
+    def update(self, x=None, y=None):
+        """
+        Draw the new roughness on the graph.
+        
+        :param x: x-coordinates to reset current class x
+        :param y: y-coordinates to reset current class y
+        
+        """
+        ## reset x, y -coordinates if given as parameters
+        if x != None:
+            self.x = numpy.sign(self.x) * math.fabs(x)
+        if y != None:
+            self.y = numpy.sign(self.y) * math.fabs(y)
+        ## draw lines and markers  
+        self.inner_marker.set(xdata=[self.x], ydata=[0]) 
+        self.left_line.set(xdata=[-self.x, -self.x], ydata=[self.y, -self.y]) 
+        self.right_line.set(xdata=[self.x, self.x], ydata=[self.y, -self.y]) 
+    
+    def save(self, ev):
+        """
+        Remember the roughness for this layer and the next so that we
+        can restore on Esc.
+        """
+        self.save_x = self.x
+        self.save_y = self.y
+        self.base.freeze_axes()
+        
+    def moveend(self, ev):
+        """
+        Called after a dragging this edge and set self.has_move to False
+        to specify the end of dragging motion
+        """
+        self.has_move = False
+        self.base.moveend(ev)
+               
+    def restore(self):
+        """
+        Restore the roughness for this layer.
+        """
+        self.x = self.save_x
+        self.y = self.save_y
+      
+    def move(self, x, y, ev):
+        """
+        Process move to a new position, making sure that the move is allowed.
+        """
+        self.has_move = True
+        self.x = x
+        self.base.base.update()
+        
+   
+class BoxInteractorX(BoxInteractor):
+    """
+    Average in Qx direction
+    """
+    def __init__(self, base, axes, color='black', zorder=3):
+        BoxInteractor.__init__(self, base, axes, color=color)
+        self.base = base
+        self._post_data()
+        
+    def _post_data(self):
+        """
+        Post data creating by averaging in Qx direction
+        """
+        from DataLoader.manipulations import SlabX
+        self.post_data(SlabX, direction="X")   
+        
+
+class BoxInteractorY(BoxInteractor):
+    """
+    Average in Qy direction
+    """
+    def __init__(self, base, axes, color='black', zorder=3):
+        BoxInteractor.__init__(self, base, axes, color=color)
+        self.base = base
+        self._post_data()
+        
+    def _post_data(self):
+        """
+        Post data creating by averaging in Qy direction
+        """
+        from DataLoader.manipulations import SlabY
+        self.post_data(SlabY, direction="Y")   
+        
+        
Index: guiframe/local_perspectives/plotting/boxSum.py
===================================================================
--- guiframe/local_perspectives/plotting/boxSum.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
+++ guiframe/local_perspectives/plotting/boxSum.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
@@ -0,0 +1,833 @@
+
+
+
+import math
+import wx
+from copy import deepcopy
+from BaseInteractor import _BaseInteractor
+from sans.guiframe.events import SlicerParamUpdateEvent
+from sans.guiframe.events import EVT_SLICER_PARS
+from sans.guiframe.events import StatusEvent
+
+
+class BoxSum(_BaseInteractor):
+    """
+        Boxsum Class: determine 2 rectangular area to compute 
+        the sum of pixel of a Data. 
+        Uses PointerInteractor , VerticalDoubleLine,HorizontalDoubleLine.
+        @param zorder:  Artists with lower zorder values are drawn first.
+        @param x_min: the minimum value of the x coordinate
+        @param x_max: the maximum value of the x coordinate
+        @param y_min: the minimum value of the y coordinate
+        @param y_max: the maximum value of the y coordinate
+
+    """
+    def __init__(self, base, axes, color='black', zorder=3, x_min=0.008,
+                  x_max=0.008, y_min=0.0025, y_max=0.0025):
+        """
+        """
+        _BaseInteractor.__init__(self, base, axes, color=color)
+        ## class initialization
+        ## list of Boxsmun markers
+        self.markers = []
+        self.axes = axes
+        ## connect the artist for the motion
+        self.connect = self.base.connect
+        ## when qmax is reached the selected line is reset the its previous value
+        self.qmax = min(self.base.data2D.xmax, self.base.data2D.xmin)
+        ## Define the boxsum limits
+        self.xmin = -1 * 0.5 * min(math.fabs(self.base.data2D.xmax),
+                                  math.fabs(self.base.data2D.xmin))
+        self.ymin = -1 * 0.5 * min(math.fabs(self.base.data2D.xmax),
+                                  math.fabs(self.base.data2D.xmin))
+        self.xmax = 0.5 * min(math.fabs(self.base.data2D.xmax),
+                              math.fabs(self.base.data2D.xmin))
+        self.ymax = 0.5 * min(math.fabs(self.base.data2D.xmax),
+                              math.fabs(self.base.data2D.xmin))
+        ## center of the boxSum
+        self.center_x = 0.0002
+        self.center_y = 0.0003
+        ## Number of points on the plot
+        self.nbins = 20
+        ## Define initial result the summation 
+        self.count = 0
+        self.error = 0
+        ## Flag to determine if the current figure has moved
+        ## set to False == no motion , set to True== motion
+        self.has_move = False
+        ## Create Boxsum edges 
+        self.horizontal_lines= HorizontalDoubleLine(self,
+                                                    self.base.subplot,
+                                                    color='blue',
+                                                      zorder=zorder,
+                                                      y=self.ymax,
+                                                      x=self.xmax,
+                                                      center_x=self.center_x,
+                                                      center_y=self.center_y)
+        self.horizontal_lines.qmax = self.qmax
+        
+        self.vertical_lines= VerticalDoubleLine(self,
+                                                self.base.subplot,
+                                                color='black',
+                                                zorder=zorder,
+                                                y=self.ymax,
+                                                x=self.xmax,
+                                                center_x=self.center_x,
+                                                center_y=self.center_y)
+        self.vertical_lines.qmax = self.qmax
+        
+        self.center= PointInteractor(self,
+                                     self.base.subplot,color='grey',
+                                    zorder=zorder,
+                                    center_x= self.center_x,
+                                    center_y= self.center_y)
+        ## Save the name of the slicer panel associate with this slicer 
+        self.panel_name = ""    
+        ## Update and post slicer parameters  
+        self.update()
+        self._post_data()
+        ## Bind to slice parameter events
+        self.base.Bind(EVT_SLICER_PARS, self._onEVT_SLICER_PARS)
+        
+    def set_panel_name(self, name):
+        """
+            Store the name of the panel associated to this slicer
+            @param name: the name of this panel
+        """
+        self.panel_name = name
+        
+    def _onEVT_SLICER_PARS(self, event):
+        """
+            receive an event containing parameters values to reset the slicer
+            @param event: event of type SlicerParameterEvent with params as 
+            attribute
+        """
+        ## Post e message to declare what kind of event has being received
+        wx.PostEvent(self.base.parent,
+                     StatusEvent(status="Boxsum._onEVT_SLICER_PARS"))
+        event.Skip()
+        ## reset the slicer with the values contains the event.params dictionary
+        if event.type == self.__class__.__name__:
+            self.set_params(event.params)
+            self.base.update()
+
+    def set_layer(self, n):
+        """
+        Allow adding plot to the same panel
+        :param n: the number of layer
+        """
+        self.layernum = n
+        self.update()
+        
+    def clear(self):
+        """
+        Clear the slicer and all connected events related to this slicer
+        """
+        self.clear_markers()
+        self.horizontal_lines.clear()
+        self.vertical_lines.clear()
+        self.center.clear()
+        self.base.connect.clearall()
+        self.base.Unbind(EVT_SLICER_PARS)
+        
+    def update(self):
+        """
+        Respond to changes in the model by recalculating the profiles and
+        resetting the widgets.
+        """
+        ## check if the center point has moved and update the figure accordingly 
+        if self.center.has_move:
+            self.center.update()
+            self.horizontal_lines.update( center= self.center)
+            self.vertical_lines.update( center= self.center)
+        ## check if the horizontal lines have moved and 
+        #update the figure accordingly    
+        if self.horizontal_lines.has_move:
+            self.horizontal_lines.update()
+            self.vertical_lines.update(y1=self.horizontal_lines.y1,
+                                       y2=self.horizontal_lines.y2,
+                                       height=self.horizontal_lines.half_height)
+        ## check if the vertical lines have moved and 
+        #update the figure accordingly    
+        if self.vertical_lines.has_move:
+            self.vertical_lines.update()
+            self.horizontal_lines.update(x1=self.vertical_lines.x1,
+                                         x2=self.vertical_lines.x2,
+                                         width=self.vertical_lines.half_width)
+            
+    def save(self, ev):
+        """
+        Remember the roughness for this layer and the next so that we
+        can restore on Esc.
+        """
+        self.base.freeze_axes()
+        self.horizontal_lines.save(ev)
+        self.vertical_lines.save(ev)
+        self.center.save(ev)
+        
+    def _post_data(self):
+        """
+        Get the limits of the boxsum and compute the sum of the pixel
+        contained in that region and the error on that sum
+        """
+        ## Data 2D for which the pixel will be summed
+        data = self.base.data2D
+        ## the region of the summation
+        x_min = self.horizontal_lines.x2 
+        x_max = self.horizontal_lines.x1 
+        y_min = self.vertical_lines.y2
+        y_max = self.vertical_lines.y1
+        ##computation of the sum and its error
+        from DataLoader.manipulations import Boxavg
+        box =  Boxavg(x_min=x_min, x_max=x_max, y_min=y_min, y_max=y_max)
+        self.count, self.error = box(self.base.data2D)
+        
+    def moveend(self, ev):
+        """
+            After a dragging motion this function is called to compute
+            the error and the sum of pixel of a given data 2D 
+        """
+        self.base.thaw_axes()
+        ## compute error an d sum of data's pixel
+        self._post_data()
+        ## Create and event ( posted to guiframe)that  set the 
+        ##current slicer parameter to a panel of name self.panel_name
+        self.type = self.__class__.__name__
+        params = self.get_params()
+        event = SlicerParamUpdateEvent(type=self.type,
+                                       params=params,
+                                       panel_name=self.panel_name)
+        wx.PostEvent(self.base.parent, event)
+        
+    def restore(self):
+        """
+        Restore the roughness for this layer.
+        """
+        self.horizontal_lines.restore()
+        self.vertical_lines.restore()
+        self.center.restore()
+        
+    def move(self, x, y, ev):
+        """
+        Process move to a new position, making sure that the move is allowed.
+        """
+        pass
+    
+    def set_cursor(self, x, y):
+        """
+        """
+        pass
+        
+    def get_params(self):
+        """
+        Store a copy of values of parameters of the slicer into a dictionary.
+        :return params: the dictionary created
+        """
+        params = {}
+        params["Width"] = math.fabs(self.vertical_lines.half_width) * 2
+        params["Height"] = math.fabs(self.horizontal_lines.half_height) * 2 
+        params["center_x"] = self.center.x
+        params["center_y"] = self.center.y
+        params["count"] = self.count
+        params["errors"] = self.error
+        return params
+    
+    def get_result(self):
+        """
+            return the result of box summation
+        """
+        result = {}
+        result["count"] = self.count
+        result["error"] = self.error
+        return result
+        
+    def set_params(self, params):
+        """
+        Receive a dictionary and reset the slicer with values contained 
+        in the values of the dictionary.
+        :param params: a dictionary containing name of slicer parameters and 
+            values the user assigned to the slicer.
+        """
+        x_max = math.fabs(params["Width"] )/2
+        y_max = math.fabs(params["Height"] )/2
+        
+        self.center_x = params["center_x"] 
+        self.center_y = params["center_y"]
+        #update the slicer given values of params
+        self.center.update(center_x=self.center_x, center_y=self.center_y)
+        self.horizontal_lines.update(center=self.center,
+                                     width=x_max, height=y_max)
+        self.vertical_lines.update(center=self.center,
+                                    width=x_max, height=y_max)
+        #compute the new error and sum given values of params
+        self._post_data()
+        
+    def freeze_axes(self):
+        """
+        """
+        self.base.freeze_axes()
+        
+    def thaw_axes(self):
+        """
+        """
+        self.base.thaw_axes()
+
+    def draw(self):
+        """
+        """
+        self.base.draw()
+        
+        
+        
+class PointInteractor(_BaseInteractor):
+    """
+    Draw a point that can be dragged with the marker.
+    this class controls the motion the center of the BoxSum 
+    """
+    def __init__(self, base, axes, color='black', zorder=5, center_x=0.0,
+                 center_y=0.0):
+        """
+        """
+        _BaseInteractor.__init__(self, base, axes, color=color)
+        ## Initialization the class 
+        self.markers = []
+        self.axes = axes
+        # center coordinates
+        self.x = center_x
+        self.y = center_y
+        ## saved value of the center coordinates
+        self.save_x = center_x
+        self.save_y = center_y
+        ## Create a marker 
+        try:
+            self.center_marker = self.axes.plot([self.x],[self.y], linestyle='',
+                                          marker='s', markersize=10,
+                                          color=self.color, alpha=0.6,
+                                          pickradius=5, label="pick", 
+                                          # Prefer this to other lines
+                                          zorder=zorder, 
+                                          visible=True)[0]
+        except:
+            self.center_marker = self.axes.plot([self.x],[self.y], linestyle='',
+                                          marker='s', markersize=10,
+                                          color=self.color, alpha=0.6,
+                                          label="pick", 
+                                          visible=True)[0]
+            message  = "\nTHIS PROTOTYPE NEEDS THE LATEST VERSION"
+            message += " OF MATPLOTLIB.\n Get the SVN version that is "
+            message += "at least as recent as June 1, 2007"
+            owner = self.base.base.parent
+            wx.PostEvent(owner,
+                         StatusEvent(status="AnnulusSlicer: %s" % message))
+        ## Draw a point
+        self.center = self.axes.plot([self.x], [self.y],
+                                      linestyle='-', marker='',
+                                      color=self.color,
+                                      visible=True)[0]
+        ## Flag to determine the motion this point 
+        self.has_move = False   
+        ## connecting the marker to allow them to move 
+        self.connect_markers([self.center_marker])
+        ## Update the figure
+        self.update()
+    
+    def set_layer(self, n):
+        """
+            Allow adding plot to the same panel
+            @param n: the number of layer
+        """
+        self.layernum = n
+        self.update()
+        
+    def clear(self):
+        """
+            Clear this figure and its markers
+        """
+        self.clear_markers()
+        try:
+            self.center.remove()
+            self.center_marker.remove()
+        except:
+            # Old version of matplotlib
+            for item in range(len(self.axes.lines)):
+                del self.axes.lines[0]
+  
+    def update(self, center_x=None,center_y=None):
+        """
+            Draw the new roughness on the graph.
+        """
+        if center_x != None:
+            self.x = center_x
+        if center_y != None:
+            self.y = center_y
+        self.center_marker.set(xdata=[self.x], ydata=[self.y])
+        self.center.set(xdata=[self.x], ydata=[self.y])
+        
+    def save(self, ev):
+        """
+        Remember the roughness for this layer and the next so that we
+        can restore on Esc.
+        """
+        self.save_x = self.x
+        self.save_y = self.y
+        self.base.freeze_axes()
+        
+    def moveend(self, ev):
+        """
+        """
+        self.has_move=False
+        self.base.moveend(ev)
+            
+    def restore(self):
+        """
+        Restore the roughness for this layer.
+        """
+        self.y = self.save_y
+        self.x = self.save_x
+    
+    def move(self, x, y, ev):
+        """
+        Process move to a new position, making sure that the move is allowed.
+        """
+        self.x = x
+        self.y = y
+        self.has_move = True
+        self.base.base.update()
+        
+    def set_cursor(self, x, y):
+        """
+        """
+        self.move(x, y, None)
+        self.update()
+    
+    def get_params(self):
+        """
+        """
+        params = {}
+        params["x"] = self.x
+        params["y"] = self.y
+        return params
+    
+    def set_params(self, params):
+        """
+        """
+        center_x = params["x"] 
+        center_y = params["y"] 
+        self.update(center_x=center_x, center_y=center_y)
+       
+        
+class VerticalDoubleLine(_BaseInteractor):
+    """
+         Draw 2 vertical lines moving in opposite direction and centered on 
+         a point (PointInteractor)
+    """
+    def __init__(self, base, axes, color='black', zorder=5, x=0.5,y=0.5,
+                 center_x=0.0, center_y=0.0):
+        """
+        """
+        _BaseInteractor.__init__(self, base, axes, color=color)
+        ## Initialization the class 
+        self.markers = []
+        self.axes = axes
+        ## Center coordinates
+        self.center_x = center_x
+        self.center_y = center_y
+        ## defined end points vertical lignes and their saved values
+        self.y1 = y + self.center_y
+        self.save_y1= self.y1
+        
+        delta = self.y1 - self.center_y
+        self.y2 = self.center_y - delta
+        self.save_y2 = self.y2
+        
+        self.x1 = x + self.center_x
+        self.save_x1 = self.x1
+         
+        delta = self.x1 - self.center_x
+        self.x2 = self.center_x - delta
+        self.save_x2 = self.x2
+        ## save the color of the line
+        self.color = color
+        ## the height of the rectangle
+        self.half_height = math.fabs(y)
+        self.save_half_height = math.fabs(y)
+        ## the with of the rectangle
+        self.half_width = math.fabs(self.x1- self.x2)/2
+        self.save_half_width = math.fabs(self.x1- self.x2)/2
+        ## Create marker 
+        try:
+            self.right_marker = self.axes.plot([self.x1],[0], linestyle='',
+                                          marker='s', markersize=10,
+                                          color=self.color, alpha=0.6,
+                                          pickradius=5, label="pick", 
+                                          # Prefer this to other lines
+                                          zorder=zorder, visible=True)[0]
+        except:
+            self.right_marker = self.axes.plot([self.x1],[0], linestyle='',
+                                          marker='s', markersize=10,
+                                          color=self.color, alpha=0.6,
+                                          label="pick", visible=True)[0]
+            message  = "\nTHIS PROTOTYPE NEEDS THE LATEST "
+            message += "VERSION OF MATPLOTLIB\n Get the SVN version that"
+            message += " is at least as recent as June 1, 2007"
+            owner = self.base.base.parent
+            wx.PostEvent(owner,
+                         StatusEvent(status="AnnulusSlicer: %s" % message))
+            
+        ## define the left and right lines of the rectangle
+        self.right_line = self.axes.plot([self.x1, self.x1], [self.y1, self.y2],
+                                      linestyle='-', marker='',
+                                      color=self.color, visible=True)[0]
+        self.left_line = self.axes.plot([self.x2, self.x2], [self.y1, self.y2],
+                                      linestyle='-', marker='',
+                                      color=self.color, visible=True)[0]
+        ## Flag to determine if the lines have moved
+        self.has_move = False  
+        ## connection the marker and draw the pictures  
+        self.connect_markers([self.right_marker])
+        self.update()
+
+    def set_layer(self, n):
+        """
+        Allow adding plot to the same panel
+        :param n: the number of layer
+        """
+        self.layernum = n
+        self.update()
+        
+    def clear(self):
+        """
+        Clear this slicer  and its markers
+        """
+        self.clear_markers()
+        try:
+            self.right_marker.remove()
+            self.right_line.remove()
+            self.left_line.remove()
+        except:
+            # Old version of matplotlib
+            for item in range(len(self.axes.lines)):
+                del self.axes.lines[0]
+   
+    def update(self, x1=None, x2=None, y1=None, y2=None,width=None,
+                height=None, center=None):
+        """
+        Draw the new roughness on the graph.
+        :param x1: new maximum value of x coordinates
+        :param x2: new minimum value of x coordinates
+        :param y1: new maximum value of y coordinates
+        :param y2: new minimum value of y coordinates
+        :param width: is the width of the new rectangle
+        :param height: is the height of the new rectangle
+        :param center: provided x, y  coordinates of the center point
+        """
+        ## save the new height, witdh of the rectangle if given as a param
+        if width != None:
+            self.half_width = width
+        if height != None:
+            self.half_height = height
+        ## If new  center coordinates are given draw the rectangle 
+        ##given these value 
+        if center != None:
+            self.center_x = center.x
+            self.center_y = center.y
+            self.x1 = self.half_width + self.center_x
+            self.x2= -self.half_width + self.center_x
+            self.y1 = self.half_height + self.center_y
+            self.y2= -self.half_height + self.center_y
+         
+            self.right_marker.set(xdata=[self.x1], ydata=[self.center_y])
+            self.right_line.set(xdata=[self.x1, self.x1], 
+                                ydata=[self.y1, self.y2])
+            self.left_line.set(xdata=[self.x2, self.x2],
+                               ydata=[self.y1, self.y2])
+            return 
+        ## if x1, y1, y2, y3 are given draw the rectangle with this value
+        if x1 != None: 
+            self.x1 = x1
+        if x2 != None: 
+            self.x2 = x2
+        if y1 != None: 
+            self.y1 = y1
+        if y2 != None: 
+            self.y2 = y2
+        ## Draw 2 vertical lines and a marker 
+        self.right_marker.set(xdata=[self.x1], ydata=[self.center_y])
+        self.right_line.set(xdata=[self.x1, self.x1], ydata=[self.y1, self.y2])
+        self.left_line.set(xdata=[self.x2, self.x2], ydata=[self.y1, self.y2])
+        
+    def save(self, ev):
+        """
+        Remember the roughness for this layer and the next so that we
+        can restore on Esc.
+        """
+        self.save_x2 = self.x2
+        self.save_y2 = self.y2
+        self.save_x1 = self.x1
+        self.save_y1 = self.y1
+        self.save_half_height = self.half_height
+        self.save_half_width = self.half_width
+        self.base.freeze_axes()
+
+    def moveend(self, ev):
+        """
+            After a dragging motion reset the flag self.has_move to False
+        """
+        self.has_move = False
+        self.base.moveend(ev)
+            
+    def restore(self):
+        """
+        Restore the roughness for this layer.
+        """
+        self.y2 = self.save_y2
+        self.x2 = self.save_x2
+        self.y1 = self.save_y1
+        self.x1 = self.save_x1
+        self.half_height = self.save_half_height
+        self.half_width = self.save_half_width
+       
+    def move(self, x, y, ev):
+        """
+        Process move to a new position, making sure that the move is allowed.
+        """
+        self.x1 = x
+        delta = self.x1 - self.center_x
+        self.x2 = self.center_x - delta
+        self.half_width = math.fabs(self.x1 - self.x2)/2
+        self.has_move = True
+        self.base.base.update()
+        
+    def set_cursor(self, x, y):
+        """
+            Update the figure given x and y 
+        """
+        self.move(x, y, None)
+        self.update()
+        
+    def get_params(self):
+        """
+            Store a copy of values of parameters of the slicer into a dictionary.
+            @return params: the dictionary created
+        """
+        params = {}
+        params["x"] = self.x1
+        params["y"] = self.y1
+        return params
+    
+    def set_params(self, params):
+        """
+            Receive a dictionary and reset the slicer with values contained 
+            in the values of the dictionary.
+            @param params: a dictionary containing name of slicer parameters and 
+            values the user assigned to the slicer.
+        """
+        x = params["x"] 
+        y = params["y"] 
+        self.update(x=x, y=y, center_x=None, center_y=None)
+
+
+class HorizontalDoubleLine(_BaseInteractor):
+    """
+         Select an annulus through a 2D plot
+    """
+    def __init__(self, base, axes, color='black', zorder=5, x=0.5, y=0.5,
+                 center_x=0.0, center_y=0.0):
+        
+        _BaseInteractor.__init__(self, base, axes, color=color)
+        ## Initialization the class 
+        self.markers = []
+        self.axes = axes
+        ## Center coordinates
+        self.center_x = center_x
+        self.center_y = center_y
+        self.y1 = y + self.center_y
+        self.save_y1 = self.y1
+        delta = self.y1 - self.center_y
+        self.y2 = self.center_y - delta
+        self.save_y2 = self.y2
+        self.x1 = x + self.center_x
+        self.save_x1 = self.x1
+        delta = self.x1 - self.center_x
+        self.x2 = self.center_x - delta
+        self.save_x2 = self.x2
+        self.color = color
+        self.half_height= math.fabs(y)
+        self.save_half_height = math.fabs(y)
+        self.half_width = math.fabs(x)
+        self.save_half_width = math.fabs(x)
+        try:
+            self.top_marker = self.axes.plot([0], [self.y1], linestyle='',
+                                          marker='s', markersize=10,
+                                          color=self.color, alpha=0.6,
+                                          pickradius=5, label="pick",
+                                          # Prefer this to other lines
+                                          zorder=zorder, visible=True)[0]
+        except:
+            self.top_marker = self.axes.plot([0], [self.y1], linestyle='',
+                                          marker='s', markersize=10,
+                                          color=self.color, alpha=0.6,
+                                          label="pick", visible=True)[0]
+            message  = "\nTHIS PROTOTYPE NEEDS THE LATEST VERSION "
+            message += "OF MATPLOTLIB\n Get the SVN version "
+            message += "that is at least as recent as June 1, 2007"
+            owner = self.base.base.parent
+            wx.PostEvent(owner,
+                         StatusEvent(status="AnnulusSlicer: %s" % message))
+            
+        # Define 2 horizotnal lines
+        self.top_line = self.axes.plot([self.x1, -self.x1], [self.y1, self.y1],
+                                      linestyle='-', marker='',
+                                      color=self.color, visible=True)[0]
+        self.bottom_line = self.axes.plot([self.x1, -self.x1], 
+                                          [self.y2, self.y2],
+                                      linestyle='-', marker='',
+                                      color=self.color, visible=True)[0]
+        ## Flag to determine if the lines have moved
+        self.has_move = False    
+        ## connection the marker and draw the pictures 
+        self.connect_markers([self.top_marker])
+        self.update()
+
+    def set_layer(self, n):
+        """
+            Allow adding plot to the same panel
+            @param n: the number of layer
+        """
+        self.layernum = n
+        self.update()
+        
+    def clear(self):
+        """
+            Clear this figure and its markers
+        """
+        self.clear_markers()
+        try:
+            self.top_marker.remove()
+            self.bottom_line.remove()
+            self.top_line.remove()
+        except:
+            # Old version of matplotlib
+            for item in range(len(self.axes.lines)):
+                del self.axes.lines[0]
+    
+    def update(self, x1=None, x2=None, y1=None, y2=None,
+               width=None, height=None, center=None):
+        """
+        Draw the new roughness on the graph.
+        :param x1: new maximum value of x coordinates
+        :param x2: new minimum value of x coordinates
+        :param y1: new maximum value of y coordinates
+        :param y2: new minimum value of y coordinates
+        :param width: is the width of the new rectangle
+        :param height: is the height of the new rectangle
+        :param center: provided x, y  coordinates of the center point
+        """
+        ## save the new height, witdh of the rectangle if given as a param
+        if width != None:
+            self.half_width = width
+        if height!= None:
+            self.half_height = height
+        ## If new  center coordinates are given draw the rectangle 
+        ##given these value 
+        if center != None:
+            self.center_x = center.x
+            self.center_y = center.y
+            self.x1 = self.half_width + self.center_x
+            self.x2 = -self.half_width + self.center_x
+            
+            self.y1 = self.half_height + self.center_y
+            self.y2 = -self.half_height + self.center_y
+            
+            self.top_marker.set(xdata=[self.center_x], ydata=[self.y1])
+            self.top_line.set(xdata=[self.x1, self.x2],
+                              ydata=[self.y1, self.y1])
+            self.bottom_line.set(xdata=[self.x1, self.x2],
+                                 ydata=[self.y2, self.y2])
+            return 
+        ## if x1, y1, y2, y3 are given draw the rectangle with this value
+        if x1 != None: 
+            self.x1 = x1
+        if x2 != None: 
+            self.x2 = x2
+        if y1 != None: 
+            self.y1 = y1
+        if y2 != None: 
+            self.y2 = y2
+        ## Draw 2 vertical lines and a marker 
+        self.top_marker.set(xdata=[self.center_x], ydata=[self.y1])
+        self.top_line.set(xdata=[self.x1, self.x2], ydata=[self.y1, self.y1])
+        self.bottom_line.set(xdata=[self.x1, self.x2], ydata=[self.y2, self.y2])
+       
+    def save(self, ev):
+        """
+        Remember the roughness for this layer and the next so that we
+        can restore on Esc.
+        """
+        self.save_x2 = self.x2
+        self.save_y2 = self.y2
+        self.save_x1 = self.x1
+        self.save_y1 = self.y1
+        self.save_half_height = self.half_height
+        self.save_half_width =  self.half_width
+        self.base.freeze_axes()
+        
+    def moveend(self, ev):
+        """
+        After a dragging motion reset the flag self.has_move to False
+        """
+        self.has_move = False
+        self.base.moveend(ev)
+           
+    def restore(self):
+        """
+        Restore the roughness for this layer.
+        """
+        self.y2 = self.save_y2
+        self.x2 = self.save_x2
+        self.y1 = self.save_y1
+        self.x1 = self.save_x1
+        self.half_height = self.save_half_height
+        self.half_width = self.save_half_width
+        
+    def move(self, x, y, ev):
+        """
+        Process move to a new position, making sure that the move is allowed.
+        """
+        self.y1 = y
+        delta = self.y1 - self.center_y
+        self.y2 =  self.center_y - delta
+        self.half_height = math.fabs(self.y1) - self.center_y
+        self.has_move = True
+        self.base.base.update()
+    
+    def set_cursor(self, x, y):
+        """
+            Update the figure given x and y 
+        """
+        self.move(x, y, None)
+        self.update()
+        
+    def get_params(self):
+        """
+        Store a copy of values of parameters of the slicer into a dictionary.
+        :return params: the dictionary created
+        """
+        params = {}
+        params["x"] = self.x
+        params["y"] = self.y
+        return params
+    
+    def set_params(self, params):
+        """
+        Receive a dictionary and reset the slicer with values contained 
+        in the values of the dictionary.
+        :param params: a dictionary containing name of slicer parameters and 
+            values the user assigned to the slicer.
+        """
+        x = params["x"] 
+        y = params["y"] 
+        self.update(x=x, y=y, center_x=None,center_y=None)
+         
+         
+         
Index: guiframe/local_perspectives/plotting/detector_dialog.py
===================================================================
--- guiframe/local_perspectives/plotting/detector_dialog.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
+++ guiframe/local_perspectives/plotting/detector_dialog.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
@@ -0,0 +1,319 @@
+#!/usr/bin/env python
+
+# version
+__id__ = "$Id: aboutdialog.py 1193 2007-05-03 17:29:59Z dmitriy $"
+__revision__ = "$Revision: 1193 $"
+
+import wx
+import sys
+from sans.guiframe.utils import format_number
+from sans.guiframe.events import StatusEvent 
+from sans.guiframe.events import NewPlotEvent
+
+import matplotlib 
+from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as Canvas
+from matplotlib import pyplot
+from matplotlib import mpl
+from matplotlib import pylab
+#FONT size 
+if sys.platform.count("win32") > 0:
+    FONT_VARIANT = 0
+else:
+    FONT_VARIANT = 1
+    
+DEFAULT_CMAP = pylab.cm.jet
+
+class DetectorDialog(wx.Dialog):
+    """
+    Dialog box to let the user edit detector settings
+    """
+    
+    def __init__(self, parent, id=1, base=None, dpi=None,
+                 cmap=DEFAULT_CMAP, reset_zmin_ctl = None,
+                 reset_zmax_ctl = None, *args, **kwds):
+        """
+        """
+        kwds["style"] = wx.DEFAULT_DIALOG_STYLE
+        wx.Dialog.__init__(self, parent, id=1, *args, **kwds)
+        self.SetWindowVariant(variant=FONT_VARIANT)
+        self.parent = base
+        self.dpi = dpi
+        self.cmap = cmap
+        self.reset_zmin_ctl = reset_zmin_ctl
+        self.reset_zmax_ctl = reset_zmax_ctl
+        self.label_xnpts = wx.StaticText(self, -1, "Detector width in pixels")
+        self.label_ynpts = wx.StaticText(self, -1, "Detector Height in pixels")
+        self.label_qmax = wx.StaticText(self, -1, "Q max")
+        self.label_zmin = wx.StaticText(self, -1,
+                                    "Min amplitude for color map (optional)")
+        self.label_zmax = wx.StaticText(self, -1,
+                                    "Max amplitude for color map (optional)")
+        self.label_beam = wx.StaticText(self, -1,
+                                        "Beam stop radius in units of q")
+        self.xnpts_ctl = wx.StaticText(self, -1, "")
+        self.ynpts_ctl = wx.StaticText(self, -1, "")
+        self.qmax_ctl = wx.StaticText(self, -1, "")
+        self.beam_ctl = wx.StaticText(self, -1, "")
+        self.zmin_ctl = wx.TextCtrl(self, -1, size=(60, 20))
+        self.zmin_ctl.Bind(wx.EVT_SET_FOCUS, self.onSetFocus)
+        self.zmax_ctl = wx.TextCtrl(self, -1, size=(60, 20))
+        self.zmax_ctl.Bind(wx.EVT_SET_FOCUS, self.onSetFocus)
+        self.static_line_3 = wx.StaticLine(self, -1)
+        self.button_Cancel = wx.Button(self, wx.ID_CANCEL, "Cancel")
+        self.button_reset = wx.Button(self, wx.NewId(), "Reset")
+        self.Bind(wx.EVT_BUTTON, self.resetValues, self.button_reset)
+        self.button_OK = wx.Button(self, wx.ID_OK, "OK")
+        self.Bind(wx.EVT_BUTTON, self.checkValues, self.button_OK)
+        self.__set_properties()
+        self.__do_layout()
+        self.Fit()
+        
+    class Event:
+        """
+        """
+        xnpts = 0
+        ynpts = 0
+        qpax = 0
+        beam = 0
+        zmin = 0
+        zmax = 0
+        cmap = None
+        sym4 = False
+    
+    def onSetFocus(self, event):
+        """
+        Highlight the txtcrtl
+        """
+        # Get a handle to the TextCtrl
+        widget = event.GetEventObject()
+        # Select the whole control, after this event resolves
+        wx.CallAfter(widget.SetSelection, -1, -1)
+        
+    def resetValues(self, event):
+        """
+        reset detector info
+        """
+        try:
+            zmin = self.reset_zmin_ctl
+            zmax = self.reset_zmax_ctl
+            if zmin == None:
+                zmin = ""
+            if zmax == None:
+                zmax = ""    
+            self.zmin_ctl.SetValue(str(zmin))
+            self.zmax_ctl.SetValue(str(zmax))
+            self.cmap = DEFAULT_CMAP
+            self.cmap_selector.SetStringSelection("jet")
+            self._on_select_cmap(event=None)
+        except:
+            msg = "error occurs while resetting Detector: %s" % sys.exc_value
+            wx.PostEvent(self.parent, StatusEvent(status=msg))
+        
+    def checkValues(self, event):
+        """
+        Check the valitidity of zmin and zmax value
+        zmax should be a float and zmin less than zmax
+        """
+        flag = True
+        try:
+            value = self.zmin_ctl.GetValue()
+            #if value and float( value) == 0.0:
+            #    flag = False
+            #    wx.PostEvent(self.parent,
+            #            StatusEvent(status="Enter number greater than zero"))
+            #    self.zmin_ctl.SetBackgroundColour("pink")
+            #    self.zmin_ctl.Refresh()
+            #else:
+            self.zmin_ctl.SetBackgroundColour(wx.WHITE)
+            self.zmin_ctl.Refresh()
+        except:
+            flag = False
+            wx.PostEvent(self.parent, StatusEvent(status="Enter float value"))
+            self.zmin_ctl.SetBackgroundColour("pink")
+            self.zmin_ctl.Refresh()
+        try:
+            value = self.zmax_ctl.GetValue()
+            if value and float(value) == 0.0:
+                flag = False
+                wx.PostEvent(self.parent,
+                        StatusEvent(status="Enter number greater than zero"))
+                self.zmax_ctl.SetBackgroundColour("pink")
+                self.zmax_ctl.Refresh()
+            else:
+                self.zmax_ctl.SetBackgroundColour(wx.WHITE)
+                self.zmax_ctl.Refresh()
+        except:
+            flag = False
+            wx.PostEvent(self.parent, StatusEvent(status="Enter Integer value"))
+            self.zmax_ctl.SetBackgroundColour("pink")
+            self.zmax_ctl.Refresh()
+        if flag:
+            event.Skip(True)
+    
+    def setContent(self, xnpts, ynpts, qmax, beam,
+                   zmin=None, zmax=None, sym=False):
+        """
+        received value and displayed them
+        
+        :param xnpts: the number of point of the x_bins of data
+        :param ynpts: the number of point of the y_bins of data
+        :param qmax: the maxmimum value of data pixel
+        :param beam: the radius of the beam
+        :param zmin:  the value to get the minimum color
+        :param zmax:  the value to get the maximum color
+        :param sym:
+        
+        """
+        self.xnpts_ctl.SetLabel(str(format_number(xnpts)))
+        self.ynpts_ctl.SetLabel(str(format_number(ynpts)))
+        self.qmax_ctl.SetLabel(str(format_number(qmax)))
+        self.beam_ctl.SetLabel(str(format_number(beam)))
+        if zmin != None:
+            self.zmin_ctl.SetValue(str(format_number(zmin)))
+        if zmax != None:
+            self.zmax_ctl.SetValue(str(format_number(zmax)))
+
+    def getContent(self):
+        """
+        return event containing value to reset the detector of a given data
+        """
+        event = self.Event()
+        t_min = self.zmin_ctl.GetValue()
+        t_max = self.zmax_ctl.GetValue()
+        v_min = None
+        v_max = None
+        if len(t_min.lstrip()) > 0:
+            try:
+                v_min = float(t_min)
+            except:
+                v_min = None
+        if len(t_max.lstrip()) > 0:
+            try:
+                v_max = float(t_max)
+            except:
+                v_max = None
+        event.zmin = v_min
+        event.zmax = v_max
+        event.cmap = self.cmap
+        return event
+
+    def __set_properties(self):
+        """
+        set proprieties of the dialog window
+        """
+        self.SetTitle("2D Color Map")
+        self.SetSize((600, 595))
+
+    def __do_layout(self):
+        """
+        fill the dialog window .
+        """
+        sizer_main = wx.BoxSizer(wx.VERTICAL)
+        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
+        sizer_params = wx.GridBagSizer(5, 5)
+        sizer_colormap = wx.BoxSizer(wx.VERTICAL)
+        sizer_selection = wx.BoxSizer(wx.HORIZONTAL)
+       
+        iy = 0
+        sizer_params.Add(self.label_xnpts, (iy, 0), (1, 1),
+                          wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
+        sizer_params.Add(self.xnpts_ctl,   (iy, 1), (1, 1),
+                         wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+        iy += 1
+        sizer_params.Add(self.label_ynpts, (iy, 0), (1, 1),
+                         wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
+        sizer_params.Add(self.ynpts_ctl,   (iy, 1), (1, 1),
+                         wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+        iy += 1
+        sizer_params.Add(self.label_qmax, (iy, 0), (1, 1),
+                         wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
+        sizer_params.Add(self.qmax_ctl,   (iy, 1), (1, 1),
+                         wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+        iy += 1
+        sizer_params.Add(self.label_beam, (iy, 0), (1, 1),
+                         wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
+        sizer_params.Add(self.beam_ctl,   (iy, 1), (1, 1),
+                         wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+        iy += 1
+        sizer_params.Add(self.label_zmin, (iy, 0), (1, 1),
+                         wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
+        sizer_params.Add(self.zmin_ctl,   (iy, 1), (1, 1),
+                         wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+        iy += 1
+        sizer_params.Add(self.label_zmax, (iy, 0), (1, 1),
+                         wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
+        sizer_params.Add(self.zmax_ctl,   (iy, 1), (1, 1),
+                         wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+        iy += 1
+        self.fig = mpl.figure.Figure(dpi=self.dpi, figsize=(4, 1))
+        self.ax1 = self.fig.add_axes([0.05, 0.65, 0.9, 0.15])
+        self.norm = mpl.colors.Normalize(vmin=0, vmax=100)
+        self.cb1 = mpl.colorbar.ColorbarBase(self.ax1, cmap=self.cmap,
+                                           norm= self.norm,
+                                           orientation='horizontal')
+        self.cb1.set_label('Detector Colors')
+        self.canvas = Canvas(self, -1, self.fig)
+        sizer_colormap.Add(self.canvas, 0, wx.LEFT | wx.EXPAND,5)
+        self.cmap_selector = wx.ComboBox(self, -1)
+        self.cmap_selector.SetValue(str(self.cmap.name))
+        maps = sorted(m for m in pylab.cm.datad if not m.endswith("_r"))
+       
+        for i, m in enumerate(maps):
+            self.cmap_selector.Append(str(m), pylab.get_cmap(m))
+        
+        wx.EVT_COMBOBOX(self.cmap_selector, -1, self._on_select_cmap)
+        sizer_selection.Add(wx.StaticText(self, -1,"Select Cmap: "), 0,
+                             wx.LEFT|wx.ADJUST_MINSIZE, 5) 
+        sizer_selection.Add(self.cmap_selector, 0, wx.EXPAND|wx.ALL, 10)
+        sizer_main.Add(sizer_params, 0, wx.EXPAND|wx.ALL, 10)
+        sizer_main.Add(sizer_selection, 0, wx.EXPAND|wx.ALL, 10)
+        sizer_main.Add(sizer_colormap, 1, wx.EXPAND|wx.ALL, 10)
+        sizer_main.Add(self.static_line_3, 0, wx.EXPAND, 0)
+        sizer_button.Add(self.button_reset, 0, wx.LEFT|wx.ADJUST_MINSIZE, 100)
+        sizer_button.Add(self.button_OK, 0, wx.LEFT|wx.ADJUST_MINSIZE, 10)
+        sizer_button.Add(self.button_Cancel, 0,
+                         wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 10)
+        sizer_main.Add(sizer_button, 0, wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
+        self.SetAutoLayout(True)
+        self.SetSizer(sizer_main)
+        self.Layout()
+        self.Centre()
+        # end wxGlade
+        
+    def _on_select_cmap(self, event):
+        """
+        display a new cmap 
+        """
+        cmap_name = self.cmap_selector.GetCurrentSelection()
+        current_cmap = self.cmap_selector.GetClientData(cmap_name)
+        self.cmap = current_cmap
+        self.cb1 = mpl.colorbar.ColorbarBase(self.ax1, cmap=self.cmap,
+                                     norm=self.norm, orientation='horizontal')
+        self.canvas.draw()
+
+
+# end of class DialogAbout
+
+##### testing code ############################################################
+class MyApp(wx.App):
+    def OnInit(self):
+        wx.InitAllImageHandlers()
+        dialog = DetectorDialog(None, -1, "")
+        self.SetTopWindow(dialog)
+        dialog.setContent(xnpts=128, ynpts=128, qmax=20,
+                           beam=20, zmin=2, zmax=60, sym=False)
+        print dialog.ShowModal()
+        evt = dialog.getContent()
+        if hasattr(evt, "npts"):
+            print "number of point: ", evt.npts
+        if hasattr(evt,"qmax"): 
+            print "qmax: ", evt.qmax
+        dialog.Destroy()
+        return 1
+
+# end of class MyApp
+
+if __name__ == "__main__":
+    app = MyApp(0)
+    app.MainLoop()
+        
Index: guiframe/local_perspectives/plotting/help_panel.py
===================================================================
--- guiframe/local_perspectives/plotting/help_panel.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
+++ guiframe/local_perspectives/plotting/help_panel.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
@@ -0,0 +1,111 @@
+#!/usr/bin/python
+"""
+Help panel for plotting
+"""
+import os
+import wx
+import wx.html as html
+from wx.lib.splitter import MultiSplitterWindow
+from sans.guiframe import get_media_path
+
+
+class HelpWindow(wx.Frame):
+    """
+    """
+    def __init__(self, parent, title='Plotting Help', pageToOpen=None):
+        wx.Frame.__init__(self, parent, title, size=(700, 450))
+        """
+             contains help info
+        """
+        splitter = MultiSplitterWindow(self, style=wx.SP_LIVE_UPDATE)
+        rpanel = wx.Panel(splitter, -1)
+        lpanel = wx.Panel(splitter, -1, style=wx.BORDER_SUNKEN)
+        
+        vbox = wx.BoxSizer(wx.VERTICAL)
+        header = wx.Panel(rpanel, -1)
+        header.SetBackgroundColour('#6666FF')
+        header.SetForegroundColour('WHITE')
+        hbox = wx.BoxSizer(wx.HORIZONTAL)
+        st = wx.StaticText(header, -1, 'Contents', (5, 5))
+        font = st.GetFont()
+        font.SetPointSize(10)
+        st.SetFont(font)
+        hbox.Add(st, 1, wx.TOP | wx.BOTTOM | wx.LEFT, 5)
+        header.SetSizer(hbox)
+        vbox.Add(header, 0, wx.EXPAND)
+       
+        vboxl = wx.BoxSizer(wx.VERTICAL)
+        headerl = wx.Panel(lpanel, -1, size=(-1, 20))
+       
+        headerl.SetBackgroundColour('#6666FF')
+        headerl.SetForegroundColour('WHITE')
+        hboxl = wx.BoxSizer(wx.HORIZONTAL)
+        lst = wx.StaticText(headerl, -1, 'Menu', (5, 5))
+        fontl = lst.GetFont()
+        fontl.SetPointSize(10)
+        lst.SetFont(fontl)
+        hboxl.Add(lst, 1, wx.TOP | wx.BOTTOM | wx.LEFT, 5)
+        headerl.SetSizer(hboxl)
+        vboxl.Add(headerl, 0, wx.EXPAND)
+        self.lhelp = html.HtmlWindow(lpanel, -1, style=wx.NO_BORDER)
+        self.rhelp = html.HtmlWindow(rpanel, -1, style=wx.NO_BORDER, 
+                                     size=(500,-1))
+        
+        self.path = get_media_path(media='media')
+       
+        page1 = """<html>
+            <body>
+             <p>Select topic on Menu</p>
+            </body>
+            </html>"""
+        self.rhelp.SetPage(page1)
+        page = """<html>
+            <body>
+            <ul>
+            <li><a href ="Graph_help.html" 
+            target ="showframe">Graph Context Menu</a><br></li>
+            <li><a href ="averaging_help.html" 
+            target ="showframe">2D Data Averaging</a><br></li>
+            <li><a href ="key_help.html" 
+            target ="showframe">Key Combination</a><br></li>
+            </ul>
+            </body>
+            </html>"""
+
+        self.lhelp.SetPage(page)
+        self.lhelp.Bind(wx.html.EVT_HTML_LINK_CLICKED, self.OnLinkClicked)
+        
+        vbox.Add(self.rhelp, 1, wx.EXPAND)
+        vboxl.Add(self.lhelp, 1, wx.EXPAND)
+        rpanel.SetSizer(vbox)
+        lpanel.SetSizer(vboxl)
+        lpanel.SetFocus()
+        
+        vbox1 = wx.BoxSizer(wx.HORIZONTAL)
+        vbox1.Add(splitter, 1, wx.EXPAND)
+        splitter.AppendWindow(lpanel, 200)
+        splitter.AppendWindow(rpanel)
+        self.SetSizer(vbox1)
+       
+        self.Centre()
+        self.Show(True)
+        
+    def OnLinkClicked(self, event):
+        """
+            Function to diplay html page related to the hyperlinktext selected
+        """
+        link = event.GetLinkInfo().GetHref()
+        link = os.path.join(self.path, link)
+        self.rhelp.LoadPage(link)
+        
+        
+class ViewApp(wx.App):
+    def OnInit(self):
+        frame = HelpWindow(None, -1, 'HelpWindow')    
+        frame.Show(True)
+        self.SetTopWindow(frame)
+        return True
+        
+if __name__ == "__main__": 
+    app = ViewApp(0)
+    app.MainLoop()     
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()     
Index: guiframe/local_perspectives/plotting/plotting.py
===================================================================
--- guiframe/local_perspectives/plotting/plotting.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
+++ guiframe/local_perspectives/plotting/plotting.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
@@ -0,0 +1,346 @@
+
+
+
+################################################################################
+#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. 
+#
+#See the license text in license.txt
+#
+#copyright 2008, University of Tennessee
+################################################################################
+
+import wx
+import sys
+from sans.guiframe.events import EVT_NEW_PLOT
+from sans.guiframe.events import StatusEvent 
+from sans.guiframe.events import DeletePlotPanelEvent
+from sans.guiframe.plugin_base import PluginBase
+from sans.guiframe.dataFitting import Data1D
+from sans.guiframe.dataFitting import Data2D
+
+DEFAULT_MENU_ITEM_LABEL = "No graph available"
+DEFAULT_MENU_ITEM_ID = wx.NewId()
+
+if sys.platform.count("darwin")==0:
+    IS_WIN = True
+else:
+    IS_WIN = False
+
+class Plugin(PluginBase):
+    """
+    Plug-in class to be instantiated by the GUI manager
+    """
+    
+    def __init__(self, standalone=False):
+        PluginBase.__init__(self, name="Plotting", standalone=standalone)
+      
+        ## Plot panels
+        self.plot_panels = {}
+        self._panel_on_focus = None
+        self.menu_default_id = None
+        # Plot menu
+        self.menu = None
+
+     
+    def set_panel_on_focus(self, panel):
+        """
+        """
+        self._panel_on_focus = panel
+        
+    def is_always_active(self):
+        """
+        return True is this plugin is always active even if the user is 
+        switching between perspectives
+        """
+        return True
+    
+    def populate_menu(self, parent):
+        """
+        Create a 'Plot' menu to list the panels
+        available for displaying
+        
+        :param id: next available unique ID for wx events
+        :param parent: parent window
+        
+        """
+        self.menu = wx.Menu()
+        self.menu.Append(DEFAULT_MENU_ITEM_ID, DEFAULT_MENU_ITEM_LABEL, 
+                             "No graph available")
+        self.menu.FindItemByPosition(0).Enable(False)
+        return [(self.menu, "Show")]
+    
+    def get_panels(self, parent):
+        """
+        Create and return a list of panel objects
+        """
+        ## Save a reference to the parent
+        self.parent = parent
+        # Connect to plotting events
+        self.parent.Bind(EVT_NEW_PLOT, self._on_plot_event)
+        # We have no initial panels for this plug-in
+        return []
+   
+    def _on_show_panel(self, event):
+        """show plug-in panel"""
+        pass
+    
+    def remove_plot(self, group_id, id):
+        """
+        remove plot of ID = id from a panel of group ID =group_id
+        """
+        
+        if group_id in self.plot_panels.keys():
+            panel = self.plot_panels[group_id]
+            panel.remove_data_by_id(id=id)
+            return True
+        return False
+        
+    def clear_panel(self):
+        """
+        Clear and Hide all plot panels, and remove them from menu
+        """
+        for group_id in self.plot_panels.keys():
+            panel = self.plot_panels[group_id]
+            panel.graph.reset()
+            self.hide_panel(group_id)
+        self.plot_panels = {}
+        item = self.menu.FindItemByPosition(0)
+        while item != None:
+            self.menu.DeleteItem(item) 
+            try:
+                item = self.menu.FindItemByPosition(0)
+            except:
+                item = None
+               
+    
+    def clear_panel_by_id(self, group_id):
+        """
+        clear the graph
+        """
+        if group_id in self.plot_panels.keys():
+            panel = self.plot_panels[group_id]
+            panel.graph.reset()
+            return True
+        return False
+            
+        
+    def hide_panel(self, group_id):
+        """
+        hide panel with group ID = group_id
+        """
+        if group_id in self.plot_panels.keys():
+            panel = self.plot_panels[group_id]
+            self.parent.hide_panel(panel.uid)
+            return True
+        return False
+    
+    def create_panel_helper(self, new_panel, data, group_id, title=None):
+        """
+        """
+         ## Set group ID if available
+        ## Assign data properties to the new create panel
+        new_panel.set_manager(self)
+        new_panel.group_id = group_id
+        if group_id not in data.list_group_id:
+            data.list_group_id.append(group_id)
+        if title is None:
+            title = data.title
+        new_panel.window_caption = title
+        new_panel.window_name = data.title
+        event_id = self.parent.popup_panel(new_panel)
+        #remove the default item in the menu
+        if len(self.plot_panels) == 0:
+            pos = self.menu.FindItem(DEFAULT_MENU_ITEM_LABEL)
+            if pos != -1:
+                self.menu.Delete(DEFAULT_MENU_ITEM_ID)
+           
+        self.menu.AppendCheckItem(event_id, new_panel.window_caption, 
+                         "Show %s plot panel" % new_panel.window_caption)
+        self.menu.Check(event_id, IS_WIN)
+        wx.EVT_MENU(self.parent, event_id, self._on_check_menu)
+
+        wx.EVT_SHOW(new_panel, self._on_close_panel)
+        
+        # Set UID to allow us to reference the panel later
+        new_panel.uid = event_id
+        # Ship the plottable to its panel
+        new_panel.plot_data(data) 
+        self.plot_panels[new_panel.group_id] = new_panel
+        
+        
+    def create_1d_panel(self, data, group_id):
+        """
+        """
+        # Create a new plot panel if none was available        
+        if issubclass(data.__class__, Data1D):
+            from Plotter1D import ModelPanel1D
+            ## get the data representation label of the data to plot
+            ## when even the user select "change scale"
+            xtransform = data.xtransform
+            ytransform = data.ytransform
+            ## create a plotpanel for 1D Data
+            new_panel = ModelPanel1D(self.parent, -1, xtransform=xtransform,
+                     ytransform=ytransform, style=wx.RAISED_BORDER)
+            return  new_panel
+        
+        msg = "1D Panel of group ID %s could not be created" % str(group_id)
+        raise ValueError, msg
+    
+    def create_2d_panel(self, data, group_id):
+        """
+        """
+        if issubclass(data.__class__, Data2D):
+            ##Create a new plotpanel for 2D data
+            from Plotter2D import ModelPanel2D
+            scale = data.scale
+            new_panel = ModelPanel2D(self.parent, id = -1,
+                                data2d=data, scale = scale, 
+                                style=wx.RAISED_BORDER)
+            return new_panel
+        msg = "2D Panel of group ID %s could not be created" % str(group_id)
+        raise ValueError, msg
+    
+    def update_panel(self, data, panel):
+        """
+        update the graph of a given panel
+        """
+        # Check whether we already have a graph with the same units
+        # as the plottable we just received. 
+        _, x_unit =  data.get_xaxis()
+        _, y_unit =  data.get_yaxis()
+        flag_x = (panel.graph.prop["xunit"] is not None) and \
+                    (panel.graph.prop["xunit"].strip() != "") and\
+                    (x_unit != panel.graph.prop["xunit"])
+        flag_y = (panel.graph.prop["yunit"] is not None) and \
+                    (panel.graph.prop["yunit"].strip() != "") and\
+                    (y_unit != panel.graph.prop["yunit"])
+        if (flag_x and flag_y):
+            msg = "Cannot add %s" % str(data.name)
+            msg += " to panel %s\n" % str(panel.window_caption)
+            msg += "Please edit %s's units, labels" % str(data.name)
+            raise ValueError, msg
+        else:
+            if panel.group_id not in data.list_group_id:
+                data.list_group_id.append(panel.group_id)
+            panel.plot_data( data)
+            self.parent.show_panel(panel.uid)   
+    
+    def delete_menu_item(self, name, uid):
+        """
+        """
+        #remove menu item
+        pos = self.menu.FindItem(name) 
+        if pos != -1:
+            self.menu.Delete(uid)
+        if self.menu.GetMenuItemCount() == 0:
+            self.menu.Append(DEFAULT_MENU_ITEM_ID, DEFAULT_MENU_ITEM_LABEL, 
+                             "No graph available")
+            self.menu.FindItemByPosition(0).Enable(False)
+        
+    def delete_panel(self, group_id):
+        """
+        """
+        if group_id in self.plot_panels.keys():
+            panel = self.plot_panels[group_id]
+            uid = panel.uid
+            wx.PostEvent(self.parent, 
+                         DeletePlotPanelEvent(name=panel.window_caption,
+                                    caption=panel.window_caption))
+            #remove menu item
+            self.delete_menu_item(panel.window_caption, panel.uid)
+            del self.plot_panels[group_id]
+            self.parent.delete_panel(uid)
+            return True
+        return False
+    
+    def _on_plot_event(self, event):
+        """
+        A new plottable is being shipped to the plotting plug-in.
+        Check whether we have a panel to put in on, or create
+        a new one
+        
+        :param event: EVT_NEW_PLOT event
+        
+        """
+        if hasattr(event, 'action'):
+            group_id = event.group_id
+            #remove data from panel
+            if event.action.lower() == 'remove':
+                id = event.id
+                return self.remove_plot(group_id, id)
+            if event.action.lower() == 'hide':
+                return self.hide_panel(group_id)
+            if event.action.lower() == 'delete':
+                return self.delete_panel(group_id)
+            if event.action.lower() == "clear":
+                return self.clear_panel_by_id(group_id)
+            
+        title = None
+        if hasattr(event, 'title'):
+            title = event.title
+                
+        data = event.plot
+        group_id = data.group_id
+            
+        if group_id in self.plot_panels.keys():
+            #update a panel graph 
+            panel = self.plot_panels[group_id]
+            self.update_panel(data, panel)
+        else:
+            #create a new panel
+            if issubclass(data.__class__, Data1D):
+                new_panel = self.create_1d_panel(data, group_id)
+            else:
+                new_panel = self.create_2d_panel(data, group_id)
+            self.create_panel_helper(new_panel, data, group_id, title)
+      
+        return
+
+    def _on_check_menu(self, event):
+        """
+        Check mark on menu
+        """
+        #event.Skip()
+        event_id = event.GetId()
+
+        if self.menu.IsChecked(event_id):
+            self.parent.on_view(event)
+            self.menu.Check(event_id, IS_WIN)
+        else:
+            self.parent.hide_panel(event_id)
+            self.menu.Check(event_id, False)
+        
+    def help(self, evt):
+        """
+        Show a general help dialog. 
+        """
+        from help_panel import  HelpWindow
+        frame = HelpWindow(None, -1, 'HelpWindow')    
+        frame.Show(True)
+              
+    def _on_close_panel(self, event):
+        """
+        Update check mark on panel's show and hide event
+        """
+        event.Skip()
+        panel = event.GetEventObject()
+        id = panel.uid
+        if event.GetShow() and self.parent._mgr.GetPane(panel).IsShown():
+            try:
+                if self.menu.FindItemById(id) != None:
+                    if not self.menu.IsChecked(id):
+                        self.menu.Check(id, True)
+                        #self.menu.Enable(id, False)
+            except:
+                # when no such item; may happens after delete
+                pass
+        else:
+            try:
+                if self.menu.FindItemById(id) != None:
+                    if self.menu.IsChecked(id):
+                        self.menu.Check(id, False)
+                        #self.menu.Enable(id, True)
+            except:
+                pass 
Index: guiframe/local_perspectives/plotting/profile_dialog.py
===================================================================
--- guiframe/local_perspectives/plotting/profile_dialog.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
+++ guiframe/local_perspectives/plotting/profile_dialog.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
@@ -0,0 +1,261 @@
+"""
+SLD Profile Dialog for multifunctional models
+"""
+import wx
+import sys
+from copy import deepcopy
+from danse.common.plottools.plottables import Graph
+from Plotter1D import ModelPanel1D as PlotPanel
+from sans.guiframe.dataFitting import Theory1D
+import pylab
+
+DEFAULT_CMAP = None#pylab.cm.jet
+_BOX_WIDTH = 76
+_STATICBOX_WIDTH = 400
+# X Y offset on plot
+_X_OFF = 15
+_Y_OFF = 0.5
+
+#SLD panel size 
+if sys.platform.count("win32") > 0:
+    _STATICBOX_WIDTH = 563
+    PANEL_SIZE = 425
+    FONT_VARIANT = 0
+else:
+    _STATICBOX_WIDTH = 605
+    PANEL_SIZE = 500
+    FONT_VARIANT = 1
+    
+     
+class SLDPanel(wx.Dialog):
+    """
+    Provides the SLD profile plot panel.
+    """
+    ## Internal nickname for the window, used by the AUI manager
+    window_name = "SLD Profile"
+    ## Name to appear on the window title bar
+    window_caption = "SLD Profile"
+    ## 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, axes =['Radius'], 
+                 id=-1, *args, **kwds):
+        kwds["style"] = wx.DEFAULT_DIALOG_STYLE
+        kwds["size"] = wx.Size(_STATICBOX_WIDTH, PANEL_SIZE) 
+        wx.Dialog.__init__(self, parent, id=id, *args, **kwds)
+
+        if data != None:
+            #Font size 
+            kwds = []
+            self.SetWindowVariant(variant=FONT_VARIANT)
+            self.SetTitle("Scattering Length Density Profile")
+            self.parent = base
+            self.data = data
+            self.str = self.data.__str__()
+            ## when 2 data have the same id override the 1 st plotted
+            self.name = self.data.name
+            # Panel for plot
+            self.plotpanel = SLDplotpanel(self, axes, -1, 
+                                             style=wx.TRANSPARENT_WINDOW)
+            self.cmap = DEFAULT_CMAP
+            ## Create Artist and bind it
+            self.subplot = self.plotpanel.subplot
+            # layout
+            self._setup_layout()
+            # plot
+            data_plot = deepcopy(self.data)
+            data_plot.dy = self._set_dy_data()
+            # unit increase to M times for users
+            data_plot.y = self._set_y_data()
+            
+            self.newplot = Theory1D(data_plot.x, data_plot.y, data_plot.dy)
+            self.newplot.dy = None
+            self.newplot.name = 'SLD'
+            self.plotpanel.add_image(self.newplot) 
+            self.plotpanel.subplot.set_ylim(min(data_plot.y) - _Y_OFF , 
+                                                max(data_plot.y) + _Y_OFF)
+            self.plotpanel.subplot.set_xlim(min(data_plot.x) - _X_OFF, 
+                                                max(data_plot.x) + _X_OFF)
+            #self.Centre()
+            self.Layout()
+
+    def _set_dy_data(self): 
+        """
+        make fake dy data
+        
+        :return dy: 
+        """
+        # set dy as zero
+        dy = [0 for y in self.data.y]
+        return dy      
+
+    def _set_y_data(self): 
+        """
+        make y data unit Mega times
+        
+        :return y_value: 
+        """
+        # changes the unit
+        y_value = [yvalue * 1e+006 for yvalue in self.data.y]
+        
+        return y_value     
+    
+    def _setup_layout(self):
+        """
+        Set up the layout
+        """
+        # panel sizer
+        sizer = wx.BoxSizer(wx.VERTICAL)
+        sizer.Add(self.plotpanel, 0, wx.LEFT|wx.RIGHT, 5)
+        sizer.Add(wx.StaticLine(self), 0, wx.ALL|wx.EXPAND, 5)
+        sizer.Add((0, 5))
+        #-----Button------------1
+        id = wx.NewId()
+        button_reset = wx.Button(self, id, "Close")
+        button_reset.SetToolTipString("Close...")
+        button_reset.Bind(wx.EVT_BUTTON, self._close, 
+                          id=button_reset.GetId()) 
+        sizer.Add(button_reset, 0, wx.LEFT, _STATICBOX_WIDTH - 80) 
+        sizer.Add((0, 10))
+        self.SetSizerAndFit(sizer)
+        self.Centre()
+        self.Show(True)
+        button_reset.SetFocus()
+        
+    def _close(self, event):
+        """
+        Close the dialog
+        """
+        self.Close(True)
+
+    def _draw_model(self, event):
+        """
+         on_close, update the model2d plot
+        """
+        pass
+
+    def get_context_menu(self, graph=None):
+        """
+        When the context menu of a plot is rendered, the 
+        get_context_menu method will be called to give you a 
+        chance to add a menu item to the context menu.
+        :param graph: the Graph object to which we attach the context menu
+        
+        :return: a list of menu items with call-back function
+        """
+        return []
+    
+    def set_schedule_full_draw(self, panel=None, func=None):
+        """
+        Set_schedule for full draw
+        """
+        # Not implemented
+        pass
+    
+    def set_schedule(self, schedule=False):
+        """
+        Set schedule for redraw
+        """
+        # Not implemented
+        pass
+        
+    def set_plot_unfocus(self):
+        """
+        Set_plot unfocus
+        """
+        # NOt implemented
+        pass
+
+    
+    
+class SLDplotpanel(PlotPanel):
+    """
+    Panel
+    """
+    def __init__(self, parent, axes=[], id=-1, color=None, dpi=None,
+                  **kwargs):
+        """
+        """
+        PlotPanel.__init__(self, parent, id=id, xtransform='x', ytransform='y', 
+                           color=color, dpi=dpi, 
+                           size=(_STATICBOX_WIDTH, PANEL_SIZE-100), **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()
+        self.axes_label = []
+        for idx in range(0, len(axes)):
+            self.axes_label.append(axes[idx])
+        
+    def add_image(self, plot):
+        """
+        Add image(Theory1D)
+        """
+        self.plots[plot.name] = plot
+        self.plots[plot.name].is_data = False
+        #init graph
+        #self.gaph = Graph()
+        #add plot
+        self.graph.add(plot)
+        #add axes
+        x1_label = self.axes_label[0]
+        self.graph.xaxis('\\rm{%s} '% x1_label, 'A')
+        self.graph.yaxis('\\rm{SLD} ', '10^{-6}A^{-2}')
+        #draw
+        self.graph.render(self)
+        self.subplot.figure.canvas.draw_idle()
+                
+        # For latter scale changes 
+        self.plots[plot.name].xaxis('\\rm{%s} '% x1_label, 'A')
+        self.plots[plot.name].yaxis('\\rm{SLD} ', '10^{-6}A^{-2}')
+        
+    def on_set_focus(self, event):
+        """
+        send to the parenet the current panel on focus
+        
+        """
+        #Not implemented
+        pass
+
+    def on_kill_focus(self, event):
+        """
+        reset the panel color
+        
+        """
+        #Not implemented
+        pass
+    
+    def _add_more_tool(self):
+        """
+        Not implemented
+        """
+        pass 
+        
+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(_STATICBOX_WIDTH, PANEL_SIZE))
+        # Panel for 1D plot
+        self.plotpanel    = SLDplotpanel(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()     
Index: guiframe/local_perspectives/plotting/sectorMask.py
===================================================================
--- guiframe/local_perspectives/plotting/sectorMask.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
+++ guiframe/local_perspectives/plotting/sectorMask.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
@@ -0,0 +1,220 @@
+
+import math
+import wx
+#from copy import deepcopy
+from BaseInteractor import _BaseInteractor
+from SectorSlicer import SideInteractor
+from SectorSlicer import LineInteractor
+from sans.guiframe.events import SlicerParameterEvent
+
+class SectorMask(_BaseInteractor):
+    """
+    Draw a sector slicer.Allow to find the data 2D inside of the sector lines
+    """
+    def __init__(self, base, axes, color='gray', zorder=3, side=False):
+        """
+        """
+        _BaseInteractor.__init__(self, base, axes, color=color)
+        ## Class initialization
+        self.markers = []
+        self.axes = axes   
+        self.is_inside = side
+        ## connect the plot to event 
+        self.connect = self.base.connect
+        
+        ## compute qmax limit to reset the graph     
+        x = math.pow(max(self.base.data.xmax, 
+                         math.fabs(self.base.data.xmin)), 2)
+        y = math.pow(max(self.base.data.ymax, 
+                         math.fabs(self.base.data.ymin)), 2)
+        self.qmax = math.sqrt(x + y)
+        ## Number of points on the plot
+        self.nbins = 20
+        ## Angle of the middle line
+        self.theta2 = math.pi/3
+        ## Absolute value of the Angle between the middle line and any side line
+        self.phi = math.pi/12
+        
+        ## Middle line
+        self.main_line = LineInteractor(self, self.base.subplot, color='blue',
+                                zorder=zorder, r=self.qmax, theta=self.theta2)
+        self.main_line.qmax = self.qmax
+        ## Right Side line
+        self.right_line = SideInteractor(self, self.base.subplot, color='gray',
+                            zorder=zorder, r=self.qmax, phi= -1*self.phi,
+                                                            theta2=self.theta2)
+        self.right_line.qmax = self.qmax
+        ## Left Side line 
+        self.left_line = SideInteractor(self, self.base.subplot, color='gray', 
+                                    zorder=zorder, r=self.qmax, phi= self.phi,
+                                                        theta2=self.theta2)
+        self.left_line.qmax = self.qmax
+        ## draw the sector               
+        self.update()
+        self._post_data()
+        
+    def clear(self):
+        """
+        Clear the slicer and all connected events related to this slicer
+        """
+        self.clear_markers()
+        self.main_line.clear()
+        self.left_line.clear()
+        self.right_line.clear()
+        self.base.connect.clearall()
+        #self.base.Unbind(EVT_SLICER_PARS)
+        
+    def update(self):
+        """
+        Respond to changes in the model by recalculating the profiles and
+        resetting the widgets.
+        """
+        # Update locations  
+        ## Check if the middle line was dragged and 
+        #update the picture accordingly     
+        if self.main_line.has_move:
+            self.main_line.update()
+            self.right_line.update(delta=-self.left_line.phi/2,
+                                    mline=self.main_line.theta)
+            self.left_line.update(delta=self.left_line.phi/2,
+                                   mline=self.main_line.theta)
+        ## Check if the left side has moved and update the slicer accordingly  
+        if self.left_line.has_move:
+            self.main_line.update()
+            self.left_line.update(phi=None, delta=None, mline=self.main_line ,
+                                  side=True, left=True )
+            self.right_line.update(phi=self.left_line.phi, delta=None,
+                                     mline=self.main_line, side=True,
+                                     left=False, right=True)
+        ## Check if the right side line has moved and 
+        #update the slicer accordingly
+        if self.right_line.has_move:
+            self.main_line.update()
+            self.right_line.update(phi=None, delta=None, mline=self.main_line,
+                                   side=True, left=False, right=True)
+            self.left_line.update(phi=self.right_line.phi, delta=None,
+                                    mline=self.main_line, side=True, left=False)
+        #if self.is_inside != None:
+        out = self._post_data()
+        return out
+
+    def save(self, ev):
+        """
+        Remember the roughness for this layer and the next so that we
+        can restore on Esc.
+        """
+        self.base.freeze_axes()
+        self.main_line.save(ev)
+        self.right_line.save(ev)
+        self.left_line.save(ev)
+
+    def _post_data(self):
+        """
+        compute sector averaging of data into data1D
+        """
+        ## get the data to average
+        data = self.base.data
+        # If we have no data, just return
+        if data == None:
+            return
+
+        mask = data.mask
+        ## Averaging
+        from DataLoader.manipulations import Sectorcut
+        radius = self.qmax 
+        phimin =  -self.left_line.phi + self.main_line.theta
+        phimax = self.left_line.phi + self.main_line.theta
+         
+        mask = Sectorcut(phi_min=phimin, phi_max=phimax)
+        if self.is_inside:
+            out = (mask(data) == False)
+        else:
+            out = (mask(data))
+        #self.base.data.mask=out
+        return out       
+
+    def moveend(self, ev):
+        """
+        Called a dragging motion ends.Get slicer event 
+        """
+        self.base.thaw_axes()
+        ## Post parameters
+        event = SlicerParameterEvent()
+        event.type = self.__class__.__name__
+        event.params = self.get_params()
+        ## Send slicer paramers to plotter2D
+        wx.PostEvent(self.base, event)
+        self._post_data()
+            
+    def restore(self):
+        """
+        Restore the roughness for this layer.
+        """
+        self.main_line.restore()
+        self.left_line.restore()
+        self.right_line.restore()
+
+    def move(self, x, y, ev):
+        """
+        Process move to a new position, making sure that the move is allowed.
+        """
+        pass
+        
+    def set_cursor(self, x, y):
+        pass
+        
+    def get_params(self):
+        """
+        Store a copy of values of parameters of the slicer into a dictionary.
+        
+        :return params: the dictionary created
+        
+        """
+        params = {}
+        ## Always make sure that the left and the right line are at phi 
+        ## angle of the middle line
+        if math.fabs(self.left_line.phi) != math.fabs(self.right_line.phi):
+            msg = "Phi left and phi right are "
+            msg += "different %f, %f" % (self.left_line.phi, 
+                                         self.right_line.phi)
+            raise ValueError, msg
+        params["Phi"] = self.main_line.theta
+        params["Delta_Phi"] = math.fabs(self.left_line.phi)
+        return params
+    
+    def set_params(self, params):
+        """
+        Receive a dictionary and reset the slicer with values contained 
+        in the values of the dictionary.
+        
+        :param params: a dictionary containing name of slicer parameters and 
+            values the user assigned to the slicer.
+        """
+        main = params["Phi"] 
+        phi = math.fabs(params["Delta_Phi"])
+        
+        self.main_line.theta = main
+        ## Reset the slicer parameters
+        self.main_line.update()
+        self.right_line.update(phi=phi, delta=None, mline=self.main_line,
+                               side=True, right=True)
+        self.left_line.update(phi=phi, delta=None,
+                              mline=self.main_line, side=True)
+        ## post the new corresponding data
+        self._post_data()
+        
+    def freeze_axes(self):
+        """
+        """
+        self.base.freeze_axes()
+        
+    def thaw_axes(self):
+        """
+        """
+        self.base.thaw_axes()
+
+    def draw(self):
+        """
+        """
+        self.base.update()
+        
Index: guiframe/local_perspectives/plotting/slicerpanel.py
===================================================================
--- guiframe/local_perspectives/plotting/slicerpanel.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
+++ guiframe/local_perspectives/plotting/slicerpanel.py	(revision 0d86fecbbabb4912834c8b42b26742648403b3ff)
@@ -0,0 +1,164 @@
+
+
+import wx
+import wx.lib.newevent
+#from copy import deepcopy
+from sans.guiframe.utils import format_number
+from sans.guiframe.events import SlicerParameterEvent
+from sans.guiframe.events import EVT_SLICER_PARS
+from sans.guiframe.events import EVT_SLICER
+
+from sans.guiframe.panel_base import PanelBase
+
+class SlicerPanel(wx.Panel, PanelBase):
+    """
+    Panel class to show the slicer parameters 
+    """
+    #TODO: show units
+    #TODO: order parameters properly
+     ## Internal name for the AUI manager
+    window_name = "Slicer panel"
+    ## Title to appear on top of the window
+    window_caption = "Slicer Panel"
+    CENTER_PANE = False
+    
+    def __init__(self, parent, id=-1, type=None, base=None,
+                params=None, *args, **kwargs):
+        wx.Panel.__init__(self, parent, id, *args, **kwargs)
+        PanelBase.__init__(self)
+        ##  Initialization of the class     
+        self.base = base
+        if params is None:
+            params = {}
+        self.params = params
+        self.parent = parent
+        self.type = type
+        self.listeners = []
+        self.parameters = []
+        self.bck = wx.GridBagSizer(5, 5)
+        self.SetSizer(self.bck)
+        if type == None and params == None:  
+            label = "Right-click on 2D plot for slicer options"  
+            title = wx.StaticText(self, -1, label, style=wx.ALIGN_LEFT)
+            self.bck.Add(title, (0, 0), (1, 2), 
+                         flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border=15)
+        else:
+            self.set_slicer(type, params)
+        ## Bindings
+        self.parent.Bind(EVT_SLICER, self.onEVT_SLICER)
+        self.parent.Bind(EVT_SLICER_PARS, self.onParamChange)
+
+    def onEVT_SLICER(self, event):
+        """
+        Process EVT_SLICER events
+        When the slicer changes, update the panel
+        
+        :param event: EVT_SLICER event
+        
+        """
+        event.Skip()
+        if event.obj_class == None:
+            self.set_slicer(None, None) 
+        else:
+            self.set_slicer(event.type, event.params)
+        
+    def set_slicer(self, type, params):
+        """
+        Rebuild the panel
+        """
+        self.bck.Clear(True)  
+        self.type = type  
+        if type == None:
+            label = "Right-click on 2D plot for slicer options"
+            title = wx.StaticText(self, -1, label, style=wx.ALIGN_LEFT)
+            self.bck.Add(title, (0, 0), (1, 2), 
+                         flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border=15)
+        else:
+            title = wx.StaticText(self, -1, "Slicer Parameters", 
+                                  style=wx.ALIGN_LEFT)
+            self.bck.Add(title, (0, 0), (1, 2), 
+                         flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border=15)
+            n = 1
+            self.parameters = []
+            keys = params.keys()
+            keys.sort()
+            for item in keys:
+                if not item.lower() in ["errors", "count"]:
+                    n += 1
+                    text = wx.StaticText(self, -1, item, style=wx.ALIGN_LEFT)
+                    self.bck.Add(text, (n-1, 0), 
+                            flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL, border=15)
+                    ctl = wx.TextCtrl(self, -1, size=(80, 20), 
+                                      style=wx.TE_PROCESS_ENTER)
+                    hint_msg = "Modify the value of %s to change " % item
+                    hint_msg += "the 2D slicer"
+                    ctl.SetToolTipString(hint_msg)
+                    ctl.SetValue(str(format_number(params[item])))
+                    self.Bind(wx.EVT_TEXT_ENTER, self.onTextEnter)
+                    ctl.Bind(wx.EVT_SET_FOCUS, self.onSetFocus)
+                    ctl.Bind(wx.EVT_KILL_FOCUS, self.onTextEnter)
+                    self.parameters.append([item, ctl])
+                    self.bck.Add(ctl, (n-1, 1), flag=wx.TOP|wx.BOTTOM, border=0)
+            for item in keys:
+                if  item.lower() in ["errors", "count"]:
+                    n += 1
+                    text = wx.StaticText(self, -1, item + ": ", 
+                                         style=wx.ALIGN_LEFT)
+                    self.bck.Add(text, (n-1, 0), 
+                                 flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL, 
+                                 border=15)
+                    ctl = wx.StaticText(self, -1, 
+                                        str(format_number(params[item])),
+                                        style=wx.ALIGN_LEFT)
+                    ctl.SetToolTipString("Result %s" % item)
+                    self.bck.Add(ctl, (n-1, 1), flag=wx.TOP|wx.BOTTOM, border=0)       
+        self.bck.Layout()
+        #self.bck.Fit(self)
+        self.Layout()
+        self.parent.GetSizer().Layout()
+        
+    def onSetFocus(self, evt):
+        """
+        Hightlight the textcrtl
+        """
+        # Get a handle to the TextCtrl
+        widget = evt.GetEventObject()
+        # Select the whole control, after this event resolves
+        wx.CallAfter(widget.SetSelection, -1, -1)
+        return
+    
+    def onParamChange(self, evt):
+        """
+        Receive and event and reset the text field contained in self.parameters
+            
+        """
+        evt.Skip()
+        for item in self.parameters:              
+            if item[0] in evt.params:
+                item[1].SetValue(format_number(evt.params[item[0]]))
+                item[1].Refresh()
+   
+    def onTextEnter(self, evt): 
+        """
+        Parameters have changed
+        """ 
+        params = {}
+        has_error = False
+        for item in self.parameters:
+            try:
+                params[item[0]] = float(item[1].GetValue())
+                item[1].SetBackgroundColour(
+                        wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
+                item[1].Refresh()
+            except:
+                has_error = True
+                item[1].SetBackgroundColour("pink")
+                item[1].Refresh()
+            
+        if has_error == False:
+            # Post parameter event
+            ## base is guiframe is this case
+            event = SlicerParameterEvent(type=self.type, params=params)
+            wx.PostEvent(self.base, event)
+            
+        
