Index: src/sas/guiframe/local_perspectives/data_loader/__init__.py
===================================================================
--- src/sas/guiframe/local_perspectives/data_loader/__init__.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/guiframe/local_perspectives/data_loader/__init__.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,2 @@
+PLUGIN_ID = "DataLoader plug-in 1.0"
+from sas.guiframe.local_perspectives.data_loader import *
Index: src/sas/guiframe/local_perspectives/data_loader/data_loader.py
===================================================================
--- src/sas/guiframe/local_perspectives/data_loader/data_loader.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/guiframe/local_perspectives/data_loader/data_loader.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,250 @@
+
+"""
+plugin DataLoader responsible of loading data
+"""
+import os
+import sys
+import wx
+import logging
+
+from sas.dataloader.loader import Loader
+import sas.dataloader.data_info as DataInfo
+from sas.guiframe.plugin_base import PluginBase
+from sas.guiframe.events import StatusEvent
+from sas.guiframe.events import NewPlotEvent
+from sas.guiframe.dataFitting import Data1D
+from sas.guiframe.dataFitting import Data2D
+from sas.guiframe.utils import parse_name
+from sas.guiframe.gui_style import GUIFRAME
+from sas.guiframe.gui_manager import DEFAULT_OPEN_FOLDER
+try:
+    # Try to find a local config
+    import imp
+    path = os.getcwd()
+    if(os.path.isfile("%s/%s.py" % (path, 'local_config'))) or \
+        (os.path.isfile("%s/%s.pyc" % (path, 'local_config'))):
+        fObj, path, descr = imp.find_module('local_config', [path])
+        config = imp.load_module('local_config', fObj, path, descr)  
+    else:
+        # Try simply importing local_config
+        import local_config as config
+except:
+    # Didn't find local config, load the default 
+import sas.guiframe.config as config
+
+if config is None:
+import sas.guiframe.config as config
+    
+       
+extension_list = []
+if config.APPLICATION_STATE_EXTENSION is not None:
+    extension_list.append(config.APPLICATION_STATE_EXTENSION)
+EXTENSIONS = config.PLUGIN_STATE_EXTENSIONS + extension_list   
+PLUGINS_WLIST = config.PLUGINS_WLIST
+APPLICATION_WLIST = config.APPLICATION_WLIST
+
+class Plugin(PluginBase):
+    
+    def __init__(self, standalone=False):
+        PluginBase.__init__(self, name="DataLoader", standalone=standalone)
+        #Default location
+        self._default_save_location = DEFAULT_OPEN_FOLDER
+        self.loader = Loader()  
+        self._data_menu = None 
+        
+    def help(self, evt):
+        """
+        Show a general help dialog. 
+        """
+        from help_panel import  HelpWindow
+        frame = HelpWindow(None, -1) 
+        if hasattr(frame, "IsIconized"):
+            if not frame.IsIconized():
+                try:
+                    icon = self.parent.GetIcon()
+                    frame.SetIcon(icon)
+                except:
+                    pass  
+        frame.Show(True)
+        
+    def populate_file_menu(self):
+        """
+        get a menu item and append it under file menu of the application
+        add load file menu item and load folder item
+        """
+        #menu for data files
+        menu_list = []
+        data_file_hint = "load one or more data in the application"
+        menu_list = [('&Load Data File(s)', data_file_hint, self.load_data)]
+        gui_style = self.parent.get_style()
+        style = gui_style & GUIFRAME.MULTIPLE_APPLICATIONS
+        style1 = gui_style & GUIFRAME.DATALOADER_ON
+        if style == GUIFRAME.MULTIPLE_APPLICATIONS:
+            #menu for data from folder
+            data_folder_hint = "load multiple data in the application"
+            menu_list.append(('&Load Data Folder', data_folder_hint, 
+                              self._load_folder))
+        return menu_list
+   
+
+    def load_data(self, event):
+        """
+        Load data
+        """
+        path = None
+        self._default_save_location = self.parent._default_save_location
+        if self._default_save_location == None:
+            self._default_save_location = os.getcwd()
+        
+        cards = self.loader.get_wildcards()
+        temp = [APPLICATION_WLIST] + PLUGINS_WLIST
+        for item in temp:
+            if item in cards:
+                cards.remove(item)
+        wlist =  '|'.join(cards)
+        style = wx.OPEN|wx.FD_MULTIPLE
+        dlg = wx.FileDialog(self.parent, 
+                            "Choose a file", 
+                            self._default_save_location, "",
+                             wlist,
+                             style=style)
+        if dlg.ShowModal() == wx.ID_OK:
+            file_list = dlg.GetPaths()
+            if len(file_list) >= 0 and not(file_list[0]is None):
+                self._default_save_location = os.path.dirname(file_list[0])
+                path = self._default_save_location
+        dlg.Destroy()
+        
+        if path is None or not file_list or file_list[0] is None:
+            return
+        self.parent._default_save_location = self._default_save_location
+        self.get_data(file_list)
+        
+        
+    def can_load_data(self):
+        """
+        if return True, then call handler to laod data
+        """
+        return True
+ 
+       
+    def _load_folder(self, event):
+        """
+        Load entire folder
+        """
+        path = None
+        self._default_save_location = self.parent._default_save_location
+        if self._default_save_location == None:
+            self._default_save_location = os.getcwd()
+        dlg = wx.DirDialog(self.parent, "Choose a directory", 
+                           self._default_save_location,
+                            style=wx.DD_DEFAULT_STYLE)
+        if dlg.ShowModal() == wx.ID_OK:
+            path = dlg.GetPath()
+            self._default_save_location = path
+        dlg.Destroy()
+        if path is not None:
+            self._default_save_location = os.path.dirname(path)
+        else:
+            return    
+        file_list = self.get_file_path(path)
+        self.get_data(file_list)
+        self.parent._default_save_location = self._default_save_location
+        
+    def load_error(self, error=None):
+        """
+        Pop up an error message.
+        
+        :param error: details error message to be displayed
+        """
+        if error is not None or str(error).strip() != "":
+            dial = wx.MessageDialog(self.parent, str(error), 'Error Loading File',
+                                wx.OK | wx.ICON_EXCLAMATION)
+            dial.ShowModal()  
+        
+    def get_file_path(self, path):
+        """
+        Receive a list containing folder then return a list of file
+        """
+        if os.path.isdir(path):
+            return [os.path.join(os.path.abspath(path),
+                                  file) for file in os.listdir(path)]
+   
+    def get_data(self, path, format=None):
+        """
+        """
+        message = ""
+        log_msg = ''
+        output = {}
+        any_error = False
+        error_message = ""
+        for p_file in path:
+            info = "info"
+            basename  = os.path.basename(p_file)
+            root, extension = os.path.splitext(basename)
+            if extension.lower() in EXTENSIONS:
+                any_error = True
+                log_msg = "Data Loader cannot "
+                log_msg += "load: %s\n" % str(p_file)
+                log_msg += """Please try to open that file from "open project" """
+                log_msg += """or "open analysis" menu\n"""
+                error_message = log_msg + "\n"
+                logging.info(log_msg)
+                continue
+        
+            try:
+                temp =  self.loader.load(p_file, format)
+                if temp.__class__.__name__ == "list":
+                    for item in temp:
+                        data = self.parent.create_gui_data(item, p_file)
+                        output[data.id] = data
+                else:
+                    data = self.parent.create_gui_data(temp, p_file)
+                    output[data.id] = data
+                message = "Loading Data..." + str(p_file) + "\n"
+                self.load_update(output=output, message=message, info=info)
+            except:
+                any_error = True
+                if error_message == "":
+                     error = "Error: " + str(sys.exc_value) + "\n"
+                     error += "while loading Data: \n%s\n" % str(p_file)
+                     error_message = "The data file you selected could not be loaded.\n"
+                     error_message += "Make sure the content of your file"
+                     error_message += " is properly formatted.\n\n"
+                     error_message += "When contacting the DANSE team, mention the"
+                     error_message += " following:\n%s" % str(error)
+                else:
+                     error_message += "%s\n"% str(p_file)
+                info = "error"
+                self.load_update(output=output, message=error_message, 
+                                  info=info)
+                
+        message = "Loading Data Complete! "
+        message += log_msg
+        if error_message != "":
+            info = 'error'
+        self.load_complete(output=output, error_message=error_message,
+                       message=message, path=path, info=info)
+            
+    def load_update(self, output=None, message="", info="warning"):
+        """
+        print update on the status bar
+        """
+        if message != "":
+            wx.PostEvent(self.parent, StatusEvent(status=message, info='info', 
+                                                  type="progress"))
+    def load_complete(self, output, message="", error_message="", path=None, 
+                      info="warning"):
+        """
+         post message to  status bar and return list of data
+        """
+        wx.PostEvent(self.parent, StatusEvent(status=message, 
+                                              info=info,
+                                              type="stop"))
+        #if error_message != "":
+        #    self.load_error(error_message)
+        self.parent.add_data(data_list=output)
+   
+    
+        
+   
Index: src/sas/guiframe/local_perspectives/data_loader/help_panel.py
===================================================================
--- src/sas/guiframe/local_perspectives/data_loader/help_panel.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/guiframe/local_perspectives/data_loader/help_panel.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,106 @@
+#!/usr/bin/python
+"""
+Help panel for data_loader plugins
+"""
+import os
+import wx
+import wx.html as html
+from wx.lib.splitter import MultiSplitterWindow
+from sas.guiframe import get_media_path
+
+class HelpWindow(wx.Frame):
+    """
+    """
+    def __init__(self, parent, title='DataLoader Help', pageToOpen=None):
+        wx.Frame.__init__(self, parent, title, size=(700, 450))
+        """
+             contains help info
+        """
+        self.SetTitle('DataLoader Help') 
+        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 ="data_explorer_help.html" 
+            target ="showframe">Data Explorer</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: src/sas/guiframe/local_perspectives/data_loader/load_thread.py
===================================================================
--- src/sas/guiframe/local_perspectives/data_loader/load_thread.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/guiframe/local_perspectives/data_loader/load_thread.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,85 @@
+
+import time
+import sys
+import os
+
+from sas.data_util.calcthread import CalcThread
+
+        
+EXTENSIONS = ['.svs', '.prv', '.inv', '.fitv']
+
+class DataReader(CalcThread):
+    """
+    Load a data given a filename
+    """
+    def __init__(self, path, loader,
+                 flag=True,
+                 transform_data=None,
+                 completefn=None,
+                 updatefn   = None,
+                 yieldtime  = 0.01,
+                 worktime   = 0.01
+                 ):
+        CalcThread.__init__(self, completefn,
+                 updatefn,
+                 yieldtime,
+                 worktime)
+        self.load_state_flag = flag
+        self.transform_data = transform_data
+        self.list_path = path
+        #Instantiate a loader 
+        self.loader = loader
+        self.message = ""
+        self.starttime = 0  
+        self.updatefn = updatefn
+        
+    def isquit(self):
+        """
+        :raise KeyboardInterrupt: when the thread is interrupted
+        """
+        try:
+            CalcThread.isquit(self)
+        except KeyboardInterrupt:
+            raise KeyboardInterrupt   
+        
+        
+    def compute(self):
+        """
+        read some data
+        """
+        self.starttime = time.time()
+        output = []
+        error_message = ""
+        for path in self.list_path:
+            basename  = os.path.basename(path)
+            root, extension = os.path.splitext(basename)
+            if self.load_state_flag:
+                if extension.lower() in EXTENSIONS:
+                    pass
+            else:
+                if extension.lower() not in EXTENSIONS:
+                    pass
+            try:
+                temp =  self.loader.load(path)
+                elapsed = time.time() - self.starttime
+                if temp.__class__.__name__ == "list":
+                    for item in temp:
+                        data = self.transform_data(item, path)
+                        output.append(data)
+                else:
+                    data = self.transform_data(temp, path)
+                    output.append(data)
+                message = "Loading ..." + str(path) + "\n"
+                if self.updatefn is not None:
+                    self.updatefn(output=output, message=message)
+            except:
+                error_message = "Error while loading: %s\n" % str(path)
+                error_message += str(sys.exc_value) + "\n"
+                self.updatefn(output=output, message=error_message)
+                
+        message = "Loading Complete!"
+        self.complete(output=output, error_message=error_message,
+                       message=message, path=self.list_path)
+            
+   
+      
Index: src/sas/guiframe/local_perspectives/plotting/AnnulusSlicer.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/AnnulusSlicer.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/guiframe/local_perspectives/plotting/AnnulusSlicer.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,613 @@
+#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 sas.guiframe.events import NewPlotEvent
+from sas.guiframe.events import StatusEvent
+from sas.guiframe.events import SlicerParameterEvent
+from sas.guiframe.events import EVT_SLICER_PARS
+from BaseInteractor import _BaseInteractor
+from sas.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 sas.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 = "AnnulusPhi" + self.base.data2D.name
+        new_plot.is_data= True
+        new_plot.xtransform = "x"
+        new_plot.ytransform = "y"
+        self.base.parent.update_theory(data_id=data.id, theory=new_plot)
+        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 sas.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: src/sas/guiframe/local_perspectives/plotting/Arc.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/Arc.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/guiframe/local_perspectives/plotting/Arc.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,168 @@
+"""
+    Arc slicer for 2D data
+"""
+import math
+
+from BaseInteractor import _BaseInteractor
+from sas.guiframe.events import SlicerParameterEvent
+ 
+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):
+        """
+            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:
+            for item in self.markers:
+                item.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):
+        """
+            Return arc radius
+        """
+        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):
+        """
+            Update the plotted arc
+            :param theta1: starting angle of the arc
+            :param theta2: ending angle of the arc
+            :param nbins: number of points along the arc
+            :param r: radius of the arc
+        """
+        # Plot inner circle
+        x = []
+        y = []
+        if theta1 != None:
+            self.theta1 = theta1
+        if theta2 != None:
+            self.theta2 = 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):
+        """
+            After a dragging motion reset the flag self.has_move to False
+            :param ev: event
+        """
+        self.has_move = False
+        
+        event = SlicerParameterEvent()
+        event.type = self.__class__.__name__
+        event.params = self.get_params()
+        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"] 
+        phi_max = self.theta2
+        nbins = self.npts
+        self.set_cursor(x, self._mouse_y, phi_max, nbins)
+        
+    
Index: src/sas/guiframe/local_perspectives/plotting/AzimutSlicer.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/AzimutSlicer.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/guiframe/local_perspectives/plotting/AzimutSlicer.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,303 @@
+#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 sas.guiframe.events import NewPlotEvent
+from sas.guiframe.events import StatusEvent
+from sas.guiframe.events import SlicerParameterEvent
+from sas.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 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 sas.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 sas.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
+        self.base.parent.update_theory(data_id=self.base.data2D.id, \
+                                       theory=new_plot)
+        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 sas.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 sas.dataloader.manipulations import SectorPhi
+        self.post_data(SectorPhi )   
+        
+        
Index: src/sas/guiframe/local_perspectives/plotting/BaseInteractor.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/BaseInteractor.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/guiframe/local_perspectives/plotting/BaseInteractor.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -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: src/sas/guiframe/local_perspectives/plotting/Edge.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/Edge.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/guiframe/local_perspectives/plotting/Edge.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,143 @@
+
+import math
+#import wx  
+#from copy import deepcopy
+from BaseInteractor import _BaseInteractor
+#from sas.guiframe.events import NewPlotEvent
+#from sas.guiframe.events import StatusEvent
+#from sas.guiframe.events import SlicerParameterEvent
+#from sas.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: src/sas/guiframe/local_perspectives/plotting/Plotter1D.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/Plotter1D.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/guiframe/local_perspectives/plotting/Plotter1D.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,884 @@
+
+################################################################################
+#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 math
+import numpy
+
+from sas.plottools.PlotPanel import PlotPanel
+from sas.guiframe.events import StatusEvent 
+from sas.guiframe.events import PanelOnFocusEvent
+from sas.guiframe.utils import PanelMenu
+from sas.guiframe.panel_base import PanelBase
+from sas.guiframe.gui_style import GUIFRAME_ICON
+from appearanceDialog import appearanceDialog
+from graphAppearance import graphAppearance
+
+DEFAULT_QMAX = 0.05
+DEFAULT_QSTEP = 0.001
+DEFAULT_BEAM = 0.005
+BIN_WIDTH = 1
+IS_MAC = (sys.platform == 'darwin')
+
+
+def find_key(dic, val):
+    """return the key of dictionary dic given the value"""
+    return [k for k, v in dic.iteritems() if v == val][0]
+
+
+
+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 = "Graph"
+    ## 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
+        if hasattr(parent, "parent"):
+            self.parent = self.parent.parent
+        ## Plottables
+        self.plots = {}
+        self.frame = None
+        #context menu
+        self._slicerpop = None
+        
+        self._available_data = []
+        self._menu_add_ids = []
+        self._symbol_labels = self.get_symbol_label()
+        self._color_labels = self.get_color_label()
+        self.currColorIndex = ""
+        self._is_changed_legend_label = False
+        self.is_xtick = False
+        self.is_ytick = False
+      
+        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  
+        self.vl_ind = 0     
+        ## Graph        
+        #self.graph = Graph()
+        self.graph.xaxis("\\rm{Q}", 'A^{-1}')
+        self.graph.yaxis("\\rm{Intensity} ", "cm^{-1}")
+        self.graph.render(self)
+        self.cursor_id = None
+        
+        # In resizing event
+        self.resizing = False
+        self.canvas.set_resizing(self.resizing)
+        self.Bind(wx.EVT_SIZE, self._OnReSize)
+        self._add_more_tool()
+        self.parent.SetFocus()
+        
+        
+    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
+        i += 1
+        _labels['Dash'] = i
+        i += 1
+        _labels['Vline'] = i
+        i += 1
+        _labels['Step'] = i
+        return _labels
+    
+    def get_color_label(self):
+        """
+        Associates label to a specific color
+        """
+        _labels = {}
+        i = 0
+        _labels['Blue'] = i
+        i += 1
+        _labels['Green'] = i
+        i += 1
+        _labels['Red'] = i
+        i += 1
+        _labels['Cyan'] = i
+        i += 1
+        _labels['Magenta'] = i
+        i += 1
+        _labels['Yellow'] = i
+        i += 1
+        _labels['Black'] = i
+        return _labels
+
+    
+    def set_data(self, list=None):
+        """
+        """
+        pass
+    
+    def _reset(self):
+        """
+        Resets internal data and graph
+        """    
+        self.graph.reset()
+        self.plots      = {}
+        self.is_zoomed = False
+        
+    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.
+        # Massy code to work around:<
+        if self.parent._mgr != None:
+            max_panel = self.parent._mgr.GetPane(self)
+            if max_panel.IsMaximized():
+                self.parent._mgr.RestorePane(max_panel)
+                max_panel.Maximize()
+        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()
+        self.SetSizer(self.sizer)
+        wx.CallAfter(self.parent.disable_app_menu,self)
+        
+    def on_plot_qrange(self, event=None):
+        """
+        On Qmin Qmax vertical line event
+        """
+        if event == None:
+            return
+        event.Skip() 
+        active_ctrl = event.active
+        if active_ctrl == None:
+            return
+        if event.id in self.plots.keys():
+            # Set line position and color
+            colors = ['red', 'purple']
+            self.cursor_id = event.id
+            ctrl = event.ctrl
+            if self.ly == None:
+                self.ly = []
+                for ind_ly in range(len(colors)):
+                    self.ly.append(self.subplot.axvline(color=colors[ind_ly], 
+                                                        lw=2.5, alpha=0.7))
+                    self.ly[ind_ly].set_rasterized(True)      
+            try:
+                # Display x,y in the status bar if possible
+                xval = float(active_ctrl.GetValue())
+                position = self.get_data_xy_vals(xval)
+                if position != None:
+                    wx.PostEvent(self.parent, StatusEvent(status=position))
+            except:
+                pass
+            if not event.leftdown:
+                # text event 
+                try:
+                    is_moved = False
+                    for idx in range(len(self.ly)):
+                        val = float(ctrl[idx].GetValue())
+                        # check if vline moved
+                        if self.ly[idx].get_xdata() != val:
+                            self.ly[idx].set_xdata(val)
+                            is_moved = True
+                    if is_moved:
+                        self.canvas.draw() 
+                except:
+                    pass
+                event.Skip() 
+                return
+            self.q_ctrl = ctrl
+            try:
+                pos_x_min = float(self.q_ctrl[0].GetValue())
+            except:
+                pos_x_min = xmin
+            try:
+                pos_x_max = float(self.q_ctrl[1].GetValue())
+            except:
+                pos_x_max = xmax
+            pos_x = [pos_x_min, pos_x_max]
+            for ind_ly in range(len(colors)):
+                self.ly[ind_ly].set_color(colors[ind_ly])
+                self.ly[ind_ly].set_xdata(pos_x[ind_ly])
+            self.canvas.draw()
+        else:
+            self.q_ctrl = None
+    
+    def get_data_xy_vals(self, xval):
+        """
+        Get x, y data values near x = x_val
+        """
+        try:
+            x_data = self.plots[self.cursor_id].x
+            y_data = self.plots[self.cursor_id].y
+            indx = self._find_nearest(x_data, xval)
+            pos_x = x_data[indx]
+            pos_y = y_data[indx]
+            position = str(pos_x), str(pos_y)
+            return position
+        except:
+            return None
+           
+    def _find_nearest(self, array, value):
+        """
+        Find and return the nearest value in array to the value.
+        Used in cusor_line()
+        :Param array: numpy array
+        :Param value: float
+        """
+        idx = (numpy.abs(array - value)).argmin()
+        return int(idx)#array.flat[idx]
+    
+    def _check_line_positions(self, pos_x=None, nop=None):
+        """
+        Check vertical line positions
+        :Param pos_x: position of the current line [float]
+        :Param nop: number of plots [int]
+        """
+        ly = self.ly
+        ly0x = ly[0].get_xdata()
+        ly1x = ly[1].get_xdata()
+        self.q_ctrl[0].SetBackgroundColour('white')
+        self.q_ctrl[1].SetBackgroundColour('white')
+        if ly0x >= ly1x:
+            if self.vl_ind == 0:
+                ly[1].set_xdata(pos_x)
+                ly[1].set_zorder(nop)
+                self.q_ctrl[1].SetValue(str(pos_x))
+                self.q_ctrl[0].SetBackgroundColour('pink')
+            elif self.vl_ind == 1:
+                ly[0].set_xdata(pos_x)
+                ly[0].set_zorder(nop)
+                self.q_ctrl[0].SetValue(str(pos_x))
+                self.q_ctrl[1].SetBackgroundColour('pink')
+                
+    def _get_cusor_lines(self, event):
+        """
+        Revmove or switch cursor line if drawn
+        :Param event: LeftClick mouse event
+        """  
+        ax = event.inaxes
+        if hasattr(event, "action"):
+            dclick = event.action == 'dclick'
+            if ax == None or dclick:
+                # remove the vline
+                self._check_zoom_plot()
+                self.canvas.draw()
+                self.q_ctrl = None
+                return 
+        if self.ly != None and event.xdata != None:
+            # Selecting a new line if cursor lines are displayed already
+            dqmin = math.fabs(event.xdata - self.ly[0].get_xdata())
+            dqmax = math.fabs(event.xdata - self.ly[1].get_xdata())
+            is_qmax = dqmin > dqmax
+            if is_qmax:
+                self.vl_ind = 1
+            else:
+                self.vl_ind = 0 
+                     
+    def cusor_line(self, event):
+        """
+        Move the cursor line to write Q range
+        """
+        if self.q_ctrl == None:
+            return
+        #release a q range vline
+        if self.ly != None and not self.leftdown:
+            for ly in self.ly:
+                ly.set_alpha(0.7)
+                self.canvas.draw()
+            return
+        ax = event.inaxes
+        if ax == None or not hasattr(event, 'action'):
+            return
+        end_drag = event.action != 'drag' and event.xdata != None
+        nop = len(self.plots)
+        pos_x, pos_y = float(event.xdata), float(event.ydata)
+        try:
+            ly = self.ly
+            ly0x = ly[0].get_xdata()
+            ly1x = ly[1].get_xdata()
+            if ly0x == ly1x:
+                if ly[0].get_zorder() > ly[1].get_zorder():
+                    self.vl_ind = 0
+                else:
+                    self.vl_ind = 1
+            vl_ind = self.vl_ind
+            x_data = self.plots[self.cursor_id].x
+            y_data = self.plots[self.cursor_id].y
+            xmin = x_data.min()
+            xmax = x_data.max()
+            indx = self._find_nearest(x_data, pos_x)
+            #pos_x = self._find_nearest(x_data, pos_x)
+            #indx = int(numpy.searchsorted(x_data, [pos_x])[0])
+            # Need to hold LeftButton to drag
+            if end_drag:
+                if event.button:
+                    self._check_line_positions(pos_x, nop)
+                return   
+            if indx >= len(x_data):
+                indx = len(x_data) - 1
+            pos_x = x_data[indx]
+            pos_y = y_data[indx]
+            if xmin == ly1x:
+                vl_ind = 1
+            elif xmax == ly0x:
+                vl_ind = 0
+            else:
+                ly[vl_ind].set_xdata(pos_x)
+                ly[vl_ind].set_zorder(nop + 1)
+                self._check_line_positions(pos_x, nop)
+            ly[vl_ind].set_xdata(pos_x)
+            ly[vl_ind].set_alpha(1.0)
+            ly[vl_ind].set_zorder(nop + 1)
+            self.canvas.draw()
+            self.q_ctrl[vl_ind].SetValue(str(pos_x))
+        except:
+            pass
+               
+    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.__class__.__name__ == 'Data2D':
+            return
+        plot_keys = self.plots.keys()
+        if data.id in plot_keys:
+            #Recover panel prop.s
+            xlo, xhi = self.subplot.get_xlim()
+            ylo, yhi = self.subplot.get_ylim()
+            old_data = self.plots[data.id]
+            if self._is_changed_legend_label:
+                data.label = old_data.label
+            if old_data.__class__.__name__ == 'Data1D':
+                data.custom_color = old_data.custom_color
+                data.symbol = old_data.symbol
+                data.markersize = old_data.markersize
+                data.zorder = len(plot_keys)
+            # Replace data
+            self.graph.replace(data)
+            self.plots[data.id] = data
+            ## Set the view scale for all plots
+            try:
+                self._onEVT_FUNC_PROPERTY()
+            except Exception, exc:
+                wx.PostEvent(self.parent, StatusEvent(status=\
+                    "Plotting Error: %s"% str(exc), info="error"))
+            if self.is_zoomed:
+                # Recover the x,y limits
+                self.subplot.set_xlim((xlo, xhi))     
+                self.subplot.set_ylim((ylo, yhi))  
+        else:
+            self.plots[data.id] = data
+            self.graph.add(self.plots[data.id]) 
+            data.zorder = len(plot_keys)
+            ## Set the view scale for all plots
+            try:
+                self._onEVT_FUNC_PROPERTY()
+                if IS_MAC:
+                    # MAC: forcing to plot 2D avg
+                    self.canvas._onDrawIdle()
+            except Exception,exc:
+                wx.PostEvent(self.parent, StatusEvent(status=\
+                    "Plotting Error: %s"% str(exc), info="error"))
+            self.toolbar.update()
+            self.is_zoomed = False
+            
+    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
+        """
+        #self.parent.set_plot_unfocus() 
+        self._get_cusor_lines(event)
+        ax = event.inaxes
+        PlotPanel.onLeftDown(self, event)
+        if ax != None:
+            try:
+                pos_x = float(event.xdata)# / size_x
+                pos_y = float(event.ydata)# / size_y
+                pos_x = "%8.3g"% pos_x
+                pos_y = "%8.3g"% pos_y
+                self.position = str(pos_x), str(pos_y)
+                wx.PostEvent(self.parent, StatusEvent(status=self.position))
+            except:
+                self.position = None  
+        # 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
+        """
+        menu = event.GetEventObject()
+        id = event.GetId()
+        self.set_selected_from_menu(menu, id)
+        # Check zoom
+        xlo, xhi = self.subplot.get_xlim()
+        ylo, yhi = self.subplot.get_ylim()
+
+        selected_plot = self.plots[self.graph.selected_plottable]
+        if self.hide_menu.GetText() == "Hide Error Bar":
+            selected_plot.hide_error = True
+        else:
+            selected_plot.hide_error = False
+        ## increment graph color
+        self.graph.render(self)
+        self.subplot.figure.canvas.draw_idle()  
+        if self.is_zoomed:
+            # Recover the x,y limits
+            self.subplot.set_xlim((xlo, xhi))     
+            self.subplot.set_ylim((ylo, yhi)) 
+
+          
+    def _onRemove(self, event):
+        """
+        Remove a plottable from the graph and render the graph 
+        
+        :param event: Menu event
+        
+        """
+        menu = event.GetEventObject()
+        id = event.GetId()
+        self.set_selected_from_menu(menu, id)
+        ## 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)   
+        if not self.graph.selected_plottable in self.plots:  
+            # 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()
+
+        for plot in self.plots.values():
+            #title = plot.title
+            name = plot.name
+            plot_menu = wx.Menu()
+            if self.graph.selected_plottable:
+                if not self.graph.selected_plottable in self.plots.keys():
+                    continue
+                if plot != self.plots[self.graph.selected_plottable]:
+                    continue
+                
+            id = wx.NewId()
+            plot_menu.Append(id, "&DataInfo", name)
+            wx.EVT_MENU(self, id, self. _onDataShow)
+            id = wx.NewId()
+            plot_menu.Append(id, "&Save Points as a File", name)
+            wx.EVT_MENU(self, id, self._onSave)
+            plot_menu.AppendSeparator()
+            
+            #add menu of other plugins
+            item_list = self.parent.get_current_context_menu(self)
+              
+            if (not item_list == None) and (not len(item_list) == 0):
+                for item in item_list:
+
+                    try:
+                        id = wx.NewId()
+                        plot_menu.Append(id, item[0], name)
+                        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
+                plot_menu.AppendSeparator()
+            
+            if self.parent.ClassName.count('wxDialog') == 0: 
+                id = wx.NewId()
+                plot_menu.Append(id, '&Linear Fit', name)
+                wx.EVT_MENU(self, id, self.onFitting)
+                plot_menu.AppendSeparator()
+    
+                id = wx.NewId()
+                plot_menu.Append(id, "Remove", name)
+                wx.EVT_MENU(self, id, self._onRemove)
+                if not plot.is_data:
+                    id = wx.NewId()
+                    plot_menu.Append(id, '&Freeze', name)
+                    wx.EVT_MENU(self, id, self.onFreeze)
+                plot_menu.AppendSeparator()    
+                symbol_menu = wx.Menu()
+                
+                if plot.is_data:
+                    id = wx.NewId()
+                    self.hide_menu = plot_menu.Append(id, 
+                                                    "Hide Error Bar", name)
+        
+                    if plot.dy is not None and plot.dy != []:
+                        if plot.hide_error :
+                            self.hide_menu.SetText('Show Error Bar')
+                        else:
+                            self.hide_menu.SetText('Hide Error Bar')
+                    else:
+                        self.hide_menu.Enable(False)
+                    wx.EVT_MENU(self, id, self._ontoggle_hide_error)
+                
+                    plot_menu.AppendSeparator()
+
+                id = wx.NewId()
+                plot_menu.Append(id, '&Modify Plot Property', name)
+                wx.EVT_MENU(self, id, self.createAppDialog)
+
+
+
+            id = wx.NewId()
+            #plot_menu.SetTitle(name)
+            self._slicerpop.AppendMenu(id, '&%s'% name, plot_menu)
+            # Option to hide
+            #TODO: implement functionality to hide a plottable (legend click)
+        if not self.graph.selected_plottable in self.plots:  
+            self._slicerpop.AppendSeparator()
+            loc_menu = wx.Menu()
+            for label in self._loc_labels:
+                id = wx.NewId()
+                loc_menu.Append(id, str(label), str(label))
+                wx.EVT_MENU(self, id, self.onChangeLegendLoc)
+            
+            id = wx.NewId()
+            self._slicerpop.Append(id, '&Modify Graph Appearance',
+                                   'Modify graph appearance')
+            wx.EVT_MENU(self, id, self.modifyGraphAppearance)
+            self._slicerpop.AppendSeparator()
+
+            
+            if self.position != None:
+                id = wx.NewId()
+                self._slicerpop.Append(id, '&Add Text')
+                wx.EVT_MENU(self, id, self._on_addtext)
+                id = wx.NewId()
+                self._slicerpop.Append(id, '&Remove Text')
+                wx.EVT_MENU(self, id, self._on_removetext)
+                self._slicerpop.AppendSeparator()
+            id = wx.NewId()
+            self._slicerpop.Append(id, '&Change Scale')
+            wx.EVT_MENU(self, id, self._onProperties)
+            self._slicerpop.AppendSeparator()
+            id = wx.NewId()
+            self._slicerpop.Append(id, '&Reset Graph Range')
+            wx.EVT_MENU(self, id, self.onResetGraph)  
+            
+            if self.parent.ClassName.count('wxDialog') == 0:    
+                self._slicerpop.AppendSeparator()
+                id = wx.NewId()
+                self._slicerpop.Append(id, '&Window Title')
+                wx.EVT_MENU(self, id, self.onChangeCaption)
+        try:
+            pos_evt = event.GetPosition()
+            pos = self.ScreenToClient(pos_evt)
+        except:
+            pos_x, pos_y = self.toolbar.GetPositionTuple()
+            pos = (pos_x, pos_y + 5)
+        self.PopupMenu(self._slicerpop, pos)
+            
+    def onFreeze(self, event):
+        """
+        on Freeze data
+        """
+        menu = event.GetEventObject()
+        id = event.GetId()
+        self.set_selected_from_menu(menu, id)
+        plot = self.plots[self.graph.selected_plottable]
+        self.parent.onfreeze([plot.id])
+        
+                       
+    def _onSave(self, evt):
+        """
+        Save a data set to a text file
+        
+        :param evt: Menu event
+        
+        """
+        menu = evt.GetEventObject()
+        id = evt.GetId()
+        self.set_selected_from_menu(menu, id)
+        data = self.plots[self.graph.selected_plottable]
+        default_name = data.label
+        if default_name.count('.') > 0:
+            default_name = default_name.split('.')[0]
+        default_name += "_out"
+        if self.parent != None:
+            self.parent.save_data1d(data, default_name)
+
+                       
+    def _onDataShow(self, evt):
+        """
+        Show the data set in text
+        
+        :param evt: Menu event
+        
+        """
+        menu = evt.GetEventObject()
+        id = evt.GetId()
+        self.set_selected_from_menu(menu, id)
+        data = self.plots[self.graph.selected_plottable]
+        default_name = data.label
+        if default_name.count('.') > 0:
+            default_name = default_name.split('.')[0]
+        #default_name += "_out"
+        if self.parent != None:
+            self.parent.show_data1d(data, default_name)
+            
+    def _add_more_tool(self):
+        """
+        Add refresh, add/hide button in the tool bar
+        """
+        return
+        if self.parent.__class__.__name__ != 'ViewerFrame':
+            return
+        self.toolbar.AddSeparator()
+        id_hide = wx.NewId()
+        hide = wx.Bitmap(GUIFRAME_ICON.HIDE_ID_PATH, wx.BITMAP_TYPE_PNG)
+        self.toolbar.AddSimpleTool(id_hide, hide, 'Hide', 'Hide')
+        self.toolbar.Realize()
+        wx.EVT_TOOL(self, id_hide,  self._on_hide)
+        
+    def _on_hide(self, event):
+        """
+        Hides the plot when button is pressed
+        """     
+        if self.parent is not None:
+            self.parent.hide_panel(self.uid)
+
+    def on_close(self, event):
+        """
+        On Close Event
+        """
+        ID = self.uid
+        self.parent.delete_panel(ID)
+    
+    def createAppDialog(self, event):
+        """
+        Create the custom dialog for fit appearance modification
+        """
+        menu = event.GetEventObject()
+        id = event.GetId()
+        self.set_selected_from_menu(menu, id)
+        self.appearance_selected_plot = \
+                        self.plots[self.graph.selected_plottable]
+        # find current properties
+        curr_color = self.appearance_selected_plot.custom_color
+        curr_symbol = self.appearance_selected_plot.symbol
+        curr_size = self.appearance_selected_plot.markersize
+        curr_label = self.appearance_selected_plot.label
+
+        if curr_color == None:
+            curr_color = self._color_labels['Blue']
+            curr_symbol = 13
+
+        self.appD = appearanceDialog(self, 'Modify Plot Property')
+        icon = self.parent.GetIcon()
+        self.appD.SetIcon(icon)
+        self.appD.set_defaults(float(curr_size), int(curr_color), 
+                    str(appearanceDialog.find_key(self.get_symbol_label(), 
+                    int(curr_symbol))), curr_label)
+        self.appD.Bind(wx.EVT_CLOSE, self.on_AppDialog_close)    
+
+    def on_AppDialog_close(self, event):
+        """
+        on_Modify Plot Property_close
+        """
+        if(self.appD.okay_clicked == True):
+            # returns (size,color,symbol,datalabel)
+            info = self.appD.get_current_values() 
+            self.appearance_selected_plot.custom_color = \
+                        self._color_labels[info[1].encode('ascii', 'ignore')]
+
+            self.appearance_selected_plot.markersize = float(info[0])
+            self.appearance_selected_plot.symbol = \
+                        self.get_symbol_label()[info[2]] 
+            self.appearance_selected_plot.label = str(info[3])
+        self.appD.Destroy()
+        self._check_zoom_plot()
+
+    def modifyGraphAppearance(self, event):
+        """
+        On Modify Graph Appearance 
+        """
+        self.graphApp = graphAppearance(self, 'Modify Graph Appearance')
+        icon = self.parent.GetIcon()
+        self.graphApp.SetIcon(icon)
+        self.graphApp.setDefaults(self.grid_on, self.legend_on, 
+                                  self.xaxis_label, self.yaxis_label, 
+                                  self.xaxis_unit, self.yaxis_unit, 
+                                  self.xaxis_font, self.yaxis_font, 
+                                  find_key(self.get_loc_label(), 
+                                  self.legendLoc), 
+                                  self.xcolor, self.ycolor, 
+                                  self.is_xtick, self.is_ytick)
+        self.graphApp.Bind(wx.EVT_CLOSE, self.on_graphApp_close)
+
+    def on_graphApp_close(self, event):
+        """
+        Gets values from graph appearance dialog and sends them off
+        to modify the plot
+        """
+        graph_app = self.graphApp
+        toggle_grid = graph_app.get_togglegrid()
+        legend_loc = graph_app.get_legend_loc()
+        toggle_legend = graph_app.get_togglelegend()
+        
+        self.onGridOnOff(toggle_grid )
+        self.ChangeLegendLoc(legend_loc)
+        self.onLegend(toggle_legend)
+
+        self.xaxis_label = graph_app.get_xlab()
+        self.yaxis_label = graph_app.get_ylab()
+        self.xaxis_unit = graph_app.get_xunit()
+        self.yaxis_unit = graph_app.get_yunit()
+        self.xaxis_font = graph_app.get_xfont()
+        self.yaxis_font = graph_app.get_yfont()
+        self.is_xtick =  graph_app.get_xtick_check()
+        self.is_ytick =  graph_app.get_ytick_check()
+        if self.is_xtick:
+            self.xaxis_tick = self.xaxis_font
+        if self.is_ytick:
+            self.yaxis_tick = self.yaxis_font
+
+        self.xaxis(self.xaxis_label, self.xaxis_unit, 
+                   graph_app.get_xfont(), graph_app.get_xcolor(), 
+                   self.xaxis_tick)
+        self.yaxis(self.yaxis_label, self.yaxis_unit, 
+                   graph_app.get_yfont(), graph_app.get_ycolor(),
+                   self.yaxis_tick)
+
+        graph_app.Destroy()
Index: src/sas/guiframe/local_perspectives/plotting/Plotter2D.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/Plotter2D.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/guiframe/local_perspectives/plotting/Plotter2D.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,825 @@
+
+################################################################################
+#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 math
+import numpy
+from sas.plottools.PlotPanel import PlotPanel
+from sas.plottools.plottables import Graph
+from sas.plottools.TextDialog import TextDialog
+from sas.guiframe.events import StatusEvent 
+from sas.guiframe.events import NewPlotEvent
+from sas.guiframe.events import PanelOnFocusEvent
+from sas.guiframe.events import SlicerEvent
+from sas.guiframe.utils import PanelMenu
+from  sas.guiframe.local_perspectives.plotting.binder import BindArtist
+from Plotter1D import ModelPanel1D
+from sas.plottools.toolbar import NavigationToolBar 
+from matplotlib.font_manager import FontProperties
+from graphAppearance import graphAppearance
+(InternalEvent, EVT_INTERNAL) = wx.lib.newevent.NewEvent()
+
+DEFAULT_QMAX = 0.05
+DEFAULT_QSTEP = 0.001
+DEFAULT_BEAM = 0.005
+BIN_WIDTH = 1.0
+
+
+def find_key(dic, val):
+    """return the key of dictionary dic given the value"""
+    return [k for k, v in dic.iteritems() if v == val][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 button
+        id_context = wx.NewId()
+        context_tip = 'Graph Menu'
+        context =  wx.ArtProvider.GetBitmap(wx.ART_LIST_VIEW, wx.ART_TOOLBAR)
+        self.InsertSimpleTool(0, id_context, context, 
+                                   context_tip, context_tip)
+        wx.EVT_TOOL(self, id_context,  self.parent.onToolContextMenu)
+        self.InsertSeparator(1)
+        #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)
+        self.parent = parent
+        ## Reference to the parent window
+        if hasattr(parent, "parent"):
+            self.parent = 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
+        self.title_label = None
+        self.title_font = None
+        self.title_color = 'black'
+        ## 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 on_plot_qrange(self, event=None):
+        """
+        On Qmin Qmax vertical line event
+        """
+        # Not implemented
+        if event == None:
+            return
+        event.Skip() 
+                       
+    def onLeftDown(self, event): 
+        """
+        left button down and ready to drag
+        
+        """
+        # Check that the LEFT button was pressed
+        PlotPanel.onLeftDown(self, event)    
+        ax = event.inaxes
+        if ax != None:
+            # data coordinate position
+            pos_x = "%8.3g"% event.xdata
+            pos_y = "%8.3g"% event.ydata
+            position = "x: %s    y: %s" % (pos_x, pos_y)
+            wx.PostEvent(self.parent, StatusEvent(status=position))
+        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
+        """
+        xlo = None
+        xhi = None
+        ylo = None 
+        yhi = None
+        if data.__class__.__name__ == 'Data1D':
+            return
+        ## Update self.data2d with the current plot
+        self.data2D = data
+        if data.id in self.plots.keys():
+            #replace
+            xlo, xhi = self.subplot.get_xlim()
+            ylo, yhi = self.subplot.get_ylim()
+            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(data.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'
+        # graph properties
+        self.graph.xaxis(data._xaxis, data._xunit)
+        self.graph.yaxis(data._yaxis, data._yunit)
+        if self._is_changed_legend_label:   
+                data.label = self.title_label
+        if data.label == None:
+            data.label = data.name   
+        if not self.title_font:
+            self.graph.title(data.label)
+            self.graph.render(self)
+            # Set the axis labels on subplot
+            self._set_axis_labels()
+            self.draw_plot()
+        else:
+            self.graph.render(self)
+            self.draw_plot()
+            self.subplot.set_title(label=data.label,
+                                   fontproperties=self.title_font,
+                                   color=self.title_color)
+            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
+        if not self.is_zoomed:
+            return
+        # Recover the x,y limits
+        if (xlo and xhi and ylo and yhi) != None:
+            if (xlo > data.xmin and xhi < data.xmax and\
+                        ylo > data.ymin and yhi < data.ymax):
+                self.subplot.set_xlim((xlo, xhi))     
+                self.subplot.set_ylim((ylo, yhi)) 
+            else: 
+                self.toolbar.update()
+                self.is_zoomed = False
+
+    def _set_axis_labels(self):
+        """
+        Set axis labels
+        """
+        data = self.data2D
+        # control axis labels from the panel itself
+        yname, yunits = data.get_yaxis()
+        if self.yaxis_label != None:
+            yname = self.yaxis_label
+            yunits = self.yaxis_unit
+        else:
+            self.yaxis_label = yname
+            self.yaxis_unit = yunits
+        xname, xunits = data.get_xaxis()
+        if self.xaxis_label != None:
+            xname = self.xaxis_label
+            xunits = self.xaxis_unit
+        else:
+            self.xaxis_label = xname
+            self.xaxis_unit = xunits
+        self.xaxis(xname, xunits, self.xaxis_font, 
+                   self.xaxis_color, self.xaxis_tick)
+        self.yaxis(yname, yunits, self.yaxis_font, 
+                   self.yaxis_color, self.yaxis_tick)
+        
+    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, "&Data Info" )
+        wx.EVT_MENU(self, id, self._onDataShow)
+
+        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_current_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, '&Edit Graph Label', 'Edit Graph Label')
+        wx.EVT_MENU(self, id, self.onEditLabels)
+        slicerpop.AppendSeparator()
+        
+        # ILL mod here
+
+        id = wx.NewId()
+        slicerpop.Append(id, '&Modify graph appearance','Modify graph appearance')
+        wx.EVT_MENU(self, id, self.modifyGraphAppearance)
+        slicerpop.AppendSeparator()
+
+
+        
+        id = wx.NewId()
+        slicerpop.Append(id, '&2D Color Map')
+        wx.EVT_MENU(self, id, self._onEditDetector)
+        slicerpop.AppendSeparator()
+        
+        id = wx.NewId()
+        slicerpop.Append(id, '&Toggle Linear/Log Scale')
+        wx.EVT_MENU(self, id, self._onToggleScale) 
+        
+        
+        slicerpop.AppendSeparator()
+        id = wx.NewId()
+        slicerpop.Append(id, '&Window Title')
+        wx.EVT_MENU(self, id, self.onChangeCaption)
+        
+        try:
+            pos_evt = event.GetPosition()
+            pos = self.ScreenToClient(pos_evt)
+        except:
+            pos_x, pos_y = self.toolbar.GetPositionTuple()
+            pos = (pos_x, pos_y + 5)
+        self.PopupMenu(slicerpop, pos)
+        
+            
+    def onEditLabels(self, event):
+        """
+        Edit legend label
+        """
+        try:
+            selected_plot = self.plots[self.graph.selected_plottable]
+        except:
+            selected_plot = self.plots[self.data2D.id]
+        label = selected_plot.label
+        dial = TextDialog(None, -1, 'Change Label', label)
+        if dial.ShowModal() == wx.ID_OK:
+            try:
+                FONT = FontProperties()
+                newlabel = dial.getText()
+                font = FONT.copy()
+                font.set_size(dial.getSize())
+                font.set_family(dial.getFamily())
+                font.set_style(dial.getStyle())
+                font.set_weight(dial.getWeight())
+                colour = dial.getColor()
+                if len(newlabel) > 0:
+                    # update Label
+                    selected_plot.label = newlabel
+                    self.graph.title(newlabel)
+                    self.title_label = selected_plot.label
+                    self.title_font = font
+                    self.title_color = colour
+                    ## render the graph
+                    self.subplot.set_title(label=self.title_label,
+                                           fontproperties=self.title_font,
+                                           color=self.title_color)
+                    self._is_changed_legend_label = True
+                    self.subplot.figure.canvas.draw_idle()  
+            except:
+                if self.parent != None:
+                    from sas.guiframe.events import StatusEvent 
+                    msg= "Add Text: Error. Check your property values..."
+                    wx.PostEvent(self.parent, StatusEvent(status = msg ))
+                else:
+                    raise
+        dial.Destroy()
+        
+    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 sas.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 sas.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 = "2daverage"  + self.data2D.name
+        new_plot.id = "Circ avg " + self.data2D.name
+        new_plot.is_data = True
+        self.parent.update_theory(data_id=self.data2D.id, \
+                                       theory=new_plot)
+        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 sas.guiframe.gui_manager import MDIFrame
+        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
+        win = MDIFrame(self.parent, None, 'None', (100, 200))
+        new_panel = SlicerPanel(parent=win, 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
+        
+        ## 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 sas.guiframe.events import SlicerPanelEvent
+        
+        win.set_panel(new_panel)
+        new_panel.frame = win
+        wx.PostEvent(self.parent, SlicerPanelEvent(panel=new_panel,
+                                                    main_panel=self))
+        wx.CallAfter(new_panel.frame.Show)
+        self.panel_slicer = new_panel
+        
+    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 self.parent != None:
+            self._default_save_location = self.parent._default_save_location
+        default_name = self.plots[self.graph.selected_plottable].label
+        if default_name.count('.') > 0:
+            default_name = default_name.split('.')[0]
+        default_name += "_out"
+        if id in self.action_ids:         
+            path = None
+            self.parent.save_data2d(self.data2D, default_name)
+            
+    def _onDataShow(self, evt):
+        """
+        Show the data set in text
+        
+        :param evt: Menu event
+        
+        """
+        menu = evt.GetEventObject()
+        id = evt.GetId()
+        self.set_selected_from_menu(menu, id)
+        data = self.plots[self.graph.selected_plottable]
+        default_name = data.label
+        if default_name.count('.') > 0:
+            default_name = default_name.split('.')[0]
+        #default_name += "_out"
+        if self.parent != None:
+            self.parent.show_data2d(data, default_name)
+        
+
+    def modifyGraphAppearance(self,e):
+        self.graphApp = graphAppearance(self,'Modify graph appearance',
+                                        legend=False)
+
+        
+
+        self.graphApp.setDefaults(self.grid_on,self.legend_on,
+                                  self.xaxis_label,self.yaxis_label,
+                                  self.xaxis_unit,self.yaxis_unit,
+                                  self.xaxis_font,self.yaxis_font,
+                                  find_key(self.get_loc_label(),self.legendLoc),
+                                  self.xcolor,self.ycolor,
+                                   self.is_xtick, self.is_ytick)
+        self.graphApp.Bind(wx.EVT_CLOSE, self.on_graphApp_close)
+    
+
+    def on_graphApp_close(self,e):
+        # gets values from graph appearance dialog and sends them off
+        # to modify the plot
+
+        self.onGridOnOff(self.graphApp.get_togglegrid())
+
+        
+        self.xaxis_label = self.graphApp.get_xlab()
+        self.yaxis_label = self.graphApp.get_ylab()
+        self.xaxis_unit = self.graphApp.get_xunit()
+        self.yaxis_unit = self.graphApp.get_yunit()
+        self.xaxis_font = self.graphApp.get_xfont()
+        self.yaxis_font = self.graphApp.get_yfont()
+        self.is_xtick =  self.graphApp.get_xtick_check()
+        self.is_ytick =  self.graphApp.get_ytick_check()
+        if self.is_xtick:
+            self.xaxis_tick = self.xaxis_font
+        if self.is_ytick:
+            self.yaxis_tick = self.yaxis_font
+
+        self.xaxis(self.xaxis_label, self.xaxis_unit, 
+                   self.graphApp.get_xfont(), self.graphApp.get_xcolor(), 
+                   self.xaxis_tick)
+        self.yaxis(self.yaxis_label, self.yaxis_unit, 
+                   self.graphApp.get_yfont(), self.graphApp.get_ycolor(),
+                   self.yaxis_tick)
+
+        self.graphApp.Destroy()
Index: src/sas/guiframe/local_perspectives/plotting/SectorSlicer.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/SectorSlicer.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/guiframe/local_perspectives/plotting/SectorSlicer.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,627 @@
+
+import math
+import wx
+#from copy import deepcopy
+from BaseInteractor import _BaseInteractor
+from sas.guiframe.events import NewPlotEvent
+from sas.guiframe.events import StatusEvent
+from sas.guiframe.events import SlicerParameterEvent
+from sas.guiframe.events import EVT_SLICER_PARS
+from sas.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 sas.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 = "2daverage" + self.base.data2D.name
+        new_plot.id = "SectorQ" + self.base.data2D.name
+        new_plot.is_data = True
+        self.base.parent.update_theory(data_id=data.id, \
+                                       theory=new_plot)
+        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 [deg]"] = self.main_line.theta * 180/math.pi
+        params["Delta_Phi [deg]"] = math.fabs(self.left_line.phi * 180/math.pi)
+        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 [deg]"]  * math.pi/180
+        phi = math.fabs(params["Delta_Phi [deg]"] * math.pi/180)
+        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, None)
+        
+
+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, None)
+        
Index: src/sas/guiframe/local_perspectives/plotting/SimplePlot.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/SimplePlot.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/guiframe/local_perspectives/plotting/SimplePlot.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,341 @@
+"""
+Simple Plot Frame : supporting only copy, print, scale
+"""
+import wx
+from sas.guiframe.local_perspectives.plotting.Plotter2D import ModelPanel2D \
+                    as PlotPanel
+from sas.plottools.toolbar import NavigationToolBar
+from sas.plottools.plottables import Graph
+from sas.guiframe.utils import PanelMenu
+
+class SimplePlotPanel(PlotPanel):   
+    """
+    PlotPanel for 1d and 2d
+    """ 
+    _window_caption = 'Simple Plot'
+    def __init__(self, parent, id=-1, color = None,
+                 dpi=None, style=wx.NO_FULL_REPAINT_ON_RESIZE, **kwargs):
+        """
+        Init
+        """
+        PlotPanel.__init__(self, parent, id=id, style=style, **kwargs)
+
+        self.SetColor(wx.WHITE)
+        
+        self.toolbar = NavigationToolBar(parent=self, canvas=self.canvas)
+        self.toolbar.Show(False)
+        self.scale = parent.scale
+        self.window_caption = self._window_caption
+            
+    def draw(self):
+        """
+        """
+        self.resizing = False
+        self.canvas.set_resizing(self.resizing)
+        self.canvas.draw()
+        
+    def add_toolbar(self):
+        """
+        """
+        pass   
+     
+    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)
+
+        if self.dimension != 3:
+            slicerpop.AppendSeparator()
+            id = wx.NewId()
+            slicerpop.Append(id, '&Toggle Grid On/Off', 'Toggle Grid On/Off')
+            wx.EVT_MENU(self, id, self.on_grid_onoff)
+
+        if self.data.__class__.__name__ == 'Data1D':
+            slicerpop.AppendSeparator()
+            id = wx.NewId()
+            slicerpop.Append(id, '&Change Scale')
+            wx.EVT_MENU(self, id, self._onProperties)        
+        elif self.data2D.__class__.__name__ == 'Data2D':
+            slicerpop.AppendSeparator()
+            id = wx.NewId()
+            slicerpop.Append(id, '&Toggle Linear/Log Scale')
+            wx.EVT_MENU(self, id, self._onToggleScale) 
+                
+        try:
+            pos_evt = event.GetPosition()
+            pos = self.ScreenToClient(pos_evt)
+        except:
+            pos_x, pos_y = self.toolbar.GetPositionTuple()
+            pos = (pos_x, pos_y + 5)
+        self.PopupMenu(slicerpop, pos)
+        if self.scale != None:
+            self.parent.scale2d = self.scale 
+    
+    def on_grid_onoff(self, event):
+        """
+        On grid on/off
+        """
+        switch = (not self.grid_on)
+        self.onGridOnOff(switch) 
+                
+    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
+                try:
+                    pos_x = float(event.xdata)  # / size_x
+                    pos_y = float(event.ydata)  # / size_y
+                    pos_x = "%8.3g" % pos_x
+                    pos_y = "%8.3g" % pos_y
+                    self.position = str(pos_x), str(pos_y)
+                    wx.PostEvent(self.parent, StatusEvent(status=self.position))
+                except:
+                    self.position = None    
+             
+    def _OnReSize(self, event):   
+        """
+        On response of the resize of a panel, set axes_visiable False
+        """
+        self.resizing = False
+        if self.x_size != None:
+            if self.x_size == self.GetSize():
+                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.canvas.set_resizing(self.resizing)
+        pos_x, pos_y = self.GetPositionTuple()
+        if pos_x != 0 and pos_y != 0:
+            self.size, _ = self.GetClientSizeTuple()
+        self.SetSizer(self.sizer)
+        
+    def on_set_focus(self, event):
+        """
+        By pass default boundary blue color drawing
+        """
+        pass
+        
+    def on_kill_focus(self, event):
+        """
+        Reset the panel color
+        """
+        pass
+    
+    def show_plot(self, plot):
+        """
+        Show the plot
+        """
+        _yaxis, _yunit = plot.get_yaxis()
+        _xaxis, _xunit = plot.get_xaxis()
+        self.data = plot
+        self.plots[plot.name] = plot
+        # Axis scales
+        if plot.xtransform != None:
+            self.xLabel = plot.xtransform
+        if plot.ytransform != None:
+            self.yLabel = plot.ytransform
+        # Init graph
+        self.graph = Graph()
+        # Add plot: Handles both 1D and 2D
+        self.graph.add(self.data)
+        self.canvas.set_resizing(False)
+        if self.data.__class__.__name__ == 'Data2D':
+            self.data2D = plot
+        elif self.data.__class__.__name__ == 'Data1D':
+            self._onEVT_FUNC_PROPERTY(show=False)
+        # Axes
+        self.graph.xaxis(_xaxis, _xunit)
+        self.graph.yaxis(_yaxis, _yunit)
+        self.xaxis(_xaxis, _xunit)
+        self.yaxis(_yaxis, _yunit) 
+        self.set_xscale(self.xscale)
+        self.set_yscale(self.yscale)   
+        self.graph.render(self)    
+                  
+class PlotFrame(wx.Frame):
+    """
+    Frame for simple plot
+    """
+    def __init__(self, parent, id, title, scale='log_{10}', 
+                 size=wx.Size(550, 470)): 
+        """
+        comment
+        :param parent: parent panel/container
+        """
+        # Initialize the Frame object
+        wx.Frame.__init__(self, parent, id, title, wx.DefaultPosition, size)
+        
+        # Panel for 1D plot
+        self.parent = parent
+        self._mgr = None
+        self.menu_bar = None
+        self._default_save_location = None
+        self.scale = scale
+        self.plotpanel = SimplePlotPanel(self, -1)
+        self._build_menubar()
+
+    def _build_menubar(self):
+        """
+        Build menubar
+        """
+        tsize = (13, 13)
+        save_bmp = wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE_AS, wx.ART_TOOLBAR,
+                                            tsize)
+        quit_bmp = wx.ArtProvider.GetBitmap(wx.ART_QUIT, wx.ART_TOOLBAR, 
+                                           tsize)
+        print_bmp = wx.ArtProvider.GetBitmap(wx.ART_PRINT, wx.ART_TOOLBAR, 
+                                           tsize)
+        preview_bmp = wx.ArtProvider.GetBitmap(wx.ART_REPORT_VIEW, wx.ART_TOOLBAR, 
+                                           tsize)
+        copy_bmp = wx.ArtProvider.GetBitmap(wx.ART_COPY, wx.ART_TOOLBAR, 
+                                           tsize)
+        menu_bar = wx.MenuBar()
+        
+        menu = wx.Menu()        
+        id = wx.NewId()
+        item = wx.MenuItem(menu, id, "&Save Image")
+        item.SetBitmap(save_bmp)
+        menu.AppendItem(item)
+        wx.EVT_MENU(self, id, self.on_save_file)
+
+        id = wx.NewId()
+        item = wx.MenuItem(menu, id, "&Print Image")
+        item.SetBitmap(print_bmp)
+        menu.AppendItem(item)
+        wx.EVT_MENU(self, id, self.on_print_image)
+
+        id = wx.NewId()
+        item = wx.MenuItem(menu, id, "&Print Preview")
+        item.SetBitmap(preview_bmp)
+        menu.AppendItem(item)
+        wx.EVT_MENU(self, id, self.on_print_preview)
+        
+        menu.AppendSeparator()
+        id = wx.NewId()
+        item = wx.MenuItem(menu, id, "&Quit")
+        item.SetBitmap(quit_bmp)
+        menu.AppendItem(item)
+
+        menu_bar.Append(menu, "&File")
+        wx.EVT_MENU(self, id, self.on_close)
+        
+        menu_edit = wx.Menu()
+        id = wx.NewId()
+        item = wx.MenuItem(menu_edit, id, "&Copy")
+        item.SetBitmap(copy_bmp)
+        menu_edit.AppendItem(item)
+        wx.EVT_MENU(self, id, self.on_copy_image)
+        
+        menu_bar.Append(menu_edit, "&Edit")
+        self.menu_bar = menu_bar
+        self.SetMenuBar(self.menu_bar)
+
+    def set_plot_unfocus(self):
+        """
+        un focusing
+        """
+        pass    
+    
+    def add_plot(self, plot):
+        """
+        Add Image
+        """
+        plotpanel = self.plotpanel
+        plotpanel.scale = self.scale
+        plotpanel.show_plot(plot)
+        
+    def set_schedule_full_draw(self, panel, func):
+        """
+        """
+        self.plotpanel.resizing = False
+    
+    def im_show(self, img):
+        """
+        Show background image
+        :Param img: [imread(path) from matplotlib.pyplot]
+        """
+        self.plotpanel.subplot.imshow(img)
+        
+    def set_schedule(self, schedule=False): 
+        """
+        """
+        #Not implemented
+    
+    def disable_app_menu(self, panel):
+        """
+        """
+        #Not implemented 
+    
+    def get_current_context_menu(self, plotpanel): 
+        """
+        """
+        #Not implemented
+    
+    def on_save_file(self, event):
+        """
+        Save image
+        """
+        self.plotpanel.onSaveImage(event)
+
+    def on_print_image(self, event):
+        """
+        Save image
+        """
+        self.plotpanel.onPrint(event)
+    
+    def on_print_preview(self, event):
+        """
+        Save image
+        """
+        self.plotpanel.onPrinterPreview(event)
+
+    def on_copy_image(self, event):
+        """
+        Save image
+        """
+        self.plotpanel.OnCopyFigureMenu(event)
+            
+    def on_close(self, event):
+        """
+        On Close 
+        """
+        try:
+            self.parent.set_scale2d(self.scale)
+            self.parent.on_panel_close(event)
+        except:
+            self.Destroy()
+                          
Index: src/sas/guiframe/local_perspectives/plotting/SlicerParameters.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/SlicerParameters.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/guiframe/local_perspectives/plotting/SlicerParameters.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,148 @@
+
+
+import wx
+import wx.lib.newevent
+#from copy import deepcopy
+from sas.guiframe.events import EVT_SLICER_PARS
+from sas.guiframe.utils import format_number
+from sas.guiframe.events import EVT_SLICER
+from sas.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)
+                    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: src/sas/guiframe/local_perspectives/plotting/__init__.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/__init__.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/guiframe/local_perspectives/plotting/__init__.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,2 @@
+PLUGIN_ID = "Plotting plug-in 1.0"
+from sas.guiframe.local_perspectives.plotting import *
Index: src/sas/guiframe/local_perspectives/plotting/appearanceDialog.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/appearanceDialog.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/guiframe/local_perspectives/plotting/appearanceDialog.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,225 @@
+#!/usr/bin/python
+"""
+Dialog for appearance of plot symbols, color, size etc.
+
+This software was developed by Institut Laue-Langevin as part of
+Distributed Data Analysis of Neutron Scattering Experiments (DANSE).
+
+Copyright 2012 Institut Laue-Langevin
+"""
+import wx
+import operator
+
+class appearanceDialog(wx.Frame):
+    """
+    Appearance dialog
+    """
+    def __init__(self, parent, title):
+        """
+        Initialization of the Panel
+        """
+        super(appearanceDialog, self).__init__(parent, title=title, 
+                        size=(570,450), 
+                        style=wx.DEFAULT_FRAME_STYLE|wx.FRAME_FLOAT_ON_PARENT)
+
+        self.okay_clicked = False
+        self.parent = parent
+        self.symbo_labels = self.parent.get_symbol_label()
+        self.color_labels = self.parent.get_color_label()
+        self.init_ui()
+        self.Centre()
+        self.Show()
+
+    def init_ui(self):
+        """
+        Create spacing needed
+        """
+        panel = wx.Panel(self)
+
+        vbox = wx.BoxSizer(wx.VERTICAL)
+
+        hbox1 = wx.BoxSizer(wx.HORIZONTAL)
+        hbox2 = wx.BoxSizer(wx.HORIZONTAL)
+        hbox3 = wx.BoxSizer(wx.HORIZONTAL)
+
+        ivbox1 = wx.BoxSizer(wx.VERTICAL)
+        ivbox2 = wx.BoxSizer(wx.VERTICAL)
+ 
+        ihbox1 = wx.BoxSizer(wx.HORIZONTAL)
+        ihbox2 = wx.BoxSizer(wx.HORIZONTAL)
+
+        symbolstaticbox = wx.StaticBox(panel, -1, 'Symbol')
+        symbolstaticboxsizer = wx.StaticBoxSizer(symbolstaticbox, wx.VERTICAL)
+
+        # add widgets - reverse order!
+        # texts
+        symboltext = wx.StaticText(panel, label='Shape')
+        colortext = wx.StaticText(panel, label='Color')
+        sizetext = wx.StaticText(panel, label='Size ')
+        labeltext = wx.StaticText(panel, label='Legend Label')
+
+        # selection widgets
+        self.symbollistbox = wx.ListBox(panel, -1, size=(200, 200))
+        self.colorlistbox = wx.ComboBox(panel, style=wx.CB_READONLY, 
+                                        size=(185, -1))
+        self.sizecombobox = wx.ComboBox(panel, style=wx.CB_READONLY, 
+                                        size=(90, -1))
+        self.sizecombobox.Bind(wx.EVT_COMBOBOX, self.combo_click)
+        self.sizecustombutton = wx.Button(panel, label='Custom...')
+        self.sizecustombutton.Bind(wx.EVT_BUTTON, self.custom_size)
+        self.labeltextbox = wx.TextCtrl(panel, -1, "",  size=(440, -1))
+
+        # buttons
+        okbutton = wx.Button(panel, label='OK')
+        okbutton.Bind(wx.EVT_BUTTON, self.on_ok)
+        cancelbutton = wx.Button(panel, label='Cancel')
+        cancelbutton.Bind(wx.EVT_BUTTON, self.close_dlg)
+
+        # now Add all the widgets to relevant spacer - tricky
+        ivbox1.Add(symboltext, flag=wx.ALL|wx.ALIGN_LEFT, border=10)
+        ivbox1.Add(self.symbollistbox, flag=wx.ALL|wx.ALIGN_LEFT, border=10)
+
+        ihbox1.Add(sizetext, flag=wx.ALL|wx.ALIGN_LEFT, border=10)
+        ihbox1.Add(self.sizecombobox, 
+                   flag= wx.ALL|wx.RIGHT|wx.ALIGN_LEFT, border=10)
+        ihbox1.Add(self.sizecustombutton, 
+                   flag=wx.ALIGN_LEFT|wx.ALL, border=10)
+
+        ihbox2.Add(colortext, flag=wx.ALL|wx.ALIGN_LEFT, border=10)
+        ihbox2.Add(self.colorlistbox, flag=wx.ALL|wx.ALIGN_LEFT, border=10)
+
+        ivbox2.Add(ihbox1, flag=wx.ALIGN_LEFT, border=10)
+        ivbox2.Add(ihbox2, flag=wx.ALIGN_LEFT, border=10)
+
+        hbox1.Add(ivbox1, flag=wx.ALIGN_LEFT, border=10)
+        hbox1.Add(ivbox2, flag=wx.ALIGN_LEFT, border=10)
+
+        hbox2.Add(okbutton, flag=wx.ALL| wx.ALIGN_RIGHT, border=10)
+        hbox2.Add(cancelbutton, flag=wx.ALL|wx.ALIGN_RIGHT, border=10)
+
+        hbox3.Add(labeltext, flag=wx.EXPAND|wx.ALL|wx.ALIGN_LEFT, border=10)
+        hbox3.Add(self.labeltextbox, flag=wx.EXPAND|wx.ALL|wx.ALIGN_LEFT, border=10)
+  
+        symbolstaticboxsizer.Add(hbox1, flag=wx.ALL|wx.EXPAND, border=10)
+        vbox.Add(symbolstaticboxsizer, flag=wx.ALL|wx.EXPAND, border=10)
+        vbox.Add(hbox3, flag=wx.EXPAND|wx.RIGHT, border=10)
+        vbox.Add(wx.StaticLine(panel), 0, wx.ALL|wx.EXPAND, 5)
+        vbox.Add(hbox2, flag=wx.RIGHT|wx.ALIGN_RIGHT, border=10)
+
+        panel.SetSizer(vbox)
+
+        self.populate_symbol()
+        self.populate_color()
+        self.populate_size()
+
+        self.SetDefaultItem(self.symbollistbox)
+
+    def custom_size(self, event):
+        """
+        On custom size
+        """
+        dlg = wx.TextEntryDialog(self, 
+                                 'Enter custom size', 
+                                 'Custom size', 
+                                 str(self.final_size))
+        if(dlg.ShowModal() == wx.ID_OK):
+            if(float(dlg.GetValue()) < 0):
+                msg = "Unfortunately imaginary icons are not yet supported."
+                msg += "Please enter a positive value"
+                dial = wx.MessageDialog(None, msg, 'Error',
+                                         wx.OK|wx.ICON_ERROR)
+                dial.ShowModal()
+                dlg.Destroy()
+                self.custom_size(event)
+            else:
+                self.final_size = dlg.GetValue()
+                dlg.Destroy()
+        else:
+            dlg.Destroy()
+
+    def set_defaults(self, size, color, symbol, label):
+        """
+        Set Defaults
+        """
+        self.final_size = size
+        # set up gui values
+        self.labeltextbox.SetValue(label)
+        if(size % 1 == 0 and size > 1 and size < 11):
+            self.sizecombobox.SetSelection(int(size) - 1)
+        else:
+            self.sizecombobox.SetSelection(4)
+        self.symbollistbox.SetSelection(self.sorted_sym_dic[symbol])
+        colorname = appearanceDialog.find_key(self.parent.get_color_label(), 
+                                              color)
+        self.colorlistbox.SetStringSelection(colorname)
+
+    def populate_symbol(self):
+        """
+        Populate Symbols
+        """
+        self.sorted_symbo_labels = sorted(self.symbo_labels.iteritems(), 
+                                          key=operator.itemgetter(1))
+        self.sorted_sym_dic = {}
+        i = 0
+        for label in self.sorted_symbo_labels:
+            self.symbollistbox.Append(str(label[0]))
+            self.sorted_sym_dic[str(label[0])] = i
+            i += 1
+
+    def populate_color(self):
+        """
+        Populate Colors
+        """
+        sortedcolor_labels = sorted(self.color_labels.iteritems(), 
+                                   key=operator.itemgetter(1))
+        for color in sortedcolor_labels:
+            self.colorlistbox.Append(str(color[0]))
+ 
+    def populate_size(self):
+        """
+        Populate Size
+        """
+        for i in range(1, 11):
+            self.sizecombobox.Append(str(i) + '.0')
+
+    def combo_click(self, event):
+        """
+        Combox on click
+        """
+        event.Skip()
+        self.final_size = self.sizecombobox.GetValue()
+
+    def close_dlg(self, event):
+        """
+        On Close Dlg
+        """
+        event.Skip()
+        self.Destroy()
+
+    @staticmethod
+    def find_key(dic, val):
+        """
+        Find key
+        """
+        return [k for k, v in dic.iteritems() if v == val][0]
+        
+    def get_current_values(self): 
+        """
+        Get Current Values
+        :returns : (size, color, symbol, dataname)
+        """
+        size = float(self.final_size)
+        name = str(self.labeltextbox.GetValue())
+        seltuple = self.symbollistbox.GetSelections()
+        symbol = appearanceDialog.find_key(self.sorted_sym_dic, 
+                                           int(seltuple[0]))
+        color = str(self.colorlistbox.GetValue()) 
+        return(size, color, symbol, name)
+
+    def on_ok(self, event):
+        """
+        On OK button clicked
+        """
+        event.Skip()
+        self.okay_clicked = True
+        self.Close()
Index: src/sas/guiframe/local_perspectives/plotting/binder.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/binder.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/guiframe/local_perspectives/plotting/binder.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -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: src/sas/guiframe/local_perspectives/plotting/boxMask.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/boxMask.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/guiframe/local_perspectives/plotting/boxMask.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,254 @@
+
+
+
+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 sas.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.mask = None
+        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 sas.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
+        self.mask = mask
+        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: src/sas/guiframe/local_perspectives/plotting/boxSlicer.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/boxSlicer.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/guiframe/local_perspectives/plotting/boxSlicer.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,570 @@
+
+
+import wx
+#import copy
+#from copy import deepcopy
+import math
+import numpy
+from sas.guiframe.events import NewPlotEvent
+from sas.guiframe.events import StatusEvent
+from sas.guiframe.events import SlicerParameterEvent
+from sas.guiframe.events import EVT_SLICER_PARS
+from BaseInteractor import _BaseInteractor
+from sas.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
+        ## which direction is the preferred interaction direction
+        self.direction = None
+        ## 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
+        
+        """
+        if self.direction == None:
+            self.direction = direction
+            
+        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 self.direction == "X":
+            if self.fold:
+                x_low = 0
+            else:
+                x_low = math.fabs(x_min)
+            bin_width = (x_max + x_low)/self.nbins
+        elif self.direction == "Y":
+            if self.fold:
+                y_low = 0
+            else:
+                y_low = math.fabs(y_min)
+            bin_width = (y_max + y_low)/self.nbins
+        else:
+            msg = "post data:no Box Average direction was supplied"
+            raise ValueError, msg
+        ## 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 = "2daverage" + self.base.data2D.name
+        new_plot.id = (self.averager.__name__) + self.base.data2D.name
+        new_plot.is_data= True
+        self.base.parent.update_theory(data_id=self.base.data2D.id, \
+                                       theory=new_plot)
+        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 sas.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 sas.dataloader.manipulations import SlabY
+        self.post_data(SlabY, direction="Y")   
+        
+        
Index: src/sas/guiframe/local_perspectives/plotting/boxSum.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/boxSum.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/guiframe/local_perspectives/plotting/boxSum.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,784 @@
+"""
+    Boxsum Class: determine 2 rectangular area to compute 
+    the sum of pixel of a Data. 
+"""
+import math
+import wx
+from BaseInteractor import _BaseInteractor
+from sas.guiframe.events import SlicerParamUpdateEvent
+from sas.guiframe.events import EVT_SLICER_PARS
+from sas.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
+        self.total = 0
+        self.totalerror = 0
+        self.points = 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 sas.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)
+        # Dig out number of points summed, SMK & PDB, 04/03/2013
+        from sas.dataloader.manipulations import Boxsum
+        boxtotal =  Boxsum(x_min=x_min, x_max=x_max, y_min=y_min, y_max=y_max)
+        self.total, self.totalerror, self.points = boxtotal(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["num_points"] = self.points
+        params["avg"] = self.count
+        params["avg_error"] = self.error
+        params["sum"] = self.total
+        params["sum_error"] = self.totalerror
+        return params
+    
+    def get_result(self):
+        """
+            return the result of box summation
+        """
+        result = {}
+        result["num_points"] = self.points
+        result["avg"] = self.count
+        result["avg_error"] = self.error
+        params["sum"] = self.total
+        params["sum_error"] = self.totalerror
+        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()
+        
+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()
+        
+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()
+         
Index: src/sas/guiframe/local_perspectives/plotting/detector_dialog.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/detector_dialog.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/guiframe/local_perspectives/plotting/detector_dialog.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,289 @@
+"""
+    Widget to display a 2D map of the detector
+"""
+import wx
+import sys
+from sas.guiframe.utils import format_number
+from sas.guiframe.events import StatusEvent 
+from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as Canvas
+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.get_cmap('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, 5)
+        sizer_main.Add(sizer_selection, 0, wx.EXPAND|wx.ALL, 5)
+        note = "   Note: This is one time option. " +\
+               "It will be reset on updating the image."
+        note_txt = wx.StaticText(self, -1, note)
+        sizer_main.Add(note_txt, 0, wx.EXPAND|wx.ALL, 5)
+        sizer_main.Add(sizer_colormap, 1, wx.EXPAND|wx.ALL, 5)
+        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()
Index: src/sas/guiframe/local_perspectives/plotting/graphAppearance.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/graphAppearance.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/guiframe/local_perspectives/plotting/graphAppearance.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,327 @@
+#!/usr/bin/python
+
+"""
+
+Dialog for general graph appearance
+
+
+/**
+    This software was developed by Institut Laue-Langevin as part of
+    Distributed Data Analysis of Neutron Scattering Experiments (DANSE).
+
+    Copyright 2012 Institut Laue-Langevin
+
+**/
+
+
+"""
+
+import wx
+from sas.plottools.SimpleFont import SimpleFont
+
+COLOR = ['black', 'blue', 'green', 'red', 'cyan', 'magenta', 'yellow']
+
+
+class graphAppearance(wx.Frame):
+
+    def __init__(self, parent, title, legend=True):
+        super(graphAppearance, self).__init__(parent, title=title, size=(520, 435))
+
+        self.legend = legend
+
+        self.InitUI()
+        self.Centre()
+        self.Show()
+
+        self.xfont = None
+        self.yfont = None
+        self.is_xtick = False
+        self.is_ytick = False
+
+
+    def InitUI(self):
+
+        panel = wx.Panel(self)
+
+        vbox = wx.BoxSizer(wx.VERTICAL)
+        hbox1 = wx.BoxSizer(wx.HORIZONTAL)
+        hbox2 = wx.BoxSizer(wx.HORIZONTAL)
+
+        xhbox1 = wx.BoxSizer(wx.HORIZONTAL)
+        xhbox2 = wx.BoxSizer(wx.HORIZONTAL)
+        yhbox1 = wx.BoxSizer(wx.HORIZONTAL)
+        yhbox2 = wx.BoxSizer(wx.HORIZONTAL)
+
+
+
+        if self.legend:
+            legendLocText = wx.StaticText(panel, label='Legend location: ')
+            self.legend_loc_combo = wx.ComboBox(panel,style = wx.CB_READONLY, size=(180,-1))
+            self.fillLegendLocs()
+        else:
+            self.legend_loc_combo = None
+
+
+        if self.legend:
+            self.toggle_legend = wx.CheckBox(panel, label='Toggle legend on/off')
+        else:
+            self.toggle_legend = None
+
+        self.toggle_grid = wx.CheckBox(panel, label='Toggle grid on/off')
+
+        
+        xstatic_box = wx.StaticBox(panel, -1, 'x-axis label')
+        xstatic_box_sizer = wx.StaticBoxSizer(xstatic_box, wx.VERTICAL)
+        ystatic_box = wx.StaticBox(panel, -1, 'y-axis label')
+        ystatic_box_sizer = wx.StaticBoxSizer(ystatic_box, wx.VERTICAL)
+
+
+        xaxis_label = wx.StaticText(panel, label='X-axis: ')
+        yaxis_label = wx.StaticText(panel, label='Y-axis: ')
+        unitlabel_1 = wx.StaticText(panel, label='Units: ')
+        unitlabel_2 = wx.StaticText(panel, label='Units: ')
+
+        self.xaxis_text = wx.TextCtrl(panel, -1, "",size=(220, -1))
+        self.yaxis_text = wx.TextCtrl(panel, -1, "",size=(220, -1))
+
+        self.xaxis_unit_text = wx.TextCtrl(panel, -1,"",size=(100, -1))
+        self.yaxis_unit_text = wx.TextCtrl(panel, -1,"",size=(100, -1))
+
+
+
+        xcolorLabel = wx.StaticText(panel, label='Font color: ')
+        self.xfont_color = wx.ComboBox(panel, size=(100, -1), style=wx.CB_READONLY)
+        self.xfill_colors()
+        self.xfont_color.SetSelection(0)
+        xfont_button = wx.Button(panel, label='Font')
+        xfont_button.Bind(wx.EVT_BUTTON, self.onxFont)
+
+        ycolorLabel = wx.StaticText(panel, label='Font color: ')
+        self.yfont_color = wx.ComboBox(panel, size=(100, -1),style=wx.CB_READONLY)
+        self.yfill_colors()
+        self.yfont_color.SetSelection(0)
+        yfont_button = wx.Button(panel, label='Font')
+        yfont_button.Bind(wx.EVT_BUTTON, self.onyFont)
+
+        
+
+        self.cancel_button = wx.Button(panel, label='Cancel')
+        self.ok_button = wx.Button(panel, label='OK')
+
+        self.cancel_button.Bind(wx.EVT_BUTTON, self.onCancel)
+        self.ok_button.Bind(wx.EVT_BUTTON, self.on_ok)
+
+
+        xhbox1.Add(xaxis_label, flag= wx.ALL | wx.EXPAND  | wx.ALIGN_LEFT, border=10)
+        xhbox1.Add(self.xaxis_text, flag=wx.ALL | wx.EXPAND  | wx.ALIGN_LEFT, border=10)
+        xhbox1.Add(unitlabel_1, flag=wx.ALL | wx.EXPAND  | wx.ALIGN_RIGHT, border=10)
+        xhbox1.Add(self.xaxis_unit_text, flag=wx.ALL | wx.EXPAND  | wx.ALIGN_RIGHT, border=10)
+
+        yhbox1.Add(yaxis_label, flag= wx.ALL | wx.EXPAND  | wx.ALIGN_LEFT, border=10)
+        yhbox1.Add(self.yaxis_text, flag=wx.ALL | wx.EXPAND  | wx.ALIGN_LEFT, border=10)
+        yhbox1.Add(unitlabel_2, flag=wx.ALL | wx.EXPAND  | wx.ALIGN_RIGHT, border=10)
+        yhbox1.Add(self.yaxis_unit_text, flag=wx.ALL | wx.EXPAND  | wx.ALIGN_RIGHT, border=10)
+
+        xhbox2.Add(xcolorLabel, flag=wx.ALL | wx.ALIGN_RIGHT, border=10)
+        xhbox2.Add(self.xfont_color, flag=wx.ALL | wx.ALIGN_RIGHT, border=5)
+        xhbox2.Add(xfont_button, flag=wx.ALL | wx.ALIGN_RIGHT, border=5)
+
+        yhbox2.Add(ycolorLabel, flag=wx.ALL | wx.ALIGN_RIGHT, border=10)
+        yhbox2.Add(self.yfont_color, flag=wx.ALL | wx.ALIGN_RIGHT, border=5)
+        yhbox2.Add(yfont_button, flag=wx.ALL | wx.ALIGN_RIGHT, border=5)
+
+        if self.legend:
+            hbox1.Add(legendLocText, flag =  wx.ALL | wx.EXPAND  | wx.ALIGN_LEFT, border=5)
+            hbox1.Add(self.legend_loc_combo, flag =  wx.ALL | wx.EXPAND  | wx.ALIGN_LEFT, border=5)
+
+        if self.legend:
+            hbox1.Add((5, -1))
+            hbox1.Add(self.toggle_legend, flag = wx.ALL | wx.EXPAND  | wx.ALIGN_LEFT, border=5)
+
+        hbox2.Add(self.ok_button, flag = wx.ALL | wx.ALIGN_RIGHT, border=5)
+        hbox2.Add(self.cancel_button, flag = wx.ALL | wx.ALIGN_RIGHT, border=5)
+        hbox2.Add((15, -1))
+
+        xstatic_box_sizer.Add(xhbox1, flag= wx.EXPAND , border=5)
+        xstatic_box_sizer.Add(xhbox2, flag=wx.ALL | wx.ALIGN_RIGHT, border=5)
+        ystatic_box_sizer.Add(yhbox1, flag= wx.EXPAND, border=5)
+        ystatic_box_sizer.Add(yhbox2, flag=wx.ALL | wx.ALIGN_RIGHT, border=5)
+
+        vbox.Add((-1, 20))
+        vbox.Add(hbox1, flag = wx.EXPAND | wx.ALL, border=5)
+        vbox.Add(xstatic_box_sizer, flag = wx.ALL | wx.EXPAND, border=10)
+        vbox.Add(ystatic_box_sizer, flag = wx.ALL | wx.EXPAND, border=10)
+
+        vbox.Add(self.toggle_grid, flag = wx.ALIGN_RIGHT | wx.RIGHT, border=20)
+        vbox.Add(hbox2, flag = wx.ALIGN_RIGHT | wx.ALL , border=5)
+
+
+        panel.SetSizer(vbox)
+
+    def xfill_colors(self):
+        c_list = COLOR
+        for idx in range(len(c_list)):
+            self.xfont_color.Append(c_list[idx], idx)
+
+    def yfill_colors(self):
+        c_list = COLOR
+        for idx in range(len(c_list)):
+            self.yfont_color.Append(c_list[idx], idx)
+
+    def onxFont(self, e):
+        title = 'Modify x axis font'
+
+        fonty = SimpleFont(self, wx.NewId(), title)
+        fonty.set_default_font(self.xfont)
+        fonty.set_ticklabel_check(self.is_xtick)
+        if(fonty.ShowModal() == wx.ID_OK):
+            self.xfont = fonty.get_font()
+            self.is_xtick = fonty.get_ticklabel_check()
+
+    def onyFont(self, e):
+        title = 'Modify y axis font'
+        fonty = SimpleFont(self, wx.NewId(), title)
+        fonty.set_default_font(self.yfont)
+        fonty.set_ticklabel_check(self.is_ytick)
+        if(fonty.ShowModal() == wx.ID_OK):
+            self.yfont = fonty.get_font()
+            self.is_ytick = fonty.get_ticklabel_check()
+
+    def on_ok(self, e):
+        self.Close()
+
+    def onCancel(self, e):
+        self.Destroy()
+
+
+    def get_loc_label(self):
+        """
+        Associates label to a specific legend location
+        """
+        _labels = {}
+        i = 0
+        _labels['best'] = i
+        i += 1
+        _labels['upper right'] = i
+        i += 1
+        _labels['upper left'] = i
+        i += 1
+        _labels['lower left'] = i
+        i += 1
+        _labels['lower right'] = i
+        i += 1
+        _labels['right'] = i
+        i += 1
+        _labels['center left'] = i
+        i += 1
+        _labels['center right'] = i
+        i += 1
+        _labels['lower center'] = i
+        i += 1
+        _labels['upper center'] = i
+        i += 1
+        _labels['center'] = i
+        return _labels
+
+
+    def fillLegendLocs(self):
+
+        # labels = []
+        # for label in self.get_loc_label():
+        #     labels.append(str(label))
+
+        # for label in reversed(labels):
+        #     self.legend_loc_combo.Append(label)
+        for label in self.get_loc_label():
+            self.legend_loc_combo.Append(label)
+
+
+    def setDefaults(self,grid,legend,xlab,ylab,xunit,yunit,
+                    xaxis_font,yaxis_font,legend_loc,
+                    xcolor,ycolor, is_xtick, is_ytick):
+        self.toggle_grid.SetValue(grid)
+        if self.legend:
+            self.toggle_legend.SetValue(legend)
+        self.xaxis_text.SetValue(xlab)
+        self.yaxis_text.SetValue(ylab)
+        self.xaxis_unit_text.SetValue(xunit)
+        self.yaxis_unit_text.SetValue(yunit)
+        self.xfont = xaxis_font
+        self.yfont = yaxis_font
+        self.is_xtick = is_xtick
+        self.is_ytick = is_ytick
+
+        if not xcolor:
+            self.xfont_color.SetSelection(0)
+        else:
+            self.xfont_color.SetStringSelection(xcolor)
+
+        if not ycolor:
+            self.yfont_color.SetSelection(0)
+        else:
+            self.yfont_color.SetStringSelection(ycolor)
+            
+
+        if self.legend:
+            self.legend_loc_combo.SetStringSelection(legend_loc)
+
+
+    # get whether grid is toggled on/off
+    def get_togglegrid(self):
+        return self.toggle_grid.GetValue()
+
+    # get whether legend is toggled on/off
+    def get_togglelegend(self):
+        return self.toggle_legend.GetValue()
+
+    # get x label
+    def get_xlab(self):
+        return self.xaxis_text.GetValue()
+
+    # get y label
+    def get_ylab(self):
+        return self.yaxis_text.GetValue()
+
+    # get x unit
+    def get_xunit(self):
+        return self.xaxis_unit_text.GetValue()
+
+    # get y unit
+    def get_yunit(self):
+        return self.yaxis_unit_text.GetValue()
+
+    # get legend location
+    def get_legend_loc(self):
+        return self.get_loc_label()[self.legend_loc_combo.GetStringSelection()]
+
+    # get x axis label color
+    def get_xcolor(self):
+        return self.xfont_color.GetValue()
+
+    # get y axis label color
+    def get_ycolor(self):
+        return self.yfont_color.GetValue()
+
+    # get x axis font (type is FontProperties)
+    def get_xfont(self):
+        return self.xfont
+
+    # get y axis font
+    def get_yfont(self):
+        return self.yfont
+    
+    def get_xtick_check(self):
+        return self.is_xtick
+
+    def get_ytick_check(self):
+        return self.is_ytick
+    
+
+if __name__ == '__main__':
+
+    app = wx.App()
+    graphD = graphAppearance(None,title='Modify graph appearance')
+    app.MainLoop()
+
+
+
Index: src/sas/guiframe/local_perspectives/plotting/help_panel.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/help_panel.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/guiframe/local_perspectives/plotting/help_panel.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,112 @@
+#!/usr/bin/python
+"""
+Help panel for plotting
+"""
+import os
+import wx
+import wx.html as html
+from wx.lib.splitter import MultiSplitterWindow
+from sas.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
+        """
+        self.SetTitle('Plotting Help') 
+        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 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: src/sas/guiframe/local_perspectives/plotting/masking.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/masking.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/guiframe/local_perspectives/plotting/masking.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,835 @@
+"""
+    Mask editor
+"""
+################################################################################
+#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 time
+import matplotlib.cm as cm
+import math
+import copy
+import numpy
+from sas.plottools.PlotPanel import PlotPanel
+from sas.plottools.plottables import Graph
+from binder import BindArtist
+from sas.guiframe.dataFitting import Data1D, Data2D
+from boxMask import BoxMask
+from sas.guiframe.events import SlicerEvent
+from sas.guiframe.events import StatusEvent
+(InternalEvent, EVT_INTERNAL) = wx.lib.newevent.NewEvent()
+
+DEFAULT_CMAP = cm.get_cmap('jet')
+_BOX_WIDTH = 76
+_SCALE = 1e-6
+_STATICBOX_WIDTH = 380
+
+#SLD panel size 
+if sys.platform.count("win32") > 0:
+    PANEL_SIZE = 350
+    FONT_VARIANT = 0
+else:
+    PANEL_SIZE = 300
+    FONT_VARIANT = 1
+    
+from sas.data_util.calcthread import CalcThread
+
+class CalcPlot(CalcThread):
+    """
+    Compute Resolution
+    """
+    def __init__(self,
+                 id = -1,
+                 panel = None,
+                 image = None,
+                 completefn = None,
+                 updatefn   = None,
+                 elapsed = 0,
+                 yieldtime  = 0.01,
+                 worktime   = 0.01
+                 ):
+        """
+        """
+        CalcThread.__init__(self, completefn,
+                 updatefn,
+                 yieldtime,
+                 worktime)
+        self.starttime = 0
+        self.id = id 
+        self.panel = panel
+        self.image = image
+        
+        
+    def compute(self):
+        """
+        excuting computation
+        """
+        elapsed = time.time() - self.starttime
+       
+        self.complete(panel=self.panel, image=self.image, elapsed=elapsed)
+        
+        
+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*0.8, 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
+        """
+        note = "Note: This masking applies\n     only to %s." % self.data.name
+        note_txt = wx.StaticText(self, -1, note) 
+        note_txt.SetForegroundColour(wx.RED)
+        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)
+        sizer.Add(note_txt, (8, 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 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 set_plot_unfocus(self):
+        """
+        Not implemented
+        """
+        pass
+    
+    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]
+        
+        #TODO: refactor this horrible logic 
+        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_{10}':
+            zmax = math.log10(max(self.data.data[self.data.data>0]))
+            zmin = math.log10(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):
+        """
+            Processing close event
+        """
+        try:
+            self.parent._draw_masked_model(event)
+        except:
+            # when called by data panel
+            event.Skip()
+            pass   
+
+class FloatPanel(wx.Dialog):
+    """
+    Provides the Mask Editor GUI.
+    """
+    ## Internal nickname for the window, used by the AUI manager
+    window_name = "Plot"
+    ## Name to appear on the window title bar
+    window_caption = "Plot"
+    ## Flag to tell the AUI manager to put this panel in the center pane
+    CENTER_PANE = False
+    ID = wx.NewId()
+    def __init__(self, parent=None, base=None, 
+                 data=None, dimension=1, id=ID, *args, **kwds):
+        kwds["style"] = wx.DEFAULT_DIALOG_STYLE
+        kwds["size"] = wx.Size(PANEL_SIZE*1.5, PANEL_SIZE*1.5) 
+        wx.Dialog.__init__(self, parent, id=id,  *args, **kwds)
+        
+        if data != None:
+            #Font size 
+            kwds = []
+            self.SetWindowVariant(variant=FONT_VARIANT)
+            self.SetTitle("Plot " + data.name)
+            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
+            self.dimension = dimension
+            # Panel for 2D plot
+            self.plotpanel = Maskplotpanel(self, -1, dimension,
+                                           style=wx.TRANSPARENT_WINDOW)
+            self.plotpanel._SetInitialSize()
+            self.plotpanel.prevXtrans = "x"
+            self.plotpanel.prevYtrans = "y"
+            
+            self.cmap = DEFAULT_CMAP
+            ## Create Artist and bind it
+            self.subplot = self.plotpanel.subplot
+            self._setup_layout()
+            if self.dimension == 1:
+                self.newplot = Data1D(x=data.x, y=data.y, 
+                                      dx=data.dx, dy=data.dy)
+                self.newplot.name = data.name
+            else:    
+                self.newplot = Data2D(image=self.data.data)
+                self.newplot.setValues(self.data)
+                    # Compute and get the image plot
+            self.get_plot()
+            #self.plotpanel.add_image(self.newplot) 
+            self.Centre()
+            self.Layout()
+    
+    def get_plot(self):
+        """
+        Get Plot panel
+        """
+        cal_plot = CalcPlot(panel=self.plotpanel, 
+                                   image=self.newplot, 
+                                   completefn=self.complete)
+        cal_plot.queue()
+    
+    def complete(self, panel, image, elapsed=None):
+        """
+        Plot image
+        
+        :param image: newplot [plotpanel]
+        """
+        wx.CallAfter(panel.add_image, image) 
+        
+    def _setup_layout(self):
+        """
+        Set up the layout
+        """
+        #  panel
+        sizer = wx.GridBagSizer(10, 10)
+        if self.dimension == 3:
+            note = "Note: I am very SLOW.     Please be PATIENT...\n"
+            if len(self.data.data) > 3600:
+                note += "Rotation disabled for pixels > 60x60."
+            note_txt = wx.StaticText(self, -1, note) 
+            note_txt.SetForegroundColour(wx.RED)
+            sizer.Add(note_txt, (0, 2), flag=wx.RIGHT|wx.TOP, border=5)
+        
+        sizer.Add(self.plotpanel, (1, 0), (9, 9), 
+                  wx.EXPAND|wx.ALL, 15)
+
+        sizer.AddGrowableCol(3)
+        sizer.AddGrowableRow(2)
+        
+        self.SetSizerAndFit(sizer)
+        self.Centre()
+        self.Show(True)
+        
+    def set_plot_unfocus(self):
+        """
+        Not implemented
+        """
+        pass
+    
+
+    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 OnClose(self, event):
+        """
+        """
+        try:
+            self.plotpanel.subplot.figure.clf()
+            self.plotpanel.Close()
+        except:
+            # when called by data panel
+            event.Skip()
+            pass
+
+                
+class Maskplotpanel(PlotPanel):
+    """
+    PlotPanel for Quick plot and masking plot
+    """
+    def __init__(self, parent, id=-1, dimension=2, 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.dimension = dimension
+        self.plots = {}
+        self.graph = Graph()
+        #add axis labels
+        self.graph.xaxis('\\rm{x} ', '')
+        self.graph.yaxis('\\rm{y} ', '')
+        
+    def add_toolbar(self):
+        """ 
+        Add toolbar
+        """
+        # Not implemented
+        pass
+    
+    def on_set_focus(self, event):
+        """
+        send to the parenet the current panel on focus
+        """
+        if self.dimension == 3:
+            pass
+        else:
+            self.draw()   
+         
+    def add_image(self, plot):
+        """
+        Add Image
+        """
+        self.plots[plot.name] = plot
+        #init graph
+        self.graph = Graph()
+        #add plot
+        self.graph.add(plot)
+        #add axes
+        if self.dimension == 1:
+            self.xaxis_label = '\\rm{x} '
+            self.xaxis_unit = ''
+            self.yaxis_label = '\\rm{y} '
+            self.yaxis_unit = ''
+        #draw
+        # message
+        status_type = 'progress' 
+        msg = 'Plotting...'
+        self._status_info(msg, status_type)
+        status_type = 'stop'           
+        self.graph.render(self)
+        self.subplot.figure.canvas.resizing = False
+        if self.dimension < 3:
+            self.graph.render(self)
+            self.subplot.figure.canvas.draw()
+        elif FONT_VARIANT:
+            self.subplot.figure.canvas.draw()
+        msg = 'Plotting Completed.'
+        self._status_info(msg, status_type)
+        
+    def onMouseMotion(self, event):
+        """
+        Disable dragging 2D image
+        """
+        pass
+    
+    def onWheel(self, event):
+        """
+        """
+        pass 
+     
+    def onLeftDown(self, event):
+        """
+        Disables LeftDown
+        """
+        pass
+    
+    def onPick(self, event):
+        """
+        Disables OnPick
+        """
+        pass
+    
+    def draw(self):
+        """
+        Draw
+        """
+        # message
+        status_type = 'progress' 
+        msg = 'Plotting...'
+        self._status_info(msg, status_type)
+        status_type = 'stop'           
+        
+        if self.dimension == 3:
+            pass
+        else:
+            self.subplot.figure.canvas.draw_idle() 
+        
+        msg = 'Plotting Completed.'
+        self._status_info(msg, status_type)
+       
+    def onContextMenu(self, event):
+        """
+        Default context menu for a plot panel
+        """
+        # Selective Slicer plot popup menu
+        slicerpop = wx.Menu()
+        
+        id = wx.NewId()
+        slicerpop.Append(id, '&Print Image', 'Print image')
+        wx.EVT_MENU(self, id, self.onPrint)
+
+        id = wx.NewId()
+        slicerpop.Append(id, '&Copy to Clipboard', 'Copy to the clipboard')
+        wx.EVT_MENU(self, id, self.OnCopyFigureMenu)
+        
+        if self.dimension == 1:
+            id = wx.NewId()
+            slicerpop.Append(id, '&Change Scale')
+            wx.EVT_MENU(self, id, self._onProperties)
+        else:
+            slicerpop.AppendSeparator()
+            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)
+        
+    def _status_info(self, msg = '', type = "update"):
+        """
+        Status msg
+        """
+        if type == "stop":
+            label = "Plotting..."
+            able = True
+        else:   
+            label = "Wait..."
+            able = False
+        if self.parent.parent.parent != None:
+                wx.PostEvent(self.parent.parent.parent, 
+                             StatusEvent(status = msg, type = type ))
+
+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: src/sas/guiframe/local_perspectives/plotting/plotting.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/plotting.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/guiframe/local_perspectives/plotting/plotting.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,351 @@
+
+
+
+################################################################################
+#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 sas.guiframe.events import EVT_NEW_PLOT
+from sas.guiframe.events import EVT_PLOT_QRANGE
+from sas.guiframe.events import StatusEvent 
+from sas.guiframe.events import DeletePlotPanelEvent
+from sas.guiframe.plugin_base import PluginBase
+from sas.guiframe.dataFitting import Data1D
+from sas.guiframe.dataFitting import Data2D
+from sas.guiframe.gui_manager import MDIFrame
+DEFAULT_MENU_ITEM_LABEL = "No graph available"
+DEFAULT_MENU_ITEM_ID = wx.NewId()
+
+IS_WIN = True    
+if sys.platform.count("win32")==0:
+    if int(str(wx.__version__).split('.')[0]) == 2:
+        if int(str(wx.__version__).split('.')[1]) < 9:
+            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
+        
+        """
+        return []
+        """
+        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)
+        self.parent.Bind(EVT_PLOT_QRANGE, self._on_plot_qrange)
+        # We have no initial panels for this plug-in
+        return []
+    
+    def _on_plot_qrange(self, event= None):
+        """
+        On Qmin Qmax vertical line event
+        """
+        if event == None:
+            return
+        if event.id in self.plot_panels.keys():
+            panel = self.plot_panels[event.id]
+        elif event.group_id in self.plot_panels.keys():
+            panel = self.plot_panels[event.group_id]
+        else:
+            return
+        panel.on_plot_qrange(event)
+            
+    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 = {}
+    
+    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]
+            for plottable in panel.graph.plottables.keys():
+                self.remove_plot(group_id, plottable.id)
+            panel.graph.reset()
+            return True
+        return False
+            
+    def hide_panel(self, group_id):
+        """
+        hide panel with group ID = group_id
+        """
+        # Not implemeted
+        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)
+
+        # Set UID to allow us to reference the panel later
+        new_panel.uid = event_id
+        # Ship the plottable to its panel
+        wx.CallAfter(new_panel.plot_data, data) 
+        #new_panel.canvas.set_resizing(new_panel.resizing)
+        self.plot_panels[new_panel.group_id] = new_panel
+        
+        # Set Graph menu and help string        
+        self.help_string = ' Graph: '
+        for plot in  new_panel.plots.itervalues():
+            help_string += (' ' + plot.label + ';')
+        #self.menu.AppendCheckItem(event_id, new_panel.window_caption, 
+        #                          helpString)
+        #self.menu.Check(event_id, IS_WIN)
+        #wx.EVT_MENU(self.parent, event_id, self._on_check_menu)
+
+        
+    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
+            win = MDIFrame(self.parent, None, 'None', (100, 200))
+            new_panel = ModelPanel1D(win, -1, xtransform=xtransform,
+                     ytransform=ytransform, style=wx.RAISED_BORDER)
+            win.set_panel(new_panel)
+            win.Show(False)
+            new_panel.frame = win
+            #win.Show(True)
+            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
+            win = MDIFrame(self.parent, None, 'None', (200, 150))
+            win.Show(False)
+            new_panel = ModelPanel2D(win, id = -1,
+                                data2d=data, scale = scale, 
+                                style=wx.RAISED_BORDER)
+            win.set_panel(new_panel)
+            new_panel.frame = win
+            #win.Show(True)
+            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"]) and False
+        flag_y = (panel.graph.prop["yunit"] is not None) and \
+                    (panel.graph.prop["yunit"].strip() != "") and\
+                    (y_unit != panel.graph.prop["yunit"]) and False
+        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)
+            wx.CallAfter(panel.plot_data, data)
+
+    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]
+            if uid in self.parent.plot_panels.keys():
+                del self.parent.plot_panels[uid]
+                panel.frame.Destroy()
+            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
+        
+        """
+        action_check = False
+        if hasattr(event, 'action'):
+            action_string = event.action.lower().strip()
+            if action_string == 'check':
+                action_check = True
+            else:
+                group_id = event.group_id
+                if group_id in self.plot_panels.keys():
+                    #remove data from panel
+                    if action_string == 'remove':
+                        id = event.id
+                        return self.remove_plot(group_id, id)
+                    if action_string == 'hide':
+                        return self.hide_panel(group_id)
+                    if action_string == 'delete':
+                        panel = self.plot_panels[group_id]
+                        uid = panel.uid
+                        return self.parent.delete_panel(uid)
+                    if action_string == "clear":
+                        return self.clear_panel_by_id(group_id)
+                    
+        if not hasattr(event, 'plot'):    
+            return
+        title = None
+        if hasattr(event, 'title'):
+            title = 'Graph'#event.title      
+        data = event.plot
+        group_id = data.group_id    
+        if group_id in self.plot_panels.keys():
+            if action_check:
+                # Check if the plot already exist. if it does, do nothing.
+                if data.id in self.plot_panels[group_id].plots.keys():
+                    return 
+            #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:
+                # Need to make the group_id consistent with 1D thus no if below
+                if len(self.plot_panels.values()) > 0:
+                    for p_group_id in self.plot_panels.keys():
+                        p_plot = self.plot_panels[p_group_id]
+                        if data.id in p_plot.plots.keys():
+                            p_plot.plots[data.id] = data
+                            self.plot_panels[group_id] = p_plot
+                            if group_id != p_group_id:
+                                del self.plot_panels[p_group_id]
+                                if p_group_id in data.list_group_id:
+                                    data.list_group_id.remove(p_group_id)
+                                if group_id not in data.list_group_id:
+                                    data.list_group_id.append(group_id)
+                            p_plot.group_id = group_id
+                            return
+                
+                new_panel = self.create_2d_panel(data, group_id)
+            self.create_panel_helper(new_panel, data, group_id, title) 
+        return
+       
+    def help(self, evt):
+        """
+        Show a general help dialog. 
+        """
+        from help_panel import  HelpWindow
+        frame = HelpWindow(None, -1) 
+        if hasattr(frame, "IsIconized"):
+            if not frame.IsIconized():
+                try:
+                    icon = self.parent.GetIcon()
+                    frame.SetIcon(icon)
+                except:
+                    pass  
+        frame.Show(True)
Index: src/sas/guiframe/local_perspectives/plotting/profile_dialog.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/profile_dialog.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/guiframe/local_perspectives/plotting/profile_dialog.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,329 @@
+"""
+SLD Profile Dialog for multifunctional models
+"""
+import wx
+import os
+import sys
+from copy import deepcopy
+from sas.plottools.plottables import Graph
+from Plotter1D import ModelPanel1D as PlotPanel
+from sas.guiframe.dataFitting import Data1D
+from sas.guiframe.gui_style import GUIFRAME_ID
+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 = "Scattering Length Density Profile"
+    ## Name to appear on the window title bar
+    window_caption = "Scattering Length Density 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 = parent
+            self._mgr = None
+            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 = Data1D(data_plot.x, data_plot.y, data_plot.dy)
+            self.newplot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
+            self.newplot.dy = None
+            self.newplot.name = 'SLD'
+            self.newplot.is_data = False
+            
+            self.newplot.id = self.newplot.name
+            self.plotpanel.add_image(self.newplot) 
+            
+            self.plotpanel.resizing = False
+            self.plotpanel.canvas.set_resizing(self.plotpanel.resizing)
+            
+            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.plotpanel.graph.render(self.plotpanel)
+            self.plotpanel.canvas.draw()
+
+    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_current_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
+    
+    def on_change_caption(self, name, old_caption, new_caption):
+        """
+        """
+        self.parent.parent.parent.on_change_caption(name, old_caption, new_caption)
+    
+    def disable_app_menu(self, panel):
+        """
+        Disable menu bar
+        """
+        # Not implemented!
+        return
+    
+    def show_data1d(self, data, name):
+        """
+        Show data dialog
+        """   
+        self.parent._manager.parent.show_data1d(data, name)
+        
+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
+        self.window_name = "Scattering Length Density Profile"
+        self.window_caption = self.window_name
+        self.prevXtrans = "x"
+        self.prevYtrans = "y"
+        self.viewModel = "--"
+        # 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])
+        self.xaxis_label = ''
+        self.xaxis_unit = ''
+        self.yaxis_label = ''
+        self.yaxis_unit = ''
+        self.resizing = True
+        self.xcolor = 'black'
+        self.ycolor = 'black'
+        
+    def add_image(self, plot):
+        """
+        Add image(Theory1D)
+        """
+        self.plots[plot.id] = plot
+        self.plots[plot.id].is_data = True
+        #add plot
+        self.graph.add(plot)
+        #add axes
+        x1_label = self.axes_label[0]
+        self.xaxis_label = '\\rm{%s} '% x1_label
+        self.xaxis_unit = 'A'
+        self.graph.xaxis(self.xaxis_label, self.xaxis_unit)
+        self.yaxis_label = '\\rm{SLD} '
+        self.yaxis_unit = '10^{-6}A^{-2}'
+        self.graph.yaxis(self.yaxis_label, self.yaxis_unit)
+        #self.subplot.figure.canvas.draw_idle()
+        # For latter scale changes 
+        self.plots[plot.id].xaxis('\\rm{%s} '% x1_label, 'A')
+        self.plots[plot.id].yaxis('\\rm{SLD} ', '10^{-6}A^{-2}')
+        #draw
+        #self.graph.render(self)
+        
+    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 
+    
+    def onChangeCaption(self, event):
+        """
+        Not implemented
+        """
+        pass
+                           
+    def _onSave(self, evt):
+        """
+        Save a data set to a text file
+        
+        :param evt: Menu event
+        
+        """
+        menu = evt.GetEventObject()
+        id = evt.GetId()
+        self.set_selected_from_menu(menu, id)
+        data = self.plots[self.graph.selected_plottable]
+        default_name = data.label
+        if default_name.count('.') > 0:
+            default_name = default_name.split('.')[0]
+        default_name += "_out"
+        if self.parent != None:
+            # What an ancestor!
+            fit_panel = self.parent.parent.parent
+            fit_panel._manager.parent.save_data1d(data, default_name)
+             
+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: src/sas/guiframe/local_perspectives/plotting/sectorMask.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/sectorMask.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/guiframe/local_perspectives/plotting/sectorMask.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -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 sas.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 sas.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: src/sas/guiframe/local_perspectives/plotting/slicerpanel.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/slicerpanel.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/guiframe/local_perspectives/plotting/slicerpanel.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,175 @@
+
+
+import wx
+import wx.lib.newevent
+#from copy import deepcopy
+from sas.guiframe.utils import format_number
+from sas.guiframe.events import SlicerParameterEvent
+from sas.guiframe.events import EVT_SLICER_PARS
+from sas.guiframe.events import EVT_SLICER
+
+from sas.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 = base.parent
+        self.frame = None
+        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 ["num_points", "avg", "avg_error", "sum",
+                                         "sum_error"]:
+                    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 ["num_points", "avg", "avg_error", "sum",
+                                      "sum_error"]:
+                    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()
+        psizer = self.parent.GetSizer()
+        if psizer != None:
+            psizer.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)
+
+    def on_close(self, event):
+        """
+        On Close Event
+        """
+        ID = self.uid
+        self.parent.delete_panel(ID)            
+        self.frame.Destroy()
