Index: sasview/local_config.py
===================================================================
--- sasview/local_config.py	(revision 73cbeec4f3f2dfd3c530e5782984529dc2c133b2)
+++ sasview/local_config.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -4,5 +4,5 @@
 import time
 import os
-from sas.sasgui.guiframe.gui_style import GUIFRAME
+#from sas.sasgui.guiframe.gui_style import GUIFRAME
 import sas.sasview
 import logging
@@ -108,6 +108,6 @@
 SPLASH_SCREEN_PATH = os.path.join(icon_path, "SVwelcome_mini.png")
 TUTORIAL_PATH = os.path.join(media_path, "Tutorial.pdf")
-DEFAULT_STYLE = GUIFRAME.MULTIPLE_APPLICATIONS|GUIFRAME.MANAGER_ON\
-                    |GUIFRAME.CALCULATOR_ON|GUIFRAME.TOOLBAR_ON
+#DEFAULT_STYLE = GUIFRAME.MULTIPLE_APPLICATIONS|GUIFRAME.MANAGER_ON\
+#                    |GUIFRAME.CALCULATOR_ON|GUIFRAME.TOOLBAR_ON
 SPLASH_SCREEN_WIDTH = 512
 SPLASH_SCREEN_HEIGHT = 366
Index: src/sas/qtgui/Calculators/GenericScatteringCalculator.py
===================================================================
--- src/sas/qtgui/Calculators/GenericScatteringCalculator.py	(revision 28a09b01c106f3e406a519f370a0c81b6cf9ee18)
+++ src/sas/qtgui/Calculators/GenericScatteringCalculator.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -8,19 +8,20 @@
 from PyQt4 import QtCore
 from twisted.internet import threads
+from mpl_toolkits.mplot3d import Axes3D
 
 import sas.qtgui.Utilities.GuiUtils as GuiUtils
 
-from sas.sasgui.perspectives.calculator.load_thread import GenReader
+from sas.qtgui.Utilities.GenericReader import GenReader
+
 from sas.sascalc.dataloader.data_info import Detector
 from sas.sascalc.dataloader.data_info import Source
 from sas.sascalc.calculator import sas_gen
 
-from mpl_toolkits.mplot3d import Axes3D
+from sas.qtgui.Plotting.Arrow3D import Arrow3D
 from sas.qtgui.Plotting.PlotterBase import PlotterBase
-from sas.sasgui.guiframe.dataFitting import Data2D
 from sas.qtgui.Plotting.Plotter2D import Plotter2D
-from sas.sasgui.guiframe.dataFitting import Data1D
 from sas.qtgui.Plotting.Plotter import Plotter
-from sas.sasgui.plottools.arrow3d import Arrow3D
+from sas.qtgui.Plotting.PlotterData import Data1D
+from sas.qtgui.Plotting.PlotterData import Data2D
 
 # Local UI
Index: src/sas/qtgui/Calculators/UnitTesting/GenericScatteringCalculatorTest.py
===================================================================
--- src/sas/qtgui/Calculators/UnitTesting/GenericScatteringCalculatorTest.py	(revision 0a6e0970e7c08e1b9779dce64e6f62ae8388a882)
+++ src/sas/qtgui/Calculators/UnitTesting/GenericScatteringCalculatorTest.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -19,5 +19,5 @@
 from sas.qtgui.Calculators.GenericScatteringCalculator import Plotter3D
 
-from sas.sasgui.guiframe.data_manager import DataManager
+from sas.qtgui.MainWindow.DataManager import DataManager
 from sas.qtgui.MainWindow.GuiManager import GuiManager
 from sas.qtgui.Utilities.GuiUtils import *
Index: src/sas/qtgui/MainWindow/DataExplorer.py
===================================================================
--- src/sas/qtgui/MainWindow/DataExplorer.py	(revision d60da0c225a08bdf2f30334bb6a79f7dbf1f63d6)
+++ src/sas/qtgui/MainWindow/DataExplorer.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -12,16 +12,19 @@
 from twisted.internet import threads
 
-# SAS
+# SASCALC
 from sas.sascalc.dataloader.loader import Loader
-from sas.sasgui.guiframe.data_manager import DataManager
-from sas.sasgui.guiframe.dataFitting import Data1D
-from sas.sasgui.guiframe.dataFitting import Data2D
-
+
+# QTGUI
 import sas.qtgui.Utilities.GuiUtils as GuiUtils
 import sas.qtgui.Plotting.PlotHelper as PlotHelper
+
+from sas.qtgui.Plotting.PlotterData import Data1D
+from sas.qtgui.Plotting.PlotterData import Data2D
 from sas.qtgui.Plotting.Plotter import Plotter
 from sas.qtgui.Plotting.Plotter2D import Plotter2D
+from sas.qtgui.Plotting.MaskEditor import MaskEditor
+
+from sas.qtgui.MainWindow.DataManager import DataManager
 from sas.qtgui.MainWindow.DroppableDataLoadWidget import DroppableDataLoadWidget
-from sas.qtgui.Plotting.MaskEditor import MaskEditor
 
 import sas.qtgui.Perspectives as Perspectives
Index: src/sas/qtgui/MainWindow/DataManager.py
===================================================================
--- src/sas/qtgui/MainWindow/DataManager.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
+++ src/sas/qtgui/MainWindow/DataManager.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -0,0 +1,461 @@
+################################################################################
+#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 2010, University of Tennessee
+################################################################################
+"""
+This module manages all data loaded into the application. Data_manager makes
+available all data loaded  for the current perspective.
+
+All modules "creating Data" posts their data to data_manager .
+Data_manager  make these new data available for all other perspectives.
+"""
+#
+# REDUNDANT CLASS!!!
+# ALL THE FUNCTIONALITY IS BEING MOVED TO GuiManager.py
+#
+import os
+import copy
+import logging
+import json
+import time
+from StringIO import StringIO
+import numpy as np
+
+from sas.qtgui.Plotting.PlotterData import Data1D
+from sas.qtgui.Plotting.PlotterData import Data2D
+from sas.qtgui.Plotting.Plottables import Plottable
+from sas.qtgui.Plotting.Plottables import PlottableTheory1D
+from sas.qtgui.Plotting.Plottables import PlottableFit1D
+from sas.qtgui.Plotting.Plottables import Text
+from sas.qtgui.Plotting.Plottables import Chisq
+from sas.qtgui.Plotting.Plottables import View
+
+from sas.qtgui.Utilities import GuiUtils
+from sas.qtgui.MainWindow.DataState import DataState
+import sas.sascalc.dataloader.data_info as DataInfo
+
+# used for import/export
+from sas.sascalc.dataloader.data_info import Sample, Source, Vector
+
+class DataManager(object):
+    """
+    Manage a list of data
+    """
+    def __init__(self):
+        """
+        Store opened path and data object created at the loading time
+        :param auto_plot: if True the datamanager sends data to plotting
+                            plugin.
+        :param auto_set_data: if True the datamanager sends to the current
+        perspective
+        """
+        self.stored_data = {}
+        self.message = ""
+        self.data_name_dict = {}
+        self.count = 0
+        self.list_of_id = []
+        self.time_stamp = time.time()
+
+    def __str__(self):
+        _str  = ""
+        _str += "No of states  is %s \n" % str(len(self.stored_data))
+        n_count = 0
+        for  value in self.stored_data.values():
+            n_count += 1
+            _str += "State No %s \n"  % str(n_count)
+            _str += str(value) + "\n"
+        return _str
+
+    def create_gui_data(self, data, path=None):
+        """
+        Receive data from loader and create a data to use for guiframe
+        """
+
+        if issubclass(Data2D, data.__class__):
+            new_plot = Data2D(image=None, err_image=None)
+        else:
+            new_plot = Data1D(x=[], y=[], dx=None, dy=None)
+
+        new_plot.copy_from_datainfo(data)
+        data.clone_without_data(clone=new_plot)
+        #creating a name for data
+        title = ""
+        file_name = os.path.basename(path) if path is not None else data.filename
+        if file_name:
+            name = file_name
+        elif data.run:
+            name = data.run[0]
+        else:
+            name = "data"
+        name = self.rename(name)
+        #find title
+        if data.title.strip():
+            title = data.title
+        if title.strip() == "":
+            title = file_name
+
+        if new_plot.filename.strip() == "":
+            new_plot.filename = file_name
+
+        new_plot.name = name
+        new_plot.title = title
+        ## allow to highlight data when plotted
+        new_plot.interactive = True
+        ## when 2 data have the same id override the 1 st plotted
+        self.time_stamp += 1
+        new_plot.id = str(name) + str(self.time_stamp)
+        ##group_id specify on which panel to plot this data
+        new_plot.group_id = str(name) + str(self.time_stamp)
+        new_plot.is_data = True
+        new_plot.path = path
+        new_plot.list_group_id = []
+        ##post data to plot
+        # plot data
+        return new_plot
+
+    def rename(self, name):
+        """
+        rename data
+        """
+        ## name of the data allow to differentiate data when plotted
+        name = GuiUtils.parseName(name=name, expression="_")
+
+        max_char = name.find("[")
+        if max_char < 0:
+            max_char = len(name)
+        name = name[0:max_char]
+
+        if name not in self.data_name_dict:
+            self.data_name_dict[name] = 0
+        else:
+            self.data_name_dict[name] += 1
+            name = name + " [" + str(self.data_name_dict[name]) + "]"
+        return name
+
+
+    def add_data(self, data_list):
+        """
+        receive a list of
+        """
+        for id, data in data_list.iteritems():
+            if id  in self.stored_data:
+                msg = "Data manager already stores %s" % str(data.name)
+                msg += ""
+                logging.info(msg)
+                data_state = self.stored_data[id]
+                data_state.data = data
+            else:
+                data_state = DataState(data)
+                data_state.id = id
+                data_state.path = data.path
+                self.stored_data[id] = data_state
+
+    def update_data(self, prev_data, new_data):
+        """
+        """
+        if prev_data.id not in self.stored_data.keys():
+            return None, {}
+        data_state = self.stored_data[prev_data.id]
+        self.stored_data[new_data.id]  = data_state.clone()
+        self.stored_data[new_data.id].data = new_data
+        if prev_data.id in self.stored_data.keys():
+            del self.stored_data[prev_data.id]
+        return prev_data.id, {new_data.id: self.stored_data[new_data.id]}
+
+    def update_theory(self, theory, data_id=None, state=None):
+        """
+        """
+        uid = data_id
+        if data_id is None and theory is not None:
+            uid = theory.id
+        if uid in self.stored_data.keys():
+             data_state = self.stored_data[uid]
+        else:
+            data_state = DataState()
+        data_state.uid = uid
+        data_state.set_theory(theory_data=theory, theory_state=state)
+        self.stored_data[uid] = data_state
+        return {uid: self.stored_data[uid]}
+
+
+    def get_message(self):
+        """
+        return message
+        """
+        return self.message
+
+    def get_by_id(self, id_list=None):
+        """
+        """
+        _selected_data = {}
+        _selected_theory_list = {}
+        if id_list is None:
+            return
+        for d_id in self.stored_data:
+            for search_id in id_list:
+                data_state = self.stored_data[d_id]
+                data = data_state.data
+                theory_list = data_state.get_theory()
+                if search_id == d_id:
+                    _selected_data[search_id] = data
+                if search_id in theory_list.keys():
+                     _selected_theory_list[search_id] = theory_list[search_id]
+
+        return _selected_data, _selected_theory_list
+
+
+    def freeze(self, theory_id):
+        """
+        """
+        return self.freeze_theory(self.stored_data.keys(), theory_id)
+
+    def freeze_theory(self, data_id, theory_id):
+        """
+        """
+        selected_theory = {}
+        for d_id in data_id:
+            if d_id in self.stored_data:
+                data_state = self.stored_data[d_id]
+                theory_list = data_state.get_theory()
+                for t_id in theory_id:
+                    if t_id in theory_list.keys():
+                        theory_data, theory_state = theory_list[t_id]
+                        new_theory = copy.deepcopy(theory_data)
+                        new_theory.id  = time.time()
+                        new_theory.is_data = True
+                        new_theory.name += '_@' + \
+                                    str(new_theory.id)[7:-1].replace('.', '')
+                        new_theory.title = new_theory.name
+                        new_theory.label = new_theory.name
+                        selected_theory[new_theory.id] = DataState(new_theory)
+                        self.stored_data[new_theory.id] = \
+                                    selected_theory[new_theory.id]
+
+        return selected_theory
+
+
+    def delete_data(self, data_id, theory_id=None, delete_all=False):
+        """
+        """
+        for d_id in data_id:
+            if d_id in self.stored_data.keys():
+                data_state = self.stored_data[d_id]
+                if data_state.data.name in self.data_name_dict:
+                    del self.data_name_dict[data_state.data.name]
+                del self.stored_data[d_id]
+
+        self.delete_theory(data_id, theory_id)
+        if delete_all:
+            self.stored_data = {}
+            self.data_name_dict = {}
+
+    def delete_theory(self, data_id, theory_id):
+        """
+        """
+        for d_id in data_id:
+            if d_id in self.stored_data:
+                data_state = self.stored_data[d_id]
+                theory_list = data_state.get_theory()
+                if theory_id in theory_list.keys():
+                    del theory_list[theory_id]
+        #del pure theory
+        self.delete_by_id(theory_id)
+
+    def delete_by_id(self, id_list=None):
+        """
+        save data and path
+        """
+        for id in id_list:
+            if id in self.stored_data:
+                del self.stored_data[id]
+
+
+    def get_by_name(self, name_list=None):
+        """
+        return a list of data given a list of data names
+        """
+        _selected_data = {}
+        for selected_name in name_list:
+            for id, data_state in self.stored_data.iteritems():
+                if data_state.data.name == selected_name:
+                    _selected_data[id] = data_state.data
+        return _selected_data
+
+    def delete_by_name(self, name_list=None):
+        """
+        save data and path
+        """
+        for selected_name in name_list:
+            for id, data_state in self.stored_data.iteritems():
+                if data_state.data.name == selected_name:
+                    del self.stored_data[id]
+
+    def get_data_state(self, data_id):
+        """
+        Send list of selected data
+        """
+        _selected_data_state = {}
+        for id in data_id:
+            if id in self.stored_data.keys():
+                _selected_data_state[id] = self.stored_data[id]
+        return _selected_data_state
+
+    def get_all_data(self):
+        """
+        return list of all available data
+        """
+        return self.stored_data
+
+    def assign(self, other):
+        self.stored_data = other.stored_data
+        self.message = other.message
+        self.data_name_dict = other.data_name_dict
+        self.count = other.count
+        self.list_of_id = other.list_of_id
+        self.time_stamp = other.time_stamp
+
+    def save_to_writable(self, fp):
+        """
+        save content of stored_data to fp (a .write()-supporting file-like object)
+        """
+
+        def add_type(dict, type):
+            dict['__type__'] = type.__name__
+            return dict
+
+        def jdefault(o):
+            """
+            objects that can't otherwise be serialized need to be converted
+            """
+            # tuples and sets (TODO: default JSONEncoder converts tuples to lists, create custom Encoder that preserves tuples)
+            if isinstance(o, (tuple, set)):
+                content = { 'data': list(o) }
+                return add_type(content, type(o))
+
+            # "simple" types
+            if isinstance(o, (Sample, Source, Vector)):
+                return add_type(o.__dict__, type(o))
+            if isinstance(o, (Plottable, View)):
+                return add_type(o.__dict__, type(o))
+
+            # DataState
+            if isinstance(o, DataState):
+                # don't store parent
+                content = o.__dict__.copy()
+                content.pop('parent')
+                return add_type(content, type(o))
+
+            # ndarray
+            if isinstance(o, np.ndarray):
+                buffer = StringIO()
+                np.save(buffer, o)
+                buffer.seek(0)
+                content = { 'data': buffer.read().decode('latin-1') }
+                return add_type(content, type(o))
+
+            # not supported
+            logging.info("data cannot be serialized to json: %s" % type(o))
+            return None
+
+        json.dump(self.stored_data, fp, indent=2, sort_keys=True, default=jdefault)
+
+
+    def load_from_readable(self, fp):
+        """
+        load content from tp to stored_data (a .read()-supporting file-like object)
+        """
+
+        supported = [
+            tuple, set,
+            Sample, Source, Vector,
+            Plottable, Data1D, Data2D, PlottableTheory1D, PlottableFit1D, Text, Chisq, View,
+            DataState, np.ndarray]
+
+        lookup = dict((cls.__name__, cls) for cls in supported)
+
+        class TooComplexException(Exception):
+            pass
+
+        def simple_type(cls, data, level):
+            class Empty(object):
+                def __init__(self):
+                    for key, value in data.iteritems():
+                        setattr(self, key, generate(value, level))
+
+            # create target object
+            o = Empty()
+            o.__class__ = cls
+
+            return o
+
+        def construct(type, data, level):
+            try:
+                cls = lookup[type]
+            except KeyError:
+                logging.info('unknown type: %s' % type)
+                return None
+
+            # tuples and sets
+            if cls in (tuple, set):
+                # convert list to tuple/set
+                return cls(generate(data['data'], level))
+
+            # "simple" types
+            if cls in (Sample, Source, Vector):
+                return simple_type(cls, data, level)
+            if issubclass(cls, Plottable) or (cls == View):
+                return simple_type(cls, data, level)
+
+            # DataState
+            if cls == DataState:
+                o = simple_type(cls, data, level)
+                o.parent = None # TODO: set to ???
+                return o
+
+            # ndarray
+            if cls == np.ndarray:
+                buffer = StringIO()
+                buffer.write(data['data'].encode('latin-1'))
+                buffer.seek(0)
+                return np.load(buffer)
+
+            logging.info('not implemented: %s, %s' % (type, cls))
+            return None
+
+        def generate(data, level):
+            if level > 16: # recursion limit (arbitrary number)
+                raise TooComplexException()
+            else:
+                level += 1
+
+            if isinstance(data, dict):
+                try:
+                    type = data['__type__']
+                except KeyError:
+                    # if dictionary doesn't have __type__ then it is assumed to be just an ordinary dictionary
+                    o = {}
+                    for key, value in data.iteritems():
+                        o[key] = generate(value, level)
+                    return o
+
+                return construct(type, data, level)
+
+            if isinstance(data, list):
+                return [generate(item, level) for item in data]
+
+            return data
+
+        new_stored_data = {}
+        for id, data in json.load(fp).iteritems():
+            try:
+                new_stored_data[id] = generate(data, 0)
+            except TooComplexException:
+                logging.info('unable to load %s' % id)
+
+        self.stored_data = new_stored_data
+
Index: src/sas/qtgui/MainWindow/DataState.py
===================================================================
--- src/sas/qtgui/MainWindow/DataState.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
+++ src/sas/qtgui/MainWindow/DataState.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -0,0 +1,108 @@
+################################################################################
+#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 2010, University of Tennessee
+################################################################################
+"""
+"""
+import copy
+
+
+class DataState(object):
+    """
+     Store information about data
+    """
+    def __init__(self, data=None, parent=None):
+        """
+        
+        """
+        self.parent = parent
+        self.data = data
+        self.name = ""
+        self.path = None
+        self.theory_list = {}
+        self.message = ""
+        self.id = None
+        
+    def __str__(self):
+        _str  = ""
+        _str += "State with ID : %s \n" % str(self.id)
+        if self.data is not None:
+            _str += "Data name : %s \n" % str(self.data.name)
+            _str += "Data ID : %s \n" % str(self.data.id)
+        _str += "Theories available: %s \n" % len(self.theory_list)
+        if self.theory_list:
+            for id, item in self.theory_list.iteritems():
+                theory_data, theory_state = item
+                _str += "Theory name : %s \n" % str(theory_data.name)
+                _str += "Theory ID : %s \n" % str(id)
+                _str += "Theory info: \n"
+                _str += str(theory_data)
+               
+        return _str
+        
+    def clone(self):
+        obj = DataState(copy.deepcopy(self.data))
+        obj.parent = self.parent
+        obj.name = self.name 
+        obj.path = self.path 
+        obj.message = self.message
+        obj.id = self.id
+        for id, item in self.theory_list.iteritems():
+            theory_data, theory_state = item
+            state = None
+            if theory_state is not None:
+                state = theory_state.clone()
+            obj.theory_list[id] = [copy.deepcopy(theory_data), 
+                                   state]
+        return obj
+        
+    def set_name(self, name):
+        self.name = name
+        
+    def get_name(self):
+        return self.name
+    
+    def set_data(self, data):
+        """
+        """
+        self.data = data
+  
+   
+    def get_data(self):
+        """
+        """
+        return self.data
+    
+    def set_path(self, path):
+        """
+        Set the path of the loaded data
+        """
+        self.path = path
+        
+    def get_path(self):
+        """
+        return the path of the loaded data
+        """
+        return self.path
+    
+    def set_theory(self, theory_data, theory_state=None):
+        """
+        """
+        self.theory_list[theory_data.id] = [theory_data, theory_state]
+        data, state = self.theory_list.values()[0]
+       
+    def get_theory(self):
+        return self.theory_list
+    
+    def get_message(self):
+        """
+        return message
+        """
+        return self.message
+    
+  
Index: src/sas/qtgui/MainWindow/GuiManager.py
===================================================================
--- src/sas/qtgui/MainWindow/GuiManager.py	(revision 2a432e7a07cf133afbed0e0395c9c9f3e27e041b)
+++ src/sas/qtgui/MainWindow/GuiManager.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -11,8 +11,7 @@
 
 from twisted.internet import reactor
+
 # General SAS imports
-
-from sas.sasgui.guiframe.data_manager import DataManager
-from sas.sasgui.guiframe.proxy import Connection
+from sas.qtgui.Utilities.ConnectionProxy import ConnectionProxy
 from sas.qtgui.Utilities.SasviewLogger import XStream
 from sas.qtgui.Utilities.IPythonWidget import IPythonWidget
@@ -24,4 +23,5 @@
 from sas.qtgui.MainWindow.AboutBox import AboutBox
 from sas.qtgui.MainWindow.WelcomePanel import WelcomePanel
+from sas.qtgui.MainWindow.DataManager import DataManager
 
 from sas.qtgui.Calculators.SldPanel import SldPanel
