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
 