@@ -278,5 +278,5 @@
         """
         version_info = {"version": "0.0.0"}
-        c = Connection(LocalConfig.__update_URL__, LocalConfig.UPDATE_TIMEOUT)
+        c = ConnectionProxy(LocalConfig.__update_URL__, LocalConfig.UPDATE_TIMEOUT)
         response = c.connect()
         if response is None:
Index: src/sas/qtgui/MainWindow/UnitTesting/DataExplorerTest.py
===================================================================
--- src/sas/qtgui/MainWindow/UnitTesting/DataExplorerTest.py	(revision 83eb5208b8acb86b064162274de583670f72da47)
+++ src/sas/qtgui/MainWindow/UnitTesting/DataExplorerTest.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -13,7 +13,7 @@
 
 # Local
-from sas.sasgui.guiframe.dataFitting import Data1D
+from sas.qtgui.Plotting.PlotterData import Data1D
 from sas.sascalc.dataloader.loader import Loader
-from sas.sasgui.guiframe.data_manager import DataManager
+from sas.qtgui.MainWindow.DataManager import DataManager
 
 from sas.qtgui.MainWindow.DataExplorer import DataExplorerWindow
Index: src/sas/qtgui/Perspectives/Fitting/FitThread.py
===================================================================
--- src/sas/qtgui/Perspectives/Fitting/FitThread.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
+++ src/sas/qtgui/Perspectives/Fitting/FitThread.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -0,0 +1,103 @@
+import sys
+import time
+import copy
+import traceback
+
+from sas.sascalc.data_util.calcthread import CalcThread
+
+def map_getattr(classInstance, classFunc, *args):
+    """
+    Take an instance of a class and a function name as a string.
+    Execute class.function and return result
+    """
+    return  getattr(classInstance, classFunc)(*args)
+
+def map_apply(arguments):
+    return apply(arguments[0], arguments[1:])
+
+class FitThread(CalcThread):
+    """Thread performing the fit """
+
+    def __init__(self,
+                 fn,
+                 page_id,
+                 handler,
+                 batch_outputs,
+                 batch_inputs=None,
+                 pars=None,
+                 completefn=None,
+                 updatefn=None,
+                 yieldtime=0.03,
+                 worktime=0.03,
+                 reset_flag=False):
+        CalcThread.__init__(self,
+                 completefn,
+                 updatefn,
+                 yieldtime,
+                 worktime)
+        self.handler = handler
+        self.fitter = fn
+        self.pars = pars
+        self.batch_inputs = batch_inputs
+        self.batch_outputs = batch_outputs
+        self.page_id = page_id
+        self.starttime = time.time()
+        self.updatefn = updatefn
+        #Relative error desired in the sum of squares.
+        self.reset_flag = reset_flag
+
+    def isquit(self):
+        """
+        :raise KeyboardInterrupt: when the thread is interrupted
+        """
+        try:
+            CalcThread.isquit(self)
+        except KeyboardInterrupt:
+            msg = "Fitting: terminated by the user."
+            raise KeyboardInterrupt, msg
+
+    def compute(self):
+        """
+        Perform a fit
+        """
+        msg = ""
+        try:
+            fitter_size = len(self.fitter)
+            list_handler = [self.handler]*fitter_size
+            list_curr_thread = [self]*fitter_size
+            list_reset_flag = [self.reset_flag]*fitter_size
+            list_map_get_attr = [map_getattr]*fitter_size
+            list_fit_function = ['fit']*fitter_size
+            list_q = [None]*fitter_size
+            inputs = zip(list_map_get_attr, self.fitter, list_fit_function,
+                         list_q, list_q, list_handler, list_curr_thread,
+                         list_reset_flag)
+            result = map(map_apply, inputs)
+
+            self.complete(result=result,
+                          batch_inputs=self.batch_inputs,
+                          batch_outputs=self.batch_outputs,
+                          page_id=self.page_id,
+                          pars=self.pars,
+                          elapsed=time.time() - self.starttime)
+
+            return result[0], time.time()-self.starttime
+
+        except KeyboardInterrupt, msg:
+            # Thread was interrupted, just proceed and re-raise.
+            # Real code should not print, but this is an example...
+            #print "keyboard exception"
+            #Stop on exception during fitting. Todo: need to put 
+            #some mssg and reset progress bar.
+
+            # Shouldn't this be re-raising? ConsoleUpdate doesn't act on it.
+            # raise KeyboardInterrupt
+            if self.handler is not None:
+                self.handler.stop(msg=msg)
+        except Exception as ex:
+            if self.handler is not None:
+                self.handler.error(msg=traceback.format_exc())
+
+
+
+
Index: src/sas/qtgui/Perspectives/Fitting/FittingLogic.py
===================================================================
--- src/sas/qtgui/Perspectives/Fitting/FittingLogic.py	(revision 2add354096316e3381348cc6317654bf004353c2)
+++ src/sas/qtgui/Perspectives/Fitting/FittingLogic.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -1,6 +1,7 @@
 import numpy as np
 
-from sas.sasgui.guiframe.dataFitting import Data1D
-from sas.sasgui.guiframe.dataFitting import Data2D
+from sas.qtgui.Plotting.PlotterData import Data1D
+from sas.qtgui.Plotting.PlotterData import Data2D
+
 from sas.sascalc.dataloader.data_info import Detector
 from sas.sascalc.dataloader.data_info import Source
Index: src/sas/qtgui/Perspectives/Fitting/FittingPerspective.py
===================================================================
--- src/sas/qtgui/Perspectives/Fitting/FittingPerspective.py	(revision 1bc27f17eb5c791b5a6b67eab47f42f7ad6e5377)
+++ src/sas/qtgui/Perspectives/Fitting/FittingPerspective.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -7,5 +7,4 @@
 
 from sas.qtgui.Perspectives.Fitting.FittingWidget import FittingWidget
-#from FitPage import FitPage
 
 class FittingWindow(QtGui.QTabWidget):
Index: src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py
===================================================================
--- src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py	(revision 1bc27f17eb5c791b5a6b67eab47f42f7ad6e5377)
+++ src/sas/qtgui/Perspectives/Fitting/FittingUtilities.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -6,6 +6,6 @@
 import numpy
 
-from sas.sasgui.guiframe.dataFitting import Data1D
-from sas.sasgui.guiframe.dataFitting import Data2D
+from sas.qtgui.Plotting.PlotterData import Data1D
+from sas.qtgui.Plotting.PlotterData import Data2D
 
 def replaceShellName(param_name, value):
@@ -363,2 +363,24 @@
     return [i >> d & 1 for d in xrange(digits)]
 
+def getWeight(data, is2d, flag=None):
+    """
+    Received flag and compute error on data.
+    :param flag: flag to transform error of data.
+    """
+    weight = None
+    if is2d:
+        dy_data = data.err_data
+        data = data.data
+    else:
+        dy_data = data.dy
+        data = data.y
+
+    if flag == 0:
+        weight = numpy.ones_like(data)
+    elif flag == 1:
+        weight = dy_data
+    elif flag == 2:
+        weight = numpy.sqrt(numpy.abs(data))
+    elif flag == 3:
+        weight = numpy.abs(data)
+    return weight
Index: src/sas/qtgui/Perspectives/Fitting/FittingWidget.py
===================================================================
--- src/sas/qtgui/Perspectives/Fitting/FittingWidget.py	(revision 2a432e7a07cf133afbed0e0395c9c9f3e27e041b)
+++ src/sas/qtgui/Perspectives/Fitting/FittingWidget.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -17,15 +17,14 @@
 from sasmodels.sasview_model import load_standard_models
 from sas.sascalc.fit.BumpsFitting import BumpsFit as Fit
-from sas.sasgui.perspectives.fitting.fit_thread import FitThread
-
-from sas.sasgui.guiframe.CategoryInstaller import CategoryInstaller
-from sas.sasgui.guiframe.dataFitting import Data1D
-from sas.sasgui.guiframe.dataFitting import Data2D
+
 import sas.qtgui.Utilities.GuiUtils as GuiUtils
-from sas.sasgui.perspectives.fitting.model_thread import Calc1D
-from sas.sasgui.perspectives.fitting.model_thread import Calc2D
-from sas.sasgui.perspectives.fitting.utils import get_weight
+from sas.qtgui.Utilities.CategoryInstaller import CategoryInstaller
+from sas.qtgui.Plotting.PlotterData import Data1D
+from sas.qtgui.Plotting.PlotterData import Data2D
 
 from sas.qtgui.Perspectives.Fitting.UI.FittingWidgetUI import Ui_FittingWidgetUI
+from sas.qtgui.Perspectives.Fitting.FitThread import FitThread
+from sas.qtgui.Perspectives.Fitting.ModelThread import Calc1D
+from sas.qtgui.Perspectives.Fitting.ModelThread import Calc2D
 from sas.qtgui.Perspectives.Fitting.FittingLogic import FittingLogic
 from sas.qtgui.Perspectives.Fitting import FittingUtilities
@@ -815,5 +814,5 @@
         """
         # Send original data for weighting
-        weight = get_weight(data=data, is2d=self.is2D, flag=self.weighting)
+        weight = FittingUtilities.getWeight(data=data, is2d=self.is2D, flag=self.weighting)
         update_module = data.err_data if self.is2D else data.dy
         update_module = weight
Index: src/sas/qtgui/Perspectives/Fitting/ModelThread.py
===================================================================
--- src/sas/qtgui/Perspectives/Fitting/ModelThread.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
+++ src/sas/qtgui/Perspectives/Fitting/ModelThread.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -0,0 +1,276 @@
+"""
+    Calculation thread for modeling
+"""
+
+import time
+import numpy
+import math
+from sas.sascalc.data_util.calcthread import CalcThread
+from sas.sascalc.fit.MultiplicationModel import MultiplicationModel
+
+class Calc2D(CalcThread):
+    """
+    Compute 2D model
+    This calculation assumes a 2-fold symmetry of the model
+    where points are computed for one half of the detector
+    and I(qx, qy) = I(-qx, -qy) is assumed.
+    """
+    def __init__(self, data, model, smearer, qmin, qmax, page_id,
+                 state=None,
+                 weight=None,
+                 fid=None,
+                 toggle_mode_on=False,
+                 completefn=None,
+                 updatefn=None,
+                 update_chisqr=True,
+                 source='model',
+                 yieldtime=0.04,
+                 worktime=0.04,
+                 exception_handler=None,
+                 ):
+        CalcThread.__init__(self, completefn, updatefn, yieldtime, worktime,
+                            exception_handler=exception_handler)
+        self.qmin = qmin
+        self.qmax = qmax
+        self.weight = weight
+        self.fid = fid
+        #self.qstep = qstep
+        self.toggle_mode_on = toggle_mode_on
+        self.data = data
+        self.page_id = page_id
+        self.state = None
+        # the model on to calculate
+        self.model = model
+        self.smearer = smearer
+        self.starttime = 0
+        self.update_chisqr = update_chisqr
+        self.source = source
+
+    def compute(self):
+        """
+        Compute the data given a model function
+        """
+        self.starttime = time.time()
+        # Determine appropriate q range
+        if self.qmin == None:
+            self.qmin = 0
+        if self.qmax == None:
+            if self.data != None:
+                newx = math.pow(max(math.fabs(self.data.xmax),
+                                   math.fabs(self.data.xmin)), 2)
+                newy = math.pow(max(math.fabs(self.data.ymax),
+                                   math.fabs(self.data.ymin)), 2)
+                self.qmax = math.sqrt(newx + newy)
+
+        if self.data is None:
+            msg = "Compute Calc2D receive data = %s.\n" % str(self.data)
+            raise ValueError, msg
+
+        # Define matrix where data will be plotted
+        radius = numpy.sqrt((self.data.qx_data * self.data.qx_data) + \
+                    (self.data.qy_data * self.data.qy_data))
+
+        # For theory, qmax is based on 1d qmax 
+        # so that must be mulitified by sqrt(2) to get actual max for 2d
+        index_model = (self.qmin <= radius) & (radius <= self.qmax)
+        index_model = index_model & self.data.mask
+        index_model = index_model & numpy.isfinite(self.data.data)
+
+        if self.smearer is not None:
+            # Set smearer w/ data, model and index.
+            fn = self.smearer
+            fn.set_model(self.model)
+            fn.set_index(index_model)
+            # Calculate smeared Intensity
+            #(by Gaussian averaging): DataLoader/smearing2d/Smearer2D()
+            value = fn.get_value()
+        else:
+            # calculation w/o smearing
+            value = self.model.evalDistribution([
+                self.data.qx_data[index_model],
+                self.data.qy_data[index_model]
+            ])
+        output = numpy.zeros(len(self.data.qx_data))
+        # output default is None
+        # This method is to distinguish between masked
+        #point(nan) and data point = 0.
+        output = output / output
+        # set value for self.mask==True, else still None to Plottools
+        output[index_model] = value
+        elapsed = time.time() - self.starttime
+        #self.complete(image=output,
+        #               data=self.data,
+        #               page_id=self.page_id,
+        #               model=self.model,
+        #               state=self.state,
+        #               toggle_mode_on=self.toggle_mode_on,
+        #               elapsed=elapsed,
+        #               index=index_model,
+        #               fid=self.fid,
+        #               qmin=self.qmin,
+        #               qmax=self.qmax,
+        #               weight=self.weight,
+        #               #qstep=self.qstep,
+        #               update_chisqr=self.update_chisqr,
+        #               source=self.source)
+        return (output,
+                self.data,
+                self.page_id,
+                self.model,
+                self.state,
+                self.toggle_mode_on,
+                elapsed,
+                index_model,
+                self.fid,
+                self.qmin,
+                self.qmax,
+                self.weight,
+                self.update_chisqr,
+                self.source)
+
+
+class Calc1D(CalcThread):
+    """
+    Compute 1D data
+    """
+    def __init__(self, model,
+                 page_id,
+                 data,
+                 fid=None,
+                 qmin=None,
+                 qmax=None,
+                 weight=None,
+                 smearer=None,
+                 toggle_mode_on=False,
+                 state=None,
+                 completefn=None,
+                 update_chisqr=True,
+                 source='model',
+                 updatefn=None,
+                 yieldtime=0.01,
+                 worktime=0.01,
+                 exception_handler=None,
+                 ):
+        """
+        """
+        CalcThread.__init__(self, completefn, updatefn, yieldtime, worktime,
+                            exception_handler=exception_handler)
+        self.fid = fid
+        self.data = data
+        self.qmin = qmin
+        self.qmax = qmax
+        self.model = model
+        self.weight = weight
+        self.toggle_mode_on = toggle_mode_on
+        self.state = state
+        self.page_id = page_id
+        self.smearer = smearer
+        self.starttime = 0
+        self.update_chisqr = update_chisqr
+        self.source = source
+        self.out = None
+        self.index = None
+
+    def compute(self):
+        """
+        Compute model 1d value given qmin , qmax , x value
+        """
+        self.starttime = time.time()
+        output = numpy.zeros((len(self.data.x)))
+        index = (self.qmin <= self.data.x) & (self.data.x <= self.qmax)
+
+        # If we use a smearer, also return the unsmeared model
+        unsmeared_output = None
+        unsmeared_data = None
+        unsmeared_error = None
+        ##smearer the ouput of the plot
+        if self.smearer is not None:
+            first_bin, last_bin = self.smearer.get_bin_range(self.qmin,
+                                                             self.qmax)
+            mask = self.data.x[first_bin:last_bin+1]
+            unsmeared_output = numpy.zeros((len(self.data.x)))
+            unsmeared_output[first_bin:last_bin+1] = self.model.evalDistribution(mask)
+            output = self.smearer(unsmeared_output, first_bin, last_bin)
+
+            # Rescale data to unsmeared model
+            # Check that the arrays are compatible. If we only have a model but no data,
+            # the length of data.y will be zero.
+            if isinstance(self.data.y, numpy.ndarray) and output.shape == self.data.y.shape:
+                unsmeared_data = numpy.zeros((len(self.data.x)))
+                unsmeared_error = numpy.zeros((len(self.data.x)))
+                unsmeared_data[first_bin:last_bin+1] = self.data.y[first_bin:last_bin+1]\
+                                                        * unsmeared_output[first_bin:last_bin+1]\
+                                                        / output[first_bin:last_bin+1]
+                unsmeared_error[first_bin:last_bin+1] = self.data.dy[first_bin:last_bin+1]\
+                                                        * unsmeared_output[first_bin:last_bin+1]\
+                                                        / output[first_bin:last_bin+1]
+                unsmeared_output=unsmeared_output[index]
+                unsmeared_data=unsmeared_data[index]
+                unsmeared_error=unsmeared_error
+        else:
+            output[index] = self.model.evalDistribution(self.data.x[index])
+
+        sq_values = None
+        pq_values = None
+        s_model = None
+        p_model = None
+        if isinstance(self.model, MultiplicationModel):
+            s_model = self.model.s_model
+            p_model = self.model.p_model
+        elif hasattr(self.model, "get_composition_models"):
+            p_model, s_model = self.model.get_composition_models()
+
+        if p_model is not None and s_model is not None:
+            sq_values = numpy.zeros((len(self.data.x)))
+            pq_values = numpy.zeros((len(self.data.x)))
+            sq_values[index] = s_model.evalDistribution(self.data.x[index])
+            pq_values[index] = p_model.evalDistribution(self.data.x[index])
+
+        elapsed = time.time() - self.starttime
+
+        #self.complete(x=self.data.x[index], y=output[index],
+        #              page_id=self.page_id,
+        #              state=self.state,
+        #              weight=self.weight,
+        #              fid=self.fid,
+        #              toggle_mode_on=self.toggle_mode_on,
+        #              elapsed=elapsed, index=index, model=self.model,
+        #              data=self.data,
+        #              update_chisqr=self.update_chisqr,
+        #              source=self.source)
+        return (self.data.x[index], output[index],
+                self.page_id,
+                self.state,
+                self.weight,
+                self.fid,
+                self.toggle_mode_on,
+                elapsed, index, self.model,
+                self.data,
+                self.update_chisqr,
+                self.source)
+
+        # TODO: as of 4.1, the output contains more items:
+        # unsmeared_* and pq_model/sq_model
+        # Need to add these too
+
+        #self.complete(x=self.data.x[index], y=output[index],
+        #              page_id=self.page_id,
+        #              state=self.state,
+        #              weight=self.weight,
+        #              fid=self.fid,
+        #              toggle_mode_on=self.toggle_mode_on,
+        #              elapsed=elapsed, index=index, model=self.model,
+        #              data=self.data,
+        #              update_chisqr=self.update_chisqr,
+        #              source=self.source,
+        #              unsmeared_model=unsmeared_output,
+        #              unsmeared_data=unsmeared_data,
+        #              unsmeared_error=unsmeared_error,
+        #              pq_model=pq_values,
+        #              sq_model=sq_values)
+
+    def results(self):
+        """
+        Send resuts of the computation
+        """
+        return [self.out, self.index]
Index: src/sas/qtgui/Perspectives/Fitting/OptionsWidget.py
===================================================================
--- src/sas/qtgui/Perspectives/Fitting/OptionsWidget.py	(revision 1bc27f17eb5c791b5a6b67eab47f42f7ad6e5377)
+++ src/sas/qtgui/Perspectives/Fitting/OptionsWidget.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -6,5 +6,5 @@
 from PyQt4 import QtCore
 
-from sas.sasgui.guiframe.dataFitting import Data2D
+from sas.qtgui.Plotting.PlotterData import Data2D
 
 # Local UI
Index: src/sas/qtgui/Perspectives/Fitting/SmearingWidget.py
===================================================================
--- src/sas/qtgui/Perspectives/Fitting/SmearingWidget.py	(revision 1bc27f17eb5c791b5a6b67eab47f42f7ad6e5377)
+++ src/sas/qtgui/Perspectives/Fitting/SmearingWidget.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -5,6 +5,6 @@
 from PyQt4 import QtCore
 
-from sas.sasgui.guiframe.dataFitting import Data1D
-from sas.sasgui.guiframe.dataFitting import Data2D
+from sas.qtgui.Plotting.PlotterData import Data1D
+from sas.qtgui.Plotting.PlotterData import Data2D
 
 # Local UI
Index: src/sas/qtgui/Perspectives/Fitting/UnitTesting/FittingLogicTest.py
===================================================================
--- src/sas/qtgui/Perspectives/Fitting/UnitTesting/FittingLogicTest.py	(revision 83eb5208b8acb86b064162274de583670f72da47)
+++ src/sas/qtgui/Perspectives/Fitting/UnitTesting/FittingLogicTest.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -11,5 +11,5 @@
 from sas.qtgui.Utilities.GuiUtils import *
 from sas.qtgui.Perspectives.Fitting.FittingWidget import *
-from sas.sasgui.guiframe.dataFitting import Data1D
+from sas.qtgui.Plotting.PlotterData import Data1D
 
 app = QtGui.QApplication(sys.argv)
Index: src/sas/qtgui/Perspectives/Fitting/UnitTesting/FittingPerspectiveTest.py
===================================================================
--- src/sas/qtgui/Perspectives/Fitting/UnitTesting/FittingPerspectiveTest.py	(revision 1bc27f17eb5c791b5a6b67eab47f42f7ad6e5377)
+++ src/sas/qtgui/Perspectives/Fitting/UnitTesting/FittingPerspectiveTest.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -13,6 +13,5 @@
 # Local
 import sas.qtgui.Utilities.GuiUtils as GuiUtils
-from sas.sasgui.guiframe.dataFitting import Data1D
-
+from sas.qtgui.Plotting.PlotterData import Data1D
 from sas.qtgui.Perspectives.Fitting.FittingPerspective import FittingWindow
 
Index: src/sas/qtgui/Perspectives/Fitting/UnitTesting/FittingUtilitiesTest.py
===================================================================
--- src/sas/qtgui/Perspectives/Fitting/UnitTesting/FittingUtilitiesTest.py	(revision 92083461dbc9920c21fcff743c7db790730dfe0c)
+++ src/sas/qtgui/Perspectives/Fitting/UnitTesting/FittingUtilitiesTest.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -3,6 +3,6 @@
 from PyQt4 import QtGui
 
-from sas.sasgui.guiframe.dataFitting import Data1D
-from sas.sasgui.guiframe.dataFitting import Data2D
+from sas.qtgui.Plotting.PlotterData import Data1D
+from sas.qtgui.Plotting.PlotterData import Data2D
 
 from UnitTesting.TestUtils import WarningTestNotImplemented
Index: src/sas/qtgui/Perspectives/Fitting/UnitTesting/FittingWidgetTest.py
===================================================================
--- src/sas/qtgui/Perspectives/Fitting/UnitTesting/FittingWidgetTest.py	(revision 224113016392685e3f7af17220091d29d434e19e)
+++ src/sas/qtgui/Perspectives/Fitting/UnitTesting/FittingWidgetTest.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -17,6 +17,6 @@
 from sas.qtgui.UnitTesting.TestUtils import QtSignalSpy
 
-from sas.sasgui.guiframe.dataFitting import Data1D
-from sas.sasgui.guiframe.dataFitting import Data2D
+from sas.qtgui.Plotting.PlotterData import Data1D
+from sas.qtgui.Plotting.PlotterData import Data2D
 
 app = QtGui.QApplication(sys.argv)
Index: src/sas/qtgui/Perspectives/Fitting/ViewDelegate.py
===================================================================
--- src/sas/qtgui/Perspectives/Fitting/ViewDelegate.py	(revision 2a432e7a07cf133afbed0e0395c9c9f3e27e041b)
+++ src/sas/qtgui/Perspectives/Fitting/ViewDelegate.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
Index: src/sas/qtgui/Perspectives/Invariant/InvariantPerspective.py
===================================================================
--- src/sas/qtgui/Perspectives/Invariant/InvariantPerspective.py	(revision 83eb5208b8acb86b064162274de583670f72da47)
+++ src/sas/qtgui/Perspectives/Invariant/InvariantPerspective.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -11,5 +11,5 @@
 # sas-global
 from sas.sascalc.invariant import invariant
-from sas.sasgui.guiframe.dataFitting import Data1D
+from sas.qtgui.Plotting.PlotterData import Data1D
 import sas.qtgui.Utilities.GuiUtils as GuiUtils
 
Index: src/sas/qtgui/Plotting/Arrow3D.py
===================================================================
--- src/sas/qtgui/Plotting/Arrow3D.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
+++ src/sas/qtgui/Plotting/Arrow3D.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -0,0 +1,69 @@
+"""
+Module that draws multiple arrows in 3D coordinates
+"""
+
+from matplotlib.patches import FancyArrowPatch
+from mpl_toolkits.mplot3d import proj3d
+import time
+# from matplotlib.artist import allow_rasterization
+class Arrow3D(FancyArrowPatch):
+    """
+    Draw 3D arrow
+    """
+
+    def __init__(self, base, xs, ys, zs, colors, *args, **kwargs):
+        """
+        Init
+
+        :Params xs: [[x0, x0+dx0], [x1, x1+dx1], ...]
+        :Params ys: [[y0, y0+dy0], [y1, y1+dy1], ...]
+        :Params zs: [[z0, z0+dz0], [z1, z1+dz1], ...]
+        :Params colors: [[R0, G0, B0], [R1, G1, B1], ...]
+        where R, G, B ranges (0,1)
+        """
+        FancyArrowPatch.__init__(self, (0, 0), (0, 0), *args, **kwargs)
+        self.leftdown = False
+        self.t_click = 0
+        self._verts3d = xs, ys, zs
+        self.colors = colors
+        self.base = base
+
+        if base != None:
+            # To turn the updating off during dragging
+            base.canvas.mpl_connect('button_press_event', self.on_left_down)
+            base.canvas.mpl_connect('button_release_event', self.on_left_up)
+
+    def on_left_down(self, event):
+        """
+            Mouse left-down event
+        """
+        self.leftdown = True
+        self.t_click = time.time()
+
+    def on_left_up(self, event):
+        """
+            Mouse left up event
+        """
+        t_up = time.time() - self.t_click
+        # Avoid just clicking
+        if t_up > 0.1:
+            self.leftdown = False
+            self.base.canvas.draw()
+
+    def draw(self, renderer, rasterized=True):
+        """
+        Drawing actually happens here
+        """
+        # Draws only when the dragging finished
+        if self.leftdown:
+            return
+        xs3d, ys3d, zs3d = self._verts3d
+        for i in xrange(len(xs3d)):
+            xs, ys, _ = proj3d.proj_transform(xs3d[i], ys3d[i], zs3d[i], renderer.M)
+            self.set_positions((xs[0], ys[0]), (xs[1], ys[1]))
+            self.set_color(self.colors[i])
+            FancyArrowPatch.draw(self, renderer)
+
+        self.leftdown = False
+
+
Index: src/sas/qtgui/Plotting/Binder.py
===================================================================
--- src/sas/qtgui/Plotting/Binder.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
+++ src/sas/qtgui/Plotting/Binder.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -0,0 +1,397 @@
+"""
+Extension to MPL to support the binding of artists to key/mouse events.
+"""
+import sys
+import logging
+
+class Selection(object):
+    """
+    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(object):
+    """
+    """
+    # 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),
+            ]
+
+        self._current = None
+        self._actions = {}
+        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:
+            logging.error("Error disconnection canvas: %s" % sys.exc_value)
+        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
+
+        # 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))
+        found = Selection()
+        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
+
+        # 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:
+                if transform.__class__.__name__ == "IdentityTransform":
+                    x, y = transform.inverted().transform((x, y))
+                else:
+                    # # For interactive plottable apply transform is not working
+                    # # don't know why maybe marker definition
+                    # #transform ="CompositeGenericTransform" crash
+                    pass
+            except:
+                # # CRUFT matplotlib-0.91 support
+                # # exception for transform ="CompositeGenericTransform"
+                # # crashes also here
+                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)
+            self.trigger(found, 'motion', event)
+
+    def _onClick(self, event):
+        """
+        Process button click
+        """
+        import time
+
+        # Check for double-click
+        event_time = time.time()
+        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/qtgui/Plotting/ColorMap.py
===================================================================
--- src/sas/qtgui/Plotting/ColorMap.py	(revision cd2cc7458970e179039a783e9409f48c4c1fca0d)
+++ src/sas/qtgui/Plotting/ColorMap.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -11,5 +11,5 @@
 
 from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
-from sas.sasgui.guiframe.dataFitting import Data2D
+from sas.qtgui.Plotting.PlotterData import Data2D
 from sas.qtgui.Utilities.GuiUtils import formatNumber
 from rangeSlider import RangeSlider
Index: src/sas/qtgui/Plotting/ConvertUnits.py
===================================================================
--- src/sas/qtgui/Plotting/ConvertUnits.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
+++ src/sas/qtgui/Plotting/ConvertUnits.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -0,0 +1,80 @@
+"""
+    Convert units to strings that can be displayed
+    This is a cleaned up version of unitConverter.py
+"""
+import re
+import string
+
+def convertUnit(power, unit):
+    """
+        Convert units to strings that can be displayed
+    """
+    if power != 0:
+        if string.find(unit, "^") != -1:  # if the unit contains a powerer ^
+            toks = re.split("\^", unit)
+            if string.find(toks[0], "/") != -1 or \
+                string.find(toks[0], "-") != -1:
+                if power == 1:
+                    unit = unit
+                else:
+                    unit = "(" + unit + ")" + "^{" + str(power) + "}"
+            else:
+                if string.find(toks[1], "{") != -1:  # if found a {
+                    find_power_toks = re.split("{", toks[1])
+                    if string.find(find_power_toks[1], "}") != -1:  # found }
+                        unit_toks = re.split("}", find_power_toks[1])
+                        if string.find(unit_toks[0], ".") != -1:
+                            powerer = float(unit_toks[0]) * power
+                        elif string.find(unit_toks[0], "/") != -1:
+                            power_toks = re.split("/", unit_toks[0])
+                            powerer = power * int(power_toks[0])\
+                                            / int(power_toks[1])
+                        else:
+                            powerer = int(unit_toks[0]) * power
+
+                        if powerer == 1.0:
+                            unit = toks[0]
+                        elif powerer == 0.5:
+                            unit = toks[0] + "^{1/2}"
+                        elif powerer == -0.5:
+                            unit = toks[0] + "^{-1/2}"
+                        else:
+                            unit = toks[0] + "^{" + str(powerer) + "}"
+                else:
+                    raise ValueError, "missing } in unit expression"
+        else:  # no powerer
+            if  power != 1:
+                unit = "(" + unit + ")" + "^{" + str(power) + "}"
+    else:
+        raise ValueError, "empty unit ,enter a powerer different from zero"
+    return unit
+
+
+if __name__ == "__main__":
+    # pylint: disable=invalid-name
+    # Input   ->  new scale  ->  Output
+    unit1 = "A^{-1} "  #             x                    A^{-1}
+    unit2 = "A"  #                   x                     A
+    unit3 = "A"  #                   x^2                  A^{2}
+    unit4 = "A "  #                  1/x                  A^{-1}
+    unit5 = "A^{0.5} "  #        x^2                      A
+    unit9 = "m^{1/2}"  #         x^2               m
+
+    # If you don't recognize the pattern, give up
+    # and just put some parentheses around the unit and write the transoformation:
+
+    unit6 = "m/s"  #                x^2               (m/s)^{2}
+    unit7 = "m/s^{2}"  #         1/x                 (m/s^{2})^{-1}
+    unit8 = "m/s^{4}"  #         x^2               (m/s^{4})^{2}
+
+    print "this unit1 %s ,its powerer %s , and value %s" % (unit1, 1, convert_unit(1, unit1))
+    print "this unit2 %s ,its powerer %s , and value %s" % (unit2, 1, convert_unit(1, unit2))
+    print "this unit3 %s ,its powerer %s , and value %s" % (unit3, 2, convert_unit(2, unit3))
+    print "this unit4 %s ,its powerer %s , and value %s" % (unit4, -1, convert_unit(-1, unit4))
+    print "this unit5 %s ,its powerer %s , and value %s" % (unit5, 2, convert_unit(2, unit5))
+    print "this unit6 %s ,its powerer %s , and value %s" % (unit6, 2, convert_unit(2, unit6))
+    print "this unit7 %s ,its powerer %s , and value %s" % (unit7, -1, convert_unit(-1, unit7))
+    print "this unit8 %s ,its powerer %s , and value %s" % (unit8, 2, convert_unit(2, unit8))
+    print "this unit9 %s ,its powerer %s , and value %s" % (unit9, 2, convert_unit(2, unit9))
+
+
Index: src/sas/qtgui/Plotting/DataTransform.py
===================================================================
--- src/sas/qtgui/Plotting/DataTransform.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
+++ src/sas/qtgui/Plotting/DataTransform.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -0,0 +1,414 @@
+import math
+
+
+def toX(x, y=None):
+    """
+    This function is used to load value on Plottable.View
+
+    :param x: Float value
+
+    :return: x
+
+    """
+    return x
+
+
+def toX_pos(x, y=None):
+    """
+    This function is used to load value on Plottable.View
+
+    :param x: Float value
+
+    :return: x
+
+    """
+    if not x > 0:
+        raise ValueError, "Transformation only accepts positive values."
+    else:
+        return x
+
+
+def toX2(x, y=None):
+    """
+    This function is used to load value on Plottable.View
+
+    Calculate x^(2)
+
+    :param x: float value
+
+    """
+    return x * x
+
+
+def fromX2(x, y=None):
+    """
+    This function is used to load value on Plottable.View
+    Calculate square root of x
+
+    :param x: float value
+
+    """
+    if not x >= 0:
+        raise ValueError, "square root of a negative value "
+    else:
+        return math.sqrt(x)
+
+
+def toX4(x, y=None):
+    """
+    This function is used to load value on Plottable.View
+
+    Calculate x^(4)
+
+    :param x: float value
+
+    """
+    return x * x * x * x
+
+
+def fromX4(x, y=None):
+    """
+    This function is used to load value on Plottable.View
+    Calculate square root of x
+
+    :param x: float value
+
+    """
+    if not x >= 0:
+        raise ValueError, "double square root of a negative value "
+    else:
+        return math.sqrt(math.sqrt(x))
+
+
+def toLogX(x, y=None):
+    """
+    This function is used to load value on Plottable.View
+    calculate log x
+
+    :param x: float value
+
+    """
+    if not x > 0:
+        raise ValueError, "Log(x)of a negative value "
+    else:
+        return math.log(x)
+
+def toOneOverX(x, y=None):
+    """
+    """
+    if x != 0:
+        return 1 / x
+    else:
+        raise ValueError, "cannot divide by zero"
+
+
+def toOneOverSqrtX(y, x=None):
+    """
+    """
+    if y > 0:
+        return 1 / math.sqrt(y)
+    else:
+        raise ValueError, "transform.toOneOverSqrtX: cannot be computed"
+
+
+def toLogYX2(y, x):
+    """
+    """
+    if (y * (x ** 2)) > 0:
+        return math.log(y * (x ** 2))
+    else:
+        raise ValueError, "transform.toLogYX2: cannot be computed"
+
+
+def toLogYX4(y, x):
+    """
+    """
+    if (math.pow(x, 4) * y) > 0:
+        return math.log(math.pow(x, 4) * y)
+    else:
+        raise ValueError, "transform.toLogYX4: input error"
+
+
+def toYX4(y, x):
+    """
+    """
+    return math.pow(x, 4) * y
+
+def toYX2(y, x):
+    """
+    """
+    return math.pow(x, 2) * y
+
+def toLogXY(y, x):
+    """
+    This function is used to load value on Plottable.View
+    calculate log x
+
+    :param x: float value
+
+    """
+    if not (x * y) > 0:
+        raise ValueError, "Log(X*Y)of a negative value "
+    else:
+        return math.log(x * y)
+
+
+def errToX(x, y=None, dx=None, dy=None):
+    """
+    calculate error of x**2
+
+    :param x: float value
+    :param dx: float value
+
+    """
+    if dx == None:
+        dx = 0
+    return dx
+
+
+def errToX_pos(x, y=None, dx=None, dy=None):
+    """
+    calculate error of x**2
+
+    :param x: float value
+    :param dx: float value
+
+    """
+    if dx == None:
+        dx = 0
+    return dx
+
+
+def errToX2(x, y=None, dx=None, dy=None):
+    """
+    calculate error of x**2
+
+    :param x: float value
+    :param dx: float value
+
+    """
+    if  dx != None:
+        err = 2 * x * dx
+        return math.fabs(err)
+    else:
+        return 0.0
+
+
+def errFromX2(x, y=None, dx=None, dy=None):
+    """
+    calculate error of sqrt(x)
+
+    :param x: float value
+    :param dx: float value
+
+    """
+    if x > 0:
+        if dx != None:
+            err = dx / (2 * math.sqrt(x))
+        else:
+            err = 0
+        return math.fabs(err)
+    else:
+        msg = "transform.errFromX2: can't compute error of negative x"
+        raise ValueError, msg
+
+
+def errToX4(x, y=None, dx=None, dy=None):
+    """
+    calculate error of x**4
+
+    :param x: float value
+    :param dx: float value
+
+    """
+    if dx != None:
+        err = 4 * math.pow(x, 3) * dx
+        return math.fabs(err)
+    else:
+        return 0.0
+
+
+def errFromX4(x, y=None, dx=None, dy=None):
+    """
+    calculate error of x^1/4
+
+    :param x: float value
+    :param dx: float value
+
+    """
+    if x > 0:
+        if dx != None:
+            err = dx / (4 * math.pow(x, 3 / 4))
+        else:
+            err = 0
+        return math.fabs(err)
+    else:
+        msg = "transform.errFromX4: can't compute error of negative x"
+        raise ValueError, msg
+
+
+def errToLog10X(x, y=None, dx=None, dy=None):
+    """
+    calculate error of Log(x)
+
+    :param x: float value
+    :param dx: float value
+
+    """
+    if dx == None:
+        dx = 0
+
+    # Check that the point on the graph is positive
+    # within errors
+    if not (x - dx) > 0:
+        msg = "Transformation does not accept"
+        msg += " point that are consistent with zero."
+        raise ValueError, msg
+    if x != 0:
+        dx = dx / (x * math.log(10))
+    else:
+        raise ValueError, "errToLogX: divide by zero"
+    return dx
+
+
+def errToLogX(x, y=None, dx=None, dy=None):
+    """
+    calculate error of Log(x)
+
+    :param x: float value
+    :param dx: float value
+
+    """
+    if dx == None:
+        dx = 0
+
+    # Check that the x point on the graph is zero
+    if x != 0:
+        dx = dx / x
+    else:
+        raise ValueError, "errToLogX: divide by zero"
+    return dx
+
+
+def errToYX2(y, x, dy=None, dx=None):
+    """
+    """
+    if dx == None:
+        dx = 0
+    if dy == None:
+        dy = 0
+    err = math.sqrt((2 * x * y * dx) ** 2 + ((x ** 2) * dy) ** 2)
+    return err
+
+
+def errToLogXY(x, y, dx=None, dy=None):
+    """
+    calculate error of Log(xy)
+
+    """
+    # Check that the point on the graph is positive
+    # within errors
+    if not (x - dx) > 0 or not (y - dy) > 0:
+        msg = "Transformation does not accept point "
+        msg += " that are consistent with zero."
+        raise ValueError, msg
+    if x != 0 and y != 0:
+        if dx == None:
+            dx = 0
+        if dy == None:
+            dy = 0
+        err = (dx / x) ** 2 + (dy / y) ** 2
+    else:
+        raise ValueError, "cannot compute this error"
+
+    return math.sqrt(math.fabs(err))
+
+
+def errToLogYX2(y, x, dy=None, dx=None):
+    """
+    calculate error of Log(yx**2)
+
+    """
+    # Check that the point on the graph is positive
+    # within errors
+    if not (x - dx) > 0 or not (y - dy) > 0:
+        msg = "Transformation does not accept point"
+        msg += " that are consistent with zero."
+        raise ValueError, msg
+    if x > 0 and y > 0:
+        if dx == None:
+            dx = 0
+        if dy == None:
+            dy = 0
+        err = (2.0 * dx / x) ** 2 + (dy / y) ** 2
+    else:
+        raise ValueError, "cannot compute this error"
+    return math.sqrt(math.fabs(err))
+
+
+def errOneOverX(x, y=None, dx=None, dy=None):
+    """
+    calculate error on 1/x
+
+    """
+    if x != 0:
+        if dx == None:
+            dx = 0
+        err = dx / x ** 2
+    else:
+        raise ValueError, "Cannot compute this error"
+    return math.fabs(err)
+
+
+def errOneOverSqrtX(x, y=None, dx=None, dy=None):
+    """
+    Calculate error on 1/sqrt(x)
+
+    """
+    if x > 0:
+        if dx == None:
+            dx = 0
+        err = -1 / 2 * math.pow(x, -3.0 / 2.0) * dx
+    else:
+        raise ValueError, "Cannot compute this error"
+    return math.fabs(err)
+
+
+def errToLogYX4(y, x, dy=None, dx=None):
+    """
+    error for ln(y*x^(4))
+
+    :param x: float value
+
+    """
+    # Check that the point on the graph is positive
+    # within errors
+    if (not (x - dx) > 0) or (not (y - dy) > 0):
+        msg = "Transformation does not accept point "
+        msg += " that are consistent with zero."
+        raise ValueError, msg
+    if dx == None:
+        dx = 0
+    if dy == None:
+        dy = 0
+    err = math.sqrt((4.0 * dx / x) ** 2 + (dy / y) ** 2)
+    return err
+
+
+def errToYX4(y, x, dy=None, dx=None):
+    """
+    error for (y*x^(4))
+
+    :param x: float value
+
+    """
+    # Check that the point on the graph is positive
+    # within errors
+
+    if dx == None:
+        dx = 0
+    if dy == None:
+        dy = 0
+    err = math.sqrt((dy * pow(x, 4)) ** 2 + (4 * y * dx * math.pow(x, 3)) ** 2)
+    return err
+
Index: src/sas/qtgui/Plotting/Fittings.py
===================================================================
--- src/sas/qtgui/Plotting/Fittings.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
+++ src/sas/qtgui/Plotting/Fittings.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -0,0 +1,110 @@
+"""
+This module is used to fit a set of x,y data to a model passed to it. It is
+used to calculate the slope and intercepts for the linearized fits.  Two things
+should be noted:
+
+First, this fitting module uses the NLLSQ module of SciPy rather than a linear
+fit.  This along with a few other modules could probably be removed if we
+move to a linear regression approach.
+
+Second, this infrastructure does not allow for resolution smearing of the 
+the models.  Hence the results are not that accurate even for pinhole
+collimation of SANS but may be good for SAXS.  It is completely wrong for 
+slit smeared data. 
+
+"""
+from scipy import optimize
+
+
+class Parameter(object):
+    """
+    Class to handle model parameters - sets the parameters and their
+    initial value from the model based to it.
+    """
+    def __init__(self, model, name, value=None):
+        self.model = model
+        self.name = name
+        if not value == None:
+            self.model.setParam(self.name, value)
+
+    def set(self, value):
+        """
+            Set the value of the parameter
+        """
+        self.model.setParam(self.name, value)
+
+    def __call__(self):
+        """
+            Return the current value of the parameter
+        """
+        return self.model.getParam(self.name)
+
+
+def sasfit(model, pars, x, y, err_y, qmin=None, qmax=None):
+    """
+    Fit function
+
+    :param model: sas model object
+    :param pars: list of parameters
+    :param x: vector of x data
+    :param y: vector of y data
+    :param err_y: vector of y errors
+    """
+    def f(params):
+        """
+        Calculates the vector of residuals for each point
+        in y for a given set of input parameters.
+
+        :param params: list of parameter values
+        :return: vector of residuals
+        """
+        i = 0
+        for p in pars:
+            p.set(params[i])
+            i += 1
+
+        residuals = []
+        for j in range(len(x)):
+            if x[j] >= qmin and x[j] <= qmax:
+                residuals.append((y[j] - model.runXY(x[j])) / err_y[j])
+        return residuals
+
+    def chi2(params):
+        """
+        Calculates chi^2
+
+        :param params: list of parameter values
+
+        :return: chi^2
+
+        """
+        sum = 0
+        res = f(params)
+        for item in res:
+            sum += item * item
+        return sum
+
+    p = [param() for param in pars]
+    out, cov_x, info, mesg, success = optimize.leastsq(f, p, full_output=1)
+    # Calculate chi squared
+    if len(pars) > 1:
+        chisqr = chi2(out)
+    elif len(pars) == 1:
+        chisqr = chi2([out])
+
+    return chisqr, out, cov_x
+
+
+def calcCommandline(event):
+    # Testing implementation
+    # Fit a Line model
+    from LineModel import LineModel
+    line = LineModel()
+    cstA = Parameter(line, 'A', event.cstA)
+    cstB = Parameter(line, 'B', event.cstB)
+    y = line.run()
+    chisqr, out, cov = sasfit(line, [cstA, cstB], event.x, y, 0)
+    # print "Output parameters:", out
+    print "The right answer is [70.0, 1.0]"
+    print chisqr, out, cov
+
Index: src/sas/qtgui/Plotting/LineModel.py
===================================================================
--- src/sas/qtgui/Plotting/LineModel.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
+++ src/sas/qtgui/Plotting/LineModel.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -0,0 +1,108 @@
+#!/usr/bin/env python
+"""
+Provide Line function (y= Ax + B). Until July 10, 2016 this function provided
+(y= A + Bx).  This however was contrary to all the other code using it which 
+assumed (y= mx+b) or in this nomenclature (y=Ax + B). This lead to some
+contortions in the code and worse incorrect calculations until now for at least
+some of the functions.  This seemed the easiest to fix particularly since this
+function should disappear in a future iteration (see notes in fitDialog)
+
+                -PDB   July 10, 2016
+"""
+
+import math
+
+class LineModel(object):
+    """
+    Class that evaluates a linear model.
+
+    f(x) = Ax + B
+
+    List of default parameters:
+    A = 1.0
+    B = 1.0
+    """
+
+    def __init__(self):
+        """ Initialization """
+        # # Name of the model
+        self.name = "LineModel"
+
+        # # Define parameters
+        self.params = {}
+        self.params['A'] = 1.0
+        self.params['B'] = 1.0
+
+        # # Parameter details [units, min, max]
+        self.details = {}
+        self.details['A'] = ['', None, None]
+        self.details['B'] = ['', None, None]
+
+    def getParam(self, name):
+        """
+            Return parameter value
+        """
+        return self.params[name.upper()]
+
+    def setParam(self, name, value):
+        """
+            Set parameter value
+        """
+        self.params[name.upper()] = value
+
+    def _line(self, x):
+        """
+        Evaluate the function
+
+        :param x: x-value
+
+        :return: function value
+
+        """
+        return  (self.params['A'] * x) + self.params['B']
+
+    def run(self, x=0.0):
+        """
+        Evaluate the model
+
+        :note: This is the function called by fitDialog to calculate the
+        the y(xmin) and y(xmax), but the only difference between this and
+        runXY is when the if statement is true. I however cannot see what that
+        function is for.  It needs to be documented here or removed.
+        -PDB 7/10/16 
+
+        :param x: simple value
+
+        :return: (Line value)
+        """
+        if x.__class__.__name__ == 'list':
+            return self._line(x[0] * math.cos(x[1])) * \
+                                self._line(x[0] * math.sin(x[1]))
+        elif x.__class__.__name__ == 'tuple':
+            msg = "Tuples are not allowed as input to BaseComponent models"
+            raise ValueError, msg
+        else:
+            return self._line(x)
+
+    def runXY(self, x=0.0):
+        """
+        Evaluate the model.
+        
+        :note: This is to be what is called by fitDialog for the actual fit
+        but the only difference between this and run is when the if 
+        statement is true. I however cannot see what that function
+        is for.  It needs to be documented here or removed. -PDB 7/10/16 
+
+        :param x: simple value
+
+        :return: Line value
+
+        """
+        if x.__class__.__name__ == 'list':
+            return self._line(x[0]) * self._line(x[1])
+        elif x.__class__.__name__ == 'tuple':
+            msg = "Tuples are not allowed as input to BaseComponent models"
+            raise ValueError, msg
+        else:
+            return self._line(x)
+
Index: src/sas/qtgui/Plotting/LinearFit.py
===================================================================
--- src/sas/qtgui/Plotting/LinearFit.py	(revision cd2cc7458970e179039a783e9409f48c4c1fca0d)
+++ src/sas/qtgui/Plotting/LinearFit.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -7,10 +7,9 @@
 from PyQt4 import QtCore
 
-#from sas.sasgui.transform import 
 from sas.qtgui.Utilities.GuiUtils import formatNumber
-from sas.sasgui.plottools import fittings
-from sas.sasgui.plottools import transform
-
-from sas.sasgui.plottools.LineModel import LineModel
+
+from sas.qtgui.Plotting import Fittings
+from sas.qtgui.Plotting import DataTransform
+from sas.qtgui.Plotting.LineModel import LineModel
 
 # Local UI
@@ -67,7 +66,7 @@
         self.default_A = self.model.getParam('A')
         self.default_B = self.model.getParam('B')
-        self.cstA = fittings.Parameter(self.model, 'A', self.default_A)
-        self.cstB = fittings.Parameter(self.model, 'B', self.default_B)
-        self.transform = transform
+        self.cstA = Fittings.Parameter(self.model, 'A', self.default_A)
+        self.cstB = Fittings.Parameter(self.model, 'B', self.default_B)
+        self.transform = DataTransform
 
         self.setFixedSize(self.minimumSizeHint())
@@ -123,6 +122,6 @@
 
         # Find the fitting parameters
-        self.cstA = fittings.Parameter(self.model, 'A', self.default_A)
-        self.cstB = fittings.Parameter(self.model, 'B', self.default_B)
+        self.cstA = Fittings.Parameter(self.model, 'A', self.default_A)
+        self.cstB = Fittings.Parameter(self.model, 'B', self.default_B)
         tempdy = numpy.asarray(tempdy)
         tempdy[tempdy == 0] = 1
@@ -132,5 +131,5 @@
             xmax = numpy.log10(xmax)
 
-        chisqr, out, cov = fittings.sasfit(self.model,
+        chisqr, out, cov = Fittings.sasfit(self.model,
                                            [self.cstA, self.cstB],
                                            tempx, tempy, tempdy,
@@ -193,9 +192,9 @@
                 tempy  = [numpy.log10(y[i])
                          for i in range(len(x)) if x[i] >= xmin_check]
-                tempdy = [transform.errToLogX(y[i], 0, dy[i], 0)
+                tempdy = [DataTransform.errToLogX(y[i], 0, dy[i], 0)
                          for i in range(len(x)) if x[i] >= xmin_check]
             else:
                 tempy = map(numpy.log10, y)
-                tempdy = map(lambda t1,t2:transform.errToLogX(t1,0,t2,0),y,dy)
+                tempdy = map(lambda t1,t2:DataTransform.errToLogX(t1,0,t2,0),y,dy)
         else:
             tempy = y
Index: src/sas/qtgui/Plotting/MaskEditor.py
===================================================================
--- src/sas/qtgui/Plotting/MaskEditor.py	(revision cd2cc7458970e179039a783e9409f48c4c1fca0d)
+++ src/sas/qtgui/Plotting/MaskEditor.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -1,5 +1,5 @@
 from PyQt4 import QtGui
 
-from sas.sasgui.guiframe.dataFitting import Data2D
+from sas.qtgui.Plotting.PlotterData import Data2D
 
 # Local UI
Index: src/sas/qtgui/Plotting/Plottables.py
===================================================================
--- src/sas/qtgui/Plotting/Plottables.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
+++ src/sas/qtgui/Plotting/Plottables.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -0,0 +1,1222 @@
+"""
+Prototype plottable object support.
+
+The main point of this prototype is to provide a clean separation between
+the style (plotter details: color, grids, widgets, etc.) and substance
+(application details: which information to plot).  Programmers should not be
+dictating line colours and plotting symbols.
+
+Unlike the problem of style in CSS or Word, where most paragraphs look
+the same, each line on a graph has to be distinguishable from its neighbours.
+Our solution is to provide parametric styles, in which a number of
+different classes of object (e.g., reflectometry data, reflectometry
+theory) representing multiple graph primitives cycle through a colour
+palette provided by the underlying plotter.
+
+A full treatment would provide perceptual dimensions of prominence and
+distinctiveness rather than a simple colour number.
+
+"""
+
+# Design question: who owns the color?
+# Is it a property of the plottable?
+# Or of the plottable as it exists on the graph?
+# Or if the graph?
+# If a plottable can appear on multiple graphs, in some case the
+# color should be the same on each graph in which it appears, and
+# in other cases (where multiple plottables from different graphs
+# coexist), the color should be assigned by the graph.  In any case
+# once a plottable is placed on the graph its color should not
+# depend on the other plottables on the graph.  Furthermore, if
+# a plottable is added and removed from a graph and added again,
+# it may be nice, but not necessary, to have the color persist.
+#
+# The safest approach seems to be to give ownership of color
+# to the graph, which will allocate the colors along with the
+# plottable.  The plottable will need to return the number of
+# colors that are needed.
+#
+# The situation is less clear for symbols.  It is less clear
+# how much the application requires that symbols be unique across
+# all plots on the graph.
+
+# Support for ancient python versions
+import copy
+import numpy
+import sys
+import logging
+
+if 'any' not in dir(__builtins__):
+    def any(L):
+        for cond in L:
+            if cond:
+                return True
+        return False
+
+    def all(L):
+        for cond in L:
+            if not cond:
+                return False
+        return True
+
+
+class Graph(object):
+    """
+    Generic plottables graph structure.
+
+    Plot styles are based on color/symbol lists.  The user gets to select
+    the list of colors/symbols/sizes to choose from, not the application
+    developer.  The programmer only gets to add/remove lines from the
+    plot and move to the next symbol/color.
+
+    Another dimension is prominence, which refers to line sizes/point sizes.
+
+    Axis transformations allow the user to select the coordinate view
+    which provides clarity to the data.  There is no way we can provide
+    every possible transformation for every application generically, so
+    the plottable objects themselves will need to provide the transformations.
+    Here are some examples from reflectometry: ::
+
+       independent: x -> f(x)
+          monitor scaling: y -> M*y
+          log:  y -> log(y if y > min else min)
+          cos:  y -> cos(y*pi/180)
+       dependent:   x -> f(x,y)
+          Q4:      y -> y*x^4
+          fresnel: y -> y*fresnel(x)
+       coordinated: x,y = f(x,y)
+          Q:    x -> 2*pi/L (cos(x*pi/180) - cos(y*pi/180))
+                y -> 2*pi/L (sin(x*pi/180) + sin(y*pi/180))
+       reducing: x,y = f(x1,x2,y1,y2)
+          spin asymmetry: x -> x1, y -> (y1 - y2)/(y1 + y2)
+          vector net: x -> x1, y -> y1*cos(y2*pi/180)
+
+    Multiple transformations are possible, such as Q4 spin asymmetry
+
+    Axes have further complications in that the units of what are being
+    plotted should correspond to the units on the axes.  Plotting multiple
+    types on the same graph should be handled gracefully, e.g., by creating
+    a separate tab for each available axis type, breaking into subplots,
+    showing multiple axes on the same plot, or generating inset plots.
+    Ultimately the decision should be left to the user.
+
+    Graph properties such as grids/crosshairs should be under user control,
+    as should the sizes of items such as axis fonts, etc.  No direct
+    access will be provided to the application.
+
+    Axis limits are mostly under user control.  If the user has zoomed or
+    panned then those limits are preserved even if new data is plotted.
+    The exception is when, e.g., scanning through a set of related lines
+    in which the user may want to fix the limits so that user can compare
+    the values directly.  Another exception is when creating multiple
+    graphs sharing the same limits, though this case may be important
+    enough that it is handled by the graph widget itself.  Axis limits
+    will of course have to understand the effects of axis transformations.
+
+    High level plottable objects may be composed of low level primitives.
+    Operations such as legend/hide/show copy/paste, etc. need to operate
+    on these primitives as a group.  E.g., allowing the user to have a
+    working canvas where they can drag lines they want to save and annotate
+    them.
+
+    Graphs need to be printable.  A page layout program for entire plots
+    would be nice.
+
+    """
+    def _xaxis_transformed(self, name, units):
+        """
+        Change the property of the x axis
+        according to an axis transformation
+        (as opposed to changing the basic properties)
+        """
+        if units != "":
+            name = "%s (%s)" % (name, units)
+        self.prop["xlabel"] = name
+        self.prop["xunit"] = units
+
+    def _yaxis_transformed(self, name, units):
+        """
+        Change the property of the y axis
+        according to an axis transformation
+        (as opposed to changing the basic properties)
+        """
+        if units != "":
+            name = "%s (%s)" % (name, units)
+        self.prop["ylabel"] = name
+        self.prop["yunit"] = units
+
+    def xaxis(self, name, units):
+        """
+        Properties of the x axis.
+        """
+        if units != "":
+            name = "%s (%s)" % (name, units)
+        self.prop["xlabel"] = name
+        self.prop["xunit"] = units
+        self.prop["xlabel_base"] = name
+        self.prop["xunit_base"] = units
+
+    def yaxis(self, name, units):
+        """
+        Properties of the y axis.
+        """
+        if units != "":
+            name = "%s (%s)" % (name, units)
+        self.prop["ylabel"] = name
+        self.prop["yunit"] = units
+        self.prop["ylabel_base"] = name
+        self.prop["yunit_base"] = units
+
+    def title(self, name):
+        """
+        Graph title
+        """
+        self.prop["title"] = name
+
+    def get(self, key):
+        """
+        Get the graph properties
+        """
+        if key == "color":
+            return self.color
+        elif key == "symbol":
+            return self.symbol
+        else:
+            return self.prop[key]
+
+    def set(self, **kw):
+        """
+        Set the graph properties
+        """
+        for key in kw:
+            if key == "color":
+                self.color = kw[key] % len(self.colorlist)
+            elif key == "symbol":
+                self.symbol = kw[key] % len(self.symbollist)
+            else:
+                self.prop[key] = kw[key]
+
+    def isPlotted(self, plottable):
+        """Return True is the plottable is already on the graph"""
+        if plottable in self.plottables:
+            return True
+        return False
+
+    def add(self, plottable, color=None):
+        """Add a new plottable to the graph"""
+        # record the colour associated with the plottable
+        if not plottable in self.plottables:
+            if color is not None:
+                self.plottables[plottable] = color
+            else:
+                self.color += plottable.colors()
+                self.plottables[plottable] = self.color
+                plottable.custom_color = self.color
+
+    def changed(self):
+        """Detect if any graphed plottables have changed"""
+        return any([p.changed() for p in self.plottables])
+
+    def get_range(self):
+        """
+        Return the range of all displayed plottables
+        """
+        min_value = None
+        max_value = None
+        for p in self.plottables:
+            if p.hidden == True:
+                continue
+            if not p.x == None:
+                for x_i in p.x:
+                    if min_value == None or x_i < min_value:
+                        min_value = x_i
+                    if max_value == None or x_i > max_value:
+                        max_value = x_i
+        return min_value, max_value
+
+    def replace(self, plottable):
+        """Replace an existing plottable from the graph"""
+        selected_color = None
+        selected_plottable = None
+        for p in self.plottables.keys():
+            if plottable.id == p.id:
+                selected_plottable = p
+                selected_color = self.plottables[p]
+                break
+        if  selected_plottable is not None and selected_color is not None:
+            del self.plottables[selected_plottable]
+            self.plottables[plottable] = selected_color
+
+    def delete(self, plottable):
+        """Remove an existing plottable from the graph"""
+        if plottable in self.plottables:
+            del self.plottables[plottable]
+            self.color = len(self.plottables)
+
+    def reset_scale(self):
+        """
+        Resets the scale transformation data to the underlying data
+        """
+        for p in self.plottables:
+            p.reset_view()
+
+    def reset(self):
+        """Reset the graph."""
+        self.color = -1
+        self.symbol = 0
+        self.prop = {"xlabel": "", "xunit": None,
+                     "ylabel": "", "yunit": None,
+                     "title": ""}
+        self.plottables = {}
+
+    def _make_labels(self):
+        """
+        """
+        # Find groups of related plottables
+        sets = {}
+        for p in self.plottables:
+            if p.__class__ in sets:
+                sets[p.__class__].append(p)
+            else:
+                sets[p.__class__] = [p]
+        # Ask each plottable class for a set of unique labels
+        labels = {}
+        for c in sets:
+            labels.update(c.labels(sets[c]))
+        return labels
+
+    def get_plottable(self, name):
+        """
+        Return the plottable with the given
+        name if it exists. Otherwise return None
+        """
+        for item in self.plottables:
+            if item.name == name:
+                return item
+        return None
+
+    def returnPlottable(self):
+        """
+        This method returns a dictionary of plottables contained in graph
+        It is just by Plotpanel to interact with the complete list of plottables
+        inside the graph.
+        """
+        return self.plottables
+
+    def render(self, plot):
+        """Redraw the graph"""
+        plot.connect.clearall()
+        plot.clear()
+        plot.properties(self.prop)
+        labels = self._make_labels()
+        for p in self.plottables:
+            if p.custom_color is not None:
+                p.render(plot, color=p.custom_color, symbol=0,
+                         markersize=p.markersize, label=labels[p])
+            else:
+                p.render(plot, color=self.plottables[p], symbol=0,
+                         markersize=p.markersize, label=labels[p])
+        plot.render()
+
+    def __init__(self, **kw):
+        self.reset()
+        self.set(**kw)
+        # Name of selected plottable, if any
+        self.selected_plottable = None
+
+
+# Transform interface definition
+# No need to inherit from this class, just need to provide
+# the same methods.
+class Transform(object):
+    """
+    Define a transform plugin to the plottable architecture.
+
+    Transforms operate on axes.  The plottable defines the
+    set of transforms available for it, and the axes on which
+    they operate.  These transforms can operate on the x axis
+    only, the y axis only or on the x and y axes together.
+
+    This infrastructure is not able to support transformations
+    such as log and polar plots as these require full control
+    over the drawing of axes and grids.
+
+    A transform has a number of attributes.
+
+    name
+      user visible name for the transform.  This will
+      appear in the context menu for the axis and the transform
+      menu for the graph.
+
+    type
+      operational axis.  This determines whether the
+      transform should appear on x,y or z axis context
+      menus, or if it should appear in the context menu for
+      the graph.
+
+    inventory
+      (not implemented)
+      a dictionary of user settable parameter names and
+      their associated types.  These should appear as keyword
+      arguments to the transform call.  For example, Fresnel
+      reflectivity requires the substrate density:
+      ``{ 'rho': type.Value(10e-6/units.angstrom**2) }``
+      Supply reasonable defaults in the callback so that
+      limited plotting clients work even though they cannot
+      set the inventory.
+
+    """
+    def __call__(self, plottable, **kwargs):
+        """
+        Transform the data.  Whenever a plottable is added
+        to the axes, the infrastructure will apply all required
+        transforms.  When the user selects a different representation
+        for the axes (via menu, script, or context menu), all
+        plottables on the axes will be transformed.  The
+        plottable should store the underlying data but set
+        the standard x,dx,y,dy,z,dz attributes appropriately.
+
+        If the call raises a NotImplemented error the dataline
+        will not be plotted.  The associated string will usually
+        be 'Not a valid transform', though other strings are possible.
+        The application may or may not display the message to the
+        user, along with an indication of which plottable was at fault.
+
+        """
+        raise NotImplemented, "Not a valid transform"
+
+    # Related issues
+    # ==============
+    #
+    # log scale:
+    #    All axes have implicit log/linear scaling options.
+    #
+    # normalization:
+    #    Want to display raw counts vs detector efficiency correction
+    #    Want to normalize by time/monitor/proton current/intensity.
+    #    Want to display by eg. counts per 3 sec or counts per 10000 monitor.
+    #    Want to divide by footprint (ab initio, fitted or measured).
+    #    Want to scale by attenuator values.
+    #
+    # compare/contrast:
+    #    Want to average all visible lines with the same tag, and
+    #    display difference from one particular line.  Not a transform
+    #    issue?
+    #
+    # multiline graph:
+    #    How do we show/hide data parts.  E.g., data or theory, or
+    #    different polarization cross sections?  One way is with
+    #    tags: each plottable has a set of tags and the tags are
+    #    listed as check boxes above the plotting area.  Click a
+    #    tag and all plottables with that tag are hidden on the
+    #    plot and on the legend.
+    #
+    # nonconformant y-axes:
+    #    What do we do with temperature vs. Q and reflectivity vs. Q
+    #    on the same graph?
+    #
+    # 2D -> 1D:
+    #    Want various slices through the data.  Do transforms apply
+    #    to the sliced data as well?
+
+
+class Plottable(object):
+    """
+    """
+    # Short ascii name to refer to the plottable in a menu
+    short_name = None
+    # Fancy name
+    name = None
+    # Data
+    x = None
+    y = None
+    dx = None
+    dy = None
+    # Parameter to allow a plot to be part of the list without being displayed
+    hidden = False
+    # Flag to set whether a plottable has an interactor or not
+    interactive = True
+    custom_color = None
+    markersize = 5  # default marker size is 'size 5'
+
+    def __init__(self):
+        self.view = View()
+        self._xaxis = ""
+        self._xunit = ""
+        self._yaxis = ""
+        self._yunit = ""
+
+    def __setattr__(self, name, value):
+        """
+        Take care of changes in View when data is changed.
+        This method is provided for backward compatibility.
+        """
+        object.__setattr__(self, name, value)
+        if name in ['x', 'y', 'dx', 'dy']:
+            self.reset_view()
+            # print "self.%s has been called" % name
+
+    def set_data(self, x, y, dx=None, dy=None):
+        """
+        """
+        self.x = x
+        self.y = y
+        self.dy = dy
+        self.dx = dx
+        self.transformView()
+
+    def xaxis(self, name, units):
+        """
+        Set the name and unit of x_axis
+
+        :param name: the name of x-axis
+        :param units: the units of x_axis
+
+        """
+        self._xaxis = name
+        self._xunit = units
+
+    def yaxis(self, name, units):
+        """
+        Set the name and unit of y_axis
+
+        :param name: the name of y-axis
+        :param units: the units of y_axis
+
+        """
+        self._yaxis = name
+        self._yunit = units
+
+    def get_xaxis(self):
+        """Return the units and name of x-axis"""
+        return self._xaxis, self._xunit
+
+    def get_yaxis(self):
+        """ Return the units and name of y- axis"""
+        return self._yaxis, self._yunit
+
+    @classmethod
+    def labels(cls, collection):
+        """
+        Construct a set of unique labels for a collection of plottables of
+        the same type.
+
+        Returns a map from plottable to name.
+
+        """
+        n = len(collection)
+        label_dict = {}
+        if n > 0:
+            basename = str(cls).split('.')[-1]
+            if n == 1:
+                label_dict[collection[0]] = basename
+            else:
+                for i in xrange(len(collection)):
+                    label_dict[collection[i]] = "%s %d" % (basename, i)
+        return label_dict
+
+    # #Use the following if @classmethod doesn't work
+    # labels = classmethod(labels)
+    def setLabel(self, labelx, labely):
+        """
+        It takes a label of the x and y transformation and set View parameters
+
+        :param transx: The label of x transformation is sent by Properties Dialog
+        :param transy: The label of y transformation is sent Properties Dialog
+
+        """
+        self.view.xLabel = labelx
+        self.view.yLabel = labely
+
+    def set_View(self, x, y):
+        """Load View"""
+        self.x = x
+        self.y = y
+        self.reset_view()
+
+    def reset_view(self):
+        """Reload view with new value to plot"""
+        self.view = View(self.x, self.y, self.dx, self.dy)
+        self.view.Xreel = self.view.x
+        self.view.Yreel = self.view.y
+        self.view.DXreel = self.view.dx
+        self.view.DYreel = self.view.dy
+
+    def render(self, plot):
+        """
+        The base class makes sure the correct units are being used for
+        subsequent plottable.
+
+        For now it is assumed that the graphs are commensurate, and if you
+        put a Qx object on a Temperature graph then you had better hope
+        that it makes sense.
+
+        """
+        plot.xaxis(self._xaxis, self._xunit)
+        plot.yaxis(self._yaxis, self._yunit)
+
+    def is_empty(self):
+        """
+        Returns True if there is no data stored in the plottable
+        """
+        if not self.x == None and len(self.x) == 0 \
+            and not self.y == None and len(self.y) == 0:
+            return True
+        return False
+
+    def colors(self):
+        """Return the number of colors need to render the object"""
+        return 1
+
+    def transformView(self):
+        """
+        It transforms x, y before displaying
+        """
+        self.view.transform(self.x, self.y, self.dx, self.dy)
+
+    def returnValuesOfView(self):
+        """
+        Return View parameters and it is used by Fit Dialog
+        """
+        return self.view.returnXview()
+
+    def check_data_PlottableX(self):
+        """
+        Since no transformation is made for log10(x), check that
+        no negative values is plot in log scale
+        """
+        self.view.check_data_logX()
+
+    def check_data_PlottableY(self):
+        """
+        Since no transformation is made for log10(y), check that
+        no negative values is plot in log scale
+        """
+        self.view.check_data_logY()
+
+    def transformX(self, transx, transdx):
+        """
+        Receive pointers to function that transform x and dx
+        and set corresponding View pointers
+
+        :param transx: pointer to function that transforms x
+        :param transdx: pointer to function that transforms dx
+
+        """
+        self.view.setTransformX(transx, transdx)
+
+    def transformY(self, transy, transdy):
+        """
+        Receive pointers to function that transform y and dy
+        and set corresponding View pointers
+
+        :param transy: pointer to function that transforms y
+        :param transdy: pointer to function that transforms dy
+
+        """
+        self.view.setTransformY(transy, transdy)
+
+    def onReset(self):
+        """
+        Reset x, y, dx, dy view with its parameters
+        """
+        self.view.onResetView()
+
+    def onFitRange(self, xmin=None, xmax=None):
+        """
+        It limits View data range to plot from min to max
+
+        :param xmin: the minimum value of x to plot.
+        :param xmax: the maximum value of x to plot
+
+        """
+        self.view.onFitRangeView(xmin, xmax)
+
+
+class View(object):
+    """
+    Representation of the data that might include a transformation
+    """
+    x = None
+    y = None
+    dx = None
+    dy = None
+
+    def __init__(self, x=None, y=None, dx=None, dy=None):
+        """
+        """
+        self.x = x
+        self.y = y
+        self.dx = dx
+        self.dy = dy
+        # To change x range to the reel range
+        self.Xreel = self.x
+        self.Yreel = self.y
+        self.DXreel = self.dx
+        self.DYreel = self.dy
+        # Labels of x and y received from Properties Dialog
+        self.xLabel = ""
+        self.yLabel = ""
+        # Function to transform x, y, dx and dy
+        self.funcx = None
+        self.funcy = None
+        self.funcdx = None
+        self.funcdy = None
+
+    def transform(self, x=None, y=None, dx=None, dy=None):
+        """
+        Transforms the x,y,dx and dy vectors and stores
+         the output in View parameters
+
+        :param x: array of x values
+        :param y: array of y values
+        :param dx: array of  errors values on x
+        :param dy: array of error values on y
+
+        """
+        # Sanity check
+        # Do the transofrmation only when x and y are empty
+        has_err_x = not (dx is None or len(dx) == 0)
+        has_err_y = not (dy is None or len(dy) == 0)
+
+        if(x is not None) and (y is not None):
+            if not dx is None and not len(dx) == 0 and not len(x) == len(dx):
+                msg = "Plottable.View: Given x and dx are not"
+                msg += " of the same length"
+                raise ValueError, msg
+            # Check length of y array
+            if not len(y) == len(x):
+                msg = "Plottable.View: Given y "
+                msg += "and x are not of the same length"
+                raise ValueError, msg
+
+            if not dy is None and not len(dy) == 0 and not len(y) == len(dy):
+                msg = "Plottable.View: Given y and dy are not of the same "
+                msg += "length: len(y)=%s, len(dy)=%s" % (len(y), len(dy))
+                raise ValueError, msg
+            self.x = []
+            self.y = []
+            if has_err_x:
+                self.dx = []
+            else:
+                self.dx = None
+            if has_err_y:
+                self.dy = []
+            else:
+                self.dy = None
+            if not has_err_x:
+                dx = numpy.zeros(len(x))
+            if not has_err_y:
+                dy = numpy.zeros(len(y))
+            for i in range(len(x)):
+                try:
+                    tempx = self.funcx(x[i], y[i])
+                    tempy = self.funcy(y[i], x[i])
+                    if has_err_x:
+                        tempdx = self.funcdx(x[i], y[i], dx[i], dy[i])
+                    if has_err_y:
+                        tempdy = self.funcdy(y[i], x[i], dy[i], dx[i])
+                    self.x.append(tempx)
+                    self.y.append(tempy)
+                    if has_err_x:
+                        self.dx.append(tempdx)
+                    if has_err_y:
+                        self.dy.append(tempdy)
+                except Exception:
+                    pass
+            # Sanity check
+            if not len(self.x) == len(self.y):
+                msg = "Plottable.View: transformed x "
+                msg += "and y are not of the same length"
+                raise ValueError, msg
+            if has_err_x and not (len(self.x) == len(self.dx)):
+                msg = "Plottable.View: transformed x and dx"
+                msg += " are not of the same length"
+                raise ValueError, msg
+            if has_err_y and not (len(self.y) == len(self.dy)):
+                msg = "Plottable.View: transformed y"
+                msg += " and dy are not of the same length"
+                raise ValueError, msg
+            # Check that negative values are not plot on x and y axis for
+            # log10 transformation
+            self.check_data_logX()
+            self.check_data_logY()
+            # Store x ,y dx,and dy in their full range for reset
+            self.Xreel = self.x
+            self.Yreel = self.y
+            self.DXreel = self.dx
+            self.DYreel = self.dy
+
+    def onResetView(self):
+        """
+        Reset x,y,dx and y in their full range  and in the initial scale
+        in case their previous range has changed
+        """
+        self.x = self.Xreel
+        self.y = self.Yreel
+        self.dx = self.DXreel
+        self.dy = self.DYreel
+
+    def setTransformX(self, funcx, funcdx):
+        """
+        Receive pointers to function that transform x and dx
+        and set corresponding View pointers
+
+        :param transx: pointer to function that transforms x
+        :param transdx: pointer to function that transforms dx
+        """
+        self.funcx = funcx
+        self.funcdx = funcdx
+
+    def setTransformY(self, funcy, funcdy):
+        """
+        Receive pointers to function that transform y and dy
+        and set corresponding View pointers
+
+        :param transx: pointer to function that transforms y
+        :param transdx: pointer to function that transforms dy
+        """
+        self.funcy = funcy
+        self.funcdy = funcdy
+
+    def returnXview(self):
+        """
+        Return View  x,y,dx,dy
+        """
+        return self.x, self.y, self.dx, self.dy
+
+    def check_data_logX(self):
+        """
+        Remove negative value in x vector to avoid plotting negative
+        value of Log10
+        """
+        tempx = []
+        tempdx = []
+        tempy = []
+        tempdy = []
+        if self.dx == None:
+            self.dx = numpy.zeros(len(self.x))
+        if self.dy == None:
+            self.dy = numpy.zeros(len(self.y))
+        if self.xLabel == "log10(x)":
+            for i in range(len(self.x)):
+                try:
+                    if self.x[i] > 0:
+                        tempx.append(self.x[i])
+                        tempdx.append(self.dx[i])
+                        tempy.append(self.y[i])
+                        tempdy.append(self.dy[i])
+                except:
+                    logging.error("check_data_logX: skipping point x %g", self.x[i])
+                    logging.error(sys.exc_value)
+            self.x = tempx
+            self.y = tempy
+            self.dx = tempdx
+            self.dy = tempdy
+
+    def check_data_logY(self):
+        """
+        Remove negative value in y vector
+        to avoid plotting negative value of Log10
+
+        """
+        tempx = []
+        tempdx = []
+        tempy = []
+        tempdy = []
+        if self.dx == None:
+            self.dx = numpy.zeros(len(self.x))
+        if self.dy == None:
+            self.dy = numpy.zeros(len(self.y))
+        if self.yLabel == "log10(y)":
+            for i in range(len(self.x)):
+                try:
+                    if self.y[i] > 0:
+                        tempx.append(self.x[i])
+                        tempdx.append(self.dx[i])
+                        tempy.append(self.y[i])
+                        tempdy.append(self.dy[i])
+                except:
+                    logging.error("check_data_logY: skipping point %g", self.y[i])
+                    logging.error(sys.exc_value)
+
+            self.x = tempx
+            self.y = tempy
+            self.dx = tempdx
+            self.dy = tempdy
+
+    def onFitRangeView(self, xmin=None, xmax=None):
+        """
+        It limits View data range to plot from min to max
+
+        :param xmin: the minimum value of x to plot.
+        :param xmax: the maximum value of x to plot
+
+        """
+        tempx = []
+        tempdx = []
+        tempy = []
+        tempdy = []
+        if self.dx == None:
+            self.dx = numpy.zeros(len(self.x))
+        if self.dy == None:
+            self.dy = numpy.zeros(len(self.y))
+        if xmin != None and xmax != None:
+            for i in range(len(self.x)):
+                if self.x[i] >= xmin and self.x[i] <= xmax:
+                    tempx.append(self.x[i])
+                    tempdx.append(self.dx[i])
+                    tempy.append(self.y[i])
+                    tempdy.append(self.dy[i])
+            self.x = tempx
+            self.y = tempy
+            self.dx = tempdx
+            self.dy = tempdy
+
+
+class PlottableData2D(Plottable):
+    """
+    2D data class for image plotting
+    """
+    def __init__(self, image=None, qx_data=None, qy_data=None,
+                 err_image=None, xmin=None, xmax=None, ymin=None,
+                 ymax=None, zmin=None, zmax=None):
+        """
+        Draw image
+        """
+        Plottable.__init__(self)
+        self.name = "Data2D"
+        self.label = None
+        self.data = image
+        self.qx_data = qx_data
+        self.qy_data = qx_data
+        self.err_data = err_image
+        self.source = None
+        self.detector = []
+
+        # # Units for Q-values
+        self.xy_unit = 'A^{-1}'
+        # # Units for I(Q) values
+        self.z_unit = 'cm^{-1}'
+        self._zaxis = ''
+        # x-axis unit and label
+        self._xaxis = '\\rm{Q_{x}}'
+        self._xunit = 'A^{-1}'
+        # y-axis unit and label
+        self._yaxis = '\\rm{Q_{y}}'
+        self._yunit = 'A^{-1}'
+
+        # ## might remove that later
+        # # Vector of Q-values at the center of each bin in x
+        self.x_bins = []
+        # # Vector of Q-values at the center of each bin in y
+        self.y_bins = []
+
+        # x and y boundaries
+        self.xmin = xmin
+        self.xmax = xmax
+        self.ymin = ymin
+        self.ymax = ymax
+
+        self.zmin = zmin
+        self.zmax = zmax
+        self.id = None
+
+    def xaxis(self, label, unit):
+        """
+        set x-axis
+
+        :param label: x-axis label
+        :param unit: x-axis unit
+
+        """
+        self._xaxis = label
+        self._xunit = unit
+
+    def yaxis(self, label, unit):
+        """
+        set y-axis
+
+        :param label: y-axis label
+        :param unit: y-axis unit
+
+        """
+        self._yaxis = label
+        self._yunit = unit
+
+    def zaxis(self, label, unit):
+        """
+        set z-axis
+
+        :param label: z-axis label
+        :param unit: z-axis unit
+
+        """
+        self._zaxis = label
+        self._zunit = unit
+
+    def setValues(self, datainfo=None):
+        """
+        Use datainfo object to initialize data2D
+
+        :param datainfo: object
+
+        """
+        self.image = copy.deepcopy(datainfo.data)
+        self.qx_data = copy.deepcopy(datainfo.qx_data)
+        self.qy_data = copy.deepcopy(datainfo.qy_data)
+        self.err_image = copy.deepcopy(datainfo.err_data)
+
+        self.xy_unit = datainfo.Q_unit
+        self.z_unit = datainfo.I_unit
+        self._zaxis = datainfo._zaxis
+
+        self.xaxis(datainfo._xunit, datainfo._xaxis)
+        self.yaxis(datainfo._yunit, datainfo._yaxis)
+        # x and y boundaries
+        self.xmin = datainfo.xmin
+        self.xmax = datainfo.xmax
+        self.ymin = datainfo.ymin
+        self.ymax = datainfo.ymax
+        # # Vector of Q-values at the center of each bin in x
+        self.x_bins = datainfo.x_bins
+        # # Vector of Q-values at the center of each bin in y
+        self.y_bins = datainfo.y_bins
+
+    def set_zrange(self, zmin=None, zmax=None):
+        """
+        """
+        if zmin < zmax:
+            self.zmin = zmin
+            self.zmax = zmax
+        else:
+            raise "zmin is greater or equal to zmax "
+
+    def render(self, plot, **kw):
+        """
+        Renders the plottable on the graph
+
+        """
+        plot.image(self.data, self.qx_data, self.qy_data,
+                   self.xmin, self.xmax, self.ymin,
+                   self.ymax, self.zmin, self.zmax, **kw)
+
+    def changed(self):
+        """
+        """
+        return False
+
+    @classmethod
+    def labels(cls, collection):
+        """Build a label mostly unique within a collection"""
+        label_dict = {}
+        for item in collection:
+            if item.label == "Data2D":
+                item.label = item.name
+            label_dict[item] = item.label
+        return label_dict
+
+
+class PlottableData1D(Plottable):
+    """
+    Data plottable: scatter plot of x,y with errors in x and y.
+    """
+
+    def __init__(self, x, y, dx=None, dy=None):
+        """
+        Draw points specified by x[i],y[i] in the current color/symbol.
+        Uncertainty in x is given by dx[i], or by (xlo[i],xhi[i]) if the
+        uncertainty is asymmetric.  Similarly for y uncertainty.
+
+        The title appears on the legend.
+        The label, if it is different, appears on the status bar.
+        """
+        Plottable.__init__(self)
+        self.name = "data"
+        self.label = "data"
+        self.x = x
+        self.y = y
+        self.dx = dx
+        self.dy = dy
+        self.source = None
+        self.detector = None
+        self.xaxis('', '')
+        self.yaxis('', '')
+        self.view = View(self.x, self.y, self.dx, self.dy)
+        self.symbol = 0
+        self.custom_color = None
+        self.markersize = 5
+        self.id = None
+        self.zorder = 1
+        self.hide_error = False
+
+    def render(self, plot, **kw):
+        """
+        Renders the plottable on the graph
+        """
+        if self.interactive == True:
+            kw['symbol'] = self.symbol
+            kw['id'] = self.id
+            kw['hide_error'] = self.hide_error
+            kw['markersize'] = self.markersize
+            plot.interactive_points(self.view.x, self.view.y,
+                                    dx=self.view.dx, dy=self.view.dy,
+                                    name=self.name, zorder=self.zorder, **kw)
+        else:
+            kw['id'] = self.id
+            kw['hide_error'] = self.hide_error
+            kw['symbol'] = self.symbol
+            kw['color'] = self.custom_color
+            kw['markersize'] = self.markersize
+            plot.points(self.view.x, self.view.y, dx=self.view.dx,
+                        dy=self.view.dy, zorder=self.zorder,
+                        marker=self.symbollist[self.symbol], **kw)
+
+    def changed(self):
+        return False
+
+    @classmethod
+    def labels(cls, collection):
+        """Build a label mostly unique within a collection"""
+        label_dict = {}
+        for item in collection:
+            if item.label == "data":
+                item.label = item.name
+            label_dict[item] = item.label
+        return label_dict
+
+
+class PlottableTheory1D(Plottable):
+    """
+    Theory plottable: line plot of x,y with confidence interval y.
+    """
+    def __init__(self, x, y, dy=None):
+        """
+        Draw lines specified in x[i],y[i] in the current color/symbol.
+        Confidence intervals in x are given by dx[i] or by (xlo[i],xhi[i])
+        if the limits are asymmetric.
+
+        The title is the name that will show up on the legend.
+        """
+        Plottable.__init__(self)
+        msg = "Theory1D is no longer supported, please use Data1D and change symbol.\n"
+        raise DeprecationWarning, msg
+
+class PlottableFit1D(Plottable):
+    """
+    Fit plottable: composed of a data line plus a theory line.  This
+    is treated like a single object from the perspective of the graph,
+    except that it will have two legend entries, one for the data and
+    one for the theory.
+
+    The color of the data and theory will be shared.
+
+    """
+    def __init__(self, data=None, theory=None):
+        """
+        """
+        Plottable.__init__(self)
+        self.data = data
+        self.theory = theory
+
+    def render(self, plot, **kw):
+        """
+        """
+        self.data.render(plot, **kw)
+        self.theory.render(plot, **kw)
+
+    def changed(self):
+        """
+        """
+        return self.data.changed() or self.theory.changed()
+
+
+# ---------------------------------------------------------------
+class Text(Plottable):
+    """
+    """
+    def __init__(self, text=None, xpos=0.5, ypos=0.9, name='text'):
+        """
+        Draw the user-defined text in plotter
+        We can specify the position of text
+        """
+        Plottable.__init__(self)
+        self.name = name
+        self.text = text
+        self.xpos = xpos
+        self.ypos = ypos
+
+    def render(self, plot, **kw):
+        """
+        """
+        from matplotlib import transforms
+
+        xcoords = transforms.blended_transform_factory(plot.subplot.transAxes,
+                                                       plot.subplot.transAxes)
+        plot.subplot.text(self.xpos,
+                          self.ypos,
+                          self.text,
+                          label=self.name,
+                          transform=xcoords)
+
+    def setText(self, text):
+        """Set the text string."""
+        self.text = text
+
+    def getText(self, text):
+        """Get the text string."""
+        return self.text
+
+    def set_x(self, x):
+        """
+        Set the x position of the text
+        ACCEPTS: float
+        """
+        self.xpos = x
+
+    def set_y(self, y):
+        """
+        Set the y position of the text
+        ACCEPTS: float
+        """
+        self.ypos = y
+
+
+# ---------------------------------------------------------------
+class Chisq(Plottable):
+    """
+    Chisq plottable plots the chisq
+    """
+    def __init__(self, chisq=None):
+        """
+        Draw the chisq in plotter
+        We can specify the position of chisq
+        """
+        Plottable.__init__(self)
+        self.name = "chisq"
+        self._chisq = chisq
+        self.xpos = 0.5
+        self.ypos = 0.9
+
+    def render(self, plot, **kw):
+        """
+        """
+        if  self._chisq == None:
+            chisqTxt = r'$\chi^2=$'
+        else:
+            chisqTxt = r'$\chi^2=%g$' % (float(self._chisq))
+
+        from matplotlib import transforms
+
+        xcoords = transforms.blended_transform_factory(plot.subplot.transAxes,
+                                                      plot.subplot.transAxes)
+        plot.subplot.text(self.xpos,
+                          self.ypos,
+                          chisqTxt, label='chisq',
+                          transform=xcoords)
+
+    def setChisq(self, chisq):
+        """
+        Set the chisq value.
+        """
+        self._chisq = chisq
Index: src/sas/qtgui/Plotting/Plotter.py
===================================================================
--- src/sas/qtgui/Plotting/Plotter.py	(revision 83eb5208b8acb86b064162274de583670f72da47)
+++ src/sas/qtgui/Plotting/Plotter.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -7,11 +7,13 @@
 from matplotlib.font_manager import FontProperties
 
-from sas.sasgui.guiframe.dataFitting import Data1D
+from sas.qtgui.Plotting.PlotterData import Data1D
+
 from sas.qtgui.Plotting.PlotterBase import PlotterBase
-import sas.qtgui.Utilities.GuiUtils as GuiUtils
 from sas.qtgui.Plotting.AddText import AddText
 from sas.qtgui.Plotting.SetGraphRange import SetGraphRange
 from sas.qtgui.Plotting.LinearFit import LinearFit
 from sas.qtgui.Plotting.PlotProperties import PlotProperties
+
+import sas.qtgui.Utilities.GuiUtils as GuiUtils
 import sas.qtgui.Plotting.PlotUtilities as PlotUtilities
 
Index: src/sas/qtgui/Plotting/Plotter2D.py
===================================================================
--- src/sas/qtgui/Plotting/Plotter2D.py	(revision 83eb5208b8acb86b064162274de583670f72da47)
+++ src/sas/qtgui/Plotting/Plotter2D.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -10,19 +10,23 @@
 from mpl_toolkits.mplot3d import Axes3D
 
+from sas.sascalc.dataloader.manipulations import CircularAverage
+
+from sas.qtgui.Plotting.PlotterData import Data1D
+from sas.qtgui.Plotting.PlotterData import Data2D
+
 import sas.qtgui.Plotting.PlotUtilities as PlotUtilities
 import sas.qtgui.Utilities.GuiUtils as GuiUtils
 from sas.qtgui.Plotting.PlotterBase import PlotterBase
 from sas.qtgui.Plotting.ColorMap import ColorMap
-from sas.sasgui.guiframe.dataFitting import Data1D
-from sas.sasgui.guiframe.dataFitting import Data2D
-from sas.sascalc.dataloader.manipulations import CircularAverage
-from sas.sasgui.guiframe.local_perspectives.plotting.binder import BindArtist
 from sas.qtgui.Plotting.BoxSum import BoxSum
 from sas.qtgui.Plotting.SlicerParameters import SlicerParameters
-from sas.sasgui.guiframe.local_perspectives.plotting.boxSlicer import BoxInteractorX
-from sas.sasgui.guiframe.local_perspectives.plotting.AnnulusSlicer import AnnulusInteractor
-from sas.sasgui.guiframe.local_perspectives.plotting.SectorSlicer import SectorInteractor
-from sas.sasgui.guiframe.local_perspectives.plotting.boxSum import BoxSumCalculator
-from sas.sasgui.guiframe.local_perspectives.plotting.boxSlicer import BoxInteractorY
+from sas.qtgui.Plotting.Binder import BindArtist
+
+# TODO: move to sas.qtgui namespace
+from sas.qtgui.Plotting.Slicers.BoxSlicer import BoxInteractorX
+from sas.qtgui.Plotting.Slicers.BoxSlicer import BoxInteractorY
+from sas.qtgui.Plotting.Slicers.AnnulusSlicer import AnnulusInteractor
+from sas.qtgui.Plotting.Slicers.SectorSlicer import SectorInteractor
+from sas.qtgui.Plotting.Slicers.BoxSum import BoxSumCalculator
 
 # Minimum value of Z for which we will present data.
Index: src/sas/qtgui/Plotting/PlotterBase.py
===================================================================
--- src/sas/qtgui/Plotting/PlotterBase.py	(revision 83eb5208b8acb86b064162274de583670f72da47)
+++ src/sas/qtgui/Plotting/PlotterBase.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -15,10 +15,11 @@
 
 DEFAULT_CMAP = pylab.cm.jet
-from sas.sasgui.plottools.binder import BindArtist
-
-import sas.qtgui.Utilities.GuiUtils as GuiUtils
-from sas.sasgui.guiframe.dataFitting import Data1D, Data2D
+from sas.qtgui.Plotting.Binder import BindArtist
+from sas.qtgui.Plotting.PlotterData import Data1D
+from sas.qtgui.Plotting.PlotterData import Data2D
+
 from sas.qtgui.Plotting.ScaleProperties import ScaleProperties
 from sas.qtgui.Plotting.WindowTitle import WindowTitle
+import sas.qtgui.Utilities.GuiUtils as GuiUtils
 import sas.qtgui.Plotting.PlotHelper as PlotHelper
 import sas.qtgui.Plotting.PlotUtilities as PlotUtilities
Index: src/sas/qtgui/Plotting/PlotterData.py
===================================================================
--- src/sas/qtgui/Plotting/PlotterData.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
+++ src/sas/qtgui/Plotting/PlotterData.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -0,0 +1,344 @@
+"""
+Adapters for fitting module
+"""
+import copy
+import numpy
+import math
+from sas.sascalc.data_util.uncertainty import Uncertainty
+
+from sas.qtgui.Plotting.Plottables import PlottableData1D
+from sas.qtgui.Plotting.Plottables import PlottableData2D
+
+from sas.sascalc.dataloader.data_info import Data1D as LoadData1D
+from sas.sascalc.dataloader.data_info import Data2D as LoadData2D
+
+
+class Data1D(PlottableData1D, LoadData1D):
+    """
+    """
+    def __init__(self, x=None, y=None, dx=None, dy=None):
+        """
+        """
+        if x is None:
+            x = []
+        if y is None:
+            y = []
+        PlottableData1D.__init__(self, x, y, dx, dy)
+        LoadData1D.__init__(self, x, y, dx, dy)
+        self.id = None
+        self.list_group_id = []
+        self.group_id = None
+        self.is_data = True
+        self.path = None
+        self.xtransform = None
+        self.ytransform = None
+        self.title = ""
+        self.scale = None
+        
+    def copy_from_datainfo(self, data1d):
+        """
+        copy values of Data1D of type DataLaoder.Data_info
+        """
+        self.x  = copy.deepcopy(data1d.x)
+        self.y  = copy.deepcopy(data1d.y)
+        self.dy = copy.deepcopy(data1d.dy)
+        
+        if hasattr(data1d, "dx"):
+            self.dx = copy.deepcopy(data1d.dx)    
+        if hasattr(data1d, "dxl"):
+            self.dxl = copy.deepcopy(data1d.dxl)
+        if hasattr(data1d, "dxw"):
+            self.dxw = copy.deepcopy(data1d.dxw)
+    
+        self.xaxis(data1d._xaxis, data1d._xunit)
+        self.yaxis(data1d._yaxis, data1d._yunit)
+        self.title = data1d.title
+        
+    def __str__(self):
+        """
+        print data
+        """
+        _str = "%s\n" % LoadData1D.__str__(self)
+      
+        return _str 
+    
+    def _perform_operation(self, other, operation):
+        """
+        """
+        # First, check the data compatibility
+        dy, dy_other = self._validity_check(other)
+        result = Data1D(x=[], y=[], dx=None, dy=None)
+        result.clone_without_data(length=len(self.x), clone=self)
+        result.copy_from_datainfo(data1d=self)
+        if self.dxw == None:
+            result.dxw = None
+        else:
+            result.dxw = numpy.zeros(len(self.x))
+        if self.dxl == None:
+            result.dxl = None
+        else:
+            result.dxl = numpy.zeros(len(self.x))
+
+        for i in range(len(self.x)):
+            result.x[i] = self.x[i]
+            if self.dx is not None and len(self.x) == len(self.dx):
+                result.dx[i] = self.dx[i]
+            if self.dxw is not None and len(self.x) == len(self.dxw):
+                result.dxw[i] = self.dxw[i]
+            if self.dxl is not None and len(self.x) == len(self.dxl):
+                result.dxl[i] = self.dxl[i]
+            
+            a = Uncertainty(self.y[i], dy[i]**2)
+            if isinstance(other, Data1D):
+                b = Uncertainty(other.y[i], dy_other[i]**2)
+                if other.dx is not None:
+                    result.dx[i] *= self.dx[i]
+                    result.dx[i] += (other.dx[i]**2)
+                    result.dx[i] /= 2
+                    result.dx[i] = math.sqrt(result.dx[i])
+                if result.dxl is not None and other.dxl is not None:
+                    result.dxl[i] *= self.dxl[i]
+                    result.dxl[i] += (other.dxl[i]**2)
+                    result.dxl[i] /= 2
+                    result.dxl[i] = math.sqrt(result.dxl[i])
+            else:
+                b = other
+            
+            output = operation(a, b)
+            result.y[i] = output.x
+            result.dy[i] = math.sqrt(math.fabs(output.variance))
+        return result
+    
+    def _perform_union(self, other):
+        """
+        """
+        # First, check the data compatibility
+        self._validity_check_union(other)
+        result = Data1D(x=[], y=[], dx=None, dy=None)
+        tot_length = len(self.x) + len(other.x)
+        result = self.clone_without_data(length=tot_length, clone=result)
+        if self.dy == None or other.dy is None:
+            result.dy = None
+        else:
+            result.dy = numpy.zeros(tot_length)
+        if self.dx == None or other.dx is None:
+            result.dx = None
+        else:
+            result.dx = numpy.zeros(tot_length)
+        if self.dxw == None or other.dxw is None:
+            result.dxw = None
+        else:
+            result.dxw = numpy.zeros(tot_length)
+        if self.dxl == None or other.dxl is None:
+            result.dxl = None
+        else:
+            result.dxl = numpy.zeros(tot_length)
+
+        result.x = numpy.append(self.x, other.x)
+        #argsorting
+        ind = numpy.argsort(result.x)
+        result.x = result.x[ind]
+        result.y = numpy.append(self.y, other.y)
+        result.y = result.y[ind]
+        if result.dy != None:
+            result.dy = numpy.append(self.dy, other.dy)
+            result.dy = result.dy[ind]
+        if result.dx is not None:
+            result.dx = numpy.append(self.dx, other.dx)
+            result.dx = result.dx[ind]
+        if result.dxw is not None:
+            result.dxw = numpy.append(self.dxw, other.dxw)
+            result.dxw = result.dxw[ind]
+        if result.dxl is not None:
+            result.dxl = numpy.append(self.dxl, other.dxl)
+            result.dxl = result.dxl[ind]
+        return result
+
+class Data2D(PlottableData2D, LoadData2D):
+    """
+    """
+    def __init__(self, image=None, err_image=None,
+                 qx_data=None, qy_data=None, q_data=None, 
+                 mask=None, dqx_data=None, dqy_data=None, 
+                 xmin=None, xmax=None, ymin=None, ymax=None,
+                 zmin=None, zmax=None):
+        """
+        """
+        PlottableData2D.__init__(self, image=image, err_image=err_image,
+                            xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax,
+                            zmin=zmin, zmax=zmax, qx_data=qx_data, 
+                            qy_data=qy_data)
+        
+        LoadData2D.__init__(self, data=image, err_data=err_image,
+                            qx_data=qx_data, qy_data=qy_data,
+                            dqx_data=dqx_data, dqy_data=dqy_data,
+                            q_data=q_data, mask=mask)
+        self.id = None
+        self.list_group_id = []
+        self.group_id = None
+        self.is_data = True
+        self.path = None
+        self.xtransform = None
+        self.ytransform = None
+        self.title = ""
+        self.scale = None
+        
+    def copy_from_datainfo(self, data2d):
+        """
+        copy value of Data2D of type DataLoader.data_info
+        """
+        self.data = copy.deepcopy(data2d.data)
+        self.qx_data = copy.deepcopy(data2d.qx_data)
+        self.qy_data = copy.deepcopy(data2d.qy_data)
+        self.q_data = copy.deepcopy(data2d.q_data)
+        self.mask = copy.deepcopy(data2d.mask)
+        self.err_data = copy.deepcopy(data2d.err_data)
+        self.x_bins = copy.deepcopy(data2d.x_bins)
+        self.y_bins = copy.deepcopy(data2d.y_bins)
+        if data2d.dqx_data is not None:
+            self.dqx_data = copy.deepcopy(data2d.dqx_data)
+        if data2d.dqy_data is not None:
+            self.dqy_data = copy.deepcopy(data2d.dqy_data)
+        self.xmin = data2d.xmin
+        self.xmax = data2d.xmax
+        self.ymin = data2d.ymin
+        self.ymax = data2d.ymax
+        if hasattr(data2d, "zmin"):
+            self.zmin = data2d.zmin
+        if hasattr(data2d, "zmax"):
+            self.zmax = data2d.zmax
+        self.xaxis(data2d._xaxis, data2d._xunit)
+        self.yaxis(data2d._yaxis, data2d._yunit)
+        self.title = data2d.title
+        
+    def __str__(self):
+        """
+        print data
+        """
+        _str = "%s\n" % LoadData2D.__str__(self)
+        return _str 
+
+    def _perform_operation(self, other, operation):
+        """
+        Perform 2D operations between data sets
+        
+        :param other: other data set
+        :param operation: function defining the operation
+        
+        """
+        # First, check the data compatibility
+        dy, dy_other = self._validity_check(other)
+        result = Data2D(image=None, qx_data=None, qy_data=None,
+                         q_data=None, err_image=None, xmin=None, xmax=None,
+                         ymin=None, ymax=None, zmin=None, zmax=None)
+        result.clone_without_data(len(self.data))
+        result.copy_from_datainfo(data2d=self)
+        result.xmin = self.xmin
+        result.xmax = self.xmax
+        result.ymin = self.ymin
+        result.ymax = self.ymax
+        if self.dqx_data == None or self.dqy_data == None:
+            result.dqx_data = None
+            result.dqy_data = None
+        else:
+            result.dqx_data = numpy.zeros(len(self.data))
+            result.dqy_data = numpy.zeros(len(self.data))
+        for i in range(numpy.size(self.data)):
+            result.data[i] = self.data[i]
+            if self.err_data is not None and \
+                numpy.size(self.data) == numpy.size(self.err_data):
+                result.err_data[i] = self.err_data[i]    
+            if self.dqx_data is not None:
+                result.dqx_data[i] = self.dqx_data[i]
+            if self.dqy_data is not None:
+                result.dqy_data[i] = self.dqy_data[i]
+            result.qx_data[i] = self.qx_data[i]
+            result.qy_data[i] = self.qy_data[i]
+            result.q_data[i] = self.q_data[i]
+            result.mask[i] = self.mask[i]
+            
+            a = Uncertainty(self.data[i], dy[i]**2)
+            if isinstance(other, Data2D):
+                b = Uncertainty(other.data[i], dy_other[i]**2)
+                if other.dqx_data is not None and \
+                        result.dqx_data is not None:
+                    result.dqx_data[i] *= self.dqx_data[i]
+                    result.dqx_data[i] += (other.dqx_data[i]**2)
+                    result.dqx_data[i] /= 2
+                    result.dqx_data[i] = math.sqrt(result.dqx_data[i])     
+                if other.dqy_data is not None and \
+                        result.dqy_data is not None:
+                    result.dqy_data[i] *= self.dqy_data[i]
+                    result.dqy_data[i] += (other.dqy_data[i]**2)
+                    result.dqy_data[i] /= 2
+                    result.dqy_data[i] = math.sqrt(result.dqy_data[i])
+            else:
+                b = other
+            
+            output = operation(a, b)
+            result.data[i] = output.x
+            result.err_data[i] = math.sqrt(math.fabs(output.variance))
+        return result
+    
+    def _perform_union(self, other):
+        """
+        Perform 2D operations between data sets
+        
+        :param other: other data set
+        :param operation: function defining the operation
+        
+        """
+        # First, check the data compatibility
+        self._validity_check_union(other)
+        result = Data2D(image=None, qx_data=None, qy_data=None,
+                         q_data=None, err_image=None, xmin=None, xmax=None,
+                         ymin=None, ymax=None, zmin=None, zmax=None)
+        length = len(self.data)
+        tot_length = length + len(other.data)
+        result.clone_without_data(tot_length)
+        result.xmin = self.xmin
+        result.xmax = self.xmax
+        result.ymin = self.ymin
+        result.ymax = self.ymax
+        if self.dqx_data == None or self.dqy_data == None or \
+                other.dqx_data == None or other.dqy_data == None :
+            result.dqx_data = None
+            result.dqy_data = None
+        else:
+            result.dqx_data = numpy.zeros(len(self.data) + \
+                                         numpy.size(other.data))
+            result.dqy_data = numpy.zeros(len(self.data) + \
+                                         numpy.size(other.data))
+        
+        result.data = numpy.append(self.data, other.data)
+        result.qx_data = numpy.append(self.qx_data, other.qx_data)
+        result.qy_data = numpy.append(self.qy_data, other.qy_data)
+        result.q_data = numpy.append(self.q_data, other.q_data)
+        result.mask = numpy.append(self.mask, other.mask)
+        if result.err_data is not None:
+            result.err_data = numpy.append(self.err_data, other.err_data) 
+        if self.dqx_data is not None:
+            result.dqx_data = numpy.append(self.dqx_data, other.dqx_data)
+        if self.dqy_data is not None:
+            result.dqy_data = numpy.append(self.dqy_data, other.dqy_data)
+
+        return result
+
+def check_data_validity(data):
+    """
+    Return True is data is valid enough to compute chisqr, else False
+    """
+    flag = True
+    if data is not None:
+        if issubclass(data.__class__, Data2D):
+            if (data.data is None) or (len(data.data) == 0)\
+            or (len(data.err_data) == 0):
+                flag = False
+        else:
+            if (data.y is None) or (len(data.y) == 0): 
+                flag = False
+        if not data.is_data:
+            flag = False
+    else:
+        flag = False
+    return flag
Index: src/sas/qtgui/Plotting/Slicers/AnnulusSlicer.py
===================================================================
--- src/sas/qtgui/Plotting/Slicers/AnnulusSlicer.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
+++ src/sas/qtgui/Plotting/Slicers/AnnulusSlicer.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -0,0 +1,538 @@
+import numpy
+from PyQt4 import QtGui
+from PyQt4 import QtCore
+
+import sas.qtgui.Utilities.GuiUtils as GuiUtils
+from BaseInteractor import BaseInteractor
+from sas.qtgui.Plotting.PlotterData import Data1D
+from sas.qtgui.Utilities.GuiUtils import formatNumber
+from sas.qtgui.Plotting.SlicerModel import SlicerModel
+
+class AnnulusInteractor(BaseInteractor, SlicerModel):
+    """
+    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, item=None, color='black', zorder=3):
+
+        BaseInteractor.__init__(self, base, axes, color=color)
+        SlicerModel.__init__(self)
+
+        self.markers = []
+        self.axes = axes
+        self.base = base
+        self._item = item
+        self.qmax = min(numpy.fabs(self.base.data.xmax),
+                        numpy.fabs(self.base.data.xmin))  # must be positive
+        self.connect = self.base.connect
+
+        # Number of points on the plot
+        self.nbins = 36
+        # 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.inner_circle = RingInteractor(self, self.axes,
+                                           zorder=zorder,
+                                           r=self.qmax / 2.0, sign=self.sign)
+        self.inner_circle.qmax = self.qmax
+        self.outer_circle = RingInteractor(self, self.axes,
+                                           zorder=zorder + 1, r=self.qmax / 1.8,
+                                           sign=self.sign)
+        self.outer_circle.qmax = self.qmax * 1.2
+        self.update()
+        self._post_data()
+
+        self.setModelFromParams()
+
+    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()
+
+    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.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.data
+        if data is None:
+            return
+
+        from sas.sascalc.dataloader.manipulations import Ring
+        rmin = min(numpy.fabs(self.inner_circle.get_radius()),
+                   numpy.fabs(self.outer_circle.get_radius()))
+        rmax = max(numpy.fabs(self.inner_circle.get_radius()),
+                   numpy.fabs(self.outer_circle.get_radius()))
+        # If the user does not specify the numbers of points to plot
+        # the default number will be nbins= 36
+        if nbins is None:
+            self.nbins = 36
+        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.data)
+
+        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 - numpy.pi) * 180 / numpy.pi,
+                          y=sector.y, dy=sector.dy)
+        new_plot.dxl = dxl
+        new_plot.dxw = dxw
+        new_plot.name = "AnnulusPhi" + "(" + self.base.data.name + ")"
+        new_plot.title = "AnnulusPhi" + "(" + self.base.data.name + ")"
+
+        new_plot.source = self.base.data.source
+        new_plot.interactive = True
+        new_plot.detector = self.base.data.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.data.name.count("Residuals") > 0:
+            new_plot.ytransform = 'y'
+            new_plot.yaxis("\\rm{Residuals} ", "/")
+
+        new_plot.group_id = "AnnulusPhi" + self.base.data.name
+        new_plot.id = "AnnulusPhi" + self.base.data.name
+        new_plot.is_data = True
+        new_plot.xtransform = "x"
+        new_plot.ytransform = "y"
+        variant_plot = QtCore.QVariant(new_plot)
+        GuiUtils.updateModelItemWithPlot(self._item, variant_plot, new_plot.id)
+        self.base.manager.communicator.plotUpdateSignal.emit([new_plot])
+
+        if self.update_model:
+            self.setModelFromParams()
+        self.draw()
+
+    def validate(self, param_name, param_value):
+        """
+        Test the proposed new value "value" for row "row" of parameters
+        """
+        MIN_DIFFERENCE = 0.01
+        isValid = True
+
+        if param_name == 'inner_radius':
+            # First, check the closeness
+            if numpy.fabs(param_value - self.getParams()['outer_radius']) < MIN_DIFFERENCE:
+                print("Inner and outer radii too close. Please adjust.")
+                isValid = False
+            elif param_value > self.qmax:
+                print("Inner radius exceeds maximum range. Please adjust.")
+                isValid = False
+        elif param_name == 'outer_radius':
+            # First, check the closeness
+            if numpy.fabs(param_value - self.getParams()['inner_radius']) < MIN_DIFFERENCE:
+                print("Inner and outer radii too close. Please adjust.")
+                isValid = False
+            elif param_value > self.qmax:
+                print("Outer radius exceeds maximum range. Please adjust.")
+                isValid = False
+        elif param_name == 'nbins':
+            # Can't be 0
+            if param_value < 1:
+                print("Number of bins cannot be less than or equal to 0. Please adjust.")
+                isValid = False
+
+        return isValid
+
+    def moveend(self, ev):
+        """
+        Called when any dragging motion ends.
+        Redraw the plot with new parameters.
+        """
+        self._post_data(self.nbins)
+
+    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 getParams(self):
+        """
+        Store a copy of values of parameters of the slicer into a dictionary.
+        :return params: the dictionary created
+        """
+        params = {}
+        params["inner_radius"] = numpy.fabs(self.inner_circle._inner_mouse_x)
+        params["outer_radius"] = numpy.fabs(self.outer_circle._inner_mouse_x)
+        params["nbins"] = self.nbins
+        return params
+
+    def setParams(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 = numpy.fabs(params["inner_radius"])
+        outer = numpy.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 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
+        # Inner circle marker
+        x_value = [self.sign * numpy.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]
+        # 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()
+        self.inner_marker.remove()
+        self.inner_circle.remove()
+
+    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 * numpy.pi / (self.npts - 1) * i
+
+            xval = 1.0 * self._inner_mouse_x * numpy.cos(phi)
+            yval = 1.0 * self._inner_mouse_x * numpy.sin(phi)
+
+            x.append(xval)
+            y.append(yval)
+
+        self.inner_marker.set(xdata=[self.sign * numpy.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
+
+    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 getParams(self):
+        """
+        Store a copy of values of parameters of the slicer into a dictionary.
+        :return params: the dictionary created
+        """
+        params = {}
+        params["radius"] = numpy.fabs(self._inner_mouse_x)
+        return params
+
+    def setParams(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(numpy.fabs(self.base.data.xmax),
+                        numpy.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.axes, '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()
+
+    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()
+
+    def update(self):
+        """
+        Respond to changes in the model by recalculating the profiles and
+        resetting the widgets.
+        """
+        # Update locations
+        self.outer_circle.update()
+        self._post_data()
+        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.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 is None:
+            return
+        mask = data.mask
+        from sas.sascalc.dataloader.manipulations import Ringcut
+
+        rmin = 0
+        rmax = numpy.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))
+        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 getParams(self):
+        """
+        Store a copy of values of parameters of the slicer into a dictionary.
+
+        :return params: the dictionary created
+
+        """
+        params = {}
+        params["outer_radius"] = numpy.fabs(self.outer_circle._inner_mouse_x)
+        return params
+
+    def setParams(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 = numpy.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 draw(self):
+        self.base.update()
+
Index: src/sas/qtgui/Plotting/Slicers/Arc.py
===================================================================
--- src/sas/qtgui/Plotting/Slicers/Arc.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
+++ src/sas/qtgui/Plotting/Slicers/Arc.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -0,0 +1,157 @@
+"""
+    Arc slicer for 2D data
+"""
+import math
+
+from BaseInteractor import BaseInteractor
+
+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
+
+        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/qtgui/Plotting/Slicers/AzimutSlicer.py
===================================================================
--- src/sas/qtgui/Plotting/Slicers/AzimutSlicer.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
+++ src/sas/qtgui/Plotting/Slicers/AzimutSlicer.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -0,0 +1,269 @@
+# 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
+from BaseInteractor import BaseInteractor
+
+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 = 2 * math.pi / 3
+        theta2 = -2 * math.pi / 3
+
+        # 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()
+
+    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()
+
+    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.sascalc.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.sasgui.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
+
+    def validate(self, param_name, param_value):
+        """
+        Test the proposed new value "value" for row "row" of parameters
+        """
+        # Here, always return true
+        return True
+
+    def moveend(self, ev):
+        #TODO: why is this empty?
+        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.sascalc.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.sascalc.dataloader.manipulations import SectorPhi
+        self.post_data(SectorPhi)
+
Index: src/sas/qtgui/Plotting/Slicers/BaseInteractor.py
===================================================================
--- src/sas/qtgui/Plotting/Slicers/BaseInteractor.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
+++ src/sas/qtgui/Plotting/Slicers/BaseInteractor.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -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(object):
+    """
+    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/qtgui/Plotting/Slicers/BoxSlicer.py
===================================================================
--- src/sas/qtgui/Plotting/Slicers/BoxSlicer.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
+++ src/sas/qtgui/Plotting/Slicers/BoxSlicer.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -0,0 +1,499 @@
+import numpy
+from PyQt4 import QtGui
+from PyQt4 import QtCore
+
+from BaseInteractor import BaseInteractor
+from sas.qtgui.Plotting.PlotterData import Data1D
+import sas.qtgui.Utilities.GuiUtils as GuiUtils
+from sas.qtgui.Plotting.SlicerModel import SlicerModel
+
+
+class BoxInteractor(BaseInteractor, SlicerModel):
+    """
+    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, item=None, color='black', zorder=3):
+        BaseInteractor.__init__(self, base, axes, color=color)
+        SlicerModel.__init__(self)
+        # Class initialization
+        self.markers = []
+        self.axes = axes
+        self._item = item
+        #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(numpy.fabs(self.base.data.xmax),
+                           numpy.fabs(self.base.data.xmin))
+        self.y = 0.5 * min(numpy.fabs(self.base.data.xmax),
+                           numpy.fabs(self.base.data.xmin))
+        # when reach qmax reset the graph
+        self.qmax = max(self.base.data.xmax, self.base.data.xmin,
+                        self.base.data.ymax, self.base.data.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.axes,
+                                            color='blue',
+                                            zorder=zorder,
+                                            y=self.y,
+                                            x=self.x)
+        self.vertical_lines.qmax = self.qmax
+
+        self.horizontal_lines = HorizontalLines(self,
+                                                self.axes,
+                                                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()
+        self.setModelFromParams()
+
+    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()
+
+    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.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 is None:
+            self.direction = direction
+
+        x_min = -1 * numpy.fabs(self.vertical_lines.x)
+        x_max = numpy.fabs(self.vertical_lines.x)
+        y_min = -1 * numpy.fabs(self.horizontal_lines.y)
+        y_max = numpy.fabs(self.horizontal_lines.y)
+
+        if nbins is not None:
+            self.nbins = nbins
+        if self.averager is None:
+            if new_slab is 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 = numpy.fabs(x_min)
+            bin_width = (x_max + x_low) / self.nbins
+        elif self.direction == "Y":
+            if self.fold:
+                y_low = 0
+            else:
+                y_low = numpy.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.data)
+        # 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.data.name + ")"
+        new_plot.title = str(self.averager.__name__) + \
+                        "(" + self.base.data.name + ")"
+        new_plot.source = self.base.data.source
+        new_plot.interactive = True
+        new_plot.detector = self.base.data.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.data
+        if hasattr(data, "scale") and data.scale == 'linear' and \
+                self.base.data.name.count("Residuals") > 0:
+            new_plot.ytransform = 'y'
+            new_plot.yaxis("\\rm{Residuals} ", "/")
+
+        new_plot.group_id = "2daverage" + self.base.data.name
+        new_plot.id = (self.averager.__name__) + self.base.data.name
+        new_plot.is_data = True
+        variant_plot = QtCore.QVariant(new_plot)
+        GuiUtils.updateModelItemWithPlot(self._item, variant_plot, new_plot.id)
+
+        if self.update_model:
+            self.setModelFromParams()
+        self.draw()
+
+    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._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 getParams(self):
+        """
+        Store a copy of values of parameters of the slicer into a dictionary.
+
+        :return params: the dictionary created
+
+        """
+        params = {}
+        params["x_max"] = numpy.fabs(self.vertical_lines.x)
+        params["y_max"] = numpy.fabs(self.horizontal_lines.y)
+        params["nbins"] = self.nbins
+        return params
+
+    def setParams(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(numpy.fabs(params["x_max"]))
+        self.y = float(numpy.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 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
+        # 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",
+                                           zorder=zorder,
+                                           visible=True)[0]
+        # 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()
+        self.inner_marker.remove()
+        self.top_line.remove()
+        self.bottom_line.remove()
+
+    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 is not None:
+            self.x = numpy.sign(self.x) * numpy.fabs(x)
+        if y is not None:
+            self.y = numpy.sign(self.y) * numpy.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
+
+    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 = numpy.fabs(x)
+        self.save_x = self.x
+        self.y = numpy.fabs(y)
+        self.save_y = y
+        # 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",
+                                           zorder=zorder, visible=True)[0]
+        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()
+        self.inner_marker.remove()
+        self.left_line.remove()
+        self.right_line.remove()
+
+    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 is not None:
+            self.x = numpy.sign(self.x) * numpy.fabs(x)
+        if y is not None:
+            self.y = numpy.sign(self.y) * numpy.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
+
+    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, item=None, color='black', zorder=3):
+        BoxInteractor.__init__(self, base, axes, item=item, color=color)
+        self.base = base
+        self._post_data()
+
+    def _post_data(self):
+        """
+        Post data creating by averaging in Qx direction
+        """
+        from sas.sascalc.dataloader.manipulations import SlabX
+        self.post_data(SlabX, direction="X")
+
+
+class BoxInteractorY(BoxInteractor):
+    """
+    Average in Qy direction
+    """
+    def __init__(self, base, axes, item=None, color='black', zorder=3):
+        BoxInteractor.__init__(self, base, axes, item=item, color=color)
+        self.base = base
+        self._post_data()
+
+    def _post_data(self):
+        """
+        Post data creating by averaging in Qy direction
+        """
+        from sas.sascalc.dataloader.manipulations import SlabY
+        self.post_data(SlabY, direction="Y")
+
Index: src/sas/qtgui/Plotting/Slicers/BoxSum.py
===================================================================
--- src/sas/qtgui/Plotting/Slicers/BoxSum.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
+++ src/sas/qtgui/Plotting/Slicers/BoxSum.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -0,0 +1,735 @@
+"""
+Boxsum Class: determine 2 rectangular area to compute
+the sum of pixel of a Data.
+"""
+import numpy
+from PyQt4 import QtGui
+from PyQt4 import QtCore
+from sas.qtgui.Utilities.GuiUtils import formatNumber
+
+from BaseInteractor import BaseInteractor
+from sas.sascalc.dataloader.manipulations import Boxavg
+from sas.sascalc.dataloader.manipulations import Boxsum
+
+from sas.qtgui.Plotting.SlicerModel import SlicerModel
+
+
+class BoxSumCalculator(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):
+        BaseInteractor.__init__(self, base, axes, color=color)
+
+        # list of Boxsmun markers
+        self.markers = []
+        self.axes = axes
+        self._model = None
+        self.update_model = False
+
+        # 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 boxsum limits
+        self.xmin = -1 * 0.5 * min(numpy.fabs(self.base.data.xmax),
+                                   numpy.fabs(self.base.data.xmin))
+        self.ymin = -1 * 0.5 * min(numpy.fabs(self.base.data.xmax),
+                                   numpy.fabs(self.base.data.xmin))
+        self.xmax = 0.5 * min(numpy.fabs(self.base.data.xmax),
+                              numpy.fabs(self.base.data.xmin))
+        self.ymax = 0.5 * min(numpy.fabs(self.base.data.xmax),
+                              numpy.fabs(self.base.data.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.axes,
+                                                     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.axes,
+                                                 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.axes, 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_model = False
+        self.update()
+        self.postData()
+
+        # set up the model
+        self._model = QtGui.QStandardItemModel(1, 9)
+        self.setModelFromParams()
+        self.update_model = True
+        self._model.itemChanged.connect(self.setParamsFromModel)
+
+    def setModelFromParams(self):
+        """
+        Set up the Qt model for data handling between controls
+        """
+        parameters = self.getParams()
+        # Crete/overwrite model items
+        self._model.setData(self._model.index(0, 0),
+                    QtCore.QVariant(formatNumber(parameters['Height'])))
+        self._model.setData(self._model.index(0, 1),
+                    QtCore.QVariant(formatNumber(parameters['Width'])))
+        self._model.setData(self._model.index(0, 2),
+                    QtCore.QVariant(formatNumber(parameters['center_x'])))
+        self._model.setData(self._model.index(0, 3),
+                    QtCore.QVariant(formatNumber(parameters['center_y'])))
+
+        self.setReadOnlyParametersFromModel()
+
+    def model(self):
+        ''' model accessor '''
+        return self._model
+
+    def setReadOnlyParametersFromModel(self):
+        """
+        Cast model content onto "read-only" subset of parameters
+        """
+        parameters = self.getParams()
+        self._model.setData(self._model.index(0, 4),
+                    QtCore.QVariant(formatNumber(parameters['avg'])))
+        self._model.setData(self._model.index(0, 5),
+                    QtCore.QVariant(formatNumber(parameters['avg_error'])))
+        self._model.setData(self._model.index(0, 6),
+                    QtCore.QVariant(formatNumber(parameters['sum'])))
+        self._model.setData(self._model.index(0, 7),
+                    QtCore.QVariant(formatNumber(parameters['sum_error'])))
+        self._model.setData(self._model.index(0, 8),
+                    QtCore.QVariant(formatNumber(parameters['num_points'])))
+
+    def setParamsFromModel(self):
+        """
+        Cast model content onto params dict
+        """
+        params = {}
+        params["Height"] = float(self.model().item(0, 0).text())
+        params["Width"] = float(self.model().item(0, 1).text())
+        params["center_x"] = float(self.model().item(0, 2).text())
+        params["center_y"] = float(self.model().item(0, 3).text())
+        self.update_model = False
+        self.setParams(params)
+        self.setReadOnlyParametersFromModel()
+        self.update_model = True
+
+    def setPanelName(self, name):
+        """
+        Store the name of the panel associated to this slicer
+        @param name: the name of this panel
+        """
+        self.panel_name = name
+
+    def setLayer(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()
+
+    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.horizontal_lines.save(ev)
+        self.vertical_lines.save(ev)
+        self.center.save(ev)
+
+    def postData(self):
+        """
+        Get the limits of the boxsum and compute the sum of the pixel
+        contained in that region and the error on that sum
+        """
+        # 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
+        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.data)
+        # Dig out number of points summed, SMK & PDB, 04/03/2013
+        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.data)
+        if self.update_model:
+            self.setModelFromParams()
+        self.draw()
+
+    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
+        """
+        # compute error an d sum of data's pixel
+        self.postData()
+
+    def restore(self):
+        """
+        Restore the roughness for this layer.
+        """
+        self.horizontal_lines.restore()
+        self.vertical_lines.restore()
+        self.center.restore()
+
+    def getParams(self):
+        """
+        Store a copy of values of parameters of the slicer into a dictionary.
+        :return params: the dictionary created
+        """
+        params = {}
+        params["Width"] = numpy.fabs(self.vertical_lines.half_width) * 2
+        params["Height"] = numpy.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 getResult(self):
+        """
+        Return the result of box summation
+        """
+        result = {}
+        result["num_points"] = self.points
+        result["avg"] = self.count
+        result["avg_error"] = self.error
+        result["sum"] = self.total
+        result["sum_error"] = self.totalerror
+        return result
+
+    def setParams(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 = numpy.fabs(params["Width"]) / 2
+        y_max = numpy.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.postData()
+
+    def draw(self):
+        """ Redraw canvas"""
+        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
+        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",
+                                            zorder=zorder,
+                                            visible=True)[0]
+        # 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 setLayer(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()
+        self.center.remove()
+        self.center_marker.remove()
+
+    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
+
+    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 setCursor(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 = numpy.fabs(y)
+        self.save_half_height = numpy.fabs(y)
+        # the with of the rectangle
+        self.half_width = numpy.fabs(self.x1 - self.x2) / 2
+        self.save_half_width = numpy.fabs(self.x1 - self.x2) / 2
+        # Create marker
+        self.right_marker = self.axes.plot([self.x1], [0], linestyle='',
+                                           marker='s', markersize=10,
+                                           color=self.color, alpha=0.6,
+                                           pickradius=5, label="pick",
+                                           zorder=zorder, visible=True)[0]
+
+        # 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 setLayer(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()
+        self.right_marker.remove()
+        self.right_line.remove()
+        self.left_line.remove()
+
+    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 is not None:
+            self.half_width = width
+        if height is not None:
+            self.half_height = height
+        # If new  center coordinates are given draw the rectangle
+        # given these value
+        if center is not 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 is not None:
+            self.x1 = x1
+        if x2 is not None:
+            self.x2 = x2
+        if y1 is not None:
+            self.y1 = y1
+        if y2 is not 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
+
+    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 = numpy.fabs(self.x1 - self.x2) / 2
+        self.has_move = True
+        self.base.base.update()
+
+    def setCursor(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 = numpy.fabs(y)
+        self.save_half_height = numpy.fabs(y)
+        self.half_width = numpy.fabs(x)
+        self.save_half_width = numpy.fabs(x)
+        self.top_marker = self.axes.plot([0], [self.y1], linestyle='',
+                                         marker='s', markersize=10,
+                                         color=self.color, alpha=0.6,
+                                         pickradius=5, label="pick",
+                                         zorder=zorder, visible=True)[0]
+
+        # 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 setLayer(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()
+        self.top_marker.remove()
+        self.bottom_line.remove()
+        self.top_line.remove()
+
+    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 is not None:
+            self.half_width = width
+        if height is not None:
+            self.half_height = height
+        # If new  center coordinates are given draw the rectangle
+        # given these value
+        if center is not 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 is not None:
+            self.x1 = x1
+        if x2 is not None:
+            self.x2 = x2
+        if y1 is not None:
+            self.y1 = y1
+        if y2 is not 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
+
+    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 = numpy.fabs(self.y1) - self.center_y
+        self.has_move = True
+        self.base.base.update()
+
+    def setCursor(self, x, y):
+        """
+        Update the figure given x and y
+        """
+        self.move(x, y, None)
+        self.update()
Index: src/sas/qtgui/Plotting/Slicers/SectorSlicer.py
===================================================================
--- src/sas/qtgui/Plotting/Slicers/SectorSlicer.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
+++ src/sas/qtgui/Plotting/Slicers/SectorSlicer.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -0,0 +1,558 @@
+"""
+    Sector interactor
+"""
+import numpy
+from PyQt4 import QtGui
+from PyQt4 import QtCore
+
+from BaseInteractor import BaseInteractor
+from sas.qtgui.Plotting.PlotterData import Data1D
+import sas.qtgui.Utilities.GuiUtils as GuiUtils
+from sas.qtgui.Plotting.SlicerModel import SlicerModel
+
+MIN_PHI = 0.05
+
+class SectorInteractor(BaseInteractor, SlicerModel):
+    """
+    Draw a sector slicer.Allow to performQ averaging on data 2D
+    """
+    def __init__(self, base, axes, item=None, color='black', zorder=3):
+
+        BaseInteractor.__init__(self, base, axes, color=color)
+        SlicerModel.__init__(self)
+        # Class initialization
+        self.markers = []
+        self.axes = axes
+        self._item = item
+        # Connect the plot to event
+        self.connect = self.base.connect
+
+        # Compute qmax limit to reset the graph
+        x = numpy.power(max(self.base.data.xmax,
+                         numpy.fabs(self.base.data.xmin)), 2)
+        y = numpy.power(max(self.base.data.ymax,
+                         numpy.fabs(self.base.data.ymin)), 2)
+        self.qmax = numpy.sqrt(x + y)
+        # Number of points on the plot
+        self.nbins = 20
+        # Angle of the middle line
+        self.theta2 = numpy.pi / 3
+        # Absolute value of the Angle between the middle line and any side line
+        self.phi = numpy.pi / 12
+        # Middle line
+        self.main_line = LineInteractor(self, self.axes, 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.axes, 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.axes, 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()
+        self.setModelFromParams()
+
+    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()
+
+    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.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.data
+        # If we have no data, just return
+        if data == None:
+            return
+        # Averaging
+        from sas.sascalc.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 + numpy.pi,
+                       phi_max=phimax + numpy.pi, nbins=nbins)
+
+        sector = sect(self.base.data)
+        # 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.data.name + ")"
+        new_plot.title = "SectorQ" + "(" + self.base.data.name + ")"
+        new_plot.source = self.base.data.source
+        new_plot.interactive = True
+        new_plot.detector = self.base.data.detector
+        # If the data file does not tell us what the axes are, just assume them.
+        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.data.name.count("Residuals") > 0:
+            new_plot.ytransform = 'y'
+            new_plot.yaxis("\\rm{Residuals} ", "/")
+
+        new_plot.group_id = "2daverage" + self.base.data.name
+        new_plot.id = "SectorQ" + self.base.data.name
+        new_plot.is_data = True
+        variant_plot = QtCore.QVariant(new_plot)
+        GuiUtils.updateModelItemWithPlot(self._item, variant_plot, new_plot.id)
+        self.base.manager.communicator.plotUpdateSignal.emit([new_plot])
+
+        if self.update_model:
+            self.setModelFromParams()
+        self.draw()
+
+    def validate(self, param_name, param_value):
+        """
+        Test the proposed new value "value" for row "row" of parameters
+        """
+        MIN_DIFFERENCE = 0.01
+        isValid = True
+
+        if param_name == 'Delta_Phi [deg]':
+            # First, check the closeness
+            if numpy.fabs(param_value) < MIN_DIFFERENCE:
+                print("Sector angles too close. Please adjust.")
+                isValid = False
+        elif param_name == 'nbins':
+            # Can't be 0
+            if param_value < 1:
+                print("Number of bins cannot be less than or equal to 0. Please adjust.")
+                isValid = False
+        return isValid
+
+    def moveend(self, ev):
+        """
+        Called a dragging motion ends.Get slicer event
+        """
+        # Post parameters
+        self._post_data(self.nbins)
+
+    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 getParams(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 numpy.fabs(self.left_line.phi) != numpy.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 / numpy.pi
+        params["Delta_Phi [deg]"] = numpy.fabs(self.left_line.phi * 180 / numpy.pi)
+        params["nbins"] = self.nbins
+        return params
+
+    def setParams(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]"] * numpy.pi / 180
+        phi = numpy.fabs(params["Delta_Phi [deg]"] * numpy.pi / 180)
+
+        # phi should not be too close.
+        if numpy.fabs(phi) < MIN_PHI:
+            phi = MIN_PHI
+            params["Delta_Phi [deg]"] = MIN_PHI
+
+        self.nbins = int(params["nbins"])
+        self.main_line.theta = main
+        # Reset the slicer parameters
+        self.main_line.update()
+        self.right_line.update(phi=phi, delta=None, mline=self.main_line,
+                               side=True, right=True)
+        self.left_line.update(phi=phi, delta=None,
+                              mline=self.main_line, side=True)
+        # Post the new corresponding data
+        self._post_data(nbins=self.nbins)
+
+    def draw(self):
+        """
+        Redraw canvas
+        """
+        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=numpy.pi / 4, theta2=numpy.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 * numpy.cos(self.theta)
+        y1 = self.radius * numpy.sin(self.theta)
+        x2 = -1 * self.radius * numpy.cos(self.theta)
+        y2 = -1 * self.radius * numpy.sin(self.theta)
+        # Defining a new 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",
+                                           zorder=zorder, visible=True)[0]
+
+        # 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
+
+        """
+        self.left_moving = left
+        theta3 = 0
+        if phi != None:
+            self.phi = phi
+        if delta == None:
+            delta = 0
+        if  right:
+            self.phi = -1 * numpy.fabs(self.phi)
+            #delta=-delta
+        else:
+            self.phi = numpy.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 * numpy.cos(theta3)
+        y1 = self.radius * numpy.sin(theta3)
+        x2 = -1 * self.radius * numpy.cos(theta3)
+        y2 = -1 * self.radius * numpy.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
+
+    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 = numpy.arctan2(y, x)
+        self.has_move = True
+        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 * numpy.pi - self.theta) >= numpy.pi / 2:
+                self.restore()
+                return
+            elif  self.theta2 < 0 and self.theta < 0 and \
+                (self.theta2 - self.theta) >= numpy.pi / 2:
+                self.restore()
+                return
+            elif self.theta2 > 0 and (self.theta2 - self.theta >= numpy.pi / 2 or \
+                (self.theta2 - self.theta >= numpy.pi / 2)):
+                self.restore()
+                return
+        else:
+            if  self.theta < 0 and (self.theta + numpy.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:
+                self.restore()
+                return
+            elif self.theta - self.theta2 >= numpy.pi / 2 or  \
+                ((self.theta + numpy.pi * 2 - self.theta2) >= numpy.pi / 2 and \
+                 self.theta < 0 and self.theta2 > 0):
+                self.restore()
+                return
+
+        self.phi = numpy.fabs(self.theta2 - self.theta)
+        if self.phi > numpy.pi:
+            self.phi = 2 * numpy.pi - numpy.fabs(self.theta2 - self.theta)
+        self.base.base.update()
+
+    def set_cursor(self, x, y):
+        self.move(x, y, None)
+        self.update()
+
+    def getParams(self):
+        params = {}
+        params["radius"] = self.radius
+        params["theta"] = self.theta
+        return params
+
+    def setParams(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=numpy.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 * numpy.cos(self.theta)
+        y1 = self.radius * numpy.sin(self.theta)
+        x2 = -1 * self.radius * numpy.cos(self.theta)
+        y2 = -1 * self.radius * numpy.sin(self.theta)
+        # 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",
+                                           zorder=zorder,
+                                           visible=True)[0]
+        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 * numpy.cos(self.theta)
+        y1 = self.radius * numpy.sin(self.theta)
+        x2 = -1 * self.radius * numpy.cos(self.theta)
+        y2 = -1 * self.radius * numpy.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
+
+    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 = numpy.arctan2(y, x)
+        self.has_move = True
+        self.base.base.update()
+
+    def set_cursor(self, x, y):
+        self.move(x, y, None)
+        self.update()
+
+    def getParams(self):
+        params = {}
+        params["radius"] = self.radius
+        params["theta"] = self.theta
+        return params
+
+    def setParams(self, params):
+        x = params["radius"]
+        self.set_cursor(x, None)
Index: src/sas/qtgui/Plotting/UnitTesting/ColorMapTest.py
===================================================================
--- src/sas/qtgui/Plotting/UnitTesting/ColorMapTest.py	(revision 83eb5208b8acb86b064162274de583670f72da47)
+++ src/sas/qtgui/Plotting/UnitTesting/ColorMapTest.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -10,5 +10,5 @@
 import path_prepare
 
-from sas.sasgui.guiframe.dataFitting import Data2D
+from sas.qtgui.Plotting.PlotterData import Data2D
 import sas.qtgui.Plotting.Plotter2D as Plotter2D
 from UnitTesting.TestUtils import WarningTestNotImplemented
Index: src/sas/qtgui/Plotting/UnitTesting/LinearFitTest.py
===================================================================
--- src/sas/qtgui/Plotting/UnitTesting/LinearFitTest.py	(revision 83eb5208b8acb86b064162274de583670f72da47)
+++ src/sas/qtgui/Plotting/UnitTesting/LinearFitTest.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -9,5 +9,5 @@
 import path_prepare
 
-from sas.sasgui.guiframe.dataFitting import Data1D
+from sas.qtgui.Plotting.PlotterData import Data1D
 import sas.qtgui.Plotting.Plotter as Plotter
 
Index: src/sas/qtgui/Plotting/UnitTesting/Plotter2DTest.py
===================================================================
--- src/sas/qtgui/Plotting/UnitTesting/Plotter2DTest.py	(revision 83eb5208b8acb86b064162274de583670f72da47)
+++ src/sas/qtgui/Plotting/UnitTesting/Plotter2DTest.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -12,6 +12,6 @@
 import path_prepare
 #######
-from sas.sasgui.guiframe.dataFitting import Data1D
-from sas.sasgui.guiframe.dataFitting import Data2D
+from sas.qtgui.Plotting.PlotterData import Data1D
+from sas.qtgui.Plotting.PlotterData import Data2D
 from UnitTesting.TestUtils import WarningTestNotImplemented
 
Index: src/sas/qtgui/Plotting/UnitTesting/PlotterBaseTest.py
===================================================================
--- src/sas/qtgui/Plotting/UnitTesting/PlotterBaseTest.py	(revision 83eb5208b8acb86b064162274de583670f72da47)
+++ src/sas/qtgui/Plotting/UnitTesting/PlotterBaseTest.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -14,5 +14,4 @@
 from sas.qtgui.Plotting.ScaleProperties import ScaleProperties
 from sas.qtgui.Plotting.WindowTitle import WindowTitle
-#import sas.qtgui.GuiUtils as GuiUtils
 from sas.qtgui.Utilities.GuiUtils import *
 import sas.qtgui.Plotting.PlotHelper as PlotHelper
Index: src/sas/qtgui/Plotting/UnitTesting/PlotterTest.py
===================================================================
--- src/sas/qtgui/Plotting/UnitTesting/PlotterTest.py	(revision 83eb5208b8acb86b064162274de583670f72da47)
+++ src/sas/qtgui/Plotting/UnitTesting/PlotterTest.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -11,7 +11,7 @@
 import path_prepare
 #######
-from sas.sasgui.guiframe.dataFitting import Data1D
-from sas.sasgui.guiframe.dataFitting import Data2D
-from UnitTesting.TestUtils import WarningTestNotImplemented
+from sas.qtgui.Plotting.PlotterData import Data1D
+from sas.qtgui.Plotting.PlotterData import Data2D
+from sas.qtgui.UnitTesting.TestUtils import WarningTestNotImplemented
 from sas.qtgui.Plotting.LinearFit import LinearFit
 from sas.qtgui.Plotting.PlotProperties import PlotProperties
Index: src/sas/qtgui/Utilities/CategoryInstaller.py
===================================================================
--- src/sas/qtgui/Utilities/CategoryInstaller.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
+++ src/sas/qtgui/Utilities/CategoryInstaller.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -0,0 +1,177 @@
+"""
+Class for making sure all category stuff is installed
+and works fine.
+
+Copyright (c) Institut Laue-Langevin 2012
+
+@author kieranrcampbell@gmail.com
+@modified by NIST/MD sasview team
+"""
+
+import os
+import sys
+import json
+import logging
+from collections import defaultdict, OrderedDict
+
+USER_FILE = 'categories.json'
+
+class CategoryInstaller:
+    """
+    Class for making sure all category stuff is installed
+
+    Note - class is entirely static!
+    """
+
+    def __init__(self):
+        """ initialization """
+
+    @staticmethod
+    def _get_installed_model_dir():
+        """
+        returns the dir where installed_models.txt should be
+        """
+        import sas.sascalc.dataloader.readers
+        return sas.sascalc.dataloader.readers.get_data_path()
+
+    #@staticmethod
+    #def _get_models_py_dir():
+    #    """
+    #    returns the dir where models.py should be
+    #    """
+    #    import sas.sasgui.perspectives.fitting.models
+    #    return sas.sasgui.perspectives.fitting.models.get_model_python_path()
+
+    @staticmethod
+    def _get_default_cat_file_dir():
+        """
+        returns the dir where default_cat.j should be
+        """
+        # The default categories file is usually found with the code, except
+        # when deploying using py2app (it will be in Contents/Resources), or
+        # py2exe (it will be in the exec dir).
+        import sas.sasview
+        cat_file = "default_categories.json"
+
+        possible_cat_file_paths = [
+            os.path.join(os.path.split(sas.sasview.__file__)[0], cat_file),           # Source
+            os.path.join(os.path.dirname(sys.executable), '..', 'Resources', cat_file), # Mac
+            os.path.join(os.path.dirname(sys.executable), cat_file)                     # Windows
+        ]
+
+        for path in possible_cat_file_paths:
+            if os.path.isfile(path):
+                return os.path.dirname(path)
+
+        raise RuntimeError('CategoryInstaller: Could not find folder containing default categories')
+
+    @staticmethod
+    def _get_home_dir():
+        """
+        returns the users sasview config dir
+        """
+        return os.path.join(os.path.expanduser("~"), ".sasview")
+
+    @staticmethod
+    def _regenerate_model_dict(master_category_dict):
+        """
+        regenerates self.by_model_dict which has each model name as the key
+        and the list of categories belonging to that model
+        along with the enabled mapping
+        returns tuplet (by_model_dict, model_enabled_dict)
+        """
+        by_model_dict = defaultdict(list)
+        model_enabled_dict = defaultdict(bool)
+        
+        for category in master_category_dict:
+            for (model, enabled) in master_category_dict[category]:
+                by_model_dict[model].append(category)
+                model_enabled_dict[model] = enabled
+
+        return (by_model_dict, model_enabled_dict)
+
+    @staticmethod
+    def _regenerate_master_dict(by_model_dict, model_enabled_dict):
+        """
+        regenerates master_category_dict from by_model_dict 
+        and model_enabled_dict
+        returns the master category dictionary
+        """
+        master_category_dict = defaultdict(list)
+        for model in by_model_dict:
+            for category in by_model_dict[model]:
+                master_category_dict[category].append(\
+                    (model, model_enabled_dict[model]))
+        return OrderedDict(sorted(master_category_dict.items(), key=lambda t: t[0]))
+
+    @staticmethod
+    def get_user_file():
+        """
+        returns the user data file, eg .sasview/categories.json.json
+        """
+        return os.path.join(CategoryInstaller._get_home_dir(), USER_FILE)
+
+    @staticmethod
+    def get_default_file():
+        logging.warning("CategoryInstaller.get_default_file is deprecated.")
+
+    @staticmethod
+    def check_install(homedir = None, model_list=None):
+        """
+        the main method of this class
+        makes sure categories.json exists and if not
+        compile it and install
+        :param homefile: Override the default home directory
+        :param model_list: List of model names except customized models
+        """
+        _model_dict = { model.name: model for model in model_list}
+        _model_list = _model_dict.keys()
+
+        serialized_file = None
+        if homedir == None:
+            serialized_file = CategoryInstaller.get_user_file()
+        else:
+            serialized_file = os.path.join(homedir, USER_FILE)
+        if os.path.isfile(serialized_file):
+            with open(serialized_file, 'rb') as f:
+                master_category_dict = json.load(f)
+        else:
+            master_category_dict = defaultdict(list)
+
+        (by_model_dict, model_enabled_dict) = \
+                CategoryInstaller._regenerate_model_dict(master_category_dict)
+        add_list = _model_list
+        del_name = False
+        for cat in master_category_dict.keys():
+            for ind in range(len(master_category_dict[cat])):
+                model_name, enabled = master_category_dict[cat][ind]
+                if model_name not in _model_list:
+                    del_name = True 
+                    try:
+                        by_model_dict.pop(model_name)
+                        model_enabled_dict.pop(model_name)
+                    except:
+                        logging.error("CategoryInstaller: %s", sys.exc_value)
+                else:
+                    add_list.remove(model_name)
+        if del_name or (len(add_list) > 0):
+            for model in add_list:
+                model_enabled_dict[model]= True
+                if _model_dict[model].category is None or len(str(_model_dict[model].category.capitalize())) == 0:
+                    by_model_dict[model].append('Uncategorized')
+                else:
+                    category = _model_dict[model].category
+                    toks = category.split(':')
+                    category = toks[-1]
+                    toks = category.split('-')
+                    capitalized_words = [t.capitalize() for t in toks]
+                    category = ' '.join(capitalized_words)
+
+                    by_model_dict[model].append(category)
+
+            master_category_dict = \
+                CategoryInstaller._regenerate_master_dict(by_model_dict,
+                                                          model_enabled_dict)
+
+            json.dump(master_category_dict, open(serialized_file, 'wb'))
+
Index: src/sas/qtgui/Utilities/ConnectionProxy.py
===================================================================
--- src/sas/qtgui/Utilities/ConnectionProxy.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
+++ src/sas/qtgui/Utilities/ConnectionProxy.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -0,0 +1,156 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+import urllib2
+import sys
+import json
+import logging
+import re
+
+'''
+HTTP Proxy parser and Connection
+
+connect() function:
+    - auto detects proxy in windows, osx
+    - in ux systems, the http_proxy enviroment variable must be set
+    - if it fails, try to find the proxy.pac address.
+      - parses the file, and looks up for all possible proxies
+'''
+
+
+class ConnectionProxy(object):
+
+    def __init__(self, url, timeout):
+        self.url = url
+        self.timeout = timeout
+
+    def _get_addresses_of_proxy_pac(self):
+        """
+        Return a list of possible auto proxy .pac files being used,
+        based on the system registry (win32) or system preferences (OSX).
+        @return: list of urls
+        """
+        pac_files = []
+        if sys.platform == 'win32':
+            try:
+                import _winreg as winreg  # used from python 2.0-2.6
+            except:
+                import winreg  # used from python 2.7 onwards
+            net = winreg.OpenKey(
+                winreg.HKEY_CURRENT_USER,
+                "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"
+            )
+            n_subs, n_vals, last_mod = winreg.QueryInfoKey(net)
+            subkeys = {}
+            for i in range(n_vals):
+                this_name, this_val, this_type = winreg.EnumValue(net, i)
+                subkeys[this_name] = this_val
+            if 'AutoConfigURL' in subkeys.keys() and len(subkeys['AutoConfigURL']) > 0:
+                pac_files.append(subkeys['AutoConfigURL'])
+        elif sys.platform == 'darwin':
+            import plistlib
+            sys_prefs = plistlib.readPlist(
+                '/Library/Preferences/SystemConfiguration/preferences.plist')
+            networks = sys_prefs['NetworkServices']
+            # loop through each possible network (e.g. Ethernet, Airport...)
+            for network in networks.items():
+                # the first part is a long identifier
+                net_key, network = network
+                if 'ProxyAutoConfigURLString' in network['Proxies'].keys():
+                    pac_files.append(
+                        network['Proxies']['ProxyAutoConfigURLString'])
+        return list(set(pac_files))  # remove redundant ones
+
+    def _parse_proxy_pac(self, pac_urls_list):
+        '''
+        For every pac file url in pac_urls_list, it tryes to connect.
+        If the connection is successful parses the file in search for
+        http proxies.
+        @param pac_urls_list: List with urls for the pac files
+        @return: list with all found http proxies
+        '''
+        proxy_url_list = []
+        for this_pac_url in pac_urls_list:
+            logging.debug('Trying pac file (%s)...' % this_pac_url)
+            try:
+                response = urllib2.urlopen(
+                    this_pac_url, timeout=self.timeout)
+                logging.debug('Succeeded (%s)...' % this_pac_url)
+            except Exception:
+                logging.debug('Failled (%s)...' % this_pac_url)
+                continue
+            pacStr = response.read()
+            possProxies = re.findall(
+                r"PROXY\s([^\s;,:]+:[0-9]{1,5})[^0-9]", pacStr + '\n')
+            for thisPoss in possProxies:
+                prox_url = 'http://' + thisPoss
+                proxy_dic = {'http': prox_url}
+                proxy_url_list.append(proxy_dic)
+        return proxy_url_list
+
+    def _set_proxy(self,proxy_dic=None):
+        '''
+        Sets connection proxy.
+        if proxy_dic is None get's teh proxy from the system.
+        To disable autodetected proxy pass an empty dictionary: {}
+        @param proxy_dic: format: {'http': 'http://www.example.com:3128/'}               
+        '''
+        if proxy_dic is None:
+            # The default is to read the list of proxies from the environment variables <protocol>_proxy.
+            # If no proxy environment variables are set, then in a Windows environment proxy settings are
+            # obtained from the registry's Internet Settings section, and in a Mac OS X environment proxy
+            # information is retrieved from the OS X System Configuration
+            # Framework.
+            proxy = urllib2.ProxyHandler()
+        else:
+            # If proxies is given, it must be a dictionary mapping protocol names to
+            # URLs of proxies.
+            proxy = urllib2.ProxyHandler(proxy_dic)
+        opener = urllib2.build_opener(proxy)
+        urllib2.install_opener(opener)
+
+    def connect(self):
+        '''
+        Performs the request and gets a response from self.url
+        @return: response object from urllib2.urlopen
+        '''
+        req = urllib2.Request(self.url)
+        response = None
+        try:
+            logging.debug("Trying Direct connection to %s..."%self.url)
+            response = urllib2.urlopen(req, timeout=self.timeout)
+        except Exception, e:
+            logging.debug("Failed!")
+            logging.debug(e)
+            try:
+                logging.debug("Trying to use system proxy if it exists...")
+                self._set_proxy()
+                response = urllib2.urlopen(req, timeout=self.timeout)
+            except Exception, e:
+                logging.debug("Failed!")
+                logging.debug(e)
+                pac_urls = self._get_addresses_of_proxy_pac()
+                proxy_urls = self._parse_proxy_pac(pac_urls)
+                for proxy in proxy_urls:
+                    try:
+                        logging.debug("Trying to use the proxy %s found in proxy.pac configuration"%proxy)
+                        self._set_proxy(proxy)
+                        response = urllib2.urlopen(req, timeout=self.timeout)
+                    except Exception, e:
+                        logging.debug("Failed!")
+                        logging.debug(e)
+        if response is not None:
+            logging.debug("The connection to %s was successful."%self.url)
+        else:
+            logging.warning("Connection to %s failed..."%self.url)
+        return response
+
+
+if __name__ == "__main__":
+    from pprint import pprint
+    c = Connection()
+    response = c.connect()
+    if response is not None:
+        print 50 * '-'
+        content = json.loads(response.read().strip())
+        pprint(content)
+
Index: src/sas/qtgui/Utilities/CustomDir.py
===================================================================
--- src/sas/qtgui/Utilities/CustomDir.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
+++ src/sas/qtgui/Utilities/CustomDir.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -0,0 +1,58 @@
+# Setup and find Custom config dir
+import os.path
+import shutil
+
+CONF_DIR = 'config' 
+APPLICATION_NAME = 'sasview'
+
+def _find_usersasview_dir():
+    """
+    Find and return user/.sasview dir
+    """
+    return os.path.join(os.path.expanduser("~"), ("." + APPLICATION_NAME))
+
+def _find_customconf_dir():
+    """
+    Find path of the config directory.
+    The plugin directory is located in the user's home directory.
+    """
+    u_dir = _find_usersasview_dir()
+    return os.path.join(u_dir, CONF_DIR)
+
+def setup_conf_dir(path):
+    """
+    Setup the custom config dir and cat file
+    """
+    conf_dir = _find_customconf_dir()
+    # If the plugin directory doesn't exist, create it
+    if not os.path.isdir(conf_dir):
+        os.makedirs(conf_dir)
+    config_file = os.path.join(conf_dir, "custom_config.py")
+
+    # Place example user models as needed
+    try:
+        if not os.path.isfile(config_file):
+            shutil.copyfile(os.path.join(path, "custom_config.py"), config_file)
+
+        #Adding SAS_OPENCL if it doesn't exist in the config file
+        # - to support backcompability
+        if not "SAS_OPENCL" in open(config_file).read():
+            open(config_file,"a+").write("SAS_OPENCL = \"None\"\n")
+    except:
+        # Check for data path next to exe/zip file.
+        #Look for maximum n_dir up of the current dir to find plugins dir
+        n_dir = 12
+        is_dir = False
+        f_dir = path
+        for i in range(n_dir):
+            if i > 1:
+                f_dir, _ = os.path.split(f_dir)
+            temp_path = os.path.join(f_dir, "custom_config.py")
+            if os.path.isfile(temp_path):
+                shutil.copyfile(temp_path, config_file)
+                is_dir = True
+                break
+        if not is_dir:
+            raise
+    return conf_dir
+
Index: src/sas/qtgui/Utilities/GenericReader.py
===================================================================
--- src/sas/qtgui/Utilities/GenericReader.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
+++ src/sas/qtgui/Utilities/GenericReader.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -0,0 +1,48 @@
+"""
+Thread handler used to load data
+"""
+import time
+from sas.sascalc.data_util.calcthread import CalcThread
+
+class GenReader(CalcThread):
+    """
+    Load a sld data given a filename
+    """
+    def __init__(self, path, loader,
+                 completefn=None,
+                 updatefn=None,
+                 yieldtime=0.01,
+                 worktime=0.01
+                 ):
+        CalcThread.__init__(self, completefn,
+                 updatefn,
+                 yieldtime,
+                 worktime)
+        self.path = path
+        #Instantiate a loader 
+        self.loader = loader
+        self.starttime = 0
+
+    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()
+        try:
+            data = self.loader.read(self.path)
+            self.complete(data=data)
+        except:
+            # Thread was interrupted, just proceed and re-raise.
+            # Real code should not print, but this is an example...
+            raise
+
Index: src/sas/qtgui/Utilities/GuiUtils.py
===================================================================
--- src/sas/qtgui/Utilities/GuiUtils.py	(revision ad6b4e2ec9617a81f76dd82998ddb4e7e176db46)
+++ src/sas/qtgui/Utilities/GuiUtils.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -4,4 +4,5 @@
 
 import os
+import re
 import sys
 import imp
@@ -16,21 +17,14 @@
 from PyQt4 import QtGui
 
-# Translate event handlers
-#from sas.sasgui.guiframe.events import EVT_CATEGORY
-#from sas.sasgui.guiframe.events import EVT_STATUS
-#from sas.sasgui.guiframe.events import EVT_APPEND_BOOKMARK
-#from sas.sasgui.guiframe.events import EVT_PANEL_ON_FOCUS
-#from sas.sasgui.guiframe.events import EVT_NEW_LOAD_DATA
-#from sas.sasgui.guiframe.events import EVT_NEW_COLOR
-#from sas.sasgui.guiframe.events import StatusEvent
-#from sas.sasgui.guiframe.events import NewPlotEvent
-
 from periodictable import formula as Formula
 
-from sas.sasgui.plottools import transform
-from sas.sasgui.plottools.convert_units import convert_unit
-from sas.sasgui.guiframe.dataFitting import Data1D
-from sas.sasgui.guiframe.dataFitting import Data2D
+from sas.qtgui.Plotting import DataTransform
+from sas.qtgui.Plotting.ConvertUnits import convertUnit
+
+from sas.qtgui.Plotting.PlotterData import Data1D
+from sas.qtgui.Plotting.PlotterData import Data2D
+
 from sas.sascalc.dataloader.loader import Loader
+from sas.qtgui.Utilities import CustomDir
 
 
@@ -109,17 +103,16 @@
 if config is None:
     config = _find_local_config('local_config', os.getcwd())
-    if config is None:
-        # Didn't find local config, load the default
-        import sas.sasgui.guiframe.config as config
-        #logging.info("using default local_config")
-    else:
-        pass
-        #logging.info("found local_config in %s", os.getcwd())
+    #if config is None:
+    #    # Didn't find local config, load the default
+    #    import sas.sasgui.guiframe.config as config
+    #    #logging.info("using default local_config")
+    #else:
+    #    pass
+    #    #logging.info("found local_config in %s", os.getcwd())
 else:
     pass
     #logging.info("found local_config in %s", PATH_APP)
 
-from sas.sasgui.guiframe.customdir  import SetupCustom
-c_conf_dir = SetupCustom().setup_dir(PATH_APP)
+c_conf_dir = CustomDir.setup_conf_dir(PATH_APP)
 
 custom_config = _find_local_config('custom_config', c_conf_dir)
@@ -183,5 +176,5 @@
     DEFAULT_OPEN_FOLDER = PATH_APP
 
-DEFAULT_STYLE = config.DEFAULT_STYLE
+#DEFAULT_STYLE = config.DEFAULT_STYLE
 
 PLUGIN_STATE_EXTENSIONS = config.PLUGIN_STATE_EXTENSIONS
@@ -672,24 +665,24 @@
     # X
     if xLabel == "x":
-        data.transformX(transform.toX, transform.errToX)
+        data.transformX(DataTransform.toX, DataTransform.errToX)
         xLabel = "%s(%s)" % (xname, xunits)
     if xLabel == "x^(2)":
-        data.transformX(transform.toX2, transform.errToX2)
-        xunits = convert_unit(2, xunits)
+        data.transformX(DataTransform.toX2, DataTransform.errToX2)
+        xunits = convertUnit(2, xunits)
         xLabel = "%s^{2}(%s)" % (xname, xunits)
     if xLabel == "x^(4)":
-        data.transformX(transform.toX4, transform.errToX4)
-        xunits = convert_unit(4, xunits)
+        data.transformX(DataTransform.toX4, DataTransform.errToX4)
+        xunits = convertUnit(4, xunits)
         xLabel = "%s^{4}(%s)" % (xname, xunits)
     if xLabel == "ln(x)":
-        data.transformX(transform.toLogX, transform.errToLogX)
+        data.transformX(DataTransform.toLogX, DataTransform.errToLogX)
         xLabel = "\ln{(%s)}(%s)" % (xname, xunits)
     if xLabel == "log10(x)":
-        data.transformX(transform.toX_pos, transform.errToX_pos)
+        data.transformX(DataTransform.toX_pos, DataTransform.errToX_pos)
         xscale = 'log'
         xLabel = "%s(%s)" % (xname, xunits)
     if xLabel == "log10(x^(4))":
-        data.transformX(transform.toX4, transform.errToX4)
-        xunits = convert_unit(4, xunits)
+        data.transformX(DataTransform.toX4, DataTransform.errToX4)
+        xunits = convertUnit(4, xunits)
         xLabel = "%s^{4}(%s)" % (xname, xunits)
         xscale = 'log'
@@ -697,48 +690,47 @@
     # Y
     if yLabel == "ln(y)":
-        data.transformY(transform.toLogX, transform.errToLogX)
+        data.transformY(DataTransform.toLogX, DataTransform.errToLogX)
         yLabel = "\ln{(%s)}(%s)" % (yname, yunits)
     if yLabel == "y":
-        data.transformY(transform.toX, transform.errToX)
+        data.transformY(DataTransform.toX, DataTransform.errToX)
         yLabel = "%s(%s)" % (yname, yunits)
     if yLabel == "log10(y)":
-        data.transformY(transform.toX_pos, transform.errToX_pos)
+        data.transformY(DataTransform.toX_pos, DataTransform.errToX_pos)
         yscale = 'log'
         yLabel = "%s(%s)" % (yname, yunits)
     if yLabel == "y^(2)":
-        data.transformY(transform.toX2, transform.errToX2)
-        yunits = convert_unit(2, yunits)
+        data.transformY(DataTransform.toX2, DataTransform.errToX2)
+        yunits = convertUnit(2, yunits)
         yLabel = "%s^{2}(%s)" % (yname, yunits)
     if yLabel == "1/y":
-        data.transformY(transform.toOneOverX, transform.errOneOverX)
-        yunits = convert_unit(-1, yunits)
+        data.transformY(DataTransform.toOneOverX, DataTransform.errOneOverX)
+        yunits = convertUnit(-1, yunits)
         yLabel = "1/%s(%s)" % (yname, yunits)
     if yLabel == "y*x^(2)":
-        data.transformY(transform.toYX2, transform.errToYX2)
-        xunits = convert_unit(2, xunits)
+        data.transformY(DataTransform.toYX2, DataTransform.errToYX2)
+        xunits = convertUnit(2, xunits)
         yLabel = "%s \ \ %s^{2}(%s%s)" % (yname, xname, yunits, xunits)
     if yLabel == "y*x^(4)":
-        data.transformY(transform.toYX4, transform.errToYX4)
-        xunits = convert_unit(4, xunits)
+        data.transformY(DataTransform.toYX4, DataTransform.errToYX4)
+        xunits = convertUnit(4, xunits)
         yLabel = "%s \ \ %s^{4}(%s%s)" % (yname, xname, yunits, xunits)
     if yLabel == "1/sqrt(y)":
-        data.transformY(transform.toOneOverSqrtX,
-                                transform.errOneOverSqrtX)
-        yunits = convert_unit(-0.5, yunits)
+        data.transformY(DataTransform.toOneOverSqrtX, DataTransform.errOneOverSqrtX)
+        yunits = convertUnit(-0.5, yunits)
         yLabel = "1/\sqrt{%s}(%s)" % (yname, yunits)
     if yLabel == "ln(y*x)":
-        data.transformY(transform.toLogXY, transform.errToLogXY)
+        data.transformY(DataTransform.toLogXY, DataTransform.errToLogXY)
         yLabel = "\ln{(%s \ \ %s)}(%s%s)" % (yname, xname, yunits, xunits)
     if yLabel == "ln(y*x^(2))":
-        data.transformY(transform.toLogYX2, transform.errToLogYX2)
-        xunits = convert_unit(2, xunits)
+        data.transformY(DataTransform.toLogYX2, DataTransform.errToLogYX2)
+        xunits = convertUnit(2, xunits)
         yLabel = "\ln (%s \ \ %s^{2})(%s%s)" % (yname, xname, yunits, xunits)
     if yLabel == "ln(y*x^(4))":
-        data.transformY(transform.toLogYX4, transform.errToLogYX4)
-        xunits = convert_unit(4, xunits)
+        data.transformY(DataTransform.toLogYX4, DataTransform.errToLogYX4)
+        xunits = convertUnit(4, xunits)
         yLabel = "\ln (%s \ \ %s^{4})(%s%s)" % (yname, xname, yunits, xunits)
     if yLabel == "log10(y*x^(4))":
-        data.transformY(transform.toYX4, transform.errToYX4)
-        xunits = convert_unit(4, xunits)
+        data.transformY(DataTransform.toYX4, DataTransform.errToYX4)
+        xunits = convertUnit(4, xunits)
         yscale = 'log'
         yLabel = "%s \ \ %s^{4}(%s%s)" % (yname, xname, yunits, xunits)
@@ -792,2 +784,14 @@
     else:
         return unit
+
+def parseName(name, expression):
+    """
+    remove "_" in front of a name
+    """
+    if re.match(expression, name) is not None:
+        word = re.split(expression, name, 1)
+        for item in word:           
+            if item.lstrip().rstrip() != '':
+                return item
+    else:
+        return name
Index: src/sas/qtgui/Utilities/LocalConfig.py
===================================================================
--- src/sas/qtgui/Utilities/LocalConfig.py	(revision 83eb5208b8acb86b064162274de583670f72da47)
+++ src/sas/qtgui/Utilities/LocalConfig.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -4,7 +4,6 @@
 import time
 import os
-import logging
 
-from sas.sasgui.guiframe.gui_style import GUIFRAME
+#from sas.sasgui.guiframe.gui_style import GUIFRAME
 import sas.sasview
 
@@ -104,6 +103,6 @@
 SPLASH_SCREEN_PATH = os.path.join(icon_path, "SVwelcome_mini.png")
 TUTORIAL_PATH = os.path.join(media_path, "Tutorial.pdf")
-DEFAULT_STYLE = GUIFRAME.MULTIPLE_APPLICATIONS|GUIFRAME.MANAGER_ON\
-                    |GUIFRAME.CALCULATOR_ON|GUIFRAME.TOOLBAR_ON
+#DEFAULT_STYLE = GUIFRAME.MULTIPLE_APPLICATIONS|GUIFRAME.MANAGER_ON\
+#                    |GUIFRAME.CALCULATOR_ON|GUIFRAME.TOOLBAR_ON
 SPLASH_SCREEN_WIDTH = 512
 SPLASH_SCREEN_HEIGHT = 366
Index: src/sas/qtgui/Utilities/UnitTesting/GuiUtilsTest.py
===================================================================
--- src/sas/qtgui/Utilities/UnitTesting/GuiUtilsTest.py	(revision 83eb5208b8acb86b064162274de583670f72da47)
+++ src/sas/qtgui/Utilities/UnitTesting/GuiUtilsTest.py	(revision dc5ef150e8b37e01a3b7e253eb5b66d3b82de99c)
@@ -12,7 +12,7 @@
 # SV imports
 from sas.sascalc.dataloader.loader import Loader
-from sas.sasgui.guiframe.data_manager import DataManager
-from sas.sasgui.guiframe.dataFitting import Data1D
-from sas.sasgui.guiframe.dataFitting import Data2D
+from sas.qtgui.MainWindow.DataManager import DataManager
+from sas.qtgui.Plotting.PlotterData import Data1D
+from sas.qtgui.Plotting.PlotterData import Data2D
 
 # Tested module
