Index: src/sas/perspectives/fitting/__init__.py
===================================================================
--- src/sas/perspectives/fitting/__init__.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/fitting/__init__.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,48 @@
+PLUGIN_ID = "Fitting plug-in 1.0"
+import os
+from fitting import *
+from distutils.filelist import findall
+def get_data_path(media):
+    """
+    """
+    # Check for data path in the package
+    path = os.path.join(os.path.dirname(__file__), media)
+    if os.path.isdir(path):
+        return path
+
+    # Check for data path next to exe/zip file.
+    # If we are inside a py2exe zip file, we need to go up
+    # to get to the directory containing 
+    # the media for this module
+    path = os.path.dirname(__file__)
+    #Look for maximum n_dir up of the current dir to find media
+    n_dir = 12
+    for i in range(n_dir):
+        path, _ = os.path.split(path)
+        media_path = os.path.join(path, media)
+        if os.path.isdir(media_path):
+            module_media_path = os.path.join(media_path, 'fitting_media')
+            if os.path.isdir(module_media_path):
+                return module_media_path
+            return media_path
+   
+    raise RuntimeError('Could not find models media files')
+
+def data_files():
+    """
+    Return the data files associated with media.
+    
+    The format is a list of (directory, [files...]) pairs which can be
+    used directly in setup(...,data_files=...) for setup.py.
+
+    """
+    data_files = []
+    path = os.path.dirname(__file__)
+    p_path = os.path.join(path, 'plugin_models')
+    for f in findall(p_path):
+        data_files.append(('plugin_models', [f]))
+    path = get_data_path(media="media")
+    for f in findall(path):
+        data_files.append(('media/fitting_media', [f]))
+    
+    return data_files
Index: src/sas/perspectives/fitting/basepage.py
===================================================================
--- src/sas/perspectives/fitting/basepage.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/fitting/basepage.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,3883 @@
+"""
+Base Page for fitting
+"""
+import sys
+import os
+import wx
+import numpy
+import time
+import copy
+import math
+import string
+import json
+from collections import defaultdict
+from wx.lib.scrolledpanel import ScrolledPanel
+from sas.guiframe.panel_base import PanelBase
+from sas.guiframe.utils import format_number, check_float
+from sas.guiframe.events import PanelOnFocusEvent
+from sas.guiframe.events import StatusEvent
+from sas.guiframe.events import AppendBookmarkEvent
+from sas.guiframe.dataFitting import Data2D
+from sas.guiframe.dataFitting import Data1D
+from sas.guiframe.dataFitting import check_data_validity
+from sas.guiframe.gui_style import GUIFRAME_ID
+from sas.dataloader.data_info import Detector
+from sas.dataloader.data_info import Source
+from sas.perspectives.fitting.pagestate import PageState
+from sas.guiframe.CategoryInstaller import CategoryInstaller
+
+(PageInfoEvent, EVT_PAGE_INFO) = wx.lib.newevent.NewEvent()
+(PreviousStateEvent, EVT_PREVIOUS_STATE) = wx.lib.newevent.NewEvent()
+(NextStateEvent, EVT_NEXT_STATE) = wx.lib.newevent.NewEvent()
+
+_BOX_WIDTH = 76
+_QMIN_DEFAULT = 0.0005
+_QMAX_DEFAULT = 0.5
+_NPTS_DEFAULT = 50
+#Control panel width
+if sys.platform.count("win32") > 0:
+    PANEL_WIDTH = 450
+    FONT_VARIANT = 0
+    ON_MAC = False
+else:
+    PANEL_WIDTH = 500
+    FONT_VARIANT = 1
+    ON_MAC = True
+
+
+class BasicPage(ScrolledPanel, PanelBase):
+    """
+    This class provide general structure of  fitpanel page
+    """
+    ## Internal name for the AUI manager
+    window_name = "Fit Page"
+    ## Title to appear on top of the window
+    window_caption = "Fit Page "
+    
+    def __init__(self, parent, color='blue', **kwargs):
+        """
+        """
+        ScrolledPanel.__init__(self, parent, **kwargs)
+        PanelBase.__init__(self, parent)
+        self.SetupScrolling()
+        #Set window's font size
+        self.SetWindowVariant(variant=FONT_VARIANT)
+      
+        self.SetBackgroundColour(color)
+        ## parent of the page
+        self.parent = parent
+        ## manager is the fitting plugin
+        ## owner of the page (fitting plugin)
+        self.event_owner = None
+        ## current model
+        self.model = None
+        self.m_name = None
+        self.index_model = None
+        self.panel = None
+        ## data
+        self.data = None
+        #list of available data
+        self.data_list = []
+        self.mask = None
+        self.uid = wx.NewId()
+        self.graph_id = None
+        #Q range for data set
+        self.qmin_data_set = numpy.inf
+        self.qmax_data_set = None
+        self.npts_data_set = 0
+        ## Q range
+        self.qmin = None
+        self.qmax = None
+        self.qmax_x = _QMAX_DEFAULT
+        self.qmin_x = _QMIN_DEFAULT
+        self.npts_x = _NPTS_DEFAULT
+        ## total number of point: float
+        self.npts = None
+        self.num_points = None
+        ## default fitengine type
+        self.engine_type = 'bumps'
+        ## smear default
+        self.current_smearer = None
+        ## 2D smear accuracy default
+        self.smear2d_accuracy = 'Low'
+        ## slit smear:
+        self.dxl = None
+        self.dxw = None
+        ## pinhole smear
+        self.dx_min = None
+        self.dx_max = None
+        ##semar attrbs
+        self.enable_smearer = None
+        self.disable_smearer = None
+        self.pinhole_smearer = None
+        self.slit_smearer = None
+        ##weigth attrbs
+        self.dI_noweight = None
+        self.dI_didata = None
+        self.dI_sqrdata = None
+        self.dI_idata = None
+        ##other attrbs
+        self.dq_l = None
+        self.dq_r = None
+        self.tcChi = None
+        self.disp_box = None
+        self.model_disp = None
+        self.Npts_fit = None
+        self.Npts_total = None
+        self.theory_qmin = None 
+        self.theory_qmax = None
+        self.theory_qmin_x = None
+        self.theory_qmax_x = None
+        self.cb1 = None
+        self.btEditMask = None
+        self.btFit = None
+        self.sld_axes = None
+        self.multi_factor = None
+       
+        self.disp_cb_dict = {}
+   
+        #self.state = PageState(parent=parent)
+        ## dictionary containing list of models
+        self.model_list_box = {}
+       
+        ## Data member to store the dispersion object created
+        self._disp_obj_dict = {}
+        ## selected parameters to apply dispersion
+        self.disp_cb_dict = {}
+        ## smearer object
+        self.enable2D = False
+        self._has_magnetic = False
+        self.magnetic_on = False
+        self.is_mac = ON_MAC
+        self.formfactorbox = None
+        self.structurebox = None
+        self.categorybox = None
+        ##list of model parameters. each item must have same length
+        ## each item related to a given parameters
+        ##[cb state, name, value, "+/-", error of fit, min, max , units]
+        self.parameters = []
+        # non-fittable parameter whose value is astring
+        self.str_parameters = []
+        ## list of parameters to fit , must be like self.parameters
+        self.param_toFit = []
+        ## list of looking like parameters but with non fittable parameters info
+        self.fixed_param = []
+        ## list of looking like parameters but with  fittable parameters info
+        self.fittable_param = []
+        ##list of dispersion parameters
+        self.disp_list = []
+        self.disp_name = ""
+        
+        ## list of orientation parameters
+        self.orientation_params = []
+        self.orientation_params_disp = []
+#       Self.model should ALWAYS be None here.  It was set to none above in 
+#       this long init setting.  no obvious function call in between setting
+#       and this - commenting out on 4/8/2014 by PDB.  Remove once clear 
+#       it is pointless.
+#        if self.model != None:
+#            self.disp_list = self.model.getDispParamList()
+        self.temp_multi_functional = False
+        ##enable model 2D draw
+        self.enable2D = False
+        ## check that the fit range is correct to plot the model again
+        self.fitrange = True
+        ## Create memento to save the current state
+        self.state = PageState(parent=self.parent,
+                               model=self.model, data=self.data)
+        ## flag to determine if state has change
+        self.state_change = False
+        ## save customized array
+        self.values = []
+        self.weights = []
+        ## retrieve saved state
+        self.number_saved_state = 0
+        ## dictionary of saved state
+        self.saved_states = {}
+        ## Create context menu for page
+        self.popUpMenu = wx.Menu()
+    
+        id = wx.NewId()
+        self._keep = wx.MenuItem(self.popUpMenu, id, "Add bookmark",
+                                 " Keep the panel status to recall it later")
+        self.popUpMenu.AppendItem(self._keep)
+        self._keep.Enable(False)
+        self._set_bookmark_flag(False)
+        self._set_save_flag(False)
+        wx.EVT_MENU(self, id, self.on_bookmark)
+        self.popUpMenu.AppendSeparator()
+    
+        ## Default locations
+        self._default_save_location = os.getcwd()
+        ## save initial state on context menu
+        #self.onSave(event=None)
+        self.Bind(wx.EVT_CONTEXT_MENU, self.onContextMenu)
+        
+        # bind key event
+        self.Bind(wx.EVT_LEFT_DOWN, self.on_left_down)
+        
+        ## create the basic structure of the panel with empty sizer
+        self.define_page_structure()
+        ## drawing Initial dispersion parameters sizer
+        self.set_dispers_sizer()
+        
+        ## layout
+        self.set_layout()
+    
+    def set_index_model(self, index):
+        """
+        Index related to this page
+        """
+        self.index_model = index
+        
+    def create_default_data(self):
+        """
+        Given the user selection, creates a 1D or 2D data
+        Only when the page is on theory mode.
+        """
+        if not hasattr(self, "model_view"):
+            return
+        toggle_mode_on = self.model_view.IsEnabled()
+        if toggle_mode_on:
+            if self.enable2D and not check_data_validity(self.data):
+                self._create_default_2d_data()
+            else:
+                if self.pointsbox.GetValue():
+                    self._create_log_1d_data()
+                else:
+                    self._create_default_1d_data()
+                        
+            if self.model != None:
+                if not self.data.is_data:
+                    self._manager.page_finder[self.uid].set_fit_data(data=\
+                                                                [self.data])
+            self.on_smear_helper(update=True)
+            self.state.enable_smearer = self.enable_smearer.GetValue()
+            self.state.disable_smearer = self.disable_smearer.GetValue()
+            self.state.pinhole_smearer = self.pinhole_smearer.GetValue()
+            self.state.slit_smearer = self.slit_smearer.GetValue()
+        
+    def _create_default_1d_data(self):
+        """
+        Create default data for fitting perspective
+        Only when the page is on theory mode.
+        :warning: This data is never plotted.
+        
+        """
+        x = numpy.linspace(start=self.qmin_x, stop=self.qmax_x,
+                           num=self.npts_x, endpoint=True)
+        self.data = Data1D(x=x)
+        self.data.xaxis('\\rm{Q}', "A^{-1}")
+        self.data.yaxis('\\rm{Intensity}', "cm^{-1}")
+        self.data.is_data = False
+        self.data.id = str(self.uid) + " data"
+        self.data.group_id = str(self.uid) + " Model1D"
+         
+    def _create_log_1d_data(self):
+        """
+        Create log-spaced data for fitting perspective
+        Only when the page is on theory mode.
+        :warning: This data is never plotted.
+        
+        """
+        if self.qmin_x >= 1.e-10:
+            qmin = numpy.log10(self.qmin_x)
+        else:
+            qmin = -10.    
+            
+        if self.qmax_x <= 1.e10:
+            qmax = numpy.log10(self.qmax_x)
+        else:
+            qmax = 10. 
+               
+        x = numpy.logspace(start=qmin, stop=qmax,
+                           num=self.npts_x, endpoint=True, base=10.0)
+        self.data = Data1D(x=x)
+        self.data.xaxis('\\rm{Q}', "A^{-1}")
+        self.data.yaxis('\\rm{Intensity}', "cm^{-1}")
+        self.data.is_data = False
+        self.data.id = str(self.uid) + " data"
+        self.data.group_id = str(self.uid) + " Model1D"
+      
+    def _create_default_2d_data(self):
+        """
+        Create 2D data by default
+        Only when the page is on theory mode.
+        :warning: This data is never plotted.
+        """
+        self.data = Data2D()
+        qmax = self.qmax_x / math.sqrt(2)
+        self.data.xaxis('\\rm{Q_{x}}', 'A^{-1}')
+        self.data.yaxis('\\rm{Q_{y}}', 'A^{-1}')
+        self.data.is_data = False
+        self.data.id = str(self.uid) + " data"
+        self.data.group_id = str(self.uid) + " Model2D"
+        ## Default values
+        self.data.detector.append(Detector())
+        index = len(self.data.detector) - 1
+        self.data.detector[index].distance = 8000   # mm
+        self.data.source.wavelength = 6             # A
+        self.data.detector[index].pixel_size.x = 5  # mm
+        self.data.detector[index].pixel_size.y = 5  # mm
+        self.data.detector[index].beam_center.x = qmax
+        self.data.detector[index].beam_center.y = qmax
+        ## create x_bins and y_bins of the model 2D
+        #pixel_width_x = self.data.detector[index].pixel_size.x
+        #pixel_width_y = self.data.detector[index].pixel_size.y
+        #center_x = self.data.detector[index].beam_center.x/pixel_width_x
+        #center_y = self.data.detector[index].beam_center.y/pixel_width_y
+        # theory default: assume the beam
+        #center is located at the center of sqr detector
+        xmax = qmax
+        xmin = -qmax
+        ymax = qmax
+        ymin = -qmax
+        qstep = self.npts_x
+
+        x = numpy.linspace(start=xmin, stop=xmax, num=qstep, endpoint=True)
+        y = numpy.linspace(start=ymin, stop=ymax, num=qstep, endpoint=True)
+        ## use data info instead
+        new_x = numpy.tile(x, (len(y), 1))
+        new_y = numpy.tile(y, (len(x), 1))
+        new_y = new_y.swapaxes(0, 1)
+        # all data reuire now in 1d array
+        qx_data = new_x.flatten()
+        qy_data = new_y.flatten()
+        q_data = numpy.sqrt(qx_data * qx_data + qy_data * qy_data)
+        # set all True (standing for unmasked) as default
+        mask = numpy.ones(len(qx_data), dtype=bool)
+        # calculate the range of qx and qy: this way,
+        # it is a little more independent
+        #x_size = xmax - xmin
+        #y_size = ymax - ymin
+        # store x and y bin centers in q space
+        x_bins = x
+        y_bins = y
+        # bin size: x- & y-directions
+        #xstep = x_size / len(x_bins - 1)
+        #ystep = y_size / len(y_bins - 1)
+  
+        self.data.source = Source()
+        self.data.data = numpy.ones(len(mask))
+        self.data.err_data = numpy.ones(len(mask))
+        self.data.qx_data = qx_data
+        self.data.qy_data = qy_data
+        self.data.q_data = q_data
+        self.data.mask = mask
+        self.data.x_bins = x_bins
+        self.data.y_bins = y_bins
+        # max and min taking account of the bin sizes
+        self.data.xmin = xmin
+        self.data.xmax = xmax
+        self.data.ymin = ymin
+        self.data.ymax = ymax
+
+    def on_set_focus(self, event):
+        """
+        On Set Focus, update guimanger and menu
+        """
+        if self._manager is not None:
+            wx.PostEvent(self._manager.parent, PanelOnFocusEvent(panel=self))
+            self.on_tap_focus()
+               
+    def on_tap_focus(self):
+        """
+        Update menu1 on cliking the page tap
+        """
+        if self._manager.menu1 != None:
+            chain_menu = self._manager.menu1.FindItemById(\
+                                                   self._manager.id_reset_flag)
+            chain_menu.Enable(self.batch_on)
+            sim_menu = self._manager.menu1.FindItemById(self._manager.id_simfit)
+            flag = self.data.is_data\
+                            and (self.model != None)
+            sim_menu.Enable(not self.batch_on and flag)
+            batch_menu = \
+                    self._manager.menu1.FindItemById(self._manager.id_batchfit)
+            batch_menu.Enable(self.batch_on and flag)
+    
+    class ModelTextCtrl(wx.TextCtrl):
+        """
+        Text control for model and fit parameters.
+        Binds the appropriate events for user interactions.
+        Default callback methods can be overwritten on initialization
+        
+        :param kill_focus_callback: callback method for EVT_KILL_FOCUS event
+        :param set_focus_callback:  callback method for EVT_SET_FOCUS event
+        :param mouse_up_callback:   callback method for EVT_LEFT_UP event
+        :param text_enter_callback: callback method for EVT_TEXT_ENTER event
+        
+        """
+        ## Set to True when the mouse is clicked while whole string is selected
+        full_selection = False
+        ## Call back for EVT_SET_FOCUS events
+        _on_set_focus_callback = None
+        
+        def __init__(self, parent, id=-1,
+                     value=wx.EmptyString,
+                     pos=wx.DefaultPosition,
+                     size=wx.DefaultSize,
+                     style=0,
+                     validator=wx.DefaultValidator,
+                     name=wx.TextCtrlNameStr,
+                     kill_focus_callback=None,
+                     set_focus_callback=None,
+                     mouse_up_callback=None,
+                     text_enter_callback=None):
+             
+            wx.TextCtrl.__init__(self, parent, id, value, pos,
+                                  size, style, validator, name)
+            
+            # Bind appropriate events
+            self._on_set_focus_callback = parent.onSetFocus \
+                      if set_focus_callback is None else set_focus_callback
+            self.Bind(wx.EVT_SET_FOCUS, self._on_set_focus)
+            self.Bind(wx.EVT_KILL_FOCUS, self._silent_kill_focus \
+                      if kill_focus_callback is None else kill_focus_callback)
+            self.Bind(wx.EVT_TEXT_ENTER, parent._onparamEnter \
+                      if text_enter_callback is None else text_enter_callback)
+            if not ON_MAC:
+                self.Bind(wx.EVT_LEFT_UP, self._highlight_text \
+                          if mouse_up_callback is None else mouse_up_callback)
+            
+        def _on_set_focus(self, event):
+            """
+            Catch when the text control is set in focus to highlight the whole
+            text if necessary
+            
+            :param event: mouse event
+            
+            """
+            event.Skip()
+            self.full_selection = True
+            return self._on_set_focus_callback(event)
+        
+        def _highlight_text(self, event):
+            """
+            Highlight text of a TextCtrl only of no text has be selected
+            
+            :param event: mouse event
+            
+            """
+            # Make sure the mouse event is available to other listeners
+            event.Skip()
+            control = event.GetEventObject()
+            if self.full_selection:
+                self.full_selection = False
+                # Check that we have a TextCtrl
+                if issubclass(control.__class__, wx.TextCtrl):
+                    # Check whether text has been selected,
+                    # if not, select the whole string
+                    (start, end) = control.GetSelection()
+                    if start == end:
+                        control.SetSelection(-1, -1)
+                        
+        def _silent_kill_focus(self, event):
+            """
+            Save the state of the page
+            """
+            
+            event.Skip()
+            #pass
+    
+    def set_page_info(self, page_info):
+        """
+        set some page important information at once
+        """
+#       THIS METHOD/FUNCTION NO LONGE APPEARS TO BE CALLED.  Started up program 
+#       and started new fit window and PR and Invariant and a fit in fitting
+#       but never entered this routine which should be an initialization
+#       routine.  Leave for a while but probably something to clean up at 
+#       some point?
+#
+#       PDB April 13 2014
+#
+        ##window_name
+        self.window_name = page_info.window_name
+        ##window_caption
+        self.window_caption = page_info.window_caption
+        ## manager is the fitting plugin
+        self._manager = page_info.manager
+        ## owner of the page (fitting plugin)
+        self.event_owner = page_info.event_owner
+        ## current model
+        self.model = page_info.model
+        ## data
+        self.data = page_info.data
+        ## dictionary containing list of models
+        self.model_list_box = page_info.model_list_box
+        ## Data member to store the dispersion object created
+        self.populate_box(model_dict=self.model_list_box)
+        
+    def onContextMenu(self, event):
+        """
+        Retrieve the state selected state
+        """
+        # Skipping the save state functionality for release 0.9.0
+        #return
+    
+        pos = event.GetPosition()
+        pos = self.ScreenToClient(pos)
+       
+        self.PopupMenu(self.popUpMenu, pos)
+      
+    def onUndo(self, event):
+        """
+        Cancel the previous action
+        """
+        event = PreviousStateEvent(page=self)
+        wx.PostEvent(self.parent, event)
+        
+    def onRedo(self, event):
+        """
+        Restore the previous action cancelled
+        """
+        event = NextStateEvent(page=self)
+        wx.PostEvent(self.parent, event)
+    
+    def define_page_structure(self):
+        """
+        Create empty sizer for a panel
+        """
+        self.vbox = wx.BoxSizer(wx.VERTICAL)
+        self.sizer0 = wx.BoxSizer(wx.VERTICAL)
+        self.sizer1 = wx.BoxSizer(wx.VERTICAL)
+        self.sizer2 = wx.BoxSizer(wx.VERTICAL)
+        self.sizer3 = wx.BoxSizer(wx.VERTICAL)
+        self.sizer4 = wx.BoxSizer(wx.VERTICAL)
+        self.sizer5 = wx.BoxSizer(wx.VERTICAL)
+        self.sizer6 = wx.BoxSizer(wx.VERTICAL)
+        
+        self.sizer0.SetMinSize((PANEL_WIDTH, -1))
+        self.sizer1.SetMinSize((PANEL_WIDTH, -1))
+        self.sizer2.SetMinSize((PANEL_WIDTH, -1))
+        self.sizer3.SetMinSize((PANEL_WIDTH, -1))
+        self.sizer4.SetMinSize((PANEL_WIDTH, -1))
+        self.sizer5.SetMinSize((PANEL_WIDTH, -1))
+        self.sizer6.SetMinSize((PANEL_WIDTH, -1))
+        
+        self.vbox.Add(self.sizer0)
+        self.vbox.Add(self.sizer1)
+        self.vbox.Add(self.sizer2)
+        self.vbox.Add(self.sizer3)
+        self.vbox.Add(self.sizer4)
+        self.vbox.Add(self.sizer5)
+        self.vbox.Add(self.sizer6)
+        
+    def set_layout(self):
+        """
+        layout
+        """
+        self.vbox.Layout()
+        self.vbox.Fit(self)
+        self.SetSizer(self.vbox)
+        self.Centre()
+ 
+    def set_owner(self, owner):
+        """
+        set owner of fitpage
+        
+        :param owner: the class responsible of plotting
+        
+        """
+        self.event_owner = owner
+        self.state.event_owner = owner
+        
+    def get_state(self):
+        """
+        """
+        return self.state
+    
+    def get_data(self):
+        """
+        return the current data
+        """
+        return self.data
+    
+    def get_data_list(self):
+        """
+        return the current data
+        """
+        return self.data_list
+    
+    def set_manager(self, manager):
+        """
+        set panel manager
+        
+        :param manager: instance of plugin fitting
+        
+        """
+        self._manager = manager
+        self.state.manager = manager
+        
+    def populate_box(self, model_dict):
+        """
+        Store list of model
+        
+        :param model_dict: dictionary containing list of models
+        
+        """
+        self.model_list_box = model_dict
+        self.state.model_list_box = self.model_list_box
+        self.initialize_combox()
+        
+    def set_model_dictionary(self, model_dict):
+        """
+        Store a dictionary linking model name -> model object
+
+        :param model_dict: dictionary containing list of models
+        """
+        self.model_dict = model_dict
+
+    def initialize_combox(self):
+        """
+        put default value in the combobox
+        """  
+        ## fill combox box
+        if self.model_list_box is None:
+            return
+        if len(self.model_list_box) > 0:
+        ## This is obsolete code since form factor box is no longer static.
+        ## It is now set dynamically through _show_combox and _show_combos_helper
+        ## These are called for first time by formfactor_combo_init
+        ## itself called from fitpanel only.  If we find that I'm wrong and
+        ## we DO need to initialize somehow here - do it by a call to 
+        ## formfactor_combo_init 
+        ## self.formfator_combo_init()
+        ## BUT NOT HERE -- make it last line of this 
+        ## method so that structure box is populated before _show_comboox_helper
+        ## is called.  Otherwise wx will complain mightily:-)
+        ##
+        ## Also change the name to initiatlize_structurebox along with changes
+        ## to other combobox methods (_populate_listbox --> _populate_categorybox
+        ## etc )
+        ##
+        ##     PDB 4/26/2014
+#            self._populate_box(self.formfactorbox,
+#                               self.model_list_box["Shapes"])
+            self._populate_box(self.structurebox,
+                                self.model_list_box["Structure Factors"])
+            self.structurebox.Insert("None", 0, None)
+            self.structurebox.SetSelection(0)
+            self.structurebox.Hide()
+            self.text2.Hide()
+            self.structurebox.Disable()
+            self.text2.Disable()
+             
+            if self.model.__class__ in self.model_list_box["P(Q)*S(Q)"]:
+                self.structurebox.Show()
+                self.text2.Show()
+                self.structurebox.Enable()
+                self.text2.Enable()
+
+                
+    def set_dispers_sizer(self):
+        """
+        fill sizer containing dispersity info
+        """
+        self.sizer4.Clear(True)
+        name = "Polydispersity and Orientational Distribution"
+        box_description = wx.StaticBox(self, -1, name)
+        box_description.SetForegroundColour(wx.BLUE)
+        boxsizer1 = wx.StaticBoxSizer(box_description, wx.VERTICAL)
+        #----------------------------------------------------
+        self.disable_disp = wx.RadioButton(self, -1, 'Off', (10, 10),
+                                            style=wx.RB_GROUP)
+        self.enable_disp = wx.RadioButton(self, -1, 'On', (10, 30))
+        # best size for MAC and PC
+        if ON_MAC:
+            size_q = (30, 20)
+        else:
+            size_q = (20, 15)
+        self.disp_help_bt = wx.Button(self, wx.NewId(), '?',
+                                      style=wx.BU_EXACTFIT,
+                                      size=size_q)
+        self.disp_help_bt.Bind(wx.EVT_BUTTON,
+                        self.on_pd_help_clicked, id=self.disp_help_bt.GetId())
+        self.disp_help_bt.SetToolTipString("Helps for Polydispersion.")
+        
+        self.Bind(wx.EVT_RADIOBUTTON, self._set_dipers_Param,
+                     id=self.disable_disp.GetId())
+        self.Bind(wx.EVT_RADIOBUTTON, self._set_dipers_Param,
+                   id=self.enable_disp.GetId())
+        #MAC needs SetValue
+        self.disable_disp.SetValue(True)
+        sizer_dispersion = wx.BoxSizer(wx.HORIZONTAL)
+        sizer_dispersion.Add((20, 20))
+        name = ""  # Polydispersity and \nOrientational Distribution "
+        sizer_dispersion.Add(wx.StaticText(self, -1, name))
+        sizer_dispersion.Add(self.enable_disp)
+        sizer_dispersion.Add((20, 20))
+        sizer_dispersion.Add(self.disable_disp)
+        sizer_dispersion.Add((25, 20))
+        sizer_dispersion.Add(self.disp_help_bt)
+        
+        ## fill a sizer for dispersion
+        boxsizer1.Add(sizer_dispersion, 0,
+                wx.TOP | wx.BOTTOM | wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE,
+                border=5)
+        self.sizer4_4 = wx.GridBagSizer(6, 5)
+
+        boxsizer1.Add(self.sizer4_4)
+        #-----------------------------------------------------
+        self.sizer4.Add(boxsizer1, 0, wx.EXPAND | wx.ALL, 10)
+        self.sizer4_4.Layout()
+        self.sizer4.Layout()
+        self.Layout()
+     
+        self.Refresh()
+        ## saving the state of enable dispersity button
+        self.state.enable_disp = self.enable_disp.GetValue()
+        self.state.disable_disp = self.disable_disp.GetValue()
+        self.SetupScrolling()
+    
+    def onResetModel(self, event):
+        """
+        Reset model state
+        """
+        menu = event.GetEventObject()
+        ## post help message for the selected model
+        msg = menu.GetHelpString(event.GetId())
+        msg += " reloaded"
+        wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
+        self.Show(False)
+        name = menu.GetLabel(event.GetId())
+        self._on_select_model_helper()
+        if self.model != None:
+            self.m_name = self.model.name
+        if name in self.saved_states.keys():
+            previous_state = self.saved_states[name]
+            ## reset state of checkbox,textcrtl  and  regular parameters value
+            
+            self.reset_page(previous_state)
+        self.state.m_name = self.m_name
+        self.Show(True)
+               
+    def on_preview(self, event):
+        """
+        Report the current fit results
+        """
+        # Get plot image from plotpanel
+        images, canvases = self.get_images()
+        # get the report dialog
+        self.state.report(images, canvases)
+        
+    def on_save(self, event):
+        """
+        Save the current state into file
+        """
+        self.save_current_state()
+        new_state = self.state.clone()
+        # Ask the user the location of the file to write to.
+        path = None
+        if self.parent != None:
+            self._default_save_location = \
+                        self._manager.parent._default_save_location
+        dlg = wx.FileDialog(self, "Choose a file", self._default_save_location,
+                                        self.window_caption, "*.fitv", wx.SAVE)
+
+        if dlg.ShowModal() == wx.ID_OK:
+            path = dlg.GetPath()
+            self._default_save_location = os.path.dirname(path)
+            self._manager.parent._default_save_location =\
+                                 self._default_save_location
+        else:
+            return None
+        # MAC always needs the extension for saving
+        extens = ".fitv"
+        # Make sure the ext included in the file name
+        fName = os.path.splitext(path)[0] + extens
+        #the manager write the state into file
+        self._manager.save_fit_state(filepath=fName, fitstate=new_state)
+        return new_state
+    
+    def on_copy(self, event):
+        """
+        Copy Parameter values to the clipboad
+        """
+        if event != None:
+            event.Skip()
+        # It seems MAC needs wxCallAfter
+        if event.GetId() == GUIFRAME_ID.COPYEX_ID:
+            print "copy excel"
+            wx.CallAfter(self.get_copy_excel)
+        elif event.GetId() == GUIFRAME_ID.COPYLAT_ID:
+            print "copy latex"
+            wx.CallAfter(self.get_copy_latex)
+        else:
+            wx.CallAfter(self.get_copy)
+
+        
+    def on_paste(self, event):
+        """
+        Paste Parameter values to the panel if possible
+        """
+        #if event != None:
+        #    event.Skip()
+        # It seems MAC needs wxCallAfter for the setvalues
+        # for multiple textctrl items, otherwise it tends to crash once a while
+        wx.CallAfter(self.get_paste)
+        # messages depending on the flag
+        #self._copy_info(True)
+        
+    def _copy_info(self, flag):
+        """
+        Send event dpemding on flag
+        
+        : Param flag: flag that distinguish event
+        """
+        # messages depending on the flag
+        if flag == None:
+            msg = " Parameter values are copied to the clipboard..."
+            infor = 'warning'
+        elif flag:
+            msg = " Parameter values are pasted from the clipboad..."
+            infor = "warning"
+        else:
+            msg = "Error occured: "
+            msg += "No valid parameter values to paste from the clipboard..."
+            infor = "error"
+            wx.PostEvent(self._manager.parent,
+                    StatusEvent(status=msg, info=infor))
+        # inform msg to wx
+        wx.PostEvent(self._manager.parent,
+                    StatusEvent(status=msg, info=infor))
+        
+    def _get_time_stamp(self):
+        """
+        return time and date stings
+        """
+        # date and time
+        year, month, day, hour, minute, second, _, _, _ = time.localtime()
+        current_time = str(hour) + ":" + str(minute) + ":" + str(second)
+        current_date = str(month) + "/" + str(day) + "/" + str(year)
+        return current_time, current_date
+      
+    def on_bookmark(self, event):
+        """
+        save history of the data and model
+        """
+        if self.model == None:
+            msg = "Can not bookmark; Please select Data and Model first..."
+            wx.MessageBox(msg, 'Info')
+            return
+        self.save_current_state()
+        new_state = self.state.clone()
+        ##Add model state on context menu
+        self.number_saved_state += 1
+        current_time, current_date = self._get_time_stamp()
+        #name= self.model.name+"[%g]"%self.number_saved_state
+        name = "Fitting: %g]" % self.number_saved_state
+        name += self.model.__class__.__name__
+        name += "bookmarked at %s on %s" % (current_time, current_date)
+        self.saved_states[name] = new_state
+        
+        ## Add item in the context menu
+        msg = "Model saved at %s on %s" % (current_time, current_date)
+        ## post help message for the selected model
+        msg += " Saved! right click on this page to retrieve this model"
+        wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
+        
+        id = wx.NewId()
+        self.popUpMenu.Append(id, name, str(msg))
+        wx.EVT_MENU(self, id, self.onResetModel)
+        wx.PostEvent(self._manager.parent,
+                     AppendBookmarkEvent(title=name,
+                                         hint=str(msg),
+                                         handler=self._back_to_bookmark))
+    
+    def _back_to_bookmark(self, event):
+        """
+        Back to bookmark
+        """
+        self._manager.on_perspective(event)
+        self.onResetModel(event)
+        self._draw_model()
+                
+    def onSetFocus(self, evt):
+        """
+        highlight the current textcrtl and hide the error text control shown
+        after fitting
+        """
+        return
+    
+    def read_file(self, path):
+        """
+        Read two columns file
+        
+        :param path: the path to the file to read
+        
+        """
+        try:
+            if path == None:
+                wx.PostEvent(self._manager.parent,
+                            StatusEvent(status= \
+                            " Selected Distribution was not loaded: %s" % path))
+                return None, None
+            input_f = open(path, 'r')
+            buff = input_f.read()
+            lines = buff.split('\n')
+            input_f.close()
+            angles = []
+            weights = []
+            for line in lines:
+                toks = line.split()
+                try:
+                    angle = float(toks[0])
+                    weight = float(toks[1])
+                    angles.append(angle)
+                    weights.append(weight)
+                except:
+                    # Skip non-data lines
+                    pass
+            return numpy.array(angles), numpy.array(weights)
+        except:
+            raise
+
+    def createMemento(self):
+        """
+        return the current state of the page
+        """
+        return self.state.clone()
+    
+    def save_current_state(self):
+        """
+        Store current state
+        """
+        self.state.engine_type = copy.deepcopy(self.engine_type)
+        ## save model option
+        if self.model != None:
+            self.disp_list = self.model.getDispParamList()
+            self.state.disp_list = copy.deepcopy(self.disp_list)
+            self.state.model = self.model.clone()
+            
+            #model combobox: complex code because of mac's silent error
+            if self.structurebox != None:
+                if self.structurebox.IsShown():
+                    self.state.structurecombobox = 'None'
+                    s_select = self.structurebox.GetSelection()
+                    if s_select > 0:
+                        self.state.structurecombobox = self.structurebox.\
+                        GetString(s_select)
+            if self.formfactorbox != None:
+                f_select = self.formfactorbox.GetSelection()
+                if f_select > 0:
+                    self.state.formfactorcombobox = self.formfactorbox.\
+                    GetString(f_select)
+        if self.categorybox != None:
+            cb_select = self.categorybox.GetSelection()
+            if cb_select > 0:
+                self.state.categorycombobox = self.categorybox.\
+                GetString(cb_select)
+        
+        self.state.enable2D = copy.deepcopy(self.enable2D)
+        self.state.values = copy.deepcopy(self.values)
+        self.state.weights = copy.deepcopy(self.weights)
+        ## save data
+        self.state.data = copy.deepcopy(self.data)
+        self.state.qmax_x = self.qmax_x
+        self.state.qmin_x = self.qmin_x
+        self.state.dI_noweight = copy.deepcopy(self.dI_noweight.GetValue())
+        self.state.dI_didata = copy.deepcopy(self.dI_didata.GetValue())
+        self.state.dI_sqrdata = copy.deepcopy(self.dI_sqrdata.GetValue())
+        self.state.dI_idata = copy.deepcopy(self.dI_idata.GetValue())
+        self.state.dq_l = self.dq_l
+        self.state.dq_r = self.dq_r
+        if hasattr(self, "enable_disp"):
+            self.state.enable_disp = self.enable_disp.GetValue()
+            self.state.disable_disp = self.disable_disp.GetValue()
+            
+        self.state.smearer = copy.deepcopy(self.current_smearer)
+        if hasattr(self, "enable_smearer"):
+            self.state.enable_smearer = \
+                                copy.deepcopy(self.enable_smearer.GetValue())
+            self.state.disable_smearer = \
+                                copy.deepcopy(self.disable_smearer.GetValue())
+
+        self.state.pinhole_smearer = \
+                                copy.deepcopy(self.pinhole_smearer.GetValue())
+        self.state.dx_max = copy.deepcopy(self.dx_max)
+        self.state.dx_min = copy.deepcopy(self.dx_min)
+        self.state.dxl = copy.deepcopy(self.dxl)
+        self.state.dxw = copy.deepcopy(self.dxw)
+        self.state.slit_smearer = copy.deepcopy(self.slit_smearer.GetValue())
+                  
+        if len(self._disp_obj_dict) > 0:
+            for k, v in self._disp_obj_dict.iteritems():
+                self.state._disp_obj_dict[k] = v
+
+            self.state.values = copy.deepcopy(self.values)
+            self.state.weights = copy.deepcopy(self.weights)
+        ## save plotting range
+        self._save_plotting_range()
+        
+        self.state.orientation_params = []
+        self.state.orientation_params_disp = []
+        self.state.parameters = []
+        self.state.fittable_param = []
+        self.state.fixed_param = []
+        self.state.str_parameters = []
+
+        ## save checkbutton state and txtcrtl values
+        self._copy_parameters_state(self.str_parameters,
+                                    self.state.str_parameters)
+        self._copy_parameters_state(self.orientation_params,
+                                     self.state.orientation_params)
+        self._copy_parameters_state(self.orientation_params_disp,
+                                     self.state.orientation_params_disp)
+        
+        self._copy_parameters_state(self.parameters, self.state.parameters)
+        self._copy_parameters_state(self.fittable_param,
+                                     self.state.fittable_param)
+        self._copy_parameters_state(self.fixed_param, self.state.fixed_param)
+        #save chisqr
+        self.state.tcChi = self.tcChi.GetValue()
+        
+    def save_current_state_fit(self):
+        """
+        Store current state for fit_page
+        """
+        ## save model option
+        if self.model != None:
+            self.disp_list = self.model.getDispParamList()
+            self.state.disp_list = copy.deepcopy(self.disp_list)
+            self.state.model = self.model.clone()
+        if hasattr(self, "engine_type"):
+            self.state.engine_type = copy.deepcopy(self.engine_type)
+            
+        self.state.enable2D = copy.deepcopy(self.enable2D)
+        self.state.values = copy.deepcopy(self.values)
+        self.state.weights = copy.deepcopy(self.weights)
+        ## save data
+        self.state.data = copy.deepcopy(self.data)
+        
+        if hasattr(self, "enable_disp"):
+            self.state.enable_disp = self.enable_disp.GetValue()
+            self.state.disable_disp = self.disable_disp.GetValue()
+            
+        self.state.smearer = copy.deepcopy(self.current_smearer)
+        if hasattr(self, "enable_smearer"):
+            self.state.enable_smearer = \
+                                copy.deepcopy(self.enable_smearer.GetValue())
+            self.state.disable_smearer = \
+                                copy.deepcopy(self.disable_smearer.GetValue())
+            
+        self.state.pinhole_smearer = \
+                                copy.deepcopy(self.pinhole_smearer.GetValue())
+        self.state.slit_smearer = copy.deepcopy(self.slit_smearer.GetValue())
+        self.state.dI_noweight = copy.deepcopy(self.dI_noweight.GetValue())
+        self.state.dI_didata = copy.deepcopy(self.dI_didata.GetValue())
+        self.state.dI_sqrdata = copy.deepcopy(self.dI_sqrdata.GetValue())
+        self.state.dI_idata = copy.deepcopy(self.dI_idata.GetValue())
+        if hasattr(self, "disp_box") and self.disp_box != None:
+            self.state.disp_box = self.disp_box.GetCurrentSelection()
+
+            if len(self.disp_cb_dict) > 0:
+                for k, v in self.disp_cb_dict.iteritems():
+                    if v == None:
+                        self.state.disp_cb_dict[k] = v
+                    else:
+                        try:
+                            self.state.disp_cb_dict[k] = v.GetValue()
+                        except:
+                            self.state.disp_cb_dict[k] = None
+            if len(self._disp_obj_dict) > 0:
+                for k, v in self._disp_obj_dict.iteritems():
+                    self.state._disp_obj_dict[k] = v
+                        
+            self.state.values = copy.deepcopy(self.values)
+            self.state.weights = copy.deepcopy(self.weights)
+           
+        ## save plotting range
+        self._save_plotting_range()
+        
+        ## save checkbutton state and txtcrtl values
+        self._copy_parameters_state(self.orientation_params,
+                                     self.state.orientation_params)
+        self._copy_parameters_state(self.orientation_params_disp,
+                                     self.state.orientation_params_disp)
+        self._copy_parameters_state(self.parameters, self.state.parameters)
+        self._copy_parameters_state(self.fittable_param,
+                                             self.state.fittable_param)
+        self._copy_parameters_state(self.fixed_param, self.state.fixed_param)
+    
+    def check_invalid_panel(self):
+        """
+        check if the user can already perform some action with this panel
+        """
+        if self.data is None:
+            self.disable_smearer.SetValue(True)
+            self.disable_disp.SetValue(True)
+            msg = "Please load Data and select Model to start..."
+            wx.MessageBox(msg, 'Info')
+            return  True
+        
+    def set_model_state(self, state):
+        """
+        reset page given a model state
+        """
+        self.disp_cb_dict = state.disp_cb_dict
+        self.disp_list = state.disp_list
+      
+        ## set the state of the radio box
+        #self.shape_rbutton.SetValue(state.shape_rbutton)
+        #self.shape_indep_rbutton.SetValue(state.shape_indep_rbutton)
+        #self.struct_rbutton.SetValue(state.struct_rbutton)
+        #self.plugin_rbutton.SetValue(state.plugin_rbutton)
+        
+        ## fill model combobox
+        self._show_combox_helper()
+        #select the current model
+        try:
+            # to support older version
+            category_pos = int(state.categorycombobox)
+        except:
+            category_pos = 0
+            for ind_cat in range(self.categorybox.GetCount()):
+                if self.categorycombobox.GetString(ind_form) == \
+                                        state.categorycombobox:
+                    category_pos = int(ind_cat)
+                    break
+            
+        self.categorybox.Select(category_pos)
+        try:
+            # to support older version
+            formfactor_pos = int(state.formfactorcombobox)
+        except:
+            formfactor_pos = 0
+            for ind_form in range(self.formfactorbox.GetCount()):
+                if self.formfactorbox.GetString(ind_form) == \
+                                        state.formfactorcombobox:
+                    formfactor_pos = int(ind_form)
+                    break
+            
+        self.formfactorbox.Select(formfactor_pos)
+        
+        try:
+            # to support older version
+            structfactor_pos = int(state.structurecombobox)
+        except:
+            structfactor_pos = 0
+            for ind_struct in range(self.structurebox.GetCount()):
+                if self.structurebox.GetString(ind_struct) == \
+                                        state.structurecombobox:
+                    structfactor_pos = int(ind_struct)
+                    break
+                
+        self.structurebox.SetSelection(structfactor_pos)
+        
+        if state.multi_factor != None:
+            self.multifactorbox.SetSelection(state.multi_factor)
+            
+        ## reset state of checkbox,textcrtl  and  regular parameters value
+        self._reset_parameters_state(self.orientation_params_disp,
+                                     state.orientation_params_disp)
+        self._reset_parameters_state(self.orientation_params,
+                                     state.orientation_params)
+        self._reset_parameters_state(self.str_parameters,
+                                     state.str_parameters)
+        self._reset_parameters_state(self.parameters, state.parameters)
+        ## display dispersion info layer
+        self.enable_disp.SetValue(state.enable_disp)
+        self.disable_disp.SetValue(state.disable_disp)
+        
+        if hasattr(self, "disp_box") and self.disp_box != None:
+            self.disp_box.SetSelection(state.disp_box)
+            n = self.disp_box.GetCurrentSelection()
+            dispersity = self.disp_box.GetClientData(n)
+            name = dispersity.__name__
+
+            self._set_dipers_Param(event=None)
+       
+            if name == "ArrayDispersion":
+                
+                for item in self.disp_cb_dict.keys():
+                    
+                    if hasattr(self.disp_cb_dict[item], "SetValue"):
+                        self.disp_cb_dict[item].SetValue(\
+                                                    state.disp_cb_dict[item])
+                        # Create the dispersion objects
+                        from sas.models.dispersion_models import ArrayDispersion
+                        disp_model = ArrayDispersion()
+                        if hasattr(state, "values") and \
+                                 self.disp_cb_dict[item].GetValue() == True:
+                            if len(state.values) > 0:
+                                self.values = state.values
+                                self.weights = state.weights
+                                disp_model.set_weights(self.values,
+                                                       state.weights)
+                            else:
+                                self._reset_dispersity()
+                        
+                        self._disp_obj_dict[item] = disp_model
+                        # Set the new model as the dispersion object
+                        #for the selected parameter
+                        self.model.set_dispersion(item, disp_model)
+                    
+                        self.model._persistency_dict[item] = \
+                                                [state.values, state.weights]
+                    
+            else:
+                keys = self.model.getParamList()
+                for item in keys:
+                    if item in self.disp_list and \
+                        not item in self.model.details:
+                        self.model.details[item] = ["", None, None]
+                self.disp_cb_dict = copy.deepcopy(state.disp_cb_dict)
+                self.state.disp_cb_dict = copy.deepcopy(state.disp_cb_dict)
+        ## smearing info  restore
+        if hasattr(self, "enable_smearer"):
+            ## set smearing value whether or not the data
+            #contain the smearing info
+            self.enable_smearer.SetValue(state.enable_smearer)
+            self.disable_smearer.SetValue(state.disable_smearer)
+            self.onSmear(event=None)
+        self.pinhole_smearer.SetValue(state.pinhole_smearer)
+        self.slit_smearer.SetValue(state.slit_smearer)
+        
+        self.dI_noweight.SetValue(state.dI_noweight)
+        self.dI_didata.SetValue(state.dI_didata)
+        self.dI_sqrdata.SetValue(state.dI_sqrdata)
+        self.dI_idata.SetValue(state.dI_idata)
+        
+        ## we have two more options for smearing
+        if self.pinhole_smearer.GetValue():
+            self.onPinholeSmear(event=None)
+        elif self.slit_smearer.GetValue():
+            self.onSlitSmear(event=None)
+       
+        ## reset state of checkbox,textcrtl  and dispersity parameters value
+        self._reset_parameters_state(self.fittable_param, state.fittable_param)
+        self._reset_parameters_state(self.fixed_param, state.fixed_param)
+        
+        ## draw the model with previous parameters value
+        self._onparamEnter_helper()
+        self.select_param(event=None)
+        #Save state_fit
+        self.save_current_state_fit()
+        self._lay_out()
+        self.Refresh()
+        
+    def reset_page_helper(self, state):
+        """
+        Use page_state and change the state of existing page
+        
+        :precondition: the page is already drawn or created
+        
+        :postcondition: the state of the underlying data change as well as the
+            state of the graphic interface
+        """
+        if state == None:
+            return
+        # set data, etc. from the state
+        # reset page between theory and fitting from bookmarking
+        #if state.data == None:
+        #    data = None
+        #else:
+        data = state.data
+
+        if data == None:
+            data_min = state.qmin
+            data_max = state.qmax
+            self.qmin_x = data_min
+            self.qmax_x = data_max
+            self.qmin.SetValue(str(data_min))
+            self.qmax.SetValue(str(data_max))
+
+            self.state.data = data
+            self.state.qmin = self.qmin_x
+            self.state.qmax = self.qmax_x
+        else:
+            self.set_data(data)
+            
+        self.enable2D = state.enable2D
+        try:
+            self.magnetic_on = state.magnetic_on
+        except:
+            # Backward compatibility (for older state files)
+            self.magnetic_on = False
+        self.engine_type = state.engine_type
+
+        self.disp_cb_dict = state.disp_cb_dict
+        self.disp_list = state.disp_list
+      
+        ## set the state of the radio box
+        #self.shape_rbutton.SetValue(state.shape_rbutton)
+        #self.shape_indep_rbutton.SetValue(state.shape_indep_rbutton)
+        #self.struct_rbutton.SetValue(state.struct_rbutton)
+        #self.plugin_rbutton.SetValue(state.plugin_rbutton)
+        
+        ## fill model combobox
+        self._show_combox_helper()
+        #select the current model
+        try:
+            # to support older version
+            category_pos = int(state.categorycombobox)
+        except:
+            category_pos = 0
+            for ind_cat in range(self.categorybox.GetCount()):
+                if self.categorybox.GetString(ind_cat) == \
+                                        state.categorycombobox:
+                    category_pos = int(ind_cat)
+                    break
+            
+        self.categorybox.Select(category_pos)
+        self._show_combox(None)
+        try:
+            # to support older version
+            formfactor_pos = int(state.formfactorcombobox)
+        except:
+            formfactor_pos = 0
+            for ind_form in range(self.formfactorbox.GetCount()):
+                if self.formfactorbox.GetString(ind_form) == \
+                                                    (state.formfactorcombobox):
+                    formfactor_pos = int(ind_form)
+                    break
+            
+        self.formfactorbox.Select(formfactor_pos)
+        
+        try:
+            # to support older version
+            structfactor_pos = int(state.structurecombobox)
+        except:
+            structfactor_pos = 0
+            for ind_struct in range(self.structurebox.GetCount()):
+                if self.structurebox.GetString(ind_struct) == \
+                                                    (state.structurecombobox):
+                    structfactor_pos = int(ind_struct)
+                    break
+            
+        self.structurebox.SetSelection(structfactor_pos)
+
+        if state.multi_factor != None:
+            self.multifactorbox.SetSelection(state.multi_factor)
+
+        #reset the fitting engine type
+        self.engine_type = state.engine_type
+        #draw the pnael according to the new model parameter
+        self._on_select_model(event=None)
+            
+        # take care of 2D button
+        if data == None and self.model_view.IsEnabled():
+            if self.enable2D:
+                self.model_view.SetLabel("2D Mode")
+            else:
+                self.model_view.SetLabel("1D Mode")
+        # else:
+                
+        if self._manager != None and self.engine_type != None:
+            self._manager._on_change_engine(engine=self.engine_type)
+        ## set the select all check box to the a given state
+        self.cb1.SetValue(state.cb1)
+     
+        ## reset state of checkbox,textcrtl  and  regular parameters value
+        self._reset_parameters_state(self.orientation_params_disp,
+                                     state.orientation_params_disp)
+        self._reset_parameters_state(self.orientation_params,
+                                     state.orientation_params)
+        self._reset_parameters_state(self.str_parameters,
+                                     state.str_parameters)
+        self._reset_parameters_state(self.parameters, state.parameters)
+        ## display dispersion info layer
+        self.enable_disp.SetValue(state.enable_disp)
+        self.disable_disp.SetValue(state.disable_disp)
+        # If the polydispersion is ON
+        if state.enable_disp:
+            # reset dispersion according the state
+            self._set_dipers_Param(event=None)
+            self._reset_page_disp_helper(state)
+        ##plotting range restore
+        self._reset_plotting_range(state)
+        ## smearing info  restore
+        if hasattr(self, "enable_smearer"):
+            ## set smearing value whether or not the data
+            #contain the smearing info
+            self.enable_smearer.SetValue(state.enable_smearer)
+            self.disable_smearer.SetValue(state.disable_smearer)
+            self.onSmear(event=None)
+        self.pinhole_smearer.SetValue(state.pinhole_smearer)
+        self.slit_smearer.SetValue(state.slit_smearer)
+        try:
+            self.dI_noweight.SetValue(state.dI_noweight)
+            self.dI_didata.SetValue(state.dI_didata)
+            self.dI_sqrdata.SetValue(state.dI_sqrdata)
+            self.dI_idata.SetValue(state.dI_idata)
+        except:
+            # to support older state file formats
+            self.dI_noweight.SetValue(False)
+            self.dI_didata.SetValue(True)
+            self.dI_sqrdata.SetValue(False)
+            self.dI_idata.SetValue(False)
+ 
+        ## we have two more options for smearing
+        if self.pinhole_smearer.GetValue():
+            self.dx_min = state.dx_min
+            self.dx_max = state.dx_max
+            if self.dx_min != None:
+                self.smear_pinhole_min.SetValue(str(self.dx_min))
+            if self.dx_max != None:
+                self.smear_pinhole_max.SetValue(str(self.dx_max))
+            self.onPinholeSmear(event=None)
+        elif self.slit_smearer.GetValue():
+            self.dxl = state.dxl
+            self.dxw = state.dxw
+            if self.dxl != None:
+                self.smear_slit_height.SetValue(str(self.dxl))
+            if self.dxw != None:
+                self.smear_slit_width.SetValue(str(self.dxw))  
+            else:
+                self.smear_slit_width.SetValue('')  
+            self.onSlitSmear(event=None)
+        
+        ## reset state of checkbox,textcrtl  and dispersity parameters value
+        self._reset_parameters_state(self.fittable_param, state.fittable_param)
+        self._reset_parameters_state(self.fixed_param, state.fixed_param)
+        
+        ## draw the model with previous parameters value
+        self._onparamEnter_helper()
+        #reset the value of chisqr when not consistent with the value computed
+        self.tcChi.SetValue(str(self.state.tcChi))
+        ## reset context menu items
+        self._reset_context_menu()
+        
+        ## set the value of the current state to the state given as parameter
+        self.state = state.clone()
+        self.state.m_name = self.m_name
+    
+    def _reset_page_disp_helper(self, state):
+        """
+        Help to rest page for dispersions
+        """
+        keys = self.model.getParamList()
+        for item in keys:
+            if item in self.disp_list and \
+                not item in self.model.details:
+                self.model.details[item] = ["", None, None]
+        #for k,v in self.state.disp_cb_dict.iteritems():
+        self.disp_cb_dict = copy.deepcopy(state.disp_cb_dict)
+        self.state.disp_cb_dict = copy.deepcopy(state.disp_cb_dict)
+        self.values = copy.deepcopy(state.values)
+        self.weights = copy.deepcopy(state.weights)
+        
+        for key, disp in state._disp_obj_dict.iteritems():
+            # From saved file, disp_model can not be sent in model obj.
+            # it will be sent as a string here, then converted to model object.
+            if disp.__class__.__name__ == 'str':
+                disp_model = None
+                com_str = "from sas.models.dispersion_models "
+                com_str += "import %s as disp_func \ndisp_model = disp_func()"
+                exec com_str % disp
+            else:
+                disp_model = disp
+            self._disp_obj_dict[key] = disp_model
+            param_name = key.split('.')[0]
+            # Try to set dispersion only when available
+            # for eg., pass the orient. angles for 1D Cal
+            try:
+                self.model.set_dispersion(param_name, disp_model)
+                self.model._persistency_dict[key] = \
+                                 [state.values, state.weights]
+            except:
+                pass
+            selection = self._find_polyfunc_selection(disp_model)
+            for list in self.fittable_param:
+                if list[1] == key and list[7] != None:
+                    list[7].SetSelection(selection)
+                    # For the array disp_model, set the values and weights
+                    if selection == 1:
+                        disp_model.set_weights(self.values[key],
+                                               self.weights[key])
+                        try:
+                            # Diables all fittable params for array
+                            list[0].SetValue(False)
+                            list[0].Disable()
+                            list[2].Disable()
+                            list[5].Disable()
+                            list[6].Disable()
+                        except:
+                            pass
+            # For array, disable all fixed params
+            if selection == 1:
+                for item in self.fixed_param:
+                    if item[1].split(".")[0] == key.split(".")[0]:
+                        # try it and pass it for the orientation for 1D
+                        try:
+                            item[2].Disable()
+                        except:
+                            pass
+    
+        # Make sure the check box updated when all checked
+        if self.cb1.GetValue():
+            self.select_all_param(None)
+      
+    def _selectDlg(self):
+        """
+        open a dialog file to selected the customized dispersity 
+        """
+        if self.parent != None:
+            self._default_save_location = \
+                        self._manager.parent.get_save_location()
+        dlg = wx.FileDialog(self, "Choose a weight file",
+                                self._default_save_location, "",
+                                "*.*", wx.OPEN)
+        path = None
+        if dlg.ShowModal() == wx.ID_OK:
+            path = dlg.GetPath()
+        dlg.Destroy()
+        return path
+
+    def _reset_context_menu(self):
+        """
+        reset the context menu
+        """
+        for name, _ in self.state.saved_states.iteritems():
+            self.number_saved_state += 1
+            ## Add item in the context menu
+            id = wx.NewId()
+            msg = 'Save model and state %g' % self.number_saved_state
+            self.popUpMenu.Append(id, name, msg)
+            wx.EVT_MENU(self, id, self.onResetModel)
+    
+    def _reset_plotting_range(self, state):
+        """
+        Reset the plotting range to a given state
+        """
+        self.qmin.SetValue(str(state.qmin))
+        self.qmax.SetValue(str(state.qmax))
+
+    def _save_typeOfmodel(self):
+        """
+        save radiobutton containing the type model that can be selected
+        """
+        #self.state.shape_rbutton = self.shape_rbutton.GetValue()
+        #self.state.shape_indep_rbutton = self.shape_indep_rbutton.GetValue()
+        #self.state.struct_rbutton = self.struct_rbutton.GetValue()
+        #self.state.plugin_rbutton = self.plugin_rbutton.GetValue()
+        self.state.structurecombobox = self.structurebox.GetLabel()
+        self.state.formfactorcombobox = self.formfactorbox.GetLabel()
+        self.state.categorycombobox = self.categorybox.GetLabel()
+       
+        ## post state to fit panel
+        event = PageInfoEvent(page=self)
+        wx.PostEvent(self.parent, event)
+        
+    def _save_plotting_range(self):
+        """
+        save the state of plotting range
+        """
+        self.state.qmin = self.qmin_x
+        self.state.qmax = self.qmax_x
+        self.state.npts = self.npts_x
+            
+    def _onparamEnter_helper(self):
+        """
+        check if values entered by the user are changed and valid to replot
+        model
+        """
+        # Flag to register when a parameter has changed.
+        is_modified = False
+        self.fitrange = True
+        is_2Ddata = False
+        #self._undo.Enable(True)
+        # check if 2d data
+        if self.data.__class__.__name__ == "Data2D":
+            is_2Ddata = True
+        if self.model != None:
+            try:
+                is_modified = self._check_value_enter(self.fittable_param,
+                                                     is_modified)
+                is_modified = self._check_value_enter(self.fixed_param,
+                                                      is_modified)
+                is_modified = self._check_value_enter(self.parameters,
+                                                      is_modified)
+            except:
+                pass
+
+            # Here we should check whether the boundaries have been modified.
+            # If qmin and qmax have been modified, update qmin and qmax and
+            # set the is_modified flag to True
+            if self._validate_qrange(self.qmin, self.qmax):
+                tempmin = float(self.qmin.GetValue())
+                if tempmin != self.qmin_x:
+                    self.qmin_x = tempmin
+                    is_modified = True
+                tempmax = float(self.qmax.GetValue())
+                if tempmax != self.qmax_x:
+                    self.qmax_x = tempmax
+                    is_modified = True
+            
+                if is_2Ddata:
+                    # set mask
+                    is_modified = self._validate_Npts()
+                    
+            else:
+                self.fitrange = False
+                
+            if not self.data.is_data:
+                is_modified = True
+
+            ## if any value is modify draw model with new value
+            if not self.fitrange:
+                #self.btFit.Disable()
+                if is_2Ddata:
+                    self.btEditMask.Disable()
+            else:
+                if is_2Ddata and self.data.is_data and not self.batch_on:
+                    self.btEditMask.Enable(True)
+            if is_modified and self.fitrange:
+                # Theory case: need to get npts value to draw
+                self.npts_x = float(self.Npts_total.GetValue())
+                self.create_default_data()
+                self.state_change = True
+                self._draw_model()
+                self.Refresh()
+        return is_modified
+    
+    def _update_paramv_on_fit(self):
+        """
+        make sure that update param values just before the fitting
+        """
+        #flag for qmin qmax check values
+        flag = True
+        self.fitrange = True
+        is_modified = False
+
+        #wx.PostEvent(self._manager.parent, StatusEvent(status=" \
+        #updating ... ",type="update"))
+
+        ##So make sure that update param values on_Fit.
+        #self._undo.Enable(True)
+        if self.model != None:
+            if self.Npts_total.GetValue() != self.Npts_fit.GetValue():
+                if not self.data.is_data:
+                    self._manager.page_finder[self.uid].set_fit_data(data=\
+                                                                [self.data])
+            ##Check the values
+            self._check_value_enter(self.fittable_param, is_modified)
+            self._check_value_enter(self.fixed_param, is_modified)
+            self._check_value_enter(self.parameters, is_modified)
+
+            # If qmin and qmax have been modified, update qmin and qmax and 
+            # Here we should check whether the boundaries have been modified.
+            # If qmin and qmax have been modified, update qmin and qmax and 
+            # set the is_modified flag to True
+            self.fitrange = self._validate_qrange(self.qmin, self.qmax)
+            if self.fitrange:
+                tempmin = float(self.qmin.GetValue())
+                if tempmin != self.qmin_x:
+                    self.qmin_x = tempmin
+                tempmax = float(self.qmax.GetValue())
+                if tempmax != self.qmax_x:
+                    self.qmax_x = tempmax
+                if tempmax == tempmin:
+                    flag = False
+                temp_smearer = None
+                if not self.disable_smearer.GetValue():
+                    temp_smearer = self.current_smearer
+                    if self.slit_smearer.GetValue():
+                        flag = self.update_slit_smear()
+                    elif self.pinhole_smearer.GetValue():
+                        flag = self.update_pinhole_smear()
+                    else:
+                        self._manager.set_smearer(smearer=temp_smearer,
+                                                  uid=self.uid,
+                                                  fid=self.data.id,
+                                                  qmin=float(self.qmin_x),
+                                                  qmax=float(self.qmax_x),
+                            enable_smearer=not self.disable_smearer.GetValue(),
+                                                      draw=False)
+                elif not self._is_2D():
+                    self._manager.set_smearer(smearer=temp_smearer,
+                                              qmin=float(self.qmin_x),
+                                              uid=self.uid,
+                                              fid=self.data.id,
+                                              qmax=float(self.qmax_x),
+                            enable_smearer=not self.disable_smearer.GetValue(),
+                                                 draw=False)
+                    if self.data != None:
+                        index_data = ((self.qmin_x <= self.data.x) & \
+                                      (self.data.x <= self.qmax_x))
+                        val = str(len(self.data.x[index_data == True]))
+                        self.Npts_fit.SetValue(val)
+                    else:
+                        # No data in the panel
+                        try:
+                            self.npts_x = float(self.Npts_total.GetValue())
+                        except:
+                            flag = False
+                            return flag
+                    flag = True
+                if self._is_2D():
+                    # only 2D case set mask
+                    flag = self._validate_Npts()
+                    if not flag:
+                        return flag
+            else:
+                flag = False
+        else:
+            flag = False
+
+        #For invalid q range, disable the mask editor and fit button, vs.
+        if not self.fitrange:
+            if self._is_2D():
+                self.btEditMask.Disable()
+        else:
+            if self._is_2D() and  self.data.is_data and not self.batch_on:
+                self.btEditMask.Enable(True)
+
+        if not flag:
+            msg = "Cannot Plot or Fit :Must select a "
+            msg += " model or Fitting range is not valid!!!  "
+            wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
+        
+        try:
+            self.save_current_state()
+        except:
+            pass
+   
+        return flag
+               
+    def _is_modified(self, is_modified):
+        """
+        return to self._is_modified
+        """
+        return is_modified
+                       
+    def _reset_parameters_state(self, listtorestore, statelist):
+        """
+        Reset the parameters at the given state
+        """
+        if len(statelist) == 0 or len(listtorestore) == 0:
+            return
+        if len(statelist) != len(listtorestore):
+            return
+
+        for j in range(len(listtorestore)):
+            item_page = listtorestore[j]
+            item_page_info = statelist[j]
+            ##change the state of the check box for simple parameters
+            if item_page[0] != None:
+                item_page[0].SetValue(item_page_info[0])
+            if item_page[2] != None:
+                item_page[2].SetValue(item_page_info[2])
+                if item_page[2].__class__.__name__ == "ComboBox":
+                    if item_page_info[2] in self.model.fun_list:
+                        fun_val = self.model.fun_list[item_page_info[2]]
+                        self.model.setParam(item_page_info[1], fun_val)
+            if item_page[3] != None:
+                ## show or hide text +/-
+                if item_page_info[2]:
+                    item_page[3].Show(True)
+                else:
+                    item_page[3].Hide()
+            if item_page[4] != None:
+                ## show of hide the text crtl for fitting error
+                if item_page_info[4][0]:
+                    item_page[4].Show(True)
+                    item_page[4].SetValue(item_page_info[4][1])
+                else:
+                    item_page[3].Hide()
+            if item_page[5] != None:
+                ## show of hide the text crtl for fitting error
+                item_page[5].Show(item_page_info[5][0])
+                item_page[5].SetValue(item_page_info[5][1])
+                
+            if item_page[6] != None:
+                ## show of hide the text crtl for fitting error
+                item_page[6].Show(item_page_info[6][0])
+                item_page[6].SetValue(item_page_info[6][1])
+                    
+    def _reset_strparam_state(self, listtorestore, statelist):
+        """
+        Reset the string parameters at the given state
+        """
+        if len(statelist) == 0:
+            return
+
+        listtorestore = copy.deepcopy(statelist)
+        
+        for j in range(len(listtorestore)):
+            item_page = listtorestore[j]
+            item_page_info = statelist[j]
+            ##change the state of the check box for simple parameters
+            
+            if item_page[0] != None:
+                item_page[0].SetValue(format_number(item_page_info[0], True))
+
+            if item_page[2] != None:
+                param_name = item_page_info[1]
+                value = item_page_info[2]
+                selection = value
+                if value in self.model.fun_list:
+                    selection = self.model.fun_list[value]
+                item_page[2].SetValue(selection)
+                self.model.setParam(param_name, selection)
+                                      
+    def _copy_parameters_state(self, listtocopy, statelist):
+        """
+        copy the state of button
+        
+        :param listtocopy: the list of check button to copy
+        :param statelist: list of state object to store the current state
+        
+        """
+        if len(listtocopy) == 0:
+            return
+       
+        for item in listtocopy:
+  
+            checkbox_state = None
+            if item[0] != None:
+                checkbox_state = item[0].GetValue()
+            parameter_name = item[1]
+            parameter_value = None
+            if item[2] != None:
+                parameter_value = item[2].GetValue()
+            static_text = None
+            if item[3] != None:
+                static_text = item[3].IsShown()
+            error_value = None
+            error_state = None
+            if item[4] != None:
+                error_value = item[4].GetValue()
+                error_state = item[4].IsShown()
+                
+            min_value = None
+            min_state = None
+            if item[5] != None:
+                min_value = item[5].GetValue()
+                min_state = item[5].IsShown()
+                
+            max_value = None
+            max_state = None
+            if item[6] != None:
+                max_value = item[6].GetValue()
+                max_state = item[6].IsShown()
+            unit = None
+            if item[7] != None:
+                unit = item[7].GetLabel()
+               
+            statelist.append([checkbox_state, parameter_name, parameter_value,
+                              static_text, [error_state, error_value],
+                              [min_state, min_value],
+                              [max_state, max_value], unit])
+           
+#    The following funcion seems to be superseded with fillsizer and 
+#    init combo box.  certainly it seems not to know about categories and uses 
+#    only shapes and shape independent -- tested after commenting out and does
+#    not seem to cause problem.  Leave commented out for now but delete in
+#    a code cleanup once clear it really is no longer used.
+#    PDB  8 April 2014
+
+#    def _set_model_sizer_selection(self, model):
+#        """
+#        Display the sizer according to the type of the current model
+#        """
+#        if model == None:
+#            return
+#        if hasattr(model, "s_model"):
+#            
+#            class_name = model.s_model.__class__
+#            name = model.s_model.name
+#            flag = (name != "NoStructure")
+#            if flag and \
+#                (class_name in self.model_list_box["Structure Factors"]):
+#                self.structurebox.Show()
+#                self.text2.Show()
+#                self.structurebox.Enable()
+#               self.text2.Enable()
+ #               items = self.structurebox.GetItems()
+ #               self.sizer1.Layout()
+ #               
+ #               for i in range(len(items)):
+ #                   if items[i] == str(name):
+ #                       self.structurebox.SetSelection(i)
+ #                       break
+ #                   
+ #       if hasattr(model, "p_model"):
+ #           class_name = model.p_model.__class__
+ #           name = model.p_model.name
+ #           self.formfactorbox.Clear()
+ #           
+ #           for k, list in self.model_list_box.iteritems():
+ #               if k in["P(Q)*S(Q)", "Shapes"] and \
+ #                   class_name in self.model_list_box["Shapes"]:
+ #                   self.shape_rbutton.SetValue(True)
+ #                   ## fill the form factor list with new model
+ #                   self._populate_box(self.formfactorbox,
+ #                                      self.model_list_box["Shapes"])
+ #                   items = self.formfactorbox.GetItems()
+ #                   ## set comboxbox to the selected item
+ #                   for i in range(len(items)):
+ #                       if items[i] == str(name):
+ #                           self.formfactorbox.SetSelection(i)
+ #                           break
+ #                   return
+ #               elif k == "Shape-Independent":
+ #                   self.shape_indep_rbutton.SetValue(True)
+ #               elif k == "Structure Factors":
+ #                   self.struct_rbutton.SetValue(True)
+ #               elif k == "Multi-Functions":
+ #                   continue
+ #               else:
+ #                   self.plugin_rbutton.SetValue(True)
+ #              
+ #               if class_name in list:
+ #                   ## fill the form factor list with new model
+ #                   self._populate_box(self.formfactorbox, list)
+ #                   items = self.formfactorbox.GetItems()
+ #                   ## set comboxbox to the selected item
+ #                   for i in range(len(items)):
+ #                       if items[i] == str(name):
+ #                           self.formfactorbox.SetSelection(i)
+ #                           break
+ #                   break
+ #       else:
+            ## Select the model from the menu
+#            class_name = model.__class__
+#            name = model.name
+#            self.formfactorbox.Clear()
+#            items = self.formfactorbox.GetItems()
+#    
+#            for k, list in self.model_list_box.iteritems():
+#                if k in["P(Q)*S(Q)", "Shapes"] and \
+#                    class_name in self.model_list_box["Shapes"]:
+#                    if class_name in self.model_list_box["P(Q)*S(Q)"]:
+#                        self.structurebox.Show()
+#                        self.text2.Show()
+#                       self.structurebox.Enable()
+#                       self.structurebox.SetSelection(0)
+#                       self.text2.Enable()
+#                   else:
+#                        self.structurebox.Hide()
+#                        self.text2.Hide()
+#                        self.structurebox.Disable()
+#                        self.structurebox.SetSelection(0)
+#                        self.text2.Disable()
+#                        
+#                    self.shape_rbutton.SetValue(True)
+                    ## fill the form factor list with new model
+#                    self._populate_box(self.formfactorbox,
+#                                       self.model_list_box["Shapes"])
+#                    items = self.formfactorbox.GetItems()
+#                    ## set comboxbox to the selected item
+#                    for i in range(len(items)):
+#                        if items[i] == str(name):
+#                            self.formfactorbox.SetSelection(i)
+#                            break
+#                    return
+#                elif k == "Shape-Independent":
+#                    self.shape_indep_rbutton.SetValue(True)
+#                elif k == "Structure Factors":
+#                    self.struct_rbutton.SetValue(True)
+#                elif k == "Multi-Functions":
+#                    continue
+#                else:
+#                    self.plugin_rbutton.SetValue(True)
+#                if class_name in list:
+#                    self.structurebox.SetSelection(0)
+#                    self.structurebox.Disable()
+#                    self.text2.Disable()
+                    ## fill the form factor list with new model
+#                    self._populate_box(self.formfactorbox, list)
+#                    items = self.formfactorbox.GetItems()
+                    ## set comboxbox to the selected item
+#                    for i in range(len(items)):
+#                        if items[i] == str(name):
+#                            self.formfactorbox.SetSelection(i)
+#                            break
+#                    break
+                
+    def _draw_model(self, update_chisqr=True, source='model'):
+        """
+        Method to draw or refresh a plotted model.
+        The method will use the data member from the model page
+        to build a call to the fitting perspective manager.
+        
+        :param chisqr: update chisqr value [bool]
+        """
+        wx.CallAfter(self._draw_model_after, update_chisqr, source)
+        
+    def _draw_model_after(self, update_chisqr=True, source='model'):
+        """
+        Method to draw or refresh a plotted model.
+        The method will use the data member from the model page
+        to build a call to the fitting perspective manager.
+        
+        :param chisqr: update chisqr value [bool]
+        """
+        #if self.check_invalid_panel():
+        #    return
+        if self.model != None:
+            temp_smear = None
+            if hasattr(self, "enable_smearer"):
+                if not self.disable_smearer.GetValue():
+                    temp_smear = self.current_smearer
+            # compute weight for the current data
+            from sas import get_weight
+            flag = self.get_weight_flag()
+            weight = get_weight(data=self.data, is2d=self._is_2D(), flag=flag)
+            toggle_mode_on = self.model_view.IsEnabled()
+            is_2d = self._is_2D()
+            self._manager.draw_model(self.model,
+                                    data=self.data,
+                                    smearer=temp_smear,
+                                    qmin=float(self.qmin_x),
+                                    qmax=float(self.qmax_x),
+                                    page_id=self.uid,
+                                    toggle_mode_on=toggle_mode_on,
+                                    state=self.state,
+                                    enable2D=is_2d,
+                                    update_chisqr=update_chisqr,
+                                    source='model',
+                                    weight=weight)
+       
+    def _on_show_sld(self, event=None):
+        """
+        Plot SLD profile
+        """
+        # get profile data
+        x, y = self.model.getProfile()
+
+        from sas.plottools import Data1D as pf_data1d
+        #from sas.perspectives.theory.profile_dialog import SLDPanel
+        from sas.guiframe.local_perspectives.plotting.profile_dialog \
+        import SLDPanel
+        sld_data = pf_data1d(x, y)
+        sld_data.name = 'SLD'
+        sld_data.axes = self.sld_axes
+        self.panel = SLDPanel(self, data=sld_data, axes=self.sld_axes, id=-1)
+        self.panel.ShowModal()
+        
+    def _set_multfactor_combobox(self, multiplicity=10):
+        """
+        Set comboBox for muitfactor of CoreMultiShellModel
+        :param multiplicit: no. of multi-functionality
+        """
+        # build content of the combobox
+        for idx in range(0, multiplicity):
+            self.multifactorbox.Append(str(idx), int(idx))
+        self._hide_multfactor_combobox()
+        
+    def _show_multfactor_combobox(self):
+        """
+        Show the comboBox of muitfactor of CoreMultiShellModel
+        """
+        if not self.mutifactor_text.IsShown():
+            self.mutifactor_text.Show(True)
+            self.mutifactor_text1.Show(True)
+        if not self.multifactorbox.IsShown():
+            self.multifactorbox.Show(True)
+             
+    def _hide_multfactor_combobox(self):
+        """
+        Hide the comboBox of muitfactor of CoreMultiShellModel
+        """
+        if self.mutifactor_text.IsShown():
+            self.mutifactor_text.Hide()
+            self.mutifactor_text1.Hide()
+        if self.multifactorbox.IsShown():
+            self.multifactorbox.Hide()
+    
+    def formfactor_combo_init(self):
+        """
+        First time calls _show_combox_helper
+        """
+        self._show_combox(None)
+                
+    def _show_combox_helper(self):
+        """
+        Fill panel's combo box according to the type of model selected
+        """
+        custom_model = 'Customized Models'
+        mod_cat = self.categorybox.GetStringSelection()
+        self.structurebox.SetSelection(0)
+        self.structurebox.Disable()
+        self.formfactorbox.Clear()
+        if mod_cat == None:
+            return
+        m_list = []
+        try:
+            if mod_cat == custom_model:
+                for model in self.model_list_box[mod_cat]:
+                    str_m = str(model).split(".")[0]
+                    #self.model_box.Append(str_m)
+                    m_list.append(self.model_dict[str_m])
+            else:
+                cat_dic = self.master_category_dict[mod_cat]
+                for (model, enabled) in cat_dic:
+                    if enabled:
+                        m_list.append(self.model_dict[model])
+                    #else:
+                    #    msg = "This model is disabled by Category Manager." 
+                    #    wx.PostEvent(self.parent.parent,
+                    #                 StatusEvent(status=msg, info="error"))
+        except:
+            msg = "%s\n" % (sys.exc_value)
+            wx.PostEvent(self._manager.parent,
+                         StatusEvent(status=msg, info="error"))
+        self._populate_box(self.formfactorbox, m_list)
+    
+    def _on_modify_cat(self, event=None):  
+        self._manager.parent.on_category_panel(event)  
+        
+    def _show_combox(self, event=None):
+        """
+        Show combox box associate with type of model selected
+        """
+        self.Show(False)
+        self._show_combox_helper()
+        self._on_select_model(event=None)
+        self.Show(True)
+        self._save_typeOfmodel()
+        self.sizer4_4.Layout()
+        self.sizer4.Layout()
+        self.Layout()
+        self.Refresh()
+  
+    def _populate_box(self, combobox, list):
+        """
+        fill combox box with dict item
+        
+        :param list: contains item to fill the combox
+            item must model class
+        """
+        mlist = []
+        for models in list:
+            model = models()
+            name = model.__class__.__name__
+            if models.__name__ != "NoStructure":
+                if hasattr(model, "name"):
+                    name = model.name
+                mlist.append((name, models))
+                
+        # Sort the models
+        mlist_sorted = sorted(mlist)
+        for item in mlist_sorted:
+            combobox.Append(item[0], item[1])
+        return 0
+    
+    def _onQrangeEnter(self, event):
+        """
+        Check validity of value enter in the Q range field
+        
+        """
+        tcrtl = event.GetEventObject()
+        #Clear msg if previously shown.
+        msg = ""
+        wx.PostEvent(self.parent, StatusEvent(status=msg))
+        # Flag to register when a parameter has changed.
+        #is_modified = False
+        if tcrtl.GetValue().lstrip().rstrip() != "":
+            try:
+                float(tcrtl.GetValue())
+                tcrtl.SetBackgroundColour(wx.WHITE)
+                # If qmin and qmax have been modified, update qmin and qmax
+                if self._validate_qrange(self.qmin, self.qmax):
+                    tempmin = float(self.qmin.GetValue())
+                    if tempmin != self.qmin_x:
+                        self.qmin_x = tempmin
+                    tempmax = float(self.qmax.GetValue())
+                    if tempmax != self.qmax_x:
+                        self.qmax_x = tempmax
+                else:
+                    tcrtl.SetBackgroundColour("pink")
+                    msg = "Model Error: wrong value entered: %s" % sys.exc_value
+                    wx.PostEvent(self.parent, StatusEvent(status=msg))
+                    return
+            except:
+                tcrtl.SetBackgroundColour("pink")
+                msg = "Model Error: wrong value entered: %s" % sys.exc_value
+                wx.PostEvent(self.parent, StatusEvent(status=msg))
+                return
+            #Check if # of points for theory model are valid(>0).
+            if self.npts != None:
+                if check_float(self.npts):
+                    temp_npts = float(self.npts.GetValue())
+                    if temp_npts != self.num_points:
+                        self.num_points = temp_npts
+                        #is_modified = True
+                else:
+                    msg = "Cannot plot: No points in Q range!!!  "
+                    wx.PostEvent(self.parent, StatusEvent(status=msg))
+        else:
+            tcrtl.SetBackgroundColour("pink")
+            msg = "Model Error: wrong value entered!!!"
+            wx.PostEvent(self.parent, StatusEvent(status=msg))
+        self.save_current_state()
+        event = PageInfoEvent(page=self)
+        wx.PostEvent(self.parent, event)
+        self.state_change = False
+        #Draw the model for a different range
+        if not self.data.is_data:
+            self.create_default_data()
+        self._draw_model()
+                   
+    def _theory_qrange_enter(self, event):
+        """
+        Check validity of value enter in the Q range field
+        """
+        
+        tcrtl = event.GetEventObject()
+        #Clear msg if previously shown.
+        msg = ""
+        wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
+        # Flag to register when a parameter has changed.
+        is_modified = False
+        if tcrtl.GetValue().lstrip().rstrip() != "":
+            try:
+                value = float(tcrtl.GetValue())
+                tcrtl.SetBackgroundColour(wx.WHITE)
+
+                # If qmin and qmax have been modified, update qmin and qmax
+                if self._validate_qrange(self.theory_qmin, self.theory_qmax):
+                    tempmin = float(self.theory_qmin.GetValue())
+                    if tempmin != self.theory_qmin_x:
+                        self.theory_qmin_x = tempmin
+                    tempmax = float(self.theory_qmax.GetValue())
+                    if tempmax != self.qmax_x:
+                        self.theory_qmax_x = tempmax
+                else:
+                    tcrtl.SetBackgroundColour("pink")
+                    msg = "Model Error: wrong value entered: %s" % sys.exc_value
+                    wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
+                    return
+            except:
+                tcrtl.SetBackgroundColour("pink")
+                msg = "Model Error: wrong value entered: %s" % sys.exc_value
+                wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
+                return
+            #Check if # of points for theory model are valid(>0).
+            if self.Npts_total.IsEditable():
+                if check_float(self.Npts_total):
+                    temp_npts = float(self.Npts_total.GetValue())
+                    if temp_npts != self.num_points:
+                        self.num_points = temp_npts
+                        is_modified = True
+                else:
+                    msg = "Cannot Plot: No points in Q range!!!  "
+                    wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
+        else:
+            tcrtl.SetBackgroundColour("pink")
+            msg = "Model Error: wrong value entered!!!"
+            wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
+        self.save_current_state()
+        event = PageInfoEvent(page=self)
+        wx.PostEvent(self.parent, event)
+        self.state_change = False
+        #Draw the model for a different range
+        self.create_default_data()
+        self._draw_model()
+                   
+    def _on_select_model_helper(self):
+        """
+        call back for model selection
+        """
+        ## reset dictionary containing reference to dispersion
+        self._disp_obj_dict = {}
+        self.disp_cb_dict = {}
+        self.temp_multi_functional = False
+        f_id = self.formfactorbox.GetCurrentSelection()
+        #For MAC
+        form_factor = None
+        if f_id >= 0:
+            form_factor = self.formfactorbox.GetClientData(f_id)
+
+        if not form_factor in  self.model_list_box["multiplication"]:
+            self.structurebox.Hide()
+            self.text2.Hide()
+            self.structurebox.Disable()
+            self.structurebox.SetSelection(0)
+            self.text2.Disable()
+        else:
+            self.structurebox.Show()
+            self.text2.Show()
+            self.structurebox.Enable()
+            self.text2.Enable()
+            
+        if form_factor != None:
+            # set multifactor for Mutifunctional models
+            if form_factor().__class__ in \
+                                        self.model_list_box["Multi-Functions"]:
+                m_id = self.multifactorbox.GetCurrentSelection()
+                multiplicity = form_factor().multiplicity_info[0]
+                self.multifactorbox.Clear()
+                self._set_multfactor_combobox(multiplicity)
+                self._show_multfactor_combobox()
+                #ToDo:  this info should be called directly from the model
+                text = form_factor().multiplicity_info[1]  # 'No. of Shells: '
+
+                self.mutifactor_text.SetLabel(text)
+                if m_id > multiplicity - 1:
+                    # default value
+                    m_id = 1
+                    
+                self.multi_factor = self.multifactorbox.GetClientData(m_id)
+                if self.multi_factor == None:
+                    self.multi_factor = 0
+                form_factor = form_factor(int(self.multi_factor))
+                self.multifactorbox.SetSelection(m_id)
+                # Check len of the text1 and max_multiplicity
+                text = ''
+                if form_factor.multiplicity_info[0] == \
+                                        len(form_factor.multiplicity_info[2]):
+                    text = form_factor.multiplicity_info[2][self.multi_factor]
+                self.mutifactor_text1.SetLabel(text)
+                # Check if model has  get sld profile.
+                if len(form_factor.multiplicity_info[3]) > 0:
+                    self.sld_axes = form_factor.multiplicity_info[3]
+                    self.show_sld_button.Show(True)
+                else:
+                    self.sld_axes = ""
+
+            else:
+                self._hide_multfactor_combobox()
+                self.show_sld_button.Hide()
+                form_factor = form_factor()
+                self.multi_factor = None
+        else:
+            self._hide_multfactor_combobox()
+            self.show_sld_button.Hide()
+            self.multi_factor = None
+              
+        s_id = self.structurebox.GetCurrentSelection()
+        struct_factor = self.structurebox.GetClientData(s_id)
+        
+        if  struct_factor != None:
+            from sas.models.MultiplicationModel import MultiplicationModel
+            self.model = MultiplicationModel(form_factor, struct_factor())
+            # multifunctional form factor
+            if len(form_factor.non_fittable) > 0:
+                self.temp_multi_functional = True
+        else:
+            if form_factor != None:
+                self.model = form_factor
+            else:
+                self.model = None
+                return self.model
+        # check if model has magnetic parameters
+        if len(self.model.magnetic_params) > 0:
+            self._has_magnetic = True 
+        else:
+            self._has_magnetic = False  
+        ## post state to fit panel
+        self.state.parameters = []
+        self.state.model = self.model
+        self.state.qmin = self.qmin_x
+        self.state.multi_factor = self.multi_factor
+        self.disp_list = self.model.getDispParamList()
+        self.state.disp_list = self.disp_list
+        self.on_set_focus(None)
+        self.Layout()
+        
+    def _validate_qrange(self, qmin_ctrl, qmax_ctrl):
+        """
+        Verify that the Q range controls have valid values
+        and that Qmin < Qmax.
+        
+        :param qmin_ctrl: text control for Qmin
+        :param qmax_ctrl: text control for Qmax
+        
+        :return: True is the Q range is value, False otherwise
+        
+        """
+        qmin_validity = check_float(qmin_ctrl)
+        qmax_validity = check_float(qmax_ctrl)
+        if not (qmin_validity and qmax_validity):
+            return False
+        else:
+            qmin = float(qmin_ctrl.GetValue())
+            qmax = float(qmax_ctrl.GetValue())
+            if qmin < qmax:
+                #Make sure to set both colours white.
+                qmin_ctrl.SetBackgroundColour(wx.WHITE)
+                qmin_ctrl.Refresh()
+                qmax_ctrl.SetBackgroundColour(wx.WHITE)
+                qmax_ctrl.Refresh()
+            else:
+                qmin_ctrl.SetBackgroundColour("pink")
+                qmin_ctrl.Refresh()
+                qmax_ctrl.SetBackgroundColour("pink")
+                qmax_ctrl.Refresh()
+                msg = "Invalid Q range: Q min must be smaller than Q max"
+                wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
+                return False
+        return True
+    
+    def _validate_Npts(self):
+        """
+        Validate the number of points for fitting is more than 10 points.
+        If valid, setvalues Npts_fit otherwise post msg.
+        """
+        #default flag
+        flag = True
+        # Theory
+        if self.data == None and self.enable2D:
+            return flag
+        for data in self.data_list:
+            # q value from qx and qy
+            radius = numpy.sqrt(data.qx_data * data.qx_data +
+                                data.qy_data * data.qy_data)
+            #get unmasked index
+            index_data = (float(self.qmin.GetValue()) <= radius) & \
+                            (radius <= float(self.qmax.GetValue()))
+            index_data = (index_data) & (data.mask)
+            index_data = (index_data) & (numpy.isfinite(data.data))
+
+            if len(index_data[index_data]) < 10:
+                # change the color pink.
+                self.qmin.SetBackgroundColour("pink")
+                self.qmin.Refresh()
+                self.qmax.SetBackgroundColour("pink")
+                self.qmax.Refresh()
+                msg = "Data Error: "
+                msg += "Too few points in %s." % data.name
+                wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
+                self.fitrange = False
+                flag = False
+            else:
+                self.Npts_fit.SetValue(str(len(index_data[index_data == True])))
+                self.fitrange = True
+            
+        return flag
+
+    def _validate_Npts_1D(self):
+        """
+        Validate the number of points for fitting is more than 5 points.
+        If valid, setvalues Npts_fit otherwise post msg.
+        """
+        #default flag
+        flag = True
+        # Theory
+        if self.data == None:
+            return flag
+        for data in self.data_list:
+            # q value from qx and qy
+            radius = data.x
+            #get unmasked index
+            index_data = (float(self.qmin.GetValue()) <= radius) & \
+                            (radius <= float(self.qmax.GetValue()))
+            index_data = (index_data) & (numpy.isfinite(data.y))
+
+            if len(index_data[index_data]) < 5:
+                # change the color pink.
+                self.qmin.SetBackgroundColour("pink")
+                self.qmin.Refresh()
+                self.qmax.SetBackgroundColour("pink")
+                self.qmax.Refresh()
+                msg = "Data Error: "
+                msg += "Too few points in %s." % data.name
+                wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
+                self.fitrange = False
+                flag = False
+            else:
+                self.Npts_fit.SetValue(str(len(index_data[index_data == True])))
+                self.fitrange = True
+            
+        return flag
+    
+    def _check_value_enter(self, list, modified):
+        """
+        :param list: model parameter and panel info
+        :Note: each item of the list should be as follow:
+            item=[check button state, parameter's name,
+                paramater's value, string="+/-",
+                parameter's error of fit,
+                parameter's minimum value,
+                parrameter's maximum value ,
+                parameter's units]
+        """
+        is_modified = modified
+        if len(list) == 0:
+            return is_modified
+        for item in list:
+            #skip angle parameters for 1D
+            if not self.enable2D:
+                if item in self.orientation_params:
+                    continue
+            #try:
+            name = str(item[1])
+            
+            if string.find(name, ".npts") == -1 and \
+                                        string.find(name, ".nsigmas") == -1:
+                ## check model parameters range
+                param_min = None
+                param_max = None
+               
+                ## check minimun value
+                if item[5] != None and item[5] != "":
+                    if item[5].GetValue().lstrip().rstrip() != "":
+                        try:
+                            param_min = float(item[5].GetValue())
+                            if not self._validate_qrange(item[5], item[2]):
+                                if numpy.isfinite(param_min):
+                                    item[2].SetValue(format_number(param_min))
+                            
+                            item[5].SetBackgroundColour(wx.WHITE)
+                            item[2].SetBackgroundColour(wx.WHITE)
+                                           
+                        except:
+                            msg = "Wrong fit parameter range entered"
+                            wx.PostEvent(self._manager.parent,
+                                         StatusEvent(status=msg))
+                            raise ValueError, msg
+                        is_modified = True
+                ## check maximum value
+                if item[6] != None and item[6] != "":
+                    if item[6].GetValue().lstrip().rstrip() != "":
+                        try:
+                            param_max = float(item[6].GetValue())
+                            if not self._validate_qrange(item[2], item[6]):
+                                if numpy.isfinite(param_max):
+                                    item[2].SetValue(format_number(param_max))
+                            
+                            item[6].SetBackgroundColour(wx.WHITE)
+                            item[2].SetBackgroundColour(wx.WHITE)
+                        except:
+                            msg = "Wrong Fit parameter range entered "
+                            wx.PostEvent(self._manager.parent,
+                                         StatusEvent(status=msg))
+                            raise ValueError, msg
+                        is_modified = True
+                
+                if param_min != None and param_max != None:
+                    if not self._validate_qrange(item[5], item[6]):
+                        msg = "Wrong Fit range entered for parameter "
+                        msg += "name %s of model %s " % (name, self.model.name)
+                        wx.PostEvent(self._manager.parent,
+                                     StatusEvent(status=msg))
+                
+                if name in self.model.details.keys():
+                    self.model.details[name][1:3] = param_min, param_max
+                    is_modified = True
+                else:
+                    self.model.details[name] = ["", param_min, param_max]
+                    is_modified = True
+            try:
+                # Check if the textctr is enabled
+                if item[2].IsEnabled():
+                    value = float(item[2].GetValue())
+                    item[2].SetBackgroundColour("white")
+                    # If the value of the parameter has changed,
+                    # +update the model and set the is_modified flag
+                    if value != self.model.getParam(name) and \
+                                                numpy.isfinite(value):
+                        self.model.setParam(name, value)
+            except:
+                item[2].SetBackgroundColour("pink")
+                msg = "Wrong Fit parameter value entered "
+                wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
+                
+        return is_modified
+        
+    def _set_dipers_Param(self, event):
+        """
+        respond to self.enable_disp and self.disable_disp radio box.
+        The dispersity object is reset inside the model into Gaussian.
+        When the user select yes , this method display a combo box for
+        more selection when the user selects No,the combo box disappears.
+        Redraw the model with the default dispersity (Gaussian)
+        """
+        ## On selction if no model exists.
+        if self.model == None:
+            self.disable_disp.SetValue(True)
+            msg = "Please select a Model first..."
+            wx.MessageBox(msg, 'Info')
+            wx.PostEvent(self._manager.parent,
+                         StatusEvent(status="Polydispersion: %s" % msg))
+            return
+
+        self._reset_dispersity()
+    
+        if self.model == None:
+            self.model_disp.Hide()
+            self.sizer4_4.Clear(True)
+            return
+
+        if self.enable_disp.GetValue():
+            ## layout for model containing no dispersity parameters
+            
+            self.disp_list = self.model.getDispParamList()
+             
+            if len(self.disp_list) == 0 and len(self.disp_cb_dict) == 0:
+                self._layout_sizer_noDipers()
+            else:
+                ## set gaussian sizer
+                self._on_select_Disp(event=None)
+        else:
+            self.sizer4_4.Clear(True)
+            
+        ## post state to fit panel
+        self.save_current_state()
+        if event != None:
+            event = PageInfoEvent(page=self)
+            wx.PostEvent(self.parent, event)
+        #draw the model with the current dispersity
+        self._draw_model()
+        self.sizer4_4.Layout()
+        self.sizer5.Layout()
+        self.Layout()
+        self.Refresh()
+          
+    def _layout_sizer_noDipers(self):
+        """
+        Draw a sizer with no dispersity info
+        """
+        ix = 0
+        iy = 1
+        self.fittable_param = []
+        self.fixed_param = []
+        self.orientation_params_disp = []
+        
+        self.sizer4_4.Clear(True)
+        text = "No polydispersity available for this model"
+        model_disp = wx.StaticText(self, -1, text)
+        self.sizer4_4.Add(model_disp, (iy, ix), (1, 1),
+                          wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 10)
+        self.sizer4_4.Layout()
+        self.sizer4.Layout()
+    
+    def _reset_dispersity(self):
+        """
+        put gaussian dispersity into current model
+        """
+        if len(self.param_toFit) > 0:
+            for item in self.fittable_param:
+                if item in self.param_toFit:
+                    self.param_toFit.remove(item)
+
+            for item in self.orientation_params_disp:
+                if item in self.param_toFit:
+                    self.param_toFit.remove(item)
+         
+        self.fittable_param = []
+        self.fixed_param = []
+        self.orientation_params_disp = []
+        self.values = {}
+        self.weights = {}
+      
+        from sas.models.dispersion_models import GaussianDispersion
+        if len(self.disp_cb_dict) == 0:
+            self.save_current_state()
+            self.sizer4_4.Clear(True)
+            self.Layout()
+            return
+        if (len(self.disp_cb_dict) > 0):
+            for p in self.disp_cb_dict:
+                # The parameter was un-selected.
+                # Go back to Gaussian model (with 0 pts)
+                disp_model = GaussianDispersion()
+               
+                self._disp_obj_dict[p] = disp_model
+                # Set the new model as the dispersion object
+                # for the selected parameter
+                try:
+                    self.model.set_dispersion(p, disp_model)
+                except:
+
+                    pass
+
+        ## save state into
+        self.save_current_state()
+        self.Layout()
+        self.Refresh()
+                  
+    def _on_select_Disp(self, event):
+        """
+        allow selecting different dispersion
+        self.disp_list should change type later .now only gaussian
+        """
+        self._set_sizer_dispersion()
+
+        ## Redraw the model
+        self._draw_model()
+        #self._undo.Enable(True)
+        event = PageInfoEvent(page=self)
+        wx.PostEvent(self.parent, event)
+        
+        self.sizer4_4.Layout()
+        self.sizer4.Layout()
+        self.SetupScrolling()
+    
+    def _on_disp_func(self, event=None):
+        """
+        Select a distribution function for the polydispersion
+        
+        :Param event: ComboBox event
+        """
+        # get ready for new event
+        if event != None:
+            event.Skip()
+        # Get event object
+        disp_box = event.GetEventObject()
+
+        # Try to select a Distr. function
+        try:
+            disp_box.SetBackgroundColour("white")
+            selection = disp_box.GetCurrentSelection()
+            param_name = disp_box.Name.split('.')[0]
+            disp_name = disp_box.GetValue()
+            dispersity = disp_box.GetClientData(selection)
+    
+            #disp_model =  GaussianDispersion()
+            disp_model = dispersity()
+            # Get param names to reset the values of the param
+            name1 = param_name + ".width"
+            name2 = param_name + ".npts"
+            name3 = param_name + ".nsigmas"
+            # Check Disp. function whether or not it is 'array'
+            if disp_name.lower() == "array":
+                value2 = ""
+                value3 = ""
+                value1 = self._set_array_disp(name=name1, disp=disp_model)
+            else:
+                self._del_array_values(name1)
+                #self._reset_array_disp(param_name)
+                self._disp_obj_dict[name1] = disp_model
+                self.model.set_dispersion(param_name, disp_model)
+                self.state._disp_obj_dict[name1] = disp_model
+ 
+                value1 = str(format_number(self.model.getParam(name1), True))
+                value2 = str(format_number(self.model.getParam(name2)))
+                value3 = str(format_number(self.model.getParam(name3)))
+            # Reset fittable polydispersin parameter value
+            for item in self.fittable_param:
+                if item[1] == name1:
+                    item[2].SetValue(value1)
+                    item[5].SetValue("")
+                    item[6].SetValue("")
+                    # Disable for array
+                    if disp_name.lower() == "array":
+                        item[0].SetValue(False)
+                        item[0].Disable()
+                        item[2].Disable()
+                        item[3].Show(False)
+                        item[4].Show(False)
+                        item[5].Disable()
+                        item[6].Disable()
+                    else:
+                        item[0].Enable()
+                        item[2].Enable()
+                        item[5].Enable()
+                        item[6].Enable()
+                    break
+            # Reset fixed polydispersion params
+            for item in self.fixed_param:
+                if item[1] == name2:
+                    item[2].SetValue(value2) 
+                    # Disable Npts for array
+                    if disp_name.lower() == "array":
+                        item[2].Disable()
+                    else:
+                        item[2].Enable()
+                if item[1] == name3:
+                    item[2].SetValue(value3)
+                    # Disable Nsigs for array
+                    if disp_name.lower() == "array":
+                        item[2].Disable()
+                    else:
+                        item[2].Enable()
+                
+            # Make sure the check box updated when all checked
+            if self.cb1.GetValue():
+                #self.select_all_param(None)
+                self.get_all_checked_params()
+
+            # update params
+            self._update_paramv_on_fit()
+            # draw
+            self._draw_model()
+            self.Refresh()
+        except:
+            # Error msg
+            msg = "Error occurred:"
+            msg += " Could not select the distribution function..."
+            msg += " Please select another distribution function."
+            disp_box.SetBackgroundColour("pink")
+            # Focus on Fit button so that users can see the pinky box
+            self.btFit.SetFocus()
+            wx.PostEvent(self._manager.parent,
+                         StatusEvent(status=msg, info="error"))
+        
+    def _set_array_disp(self, name=None, disp=None):
+        """
+        Set array dispersion
+        
+        :param name: name of the parameter for the dispersion to be set
+        :param disp: the polydisperion object
+        """
+        # The user wants this parameter to be averaged.
+        # Pop up the file selection dialog.
+        path = self._selectDlg()
+        # Array data
+        values = []
+        weights = []
+        # If nothing was selected, just return
+        if path is None:
+            self.disp_cb_dict[name].SetValue(False)
+            #self.noDisper_rbox.SetValue(True)
+            return
+        self._default_save_location = os.path.dirname(path)
+        if self._manager != None:
+            self._manager.parent._default_save_location =\
+                             self._default_save_location
+
+        basename = os.path.basename(path)
+        values, weights = self.read_file(path)
+        
+        # If any of the two arrays is empty, notify the user that we won't
+        # proceed
+        if len(self.param_toFit) > 0:
+            if name in self.param_toFit:
+                self.param_toFit.remove(name)
+
+        # Tell the user that we are about to apply the distribution
+        msg = "Applying loaded %s distribution: %s" % (name, path)
+        wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
+        self._set_array_disp_model(name=name, disp=disp,
+                                    values=values, weights=weights)
+        return basename
+    
+    def _set_array_disp_model(self, name=None, disp=None,
+                              values=[], weights=[]):
+        """
+        Set array dispersion model
+        
+        :param name: name of the parameter for the dispersion to be set
+        :param disp: the polydisperion object
+        """
+        disp.set_weights(values, weights)
+        self._disp_obj_dict[name] = disp
+        self.model.set_dispersion(name.split('.')[0], disp)
+        self.state._disp_obj_dict[name] = disp
+        self.values[name] = values
+        self.weights[name] = weights
+        # Store the object to make it persist outside the
+        # scope of this method
+        #TODO: refactor model to clean this up?
+        self.state.values = {}
+        self.state.weights = {}
+        self.state.values = copy.deepcopy(self.values)
+        self.state.weights = copy.deepcopy(self.weights)
+
+        # Set the new model as the dispersion object for the
+        #selected parameter
+        #self.model.set_dispersion(p, disp_model)
+        # Store a reference to the weights in the model object
+        #so that
+        # it's not lost when we use the model within another thread.
+        self.state.model = self.model.clone()
+        self.model._persistency_dict[name.split('.')[0]] = \
+                                        [values, weights]
+        self.state.model._persistency_dict[name.split('.')[0]] = \
+                                        [values, weights]
+                                        
+    def _del_array_values(self, name=None):
+        """
+        Reset array dispersion
+        
+        :param name: name of the parameter for the dispersion to be set
+        """
+        # Try to delete values and weight of the names array dic if exists
+        try:
+            del self.values[name]
+            del self.weights[name]
+            # delete all other dic
+            del self.state.values[name]
+            del self.state.weights[name]
+            del self.model._persistency_dict[name.split('.')[0]]
+            del self.state.model._persistency_dict[name.split('.')[0]]
+        except:
+            pass
+                                            
+    def _lay_out(self):
+        """
+        returns self.Layout
+        
+        :Note: Mac seems to like this better when self.
+            Layout is called after fitting.
+        """
+        self._sleep4sec()
+        self.Layout()
+        return
+    
+    def _sleep4sec(self):
+        """
+            sleep for 1 sec only applied on Mac
+            Note: This 1sec helps for Mac not to crash on self.
+            Layout after self._draw_model
+        """
+        if ON_MAC == True:
+            time.sleep(1)
+            
+    def _find_polyfunc_selection(self, disp_func=None):
+        """
+        FInd Comboox selection from disp_func
+        
+        :param disp_function: dispersion distr. function
+        """
+        # List of the poly_model name in the combobox
+        list = ["RectangleDispersion", "ArrayDispersion",
+                "LogNormalDispersion", "GaussianDispersion",
+                "SchulzDispersion"]
+
+        # Find the selection
+        try:
+            selection = list.index(disp_func.__class__.__name__)
+            return selection
+        except:
+            return 3
+                            
+    def on_reset_clicked(self, event):
+        """
+        On 'Reset' button  for Q range clicked
+        """
+        flag = True
+        ##For 3 different cases: Data2D, Data1D, and theory
+        if self.model == None:
+            msg = "Please select a model first..."
+            wx.MessageBox(msg, 'Info')
+            flag = False
+            return
+            
+        elif self.data.__class__.__name__ == "Data2D":
+            data_min = 0
+            x = max(math.fabs(self.data.xmin), math.fabs(self.data.xmax))
+            y = max(math.fabs(self.data.ymin), math.fabs(self.data.ymax))
+            self.qmin_x = data_min
+            self.qmax_x = math.sqrt(x*x + y*y)
+            #self.data.mask = numpy.ones(len(self.data.data),dtype=bool)
+            # check smearing
+            if not self.disable_smearer.GetValue():
+                ## set smearing value whether or
+                # not the data contain the smearing info
+                if self.pinhole_smearer.GetValue():
+                    flag = self.update_pinhole_smear()
+                else:
+                    flag = True
+                    
+        elif self.data == None:
+            self.qmin_x = _QMIN_DEFAULT
+            self.qmax_x = _QMAX_DEFAULT
+            self.num_points = _NPTS_DEFAULT
+            self.state.npts = self.num_points
+            
+        elif self.data.__class__.__name__ != "Data2D":
+            self.qmin_x = min(self.data.x)
+            self.qmax_x = max(self.data.x)
+            # check smearing
+            if not self.disable_smearer.GetValue():
+                ## set smearing value whether or
+                # not the data contain the smearing info
+                if self.slit_smearer.GetValue():
+                    flag = self.update_slit_smear()
+                elif self.pinhole_smearer.GetValue():
+                    flag = self.update_pinhole_smear()
+                else:
+                    flag = True
+        else:
+            flag = False
+            
+        if flag == False:
+            msg = "Cannot Plot :Must enter a number!!!  "
+            wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
+        else:
+            # set relative text ctrs.
+            self.qmin.SetValue(str(self.qmin_x))
+            self.qmax.SetValue(str(self.qmax_x))
+            self.show_npts2fit()
+            # At this point, some button and variables satatus (disabled?)
+            # should be checked such as color that should be reset to
+            # white in case that it was pink.
+            self._onparamEnter_helper()
+
+        self.save_current_state()
+        self.state.qmin = self.qmin_x
+        self.state.qmax = self.qmax_x
+        
+        #reset the q range values
+        self._reset_plotting_range(self.state)
+        self._draw_model()
+        
+    def select_log(self, event):
+        """
+        Log checked to generate log spaced points for theory model
+        """
+
+    def get_images(self):
+        """
+        Get the images of the plots corresponding this panel for report
+        
+        : return graphs: list of figures
+        : Need Move to guiframe
+        """
+        # set list of graphs
+        graphs = []
+        canvases = []
+        res_item = None
+        # call gui_manager
+        gui_manager = self._manager.parent
+        # loops through the panels [dic]
+        for _, item2 in gui_manager.plot_panels.iteritems():
+            data_title = self.data.group_id
+            # try to get all plots belonging to this control panel
+            try:
+                g_id = item2.group_id
+                if g_id == data_title or \
+                        str(g_id).count("res" + str(self.graph_id)) or \
+                        str(g_id).count(str(self.uid)) > 0:
+                    if str(g_id).count("res" + str(self.graph_id)) > 0:
+                        res_item = [item2.figure, item2.canvas]
+                    else:
+                        # append to the list
+                        graphs.append(item2.figure)
+                        canvases.append(item2.canvas)  
+            except:
+                # Not for control panels
+                pass
+        # Make sure the resduals plot goes to the last
+        if res_item != None:
+            graphs.append(res_item[0])
+            canvases.append(res_item[1])
+        # return the list of graphs
+        return graphs, canvases
+
+    def on_model_help_clicked(self, event):
+        """
+        on 'More details' button
+        """
+        from sas.perspectives.fitting.help_panel import  HelpWindow
+        from sas.models import get_data_path
+        
+        # Get models help model_function path
+        path = get_data_path(media='media')
+        model_path = os.path.join(path, "model_functions.html")
+        if self.model == None:
+            name = 'FuncHelp'
+        else:
+            name = self.formfactorbox.GetValue()
+        frame = HelpWindow(None, -1, pageToOpen=model_path)
+        # If model name exists and model is not a custom model
+        #mod_cat = self.categorybox.GetStringSelection()
+        if frame.rhelp.HasAnchor(name):
+            frame.Show(True)
+            frame.rhelp.ScrollToAnchor(name)
+        else:
+            if self.model != None:
+                frame.Destroy()
+                msg = 'Model description:\n'
+                if str(self.model.description).rstrip().lstrip() == '':
+                    msg += "Sorry, no information is available for this model."
+                else:
+                    msg += self.model.description + '\n'
+                info = "Info"
+                wx.MessageBox(msg, info)
+            else:
+                frame.Show(True)
+
+    def _on_mag_help(self, event):    
+        """
+        Magnetic angles help panel
+        """
+        from sas.perspectives.fitting.help_panel import  HelpWindow
+        # Get models help model_function path
+        #import sas.perspectives.fitting as fitmedia
+        from sas.models import get_data_path
+
+        media = get_data_path(media='media')
+        path = os.path.join(media, "mag_pic.html") 
+        name = "Polar/Magnetic Angles"
+        frame = HelpWindow(None, -1,  
+                           title=' Help: Polarization/Magnetization Angles',  
+                           pageToOpen=path, size=(865, 450))   
+        try: 
+            frame.splitter.DetachWindow(frame.lpanel)
+            # Display only the right side one
+            frame.lpanel.Hide() 
+            frame.Show(True)
+        except:
+            frame.Destroy() 
+            msg = 'Display Error\n'
+            info = "Info"
+            wx.MessageBox(msg, info)
+
+    def _on_mag_on(self, event):    
+        """
+        Magnetic Parameters ON/OFF
+        """
+        button = event.GetEventObject()
+
+        if button.GetLabel().count('ON') > 0:
+            self.magnetic_on = True
+            button.SetLabel("Magnetic OFF")
+            m_value = 1.0e-06
+            for key in self.model.magnetic_params:
+                if key.count('M0') > 0:
+                    self.model.setParam(key, m_value)
+                    m_value += 0.5e-06
+        else:
+            self.magnetic_on = False
+            button.SetLabel("Magnetic ON")
+            for key in self.model.magnetic_params:
+                if key.count('M0') > 0:
+                    #reset mag value to zero fo safety
+                    self.model.setParam(key, 0.0)
+                    
+        self.Show(False)    
+        self.set_model_param_sizer(self.model)
+        #self._set_sizer_dispersion() 
+        self.state.magnetic_on = self.magnetic_on
+        self.SetupScrolling()
+        self.Show(True)
+            
+    def on_pd_help_clicked(self, event):
+        """
+        Button event for PD help
+        """
+        from help_panel import  HelpWindow
+import sas.models as models
+        
+        # Get models help model_function path
+        path = models.get_data_path(media='media')
+        pd_path = os.path.join(path, "pd_help.html")
+
+        frame = HelpWindow(None, -1, pageToOpen=pd_path)
+        frame.Show(True)
+        
+    def on_left_down(self, event):
+        """
+        Get key stroke event
+        """
+        # Figuring out key combo: Cmd for copy, Alt for paste
+        if event.CmdDown() and event.ShiftDown():
+            self.get_paste()
+        elif event.CmdDown():
+            self.get_copy()
+        else:
+            event.Skip()
+            return
+        # make event free
+        event.Skip()
+        
+    def get_copy(self):
+        """
+        Get copy params to clipboard
+        """
+        content = self.get_copy_params()
+        flag = self.set_clipboard(content)
+        self._copy_info(flag)
+        return flag
+            
+    def get_copy_params(self):
+        """
+        Get the string copies of the param names and values in the tap
+        """
+        content = 'sansview_parameter_values:'
+        # Do it if params exist
+        if  self.parameters != []:
+            
+            # go through the parameters
+            strings = self._get_copy_helper(self.parameters,
+                                           self.orientation_params)
+            content += strings
+            
+            # go through the fittables
+            strings = self._get_copy_helper(self.fittable_param,
+                                           self.orientation_params_disp)
+            content += strings
+
+            # go through the fixed params
+            strings = self._get_copy_helper(self.fixed_param,
+                                           self.orientation_params_disp)
+            content += strings
+                
+            # go through the str params
+            strings = self._get_copy_helper(self.str_parameters,
+                                           self.orientation_params)
+            content += strings
+            return content
+        else:
+            return False
+
+    def get_copy_excel(self):
+        """
+        Get copy params to clipboard
+        """
+        content = self.get_copy_params_excel()
+        flag = self.set_clipboard(content)
+        self._copy_info(flag)
+        return flag
+
+    def get_copy_params_excel(self):
+        """
+        Get the string copies of the param names and values in the tap
+        """
+        content = ''
+
+        crlf = chr(13) + chr(10)
+        tab = chr(9)
+
+        # Do it if params exist
+        if  self.parameters != []:
+
+            for param in self.parameters:
+                content += param[1] #parameter name
+                content += tab
+                content += param[1]+"_err"
+                content += tab
+
+            content += crlf
+
+            #row of values and errors...
+            for param in self.parameters:
+                content += param[2].GetValue() #value
+                content +=tab
+                content += param[4].GetValue() #error
+                content +=tab
+
+            return content
+        else:
+            return False
+
+
+    def get_copy_latex(self):
+        """
+        Get copy params to clipboard
+        """
+        content = self.get_copy_params_latex()
+        flag = self.set_clipboard(content)
+        self._copy_info(flag)
+        return flag
+
+    def get_copy_params_latex(self):
+        """
+        Get the string copies of the param names and values in the tap
+        """
+        content = '\\begin{table}'
+        content += '\\begin{tabular}[h]'
+
+        crlf = chr(13) + chr(10)
+        tab = chr(9)
+
+        # Do it if params exist
+        if  self.parameters != []:
+
+            content += '{|'
+            for param in self.parameters:
+                content += 'l|l|'
+            content += '}\hline'
+            content += crlf
+
+            for index, param in enumerate(self.parameters):
+                content += param[1].replace('_','\_') #parameter name
+                content += ' & '
+                content += param[1].replace('_','\_')+"\_err"
+                if index < len(self.parameters)-1:
+                    content += ' & '
+            content += '\\\\ \\hline'
+            content += crlf
+
+            #row of values and errors...
+            for index, param in enumerate(self.parameters):
+                content += param[2].GetValue() #parameter value
+                content += ' & '
+                content += param[4].GetValue() #parameter error
+                if index < len(self.parameters)-1:
+                    content += ' & '
+            content += '\\\\ \\hline'
+            content += crlf
+
+            content += '\\end{tabular}'
+            content += '\\end{table}'
+            return content
+        else:
+            return False
+
+
+    def set_clipboard(self, content=None):
+        """
+        Put the string to the clipboard
+        """
+        if not content:
+            return False
+        if wx.TheClipboard.Open():
+            wx.TheClipboard.SetData(wx.TextDataObject(str(content)))
+            wx.TheClipboard.Close()
+            return True
+        return None
+    
+    def _get_copy_helper(self, param, orient_param):
+        """
+        Helping get value and name of the params
+        
+        : param param:  parameters
+        : param orient_param = oritational params
+        : return content: strings [list] [name,value:....]
+        """
+        content = ''
+        # go through the str params
+        for item in param:
+            # copy only the params shown
+            if not item[2].IsShown():
+                continue
+            disfunc = ''
+            try:
+                if item[7].__class__.__name__ == 'ComboBox':
+                    disfunc = str(item[7].GetValue())
+            except:
+                pass
+            
+            # 2D
+            if self.data.__class__.__name__ == "Data2D":
+                try:
+                    check = item[0].GetValue()
+                except:
+                    check = None
+                name = item[1]
+                value = item[2].GetValue()
+            # 1D
+            else:
+                ## for 1D all parameters except orientation
+                if not item[1] in orient_param:
+                    try:
+                        check = item[0].GetValue()
+                    except:
+                        check = None
+                    name = item[1]
+                    value = item[2].GetValue()
+
+            # add to the content
+            if disfunc != '':
+                
+                disfunc = ',' + disfunc
+            # Need to support array func for copy/paste
+            try:
+                if disfunc.count('array') > 0:
+                    disfunc += ','
+                    for val in self.values[name]:
+                        disfunc += ' ' + str(val)
+                    disfunc += ','
+                    for weight in self.weights[name]:
+                        disfunc += ' ' + str(weight)
+            except:
+                pass
+            content += name + ',' + str(check) + ',' + value + disfunc + ':'
+
+        return content
+   
+    def get_clipboard(self):
+        """
+        Get strings in the clipboard
+        """
+        text = ""
+        # Get text from the clip board
+        if wx.TheClipboard.Open():
+            if wx.TheClipboard.IsSupported(wx.DataFormat(wx.DF_TEXT)):
+                data = wx.TextDataObject()
+                # get wx dataobject
+                success = wx.TheClipboard.GetData(data)
+                # get text
+                if success:
+                    text = data.GetText()
+                else:
+                    text = ''
+            # close clipboard
+            wx.TheClipboard.Close()
+        return text
+    
+    def get_paste(self):
+        """
+        Paste params from the clipboard
+        """
+        text = self.get_clipboard()
+        flag = self.get_paste_params(text)
+        self._copy_info(flag)
+        return flag
+        
+    def get_paste_params(self, text=''):
+        """
+        Get the string copies of the param names and values in the tap
+        """
+        context = {}
+        # put the text into dictionary
+        lines = text.split(':')
+        if lines[0] != 'sansview_parameter_values':
+            self._copy_info(False)
+            return False
+        for line in lines[1:-1]:
+            if len(line) != 0:
+                item = line.split(',')
+                check = item[1]
+                name = item[0]
+                value = item[2]
+                # Transfer the text to content[dictionary]
+                context[name] = [check, value]
+            # ToDo: PlugIn this poly disp function for pasting
+            try:
+                poly_func = item[3]
+                context[name].append(poly_func)
+                try:
+                    # take the vals and weights for  array
+                    array_values = item[4].split(' ')
+                    array_weights = item[5].split(' ')
+                    val = [float(a_val) for a_val in array_values[1:]]
+                    weit = [float(a_weit) for a_weit in array_weights[1:]]
+                    
+                    context[name].append(val)
+                    context[name].append(weit)
+                except:
+                    raise
+            except:
+                poly_func = ''
+                context[name].append(poly_func)
+
+        # Do it if params exist
+        if  self.parameters != []:
+            # go through the parameters
+            self._get_paste_helper(self.parameters,
+                                   self.orientation_params, context)
+
+            # go through the fittables
+            self._get_paste_helper(self.fittable_param,
+                                   self.orientation_params_disp,
+                                   context)
+
+            # go through the fixed params
+            self._get_paste_helper(self.fixed_param,
+                                   self.orientation_params_disp, context)
+            
+            # go through the str params
+            self._get_paste_helper(self.str_parameters,
+                                   self.orientation_params, context)
+                
+            return True
+        return None
+    
+    def _get_paste_helper(self, param, orient_param, content):
+        """
+        Helping set values of the params
+        
+        : param param:  parameters
+        : param orient_param: oritational params
+        : param content: dictionary [ name, value: name1.value1,...]
+        """
+        # go through the str params
+        for item in param:
+            # 2D
+            if self.data.__class__.__name__ == "Data2D":
+                name = item[1]
+                if name in content.keys():
+                    check = content[name][0]
+                    pd = content[name][1]
+                    if name.count('.') > 0:
+                        try:
+                            float(pd)
+                        except:
+                            #continue
+                            if not pd and pd != '':
+                                continue
+                    item[2].SetValue(str(pd))
+                    if item in self.fixed_param and pd == '':
+                        # Only array func has pd == '' case.
+                        item[2].Enable(False)
+                    if item[2].__class__.__name__ == "ComboBox":
+                        if content[name][1] in self.model.fun_list:
+                            fun_val = self.model.fun_list[content[name][1]]
+                            self.model.setParam(name, fun_val)
+                    
+                    value = content[name][1:]
+                    self._paste_poly_help(item, value)
+                    if check == 'True':
+                        is_true = True
+                    elif check == 'False':
+                        is_true = False
+                    else:
+                        is_true = None
+                    if is_true != None:
+                        item[0].SetValue(is_true)
+            # 1D
+            else:
+                ## for 1D all parameters except orientation
+                if not item[1] in orient_param:
+                    name = item[1]
+                    if name in content.keys():
+                        check = content[name][0]
+                        # Avoid changing combox content
+                        value = content[name][1:]
+                        pd = value[0]
+                        if name.count('.') > 0:
+                            try:
+                                pd = float(pd)
+                            except:
+                                #continue
+                                if not pd and pd != '':
+                                    continue
+                        item[2].SetValue(str(pd))
+                        if item in self.fixed_param and pd == '':
+                            # Only array func has pd == '' case.
+                            item[2].Enable(False)
+                        if item[2].__class__.__name__ == "ComboBox":
+                            if value[0] in self.model.fun_list:
+                                fun_val = self.model.fun_list[value[0]]
+                                self.model.setParam(name, fun_val)
+                                # save state
+                        self._paste_poly_help(item, value)
+                        if check == 'True':
+                            is_true = True
+                        elif check == 'False':
+                            is_true = False
+                        else:
+                            is_true = None
+                        if is_true != None:
+                            item[0].SetValue(is_true)
+                        
+    def _paste_poly_help(self, item, value):
+        """
+        Helps get paste for poly function
+        
+        :param item: Gui param items
+        :param value: the values for parameter ctrols
+        """
+        is_array = False
+        if len(value[1]) > 0:
+            # Only for dispersion func.s
+            try:
+                item[7].SetValue(value[1])
+                selection = item[7].GetCurrentSelection()
+                name = item[7].Name
+                param_name = name.split('.')[0]
+                dispersity = item[7].GetClientData(selection)
+                disp_model = dispersity()
+                # Only for array disp
+                try:
+                    pd_vals = numpy.array(value[2])
+                    pd_weights = numpy.array(value[3])
+                    if len(pd_vals) > 0 and len(pd_vals) > 0:
+                        if len(pd_vals) == len(pd_weights):
+                            self._set_disp_array_cb(item=item)
+                            self._set_array_disp_model(name=name,
+                                                       disp=disp_model,
+                                                       values=pd_vals,
+                                                       weights=pd_weights)
+                            is_array = True
+                except:
+                    pass
+                if not is_array:
+                    self._disp_obj_dict[name] = disp_model
+                    self.model.set_dispersion(name,
+                                              disp_model)
+                    self.state._disp_obj_dict[name] = \
+                                              disp_model
+                    self.model.set_dispersion(param_name, disp_model)
+                    self.state.values = self.values
+                    self.state.weights = self.weights
+                    self.model._persistency_dict[param_name] = \
+                                            [self.state.values,
+                                             self.state.weights]
+                         
+            except:
+                print "Error in BasePage._paste_poly_help: %s" % sys.exc_value
+    
+    def _set_disp_array_cb(self, item):
+        """
+        Set cb for array disp
+        """
+        item[0].SetValue(False)
+        item[0].Enable(False)
+        item[2].Enable(False)
+        item[3].Show(False)
+        item[4].Show(False)
+        item[5].SetValue('')
+        item[5].Enable(False)
+        item[6].SetValue('')
+        item[6].Enable(False)
+        
+    def update_pinhole_smear(self):
+        """
+            Method to be called by sub-classes
+            Moveit; This method doesn't belong here
+        """
+        print "BasicPage.update_pinhole_smear was called: skipping"
+        return
+
+
+
+
+    def _read_category_info(self):
+        """
+        Reads the categories in from file
+        """
+
+        # # ILL mod starts here - July 2012 kieranrcampbell@gmail.com
+        self.master_category_dict = defaultdict(list)
+        self.by_model_dict = defaultdict(list)
+        self.model_enabled_dict = defaultdict(bool)
+
+        try:
+            categorization_file = CategoryInstaller.get_user_file()
+            if not os.path.isfile(categorization_file):
+                categorization_file = CategoryInstaller.get_default_file()
+            cat_file = open(categorization_file, 'rb')                           
+            self.master_category_dict = json.load(cat_file)
+            self._regenerate_model_dict()
+            cat_file.close()
+
+        except IOError:
+            raise
+            print 'Problem reading in category file.'
+            print 'We even looked for it, made sure it was there.'
+            print 'An existential crisis if there ever was one.'
+
+    def _regenerate_model_dict(self):
+        """
+        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
+        """
+
+        self.by_model_dict = defaultdict(list)
+        for category in self.master_category_dict:
+            for (model, enabled) in self.master_category_dict[category]:
+                self.by_model_dict[model].append(category)
+                self.model_enabled_dict[model] = enabled
+    
+    def _populate_listbox(self):
+        """
+        fills out the category list box
+        """
+        uncat_str = 'Customized Models'
+        self._read_category_info()
+
+        self.categorybox.Clear()
+        cat_list = sorted(self.master_category_dict.keys())
+        if not uncat_str in cat_list:
+            cat_list.append(uncat_str)
+        
+        for category in cat_list:
+            if category != '':
+                self.categorybox.Append(category)
+
+        if self.categorybox.GetSelection() == wx.NOT_FOUND:
+            self.categorybox.SetSelection(0)
+        else:
+            self.categorybox.SetSelection( \
+                self.categorybox.GetSelection())
+
+        #self._on_change_cat(None)
+
+
+    def _on_change_cat(self, event):
+        """
+        Callback for category change action
+        """
+        self.model_name = None
+        category = self.categorybox.GetStringSelection()
+        if category == None:
+            return
+        self.model_box.Clear()
+
+        if category == 'Customized Models':
+            for model in self.model_list_box[category]:
+                str_m = str(model).split(".")[0]
+                self.model_box.Append(str_m)
+
+        else:
+            for (model,enabled) in sorted(self.master_category_dict[category],
+                                      key = lambda name: name[0]):
+                if(enabled):
+                    self.model_box.Append(model)
+
+
+
+
+    def _fill_model_sizer(self, sizer):
+        """
+        fill sizer containing model info
+        """
+        ##Add model function Details button in fitpanel.
+        ##The following 3 lines are for Mac. Let JHC know before modifying...
+        title = "Model"
+        self.formfactorbox = None
+        self.multifactorbox = None
+        self.mbox_description = wx.StaticBox(self, -1, str(title))
+        boxsizer1 = wx.StaticBoxSizer(self.mbox_description, wx.VERTICAL)
+        sizer_cat = wx.BoxSizer(wx.HORIZONTAL)
+        self.mbox_description.SetForegroundColour(wx.RED)
+        id = wx.NewId()
+        self.model_help = wx.Button(self, id, 'Details', size=(80, 23))
+        self.model_help.Bind(wx.EVT_BUTTON, self.on_model_help_clicked, id=id)
+        self.model_help.SetToolTipString("Model Function Help")
+        id = wx.NewId()
+        self.model_view = wx.Button(self, id, "Show 2D", size=(80, 23))
+        self.model_view.Bind(wx.EVT_BUTTON, self._onModel2D, id=id)
+        hint = "toggle view of model from 1D to 2D  or 2D to 1D"
+        self.model_view.SetToolTipString(hint)
+        
+        cat_set_box = wx.StaticBox(self, -1, 'Category')
+        sizer_cat_box = wx.StaticBoxSizer(cat_set_box, wx.HORIZONTAL)
+        sizer_cat_box.SetMinSize((200, 50))
+        self.categorybox = wx.ComboBox(self, -1, style=wx.CB_READONLY)
+        self.categorybox.SetToolTip( wx.ToolTip("Select a Category/Type") )
+        self._populate_listbox()
+        wx.EVT_COMBOBOX(self.categorybox, -1, self._show_combox)
+        #self.shape_rbutton = wx.RadioButton(self, -1, 'Shapes',
+        #                                     style=wx.RB_GROUP)
+        #self.shape_indep_rbutton = wx.RadioButton(self, -1,
+        #                                          "Shape-Independent")
+        #self.struct_rbutton = wx.RadioButton(self, -1, "Structure Factor ")
+        #self.plugin_rbutton = wx.RadioButton(self, -1, "Uncategorized")
+                
+        #self.Bind(wx.EVT_RADIOBUTTON, self._show_combox,
+        #                   id=self.shape_rbutton.GetId())
+        #self.Bind(wx.EVT_RADIOBUTTON, self._show_combox,
+        #                    id=self.shape_indep_rbutton.GetId())
+        #self.Bind(wx.EVT_RADIOBUTTON, self._show_combox,
+        #                    id=self.struct_rbutton.GetId())
+        #self.Bind(wx.EVT_RADIOBUTTON, self._show_combox,
+        #                    id=self.plugin_rbutton.GetId())
+        #MAC needs SetValue
+        
+        show_cat_button = wx.Button(self, -1, "Modify")
+        cat_tip = "Modify model categories \n"
+        cat_tip += "(also accessible from the menu bar)."
+        show_cat_button.SetToolTip( wx.ToolTip(cat_tip) )
+        show_cat_button.Bind(wx.EVT_BUTTON, self._on_modify_cat)
+        sizer_cat_box.Add(self.categorybox, 1, wx.RIGHT, 3)
+        sizer_cat_box.Add((10,10))
+        sizer_cat_box.Add(show_cat_button)
+        #self.shape_rbutton.SetValue(True)
+      
+        sizer_radiobutton = wx.GridSizer(2, 2, 5, 5)
+
+        #sizer_radiobutton.Add(self.shape_rbutton)
+        #sizer_radiobutton.Add(self.shape_indep_rbutton)
+        sizer_radiobutton.Add((5,5))
+        sizer_radiobutton.Add(self.model_view, 1, wx.RIGHT, 15)
+        #sizer_radiobutton.Add(self.plugin_rbutton)
+        #sizer_radiobutton.Add(self.struct_rbutton)
+        sizer_radiobutton.Add((5,5))
+        sizer_radiobutton.Add(self.model_help, 1, wx.RIGHT, 15)
+        sizer_cat.Add(sizer_cat_box, 1, wx.LEFT, 2.5)
+        sizer_cat.Add(sizer_radiobutton)
+        sizer_selection = wx.BoxSizer(wx.HORIZONTAL)
+        mutifactor_selection = wx.BoxSizer(wx.HORIZONTAL)
+        
+        self.text1 = wx.StaticText(self, -1, "")
+        self.text2 = wx.StaticText(self, -1, "P(Q)*S(Q)")
+        self.mutifactor_text = wx.StaticText(self, -1, "No. of Shells: ")
+        self.mutifactor_text1 = wx.StaticText(self, -1, "")
+        self.show_sld_button = wx.Button(self, -1, "Show SLD Profile")
+        self.show_sld_button.Bind(wx.EVT_BUTTON, self._on_show_sld)
+
+        self.formfactorbox = wx.ComboBox(self, -1, style=wx.CB_READONLY)
+        self.formfactorbox.SetToolTip( wx.ToolTip("Select a Model") )
+        if self.model != None:
+            self.formfactorbox.SetValue(self.model.name)
+        self.structurebox = wx.ComboBox(self, -1, style=wx.CB_READONLY)
+        self.multifactorbox = wx.ComboBox(self, -1, style=wx.CB_READONLY)
+        self.initialize_combox()
+        wx.EVT_COMBOBOX(self.formfactorbox, -1, self._on_select_model)
+
+        wx.EVT_COMBOBOX(self.structurebox, -1, self._on_select_model)
+        wx.EVT_COMBOBOX(self.multifactorbox, -1, self._on_select_model)
+        ## check model type to show sizer
+        if self.model != None:
+            print "_set_model_sizer_selection: disabled."
+            #self._set_model_sizer_selection(self.model)
+        
+        sizer_selection.Add(self.text1)
+        sizer_selection.Add((10, 5))
+        sizer_selection.Add(self.formfactorbox)
+        sizer_selection.Add((5, 5))
+        sizer_selection.Add(self.text2)
+        sizer_selection.Add((5, 5))
+        sizer_selection.Add(self.structurebox)
+       
+        mutifactor_selection.Add((13, 5))
+        mutifactor_selection.Add(self.mutifactor_text)
+        mutifactor_selection.Add(self.multifactorbox)
+        mutifactor_selection.Add((5, 5))
+        mutifactor_selection.Add(self.mutifactor_text1)
+        mutifactor_selection.Add((10, 5))
+        mutifactor_selection.Add(self.show_sld_button)
+
+        boxsizer1.Add(sizer_cat)
+        boxsizer1.Add((10, 10))
+        boxsizer1.Add(sizer_selection)
+        boxsizer1.Add((10, 10))
+        boxsizer1.Add(mutifactor_selection)
+        
+        self._set_multfactor_combobox()
+        self.multifactorbox.SetSelection(1)
+        self.show_sld_button.Hide()
+        sizer.Add(boxsizer1, 0, wx.EXPAND | wx.ALL, 10)
+        sizer.Layout()
+        
+    def on_smear_helper(self, update=False):
+        """
+        Help for onSmear if implemented
+        
+        :param update: force or not to update
+        """
+    def reset_page(self, state, first=False):
+        """
+        reset the state  if implemented
+        """
+    def onSmear(self, event):
+        """
+        Create a smear object if implemented 
+        """
+    def onPinholeSmear(self, event):
+        """
+        Create a custom pinhole smear object if implemented
+        """
+    def onSlitSmear(self, event):
+        """
+        Create a custom slit smear object if implemented
+        """
+    def update_slit_smear(self):
+        """
+        called by kill_focus on pinhole TextCntrl
+        to update the changes if implemented
+        """
+    def select_param(self, event):
+        """
+        Select TextCtrl  checked if implemented
+        """
+    def set_data(self, data=None):
+        """
+        Sets data if implemented
+        """
+    def _is_2D(self):
+        """
+        Check if data_name is Data2D if implemented
+        """
+    def _on_select_model(self, event=None):
+        """
+        call back for model selection if implemented
+        """
+    def select_all_param(self, event):
+        """
+        set to true or false all checkBox if implemented
+        """
+    def get_weight_flag(self):
+        """
+        Get flag corresponding to a given weighting dI data if implemented
+        """
+    def _set_sizer_dispersion(self):
+        """
+        draw sizer for dispersity if implemented
+        """
+    def get_all_checked_params(self):
+        """
+        Found all parameters current check and add them to list of parameters
+        to fit if implemented
+        """
+    def show_npts2fit(self):
+        """
+        setValue Npts for fitting if implemented
+        """
+    def _onModel2D(self, event):
+        """
+        toggle view of model from 1D to 2D  or 2D from 1D if implemented
+        """
Index: src/sas/perspectives/fitting/batchfitpage.py
===================================================================
--- src/sas/perspectives/fitting/batchfitpage.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/fitting/batchfitpage.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,390 @@
+""" 
+Batch panel
+"""
+import wx
+import wx.lib.newevent
+import math
+from sas.guiframe.events import StatusEvent    
+from sas.guiframe.events import NewPlotEvent  
+
+(Chi2UpdateEvent, EVT_CHI2_UPDATE)   = wx.lib.newevent.NewEvent()
+_BOX_WIDTH = 76
+_DATA_BOX_WIDTH = 300
+SMEAR_SIZE_L = 0.00
+SMEAR_SIZE_H = 0.00
+
+from sas.perspectives.fitting.basepage import PageInfoEvent
+from sas.models.qsmearing import smear_selection
+from sas.perspectives.fitting.fitpage import FitPage
+
+class BatchFitPage(FitPage):
+    """
+    Batch Page
+    """
+    window_name = "BatchFit"
+    window_caption  = "BatchFit"
+
+    def __init__(self, parent, color=None):
+        """ 
+        Initialization of the Panel
+        """
+        FitPage.__init__(self, parent, color=color)
+        self.window_name = "BatchFit"
+        self.window_caption  = "BatchFit"
+        self._set_save_flag(False)
+        self._set_bookmark_flag(False)
+        
+    def _fill_range_sizer(self):
+        """
+        Fill the sizer containing the plotting range
+        add  access to npts
+        """
+        is_2Ddata = False
+        
+        # Check if data is 2D
+        if self.data.__class__.__name__ ==  "Data2D" or \
+                        self.enable2D:
+            is_2Ddata = True
+            
+        title = "Fitting"     
+        self._get_smear_info()
+        
+        #Sizers
+        box_description_range = wx.StaticBox(self, -1, str(title))
+        boxsizer_range = wx.StaticBoxSizer(box_description_range, wx.VERTICAL)      
+        self.sizer_set_smearer = wx.BoxSizer(wx.VERTICAL)
+        #sizer_smearer = wx.BoxSizer(wx.HORIZONTAL)
+        self.sizer_new_smear = wx.BoxSizer(wx.HORIZONTAL)
+        self.sizer_set_masking = wx.BoxSizer(wx.HORIZONTAL)
+        sizer_chi2 = wx.BoxSizer(wx.VERTICAL)
+
+        sizer_fit = wx.GridSizer(2, 4, 2, 6)
+        #Fit button
+        self.btFit = wx.Button(self, wx.NewId(), 'Fit', size=(88, 25))
+        self.default_bt_colour =  self.btFit.GetDefaultAttributes()
+        self.btFit.Bind(wx.EVT_BUTTON, self._onFit, id= self.btFit.GetId())
+        self.btFit.SetToolTipString("Start fitting.")
+
+        # Update and Draw button
+        self.draw_button = wx.Button(self, wx.NewId(), 'Compute', size=(88, 24))
+        self.draw_button.Bind(wx.EVT_BUTTON, \
+                              self._onDraw,id=self.draw_button.GetId())
+        self.draw_button.SetToolTipString("Compute and Draw.")  
+        sizer_fit.Add(self.draw_button, 0, 0)
+        sizer_fit.Add(self.btFit, 0, 0) 
+        sizer_chi2.Add((-1, 5))
+        # get smear_selection
+        self.current_smearer = smear_selection( self.data, self.model )
+        boxsizer_range.Add(self.sizer_set_masking)
+         #2D data? default
+        is_2Ddata = False
+        
+        #check if it is 2D data
+        if self.data.__class__.__name__ ==  "Data2D" or \
+                        self.enable2D:
+            is_2Ddata = True
+            
+        self.sizer5.Clear(True)
+     
+        self.qmin  = self.ModelTextCtrl(self, -1, size=(_BOX_WIDTH, 20), 
+                                          style=wx.TE_PROCESS_ENTER, 
+                                    text_enter_callback = self._onQrangeEnter)
+        self.qmin.SetValue(str(self.qmin_x))
+        self.qmin.SetToolTipString("Minimun value of Q in linear scale.")
+     
+        self.qmax  = self.ModelTextCtrl(self, -1, size=(_BOX_WIDTH, 20), 
+                                          style=wx.TE_PROCESS_ENTER, 
+                                        text_enter_callback=self._onQrangeEnter)
+        self.qmax.SetValue(str(self.qmax_x))
+        self.qmax.SetToolTipString("Maximum value of Q in linear scale.")
+        
+        id = wx.NewId()
+        self.reset_qrange =wx.Button(self, id, 'Reset', size=(77, 20))
+      
+        self.reset_qrange.Bind(wx.EVT_BUTTON, self.on_reset_clicked, id=id)
+        self.reset_qrange.SetToolTipString(\
+                                    "Reset Q range to the default values")
+     
+        sizer_horizontal = wx.BoxSizer(wx.HORIZONTAL)
+        sizer = wx.GridSizer(2, 4, 2, 6)
+
+        self.btEditMask = wx.Button(self, wx.NewId(),'Editor', size=(88, 23))
+        self.btEditMask.Bind(wx.EVT_BUTTON, 
+                             self._onMask,id=self.btEditMask.GetId())
+        self.btEditMask.SetToolTipString("Edit Mask.")
+        self.EditMask_title = wx.StaticText(self, -1, ' Masking(2D)')
+
+        sizer.Add(wx.StaticText(self, -1, 'Q range'))     
+        sizer.Add(wx.StaticText(self, -1, ' Min[1/A]'))
+        sizer.Add(wx.StaticText(self, -1, ' Max[1/A]'))
+        sizer.Add(self.EditMask_title)
+ 
+        sizer.Add(self.reset_qrange)   
+        sizer.Add(self.qmin)
+        sizer.Add(self.qmax)
+
+        sizer.Add(self.btEditMask)
+        boxsizer_range.Add(sizer_chi2) 
+        boxsizer_range.Add((10, 10))
+        boxsizer_range.Add(sizer)
+        
+        boxsizer_range.Add((10, 15))
+        boxsizer_range.Add(sizer_fit)
+        if is_2Ddata:
+            self.btEditMask.Enable()  
+            self.EditMask_title.Enable() 
+        else:
+            self.btEditMask.Disable()  
+            self.EditMask_title.Disable()
+
+        ## save state
+        #self.save_current_state()
+
+        self.sizer5.Add(boxsizer_range, 0, wx.EXPAND | wx.ALL, 10)
+        self.sizer5.Layout()
+       
+    def _on_select_model(self, event=None): 
+        """
+        call back for model selection
+        """  
+        
+        self.Show(False)    
+        self._on_select_model_helper() 
+        self.set_model_param_sizer(self.model)                   
+        if self.model is None:
+            self._set_bookmark_flag(False)
+            self._keep.Enable(False)
+            self._set_save_flag(False)
+        self.enable_disp.SetValue(False)
+        self.disable_disp.SetValue(True)
+        try:
+            self.set_dispers_sizer()
+        except:
+            pass
+        self.state.structurecombobox = self.structurebox.GetCurrentSelection()
+        self.state.formfactorcombobox = self.formfactorbox.GetCurrentSelection()
+      
+        if self.model != None:
+            self._set_copy_flag(True)
+            self._set_paste_flag(True)
+            if self.data != None:
+                self._set_bookmark_flag(False)
+                self._keep.Enable(False)
+                
+            temp_smear = None
+            ## event to post model to fit to fitting plugins
+            (ModelEventbox, _) = wx.lib.newevent.NewEvent()
+         
+            ## set smearing value whether or not 
+            #    the data contain the smearing info
+            evt = ModelEventbox(model=self.model, 
+                                        smearer=temp_smear, 
+                                        qmin=float(self.qmin_x),
+                                        uid=self.uid,
+                                     qmax=float(self.qmax_x)) 
+   
+            self._manager._on_model_panel(evt=evt)
+            self.mbox_description.SetLabel("Model [%s]" % str(self.model.name))
+            self.state.model = self.model.clone()
+            self.state.model.name = self.model.name
+
+            
+        if event != None:
+            ## post state to fit panel
+            new_event = PageInfoEvent(page = self)
+            wx.PostEvent(self.parent, new_event) 
+            #update list of plugins if new plugin is available
+            if self.plugin_rbutton.GetValue():
+                temp = self.parent.update_model_list()
+                if temp:
+                    self.model_list_box = temp
+                    current_val = self.formfactorbox.GetValue()
+                    pos = self.formfactorbox.GetSelection()
+                    self._show_combox_helper()
+                    self.formfactorbox.SetSelection(pos)
+                    self.formfactorbox.SetValue(current_val)
+            self._onDraw(event=None)
+        else:
+            self._draw_model()
+        self.SetupScrolling()
+        self.Show(True)   
+        
+    def _update_paramv_on_fit(self):
+        """
+        make sure that update param values just before the fitting
+        """
+        #flag for qmin qmax check values
+        flag = True
+        self.fitrange = True
+        is_modified = False
+
+        if self.model != None:           
+            ##Check the values
+            self._check_value_enter( self.fittable_param, is_modified)
+            self._check_value_enter( self.fixed_param, is_modified)
+            self._check_value_enter( self.parameters, is_modified)
+
+            # If qmin and qmax have been modified, update qmin and qmax and 
+             # Here we should check whether the boundaries have been modified.
+            # If qmin and qmax have been modified, update qmin and qmax and 
+            # set the is_modified flag to True
+            self.fitrange = self._validate_qrange(self.qmin, self.qmax)
+            if self.fitrange:
+                tempmin = float(self.qmin.GetValue())
+                if tempmin != self.qmin_x:
+                    self.qmin_x = tempmin
+                tempmax = float(self.qmax.GetValue())
+                if tempmax != self.qmax_x:
+                    self.qmax_x = tempmax
+                if tempmax == tempmin:
+                    flag = False    
+                #temp_smearer = None
+                if self._is_2D():
+                    # only 2D case set mask  
+                    flag = self._validate_Npts()
+                    if not flag:
+                        return flag
+            else: flag = False
+        else: 
+            flag = False
+
+        #For invalid q range, disable the mask editor and fit button, vs.    
+        if not self.fitrange:
+            #self.btFit.Disable()
+            if self._is_2D():
+                self.btEditMask.Disable()
+        else:
+            #self.btFit.Enable(True)
+            if self._is_2D() and  self.data != None:
+                self.btEditMask.Enable(True)
+
+        if not flag:
+            msg = "Cannot Plot or Fit :Must select a "
+            msg += " model or Fitting range is not valid!!!  "
+            wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
+        
+        self.save_current_state()
+   
+        return flag  
+    def save_current_state(self):
+        """
+        Currently no save option implemented for batch page
+        """
+        pass 
+    def save_current_state_fit(self):
+        """
+        Currently no save option implemented for batch page
+        """
+        pass
+    def set_data(self, data):
+        """
+        reset the current data 
+        """
+        #id = None
+        group_id = None
+        flag = False
+        if self.data is None and data is not None:
+            flag = True
+        if data is not None:
+            #id = data.id
+            group_id = data.group_id
+            if self.data is not None:
+                flag = (data.id != self.data.id)
+        self.data = data
+        if self.data is None:
+            data_min = ""
+            data_max = ""
+            data_name = ""
+            self._set_bookmark_flag(False)
+            self._keep.Enable(False)
+            self._set_save_flag(False)
+        else:
+            if self.model != None:
+                self._set_bookmark_flag(False)
+                self._keep.Enable(False)
+            self._set_save_flag(False)
+            self._set_preview_flag(True)
+  
+            self.formfactorbox.Enable()
+            self.structurebox.Enable()
+            data_name = self.data.name
+            #set maximum range for x in linear scale
+            if not hasattr(self.data,"data"): #Display only for 1D data fit
+                # Minimum value of data   
+                data_min = min(self.data.x)
+                # Maximum value of data  
+                data_max = max(self.data.x)
+                self.btEditMask.Disable()  
+                self.EditMask_title.Disable()
+            else:
+                
+                ## Minimum value of data 
+                data_min = 0
+                x = max(math.fabs(self.data.xmin), math.fabs(self.data.xmax)) 
+                y = max(math.fabs(self.data.ymin), math.fabs(self.data.ymax))
+                ## Maximum value of data  
+                data_max = math.sqrt(x*x + y*y)
+                self.btEditMask.Enable()  
+                self.EditMask_title.Enable() 
+
+        self.dataSource.SetValue(data_name)
+        self.qmin_x = data_min
+        self.qmax_x = data_max
+        #self.minimum_q.SetValue(str(data_min))
+        #self.maximum_q.SetValue(str(data_max))
+        self.qmin.SetValue(str(data_min))
+        self.qmax.SetValue(str(data_max))
+        self.qmin.SetBackgroundColour("white")
+        self.qmax.SetBackgroundColour("white")
+        self.state.data = data
+        self.state.qmin = self.qmin_x
+        self.state.qmax = self.qmax_x
+        
+        #update model plot with new data information
+        if flag:
+            #set model view button
+            if self.data.__class__.__name__ == "Data2D":
+                self.enable2D = True
+                self.model_view.SetLabel("2D Mode")
+            else:
+                self.enable2D = False
+                self.model_view.SetLabel("1D Mode")
+                
+            self.model_view.Disable()
+            
+            wx.PostEvent(self._manager.parent, 
+                             NewPlotEvent(group_id=group_id,
+                                               action="delete"))
+            #plot the current selected data
+            wx.PostEvent(self._manager.parent, NewPlotEvent(plot=self.data, 
+                                                    title=str(self.data.title)))
+            self._manager.store_data(uid=self.uid, data=data,
+                                     data_list=self.data_list,
+                                      caption=self.window_name)
+            self._draw_model()
+    
+
+        
+class BGTextCtrl(wx.TextCtrl):
+    """
+    Text control used to display outputs.
+    No editing allowed. The background is 
+    grayed out. User can't select text.
+    """
+    def __init__(self, *args, **kwds):
+        wx.TextCtrl.__init__(self, *args, **kwds)
+        self.SetEditable(False)
+        self.SetBackgroundColour(self.GetParent().parent.GetBackgroundColour())
+        
+        # Bind to mouse event to avoid text highlighting
+        # The event will be skipped once the call-back
+        # is called.
+        self.Bind(wx.EVT_MOUSE_EVENTS, self._click)
+        
+    def _click(self, event):
+        """
+        Prevent further handling of the mouse event
+        by not calling Skip().
+        """ 
+        pass
+ 
Index: src/sas/perspectives/fitting/console.py
===================================================================
--- src/sas/perspectives/fitting/console.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/fitting/console.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,183 @@
+
+
+
+from sas.guiframe.events import StatusEvent 
+import time
+import wx
+from sas.fit import FitHandler
+
+class ConsoleUpdate(FitHandler):
+    """
+    Print progress to the console.
+    """
+    isbetter = False
+    """Record whether results improved since last update"""
+    progress_delta =  60
+    """Number of seconds between progress updates"""
+    improvement_delta = 5
+    """Number of seconds between improvement updates"""
+    def __init__(self, parent, manager=None,
+                 quiet=False,progress_delta=60,improvement_delta=5):
+        """
+        If quiet is true, only print out final summary, not progress and
+        improvements.
+        
+        :attr parent: the object that handle the messages
+        
+        """
+        self.parent= parent
+        self.manager = manager
+        self.progress_time = time.time()
+        self.progress_percent = 0
+        self.improvement_time = self.progress_time
+        self.isbetter = False
+        self.quiet = quiet
+        self.progress_delta = progress_delta
+        self.improvement_delta = improvement_delta
+        self.elapsed_time = time.time()
+        self.update_duration = time.time()
+        self.fit_duration = 0
+        
+        
+    def progress(self, k, n):
+        """
+        Report on progress.
+        """
+        if self.quiet: return
+
+        t = time.time()
+        p = int((100*k)//n)
+        
+        # Show improvements if there are any
+        dt = t - self.improvement_time
+        if self.isbetter and dt > self.improvement_delta:
+            #self.result.print_summary()
+            self.update_fit()
+            self.isbetter = False
+            self.improvement_time = t
+       
+        # Update percent complete
+        dp = p-self.progress_percent
+        if dp < 1: return
+        dt = t - self.progress_time
+        if dt > self.progress_delta:
+            if 1 <= dp <= 2:
+                self.progress_percent = p
+                self.progress_time = t
+                self.update_fit()
+            elif 2 < dp <= 5:
+                if p//5 != self.progress_percent//5:
+                    self.progress_percent = p
+                    self.progress_time = t
+            else:
+                if p//10 != self.progress_percent//10:
+                    self.progress_percent = p
+                    self.progress_time = t
+                    self.update_fit()
+        
+    def improvement(self):
+        """
+        Called when a result is observed which is better than previous
+        results from the fit.
+        """
+        self.isbetter = True
+    
+    def print_result(self):
+        """
+        Print result object
+        """
+        msg = " \n %s \n" % str(self.result)
+        wx.PostEvent(self.parent, StatusEvent(status=msg))
+                     
+    def error(self, msg):
+        """
+        Model had an error; print traceback
+        """
+        if self.isbetter:
+            #self.result.print_summary()
+            self.update_fit()
+
+        message = str(msg) + " \n %s \n" % self.result.__str__()
+        wx.PostEvent(self.parent, StatusEvent(status=message,
+                                   info="error", type="stop"))
+    def stop(self, msg):
+        """
+        Post event msg and stop
+        """
+        if self.isbetter:
+            #self.result.print_summary()
+            self.update_fit()
+
+        message = str(msg) + " \n %s \n" % self.result.__str__()
+        wx.PostEvent(self.parent, StatusEvent(status=message,
+                                   info="info", type="stop"))
+        
+    def finalize(self):
+        """
+        """
+        if self.isbetter:
+            #self.result.print_summary()
+            self.update_fit()
+
+    def abort(self):
+        """
+        """
+        if self.isbetter:
+            #self.result.print_summary()
+            self.update_fit()
+            
+        
+    def update_fit(self, last=False):
+        """
+        """
+        t1 = time.time()
+        self.elapsed_time =  t1 - self.update_duration
+        self.update_duration = t1
+        self.fit_duration += self.elapsed_time
+        str_time = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(t1))
+        UPDATE_INTERVAL = 5.0
+        u_flag = False
+        if self.fit_duration >= UPDATE_INTERVAL:
+            self.fit_duration = 0
+            u_flag = True
+        msg = str_time
+        if u_flag or last:
+            if self.result is not None:
+                data_name, model_name = None, None
+                d_flag = (hasattr(self.result, "data") and \
+                    self.result.data is not None and \
+                    hasattr(self.result.data, "sans_data") and 
+                    self.result.data.sans_data is not None)
+                m_flag = (hasattr(self.result, "model") and \
+                          self.result.model is not None)
+                if d_flag:
+                    data_name = self.result.data.sans_data.name
+                if m_flag:
+                    model_name = str(self.result.model.name)
+                if m_flag and d_flag:
+                    msg += "Data : %s \n"  % (str(data_name))#,
+                                                     #str(model_name))
+                msg +=  str(self.result)
+                msg += "\n"
+            else:
+                msg += "No result available\n"
+            wx.PostEvent(self.parent, StatusEvent(status=msg, info="info",
+                                              type="progress"))
+     
+    def starting_fit(self):
+        """
+        """
+        wx.PostEvent(self.parent, StatusEvent(status="Starting the Fit...",
+                                        info="info",type="progress"))
+        
+    def set_result(self, result):
+        """
+        """
+        self.result = result
+    
+    def get_result(self):
+        """
+        """
+        return self.result
+        
+    
Index: src/sas/perspectives/fitting/fit_thread.py
===================================================================
--- src/sas/perspectives/fitting/fit_thread.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/fitting/fit_thread.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,110 @@
+
+import sys
+import time
+from sas.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,
+                 ftol       = None,
+                 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.ftol = ftol
+        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:
+            import copy
+            list_handler = []
+            list_curr_thread = [] 
+            list_ftol = []
+            list_reset_flag = []
+            list_map_get_attr = []
+            list_fit_function = []
+            list_q = []
+            for i in range(len(self.fitter)):
+                list_handler.append(self.handler)
+                list_q.append(None)
+                list_curr_thread.append(self)
+                list_ftol.append(self.ftol)
+                list_reset_flag.append(self.reset_flag)
+                list_fit_function.append('fit')
+                list_map_get_attr.append(map_getattr)
+            #from multiprocessing import Pool
+            inputs = zip(list_map_get_attr, self.fitter, list_fit_function,
+                         list_q, list_q, list_handler,list_curr_thread,list_ftol,
+                         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)
+           
+        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.
+            
+            if self.handler is not None:
+                self.handler.stop(msg=msg)
+        except:
+            import traceback
+            if self.handler is not None:
+                self.handler.error(msg=traceback.format_exc())
+           
+        
+    
Index: src/sas/perspectives/fitting/fitpage.py
===================================================================
--- src/sas/perspectives/fitting/fitpage.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/fitting/fitpage.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,3289 @@
+"""
+    FitPanel class contains fields allowing to display results when
+    fitting  a model and one data
+"""
+import sys
+import wx
+import wx.lib.newevent
+import numpy
+import copy
+import math
+import time
+from sas.guiframe.events import StatusEvent
+from sas.guiframe.events import NewPlotEvent
+from sas.guiframe.events import PlotQrangeEvent
+from sas.guiframe.dataFitting import check_data_validity
+from sas.guiframe.utils import format_number
+from sas.guiframe.utils import check_float
+
+(Chi2UpdateEvent, EVT_CHI2_UPDATE) = wx.lib.newevent.NewEvent()
+_BOX_WIDTH = 76
+_DATA_BOX_WIDTH = 300
+SMEAR_SIZE_L = 0.00
+SMEAR_SIZE_H = 0.00
+
+from sas.perspectives.fitting.basepage import BasicPage as BasicPage
+from sas.perspectives.fitting.basepage import PageInfoEvent as PageInfoEvent
+from sas.models.qsmearing import smear_selection
+
+
+class FitPage(BasicPage):
+    """
+    FitPanel class contains fields allowing to display results when
+    fitting  a model and one data
+    
+    :note: For Fit to be performed the user should check at least one parameter
+        on fit Panel window.
+    """
+
+    def __init__(self, parent, color=None):
+        """
+        Initialization of the Panel
+        """
+        BasicPage.__init__(self, parent, color=color)
+        
+        ## draw sizer
+        self._fill_data_sizer()
+        self.is_2D = None
+        self.fit_started = False
+        self.weightbt_string = None
+        self.m_name = None
+        # get smear info from data
+        self._get_smear_info()
+        self._fill_model_sizer(self.sizer1)
+        self._get_defult_custom_smear()
+        self._fill_range_sizer()
+        self._set_smear(self.data)
+        self.Bind(EVT_CHI2_UPDATE, self.on_complete_chisqr)
+        # bind key event
+        self.Bind(wx.EVT_RIGHT_DOWN, self.on_right_down)
+        self._set_bookmark_flag(False)
+        self._set_save_flag(False)
+        self._set_preview_flag(False)
+        self._set_copy_flag(False)
+        self._set_paste_flag(False)
+        self.btFit.SetFocus()
+        self.enable_fit_button()
+        self.fill_data_combobox(data_list=self.data_list)
+        #create a default data for an empty panel
+        self.create_default_data()
+        #self._manager.frame.Bind(wx.EVT_SET_FOCUS, self.on_set_focus)
+    
+    def enable_fit_button(self):
+        """
+        Enable fit button if data is valid and model is valid
+        """
+        flag = check_data_validity(self.data) & (self.model is not None)
+        self.btFit.Enable(flag)
+        
+    def _fill_data_sizer(self):
+        """
+        fill sizer 0 with data info
+        """
+        self.data_box_description = wx.StaticBox(self, -1, 'I(q) Data Source')
+        if check_data_validity(self.data):
+            dname_color = wx.BLUE
+        else:
+            dname_color = wx.RED
+        self.data_box_description.SetForegroundColour(dname_color)
+        boxsizer1 = wx.StaticBoxSizer(self.data_box_description, wx.VERTICAL)
+        #----------------------------------------------------------
+        sizer_data = wx.BoxSizer(wx.HORIZONTAL)
+        self.dataSource = wx.ComboBox(self, -1, style=wx.CB_READONLY)
+        wx.EVT_COMBOBOX(self.dataSource, -1, self.on_select_data)
+        self.dataSource.SetMinSize((_DATA_BOX_WIDTH, -1))
+        sizer_data.Add(wx.StaticText(self, -1, 'Name : '))
+        sizer_data.Add(self.dataSource)
+        sizer_data.Add((0, 5))
+        boxsizer1.Add(sizer_data, 0, wx.ALL, 10)
+        self.sizer0.Add(boxsizer1, 0, wx.EXPAND | wx.ALL, 10)
+        self.sizer0.Layout()
+        
+    def enable_datasource(self):
+        """
+        Enable or disable data source control depending on existing data
+        """
+        if not self.data_list:
+            self.dataSource.Disable()
+        else:
+            self.dataSource.Enable()
+            
+    def fill_data_combobox(self, data_list):
+        """
+        Get a list of data and fill the corresponding combobox
+        """
+        self.dataSource.Clear()
+        self.data_list = data_list
+        self.enable_datasource()
+        if len(data_list) > 0:
+            #find the maximum range covering all data
+            qmin, qmax, npts = self.compute_data_set_range(data_list)
+            self.qmin_data_set = qmin
+            self.qmax_data_set = qmax
+            self.npts_data_set = npts
+
+            self.qmin.SetValue(str(self.qmin_data_set))
+            self.qmax.SetValue(str(self.qmax_data_set))
+            self.qmin.SetBackgroundColour("white")
+            self.qmax.SetBackgroundColour("white")
+            self.qmin_x = self.qmin_data_set
+            self.qmax_x = self.qmax_data_set
+            self.state.qmin = self.qmin_x
+            self.state.qmax = self.qmax_x
+        is_data = False
+        for data in self.data_list:
+            if data is not None:
+                self.dataSource.Append(str(data.name), clientData=data)
+                if not is_data:
+                    is_data = check_data_validity(data)
+        if is_data:
+            self.dataSource.SetSelection(0)
+            self.on_select_data(event=None)
+
+        if len(data_list) == 1:
+            self.dataSource.Disable()
+                
+    def on_select_data(self, event=None):
+        """
+        On_select_data
+        """
+        if event is None and self.dataSource.GetCount() > 0:
+            data = self.dataSource.GetClientData(0)
+            self.set_data(data)
+        elif self.dataSource.GetCount() > 0:
+            pos = self.dataSource.GetSelection()
+            data = self.dataSource.GetClientData(pos)
+            self.set_data(data)
+    
+    def _on_fit_complete(self):
+        """
+        When fit is complete ,reset the fit button label.
+        """
+        self.fit_started = False
+        self.set_fitbutton()
+        
+    def _is_2D(self):
+        """
+        Check if data_name is Data2D
+        
+        :return: True or False
+        
+        """
+        if self.data.__class__.__name__ == "Data2D" or \
+                        self.enable2D:
+            return True
+        return False
+            
+    def _on_engine_change(self, name):
+        """
+        get the current name of the fit engine type
+         and update the panel accordingly
+        """
+        
+        self.engine_type = str(name)
+        self.state.engine_type = self.engine_type
+        if not self.is_mac:
+            if len(self.parameters) == 0:
+                self.Layout()
+                return
+            self.Layout()
+            self.Refresh()
+        
+    def _fill_range_sizer(self):
+        """
+        Fill the sizer containing the plotting range
+        add  access to npts
+        """
+        is_2Ddata = False
+        
+        # Check if data is 2D
+        if self.data.__class__.__name__ == "Data2D" or \
+                        self.enable2D:
+            is_2Ddata = True
+            
+        title = "Fitting"
+        #smear messages & titles
+        smear_message_none = "No smearing is selected..."
+        smear_message_dqdata = "The dQ data is being used for smearing..."
+        smear_message_2d = \
+              "Higher accuracy is very time-expensive. Use it with care..."
+        smear_message_new_ssmear = \
+              "Please enter only the value of interest to customize smearing..."
+        smear_message_new_psmear = \
+              "Please enter both; the dQ will be generated by interpolation..."
+        smear_message_2d_x_title = "<dQp>[1/A]:"
+        smear_message_2d_y_title = "<dQs>[1/A]:"
+        smear_message_pinhole_min_title = "dQ_low[1/A]:"
+        smear_message_pinhole_max_title = "dQ_high[1/A]:"
+        smear_message_slit_height_title = "Slit height[1/A]:"
+        smear_message_slit_width_title = "Slit width[1/A]:"
+        
+        self._get_smear_info()
+        
+        #Sizers
+        box_description_range = wx.StaticBox(self, -1, str(title))
+        box_description_range.SetForegroundColour(wx.BLUE)
+        boxsizer_range = wx.StaticBoxSizer(box_description_range, wx.VERTICAL)
+        self.sizer_set_smearer = wx.BoxSizer(wx.VERTICAL)
+        sizer_smearer = wx.BoxSizer(wx.HORIZONTAL)
+        self.sizer_new_smear = wx.BoxSizer(wx.HORIZONTAL)
+        self.sizer_set_masking = wx.BoxSizer(wx.HORIZONTAL)
+        sizer_chi2 = wx.BoxSizer(wx.VERTICAL)
+        smear_set_box = wx.StaticBox(self, -1, 'Set Instrumental Smearing')
+        sizer_smearer_box = wx.StaticBoxSizer(smear_set_box, wx.HORIZONTAL)
+        sizer_smearer_box.SetMinSize((_DATA_BOX_WIDTH, 60))
+        
+        weighting_set_box = wx.StaticBox(self, -1, \
+                                'Set Weighting by Selecting dI Source')
+        weighting_box = wx.StaticBoxSizer(weighting_set_box, wx.HORIZONTAL)
+        sizer_weighting = wx.BoxSizer(wx.HORIZONTAL)
+        weighting_box.SetMinSize((_DATA_BOX_WIDTH, 40))
+        #Filling the sizer containing weighting info.
+        self.dI_noweight = wx.RadioButton(self, -1, 'No Weighting',
+                                          style=wx.RB_GROUP)
+        self.dI_didata = wx.RadioButton(self, -1, 'Use dI Data')
+        self.dI_sqrdata = wx.RadioButton(self, -1, 'Use |sqrt(I Data)|')
+        self.dI_idata = wx.RadioButton(self, -1, 'Use |I Data|')
+        self.Bind(wx.EVT_RADIOBUTTON, self.onWeighting,
+                  id=self.dI_noweight.GetId())
+        self.Bind(wx.EVT_RADIOBUTTON, self.onWeighting,
+                  id=self.dI_didata.GetId())
+        self.Bind(wx.EVT_RADIOBUTTON, self.onWeighting,
+                  id=self.dI_sqrdata.GetId())
+        self.Bind(wx.EVT_RADIOBUTTON, self.onWeighting,
+                  id=self.dI_idata.GetId())
+        self.dI_didata.SetValue(True)
+        # add 4 types of weighting to the sizer
+        sizer_weighting.Add(self.dI_noweight, 0, wx.LEFT, 10)
+        sizer_weighting.Add((14, 10))
+        sizer_weighting.Add(self.dI_didata)
+        sizer_weighting.Add((14, 10))
+        sizer_weighting.Add(self.dI_sqrdata)
+        sizer_weighting.Add((14, 10))
+        sizer_weighting.Add(self.dI_idata)
+        sizer_weighting.Add((10, 10))
+        self.dI_noweight.Enable(False)
+        self.dI_didata.Enable(False)
+        self.dI_sqrdata.Enable(False)
+        self.dI_idata.Enable(False)
+        weighting_box.Add(sizer_weighting)
+        
+        sizer_fit = wx.GridSizer(2, 4, 2, 6)
+        
+        # combobox for smear2d accuracy selection
+        self.smear_accuracy = wx.ComboBox(self, -1, size=(50, -1),
+                                          style=wx.CB_READONLY)
+        self._set_accuracy_list()
+        self.smear_accuracy.SetValue(self.smear2d_accuracy)
+        self.smear_accuracy.SetSelection(0)
+        self.smear_accuracy.SetToolTipString(\
+            "'Higher' uses more Gaussian points for smearing computation.")
+                   
+        wx.EVT_COMBOBOX(self.smear_accuracy, -1, self._on_select_accuracy)
+
+        #Fit button
+        self.btFit = wx.Button(self, wx.NewId(), 'Fit', size=(88, 25))
+        self.default_bt_colour = self.btFit.GetDefaultAttributes()
+        self.btFit.Bind(wx.EVT_BUTTON, self._onFit, id=self.btFit.GetId())
+        self.btFit.SetToolTipString("Start fitting.")
+        
+        #textcntrl for custom resolution
+        self.smear_pinhole_max = self.ModelTextCtrl(self, -1,
+                            size=(_BOX_WIDTH - 25, 20),
+                            style=wx.TE_PROCESS_ENTER,
+                            text_enter_callback=self.onPinholeSmear)
+        self.smear_pinhole_min = self.ModelTextCtrl(self, -1,
+                            size=(_BOX_WIDTH - 25, 20),
+                            style=wx.TE_PROCESS_ENTER,
+                            text_enter_callback=self.onPinholeSmear)
+        self.smear_slit_height = self.ModelTextCtrl(self, -1,
+                            size=(_BOX_WIDTH - 25, 20),
+                            style=wx.TE_PROCESS_ENTER,
+                            text_enter_callback=self.onSlitSmear)
+        self.smear_slit_width = self.ModelTextCtrl(self, -1,
+                            size=(_BOX_WIDTH - 25, 20),
+                            style=wx.TE_PROCESS_ENTER,
+                            text_enter_callback=self.onSlitSmear)
+
+        ## smear
+        self.smear_data_left = BGTextCtrl(self, -1,
+                                         size=(_BOX_WIDTH - 25, 20), style=0)
+        self.smear_data_left.SetValue(str(self.dq_l))
+        self.smear_data_right = BGTextCtrl(self, -1,
+                                        size=(_BOX_WIDTH - 25, 20), style=0)
+        self.smear_data_right.SetValue(str(self.dq_r))
+
+        #set default values for smear
+        self.smear_pinhole_max.SetValue(str(self.dx_max))
+        self.smear_pinhole_min.SetValue(str(self.dx_min))
+        self.smear_slit_height.SetValue(str(self.dxl))
+        self.smear_slit_width.SetValue(str(self.dxw))
+
+        #Filling the sizer containing instruments smearing info.
+        self.disable_smearer = wx.RadioButton(self, -1,
+                                              'None', style=wx.RB_GROUP)
+        self.enable_smearer = wx.RadioButton(self, -1,
+                                             'Use dQ Data')
+        #self.enable_smearer.SetToolTipString(
+        #"Click to use the loaded dQ data for smearing.")
+        self.pinhole_smearer = wx.RadioButton(self, -1,
+                                              'Custom Pinhole Smear')
+        #self.pinhole_smearer.SetToolTipString
+        #("Click to input custom resolution for pinhole smearing.")
+        self.slit_smearer = wx.RadioButton(self, -1, 'Custom Slit Smear')
+        #self.slit_smearer.SetToolTipString
+        #("Click to input custom resolution for slit smearing.")
+        self.Bind(wx.EVT_RADIOBUTTON, self.onSmear,
+                  id=self.disable_smearer.GetId())
+        self.Bind(wx.EVT_RADIOBUTTON, self.onSmear,
+                  id=self.enable_smearer.GetId())
+        self.Bind(wx.EVT_RADIOBUTTON, self.onPinholeSmear,
+                  id=self.pinhole_smearer.GetId())
+        self.Bind(wx.EVT_RADIOBUTTON, self.onSlitSmear,
+                  id=self.slit_smearer.GetId())
+        self.disable_smearer.SetValue(True)
+        
+        # add 4 types of smearing to the sizer
+        sizer_smearer.Add(self.disable_smearer, 0, wx.LEFT, 10)
+        sizer_smearer.Add((10, 10))
+        sizer_smearer.Add(self.enable_smearer)
+        sizer_smearer.Add((10, 10))
+        sizer_smearer.Add(self.pinhole_smearer)
+        sizer_smearer.Add((10, 10))
+        sizer_smearer.Add(self.slit_smearer)
+        sizer_smearer.Add((10, 10))
+        
+        # StaticText for chi2, N(for fitting), Npts + Log/linear spacing
+        self.tcChi = BGTextCtrl(self, -1, "-", size=(75, 20), style=0)
+        self.tcChi.SetToolTipString("Chi2/Npts(Fit)")
+        self.Npts_fit = BGTextCtrl(self, -1, "-", size=(75, 20), style=0)
+        self.Npts_fit.SetToolTipString(\
+                            " Npts : number of points selected for fitting")
+        self.Npts_total = self.ModelTextCtrl(self, -1,
+                        size=(_BOX_WIDTH, 20),
+                        style=wx.TE_PROCESS_ENTER,
+                        text_enter_callback=self._onQrangeEnter)
+        self.Npts_total.SetValue(format_number(self.npts_x))
+        self.Npts_total.SetToolTipString(\
+                                " Total Npts : total number of data points")
+                
+        # Update and Draw button
+        self.draw_button = wx.Button(self, wx.NewId(),
+                                     'Compute', size=(88, 24))
+        self.draw_button.Bind(wx.EVT_BUTTON, \
+                              self._onDraw, id=self.draw_button.GetId())
+        self.draw_button.SetToolTipString("Compute and Draw.")
+        
+        self.points_sizer = wx.BoxSizer(wx.HORIZONTAL) 
+        self.pointsbox = wx.CheckBox(self, -1, 'Log?', (10, 10))
+        self.pointsbox.SetValue(False)
+        self.pointsbox.SetToolTipString("Check mark to use log spaced points")
+        wx.EVT_CHECKBOX(self, self.pointsbox.GetId(), self.select_log)
+        
+        self.points_sizer.Add(wx.StaticText(self, -1, 'Npts    '))
+        self.points_sizer.Add(self.pointsbox)
+
+        box_description_1 = wx.StaticText(self, -1, '   Chi2/Npts')
+        box_description_2 = wx.StaticText(self, -1, 'Npts(Fit)')
+        #box_description_3 = wx.StaticText(self, -1, 'Total Npts')
+        #box_description_3.SetToolTipString( \
+        #                        " Total Npts : total number of data points")
+        
+        sizer_fit.Add(box_description_1, 0, 0)
+        sizer_fit.Add(box_description_2, 0, 0)
+        sizer_fit.Add(self.points_sizer, 0, 0)
+        #sizer_fit.Add(box_description_3, 0, 0)
+        sizer_fit.Add(self.draw_button, 0, 0)
+        sizer_fit.Add(self.tcChi, 0, 0)
+        sizer_fit.Add(self.Npts_fit, 0, 0)
+        sizer_fit.Add(self.Npts_total, 0, 0)
+        sizer_fit.Add(self.btFit, 0, 0)
+
+        # StaticText for smear
+        self.smear_description_none = wx.StaticText(self, -1,
+                                    smear_message_none, style=wx.ALIGN_LEFT)
+        self.smear_description_dqdata = wx.StaticText(self,
+                                -1, smear_message_dqdata, style=wx.ALIGN_LEFT)
+        self.smear_description_type = wx.StaticText(self,
+                                     -1, "Type:", style=wx.ALIGN_LEFT)
+        self.smear_description_accuracy_type = wx.StaticText(self, -1,
+                                        "Accuracy:", style=wx.ALIGN_LEFT)
+        self.smear_description_smear_type = BGTextCtrl(self, -1,
+                                                       size=(57, 20), style=0)
+        self.smear_description_smear_type.SetValue(str(self.dq_l))
+        self.SetBackgroundColour(self.GetParent().GetBackgroundColour())
+        self.smear_description_2d = wx.StaticText(self, -1,
+                                    smear_message_2d, style=wx.ALIGN_LEFT)
+        self.smear_message_new_s = wx.StaticText(self, -1,
+                         smear_message_new_ssmear, style=wx.ALIGN_LEFT)
+        self.smear_message_new_p = wx.StaticText(self, -1,
+                            smear_message_new_psmear, style=wx.ALIGN_LEFT)
+        self.smear_description_2d_x = wx.StaticText(self, -1,
+                            smear_message_2d_x_title, style=wx.ALIGN_LEFT)
+        self.smear_description_2d_x.SetToolTipString(\
+                                        "  dQp(parallel) in q_r direction.")
+        self.smear_description_2d_y = wx.StaticText(self, -1,
+                            smear_message_2d_y_title, style=wx.ALIGN_LEFT)
+        self.smear_description_2d_y.SetToolTipString(\
+                                    " dQs(perpendicular) in q_phi direction.")
+        self.smear_description_pin_min = wx.StaticText(self, -1,
+                        smear_message_pinhole_min_title, style=wx.ALIGN_LEFT)
+        self.smear_description_pin_max = wx.StaticText(self, -1,
+                        smear_message_pinhole_max_title, style=wx.ALIGN_LEFT)
+        self.smear_description_slit_height = wx.StaticText(self, -1,
+                        smear_message_slit_height_title, style=wx.ALIGN_LEFT)
+        self.smear_description_slit_width = wx.StaticText(self, -1,
+                        smear_message_slit_width_title, style=wx.ALIGN_LEFT)
+        
+        #arrange sizers
+        self.sizer_set_smearer.Add(sizer_smearer)
+        self.sizer_set_smearer.Add((10, 10))
+        self.sizer_set_smearer.Add(self.smear_description_none,
+                                    0, wx.CENTER, 10)
+        self.sizer_set_smearer.Add(self.smear_description_dqdata,
+                                    0, wx.CENTER, 10)
+        self.sizer_set_smearer.Add(self.smear_description_2d,
+                                    0, wx.CENTER, 10)
+        self.sizer_new_smear.Add(self.smear_description_type,
+                                  0, wx.CENTER, 10)
+        self.sizer_new_smear.Add(self.smear_description_accuracy_type,
+                                  0, wx.CENTER, 10)
+        self.sizer_new_smear.Add(self.smear_accuracy)
+        self.sizer_new_smear.Add(self.smear_description_smear_type,
+                                  0, wx.CENTER, 10)
+        self.sizer_new_smear.Add((15, -1))
+        self.sizer_new_smear.Add(self.smear_description_2d_x,
+                                  0, wx.CENTER, 10)
+        self.sizer_new_smear.Add(self.smear_description_pin_min,
+                                  0, wx.CENTER, 10)
+        self.sizer_new_smear.Add(self.smear_description_slit_height,
+                                  0, wx.CENTER, 10)
+
+        self.sizer_new_smear.Add(self.smear_pinhole_min,
+                                  0, wx.CENTER, 10)
+        self.sizer_new_smear.Add(self.smear_slit_height,
+                                  0, wx.CENTER, 10)
+        self.sizer_new_smear.Add(self.smear_data_left,
+                                  0, wx.CENTER, 10)
+        self.sizer_new_smear.Add((20, -1))
+        self.sizer_new_smear.Add(self.smear_description_2d_y,
+                                  0, wx.CENTER, 10 )
+        self.sizer_new_smear.Add(self.smear_description_pin_max,
+                                  0, wx.CENTER, 10 )
+        self.sizer_new_smear.Add(self.smear_description_slit_width,
+                                  0, wx.CENTER, 10 )
+
+        self.sizer_new_smear.Add(self.smear_pinhole_max, 0, wx.CENTER, 10)
+        self.sizer_new_smear.Add(self.smear_slit_width, 0, wx.CENTER, 10)
+        self.sizer_new_smear.Add(self.smear_data_right, 0, wx.CENTER, 10)
+           
+        self.sizer_set_smearer.Add(self.smear_message_new_s, 0, wx.CENTER, 10)
+        self.sizer_set_smearer.Add(self.smear_message_new_p, 0, wx.CENTER, 10)
+        self.sizer_set_smearer.Add((5, 2))
+        self.sizer_set_smearer.Add(self.sizer_new_smear, 0, wx.CENTER, 10)
+        
+        # add all to chi2 sizer
+        sizer_smearer_box.Add(self.sizer_set_smearer)
+        sizer_chi2.Add(sizer_smearer_box)
+        sizer_chi2.Add((-1, 5))
+        sizer_chi2.Add(weighting_box)
+        sizer_chi2.Add((-1, 5))
+        
+        # hide all smear messages and textctrl
+        self._hide_all_smear_info()
+        
+        # get smear_selection
+        self.current_smearer = smear_selection(self.data, self.model)
+
+        # Show only the relevant smear messages, etc
+        if self.current_smearer == None:
+            if not is_2Ddata:
+                self.smear_description_none.Show(True)
+                self.enable_smearer.Disable()
+            else:
+                self.smear_description_none.Show(True)
+                self.slit_smearer.Disable()
+            if self.data == None:
+                self.slit_smearer.Disable()
+                self.pinhole_smearer.Disable()
+                self.enable_smearer.Disable()
+        else:
+            self._show_smear_sizer()
+        boxsizer_range.Add(self.sizer_set_masking)
+        #2D data? default
+        is_2Ddata = False
+        
+        #check if it is 2D data
+        if self.data.__class__.__name__ == "Data2D" or self.enable2D:
+            is_2Ddata = True
+            
+        self.sizer5.Clear(True)
+     
+        self.qmin = self.ModelTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
+                                    style=wx.TE_PROCESS_ENTER,
+                                    set_focus_callback=self.qrang_set_focus,
+                                    text_enter_callback=self._onQrangeEnter,
+                                    name='qmin')
+        self.qmin.SetValue(str(self.qmin_x))
+        q_tip = "Click outside of the axes\n to remove the lines."
+        qmin_tip = "Minimun value of Q.\n"
+        qmin_tip += q_tip
+        self.qmin.SetToolTipString(qmin_tip)
+     
+        self.qmax = self.ModelTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
+                                       style=wx.TE_PROCESS_ENTER,
+                                       set_focus_callback=self.qrang_set_focus,
+                                       text_enter_callback=self._onQrangeEnter,
+                                       name='qmax')
+        self.qmax.SetValue(str(self.qmax_x))
+        qmax_tip = "Maximum value of Q.\n"
+        qmax_tip += q_tip
+        self.qmax.SetToolTipString(qmax_tip)
+        self.qmin.Bind(wx.EVT_MOUSE_EVENTS, self.qrange_click)
+        self.qmax.Bind(wx.EVT_MOUSE_EVENTS, self.qrange_click)
+        self.qmin.Bind(wx.EVT_KEY_DOWN, self.on_key)
+        self.qmax.Bind(wx.EVT_KEY_DOWN, self.on_key)
+        self.qmin.Bind(wx.EVT_TEXT, self.on_qrange_text)
+        self.qmax.Bind(wx.EVT_TEXT, self.on_qrange_text)
+        id = wx.NewId()
+        self.reset_qrange = wx.Button(self, id, 'Reset', size=(77, 20))
+      
+        self.reset_qrange.Bind(wx.EVT_BUTTON, self.on_reset_clicked, id=id)
+        self.reset_qrange.SetToolTipString("Reset Q range to the default")
+     
+        sizer = wx.GridSizer(2, 4, 2, 6)
+
+        self.btEditMask = wx.Button(self, wx.NewId(), 'Editor', size=(88, 23))
+        self.btEditMask.Bind(wx.EVT_BUTTON, self._onMask,
+                             id=self.btEditMask.GetId())
+        self.btEditMask.SetToolTipString("Edit Mask.")
+        self.EditMask_title = wx.StaticText(self, -1, ' Masking(2D)')
+        
+        sizer.Add(wx.StaticText(self, -1, '   Q range'))
+        sizer.Add(wx.StaticText(self, -1, ' Min[1/A]'))
+        sizer.Add(wx.StaticText(self, -1, ' Max[1/A]'))
+        sizer.Add(self.EditMask_title)
+        sizer.Add(self.reset_qrange)
+        sizer.Add(self.qmin)
+        sizer.Add(self.qmax)
+        #sizer.Add(self.theory_npts_tcrtl)
+        sizer.Add(self.btEditMask)
+        boxsizer_range.Add(sizer_chi2)
+        boxsizer_range.Add((10, 10))
+        boxsizer_range.Add(sizer)
+        
+        boxsizer_range.Add((10, 15))
+        boxsizer_range.Add(sizer_fit)
+        if is_2Ddata:
+            self.btEditMask.Enable()
+            self.EditMask_title.Enable()
+        else:
+            self.btEditMask.Disable()
+            self.EditMask_title.Disable()
+        ## save state
+        self.save_current_state()
+        self.sizer5.Add(boxsizer_range, 0, wx.EXPAND | wx.ALL, 10)
+        self.sizer5.Layout()
+
+        
+    def _set_sizer_dispersion(self):
+        """
+        draw sizer with gaussian dispersity parameters
+        """
+        self.fittable_param = []
+        self.fixed_param = []
+        self.orientation_params_disp = []
+
+        self.sizer4_4.Clear(True)
+        if self.model == None:
+            ##no model is selected
+            return
+        if not self.enable_disp.GetValue():
+            ## the user didn't select dispersity display
+            return
+            
+        self._reset_dispersity()
+        
+        ## fill a sizer with the combobox to select dispersion type
+        model_disp = wx.StaticText(self, -1, 'Function')
+        CHECK_STATE = self.cb1.GetValue()
+import sas.models.dispersion_models
+        self.polydisp = sas.models.dispersion_models.models
+
+        ix = 0
+        iy = 0
+        disp = wx.StaticText(self, -1, ' ')
+        self.sizer4_4.Add(disp, (iy, ix), (1, 1),
+                          wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
+        ix += 1
+        values = wx.StaticText(self, -1, 'PD[ratio]')
+        polytext = "Polydispersity (= STD/mean); "
+        polytext += "the standard deviation over the mean value."
+        values.SetToolTipString(polytext)
+
+        self.sizer4_4.Add(values, (iy, ix), (1, 1),
+                          wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+        ix += 2
+        if self.is_mac:
+            err_text = 'Error'
+        else:
+            err_text = ''
+        self.text_disp_1 = wx.StaticText(self, -1, err_text)
+        self.sizer4_4.Add(self.text_disp_1, (iy, ix), (1, 1), \
+                          wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+        
+        ix += 1
+        self.text_disp_min = wx.StaticText(self, -1, 'Min')
+        self.sizer4_4.Add(self.text_disp_min, (iy, ix), (1, 1), \
+                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+
+        ix += 1
+        self.text_disp_max = wx.StaticText(self, -1, 'Max')
+        self.sizer4_4.Add(self.text_disp_max, (iy, ix), (1, 1),
+                          wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+       
+        ix += 1
+        npts = wx.StaticText(self, -1, 'Npts')
+        npts.SetToolTipString("Number of sampling points for the numerical\n\
+        integration over the distribution function.")
+        self.sizer4_4.Add(npts, (iy, ix), (1, 1),
+                          wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+        ix += 1
+        nsigmas = wx.StaticText(self, -1, 'Nsigs')
+        nsigmas.SetToolTipString("Number of sigmas between which the range\n\
+         of the distribution function will be used for weighting. \n\
+        The value '3' covers 99.5% for Gaussian distribution \n\
+        function. Note: Not recommended to change this value.")
+        self.sizer4_4.Add(nsigmas, (iy, ix), (1, 1),
+                          wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+        ix += 1
+        self.sizer4_4.Add(model_disp, (iy, ix), (1, 1),
+                          wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+        
+        self.text_disp_max.Show(True)
+        self.text_disp_min.Show(True)
+
+        for item in self.model.dispersion.keys():
+            if not self.magnetic_on:
+                if item in self.model.magnetic_params:
+                    continue
+            if not item in self.model.orientation_params:
+                if not item in self.disp_cb_dict:
+                    self.disp_cb_dict[item] = None
+                name0 = "Distribution of " + item
+                name1 = item + ".width"
+                name2 = item + ".npts"
+                name3 = item + ".nsigmas"
+                if not name1 in self.model.details:
+                    self.model.details[name1] = ["", None, None]
+
+                iy += 1
+                for p in self.model.dispersion[item].keys():
+        
+                    if p == "width":
+                        ix = 0
+                        cb = wx.CheckBox(self, -1, name0, (10, 10))
+                        cb.SetValue(CHECK_STATE)
+                        cb.SetToolTipString("Check mark to fit")
+                        wx.EVT_CHECKBOX(self, cb.GetId(), self.select_param)
+                        self.sizer4_4.Add(cb, (iy, ix), (1, 1),
+                                wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 5)
+                        ix = 1
+                        value = self.model.getParam(name1)
+                        ctl1 = self.ModelTextCtrl(self, -1,
+                                                  size=(_BOX_WIDTH / 1.3, 20),
+                                                  style=wx.TE_PROCESS_ENTER)
+                        ctl1.SetLabel('PD[ratio]')
+                        poly_text = "Polydispersity (STD/mean) of %s\n" % item
+                        poly_text += "STD: the standard deviation"
+                        poly_text += " from the mean value."
+                        ctl1.SetToolTipString(poly_text)
+                        ctl1.SetValue(str(format_number(value, True)))
+                        self.sizer4_4.Add(ctl1, (iy, ix), (1, 1), wx.EXPAND)
+                        ## text to show error sign
+                        ix = 2
+                        text2 = wx.StaticText(self, -1, '+/-')
+                        self.sizer4_4.Add(text2, (iy, ix), (1, 1),
+                                          wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+                        if not self.is_mac:
+                            text2.Hide()
+
+                        ix = 3
+                        ctl2 = wx.TextCtrl(self, -1,
+                                           size=(_BOX_WIDTH / 1.3, 20),
+                                           style=0)
+                  
+                        self.sizer4_4.Add(ctl2, (iy, ix), (1, 1),
+                                          wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+                        if not self.is_mac:
+                            ctl2.Hide()
+
+                        ix = 4
+                        ctl3 = self.ModelTextCtrl(self, -1,
+                                                  size=(_BOX_WIDTH / 2, 20),
+                                                  style=wx.TE_PROCESS_ENTER,
+                                text_enter_callback=self._onparamRangeEnter)
+                        
+                        self.sizer4_4.Add(ctl3, (iy, ix), (1, 1),
+                                          wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+                       
+                        ix = 5
+                        ctl4 = self.ModelTextCtrl(self, -1,
+                                                  size=(_BOX_WIDTH / 2, 20),
+                                                  style=wx.TE_PROCESS_ENTER,
+                            text_enter_callback=self._onparamRangeEnter)
+                        
+                        self.sizer4_4.Add(ctl4, (iy, ix), (1, 1),
+                                          wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+
+                        ctl3.Show(True)
+                        ctl4.Show(True)
+                                                              
+                    elif p == "npts":
+                        ix = 6
+                        value = self.model.getParam(name2)
+                        Tctl = self.ModelTextCtrl(self, -1,
+                                                  size=(_BOX_WIDTH / 2.2, 20),
+                                                  style=wx.TE_PROCESS_ENTER)
+                        
+                        Tctl.SetValue(str(format_number(value)))
+                        self.sizer4_4.Add(Tctl, (iy, ix), (1, 1),
+                                           wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+                        self.fixed_param.append([None, name2, Tctl, None, None,
+                                                 None, None, None])
+                    elif p == "nsigmas":
+                        ix = 7
+                        value = self.model.getParam(name3)
+                        Tct2 = self.ModelTextCtrl(self, -1,
+                                                  size=(_BOX_WIDTH / 2.2, 20),
+                                                  style=wx.TE_PROCESS_ENTER)
+                        
+                        Tct2.SetValue(str(format_number(value)))
+                        self.sizer4_4.Add(Tct2, (iy, ix), (1, 1),
+                                           wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+                        self.fixed_param.append([None, name3, Tct2,
+                                                 None, None, None,
+                                                 None, None])
+
+                ix = 8
+                disp_box = wx.ComboBox(self, -1, size=(65, -1),
+                                       style=wx.CB_READONLY, name='%s' % name1)
+                for key, value in self.polydisp.iteritems():
+                    name_disp = str(key)
+                    disp_box.Append(name_disp, value)
+                    disp_box.SetStringSelection("gaussian")
+                wx.EVT_COMBOBOX(disp_box, -1, self._on_disp_func)
+                self.sizer4_4.Add(disp_box, (iy, ix), (1, 1), wx.EXPAND)
+                self.fittable_param.append([cb, name1, ctl1, text2,
+                                            ctl2, ctl3, ctl4, disp_box])
+                           
+        ix = 0
+        iy += 1
+        self.sizer4_4.Add((20, 20), (iy, ix), (1, 1),
+                          wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
+        first_orient = True
+        for item in self.model.dispersion.keys():
+            if not self.magnetic_on:
+                if item in self.model.magnetic_params:
+                    continue
+            if  item in self.model.orientation_params:
+                if not item in self.disp_cb_dict:
+                    self.disp_cb_dict[item] = None
+                name0 = "Distribution of " + item
+                name1 = item + ".width"
+                name2 = item + ".npts"
+                name3 = item + ".nsigmas"
+                
+                if not name1 in self.model.details:
+                    self.model.details[name1] = ["", None, None]
+ 
+                iy += 1
+                for p in self.model.dispersion[item].keys():
+        
+                    if p == "width":
+                        ix = 0
+                        cb = wx.CheckBox(self, -1, name0, (10, 10))
+                        cb.SetValue(CHECK_STATE)
+                        cb.SetToolTipString("Check mark to fit")
+                        wx.EVT_CHECKBOX(self, cb.GetId(), self.select_param)
+                        self.sizer4_4.Add(cb, (iy, ix), (1, 1),
+                                wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 5)
+                        if self.data.__class__.__name__ == "Data2D" or \
+                                    self.enable2D:
+                            cb.Show(True)
+                        elif cb.IsShown():
+                            cb.Hide()
+                        ix = 1
+                        value = self.model.getParam(name1)
+                        ctl1 = self.ModelTextCtrl(self, -1,
+                                                  size=(_BOX_WIDTH / 1.3, 20),
+                                                  style=wx.TE_PROCESS_ENTER)
+                        poly_tip = "Absolute Sigma for %s." % item
+                        ctl1.SetToolTipString(poly_tip)
+                        ctl1.SetValue(str(format_number(value, True)))
+                        if self.data.__class__.__name__ == "Data2D" or \
+                                    self.enable2D:
+                            if first_orient:
+                                values.SetLabel('PD[ratio], Sig[deg]')
+                                poly_text = "PD(polydispersity for lengths):\n"
+                                poly_text += "It should be a value between"
+                                poly_text += "0 and 1\n"
+                                poly_text += "Sigma for angles: \n"
+                                poly_text += "It is the STD (ratio*mean)"
+                                poly_text += " of the distribution.\n "
+                            
+                                values.SetToolTipString(poly_text)
+                                first_orient = False
+                            ctl1.Show(True)
+                        elif ctl1.IsShown():
+                            ctl1.Hide()
+                        
+                        self.sizer4_4.Add(ctl1, (iy, ix), (1, 1), wx.EXPAND)
+                        ## text to show error sign
+                        ix = 2
+                        text2 = wx.StaticText(self, -1, '+/-')
+                        self.sizer4_4.Add(text2, (iy, ix), (1, 1),
+                                          wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+
+                        text2.Hide()
+
+                        ix = 3
+                        ctl2 = wx.TextCtrl(self, -1,
+                                           size=(_BOX_WIDTH / 1.3, 20),
+                                           style=0)
+                    
+                        self.sizer4_4.Add(ctl2, (iy, ix), (1, 1),
+                                          wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+
+                        ctl2.Hide()
+                        if self.data.__class__.__name__ == "Data2D" or \
+                                self.enable2D:
+                            if self.is_mac:
+                                text2.Show(True)
+                                ctl2.Show(True)
+                            
+                        ix = 4
+                        ctl3 = self.ModelTextCtrl(self, -1,
+                                                  size=(_BOX_WIDTH / 2, 20),
+                                                  style=wx.TE_PROCESS_ENTER,
+                                text_enter_callback=self._onparamRangeEnter)
+
+                        self.sizer4_4.Add(ctl3, (iy, ix), (1, 1),
+                                          wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+
+                        ctl3.Hide()
+                
+                        ix = 5
+                        ctl4 = self.ModelTextCtrl(self, -1,
+                            size=(_BOX_WIDTH / 2, 20),
+                            style=wx.TE_PROCESS_ENTER,
+                            text_enter_callback=self._onparamRangeEnter)
+                        self.sizer4_4.Add(ctl4, (iy, ix), (1, 1),
+                                          wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+                        ctl4.Hide()
+                        
+                        if self.data.__class__.__name__ == "Data2D" or \
+                                self.enable2D:
+                            ctl3.Show(True)
+                            ctl4.Show(True)
+                             
+                    elif p == "npts":
+                        ix = 6
+                        value = self.model.getParam(name2)
+                        Tctl = self.ModelTextCtrl(self, -1,
+                                                 size=(_BOX_WIDTH / 2.2, 20),
+                                                 style=wx.TE_PROCESS_ENTER)
+                        
+                        Tctl.SetValue(str(format_number(value)))
+                        if self.data.__class__.__name__ == "Data2D" or \
+                                self.enable2D:
+                            Tctl.Show(True)
+                        else:
+                            Tctl.Hide()
+                        self.sizer4_4.Add(Tctl, (iy, ix), (1, 1),
+                                          wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+                        self.fixed_param.append([None, name2, Tctl, None, None,
+                                                 None, None, None])
+                        self.orientation_params_disp.append([None, name2,
+                                                             Tctl, None, None,
+                                                             None, None, None])
+                    elif p == "nsigmas":
+                        ix = 7
+                        value = self.model.getParam(name3)
+                        Tct2 = self.ModelTextCtrl(self, -1,
+                                                  size=(_BOX_WIDTH / 2.2, 20),
+                                                  style=wx.TE_PROCESS_ENTER)
+                        
+                        Tct2.SetValue(str(format_number(value)))
+                        if self.data.__class__.__name__ == "Data2D" or \
+                                self.enable2D:
+                            Tct2.Show(True)
+                        else:
+                            Tct2.Hide()
+                        self.sizer4_4.Add(Tct2, (iy, ix), (1, 1),
+                                          wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+
+                        self.fixed_param.append([None, name3, Tct2,
+                                                 None, None, None, None, None])
+                                                   
+                        self.orientation_params_disp.append([None, name3,
+                                        Tct2, None, None, None, None, None])
+
+                ix = 8
+                disp_box = wx.ComboBox(self, -1, size=(65, -1),
+                                style=wx.CB_READONLY, name='%s' % name1)
+                for key, value in self.polydisp.iteritems():
+                    name_disp = str(key)
+                    disp_box.Append(name_disp, value)
+                    disp_box.SetStringSelection("gaussian")
+                wx.EVT_COMBOBOX(disp_box, -1, self._on_disp_func)
+                self.sizer4_4.Add(disp_box, (iy, ix), (1, 1), wx.EXPAND)
+                self.fittable_param.append([cb, name1, ctl1, text2,
+                                            ctl2, ctl3, ctl4, disp_box])
+                self.orientation_params_disp.append([cb, name1, ctl1,
+                                            text2, ctl2, ctl3, ctl4, disp_box])
+                       
+                if self.data.__class__.__name__ == "Data2D" or \
+                                self.enable2D:
+                    disp_box.Show(True)
+                else:
+                    disp_box.Hide()
+        
+        self.state.disp_cb_dict = copy.deepcopy(self.disp_cb_dict)
+          
+        self.state.model = self.model.clone()
+        ## save state into
+        self.state.cb1 = self.cb1.GetValue()
+        self._copy_parameters_state(self.parameters, self.state.parameters)
+        self._copy_parameters_state(self.orientation_params_disp,
+                                     self.state.orientation_params_disp)
+        self._copy_parameters_state(self.fittable_param,
+                                    self.state.fittable_param)
+        self._copy_parameters_state(self.fixed_param, self.state.fixed_param)
+
+        wx.PostEvent(self.parent,
+                     StatusEvent(status=" Selected Distribution: Gaussian"))
+        #Fill the list of fittable parameters
+        #self.select_all_param(event=None)
+        self.get_all_checked_params()
+        self.Layout()
+
+    def _onDraw(self, event):
+        """
+        Update and Draw the model
+        """
+        if self.model == None:
+            msg = "Please select a Model first..."
+            wx.MessageBox(msg, 'Info')
+            return
+        """
+        if not self.data.is_data:
+            self.npts_x = self.Npts_total.GetValue()
+            self.Npts_fit.SetValue(self.npts_x)
+            self.create_default_data()
+        """
+        flag = self._update_paramv_on_fit()
+    
+        wx.CallAfter(self._onparamEnter_helper)
+        if not flag:
+            msg = "The parameters are invalid"
+            wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
+            return
+        
+    def _onFit(self, event):
+        """
+        Allow to fit
+        """
+        if event != None:
+            event.Skip()
+        if self.fit_started:
+            self._StopFit()
+            self.fit_started = False
+            wx.CallAfter(self.set_fitbutton)
+            return
+
+        if (len(self._manager.fit_thread_list) > 0
+                and self._manager._fit_engine not in ("park","bumps")
+                and self._manager.sim_page != None
+                and self._manager.sim_page.uid == self.uid):
+            msg = "The FitEnging will be set to 'ParkMC'\n"
+            msg += " to fit with more than one data set..."
+            wx.MessageBox(msg, 'Info')
+            
+        if self.data is None:
+            msg = "Please get Data first..."
+            wx.MessageBox(msg, 'Info')
+            wx.PostEvent(self._manager.parent,
+                         StatusEvent(status="Fit: %s" % msg))
+            return
+        if self.model is None:
+            msg = "Please select a Model first..."
+            wx.MessageBox(msg, 'Info')
+            wx.PostEvent(self._manager.parent,
+                         StatusEvent(status="Fit: %s" % msg, type="stop"))
+            return
+        
+        if len(self.param_toFit) <= 0:
+            msg = "Select at least one parameter to fit"
+            wx.MessageBox(msg, 'Info')
+            wx.PostEvent(self._manager.parent,
+                         StatusEvent(status=msg, type="stop"))
+            return
+        
+        flag = self._update_paramv_on_fit()
+        
+        if self.batch_on and not self._is_2D():
+            if not self._validate_Npts_1D():
+                return
+                
+        if not flag:
+            msg = "Fitting range or parameters are invalid"
+            wx.PostEvent(self._manager.parent,
+                         StatusEvent(status=msg, type="stop"))
+            return
+              
+        self.select_param(event=None)
+
+        # Remove or do not allow fitting on the Q=0 point, especially
+        # when y(q=0)=None at x[0].
+        self.qmin_x = float(self.qmin.GetValue())
+        self.qmax_x = float(self.qmax.GetValue())
+        self._manager._reset_schedule_problem(value=0, uid=self.uid)
+        self._manager.schedule_for_fit(uid=self.uid, value=1)
+        self._manager.set_fit_range(uid=self.uid, qmin=self.qmin_x,
+                                    qmax=self.qmax_x)
+
+        #single fit
+        #self._manager.onFit(uid=self.uid)
+        self.fit_started = self._manager.onFit(uid=self.uid)
+        wx.CallAfter(self.set_fitbutton)
+    
+    def set_fitbutton(self):
+        """
+        Set fit button label depending on the fit_started[bool]
+        """
+        # Skip this feature if we are not on Windows
+        #NOTE: the is_mac data member actually means "is no Windows".
+        if self.is_mac:
+            return
+        
+        if self.fit_started:
+            label = "Stop"
+            color = "red"
+        else:
+            label = "Fit"
+            color = "black"
+        self.btFit.Enable(False)
+        self.btFit.SetLabel(label)
+        self.btFit.SetForegroundColour(color)
+        self.btFit.Enable(True)
+                     
+    def get_weight_flag(self):
+        """
+        Get flag corresponding to a given weighting dI data.
+        """
+        button_list = [self.dI_noweight,
+                       self.dI_didata,
+                       self.dI_sqrdata,
+                       self.dI_idata]
+        flag = 1
+        for item in button_list:
+            if item.GetValue():
+                if button_list.index(item) == 0:
+                    flag = 0  # dy = numpy.ones_like(dy_data)
+                elif button_list.index(item) == 1:
+                    flag = 1  # dy = dy_data
+                elif button_list.index(item) == 2:
+                    flag = 2  # dy = numpy.sqrt(numpy.abs(data))
+                elif button_list.index(item) == 3:
+                    flag = 3  # dy = numpy.abs(data)
+                break
+        return flag
+                
+    def _StopFit(self, event=None):
+        """
+        Stop fit
+        """
+        if event != None:
+            event.Skip()
+        self._manager.stop_fit(self.uid)
+        self._manager._reset_schedule_problem(value=0)
+        self._on_fit_complete()
+         
+    def rename_model(self):
+        """
+        find a short name for model
+        """
+        if self.model is not None:
+            self.model.name = "M" + str(self.index_model)
+    
+    def _on_select_model(self, event=None):
+        """
+        call back for model selection
+        """
+        self.Show(False)
+        copy_flag = False
+        is_poly_enabled = None
+        if event != None:
+            if (event.GetEventObject() == self.formfactorbox\
+                        and self.structurebox.GetLabel() != 'None')\
+                        or event.GetEventObject() == self.structurebox\
+                        or event.GetEventObject() == self.multifactorbox:
+                copy_flag = self.get_copy_params()
+                is_poly_enabled = self.enable_disp.GetValue()
+
+        self._on_select_model_helper()
+        self.set_model_param_sizer(self.model)
+        if self.model is None:
+            self._set_bookmark_flag(False)
+            self._keep.Enable(False)
+            self._set_save_flag(False)
+        self.enable_disp.SetValue(False)
+        self.disable_disp.SetValue(True)
+        try:
+            self.set_dispers_sizer()
+        except:
+            pass
+        self.state.enable_disp = self.enable_disp.GetValue()
+        self.state.disable_disp = self.disable_disp.GetValue()
+        self.state.pinhole_smearer = self.pinhole_smearer.GetValue()
+        self.state.slit_smearer = self.slit_smearer.GetValue()
+    
+        self.state.structurecombobox = self.structurebox.GetLabel()
+        self.state.formfactorcombobox = self.formfactorbox.GetLabel()
+        self.enable_fit_button()
+        if self.model != None:
+            self.m_name = self.model.name
+            self.state.m_name = self.m_name
+            self.rename_model()
+            self._set_copy_flag(True)
+            self._set_paste_flag(True)
+            if self.data is not None:
+                is_data = check_data_validity(self.data)
+                if is_data:
+                    self._set_bookmark_flag(not self.batch_on)
+                    self._keep.Enable(not self.batch_on)
+                    self._set_save_flag(True)
+            # Reset smearer, model and data
+            if not copy_flag:
+                self.disable_smearer.SetValue(True)
+                self.enable_smearer.SetValue(False)
+    
+            # more disables for 2D
+            self._set_smear_buttons()
+            
+            try:
+                # update smearer sizer
+                #if not self.enable_smearer.GetValue():
+                #    self.disable_smearer.SetValue(True)
+                self.onSmear(None)
+                temp_smear = None
+                if not self.disable_smearer.GetValue():
+                    # Set the smearer environments
+                    temp_smear = self.current_smearer
+            except:
+                raise
+                ## error occured on chisqr computation
+                #pass
+            ## event to post model to fit to fitting plugins
+            (ModelEventbox, EVT_MODEL_BOX) = wx.lib.newevent.NewEvent()
+         
+            ## set smearing value whether or not
+            #    the data contain the smearing info
+            evt = ModelEventbox(model=self.model,
+                            smearer=temp_smear,
+                            enable_smearer=not self.disable_smearer.GetValue(),
+                            qmin=float(self.qmin_x),
+                            uid=self.uid,
+                            caption=self.window_caption,
+                            qmax=float(self.qmax_x))
+   
+            self._manager._on_model_panel(evt=evt)
+            self.mbox_description.SetLabel("Model [ %s ]"% str(self.model.name))
+            self.mbox_description.SetForegroundColour(wx.BLUE)
+            self.state.model = self.model.clone()
+            self.state.model.name = self.model.name
+
+        if event != None:
+            ## post state to fit panel
+            new_event = PageInfoEvent(page=self)
+            wx.PostEvent(self.parent, new_event)
+            #update list of plugins if new plugin is available
+            custom_model = 'Customized Models'
+            mod_cat = self.categorybox.GetStringSelection()
+            if mod_cat == custom_model:
+                temp = self.parent.update_model_list()
+                if temp:
+                    self.model_list_box = temp
+                    current_val = self.formfactorbox.GetLabel()
+                    pos = self.formfactorbox.GetSelection()
+                    self._show_combox_helper()
+                    self.formfactorbox.SetSelection(pos)
+                    self.formfactorbox.SetValue(current_val)
+            # when select a model only from guictr/button
+            if is_poly_enabled != None:
+                self.enable_disp.SetValue(is_poly_enabled)
+                self.disable_disp.SetValue(not is_poly_enabled)
+                self._set_dipers_Param(event=None)
+                self.state.enable_disp = self.enable_disp.GetValue()
+                self.state.disable_disp = self.disable_disp.GetValue()
+
+            # Keep the previous param values
+            if copy_flag:
+                self.get_paste_params(copy_flag)
+                wx.CallAfter(self._onDraw, None)
+                
+        else:
+            self._draw_model()
+            
+        if self.batch_on:
+            self.slit_smearer.Enable(False)
+            self.pinhole_smearer.Enable(False)
+            self.btEditMask.Disable()
+            self.EditMask_title.Disable()
+            
+        self.Show(True)
+        self.SetupScrolling()
+         
+    def _onparamEnter(self, event):
+        """
+        when enter value on panel redraw model according to changed
+        """
+        if self.model == None:
+            msg = "Please select a Model first..."
+            wx.MessageBox(msg, 'Info')
+            return
+
+        #default flag
+        flag = False
+        self.fitrange = True
+        #get event object
+        tcrtl = event.GetEventObject()
+        #Clear msg if previously shown.
+        msg = ""
+        wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
+
+        if check_float(tcrtl):
+            flag = self._onparamEnter_helper()
+            self.show_npts2fit()
+            if self.fitrange:
+                temp_smearer = None
+                if not self.disable_smearer.GetValue():
+                    temp_smearer = self.current_smearer
+                    ## set smearing value whether or not
+                    #        the data contain the smearing info
+                    if self.slit_smearer.GetValue():
+                        flag1 = self.update_slit_smear()
+                        flag = flag or flag1
+                    elif self.pinhole_smearer.GetValue():
+                        flag1 = self.update_pinhole_smear()
+                        flag = flag or flag1
+                elif self.data.__class__.__name__ != "Data2D" and \
+                        not self.enable2D:
+                    self._manager.set_smearer(smearer=temp_smearer,
+                                              fid=self.data.id,
+                                              uid=self.uid,
+                                              qmin=float(self.qmin_x),
+                                              qmax=float(self.qmax_x),
+                            enable_smearer=not self.disable_smearer.GetValue(),
+                                            draw=True)
+                if flag:
+                    #self.compute_chisqr(smearer= temp_smearer)
+        
+                    ## new state posted
+                    if self.state_change:
+                        #self._undo.Enable(True)
+                        event = PageInfoEvent(page=self)
+                        wx.PostEvent(self.parent, event)
+                    self.state_change = False
+            else:
+                # invalid fit range: do nothing here:
+                # msg already displayed in validate
+                return
+        else:
+            self.save_current_state()
+            msg = "Cannot Plot :Must enter a number!!!  "
+            wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
+             
+        self.save_current_state()
+        return
+   
+    def _onparamRangeEnter(self, event):
+        """
+        Check validity of value enter in the parameters range field
+        """
+        tcrtl = event.GetEventObject()
+        #Clear msg if previously shown.
+        msg = ""
+        wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
+        # Flag to register when a parameter has changed.
+        is_modified = False
+        if tcrtl.GetValue().lstrip().rstrip() != "":
+            try:
+                tcrtl.SetBackgroundColour(wx.WHITE)
+                self._check_value_enter(self.fittable_param, is_modified)
+                self._check_value_enter(self.parameters, is_modified)
+            except:
+                tcrtl.SetBackgroundColour("pink")
+                msg = "Model Error:wrong value entered : %s" % sys.exc_value
+                wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
+                return
+        else:
+            tcrtl.SetBackgroundColour(wx.WHITE)
+           
+        #self._undo.Enable(True)
+        self.save_current_state()
+        event = PageInfoEvent(page=self)
+        wx.PostEvent(self.parent, event)
+        self.state_change = False
+        
+    def qrang_set_focus(self, event=None):  
+        """
+        ON Qrange focus
+        """
+        if event != None:
+            event.Skip()
+        #tcrtl = event.GetEventObject()
+        self._validate_qrange(self.qmin, self.qmax)
+        
+    def qrange_click(self, event):
+        """
+        On Qrange textctrl click, make the qrange lines in the plot
+        """
+        if event != None:
+            event.Skip()
+        if self.data.__class__.__name__ == "Data2D":
+            return
+        is_click = event.LeftDown()
+        if is_click:
+            d_id = self.data.id
+            d_group_id = self.data.group_id
+            act_ctrl = event.GetEventObject()
+            wx.PostEvent(self._manager.parent, 
+                         PlotQrangeEvent(ctrl=[self.qmin, self.qmax], id=d_id, 
+                                     group_id=d_group_id, leftdown=is_click,
+                                     active=act_ctrl))
+            
+    def on_qrange_text(self, event):
+        """
+        #On q range value updated. DO not combine with qrange_click().
+        """
+        if event != None:
+            event.Skip()
+        if self.data.__class__.__name__ == "Data2D":
+            return
+        act_ctrl = event.GetEventObject()
+        d_id = self.data.id
+        d_group_id = self.data.group_id
+        wx.PostEvent(self._manager.parent, 
+                     PlotQrangeEvent(ctrl=[self.qmin, self.qmax], id=d_id, 
+                                     group_id=d_group_id, leftdown=False, 
+                                     active=act_ctrl))
+        self._validate_qrange(self.qmin, self.qmax)
+    
+    def on_key(self, event):   
+        """
+        On Key down
+        """
+        event.Skip()
+        if self.data.__class__.__name__ == "Data2D":
+            return
+        ctrl = event.GetEventObject()
+        try:
+            x_data = float(ctrl.GetValue())
+        except:
+            return 
+        key = event.GetKeyCode()
+        length = len(self.data.x)
+        indx = (numpy.abs(self.data.x - x_data)).argmin()
+        #return array.flat[idx]
+        if key == wx.WXK_PAGEUP or key == wx.WXK_NUMPAD_PAGEUP:
+            indx += 1
+            if indx >= length:
+                indx = length - 1
+        elif key == wx.WXK_PAGEDOWN or key == wx.WXK_NUMPAD_PAGEDOWN:
+            indx -= 1
+            if indx < 0:
+                indx = 0
+        else:
+            return
+        ctrl.SetValue(str(self.data.x[indx]))
+        self._validate_qrange(self.qmin, self.qmax)
+               
+    def _onQrangeEnter(self, event):
+        """
+        Check validity of value enter in the Q range field
+        """
+        tcrtl = event.GetEventObject()
+        #Clear msg if previously shown.
+        msg = ""
+        wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
+        # For theory mode
+        if not self.data.is_data:
+            self.npts_x = self.Npts_total.GetValue()
+            self.Npts_fit.SetValue(self.npts_x)
+            self.create_default_data()
+        # Flag to register when a parameter has changed.
+        if tcrtl.GetValue().lstrip().rstrip() != "":
+            try:
+                tcrtl.SetBackgroundColour(wx.WHITE)
+                # If qmin and qmax have been modified, update qmin and qmax
+                if self._validate_qrange(self.qmin, self.qmax):
+                    tempmin = float(self.qmin.GetValue())
+                    if tempmin != self.qmin_x:
+                        self.qmin_x = tempmin
+                    tempmax = float(self.qmax.GetValue())
+                    if tempmax != self.qmax_x:
+                        self.qmax_x = tempmax
+                else:
+                    tcrtl.SetBackgroundColour("pink")
+                    msg = "Model Error:wrong value entered : %s" % sys.exc_value
+                    wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
+                    return
+            except:
+                tcrtl.SetBackgroundColour("pink")
+                msg = "Model Error:wrong value entered : %s" % sys.exc_value
+                wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
+                return
+            #Check if # of points for theory model are valid(>0).
+            # check for 2d
+            if self.data.__class__.__name__ == "Data2D" or \
+                    self.enable2D:
+                # set mask
+                radius = numpy.sqrt(self.data.qx_data * self.data.qx_data +
+                                    self.data.qy_data * self.data.qy_data)
+                index_data = ((self.qmin_x <= radius) & \
+                                (radius <= self.qmax_x))
+                index_data = (index_data) & (self.data.mask)
+                index_data = (index_data) & (numpy.isfinite(self.data.data))
+                if len(index_data[index_data]) < 10:
+                    msg = "Cannot Plot :No or too little npts in"
+                    msg += " that data range!!!  "
+                    wx.PostEvent(self._manager.parent,
+                                 StatusEvent(status=msg))
+                    return
+                else:
+                    #self.data.mask = index_data
+                    #self.Npts_fit.SetValue(str(len(self.data.mask)))
+                    self.show_npts2fit()
+            else:
+                index_data = ((self.qmin_x <= self.data.x) & \
+                              (self.data.x <= self.qmax_x))
+                self.Npts_fit.SetValue(str(len(self.data.x[index_data])))
+            
+            self.npts_x = self.Npts_total.GetValue()
+            self.create_default_data()
+            self._save_plotting_range()
+        else:
+            tcrtl.SetBackgroundColour("pink")
+            msg = "Model Error:wrong value entered!!!"
+            wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
+        
+        self._draw_model()
+        self.save_current_state()
+        event = PageInfoEvent(page=self)
+        wx.PostEvent(self.parent, event)
+        self.state_change = False
+        return
+    
+    def _clear_Err_on_Fit(self):
+        """
+        hide the error text control shown
+        after fitting
+        """
+        if self.is_mac:
+            return
+        if hasattr(self, "text2_3"):
+            self.text2_3.Hide()
+
+        if len(self.parameters) > 0:
+            for item in self.parameters:
+                if item[0].IsShown():
+                    #Skip t ifhe angle parameters if 1D data
+                    if self.data.__class__.__name__ != "Data2D" and \
+                            not self.enable2D:
+                        if item in self.orientation_params:
+                            continue
+                    if item in self.param_toFit:
+                        continue
+                    ## hide statictext +/-
+                    if len(item) < 4:
+                        continue
+                    if item[3] != None and item[3].IsShown():
+                        item[3].Hide()
+                    ## hide textcrtl  for error after fit
+                    if item[4] != None and item[4].IsShown():
+                        item[4].Hide()
+  
+        if len(self.fittable_param) > 0:
+            for item in self.fittable_param:
+                if item[0].IsShown():
+                    #Skip t ifhe angle parameters if 1D data
+                    if self.data.__class__.__name__ != "Data2D" and \
+                            not self.enable2D:
+                        if item in self.orientation_params:
+                            continue
+                    if item in self.param_toFit:
+                        continue
+                    if len(item) < 4:
+                        continue
+                    ## hide statictext +/-
+                    if item[3] != None and item[3].IsShown():
+                        item[3].Hide()
+                    ## hide textcrtl  for error after fit
+                    if item[4] != None and item[4].IsShown():
+                        item[4].Hide()
+        return
+                
+    def _get_defult_custom_smear(self):
+        """
+        Get the defult values for custum smearing.
+        """
+        # get the default values
+        if self.dxl == None:
+            self.dxl = 0.0
+        if self.dxw == None:
+            self.dxw = ""
+        if self.dx_min == None:
+            self.dx_min = SMEAR_SIZE_L
+        if self.dx_max == None:
+            self.dx_max = SMEAR_SIZE_H
+        
+    def _get_smear_info(self):
+        """
+        Get the smear info from data.
+       
+        :return: self.smear_type, self.dq_l and self.dq_r,
+            respectively the type of the smear, dq_min and
+            dq_max for pinhole smear data
+            while dxl and dxw for slit smear
+        """
+        # default
+        self.smear_type = None
+        self.dq_l = None
+        self.dq_r = None
+        data = self.data
+        if self.data is None:
+            return
+        elif self.data.__class__.__name__ == "Data2D" or \
+            self.enable2D:
+            if data.dqx_data == None or  data.dqy_data == None:
+                return
+            elif self.current_smearer != None \
+                and data.dqx_data.any() != 0 \
+                and data.dqx_data.any() != 0:
+                self.smear_type = "Pinhole2d"
+                self.dq_l = format_number(numpy.average(data.dqx_data))
+                self.dq_r = format_number(numpy.average(data.dqy_data))
+                return
+            else:
+                return
+        # check if it is pinhole smear and get min max if it is.
+        if data.dx != None and all(data.dx != 0):
+            self.smear_type = "Pinhole"
+            self.dq_l = data.dx[0]
+            self.dq_r = data.dx[-1]
+            
+        # check if it is slit smear and get min max if it is.
+        elif data.dxl != None or data.dxw != None:
+            self.smear_type = "Slit"
+            if data.dxl != None and all(data.dxl != 0):
+                self.dq_l = data.dxl[0]
+            if data.dxw != None and all(data.dxw != 0):
+                self.dq_r = data.dxw[0]
+        #return self.smear_type,self.dq_l,self.dq_r
+    
+    def _show_smear_sizer(self):
+        """
+        Show only the sizers depending on smear selection
+        """
+        # smear disabled
+        if self.disable_smearer.GetValue():
+            self.smear_description_none.Show(True)
+        # 2Dsmear
+        elif self._is_2D():
+            self.smear_description_accuracy_type.Show(True)
+            self.smear_accuracy.Show(True)
+            self.smear_description_accuracy_type.Show(True)
+            self.smear_description_2d.Show(True)
+            self.smear_description_2d_x.Show(True)
+            self.smear_description_2d_y.Show(True)
+            if self.pinhole_smearer.GetValue():
+                self.smear_pinhole_min.Show(True)
+                self.smear_pinhole_max.Show(True)
+        # smear from data
+        elif self.enable_smearer.GetValue():
+
+            self.smear_description_dqdata.Show(True)
+            if self.smear_type != None:
+                self.smear_description_smear_type.Show(True)
+                if self.smear_type == 'Slit':
+                    self.smear_description_slit_height.Show(True)
+                    self.smear_description_slit_width.Show(True)
+                elif self.smear_type == 'Pinhole':
+                    self.smear_description_pin_min.Show(True)
+                    self.smear_description_pin_max.Show(True)
+                self.smear_description_smear_type.Show(True)
+                self.smear_description_type.Show(True)
+                self.smear_data_left.Show(True)
+                self.smear_data_right.Show(True)
+        # custom pinhole smear
+        elif self.pinhole_smearer.GetValue():
+            if self.smear_type == 'Pinhole':
+                self.smear_message_new_p.Show(True)
+                self.smear_description_pin_min.Show(True)
+                self.smear_description_pin_max.Show(True)
+
+            self.smear_pinhole_min.Show(True)
+            self.smear_pinhole_max.Show(True)
+        # custom slit smear
+        elif self.slit_smearer.GetValue():
+            self.smear_message_new_s.Show(True)
+            self.smear_description_slit_height.Show(True)
+            self.smear_slit_height.Show(True)
+            self.smear_description_slit_width.Show(True)
+            self.smear_slit_width.Show(True)
+
+    def _hide_all_smear_info(self):
+        """
+        Hide all smearing messages in the set_smearer sizer
+        """
+        self.smear_description_none.Hide()
+        self.smear_description_dqdata.Hide()
+        self.smear_description_type.Hide()
+        self.smear_description_smear_type.Hide()
+        self.smear_description_accuracy_type.Hide()
+        self.smear_description_2d_x.Hide()
+        self.smear_description_2d_y.Hide()
+        self.smear_description_2d.Hide()
+        
+        self.smear_accuracy.Hide()
+        self.smear_data_left.Hide()
+        self.smear_data_right.Hide()
+        self.smear_description_pin_min.Hide()
+        self.smear_pinhole_min.Hide()
+        self.smear_description_pin_max.Hide()
+        self.smear_pinhole_max.Hide()
+        self.smear_description_slit_height.Hide()
+        self.smear_slit_height.Hide()
+        self.smear_description_slit_width.Hide()
+        self.smear_slit_width.Hide()
+        self.smear_message_new_p.Hide()
+        self.smear_message_new_s.Hide()
+    
+    def _set_accuracy_list(self):
+        """
+        Set the list of an accuracy in 2D custum smear:
+                Xhigh, High, Med, or Low
+        """
+        # list of accuracy choices
+        list = ['Low', 'Med', 'High', 'Xhigh']
+        for idx in range(len(list)):
+            self.smear_accuracy.Append(list[idx], idx)
+            
+    def _set_fun_box_list(self, fun_box):
+        """
+        Set the list of func for multifunctional models
+        """
+        # Check if it is multi_functional model
+        if self.model.__class__ not in self.model_list_box["Multi-Functions"] \
+                and not self.temp_multi_functional:
+            return None
+        # Get the func name list
+        list = self.model.fun_list
+        if len(list) == 0:
+            return None
+        # build function (combo)box
+        ind = 0
+        while(ind < len(list)):
+            for key, val in list.iteritems():
+                if (val == ind):
+                    fun_box.Append(key, val)
+                    break
+            ind += 1
+        
+    def _on_select_accuracy(self, event):
+        """
+        Select an accuracy in 2D custom smear: Xhigh, High, Med, or Low
+        """
+        #event.Skip()
+        # Check if the accuracy is same as before
+        #self.smear2d_accuracy = event.GetEventObject().GetValue()
+        self.smear2d_accuracy = self.smear_accuracy.GetValue()
+        if self.pinhole_smearer.GetValue():
+            self.onPinholeSmear(event=None)
+        else:
+            self.onSmear(event=None)
+            if self.current_smearer != None:
+                self.current_smearer.set_accuracy(accuracy=\
+                                                  self.smear2d_accuracy) 
+        event.Skip()
+
+    def _on_fun_box(self, event):
+        """
+        Select an func: Erf,Rparabola,LParabola
+        """
+        fun_val = None
+        fun_box = event.GetEventObject()
+        name = fun_box.Name
+        value = fun_box.GetValue()
+        if value in self.model.fun_list:
+            fun_val = self.model.fun_list[value]
+
+        self.model.setParam(name, fun_val)
+        # save state
+        self._copy_parameters_state(self.str_parameters,
+                                    self.state.str_parameters)
+        # update params
+        self._update_paramv_on_fit()
+        # draw
+        self._draw_model()
+        self.Refresh()
+        # get ready for new event
+        event.Skip()
+        
+    def _onMask(self, event):
+        """
+        Build a panel to allow to edit Mask
+        """
+        from sas.guiframe.local_perspectives.plotting.masking \
+        import MaskPanel as MaskDialog
+        
+        self.panel = MaskDialog(base=self, data=self.data, id=wx.NewId())
+        self.panel.ShowModal()
+        
+    def _draw_masked_model(self, event):
+        """
+        Draw model image w/mask
+        """
+        is_valid_qrange = self._update_paramv_on_fit()
+
+        if is_valid_qrange and self.model != None:
+            self.panel.MakeModal(False)
+            event.Skip()
+            # try re draw the model plot if it exists
+            self._draw_model()
+            self.show_npts2fit()
+        elif self.model == None:
+            self.panel.MakeModal(False)
+            event.Skip()
+            self.show_npts2fit()
+            msg = "No model is found on updating MASK in the model plot... "
+            wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
+        else:
+            event.Skip()
+            msg = ' Please consider your Q range, too.'
+            self.panel.ShowMessage(msg)
+
+    def _set_smear(self, data):
+        """
+        Set_smear
+        """
+        if data is None:
+            return
+        self.current_smearer = smear_selection(data, self.model)
+        flag = self.disable_smearer.GetValue()
+        self.disable_smearer.SetValue(flag)
+        if self.current_smearer == None:
+            self.enable_smearer.Disable()
+        else:
+            self.enable_smearer.Enable()
+        if not flag:
+            self.onSmear(None)
+
+    def _mac_sleep(self, sec=0.2):
+        """
+        Give sleep to MAC
+        """
+        if self.is_mac:
+            time.sleep(sec)
+
+    def get_view_mode(self):
+        """
+        return True if the panel allow 2D or False if 1D
+        """
+        return self.enable2D
+    
+    def compute_data_set_range(self, data_list):
+        """
+        find the range that include all data  in the set
+        return the minimum and the maximum values
+        """
+        if data_list is not None and data_list != []:
+            for data in data_list:
+                qmin, qmax, npts = self.compute_data_range(data)
+                self.qmin_data_set = min(self.qmin_data_set, qmin)
+                self.qmax_data_set = max(self.qmax_data_set, qmax)
+                self.npts_data_set += npts
+        return self.qmin_data_set, self.qmax_data_set, self.npts_data_set
+        
+    def compute_data_range(self, data):
+        """
+        compute the minimum and the maximum range of the data
+        return the npts contains in data
+        :param data:
+        """
+        qmin, qmax, npts = None, None, None
+        if data is not None:
+            if not hasattr(data, "data"):
+                try:
+                    qmin = min(data.x)
+                    # Maximum value of data
+                    qmax = max(data.x)
+                    npts = len(data.x)
+                except:
+                    msg = "Unable to find min/max/length of \n data named %s"% \
+                                data.filename  
+                    wx.PostEvent(self._manager.parent, StatusEvent(status=msg,
+                                               info="error"))
+                    raise ValueError, msg
+                    
+            else:
+                qmin = 0
+                try:
+                    x = max(math.fabs(data.xmin), math.fabs(data.xmax))
+                    y = max(math.fabs(data.ymin), math.fabs(data.ymax))
+                except:
+                    msg = "Unable to find min/max of \n data named %s"% \
+                                data.filename  
+                    wx.PostEvent(self._manager.parent, StatusEvent(status=msg,
+                                               info="error"))
+                    raise ValueError, msg
+                ## Maximum value of data
+                qmax = math.sqrt(x * x + y * y)
+                npts = len(data.data)
+        return qmin, qmax, npts
+            
+    def set_data(self, data):
+        """
+        reset the current data
+        """
+        id = None
+        flag = False
+        is_data = False
+        try:
+            old_id = self.data.id
+            old_group_id = self.data.group_id
+        except:
+            old_id = id
+            old_group_id = id
+        if self.data is not None:
+            is_data = check_data_validity(self.data)
+        if not is_data and data is not None:
+                flag = True
+        if data is not None:
+            id = data.id
+            if is_data:
+                self.graph_id = self.data.group_id
+                flag = (data.id != self.data.id)
+        self.data = data
+        if check_data_validity(data):
+            self.graph_id = data.group_id
+        self.data.group_id = self.graph_id
+        
+        if self.data is None:
+            data_name = ""
+            self._set_bookmark_flag(False)
+            self._keep.Enable(False)
+            self._set_save_flag(False)
+        else:
+            if self.model != None:
+                self._set_bookmark_flag(not self.batch_on)
+                self._keep.Enable(not self.batch_on)
+            if self.data.is_data:    
+                self._set_save_flag(True)
+                self._set_preview_flag(True)
+
+            self._set_smear(data)
+            # more disables for 2D
+            if self.data.__class__.__name__ == "Data2D" or \
+                        self.enable2D:
+                self.slit_smearer.Disable()
+                self.pinhole_smearer.Enable(True)
+                self.default_mask = copy.deepcopy(self.data.mask)
+                if self.data.err_data == None or\
+                        (self.data.err_data == 1).all() or\
+                        (self.data.err_data == 0).all():
+                    self.dI_didata.Enable(False)
+                    self.dI_noweight.SetValue(True)
+                    self.weightbt_string = self.dI_noweight.GetLabelText()
+                else:
+                    self.dI_didata.Enable(True)
+                    self.dI_didata.SetValue(True)
+                    self.weightbt_string = self.dI_didata.GetLabelText()
+            else:
+                self.slit_smearer.Enable(True)
+                self.pinhole_smearer.Enable(True)
+                if self.data.dy == None or\
+                     (self.data.dy == 1).all() or\
+                     (self.data.dy == 0).all():
+                    self.dI_didata.Enable(False)
+                    self.dI_noweight.SetValue(True)
+                    self.weightbt_string = self.dI_noweight.GetLabelText()
+                else:
+                    self.dI_didata.Enable(True)
+                    self.dI_didata.SetValue(True)
+                    self.weightbt_string = self.dI_didata.GetLabelText()
+            # Enable weighting radio uttons
+            self.dI_noweight.Enable(True)
+            self.dI_sqrdata.Enable(True)
+            self.dI_idata.Enable(True)
+              
+            self.formfactorbox.Enable()
+            self.structurebox.Enable()
+            data_name = self.data.name
+            _, _, npts = self.compute_data_range(self.data)
+            #set maximum range for x in linear scale
+            if not hasattr(self.data, "data"):  # Display only for 1D data fit
+                self.btEditMask.Disable()
+                self.EditMask_title.Disable()
+            else:
+                self.btEditMask.Enable()
+                self.EditMask_title.Enable()
+    
+        self.Npts_total.SetValue(str(npts))
+        #default:number of data points selected to fit
+        self.Npts_fit.SetValue(str(npts))
+        self.Npts_total.SetEditable(False)
+        self.Npts_total.SetBackgroundColour(\
+                                    self.GetParent().GetBackgroundColour())
+        
+        self.Npts_total.Bind(wx.EVT_MOUSE_EVENTS, self._npts_click)
+        self.pointsbox.Disable()
+        self.dataSource.SetValue(data_name)
+        self.state.data = data
+        self.enable_fit_button()
+        # send graph_id to page_finder
+        self._manager.set_graph_id(uid=self.uid, graph_id=self.graph_id)
+        #focus the page
+        if check_data_validity(data):
+            self.data_box_description.SetForegroundColour(wx.BLUE)
+        
+        if self.batch_on:
+            self.slit_smearer.Enable(False)
+            self.pinhole_smearer.Enable(False)
+            self.btEditMask.Disable()
+            self.EditMask_title.Disable()
+
+        self.on_set_focus(None)
+        self.Refresh()
+        #update model plot with new data information
+        if flag:
+            #set model view button
+            if not self.enable_smearer.GetValue():
+                    self.disable_smearer.SetValue(True)
+            self.onSmear(None)
+
+            if self.data.__class__.__name__ == "Data2D":
+                self.enable2D = True
+                self.model_view.SetLabel("2D Mode")
+            else:
+                self.enable2D = False
+                self.model_view.SetLabel("1D Mode")
+            self.model_view.Disable()
+            #replace data plot on combo box selection
+            #by removing the previous selected data
+            try:
+                wx.PostEvent(self._manager.parent,
+                             NewPlotEvent(action="delete",
+                                          group_id=old_group_id, id=old_id))
+            except:
+                pass
+            #plot the current selected data
+            wx.PostEvent(self._manager.parent,
+                         NewPlotEvent(action="check", plot=self.data,
+                                      title=str(self.data.title)))
+            self._draw_model()
+    
+    def _npts_click(self, event):
+        """
+        Prevent further handling of the mouse event on Npts_total
+        by not calling Skip().
+        """
+        pass
+    
+    def reset_page(self, state, first=False):
+        """
+        reset the state
+        """
+        try:
+            self.reset_page_helper(state)
+    
+            self.select_param(event=None)
+            #Save state_fit
+            self.save_current_state_fit()
+        except:
+            self._show_combox_helper()
+            msg = "Error: This model state has missing or outdated "
+            msg += "information.\n"
+            msg += "%s"% (sys.exc_value)
+            wx.PostEvent(self._manager.parent,
+                         StatusEvent(status=msg, info="error"))
+        self._lay_out()
+        self.Refresh()
+        
+    def get_range(self):
+        """
+        return the fitting range
+        """
+        return float(self.qmin_x), float(self.qmax_x)
+    
+    def get_npts2fit(self):
+        """
+        return numbers of data points within qrange
+        
+        :Note: This is for Park where chi2 is not normalized by Npts of fit
+        
+        """
+        if self.data is None:
+            return
+        npts2fit = 0
+        qmin, qmax = self.get_range()
+        if self.data.__class__.__name__ == "Data2D" or \
+                        self.enable2D:
+            radius = numpy.sqrt(self.data.qx_data * self.data.qx_data +
+                                self.data.qy_data * self.data.qy_data)
+            index_data = (self.qmin_x <= radius) & (radius <= self.qmax_x)
+            index_data = (index_data) & (self.data.mask)
+            index_data = (index_data) & (numpy.isfinite(self.data.data))
+            npts2fit = len(self.data.data[index_data])
+        else:
+            for qx in self.data.x:
+                if qx >= qmin and qx <= qmax:
+                    npts2fit += 1
+        return npts2fit
+
+    def show_npts2fit(self):
+        """
+        setValue Npts for fitting
+        """
+        self.Npts_fit.SetValue(str(self.get_npts2fit()))
+        
+    def get_chi2(self):
+        """
+        return the current chi2
+        """
+        return self.tcChi.GetValue()
+        
+    def onsetValues(self, chisqr, p_name, out, cov):
+        """
+        Build the panel from the fit result
+        
+        :param chisqr: Value of the goodness of fit metric
+        :param p_name: the name of parameters
+        :param out: list of parameter with the best value found during fitting
+        :param cov: Covariance matrix
+   
+        """
+        # make sure stop button to fit button all the time
+        self._on_fit_complete()
+        if out == None or not numpy.isfinite(chisqr):
+            raise ValueError, "Fit error occured..."
+        
+        is_modified = False
+        has_error = False
+        dispersity = ''
+        
+        #Hide textctrl boxes of errors.
+        self._clear_Err_on_Fit()
+        
+        #Check if chi2 is finite
+        if chisqr != None and numpy.isfinite(chisqr):
+            #format chi2
+            chi2 = format_number(chisqr, True)
+            self.tcChi.SetValue(chi2)
+            self.tcChi.Refresh()
+        else:
+            self.tcChi.SetValue("-")
+        
+        #Hide error title
+        if self.text2_3.IsShown() and not self.is_mac:
+            self.text2_3.Hide()
+      
+        try:
+            if self.enable_disp.GetValue():
+                if hasattr(self, "text_disp_1"):
+                    if self.text_disp_1 != None and not self.is_mac:
+                        self.text_disp_1.Hide()
+        except:
+            dispersity = None
+            pass
+      
+        i = 0
+        #Set the panel when fit result are list
+        for item in self.param_toFit:
+            if len(item) > 5 and item != None:
+                if item[0].IsShown():
+                    ## reset error value to initial state
+                    if not self.is_mac:
+                        item[3].Hide()
+                        item[4].Hide()
+                    for ind in range(len(out)):
+                        if item[1] == p_name[ind]:
+                            break
+                    if len(out) > 0 and out[ind] != None:
+                        val_out = format_number(out[ind], True)
+                        item[2].SetValue(val_out)
+    
+                    if(cov != None and len(cov) == len(out)):
+                        try:
+                            if dispersity != None:
+                                if self.enable_disp.GetValue():
+                                    if hasattr(self, "text_disp_1"):
+                                        if self.text_disp_1 != None:
+                                            if not self.text_disp_1.IsShown()\
+                                                and not self.is_mac:
+                                                self.text_disp_1.Show(True)
+                        except:
+                            pass
+                        
+                        if cov[ind] != None:
+                            if numpy.isfinite(float(cov[ind])):
+                                val_err = format_number(cov[ind], True)
+                                if not self.is_mac:
+                                    item[3].Show(True)
+                                    item[4].Show(True)
+                                item[4].SetValue(val_err)
+                                has_error = True
+                i += 1
+            else:
+                raise ValueError, "onsetValues: Invalid parameters..."
+        #Show error title when any errors displayed
+        if has_error:
+            if not self.text2_3.IsShown():
+                self.text2_3.Show(True)
+        ## save current state
+        self.save_current_state()
+        
+        if not self.is_mac:
+            self.Layout()
+            self.Refresh()
+        self._mac_sleep(0.1)
+        #plot model ( when drawing, do not update chisqr value again)
+        self._draw_model(update_chisqr=False, source='fit')
+    
+    def onWeighting(self, event):
+        """
+        On Weighting radio button event, sets the weightbt_string
+        """
+        self.weightbt_string = event.GetEventObject().GetLabelText()
+        self._set_weight()
+        
+    def _set_weight(self, is_2D=None):
+        """
+        Set weight in fit problem
+        """
+        # compute weight for the current data
+        from sas import get_weight
+        flag_weight = self.get_weight_flag()
+        if is_2D == None:
+            is_2D = self._is_2D()
+        weight = get_weight(data=self.data,
+                            is2d=is_2D,
+                            flag=flag_weight)
+        self._manager.set_fit_weight(uid=self.uid,
+                                     flag=flag_weight,
+                                     is2d=is_2D,
+                                     fid=None)
+    
+    def onPinholeSmear(self, event):
+        """
+        Create a custom pinhole smear object that will change the way residuals
+        are compute when fitting
+        
+        :Note: accuracy is given by strings'High','Med', 'Low' FOR 2d,
+                     None for 1D
+                     
+        """
+        if self.model == None:
+            self.disable_smearer.SetValue(True)
+            if event == None:
+                return
+            msg = "Please select a Model first..."
+            wx.MessageBox(msg, 'Info')
+            wx.PostEvent(self._manager.parent,
+                         StatusEvent(status="Smear: %s" % msg))
+            return
+
+        # Need update param values
+        self._update_paramv_on_fit()
+
+        # msg default
+        msg = None
+        if event != None:
+            tcrtl = event.GetEventObject()
+            # event case of radio button
+            if tcrtl.GetValue() == True:
+                self.dx_min = 0.0
+                self.dx_max = 0.0
+                is_new_pinhole = True
+            else:
+                is_new_pinhole = self._is_changed_pinhole()
+        else:
+            is_new_pinhole = True
+        # if any value is changed
+        if is_new_pinhole:
+            msg = self._set_pinhole_smear()
+        # hide all silt sizer
+        self._hide_all_smear_info()
+        
+        # show relevant slit sizers
+        self._show_smear_sizer()
+
+        self.sizer_set_smearer.Layout()
+        self.Layout()
+        
+        if event != None:
+            event.Skip()
+        #self._undo.Enable(True)
+        self.save_current_state()
+        event = PageInfoEvent(page=self)
+        wx.PostEvent(self.parent, event)
+        
+    def _is_changed_pinhole(self):
+        """
+        check if any of pinhole smear is changed
+        
+        :return: True or False
+        
+        """
+        # get the values
+        pin_min = self.smear_pinhole_min.GetValue()
+        pin_max = self.smear_pinhole_max.GetValue()
+                    
+        # Check changes in slit width
+        try:
+            dx_min = float(pin_min)
+        except:
+            return True
+        if self.dx_min != dx_min:
+            return True
+        
+        # Check changes in slit heigth
+        try:
+            dx_max = float(pin_max)
+        except:
+            return True
+        if self.dx_max != dx_max:
+            return True
+        return False
+    
+    def _set_pinhole_smear(self):
+        """
+        Set custom pinhole smear
+        
+        :return: msg
+        
+        """
+        # copy data
+        data = copy.deepcopy(self.data)
+        if self._is_2D():
+            self.smear_type = 'Pinhole2d'
+            len_data = len(data.data)
+            data.dqx_data = numpy.zeros(len_data)
+            data.dqy_data = numpy.zeros(len_data)
+        else:
+            self.smear_type = 'Pinhole'
+            len_data = len(data.x)
+            data.dx = numpy.zeros(len_data)
+            data.dxl = None
+            data.dxw = None
+        msg = None
+   
+        get_pin_min = self.smear_pinhole_min
+        get_pin_max = self.smear_pinhole_max
+
+        if not check_float(get_pin_min):
+            get_pin_min.SetBackgroundColour("pink")
+            msg = "Model Error:wrong value entered!!!"
+        elif not check_float(get_pin_max):
+            get_pin_max.SetBackgroundColour("pink")
+            msg = "Model Error:wrong value entered!!!"
+        else:
+            if len_data < 2:
+                len_data = 2
+            self.dx_min = float(get_pin_min.GetValue())
+            self.dx_max = float(get_pin_max.GetValue())
+            if self.dx_min < 0:
+                get_pin_min.SetBackgroundColour("pink")
+                msg = "Model Error:This value can not be negative!!!"
+            elif self.dx_max < 0:
+                get_pin_max.SetBackgroundColour("pink")
+                msg = "Model Error:This value can not be negative!!!"
+            elif self.dx_min != None and self.dx_max != None:
+                if self._is_2D():
+                    data.dqx_data[data.dqx_data == 0] = self.dx_min
+                    data.dqy_data[data.dqy_data == 0] = self.dx_max
+                elif self.dx_min == self.dx_max:
+                    data.dx[data.dx == 0] = self.dx_min
+                else:
+                    step = (self.dx_max - self.dx_min) / (len_data - 1)
+                    data.dx = numpy.arange(self.dx_min,
+                                           self.dx_max + step / 1.1,
+                                           step)
+            elif self.dx_min != None:
+                if self._is_2D():
+                    data.dqx_data[data.dqx_data == 0] = self.dx_min
+                else:
+                    data.dx[data.dx == 0] = self.dx_min
+            elif self.dx_max != None:
+                if self._is_2D():
+                    data.dqy_data[data.dqy_data == 0] = self.dx_max
+                else:
+                    data.dx[data.dx == 0] = self.dx_max
+            self.current_smearer = smear_selection(data, self.model)
+            # 2D need to set accuracy
+            if self._is_2D():
+                self.current_smearer.set_accuracy(accuracy=\
+                                                  self.smear2d_accuracy)
+
+        if msg != None:
+            wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
+        else:
+            get_pin_min.SetBackgroundColour("white")
+            get_pin_max.SetBackgroundColour("white")
+        ## set smearing value whether or not the data contain the smearing info
+        
+        self._manager.set_smearer(smearer=self.current_smearer,
+                            fid=self.data.id,
+                            qmin=float(self.qmin_x),
+                            qmax=float(self.qmax_x),
+                            enable_smearer=not self.disable_smearer.GetValue(),
+                            uid=self.uid)
+        return msg
+        
+    def update_pinhole_smear(self):
+        """
+        called by kill_focus on pinhole TextCntrl
+        to update the changes
+        
+        :return: False when wrong value was entered
+        
+        """
+        # msg default
+        msg = None
+        # check if any value is changed
+        if self._is_changed_pinhole():
+            msg = self._set_pinhole_smear()
+        wx.CallAfter(self.save_current_state)
+
+        if msg != None:
+            return False
+        else:
+            return True
+                     
+    def onSlitSmear(self, event):
+        """
+        Create a custom slit smear object that will change the way residuals
+        are compute when fitting
+        """
+        if self.model == None:
+            self.disable_smearer.SetValue(True)
+            if event == None:
+                return
+            msg = "Please select a Model first..."
+            wx.MessageBox(msg, 'Info')
+            wx.PostEvent(self._manager.parent,
+                         StatusEvent(status="Smear: %s" % msg))
+            return
+
+        # Need update param values
+        self._update_paramv_on_fit()
+
+        # msg default
+        msg = None
+        # for event given
+        if event != None:
+            tcrtl = event.GetEventObject()
+            # event case of radio button
+            if tcrtl.GetValue():
+                self.dxl = 0.0
+                self.dxw = 0.0
+                is_new_slit = True
+            else:
+                is_new_slit = self._is_changed_slit()
+        else:
+            is_new_slit = True
+        
+        # if any value is changed
+        if is_new_slit:
+            msg = self._set_slit_smear()
+            
+        # hide all silt sizer
+        self._hide_all_smear_info()
+        # show relevant slit sizers
+        self._show_smear_sizer()
+        self.sizer_set_smearer.Layout()
+        self.Layout()
+
+        if event != None:
+            event.Skip()
+        self.save_current_state()
+        event = PageInfoEvent(page=self)
+        wx.PostEvent(self.parent, event)
+        if msg != None:
+            wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
+
+    def _is_changed_slit(self):
+        """
+        check if any of slit lengths is changed
+        
+        :return: True or False
+        
+        """
+        # get the values
+        width = self.smear_slit_width.GetValue()
+        height = self.smear_slit_height.GetValue()
+        
+        # check and change the box bg color if it was pink
+        #    but it should be white now
+        # because this is the case that _set_slit_smear() will not handle
+        if height.lstrip().rstrip() == "":
+            self.smear_slit_height.SetBackgroundColour(wx.WHITE)
+        if width.lstrip().rstrip() == "":
+            self.smear_slit_width.SetBackgroundColour(wx.WHITE)
+            
+        # Check changes in slit width
+        if width == "":
+            dxw = 0.0
+        else:
+            try:
+                dxw = float(width)
+            except:
+                return True
+        if self.dxw != dxw:
+            return True
+        
+        # Check changes in slit heigth
+        if height == "":
+            dxl = 0.0
+        else:
+            try:
+                dxl = float(height)
+            except:
+                return True
+        if self.dxl != dxl:
+            return True
+
+        return False
+    
+    def _set_slit_smear(self):
+        """
+        Set custom slit smear
+        
+        :return: message to inform the user about the validity
+            of the values entered for slit smear
+        """
+        if self.data.__class__.__name__ == "Data2D" or \
+                        self.enable2D:
+            return
+        # make sure once more if it is smearer
+        data = copy.deepcopy(self.data)
+        data_len = len(data.x)
+        data.dx = None
+        data.dxl = None
+        data.dxw = None
+        msg = None
+   
+        try:
+            self.dxl = float(self.smear_slit_height.GetValue())
+            data.dxl = self.dxl * numpy.ones(data_len)
+            self.smear_slit_height.SetBackgroundColour(wx.WHITE)
+        except:
+            self.dxl = None
+            data.dxl = numpy.zeros(data_len)
+            if self.smear_slit_height.GetValue().lstrip().rstrip() != "":
+                self.smear_slit_height.SetBackgroundColour("pink")
+                msg = "Wrong value entered... "
+            else:
+                self.smear_slit_height.SetBackgroundColour(wx.WHITE)
+        try:
+            self.dxw = float(self.smear_slit_width.GetValue())
+            self.smear_slit_width.SetBackgroundColour(wx.WHITE)
+            data.dxw = self.dxw * numpy.ones(data_len)
+        except:
+            self.dxw = None
+            data.dxw = numpy.zeros(data_len)
+            if self.smear_slit_width.GetValue().lstrip().rstrip() != "":
+                self.smear_slit_width.SetBackgroundColour("pink")
+                msg = "Wrong Fit value entered... "
+            else:
+                self.smear_slit_width.SetBackgroundColour(wx.WHITE)
+              
+        self.current_smearer = smear_selection(data, self.model)
+        ## set smearing value whether or not the data contain the smearing info
+        self._manager.set_smearer(smearer=self.current_smearer,
+                                 fid=self.data.id,
+                                 qmin=float(self.qmin_x),
+                                 qmax=float(self.qmax_x),
+                        enable_smearer=not self.disable_smearer.GetValue(),
+                                 uid=self.uid)
+        return msg
+    
+    def update_slit_smear(self):
+        """
+        called by kill_focus on pinhole TextCntrl
+        to update the changes
+        
+        :return: False when wrong value was entered
+        
+        """
+        # msg default
+        msg = None
+        # check if any value is changed
+        if self._is_changed_slit():
+            msg = self._set_slit_smear()
+        #self._undo.Enable(True)
+        self.save_current_state()
+
+        if msg != None:
+            return False
+        else:
+            return True
+                            
+    def onSmear(self, event):
+        """
+        Create a smear object that will change the way residuals
+        are compute when fitting
+        """
+        if event != None:
+            event.Skip()
+        if self.data is None:
+            return
+
+        if self.model == None:
+            self.disable_smearer.SetValue(True)
+            if event == None:
+                return
+            msg = "Please select a Model first..."
+            wx.MessageBox(msg, 'Info')
+            wx.PostEvent(self._manager.parent,
+                         StatusEvent(status="Smear: %s" % msg))
+            return
+        # Need update param values
+        self._update_paramv_on_fit()
+        if self.model != None:
+            if self.data.is_data:
+                self._manager.page_finder[self.uid].add_data(data=self.data)
+        temp_smearer = self.on_smear_helper()
+        
+        self.sizer_set_smearer.Layout()
+        self.Layout()
+        self._set_weight()
+        
+        ## set smearing value whether or not the data contain the smearing info
+        wx.CallAfter(self._manager.set_smearer, uid=self.uid,
+                     smearer=temp_smearer,
+                     fid=self.data.id,
+                     qmin=float(self.qmin_x),
+                     qmax=float(self.qmax_x),
+                     enable_smearer=not self.disable_smearer.GetValue(),
+                     draw=True)
+        
+        self.state.enable_smearer = self.enable_smearer.GetValue()
+        self.state.disable_smearer = self.disable_smearer.GetValue()
+        self.state.pinhole_smearer = self.pinhole_smearer.GetValue()
+        self.state.slit_smearer = self.slit_smearer.GetValue()
+        
+    def on_smear_helper(self, update=False):
+        """
+        Help for onSmear
+        
+        :param update: force or not to update
+        """
+        self._get_smear_info()
+        #renew smear sizer
+        if self.smear_type != None:
+            self.smear_description_smear_type.SetValue(str(self.smear_type))
+            self.smear_data_left.SetValue(str(self.dq_l))
+            self.smear_data_right.SetValue(str(self.dq_r))
+
+        self._hide_all_smear_info()
+        data = copy.deepcopy(self.data)
+        
+        # make sure once more if it is smearer
+        temp_smearer = smear_selection(data, self.model)
+        if self.current_smearer != temp_smearer or update:
+            self.current_smearer = temp_smearer
+        if self.enable_smearer.GetValue():
+            if self.current_smearer == None:
+                wx.PostEvent(self._manager.parent,
+                    StatusEvent(status="Data contains no smearing information"))
+            else:
+                wx.PostEvent(self._manager.parent,
+                    StatusEvent(status="Data contains smearing information"))
+
+            self.smear_data_left.Show(True)
+            self.smear_data_right.Show(True)
+            temp_smearer = self.current_smearer
+        elif self.disable_smearer.GetValue():
+            self.smear_description_none.Show(True)
+        elif self.pinhole_smearer.GetValue():
+            self.onPinholeSmear(None)
+        elif self.slit_smearer.GetValue():
+            self.onSlitSmear(None)
+        self._show_smear_sizer()
+        
+        return temp_smearer
+    
+    def on_complete_chisqr(self, event):
+        """
+        Display result chisqr on the panel
+        :event: activated by fitting/ complete after draw
+        """
+        try:
+            if event == None:
+                output = "-"
+            elif not numpy.isfinite(event.output):
+                output = "-"
+            else:
+                output = event.output
+            self.tcChi.SetValue(str(format_number(output, True)))
+            self.state.tcChi = self.tcChi.GetValue()
+        except:
+            pass
+            
+    def get_all_checked_params(self):
+        """
+        Found all parameters current check and add them to list of parameters
+        to fit
+        """
+        self.param_toFit = []
+        for item in self.parameters:
+            if item[0].GetValue() and item not in self.param_toFit:
+                if item[0].IsShown():
+                    self.param_toFit.append(item)
+        for item in self.fittable_param:
+            if item[0].GetValue() and item not in self.param_toFit:
+                if item[0].IsShown():
+                    self.param_toFit.append(item)
+        self.save_current_state_fit()
+       
+        event = PageInfoEvent(page=self)
+        wx.PostEvent(self.parent, event)
+        param2fit = []
+        for item in self.param_toFit:
+            if item[0] and item[0].IsShown():
+                param2fit.append(item[1])
+        self._manager.set_param2fit(self.uid, param2fit)
+                
+    def select_all_param(self, event):
+        """
+        set to true or false all checkBox given the main checkbox value cb1
+        """
+        self.param_toFit = []
+        if  self.parameters != []:
+            if  self.cb1.GetValue():
+                for item in self.parameters:
+                    if item[0].IsShown():
+                        ## for data2D select all to fit
+                        if self.data.__class__.__name__ == "Data2D" or \
+                                self.enable2D:
+                            item[0].SetValue(True)
+                            self.param_toFit.append(item)
+                        else:
+                            ## for 1D all parameters except orientation
+                            if not item in self.orientation_params:
+                                item[0].SetValue(True)
+                                self.param_toFit.append(item)
+                    else:
+                        item[0].SetValue(False)
+                #if len(self.fittable_param)>0:
+                for item in self.fittable_param:
+                    if item[0].IsShown():
+                        if self.data.__class__.__name__ == "Data2D" or \
+                                self.enable2D:
+                            item[0].SetValue(True)
+                            self.param_toFit.append(item)
+                            try:
+                                if len(self.values[item[1]]) > 0:
+                                    item[0].SetValue(False)
+                            except:
+                                pass
+    
+                        else:
+                            ## for 1D all parameters except orientation
+                            if not item in self.orientation_params_disp:
+                                item[0].SetValue(True)
+                                self.param_toFit.append(item)
+                                try:
+                                    if len(self.values[item[1]]) > 0:
+                                        item[0].SetValue(False)
+                                except:
+                                    pass
+                    else:
+                        item[0].SetValue(False)
+
+            else:
+                for item in self.parameters:
+                    item[0].SetValue(False)
+                for item in self.fittable_param:
+                    item[0].SetValue(False)
+                self.param_toFit = []
+           
+        self.save_current_state_fit()
+       
+        if event != None:
+            #self._undo.Enable(True)
+            ## post state to fit panel
+            event = PageInfoEvent(page=self)
+            wx.PostEvent(self.parent, event)
+        param2fit = []
+        for item in self.param_toFit:
+            if item[0] and item[0].IsShown():
+                param2fit.append(item[1])
+        self.parent._manager.set_param2fit(self.uid, param2fit)
+    
+    def select_param(self, event):
+        """
+        Select TextCtrl  checked for fitting purpose and stores them
+        in  self.param_toFit=[] list
+        """
+        self.param_toFit = []
+        for item in self.parameters:
+            #Skip t ifhe angle parameters if 1D data
+            if self.data.__class__.__name__ != "Data2D" and\
+                        not self.enable2D:
+                if item in self.orientation_params:
+                    continue
+            #Select parameters to fit for list of primary parameters
+            if item[0].GetValue() and item[0].IsShown():
+                if not (item in self.param_toFit):
+                    self.param_toFit.append(item)
+            else:
+                #remove parameters from the fitting list
+                if item in self.param_toFit:
+                    self.param_toFit.remove(item)
+
+        #Select parameters to fit for list of fittable parameters
+        #        with dispersion
+        for item in self.fittable_param:
+            #Skip t ifhe angle parameters if 1D data
+            if self.data.__class__.__name__ != "Data2D" and\
+                        not self.enable2D:
+                if item in self.orientation_params:
+                    continue
+            if item[0].GetValue() and item[0].IsShown():
+                if not (item in self.param_toFit):
+                    self.param_toFit.append(item)
+            else:
+                #remove parameters from the fitting list
+                if item in self.param_toFit:
+                    self.param_toFit.remove(item)
+
+        #Calculate num. of angle parameters
+        if self.data.__class__.__name__ == "Data2D" or \
+                       self.enable2D:
+            len_orient_para = 0
+        else:
+            len_orient_para = len(self.orientation_params)  # assume even len
+        #Total num. of angle parameters
+        if len(self.fittable_param) > 0:
+            len_orient_para *= 2
+        #Set the value of checkbox that selected every checkbox or not
+        if len(self.parameters) + len(self.fittable_param) - len_orient_para \
+            == len(self.param_toFit):
+            self.cb1.SetValue(True)
+        else:
+            self.cb1.SetValue(False)
+       
+        self.save_current_state_fit()
+        if event != None:
+            ## post state to fit panel
+            event = PageInfoEvent(page=self)
+            wx.PostEvent(self.parent, event)
+        
+        param2fit = []
+        for item in self.param_toFit:
+            if item[0] and item[0].IsShown():
+                param2fit.append(item[1])
+        self._manager.set_param2fit(self.uid, param2fit)
+        
+    def set_model_param_sizer(self, model):
+        """
+        Build the panel from the model content
+        
+        :param model: the model selected in combo box for fitting purpose
+        
+        """
+        self.sizer3.Clear(True)
+        self.parameters = []
+        self.str_parameters = []
+        self.param_toFit = []
+        self.fittable_param = []
+        self.fixed_param = []
+        self.orientation_params = []
+        self.orientation_params_disp = []
+        
+        if model == None:
+            self.sizer3.Layout()
+            self.SetupScrolling()
+            return
+        ## the panel is drawn using the current value of the fit engine
+        if self.engine_type == None and self._manager != None:
+            self.engine_type = self._manager._return_engine_type()
+
+        box_description = wx.StaticBox(self, -1, str("Model Parameters"))
+        boxsizer1 = wx.StaticBoxSizer(box_description, wx.VERTICAL)
+        sizer = wx.GridBagSizer(5, 5)
+        ## save the current model
+        self.model = model
+           
+        keys = self.model.getParamList()
+        #list of dispersion paramaters
+        self.disp_list = self.model.getDispParamList()
+
+        def custom_compare(a, b):
+            """
+            Custom compare to order, first by alphabets then second by number.
+            """
+            # number at the last digit
+            a_last = a[len(a) - 1]
+            b_last = b[len(b) - 1]
+            # default
+            num_a = None
+            num_b = None
+            # split the names
+            a2 = a.lower().split('_')
+            b2 = b.lower().split('_')
+            # check length of a2, b2
+            len_a2 = len(a2)
+            len_b2 = len(b2)
+            # check if it contains a int number(<10)
+            try:
+                num_a = int(a_last)
+            except:
+                pass
+            try:
+                num_b = int(b_last)
+            except:
+                pass
+            # Put 'scale' near the top; happens
+            # when numbered param name exists
+            if a == 'scale':
+                return -1
+            # both have a number
+            if num_a != None and num_b != None:
+                if num_a > num_b:
+                    return -1
+                # same number
+                elif num_a == num_b:
+                    # different last names
+                    if a2[len_a2 - 1] != b2[len_b2 - 1] and num_a != 0:
+                        return -cmp(a2[len_a2 - 1], b2[len_b2 - 1])
+                    else:
+                        return cmp(a, b)
+                else:
+                    return 1
+            # one of them has a number
+            elif num_a != None:
+                return 1
+            elif num_b != None:
+                return -1
+            # no numbers
+            else:
+                return cmp(a.lower(), b.lower())
+
+        keys.sort(custom_compare)
+    
+        iy = 0
+        ix = 0
+        select_text = "Select All"
+        self.cb1 = wx.CheckBox(self, -1, str(select_text), (10, 10))
+        wx.EVT_CHECKBOX(self, self.cb1.GetId(), self.select_all_param)
+        self.cb1.SetToolTipString("To check/uncheck all the boxes below.")
+        self.cb1.SetValue(True)
+        
+        sizer.Add(self.cb1, (iy, ix), (1, 1), \
+                             wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 5)
+        ix += 1
+        self.text2_2 = wx.StaticText(self, -1, 'Value')
+        sizer.Add(self.text2_2, (iy, ix), (1, 1), \
+                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+        ix += 2
+        self.text2_3 = wx.StaticText(self, -1, 'Error')
+        sizer.Add(self.text2_3, (iy, ix), (1, 1), \
+                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+        if not self.is_mac:
+            self.text2_3.Hide()
+        ix += 1
+        self.text2_min = wx.StaticText(self, -1, 'Min')
+        sizer.Add(self.text2_min, (iy, ix), (1, 1), \
+                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+        #self.text2_min.Hide()
+        ix += 1
+        self.text2_max = wx.StaticText(self, -1, 'Max')
+        sizer.Add(self.text2_max, (iy, ix), (1, 1), \
+                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+        #self.text2_max.Hide()
+        ix += 1
+        self.text2_4 = wx.StaticText(self, -1, '[Units]')
+        sizer.Add(self.text2_4, (iy, ix), (1, 1), \
+                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+        self.text2_4.Hide()
+        
+        CHECK_STATE = self.cb1.GetValue()
+        for item in keys:
+            if not item in self.disp_list and not item in \
+                    self.model.orientation_params:
+                
+                ##prepare a spot to store errors
+                if not item in self.model.details:
+                    self.model.details[item] = ["", None, None]
+         
+                iy += 1
+                ix = 0
+                if (self.model.__class__ in \
+                    self.model_list_box["Multi-Functions"] or \
+                    self.temp_multi_functional)\
+                    and (item in self.model.non_fittable):
+                    non_fittable_name = wx.StaticText(self, -1, item)
+                    sizer.Add(non_fittable_name, (iy, ix), (1, 1), \
+                            wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 21)
+                    ## add parameter value
+                    ix += 1
+                    value = self.model.getParam(item)
+                    if len(self.model.fun_list) > 0:
+                        #num = item.split('_')[1][5:7]
+                        fun_box = wx.ComboBox(self, -1, size=(100, -1),
+                                    style=wx.CB_READONLY, name='%s' % item)
+                        self._set_fun_box_list(fun_box)
+                        fun_box.SetSelection(0)
+                        #self.fun_box.SetToolTipString("A function
+                        #    describing the interface")
+                        wx.EVT_COMBOBOX(fun_box, -1, self._on_fun_box)
+                    else:
+                        fun_box = self.ModelTextCtrl(self, -1,
+                                                     size=(_BOX_WIDTH, 20),
+                                style=wx.TE_PROCESS_ENTER, name='%s' % item)
+                        fun_box.SetToolTipString(\
+                                "Hit 'Enter' after typing to update the plot.")
+                        fun_box.SetValue(format_number(value, True))
+                    sizer.Add(fun_box, (iy, ix), (1, 1), wx.EXPAND)
+                    self.str_parameters.append([None, item, fun_box,
+                                                None, None, None,
+                                                None, None])
+                else:
+                    ## add parameters name with checkbox for selecting to fit
+                    cb = wx.CheckBox(self, -1, item)
+                    cb.SetValue(CHECK_STATE)
+                    cb.SetToolTipString(" Check mark to fit.")
+                    #cb.SetValue(True)
+                    wx.EVT_CHECKBOX(self, cb.GetId(), self.select_param)
+                    
+                    sizer.Add(cb, (iy, ix), (1, 1),
+                              wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 5)
+
+                    ## add parameter value
+                    ix += 1
+                    value = self.model.getParam(item)
+                    ctl1 = self.ModelTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
+                                        style=wx.TE_PROCESS_ENTER)
+                    ctl1.SetToolTipString(\
+                                "Hit 'Enter' after typing to update the plot.")
+                    ctl1.SetValue(format_number(value, True))
+                    sizer.Add(ctl1, (iy, ix), (1, 1), wx.EXPAND)
+                    ## text to show error sign
+                    ix += 1
+                    text2 = wx.StaticText(self, -1, '+/-')
+                    sizer.Add(text2, (iy, ix), (1, 1), \
+                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+                    if not self.is_mac:
+                        text2.Hide()
+                    ix += 1
+                    ctl2 = wx.TextCtrl(self, -1,
+                                       size=(_BOX_WIDTH / 1.2, 20), style=0)
+                    sizer.Add(ctl2, (iy, ix), (1, 1),
+                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+                    if not self.is_mac:
+                        ctl2.Hide()
+                    
+                    ix += 1
+                    ctl3 = self.ModelTextCtrl(self, -1,
+                                              size=(_BOX_WIDTH / 1.9, 20),
+                                              style=wx.TE_PROCESS_ENTER,
+                                text_enter_callback=self._onparamRangeEnter)
+         
+                    sizer.Add(ctl3, (iy, ix), (1, 1),
+                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+            
+                    ix += 1
+                    ctl4 = self.ModelTextCtrl(self, -1,
+                                              size=(_BOX_WIDTH / 1.9, 20),
+                                              style=wx.TE_PROCESS_ENTER,
+                                text_enter_callback=self._onparamRangeEnter)
+                    sizer.Add(ctl4, (iy, ix), (1, 1),
+                              wx.EXPAND | wx.FIXED_MINSIZE, 0)
+    
+                    ix += 1
+                    # Units
+                    if item in self.model.details:
+                        units = wx.StaticText(self, -1,
+                            self.model.details[item][0], style=wx.ALIGN_LEFT)
+                    else:
+                        units = wx.StaticText(self, -1, "",
+                                              style=wx.ALIGN_LEFT)
+                    sizer.Add(units, (iy, ix), (1, 1),
+                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+                        
+                    self.parameters.append([cb, item, ctl1,
+                                            text2, ctl2, ctl3, ctl4, units])
+                                  
+        iy += 1
+        sizer.Add((10, 10), (iy, ix), (1, 1),
+                  wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
+        
+        # type can be either Guassian or Array
+        if len(self.model.dispersion.values()) > 0:
+            type = self.model.dispersion.values()[0]["type"]
+        else:
+            type = "Gaussian"
+            
+        iy += 1
+        ix = 0
+        #Add tile for orientational angle
+        for item in keys:
+            if item in self.model.orientation_params:
+                orient_angle = wx.StaticText(self, -1, '[For 2D only]:')
+                mag_on_button = wx.Button(self, -1, "Magnetic ON" )
+                mag_on_button.Bind(wx.EVT_BUTTON, self._on_mag_on)
+                mag_help_button = wx.Button(self, -1,"Magnetic angles?" )
+                mag_help_button.Bind(wx.EVT_BUTTON,self._on_mag_help)
+                sizer.Add(orient_angle, (iy, ix), (1, 1),
+                          wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
+                iy += 1
+                sizer.Add(mag_on_button,(iy, ix ),(1,1), 
+                          wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15) 
+                sizer.Add(mag_help_button,(iy, ix + 1),(1,1), 
+                          wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15) 
+                
+                #handle the magnetic buttons
+                if not self._has_magnetic:
+                    mag_on_button.Show(False)
+                elif not self.data.__class__.__name__ == "Data2D":
+                    mag_on_button.Show(False)
+                else:
+                    mag_on_button.Show(True)
+                mag_help_button.Show(False)
+                if mag_on_button.IsShown():
+                    if self.magnetic_on:
+                        mag_on_button.SetLabel("Magnetic OFF")
+                        mag_help_button.Show(True) 
+                    else:
+                        mag_on_button.SetLabel("Magnetic ON")
+                        mag_help_button.Show(False)
+                        
+                if not self.data.__class__.__name__ == "Data2D" and \
+                        not self.enable2D:
+                    orient_angle.Hide()
+                else:
+                    orient_angle.Show(True)
+                break
+      
+        #For Gaussian only
+        if type.lower() != "array":
+            for item in self.model.orientation_params:
+                if not self.magnetic_on:
+                    if item in self.model.magnetic_params:
+                        continue
+                if not item in self.disp_list:
+                    ##prepare a spot to store min max
+                    if not item in self.model.details:
+                        self.model.details[item] = ["", None, None]
+                          
+                    iy += 1
+                    ix = 0
+                    ## add parameters name with checkbox for selecting to fit
+                    cb = wx.CheckBox(self, -1, item)
+                    cb.SetValue(CHECK_STATE)
+                    cb.SetToolTipString("Check mark to fit")
+                    wx.EVT_CHECKBOX(self, cb.GetId(), self.select_param)
+                    if self.data.__class__.__name__ == "Data2D" or \
+                            self.enable2D:
+                        cb.Show(True)
+                    else:
+                        cb.Hide()
+                    sizer.Add(cb, (iy, ix), (1, 1),
+                              wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 5)
+    
+                    ## add parameter value
+                    ix += 1
+                    value = self.model.getParam(item)
+                    ctl1 = self.ModelTextCtrl(self, -1, size=(_BOX_WIDTH, 20),
+                                        style=wx.TE_PROCESS_ENTER)
+                    ctl1.SetToolTipString(\
+                                "Hit 'Enter' after typing to update the plot.")
+                    ctl1.SetValue(format_number(value, True))
+                    if self.data.__class__.__name__ == "Data2D" or \
+                            self.enable2D:
+                        ctl1.Show(True)
+                    else:
+                        ctl1.Hide()
+                    sizer.Add(ctl1, (iy, ix), (1, 1), wx.EXPAND)
+                    ## text to show error sign
+                    ix += 1
+                    text2 = wx.StaticText(self, -1, '+/-')
+                    sizer.Add(text2, (iy, ix), (1, 1), \
+                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+
+                    text2.Hide()
+                    ix += 1
+                    ctl2 = wx.TextCtrl(self, -1,
+                                       size=(_BOX_WIDTH / 1.2, 20), style=0)
+                    sizer.Add(ctl2, (iy, ix), (1, 1),
+                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+
+                    ctl2.Hide()
+                    
+                    ix += 1
+                    ctl3 = self.ModelTextCtrl(self, -1,
+                                              size=(_BOX_WIDTH / 1.8, 20),
+                                              style=wx.TE_PROCESS_ENTER,
+                                text_enter_callback=self._onparamRangeEnter)
+                    
+                    sizer.Add(ctl3, (iy, ix), (1, 1),
+                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+                    ctl3.Hide()
+                 
+                    ix += 1
+                    ctl4 = self.ModelTextCtrl(self, -1,
+                                              size=(_BOX_WIDTH / 1.8, 20),
+                                              style=wx.TE_PROCESS_ENTER,
+                            text_enter_callback=self._onparamRangeEnter)
+                    sizer.Add(ctl4, (iy, ix), (1, 1),
+                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+                   
+                    ctl4.Hide()
+                    
+                    if self.data.__class__.__name__ == "Data2D" or \
+                            self.enable2D:
+                        if self.is_mac:
+                            text2.Show(True)
+                            ctl2.Show(True)
+                        ctl3.Show(True)
+                        ctl4.Show(True)
+                    
+                    ix += 1
+                    # Units
+                    if item in self.model.details:
+                        units = wx.StaticText(self, -1,
+                                              self.model.details[item][0],
+                                              style=wx.ALIGN_LEFT)
+                    else:
+                        units = wx.StaticText(self, -1, "",
+                                              style=wx.ALIGN_LEFT)
+                    if self.data.__class__.__name__ == "Data2D" or \
+                            self.enable2D:
+                        units.Show(True)
+                    else:
+                        units.Hide()
+                    
+                    sizer.Add(units, (iy, ix), (1, 1),
+                              wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+                                          
+                    self.parameters.append([cb, item, ctl1,
+                                            text2, ctl2, ctl3, ctl4, units])
+                    self.orientation_params.append([cb, item, ctl1,
+                                            text2, ctl2, ctl3, ctl4, units])
+              
+        iy += 1
+        box_description.SetForegroundColour(wx.BLUE)
+        #Display units text on panel
+        for item in keys:
+            if item in self.model.details:
+                self.text2_4.Show()
+        #Fill the list of fittable parameters
+        self.get_all_checked_params()
+        self.save_current_state_fit()
+        boxsizer1.Add(sizer)
+        self.sizer3.Add(boxsizer1, 0, wx.EXPAND | wx.ALL, 10)
+        self.sizer3.Layout()
+        self.Layout()
+
+    def on_right_down(self, event):
+        """
+        Get key stroke event
+        """
+        if self.data == None:
+            return
+        # Figuring out key combo: Cmd for copy, Alt for paste
+        if event.AltDown() and event.ShiftDown():
+            self._manager.show_ftol_dialog()
+            flag = True
+        elif event.AltDown() or event.ShiftDown():
+            flag = False
+        else:
+            return
+        # make event free
+        event.Skip()
+        # messages depending on the flag
+        if not flag:
+            msg = " Could not open ftol dialog;"
+            msg += " Check if the Scipy fit engine is selected in the menubar."
+            infor = 'warning'
+            # inform msg to wx
+            wx.PostEvent(self._manager.parent,
+                        StatusEvent(status=msg, info=infor))
+        
+    def _onModel2D(self, event):
+        """
+        toggle view of model from 1D to 2D  or 2D from 1D
+        """
+        if self.model_view.GetLabelText() == "Show 2D":
+            self.model_view.SetLabel("Show 1D")
+            self.enable2D = True
+              
+        else:
+            self.model_view.SetLabel("Show 2D")
+            self.enable2D = False
+        self.Show(False)
+        self.create_default_data()
+        self._manager.store_data(self.uid, data_list=[self.data])
+
+        self.set_model_param_sizer(self.model)
+        self._set_sizer_dispersion()
+        self._set_weight(is_2D=self.enable2D)
+        self._set_smear_buttons()
+        self.Show(True)
+        self.SetupScrolling()
+        self._draw_model()
+        
+        self.state.enable2D = copy.deepcopy(self.enable2D)
+    
+    def _set_smear_buttons(self):
+        """
+        Set semarer radio buttons
+        """
+        # more disables for 2D
+        if self.data.__class__.__name__ == "Data2D" or \
+                    self.enable2D:
+            self.slit_smearer.Disable()
+            self.pinhole_smearer.Enable(True)
+            self.default_mask = copy.deepcopy(self.data.mask)
+        else:
+            self.slit_smearer.Enable(True)
+            self.pinhole_smearer.Enable(True)
+            
+            
+class BGTextCtrl(wx.TextCtrl):
+    """
+    Text control used to display outputs.
+    No editing allowed. The background is
+    grayed out. User can't select text.
+    """
+    def __init__(self, *args, **kwds):
+        wx.TextCtrl.__init__(self, *args, **kwds)
+        self.SetEditable(False)
+        self.SetBackgroundColour(self.GetParent().parent.GetBackgroundColour())
+        
+        # Bind to mouse event to avoid text highlighting
+        # The event will be skipped once the call-back
+        # is called.
+        self.Bind(wx.EVT_MOUSE_EVENTS, self._click)
+        
+    def _click(self, event):
+        """
+        Prevent further handling of the mouse event
+        by not calling Skip().
+        """
+        pass
Index: src/sas/perspectives/fitting/fitpanel.py
===================================================================
--- src/sas/perspectives/fitting/fitpanel.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/fitting/fitpanel.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,644 @@
+"""
+FitPanel class contains fields allowing to fit  models and  data
+
+:note: For Fit to be performed the user should check at least one parameter
+    on fit Panel window.
+   
+"""
+import wx
+from wx.aui import AuiNotebook as nb
+
+from sas.guiframe.panel_base import PanelBase
+from sas.guiframe.events import PanelOnFocusEvent
+from sas.guiframe.events import StatusEvent
+from sas.guiframe.dataFitting import check_data_validity
+
+import basepage
+import models
+_BOX_WIDTH = 80
+
+
+class FitPanel(nb, PanelBase):
+    """
+    FitPanel class contains fields allowing to fit  models and  data
+    
+    :note: For Fit to be performed the user should check at least one parameter
+        on fit Panel window.
+       
+    """
+    ## Internal name for the AUI manager
+    window_name = "Fit panel"
+    ## Title to appear on top of the window
+    window_caption = "Fit Panel "
+    CENTER_PANE = True
+    
+    def __init__(self, parent, manager=None, *args, **kwargs):
+        """
+        """
+        nb.__init__(self, parent, -1,
+                    style=wx.aui.AUI_NB_WINDOWLIST_BUTTON|
+                    wx.aui.AUI_NB_DEFAULT_STYLE|
+                    wx.CLIP_CHILDREN)
+        PanelBase.__init__(self, parent)
+        #self.SetWindowStyleFlag(style=nb.FNB_FANCY_TABS)
+        self._manager = manager
+        self.parent = parent
+        self.event_owner = None
+        #dictionary of miodel {model class name, model class}
+        self.menu_mng = models.ModelManager()
+        self.model_list_box = self.menu_mng.get_model_list()
+        #pageClosedEvent = nb.EVT_FLATNOTEBOOK_PAGE_CLOSING
+        self.model_dictionary = self.menu_mng.get_model_dictionary()
+        self.pageClosedEvent = wx.aui.EVT_AUINOTEBOOK_PAGE_CLOSE
+        
+        self.Bind(self.pageClosedEvent, self.on_close_page)
+        ## save the title of the last page tab added
+        self.fit_page_name = {}
+        ## list of existing fit page
+        self.opened_pages = {}
+        #index of fit page
+        self.fit_page_index = 0
+        #index of batch page
+        self.batch_page_index = 0
+        #page of simultaneous fit
+        self.sim_page = None
+        self.batch_page = None
+        self.fit_engine_type = "bumps"
+        ## get the state of a page
+        self.Bind(basepage.EVT_PAGE_INFO, self._onGetstate)
+        self.Bind(basepage.EVT_PREVIOUS_STATE, self._onUndo)
+        self.Bind(basepage.EVT_NEXT_STATE, self._onRedo)
+        self.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, self.on_page_changing)
+        self.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CLOSED, self.on_closed)
+        
+    def on_closed(self, event):
+        """
+        """
+        if self.GetPageCount() == 0:
+            self.add_empty_page()
+            self.enable_close_button()
+        
+    def save_project(self, doc=None):
+        """
+        return an xml node containing state of the panel
+         that guiframe can write to file
+        """
+        msg = ""
+        for uid, page in self.opened_pages.iteritems():
+            if page.batch_on:
+                pos = self.GetPageIndex(page)
+                if pos != -1 and page not in [self.sim_page, self.batch_page]:
+                    msg += "%s .\n" % str(self.GetPageText(pos))
+            else:
+                data = page.get_data()
+                # state must be cloned
+                state = page.get_state().clone()
+                if data is not None and page.model is not None:
+                    new_doc = self._manager.state_reader.write_toXML(data,
+                                                                     state)
+                    if doc != None and hasattr(doc, "firstChild"):
+                        child = new_doc.firstChild.firstChild
+                        doc.firstChild.appendChild(child)
+                    else:
+                        doc = new_doc
+        if msg.strip() != "":
+            temp = "Save Project is not supported for Batch page.\n"
+            temp += "The following pages will not be save:\n"
+            message = temp + msg
+            wx.PostEvent(self._manager.parent, StatusEvent(status=message,
+                                                            info="warning"))
+        return doc
+    
+    def _on_engine_change(self, name='scipy'):
+        """
+        """
+        for panel in self.opened_pages.values():
+            self.set_engine_helper(panel=panel, name=name)
+            
+    def set_engine_helper(self, panel, name='scipy'):
+        """
+        """
+        self.fit_engine_type = name
+        if panel not in[self.batch_page, self.sim_page]:
+            panel._on_engine_change(name=self.fit_engine_type)
+                
+    def update_model_list(self):
+        """
+        """
+        temp = self.menu_mng.update()
+        if len(temp):
+            self.model_list_box = temp
+        return temp
+    
+    def reset_pmodel_list(self):
+        """
+        """
+        temp = self.menu_mng.pulgins_reset()
+        if len(temp):
+            self.model_list_box = temp
+        return temp
+    
+    def get_page_by_id(self, uid):
+        """
+        """
+        if uid not in self.opened_pages:
+            msg = "Fitpanel cannot find ID: %s in self.opened_pages" % str(uid)
+            raise ValueError, msg
+        else:
+            return self.opened_pages[uid]
+        
+    def on_page_changing(self, event):
+        """
+        calls the function when the current event handler has exited. avoiding
+        to call panel on focus on a panel that is currently deleted
+        """
+        wx.CallAfter(self.helper_on_page_change)
+        
+    def helper_on_page_change(self):
+        """
+        """
+        pos = self.GetSelection()
+        if pos != -1:
+            selected_page = self.GetPage(pos)
+            wx.PostEvent(self._manager.parent, 
+                         PanelOnFocusEvent(panel=selected_page))
+        self.enable_close_button()
+       
+    def on_set_focus(self, event):
+        """
+        """
+        pos = self.GetSelection()
+        if pos != -1:
+            selected_page = self.GetPage(pos)
+            wx.PostEvent(self._manager.parent, 
+                         PanelOnFocusEvent(panel=selected_page))
+        
+    def get_data(self):
+        """
+        get the data in the current page
+        """
+        pos = self.GetSelection()
+        if pos != -1:
+            selected_page = self.GetPage(pos)
+            return selected_page.get_data()
+    
+    def set_model_state(self, state):
+        """
+        receive a state to reset the model in the current page
+        """
+        pos = self.GetSelection()
+        if pos != -1:
+            selected_page = self.GetPage(pos)
+            selected_page.set_model_state(state)
+            
+    def get_state(self):
+        """
+         return the state of the current selected page
+        """
+        pos = self.GetSelection()
+        if pos != -1:
+            selected_page = self.GetPage(pos)
+            return selected_page.get_state()
+    
+    def close_all(self):
+        """
+        remove all pages, used when a svs file is opened
+        """
+        
+        #get number of pages
+        nop = self.GetPageCount()
+        #use while-loop, for-loop will not do the job well.
+        while (nop > 0):
+            #delete the first page until no page exists
+            page = self.GetPage(0)
+            if self._manager.parent.panel_on_focus == page:
+                self._manager.parent.panel_on_focus = None
+            self._close_helper(selected_page=page)
+            self.DeletePage(0)
+            nop = nop - 1
+            
+        ## save the title of the last page tab added
+        self.fit_page_name = {}
+        ## list of existing fit page
+        self.opened_pages = {}
+         
+    def set_state(self, state):
+        """
+        Restore state of the panel
+        """
+        page_is_opened = False
+        if state is not None:
+            for uid, panel in self.opened_pages.iteritems():
+                #Don't return any panel is the exact same page is created
+                if uid == panel.uid and panel.data == state.data:
+                    # the page is still opened
+                    panel.reset_page(state=state)
+                    panel.save_current_state()
+                    page_is_opened = True
+            if not page_is_opened:
+                if state.data.__class__.__name__ != 'list':
+                    #To support older state file format
+                    list_data = [state.data]
+                else:
+                    #Todo: need new file format for the list
+                    list_data = state.data
+                panel = self._manager.add_fit_page(data=list_data)
+                # add data associated to the page created
+                if panel is not None:
+                    self._manager.store_data(uid=panel.uid,
+                                             data_list=list_data,
+                                             caption=panel.window_caption)
+                    panel.reset_page(state=state)
+                    panel.save_current_state()
+                    
+    def clear_panel(self):
+        """
+        Clear and close all panels, used by guimanager
+        """
+       
+        #close all panels only when svs file opened
+        self.close_all()
+        self._manager.mypanels = []
+        
+    def on_close_page(self, event=None):
+        """
+        close page and remove all references to the closed page
+        """
+        nbr_page = self.GetPageCount()
+        selected_page = self.GetPage(self.GetSelection())
+        if nbr_page == 1:
+            if selected_page.get_data() == None:
+                if event is not None:
+                    event.Veto()
+                return
+        self._close_helper(selected_page=selected_page)
+        
+    def close_page_with_data(self, deleted_data):
+        """
+        close a fit page when its data is completely remove from the graph
+        """
+        if deleted_data is None:
+            return
+        for index in range(self.GetPageCount()):
+            selected_page = self.GetPage(index)
+            if hasattr(selected_page, "get_data"):
+                data = selected_page.get_data()
+                
+                if data is None:
+                    #the fitpanel exists and only the initial fit page is open
+                    #with no selected data
+                    return
+                if data.id == deleted_data.id:
+                    self._close_helper(selected_page)
+                    self.DeletePage(index)
+                    break
+        
+    def set_manager(self, manager):
+        """
+        set panel manager
+        
+        :param manager: instance of plugin fitting
+        
+        """
+        self._manager = manager
+        for pos in range(self.GetPageCount()):
+            page = self.GetPage(pos)
+            if page is not None:
+                page.set_manager(self._manager)
+
+    def set_model_list(self, dict):
+        """
+        copy a dictionary of model into its own dictionary
+        
+        :param m_dict: dictionnary made of model name as key and model class
+        as value
+        """
+        self.model_list_box = dict
+        
+    def set_model_dict(self, m_dict):
+        """
+        copy a dictionary of model name -> model object
+
+        :param m_dict: dictionary linking model name -> model object
+        """
+
+    def get_current_page(self):
+        """
+        :return: the current page selected
+        
+        """
+        return self.GetPage(self.GetSelection())
+    
+    def add_sim_page(self, caption="Const & Simul Fit"):
+        """
+        Add the simultaneous fit page
+        """
+        from simfitpage import SimultaneousFitPage
+        page_finder = self._manager.get_page_finder()
+        if caption == "Const & Simul Fit":
+            self.sim_page = SimultaneousFitPage(self, page_finder=page_finder,
+                                                 id=-1, batch_on=False)
+            self.sim_page.window_caption = caption
+            self.sim_page.window_name = caption
+            self.sim_page.uid = wx.NewId()
+            self.AddPage(self.sim_page, caption, True)
+            self.sim_page.set_manager(self._manager)
+            self.enable_close_button()
+            return self.sim_page
+        else:
+            self.batch_page = SimultaneousFitPage(self, batch_on=True,
+                                                   page_finder=page_finder)
+            self.batch_page.window_caption = caption
+            self.batch_page.window_name = caption
+            self.batch_page.uid = wx.NewId()
+            self.AddPage(self.batch_page, caption, True)
+            self.batch_page.set_manager(self._manager)
+            self.enable_close_button()
+            return self.batch_page
+ 
+    def add_empty_page(self):
+        """
+        add an empty page
+        """
+        """
+        if self.batch_on:
+            from batchfitpage import BatchFitPage
+            panel = BatchFitPage(parent=self)
+            #Increment index of batch page
+            self.batch_page_index += 1
+            index = self.batch_page_index
+        else:
+        """
+        from fitpage import FitPage
+        panel = FitPage(parent=self)
+        if self.batch_on:
+            self.batch_page_index += 1
+            caption = "BatchPage" + str(self.batch_page_index)
+            panel.set_index_model(self.batch_page_index)
+        else:
+            #Increment index of fit page
+            self.fit_page_index += 1
+            caption = "FitPage" + str(self.fit_page_index)
+            panel.set_index_model(self.fit_page_index)
+        panel.batch_on = self.batch_on
+        panel._set_save_flag(not panel.batch_on)
+        panel.set_model_dictionary(self.model_dictionary)
+        panel.populate_box(model_dict=self.model_list_box)
+        panel.formfactor_combo_init()
+        panel.set_manager(self._manager)
+        panel.window_caption = caption
+        panel.window_name = caption
+        self.AddPage(panel, caption, select=True)
+        self.opened_pages[panel.uid] = panel
+        self.set_engine_helper(panel=panel)
+        self._manager.create_fit_problem(panel.uid)
+        self._manager.page_finder[panel.uid].add_data(panel.get_data())
+        self.enable_close_button()
+        panel.on_set_focus(None)
+        return panel
+    
+    def enable_close_button(self):
+        """
+        display the close button on tab for more than 1 tabs else remove the
+        close button
+        """
+        if self.GetPageCount() <= 1:
+            style = self.GetWindowStyleFlag()
+            flag = wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB
+            if style & wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB == flag:
+                style = style & ~wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB
+                self.SetWindowStyle(style)
+        else:
+            style = self.GetWindowStyleFlag()
+            flag = wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB
+            if style & wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB != flag:
+                style |= wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB
+                self.SetWindowStyle(style)
+            
+    def delete_data(self, data):
+        """
+        Delete the given data
+        """
+        if data.__class__.__name__ != "list":
+            raise ValueError, "Fitpanel delete_data expect list of id"
+        else:
+            n = self.GetPageCount()
+            for page in self.opened_pages.values():
+                pos = self.GetPageIndex(page)
+                temp_data = page.get_data()
+                if temp_data is not None and temp_data.id in data:
+                    self.SetSelection(pos)
+                    self.on_close_page(event=None)
+                    temp = self.GetSelection()
+                    self.DeletePage(temp)
+            if self.GetPageCount() == 0:
+                self._manager.on_add_new_page(event=None)
+        
+    def set_data_on_batch_mode(self, data_list):
+        """
+        Add all data to a single tab when the application is on Batch mode.
+        However all data in the set of data must be either 1D or 2D type. 
+        This method presents option to select the data type before creating a 
+        tab.
+        """
+        data_1d_list = []
+        data_2d_list = []
+        group_id_1d = wx.NewId()
+        # separate data into data1d and data2d list
+        for data in data_list:
+            if data.__class__.__name__ == "Data1D":
+                data.group_id = group_id_1d
+                data_1d_list.append(data)
+            if data.__class__.__name__ == "Data2D":
+                data.group_id = wx.NewId()
+                data_2d_list.append(data)
+        page = None
+        for p in self.opened_pages.values():
+            #check if there is an empty page to fill up
+            if not check_data_validity(p.get_data()) and p.batch_on:
+                
+                #make sure data get placed in 1D empty tab if data is 1D
+                #else data get place on 2D tab empty tab
+                enable2D = p.get_view_mode()
+                if (data.__class__.__name__ == "Data2D" and enable2D)\
+                or (data.__class__.__name__ == "Data1D" and not enable2D):
+                    page = p
+                    break
+        if data_1d_list and data_2d_list:
+            # need to warning the user that this batch is a special case
+            from sas import BatchDataDialog
+            dlg = BatchDataDialog(self)
+            if dlg.ShowModal() == wx.ID_OK:
+                data_type = dlg.get_data()
+                dlg.Destroy()
+                if page  is None:
+                    page = self.add_empty_page()
+                if data_type == 1:
+                    #user has selected only data1D
+                    page.fill_data_combobox(data_1d_list)
+                elif data_type == 2:
+                    page.fill_data_combobox(data_2d_list)
+            else:
+                #the batch analysis is canceled
+                dlg.Destroy()
+                return None
+        else:
+            if page is None:
+                page = self.add_empty_page()
+            if data_1d_list and not data_2d_list:
+                #only on type of data
+                page.fill_data_combobox(data_1d_list)
+            elif not data_1d_list and data_2d_list:
+                page.fill_data_combobox(data_2d_list)
+                
+        pos = self.GetPageIndex(page)
+        page.batch_on = self.batch_on
+        page._set_save_flag(not page.batch_on)
+        self.SetSelection(pos)
+        self.opened_pages[page.uid] = page
+        return page
+    
+    def set_data(self, data_list):
+        """
+        Add a fitting page on the notebook contained by fitpanel
+        
+        :param data: data to fit
+        
+        :return panel : page just added for further used.
+        is used by fitting module
+        
+        """
+        if not data_list:
+            return None
+        if self.batch_on:
+            return self.set_data_on_batch_mode(data_list)
+        else:
+            data = None
+            try:
+                data = data_list[0]
+            except:
+                # for 'fitv' files
+                data_list = [data]
+                data = data_list[0]
+                
+            if data is None:
+                return None
+        for page in self.opened_pages.values():
+            #check if the selected data existing in the fitpanel
+            pos = self.GetPageIndex(page)
+            if not check_data_validity(page.get_data()) and not page.batch_on:
+                #make sure data get placed in 1D empty tab if data is 1D
+                #else data get place on 2D tab empty tab
+                enable2D = page.get_view_mode()
+                if (data.__class__.__name__ == "Data2D" and enable2D)\
+                or (data.__class__.__name__ == "Data1D" and not enable2D):
+                    page.batch_on = self.batch_on
+                    page._set_save_flag(not page.batch_on)
+                    page.fill_data_combobox(data_list)
+                    #caption = "FitPage" + str(self.fit_page_index)
+                    self.SetPageText(pos, page.window_caption)
+                    self.SetSelection(pos)
+                    return page
+        #create new page and add data
+        page = self.add_empty_page()
+        pos = self.GetPageIndex(page)
+        page.fill_data_combobox(data_list)
+        self.opened_pages[page.uid] = page
+        self.SetSelection(pos)
+        return page
+       
+    def _onGetstate(self, event):
+        """
+        copy the state of a page
+        """
+        page = event.page
+        if page.uid in self.fit_page_name:
+            self.fit_page_name[page.uid].appendItem(page.createMemento())
+            
+    def _onUndo(self, event):
+        """
+        return the previous state of a given page is available
+        """
+        page = event.page
+        if page.uid in self.fit_page_name:
+            if self.fit_page_name[page.uid].getCurrentPosition() == 0:
+                state = None
+            else:
+                state = self.fit_page_name[page.uid].getPreviousItem()
+                page._redo.Enable(True)
+            page.reset_page(state)
+        
+    def _onRedo(self, event):
+        """
+        return the next state available
+        """
+        page = event.page
+        if page.uid in self.fit_page_name:
+            length = len(self.fit_page_name[page.uid])
+            if self.fit_page_name[page.uid].getCurrentPosition() == length - 1:
+                state = None
+                page._redo.Enable(False)
+                page._redo.Enable(True)
+            else:
+                state = self.fit_page_name[page.uid].getNextItem()
+            page.reset_page(state)
+                 
+    def _close_helper(self, selected_page):
+        """
+        Delete the given page from the notebook
+        """
+        #remove hint page
+        #if selected_page == self.hint_page:
+        #    return
+        ## removing sim_page
+        if selected_page == self.sim_page:
+            self._manager.sim_page = None
+            return
+        if selected_page == self.batch_page:
+            self._manager.batch_page = None
+            return
+        """
+        # The below is not working when delete #5 and still have #6.
+        if selected_page.__class__.__name__ == "FitPage":
+            self.fit_page_index -= 1
+        else:
+            self.batch_page_index -= 1
+        """
+        ## closing other pages
+        state = selected_page.createMemento()
+        page_finder = self._manager.get_page_finder()
+        ## removing fit page
+        data = selected_page.get_data()
+        #Don' t remove plot for 2D
+        flag = True
+        if data.__class__.__name__ == 'Data2D':
+            flag = False
+        if selected_page in page_finder:
+            #Delete the name of the page into the list of open page
+            for uid, list in self.opened_pages.iteritems():
+                #Don't return any panel is the exact same page is created
+                
+                if flag and selected_page.uid == uid:
+                    self._manager.remove_plot(uid, theory=False)
+                    break
+            del page_finder[selected_page]
+  
+        #Delete the name of the page into the list of open page
+        for uid, list in self.opened_pages.iteritems():
+            #Don't return any panel is the exact same page is created
+            if selected_page.uid == uid:
+                del self.opened_pages[selected_page.uid]
+                break
+        ##remove the check box link to the model name of this page (selected_page)
+        try:
+            self.sim_page.draw_page()
+        except:
+            ## that page is already deleted no need to remove check box on
+            ##non existing page
+            pass
+        try:
+            self.batch_page.draw_page()
+        except:
+            ## that page is already deleted no need to remove check box on
+            ##non existing page
+            pass
Index: src/sas/perspectives/fitting/fitproblem.py
===================================================================
--- src/sas/perspectives/fitting/fitproblem.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/fitting/fitproblem.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,753 @@
+"""
+Inferface containing information to store data, model, range of data, etc...
+and retreive this information. This is an inferface
+for a fitProblem i.e relationship between data and model.
+"""
+################################################################################
+#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 2009, University of Tennessee
+################################################################################
+import copy
+from sas.models.qsmearing import smear_selection
+
+class FitProblemComponent(object):
+    """
+    Inferface containing information to store data, model, range of data, etc...
+    and retreive this information. This is an inferface
+    for a fitProblem i.e relationship between data and model.
+    """
+    def enable_smearing(self, flag=False):
+        """
+        :param flag: bool.When flag is 1 get the computer smear value. When
+        flag is 0 ingore smear value.
+        """
+        
+    def get_smearer(self):
+        """
+        return smear object
+        """
+    def save_model_name(self, name):
+        """
+        """
+        
+    def get_name(self):
+        """
+        """
+        
+    def set_model(self, model):
+        """
+        associates each model with its new created name
+        :param model: model selected
+        :param name: name created for model
+        """
+        
+    def get_model(self):
+        """
+        :return: saved model
+        """
+        
+    def set_residuals(self, residuals):
+        """
+        save a copy of residual
+        :param data: data selected
+        """
+        
+    def get_residuals(self):
+        """
+        :return: residuals
+        """
+        
+    def set_theory_data(self, data):
+        """
+        save a copy of the data select to fit
+        :param data: data selected
+        """
+        
+    def get_theory_data(self):
+        """
+        :return: list of data dList
+        """
+        
+    def set_fit_data(self, data):
+        """
+         Store of list of data and create  by create new fitproblem of each data
+         id , if there was existing information about model, this information
+         get copy to the new fitproblem
+        :param data: list of data selected
+        """
+        
+    def get_fit_data(self):
+        """
+        """
+        
+    def set_model_param(self, name, value=None):
+        """
+        Store the name and value of a parameter of this fitproblem's model
+        :param name: name of the given parameter
+        :param value: value of that parameter
+        """
+        
+    def set_param2fit(self, list):
+        """
+        Store param names to fit (checked)
+        :param list: list of the param names
+        """
+        
+    def get_param2fit(self):
+        """
+        return the list param names to fit
+        """
+        
+    def get_model_param(self):
+        """
+        return list of couple of parameter name and value
+        """
+        
+    def schedule_tofit(self, schedule=0):
+        """
+        set schedule to true to decide if this fit  must be performed
+        """
+        
+    def get_scheduled(self):
+        """
+        return true or false if a problem as being schedule for fitting
+        """
+        
+    def set_range(self, qmin=None, qmax=None):
+        """
+        set fitting range
+        """
+        
+    def get_range(self):
+        """
+        :return: fitting range
+        """
+        
+    def set_weight(self, flag=None):
+        """
+        set fitting range
+        """
+        
+    def get_weight(self):
+        """
+        get fitting weight
+        """
+        
+    def clear_model_param(self):
+        """
+        clear constraint info
+        """
+        
+    def set_fit_tab_caption(self, caption):
+        """
+        store the caption of the page associated with object
+        """
+        
+    def get_fit_tab_caption(self):
+        """
+        Return the caption of the page associated with object
+        """
+        
+    def set_graph_id(self, id):
+        """
+        Set graph id (from data_group_id at the time the graph produced) 
+        """
+        
+    def get_graph_id(self):
+        """
+        Get graph_id
+        """
+
+    def set_result(self, result):
+        """
+        """
+
+    def get_result(self):
+        """
+        get result
+        """
+
+   
+class FitProblemDictionary(FitProblemComponent, dict):
+    """
+    This module implements a dictionary of fitproblem objects
+    """
+    def __init__(self):
+        FitProblemComponent.__init__(self)
+        dict.__init__(self)
+        ## the current model
+        self.model = None
+        ## if 1 this fit problem will be selected to fit , if 0
+        ## it will not be selected for fit
+        self.schedule = 0
+        ##list containing parameter name and value
+        self.list_param = []
+        ## fitting range
+        self.qmin = None
+        self.qmax = None
+        self.graph_id = None
+        self._smear_on = False
+        self.scheduled = 0
+        self.fit_tab_caption = ''
+        self.nbr_residuals_computed = 0
+        self.batch_inputs = {}
+        self.batch_outputs = {}
+ 
+    def enable_smearing(self, flag=False, fid=None):
+        """
+        :param flag: bool.When flag is 1 get the computer smear value. When
+        flag is 0 ingore smear value.
+        """
+        self._smear_on = flag
+        if fid is None:
+            for value in self.itervalues():
+                value.enable_smearing(flag)
+        else:
+            if fid in self.iterkeys():
+                self[fid].enable_smearing(flag)
+        
+    def set_smearer(self, smearer, fid=None):
+        """
+        save reference of  smear object on fitdata
+        :param smear: smear object from DataLoader
+        """
+        if fid is None:
+            for value in self.itervalues():
+                value.set_smearer(smearer)
+        else:
+            if fid in self.iterkeys():
+                self[fid].set_smearer(smearer)
+                
+    def get_smearer(self, fid=None):
+        """
+        return smear object
+        """
+        if fid in self.iterkeys():
+            return self[fid].get_smearer()
+     
+    def save_model_name(self, name, fid=None):
+        """
+        """
+        if fid is None:
+            for value in self.itervalues():
+                value.save_model_name(name)
+        else:
+            if fid in self.iterkeys():
+                self[fid].save_model_name(name)
+                
+    def get_name(self, fid=None):
+        """
+        """
+        result = []
+        if fid is None:
+            for value in self.itervalues():
+                result.append(value.get_name())
+        else:
+            if fid in self.iterkeys():
+                result.append(self[fid].get_name())
+        return result
+    
+    def set_model(self, model, fid=None):
+        """
+        associates each model with its new created name
+        :param model: model selected
+        :param name: name created for model
+        """
+        self.model = model
+        if fid is None:
+            for value in self.itervalues():
+                value.set_model(self.model)
+        else:
+            if fid in self.iterkeys():
+                self[fid].set_model(self.model)
+      
+    def get_model(self, fid):
+        """
+        :return: saved model
+        """
+        if fid in self.iterkeys():
+            return self[fid].get_model()
+       
+    def set_fit_tab_caption(self, caption):
+        """
+        store the caption of the page associated with object
+        """
+        self.fit_tab_caption = caption
+    
+    def get_fit_tab_caption(self):
+        """
+        Return the caption of the page associated with object
+        """
+        return self.fit_tab_caption
+    
+    def set_residuals(self, residuals, fid):
+        """
+        save a copy of residual
+        :param data: data selected
+        """
+        if fid in self.iterkeys():
+            self[fid].set_residuals(residuals)
+            
+    def get_residuals(self, fid):
+        """
+        :return: residuals
+        """
+        if fid in self.iterkeys():
+            return self[fid].get_residuals()
+        
+    def set_theory_data(self, fid, data=None):
+        """
+        save a copy of the data select to fit
+        :param data: data selected
+        """
+        if fid in self.iterkeys():
+            self[fid].set_theory_data(data)
+            
+    def get_theory_data(self, fid):
+        """
+        :return: list of data dList
+        """
+        if fid in self.iterkeys():
+            return self[fid].get_theory_data()
+            
+    def add_data(self, data):
+        """
+        Add data to the current dictionary of fitproblem. if data id does not
+        exist create a new fit problem.
+        :note: only data changes in the fit problem
+        """
+        if data.id not in self.iterkeys():
+            self[data.id] = FitProblem()
+        self[data.id].set_fit_data(data)
+        
+    def set_fit_data(self, data):
+        """
+        save a copy of the data select to fit
+        :param data: data selected
+        
+        """
+        self.clear()
+        if data is None:
+            data = []
+        for d in data:
+            if (d is not None):
+                if (d.id not in self.iterkeys()):
+                    self[d.id] = FitProblem()
+                self[d.id].set_fit_data(d)
+                self[d.id].set_model(self.model)
+                self[d.id].set_range(self.qmin, self.qmax)
+                
+    def get_fit_data(self, fid):
+        """
+        return data for the given fitproblem id
+        :param fid: is key representing a fitproblem. usually extract from data
+                    id
+        """
+        if fid in self.iterkeys():
+            return self[fid].get_fit_data()
+   
+    def set_model_param(self, name, value=None, fid=None):
+        """
+        Store the name and value of a parameter of this fitproblem's model
+        :param name: name of the given parameter
+        :param value: value of that parameter
+        """
+        if fid is None:
+            for value in self.itervalues():
+                value.set_model_param(name, value)
+        else:
+            if fid in self.iterkeys():
+                self[fid].set_model_param(name, value)
+                
+    def get_model_param(self, fid):
+        """
+        return list of couple of parameter name and value
+        """
+        if fid in self.iterkeys():
+            return self[fid].get_model_param()
+    
+    def set_param2fit(self, list):
+        """
+        Store param names to fit (checked)
+        :param list: list of the param names
+        """
+        self.list_param2fit = list
+        
+    def get_param2fit(self):
+        """
+        return the list param names to fit
+        """
+        return self.list_param2fit
+          
+    def schedule_tofit(self, schedule=0):
+        """
+        set schedule to true to decide if this fit  must be performed
+        """
+        self.scheduled = schedule
+        for value in self.itervalues():
+            value.schedule_tofit(schedule)
+      
+    def get_scheduled(self):
+        """
+        return true or false if a problem as being schedule for fitting
+        """
+        return self.scheduled
+    
+    def set_range(self, qmin=None, qmax=None, fid=None):
+        """
+        set fitting range
+        """
+        self.qmin = qmin
+        self.qmax = qmax
+        if fid is None:
+            for value in self.itervalues():
+                value.set_range(self.qmin, self.qmax)
+        else:
+            if fid in self.iterkeys():
+                self[fid].value.set_range(self.qmin, self.qmax)
+        
+    def get_range(self, fid):
+        """
+        :return: fitting range
+        """
+        if fid in self.iterkeys():
+            return self[fid].get_range()
+        
+    def set_weight(self, is2d, flag=None, fid=None):
+        """
+        fit weight
+        """
+        if fid is None:
+            for value in self.itervalues():
+                value.set_weight(flag=flag, is2d=is2d)
+        else:
+            if fid in self.iterkeys():
+                self[fid].set_weight(flag=flag, is2d=is2d)
+                
+    def get_weight(self, fid=None):
+        """
+        return fit weight
+        """
+        if fid in self.iterkeys():
+            return self[fid].get_weight()
+                 
+    def clear_model_param(self, fid=None):
+        """
+        clear constraint info
+        """
+        if fid is None:
+            for value in self.itervalues():
+                value.clear_model_param()
+        else:
+            if fid in self.iterkeys():
+                self[fid].clear_model_param()
+                
+    def get_fit_problem(self):
+        """
+        return fitproblem contained in this dictionary
+        """
+        return self.itervalues()
+    
+    def set_result(self, result, fid):
+        """
+        """
+        if fid in self.iterkeys():
+            self[fid].set_result(result)
+            
+    def set_batch_result(self, batch_inputs, batch_outputs):
+        """
+        set a list of result
+        """
+        self.batch_inputs = batch_inputs
+        self.batch_outputs = batch_outputs
+             
+    def get_result(self, fid):
+        """
+        get result
+        """
+        if fid in self.iterkeys():
+            return self[fid].get_result()
+            
+    def get_batch_result(self):
+        """
+        get result
+        """
+        return self.batch_inputs, self.batch_outputs
+    
+    def set_graph_id(self, id):
+        """
+        Set graph id (from data_group_id at the time the graph produced) 
+        """
+        self.graph_id = id
+        
+    def get_graph_id(self):
+        """
+        Get graph_id
+        """
+        return self.graph_id
+    
+    
+class FitProblem(FitProblemComponent):
+    """
+    FitProblem class allows to link a model with the new name created in _on_model,
+    a name theory created with that model  and the data fitted with the model.
+    FitProblem is mostly used  as value of the dictionary by fitting module.
+    """
+    def __init__(self):
+        FitProblemComponent.__init__(self)
+        """
+        contains information about data and model to fit
+        """
+        ## data used for fitting
+        self.fit_data = None
+        self.theory_data = None
+        self.residuals = None
+        # original data: should not be modified
+        self.original_data = None
+        ## the current model
+        self.model = None
+        ## if 1 this fit problem will be selected to fit , if 0
+        ## it will not be selected for fit
+        self.schedule = 0
+        ##list containing parameter name and value
+        self.list_param = []
+        ## smear object to smear or not data1D
+        self.smearer_computed = False
+        self.smearer_enable = False
+        self.smearer_computer_value = None
+        ## fitting range
+        self.qmin = None
+        self.qmax = None
+        # fit weight
+        self.weight = None
+        self.result = None
+        
+    def enable_smearing(self, flag=False):
+        """
+        :param flag: bool.When flag is 1 get the computer smear value. When
+        flag is 0 ingore smear value.
+        """
+        self.smearer_enable = flag
+        
+    def set_smearer(self, smearer):
+        """
+        save reference of  smear object on fitdata
+        
+        :param smear: smear object from DataLoader
+        
+        """
+        self.smearer_computer_value = smearer
+       
+    def get_smearer(self):
+        """
+        return smear object
+        """
+        if not self.smearer_enable:
+            return None
+        if not self.smearer_computed:
+            #smeari_selection should be call only once per fitproblem
+            self.smearer_computer_value = smear_selection(self.fit_data,
+                                                           self.model)
+            self.smearer_computed = True
+        return self.smearer_computer_value
+    
+    def save_model_name(self, name):
+        """
+        """
+        self.name_per_page = name
+        
+    def get_name(self):
+        """
+        """
+        return self.name_per_page
+    
+    def set_model(self, model):
+        """
+        associates each model with its new created name
+        :param model: model selected
+        :param name: name created for model
+        """
+        self.model = model
+        self.smearer_computer_value = smear_selection(self.fit_data,
+                                                           self.model)
+        self.smearer_computed = True
+        
+    def get_model(self):
+        """
+        :return: saved model
+        """
+        return self.model
+   
+    def set_residuals(self, residuals):
+        """
+        save a copy of residual
+        :param data: data selected
+        """
+        self.residuals = residuals
+            
+    def get_residuals(self):
+        """
+        :return: residuals
+        """
+        return self.residuals
+        
+    def set_theory_data(self, data):
+        """
+        save a copy of the data select to fit
+        
+        :param data: data selected
+        
+        """
+        self.theory_data = copy.deepcopy(data)
+        
+    def get_theory_data(self):
+        """
+        :return: theory generated with the current model and data of this class
+        """
+        return self.theory_data
+
+    def set_fit_data(self, data):
+        """
+        Store data associated with this class
+        :param data: list of data selected
+        """
+        self.original_data = None
+        self.fit_data = None
+        # original data: should not be modified
+        self.original_data = data
+        # fit data: used for fit and can be modified for convenience
+        self.fit_data = copy.deepcopy(data)
+        self.smearer_computer_value = smear_selection(self.fit_data,
+                                                           self.model)
+        self.smearer_computed = True
+        self.result = None
+        
+    def get_fit_data(self):
+        """
+        :return: data associate with this class
+        """
+        return self.fit_data
+    
+    def get_origin_data(self):
+        """
+        """
+        return self.original_data
+    
+    def set_weight(self, is2d, flag=None):
+        """
+        Received flag and compute error on data.
+        :param flag: flag to transform error of data.
+        :param is2d: flag to distinguish 1D to 2D Data
+        """
+        from sas import get_weight
+        # send original data for weighting
+        self.weight = get_weight(data=self.original_data, is2d=is2d, flag=flag)
+        if is2d:
+            self.fit_data.err_data = self.weight
+        else:
+            self.fit_data.dy = self.weight
+
+    def get_weight(self):
+        """
+        returns weight array
+        """
+        return self.weight
+    
+    def set_param2fit(self, list):
+        """
+        Store param names to fit (checked)
+        :param list: list of the param names
+        """
+        self.list_param2fit = list
+        
+    def get_param2fit(self):
+        """
+        return the list param names to fit
+        """
+        return self.list_param2fit
+    
+    def set_model_param(self, name, value=None):
+        """
+        Store the name and value of a parameter of this fitproblem's model
+        :param name: name of the given parameter
+        :param value: value of that parameter
+        """
+        self.list_param.append([name, value])
+        
+    def get_model_param(self):
+        """
+        return list of couple of parameter name and value
+        """
+        return self.list_param
+        
+    def schedule_tofit(self, schedule=0):
+        """
+        set schedule to true to decide if this fit  must be performed
+        """
+        self.schedule = schedule
+        
+    def get_scheduled(self):
+        """
+        return true or false if a problem as being schedule for fitting
+        """
+        return self.schedule
+    
+    def set_range(self, qmin=None, qmax=None):
+        """
+        set fitting range
+        :param qmin: minimum value to consider for the fit range
+        :param qmax: maximum value to consider for the fit range
+        """
+        self.qmin = qmin
+        self.qmax = qmax
+        
+    def get_range(self):
+        """
+        :return: fitting range
+        
+        """
+        return self.qmin, self.qmax
+    
+    def clear_model_param(self):
+        """
+        clear constraint info
+        """
+        self.list_param = []
+        
+    def set_fit_tab_caption(self, caption):
+        """
+        """
+        self.fit_tab_caption = str(caption)
+        
+    def get_fit_tab_caption(self):
+        """
+        """
+        return self.fit_tab_caption
+    
+    def set_graph_id(self, id):
+        """
+        Set graph id (from data_group_id at the time the graph produced)
+        """
+        self.graph_id = id
+        
+    def get_graph_id(self):
+        """
+        Get graph_id
+        """
+        return self.graph_id
+    
+    def set_result(self, result):
+        """
+        """
+        self.result = result
+        
+    def get_result(self):
+        """
+        get result
+        """
+        return self.result
Index: src/sas/perspectives/fitting/fitting.py
===================================================================
--- src/sas/perspectives/fitting/fitting.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/fitting/fitting.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,2208 @@
+"""
+    Fitting perspective
+"""
+################################################################################
+#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 2009, University of Tennessee
+################################################################################
+import re
+import sys
+import os
+import wx
+import logging
+import numpy
+import time
+from copy import deepcopy
+import models
+
+from sas.dataloader.loader import Loader
+from sas.guiframe.dataFitting import Data2D
+from sas.guiframe.dataFitting import Data1D
+from sas.guiframe.dataFitting import check_data_validity
+from sas.guiframe.events import NewPlotEvent 
+from sas.guiframe.events import StatusEvent  
+from sas.guiframe.events import EVT_SLICER_PANEL
+from sas.guiframe.events import EVT_SLICER_PARS_UPDATE
+from sas.guiframe.gui_style import GUIFRAME_ID
+from sas.guiframe.plugin_base import PluginBase 
+from sas.guiframe.data_processor import BatchCell
+from sas.fit.Fitting import Fit
+from sas import ConsoleUpdate
+from sas import FitProblemDictionary
+from sas import FitPanel
+from sas import ResultPanel, PlotResultEvent
+
+from sas import FitThread
+from sas import Reader
+from sas import Chi2UpdateEvent
+from sas.perspectives.calculator.model_editor import TextDialog
+from sas.perspectives.calculator.model_editor import EditorWindow
+from sas.guiframe.gui_manager import MDIFrame
+
+# TODO: remove globals from interface to bumps options!
+# Default bumps to use the levenberg-marquardt optimizer
+import bumps.fitters
+bumps.fitters.FIT_DEFAULT = 'lm'
+
+MAX_NBR_DATA = 4
+SANS_F_TOL = 5e-05
+
+(PageInfoEvent, EVT_PAGE_INFO) = wx.lib.newevent.NewEvent()
+
+
+if sys.platform == "win32":
+    ON_MAC = False
+else:
+    ON_MAC = True
+
+
+
+class Plugin(PluginBase):
+    """
+    Fitting plugin is used to perform fit
+    """
+    def __init__(self, standalone=False):
+        PluginBase.__init__(self, name="Fitting", standalone=standalone)
+        
+        #list of panel to send to guiframe
+        self.mypanels = []
+        # reference to the current running thread
+        self.calc_2D = None
+        self.calc_1D = None
+        
+        self.color_dict = {}
+        
+        self.fit_thread_list = {}
+        self.residuals = None
+        self.weight = None
+        self.fit_panel = None
+        self.plot_panel = None
+        # Start with a good default
+        self.elapsed = 0.022
+        self.fit_panel = None
+        #Flag to let the plug-in know that it is running stand alone
+        self.standalone = True
+        ## dictionary of page closed and id
+        self.closed_page_dict = {}
+        ## Fit engine
+        self._fit_engine = 'bumps'
+        self._gui_engine = None
+        ## Relative error desired in the sum of squares (float); scipy only
+        self.ftol = SANS_F_TOL
+        self.batch_reset_flag = True
+        #List of selected data
+        self.selected_data_list = []
+        ## list of slicer panel created to display slicer parameters and results
+        self.slicer_panels = []
+        # model 2D view
+        self.model2D_id = None
+        #keep reference of the simultaneous fit page
+        self.sim_page = None
+        self.sim_menu = None
+        self.batch_page = None
+        self.batch_menu = None
+        self.index_model = 0
+        self.test_model_color = None
+        #Create a reader for fit page's state
+        self.state_reader = None
+        self._extensions = '.fitv'
+        self.scipy_id = wx.NewId()
+        self.park_id = wx.NewId()
+        self.bumps_id = wx.NewId()
+        self.menu1 = None
+        self.new_model_frame = None
+        
+        self.temp_state = []
+        self.state_index = 0
+        self.sfile_ext = None
+        # take care of saving  data, model and page associated with each other
+        self.page_finder = {}
+        # Log startup
+        logging.info("Fitting plug-in started")
+        self.batch_capable = self.get_batch_capable()
+    
+    def get_batch_capable(self):
+        """
+        Check if the plugin has a batch capability
+        """
+        return True
+    
+    def create_fit_problem(self, page_id):
+        """
+        Given an ID create a fitproblem container
+        """
+        self.page_finder[page_id] = FitProblemDictionary()
+        
+    def delete_fit_problem(self, page_id):
+        """
+        Given an ID create a fitproblem container
+        """
+        if page_id in self.page_finder.iterkeys():
+            del self.page_finder[page_id]
+        
+    def add_color(self, color, id):
+        """
+        adds a color as a key with a plot id as its value to a dictionary
+        """
+        self.color_dict[id] = color
+        
+    def on_batch_selection(self, flag):
+        """
+        switch the the notebook of batch mode or not
+        """
+        self.batch_on = flag
+        if self.fit_panel is not None:
+            self.fit_panel.batch_on = self.batch_on
+        
+    def populate_menu(self, owner):
+        """
+        Create a menu for the Fitting plug-in
+        
+        :param id: id to create a menu
+        :param owner: owner of menu
+        
+        :return: list of information to populate the main menu
+        
+        """
+        #Menu for fitting
+        self.menu1 = wx.Menu()
+        id1 = wx.NewId()
+        simul_help = "Add new fit panel"
+        self.menu1.Append(id1, '&New Fit Page', simul_help)
+        wx.EVT_MENU(owner, id1, self.on_add_new_page)
+        self.menu1.AppendSeparator()
+        self.id_simfit = wx.NewId()
+        simul_help = "Constrained or Simultaneous Fit"
+        self.menu1.Append(self.id_simfit, '&Constrained or Simultaneous Fit', simul_help)
+        wx.EVT_MENU(owner, self.id_simfit, self.on_add_sim_page)
+        self.sim_menu = self.menu1.FindItemById(self.id_simfit)
+        self.sim_menu.Enable(False)
+        #combined Batch
+        self.id_batchfit = wx.NewId()
+        batch_help = "Combined Batch"
+        self.menu1.Append(self.id_batchfit, '&Combine Batch Fit', batch_help)
+        wx.EVT_MENU(owner, self.id_batchfit, self.on_add_sim_page)
+        self.batch_menu = self.menu1.FindItemById(self.id_batchfit)
+        self.batch_menu.Enable(False)
+        self.menu1.AppendSeparator()
+        #Set park engine
+        scipy_help = "Scipy Engine: Perform Simple fit. More in Help window...."
+        self.menu1.AppendCheckItem(self.scipy_id, "Simple FitEngine [LeastSq]",
+                                   scipy_help)
+        wx.EVT_MENU(owner, self.scipy_id, self._onset_engine_scipy)
+        
+        park_help = "Park Engine: Perform Complex fit. More in Help window...."
+        self.menu1.AppendCheckItem(self.park_id, "Complex FitEngine [ParkMC]",
+                                   park_help)
+        wx.EVT_MENU(owner, self.park_id, self._onset_engine_park)
+        
+        bumps_help = "Bumps: fitting and uncertainty analysis. More in Help window...."
+        self.menu1.AppendCheckItem(self.bumps_id, "Bumps fit",
+                                   bumps_help)
+        wx.EVT_MENU(owner, self.bumps_id, self._onset_engine_bumps)
+        
+        self.menu1.FindItemById(self.scipy_id).Check(self._fit_engine=="scipy")
+        self.menu1.FindItemById(self.park_id).Check(self._fit_engine=="park")
+        self.menu1.FindItemById(self.bumps_id).Check(self._fit_engine=="bumps")
+        self.menu1.AppendSeparator()
+        self.id_tol = wx.NewId()
+        ftol_help = "Change the current FTolerance (=%s) " % str(self.ftol)
+        ftol_help += "of Simple FitEngine..."
+        self.menu1.Append(self.id_tol, "Change FTolerance",
+                                   ftol_help)
+        wx.EVT_MENU(owner, self.id_tol, self.show_ftol_dialog)
+
+        self.id_bumps_options = wx.NewId()
+        bopts_help = "Bumps fitting options"
+        self.menu1.Append(self.id_bumps_options, 'Bumps &Options', bopts_help)
+        wx.EVT_MENU(owner, self.id_bumps_options, self.on_bumps_options)
+        self.bumps_options_menu = self.menu1.FindItemById(self.id_bumps_options)
+        self.bumps_options_menu.Enable(True)
+
+        self.id_result_panel = wx.NewId()
+        self.menu1.Append(self.id_result_panel, "Fit Results", "Show fit results panel")
+        wx.EVT_MENU(owner, self.id_result_panel, lambda ev: self.result_frame.Show())
+        self.menu1.AppendSeparator()
+        
+        self.id_reset_flag = wx.NewId()
+        resetf_help = "BatchFit: If checked, the initial param values will be "
+        resetf_help += "propagated from the previous results. "
+        resetf_help += "Otherwise, the same initial param values will be used "
+        resetf_help += "for all fittings."
+        self.menu1.AppendCheckItem(self.id_reset_flag,
+                                   "Chain Fitting [BatchFit Only]",
+                                   resetf_help)
+        wx.EVT_MENU(owner, self.id_reset_flag, self.on_reset_batch_flag)
+        chain_menu = self.menu1.FindItemById(self.id_reset_flag)
+        chain_menu.Check(not self.batch_reset_flag)
+        chain_menu.Enable(self.batch_on)
+        
+        self.menu1.AppendSeparator()
+        self.edit_model_menu = wx.Menu()
+        # Find and put files name in menu
+        try:
+            self.set_edit_menu(owner=owner)
+        except:
+            raise
+        
+        self.id_edit = wx.NewId()
+        editmodel_help = "Edit customized model sample file"
+        self.menu1.AppendMenu(self.id_edit, "Edit Custom Model",
+                              self.edit_model_menu, editmodel_help)
+        #create  menubar items
+        return [(self.menu1, self.sub_menu)]
+    
+    def edit_custom_model(self, event):
+        """
+        Get the python editor panel
+        """
+        id = event.GetId()
+        label = self.edit_menu.GetLabel(id)
+        from sas.perspectives.calculator.pyconsole import PyConsole
+        filename = os.path.join(models.find_plugins_dir(), label)
+        frame = PyConsole(parent=self.parent, manager=self,
+                          panel=self.fit_panel,
+                          title='Advanced Custom Model Editor',
+                          filename=filename)
+        self.put_icon(frame)
+        frame.Show(True)
+    
+    def delete_custom_model(self, event):
+        """
+        Delete custom model file
+        """
+        id = event.GetId()
+        label = self.delete_menu.GetLabel(id)
+        toks = os.path.splitext(label)
+        path = os.path.join(models.find_plugins_dir(), toks[0])
+        try:
+            for ext in ['.py', '.pyc']:
+                p_path = path + ext
+                os.remove(p_path)
+            self.update_custom_combo()
+            if os.path.isfile(p_path):
+                msg = "Sorry! Could not be able to delete the default "
+                msg += "custom model... \n"
+                msg += "Please manually remove the files (.py, .pyc) "
+                msg += "in the 'plugin_models' folder \n"
+                msg += "inside of the SansView application, "
+                msg += "and try it again."
+                wx.MessageBox(msg, 'Info')
+                #wx.PostEvent(self.parent, StatusEvent(status=msg, type='stop',
+                #                                      info='warning'))
+            else:
+                self.delete_menu.Delete(id)
+                for item in self.edit_menu.GetMenuItems():
+                    if item.GetLabel() == label:
+                        self.edit_menu.DeleteItem(item)
+                        msg = "The custom model, %s, has been deleted."% label
+                        wx.PostEvent(self.parent, StatusEvent(status=msg, 
+                                                type='stop', info='info'))
+                        break
+        except:
+            msg ='Delete Error: \nCould not delete the file; Check if in use.'
+            wx.MessageBox(msg, 'Error')
+    
+    def make_sum_model(self, event):
+        """
+        Edit summodel template and make one
+        """
+        id = event.GetId()
+        model_manager = models.ModelManager()
+        model_list = model_manager.get_model_name_list()
+        plug_dir = models.find_plugins_dir()
+        textdial = TextDialog(None, self, -1, 'Easy Sum/Multi(p1, p2) Editor', 
+                              model_list, plug_dir)
+        self.put_icon(textdial)
+        textdial.ShowModal()
+        textdial.Destroy()
+    
+    def make_new_model(self, event):
+        """
+        Make new model
+        """
+        if self.new_model_frame != None:
+            self.new_model_frame.Show(False)
+            self.new_model_frame.Show(True)
+        else:
+            id = event.GetId()
+            dir_path = models.find_plugins_dir()
+            title = "New Custom Model Function"
+            self.new_model_frame = EditorWindow(parent=self, base=self,
+                                                path=dir_path, title=title)
+            self.put_icon(self.new_model_frame)
+        self.new_model_frame.Show(True)
+
+    def update_custom_combo(self):
+        """
+        Update custom model list in the fitpage combo box
+        """
+        custom_model = 'Customized Models'
+        try:
+            # Update edit menus
+            self.set_edit_menu_helper(self.parent, self.edit_custom_model)
+            self.set_edit_menu_helper(self.parent, self.delete_custom_model)
+            temp = self.fit_panel.reset_pmodel_list()
+            if temp:
+                # Set the new custom model list for all fit pages
+                for uid, page in self.fit_panel.opened_pages.iteritems():
+                    if hasattr(page, "formfactorbox"):
+                        page.model_list_box = temp
+                        current_val = page.formfactorbox.GetLabel()
+                        #if page.plugin_rbutton.GetValue():
+                        mod_cat = page.categorybox.GetStringSelection()
+                        if mod_cat == custom_model:
+                            #pos = page.formfactorbox.GetSelection()
+                            page._show_combox_helper()
+                            new_val = page.formfactorbox.GetLabel()
+                            if current_val != new_val and new_val != '':
+                                page.formfactorbox.SetLabel(new_val)
+                            else:
+                                page.formfactorbox.SetLabel(current_val)
+        except:
+            pass
+        
+        
+    def set_edit_menu(self, owner):
+        """
+        Set list of the edit model menu labels
+        """
+        id = wx.NewId()
+        #new_model_menu = wx.Menu()
+        self.edit_model_menu.Append(id, 'New',
+                                   'Add a new model function')
+        wx.EVT_MENU(owner, id, self.make_new_model)
+        id = wx.NewId()
+        self.edit_model_menu.Append(id, 'Sum|Multi(p1, p2)',
+                                    'Sum of two model functions')
+        wx.EVT_MENU(owner, id, self.make_sum_model)
+        e_id = wx.NewId()
+        self.edit_menu = wx.Menu()
+        self.edit_model_menu.AppendMenu(e_id, 
+                                    'Advanced', self.edit_menu) 
+        self.set_edit_menu_helper(owner, self.edit_custom_model)
+
+        d_id = wx.NewId()
+        self.delete_menu = wx.Menu()
+        self.edit_model_menu.AppendMenu(d_id,
+                                        'Delete', self.delete_menu)
+        self.set_edit_menu_helper(owner, self.delete_custom_model)
+    
+    def set_edit_menu_helper(self, owner=None, menu=None):
+        """
+        help for setting list of the edit model menu labels
+        """
+        if menu == None:
+            menu = self.edit_custom_model
+        list_fnames = os.listdir(models.find_plugins_dir())
+        list_fnames.sort()
+        for f_item in list_fnames:
+            name = os.path.basename(f_item)
+            toks = os.path.splitext(name)
+            if toks[-1] == '.py' and not toks[0] == '__init__':
+                if menu == self.edit_custom_model:
+                    if toks[0] == 'easy_sum_of_p1_p2':
+                        continue
+                    submenu = self.edit_menu
+                else:
+                    submenu = self.delete_menu
+                has_file = False
+                for item in submenu.GetMenuItems():
+                    if name == submenu.GetLabel(item.GetId()):
+                        has_file = True
+                if not has_file:
+                    id = wx.NewId()
+                    submenu.Append(id, name)
+                    wx.EVT_MENU(owner, id, menu)
+                    has_file = False
+
+    def put_icon(self, frame):
+        """
+        Put icon in the frame title bar
+        """
+        if hasattr(frame, "IsIconized"):
+            if not frame.IsIconized():
+                try:
+                    icon = self.parent.GetIcon()
+                    frame.SetIcon(icon)
+                except:
+                    pass
+
+    def on_add_sim_page(self, event):
+        """
+        Create a page to access simultaneous fit option
+        """
+        id = event.GetId()
+        caption = "Const & Simul Fit"
+        page = self.sim_page
+        if id == self.id_batchfit:
+            caption = "Combined Batch"
+            page = self.batch_page
+            
+        def set_focus_page(page):
+            page.Show(True)
+            page.Refresh()
+            page.SetFocus()
+            #self.parent._mgr.Update()
+            msg = "%s already opened\n" % str(page.window_caption)
+            wx.PostEvent(self.parent, StatusEvent(status=msg))
+            
+        if page != None:
+            return set_focus_page(page)
+        if caption == "Const & Simul Fit":
+            self.sim_page = self.fit_panel.add_sim_page(caption=caption)
+        else:
+            self.batch_page = self.fit_panel.add_sim_page(caption=caption)
+        
+    def help(self, evt):
+        """
+        Show a general help dialog.
+        """
+        from help_panel import  HelpWindow
+        frame = HelpWindow(None, -1, 'HelpWindow')
+        if hasattr(frame, "IsIconized"):
+            if not frame.IsIconized():
+                try:
+                    icon = self.parent.GetIcon()
+                    frame.SetIcon(icon)
+                except:
+                    pass
+        frame.Show(True)
+        
+    def get_context_menu(self, plotpanel=None):
+        """
+        Get the context menu items available for P(r).them allow fitting option
+        for Data2D and Data1D only.
+        
+        :param graph: the Graph object to which we attach the context menu
+        
+        :return: a list of menu items with call-back function
+        
+        :note: if Data1D was generated from Theory1D
+                the fitting option is not allowed
+                
+        """
+        self.plot_panel = plotpanel
+        graph = plotpanel.graph
+        fit_option = "Select data for fitting"
+        fit_hint =  "Dialog with fitting parameters "
+        
+        if graph.selected_plottable not in plotpanel.plots:
+            return []
+        item = plotpanel.plots[graph.selected_plottable]
+        if item.__class__.__name__ is "Data2D":
+            if hasattr(item, "is_data"):
+                if item.is_data:
+                    return [[fit_option, fit_hint, self._onSelect]]
+                else:
+                    return [] 
+            return [[fit_option, fit_hint, self._onSelect]]
+        else:
+            
+            # if is_data is true , this in an actual data loaded
+            #else it is a data created from a theory model
+            if hasattr(item, "is_data"):
+                if item.is_data:
+                    return [[fit_option, fit_hint, self._onSelect]]
+                else:
+                    return []
+            return [[fit_option, fit_hint, self._onSelect]]
+        return []
+
+    def get_panels(self, parent):
+        """
+        Create and return a list of panel objects
+        """
+        self.parent = parent
+        #self.parent.Bind(EVT_FITSTATE_UPDATE, self.on_set_state_helper)
+        # Creation of the fit panel
+        self.frame = MDIFrame(self.parent, None, 'None', (100, 200))
+        self.fit_panel = FitPanel(parent=self.frame, manager=self)
+        self.frame.set_panel(self.fit_panel)
+        self._frame_set_helper()
+        self.on_add_new_page(event=None)
+        #Set the manager for the main panel
+        self.fit_panel.set_manager(self)
+        # List of windows used for the perspective
+        self.perspective = []
+        self.perspective.append(self.fit_panel.window_name)
+
+        self.result_frame = MDIFrame(self.parent, None, ResultPanel.window_caption, (220, 200))
+        self.result_panel = ResultPanel(parent=self.result_frame, manager=self)
+        self.perspective.append(self.result_panel.window_name)
+       
+        #index number to create random model name
+        self.index_model = 0
+        self.index_theory = 0
+        self.parent.Bind(EVT_SLICER_PANEL, self._on_slicer_event)
+        self.parent.Bind(EVT_SLICER_PARS_UPDATE, self._onEVT_SLICER_PANEL)
+        #self.parent._mgr.Bind(wx.aui.EVT_AUI_PANE_CLOSE,self._onclearslicer)
+        #Create reader when fitting panel are created
+        self.state_reader = Reader(self.set_state)
+        #append that reader to list of available reader
+        loader = Loader()
+        loader.associate_file_reader(".fitv", self.state_reader)
+        #Send the fitting panel to guiframe
+        self.mypanels.append(self.fit_panel)
+        self.mypanels.append(self.result_panel)
+        return self.mypanels
+    
+    def clear_panel(self):
+        """
+        """
+        self.fit_panel.clear_panel()
+        
+    def set_default_perspective(self):
+        """
+        Call back method that True to notify the parent that the current plug-in
+        can be set as default perspective.
+        when returning False, the plug-in is not candidate for an automatic
+        default perspective setting
+        """
+        return True
+    
+    def delete_data(self, data):
+        """
+        delete  the given data from panel
+        """
+        self.fit_panel.delete_data(data)
+        
+    def set_data(self, data_list=None):
+        """
+        receive a list of data to fit
+        """
+        if data_list is None:
+            data_list = []
+        selected_data_list = []
+        if self.batch_on:
+            page = self.add_fit_page(data=data_list)
+        else:
+            if len(data_list) > MAX_NBR_DATA:
+                from fitting_widgets import DataDialog
+                dlg = DataDialog(data_list=data_list, nb_data=MAX_NBR_DATA)
+                if dlg.ShowModal() == wx.ID_OK:
+                    selected_data_list = dlg.get_data()
+                dlg.Destroy()
+                
+            else:
+                selected_data_list = data_list
+            try:
+                group_id = wx.NewId()
+                for data in selected_data_list:
+                    if data is not None:
+                        # 2D has no same group_id
+                        if data.__class__.__name__ == 'Data2D':
+                            group_id = wx.NewId()
+                        data.group_id = group_id
+                        if group_id not in data.list_group_id:
+                            data.list_group_id.append(group_id)
+                        page = self.add_fit_page(data=[data])
+            except:
+                msg = "Fitting Set_data: " + str(sys.exc_value)
+                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error"))
+    
+    def set_theory(self, theory_list=None):
+        """
+        """
+        #set the model state for a given theory_state:
+        for item in theory_list:
+            try:
+                _, theory_state = item
+                self.fit_panel.set_model_state(theory_state)
+            except:
+                msg = "Fitting: cannot deal with the theory received"
+                logging.error("set_theory " + msg + "\n" + str(sys.exc_value))
+                wx.PostEvent(self.parent,
+                             StatusEvent(status=msg, info="error"))
+            
+    def set_state(self, state=None, datainfo=None, format=None):
+        """
+        Call-back method for the fit page state reader.
+        This method is called when a .fitv/.svs file is loaded.
+        
+        : param state: PageState object
+        : param datainfo: data
+        """
+        #state = self.state_reader.get_state()
+        if state != None:
+            state = state.clone()
+            # store fitting state in temp_state
+            self.temp_state.append(state)
+        else:
+            self.temp_state = []
+        # index to start with for a new set_state
+        self.state_index = 0
+        # state file format
+        self.sfile_ext = format
+        
+        self.on_set_state_helper(event=None)
+
+    def  on_set_state_helper(self, event=None):
+        """
+        Set_state_helper. This actually sets state
+        after plotting data from state file.
+        
+        : event: FitStateUpdateEvent called
+            by dataloader.plot_data from guiframe
+        """
+        if len(self.temp_state) == 0:
+            if self.state_index == 0 and len(self.mypanels) <= 0 \
+            and self.sfile_ext == '.svs':
+                self.temp_state = []
+                self.state_index = 0
+            return
+        
+        try:
+            # Load fitting state
+            state = self.temp_state[self.state_index]
+            #panel state should have model selection to set_state
+            if state.formfactorcombobox != None:
+                #set state
+                data = self.parent.create_gui_data(state.data)
+                data.group_id = state.data.group_id
+                self.parent.add_data(data_list={data.id: data})
+                wx.PostEvent(self.parent, NewPlotEvent(plot=data,
+                                        title=data.title))
+                #need to be fix later make sure we are sendind guiframe.data
+                #to panel
+                state.data = data
+                page = self.fit_panel.set_state(state)
+            else:
+                #just set data because set_state won't work
+                data = self.parent.create_gui_data(state.data)
+                data.group_id = state.data.group_id
+                self.parent.add_data(data_list={data.id: data})
+                wx.PostEvent(self.parent, NewPlotEvent(plot=data,
+                                        title=data.title))
+                page = self.add_fit_page([data])
+                caption = page.window_caption
+                self.store_data(uid=page.uid, data_list=page.get_data_list(),
+                        caption=caption)
+                self.mypanels.append(page)
+                
+            # get ready for the next set_state
+            self.state_index += 1
+
+            #reset state variables to default when all set_state is finished.
+            if len(self.temp_state) == self.state_index:
+                
+                self.temp_state = []
+                #self.state_index = 0
+                # Make sure the user sees the fitting panel after loading
+                #self.parent.set_perspective(self.perspective)
+                self.on_perspective(event=None)
+        except:
+            self.state_index = 0
+            self.temp_state = []
+            raise
+        
+    def set_param2fit(self, uid, param2fit):
+        """
+        Set the list of param names to fit for fitprobelm
+        """
+        self.page_finder[uid].set_param2fit(param2fit)
+        
+    def set_graph_id(self, uid, graph_id):
+        """
+        Set graph_id for fitprobelm
+        """
+        self.page_finder[uid].set_graph_id(graph_id)
+        
+    def get_graph_id(self, uid):
+        """
+        Set graph_id for fitprobelm
+        """
+        return self.page_finder[uid].get_graph_id()
+                          
+    def save_fit_state(self, filepath, fitstate):
+        """
+        save fit page state into file
+        """
+        self.state_reader.write(filename=filepath, fitstate=fitstate)
+
+    def set_fit_weight(self, uid, flag, is2d=False, fid=None):
+        """
+        Set the fit weights of a given page for all
+        its data by default. If fid is provide then set the range
+        only for the data with fid as id
+        :param uid: id corresponding to a fit page
+        :param fid: id corresponding to a fit problem (data, model)
+        :param weight: current dy data
+        """
+        if uid in self.page_finder.keys():
+            self.page_finder[uid].set_weight(flag=flag, is2d=is2d)
+                    
+    def set_fit_range(self, uid, qmin, qmax, fid=None):
+        """
+        Set the fitting range of a given page for all
+        its data by default. If fid is provide then set the range
+        only for the data with fid as id
+        :param uid: id corresponding to a fit page
+        :param fid: id corresponding to a fit problem (data, model)
+        :param qmin: minimum  value of the fit range
+        :param qmax: maximum  value of the fit range
+        """
+        if uid in self.page_finder.keys():
+            self.page_finder[uid].set_range(qmin=qmin, qmax=qmax, fid=fid)
+      
+    def schedule_for_fit(self, value=0, uid=None):
+        """
+        Set the fit problem field to 0 or 1 to schedule that problem to fit.
+        Schedule the specified fitproblem or get the fit problem related to
+        the current page and set value.
+        :param value: integer 0 or 1
+        :param uid: the id related to a page contaning fitting information
+        """
+        if uid in self.page_finder.keys():
+            self.page_finder[uid].schedule_tofit(value)
+          
+    def get_page_finder(self):
+        """
+        return self.page_finder used also by simfitpage.py
+        """
+        return self.page_finder
+    
+    def set_page_finder(self, modelname, names, values):
+        """
+        Used by simfitpage.py to reset a parameter given the string constrainst.
+         
+        :param modelname: the name ot the model for with the parameter
+                            has to reset
+        :param value: can be a string in this case.
+        :param names: the paramter name
+         
+        :note: expecting park used for fit.
+         
+        """
+        sim_page_id = self.sim_page.uid
+        for uid, value in self.page_finder.iteritems():
+            if uid != sim_page_id and uid != self.batch_page.uid:
+                list = value.get_model()
+                model = list[0]
+                if model.name == modelname:
+                    value.set_model_param(names, values)
+                    break
+         
+    def split_string(self, item):
+        """
+        receive a word containing dot and split it. used to split parameterset
+        name into model name and parameter name example: ::
+        
+            paramaterset (item) = M1.A
+            Will return model_name = M1 , parameter name = A
+            
+        """
+        if item.find(".") >= 0:
+            param_names = re.split("\.", item)
+            model_name = param_names[0]
+            ##Assume max len is 3; eg., M0.radius.width
+            if len(param_names) == 3:
+                param_name = param_names[1] + "." + param_names[2]
+            else:
+                param_name = param_names[1]
+            return model_name, param_name
+   
+    def set_ftol(self, ftol=None):
+        """
+        Set ftol: Relative error desired in the sum of chi squares.
+        """
+        # check if it is flaot
+        try:
+            f_tol = float(ftol)
+        except:
+            # default
+            f_tol = SANS_F_TOL
+            
+        self.ftol = f_tol
+        # update ftol menu help strings
+        ftol_help = "Change the current FTolerance (=%s) " % str(self.ftol)
+        ftol_help += "of Simple FitEngine..."
+        if self.menu1 != None:
+            self.menu1.SetHelpString(self.id_tol, ftol_help)
+        
+    def show_ftol_dialog(self, event=None):
+        """
+        Dialog to select ftol for Scipy
+        """
+        from ftol_dialog import ChangeFtol
+        dialog = ChangeFtol(self.parent, self)
+        result = dialog.ShowModal()
+        if result == wx.ID_OK:
+            value = dialog.get_ftol()
+            if value is not None:
+                self.set_ftol(value)
+                msg = "The ftol (LeastSq) is set to %s." % value
+            else:
+                msg = "Error in the selection... No change in ftol."
+            # post event for info
+            wx.PostEvent(self.parent,
+                         StatusEvent(status=msg, info='warning'))
+        dialog.Destroy()
+
+    def on_bumps_options(self, event=None):
+        from bumps.gui.fit_dialog import OpenFitOptions
+        OpenFitOptions()
+
+    def stop_fit(self, uid):
+        """
+        Stop the fit engine
+        """
+        if uid in self.fit_thread_list.keys():
+            calc_fit = self.fit_thread_list[uid]
+            if calc_fit is not  None and calc_fit.isrunning():
+                calc_fit.stop()
+                msg = "Fit stop!"
+                wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
+            del self.fit_thread_list[uid]
+        #set the fit button label of page when fit stop is trigger from
+        #simultaneous fit pane
+        sim_flag = self.sim_page is not None and uid == self.sim_page.uid
+        batch_flag = self.batch_page is not None and uid == self.batch_page.uid
+        if sim_flag or batch_flag:
+            for uid, value in self.page_finder.iteritems():
+                if value.get_scheduled() == 1:
+                    if uid in self.fit_panel.opened_pages.keys():
+                        panel = self.fit_panel.opened_pages[uid]
+                        panel._on_fit_complete()
+  
+    def set_smearer(self, uid, smearer, fid, qmin=None, qmax=None, draw=True,
+                    enable_smearer=False):
+        """
+        Get a smear object and store it to a fit problem of fid as id. If proper
+        flag is enable , will plot the theory with smearing information.
+        
+        :param smearer: smear object to allow smearing data of id fid
+        :param enable_smearer: Define whether or not all (data, model) contained
+            in the structure of id uid will be smeared before fitting.
+        :param qmin: the maximum value of the theory plotting range
+        :param qmax: the maximum value of the theory plotting range
+        :param draw: Determine if the theory needs to be plot
+        """
+        if uid not in self.page_finder.keys():
+            return
+        self.page_finder[uid].enable_smearing(flag=enable_smearer)
+        self.page_finder[uid].set_smearer(smearer, fid=fid)
+        if draw:
+            ## draw model 1D with smeared data
+            data = self.page_finder[uid].get_fit_data(fid=fid)
+            if data is None:
+                msg = "set_mearer requires at least data.\n"
+                msg += "Got data = %s .\n" % str(data)
+                return
+                #raise ValueError, msg
+            model = self.page_finder[uid].get_model(fid=fid)
+            if model is None:
+                return
+            enable1D = issubclass(data.__class__, Data1D)
+            enable2D = issubclass(data.__class__, Data2D)
+            ## if user has already selected a model to plot
+            ## redraw the model with data smeared
+            smear = self.page_finder[uid].get_smearer(fid=fid)
+
+            # compute weight for the current data
+            weight = self.page_finder[uid].get_weight(fid=fid)
+
+            self.draw_model(model=model, data=data, page_id=uid, smearer=smear,
+                enable1D=enable1D, enable2D=enable2D,
+                qmin=qmin, qmax=qmax, weight=weight)
+            self._mac_sleep(0.2)
+            
+    def _mac_sleep(self, sec=0.2):
+        """
+        Give sleep to MAC
+        """
+        if ON_MAC:
+            time.sleep(sec)
+        
+    def draw_model(self, model, page_id, data=None, smearer=None,
+                   enable1D=True, enable2D=False,
+                   state=None,
+                   fid=None,
+                   toggle_mode_on=False,
+                   qmin=None, qmax=None,
+                   update_chisqr=True, weight=None, source='model'):
+        """
+        Draw model.
+        
+        :param model: the model to draw
+        :param name: the name of the model to draw
+        :param data: the data on which the model is based to be drawn
+        :param description: model's description
+        :param enable1D: if true enable drawing model 1D
+        :param enable2D: if true enable drawing model 2D
+        :param qmin:  Range's minimum value to draw model
+        :param qmax:  Range's maximum value to draw model
+        :param qstep: number of step to divide the x and y-axis
+        :param update_chisqr: update chisqr [bool]
+             
+        """
+        #self.weight = weight
+        if issubclass(data.__class__, Data1D) or not enable2D:
+            ## draw model 1D with no loaded data
+            self._draw_model1D(model=model,
+                               data=data,
+                               page_id=page_id,
+                               enable1D=enable1D,
+                               smearer=smearer,
+                               qmin=qmin,
+                               qmax=qmax,
+                               fid=fid,
+                               weight=weight,
+                               toggle_mode_on=toggle_mode_on,
+                               state=state,
+                               update_chisqr=update_chisqr,
+                               source=source)
+        else:
+            ## draw model 2D with no initial data
+            self._draw_model2D(model=model,
+                                page_id=page_id,
+                                data=data,
+                                enable2D=enable2D,
+                                smearer=smearer,
+                                qmin=qmin,
+                                qmax=qmax,
+                                fid=fid,
+                                weight=weight,
+                                state=state,
+                                toggle_mode_on=toggle_mode_on,
+                                update_chisqr=update_chisqr,
+                                source=source)
+            
+    def onFit(self, uid):
+        """
+        Get series of data, model, associates parameters and range and send then
+        to  series of fit engines. Fit data and model, display result to
+        corresponding panels.
+        :param uid: id related to the panel currently calling this fit function.
+        """
+        if uid is None: raise RuntimeError("no page to fit") # Should never happen
+
+        # Remember the user selected fit engine before the fit.  Simultaneous
+        # fitting may change the selected engine, so it needs to be restored
+        # when the fit is complete.
+        self._gui_engine = self._fit_engine
+
+        sim_page_uid = getattr(self.sim_page, 'uid', None)
+        batch_page_uid = getattr(self.batch_page, 'uid', None)
+
+        if uid == sim_page_uid:
+            fit_type = 'simultaneous'
+        elif uid == batch_page_uid:
+            fit_type = 'combined_batch'
+        else:
+            fit_type = 'single'
+
+        # if constrained fit, don't use scipy leastsq directly
+        if fit_type == 'simultaneous':
+            if self._fit_engine not in ("park","bumps"):
+                self._on_change_engine(engine='bumps')
+
+
+        fitter_list = []
+        sim_fitter = None
+        if fit_type == 'simultaneous':
+            #simulatanous fit only one engine need to be created
+            sim_fitter = Fit(self._fit_engine)
+            sim_fitter.fitter_id = self.sim_page.uid
+            fitter_list.append(sim_fitter)
+
+        self.current_pg = None
+        list_page_id = []
+        fit_id = 0
+        for page_id, page_info in self.page_finder.iteritems():
+            # For simulfit (uid give with None), do for-loop
+            # if uid is specified (singlefit), do it only on the page.
+            if page_id in (sim_page_uid, batch_page_uid): continue
+            if fit_type == "single" and page_id != uid: continue
+
+            try:
+                if page_info.get_scheduled() == 1:
+                    page_info.nbr_residuals_computed = 0
+                    page = self.fit_panel.get_page_by_id(page_id)
+                    self.set_fit_weight(uid=page.uid,
+                                     flag=page.get_weight_flag(),
+                                     is2d=page._is_2D())
+                    if not page.param_toFit:
+                        msg = "No fitting parameters for %s"%page.window_caption
+                        wx.PostEvent(page.parent.parent,
+                                     StatusEvent(status=msg, info="error",
+                                                 type="stop"))
+                        return False
+                    if not page._update_paramv_on_fit():
+                        msg = "Fitting range or parameter values are"
+                        msg += " invalid in %s" % \
+                                    page.window_caption
+                        wx.PostEvent(page.parent.parent,
+                                     StatusEvent(status=msg, info="error",
+                                     type="stop"))
+                        return False
+
+                    pars = [str(element[1]) for element in page.param_toFit]
+                    fitproblem_list = page_info.values()
+                    for fitproblem in  fitproblem_list:
+                        if sim_fitter is None:
+                            fitter = Fit(self._fit_engine)
+                            fitter.fitter_id = page_id
+                            fitter_list.append(fitter)
+                        else:
+                            fitter = sim_fitter
+                        self._add_problem_to_fit(fitproblem=fitproblem,
+                                             pars=pars,
+                                             fitter=fitter,
+                                             fit_id=fit_id)
+                        fit_id += 1
+                    list_page_id.append(page_id)
+                    page_info.clear_model_param()
+            except KeyboardInterrupt:
+                msg = "Fitting terminated"
+                wx.PostEvent(self.parent, StatusEvent(status=msg, info="info",
+                                                      type="stop"))
+                return True
+            except:
+                msg = "Fitting error: %s" % str(sys.exc_value)
+                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
+                                                      type="stop"))
+                return False
+        ## If a thread is already started, stop it
+        #if self.calc_fit!= None and self.calc_fit.isrunning():
+        #    self.calc_fit.stop()
+        msg = "Fitting is in progress..."
+        wx.PostEvent(self.parent, StatusEvent(status=msg, type="progress"))
+        
+        #Handler used for park engine displayed message
+        handler = ConsoleUpdate(parent=self.parent,
+                                manager=self,
+                                improvement_delta=0.1)
+        self._mac_sleep(0.2)
+
+        # batch fit
+        batch_inputs = {}
+        batch_outputs = {}
+        if fit_type == "simultaneous":
+            page = self.sim_page
+        elif fit_type == "combined_batch":
+            page = self.batch_page
+        else:
+            page = self.fit_panel.get_page_by_id(uid)
+        if page.batch_on:
+            calc_fit = FitThread(handler=handler,
+                                 fn=fitter_list,
+                                 pars=pars,
+                                 batch_inputs=batch_inputs,
+                                 batch_outputs=batch_outputs,
+                                 page_id=list_page_id,
+                                 completefn=self._batch_fit_complete,
+                                 ftol=self.ftol,
+                                 reset_flag=self.batch_reset_flag)
+        else:
+            ## Perform more than 1 fit at the time
+            calc_fit = FitThread(handler=handler,
+                                    fn=fitter_list,
+                                    batch_inputs=batch_inputs,
+                                    batch_outputs=batch_outputs,
+                                    page_id=list_page_id,
+                                    updatefn=handler.update_fit,
+                                    completefn=self._fit_completed,
+                                    ftol=self.ftol)
+        #self.fit_thread_list[current_page_id] = calc_fit
+        self.fit_thread_list[uid] = calc_fit
+        calc_fit.queue()
+        calc_fit.ready(2.5)
+        msg = "Fitting is in progress..."
+        wx.PostEvent(self.parent, StatusEvent(status=msg, type="progress"))
+        
+        return True
+
+    def remove_plot(self, uid, fid=None, theory=False):
+        """
+        remove model plot when a fit page is closed
+        :param uid: the id related to the fitpage to close
+        :param fid: the id of the fitproblem(data, model, range,etc)
+        """
+        if uid not in self.page_finder.keys():
+            return
+        fitproblemList = self.page_finder[uid].get_fit_problem(fid)
+        for fitproblem in fitproblemList:
+            data = fitproblem.get_fit_data()
+            model = fitproblem.get_model()
+            plot_id = None
+            if model is not None:
+                plot_id = data.id + model.name
+            if theory:
+                plot_id = data.id + model.name
+            group_id = data.group_id
+            wx.PostEvent(self.parent, NewPlotEvent(id=plot_id,
+                                                   group_id=group_id,
+                                                   action='remove'))
+           
+    def store_data(self, uid, data_list=None, caption=None):
+        """
+        Recieve a list of data and store them ans well as a caption of
+        the fit page where they come from.
+        :param uid: if related to a fit page
+        :param data_list: list of data to fit
+        :param caption: caption of the window related to these data
+        """
+        if data_list is None:
+            data_list = []
+        
+        self.page_finder[uid].set_fit_data(data=data_list)
+        if caption is not None:
+            self.page_finder[uid].set_fit_tab_caption(caption=caption)
+            
+    def on_add_new_page(self, event=None):
+        """
+        ask fit panel to create a new empty page
+        """
+        try:
+            page = self.fit_panel.add_empty_page()
+            # add data associated to the page created
+            if page != None:
+                wx.PostEvent(self.parent, StatusEvent(status="Page Created",
+                                               info="info"))
+            else:
+                msg = "Page was already Created"
+                wx.PostEvent(self.parent, StatusEvent(status=msg,
+                                                       info="warning"))
+        except:
+            msg = "Creating Fit page: %s"%sys.exc_value
+            wx.PostEvent(self.parent, StatusEvent(status=msg, info="error"))
+        
+    def add_fit_page(self, data):
+        """
+        given a data, ask to the fitting panel to create a new fitting page,
+        get this page and store it into the page_finder of this plug-in
+        :param data: is a list of data
+        """
+        page = self.fit_panel.set_data(data)
+        # page could be None when loading state files
+        if page == None:
+            return page
+        #append Data1D to the panel containing its theory
+        #if theory already plotted
+        if page.uid in self.page_finder:
+            data = page.get_data()
+            theory_data = self.page_finder[page.uid].get_theory_data(data.id)
+            if issubclass(data.__class__, Data2D):
+                data.group_id = wx.NewId()
+                if theory_data is not None:
+                    group_id = str(page.uid) + " Model1D"
+                    wx.PostEvent(self.parent,
+                             NewPlotEvent(group_id=group_id,
+                                               action="delete"))
+                    self.parent.update_data(prev_data=theory_data,
+                                             new_data=data)
+            else:
+                if theory_data is not None:
+                    group_id = str(page.uid) + " Model2D"
+                    data.group_id = theory_data.group_id
+                    wx.PostEvent(self.parent, 
+                             NewPlotEvent(group_id=group_id,
+                                               action="delete"))
+                    self.parent.update_data(prev_data=theory_data,
+                                             new_data=data)
+        self.store_data(uid=page.uid, data_list=page.get_data_list(),
+                        caption=page.window_caption)
+        if self.sim_page is not None and not self.batch_on:
+            self.sim_page.draw_page()
+        if self.batch_page is not None and self.batch_on:
+            self.batch_page.draw_page()
+            
+        return page
+            
+    def _onEVT_SLICER_PANEL(self, event):
+        """
+        receive and event telling to update a panel with a name starting with
+        event.panel_name. this method update slicer panel
+        for a given interactor.
+        
+        :param event: contains type of slicer , paramaters for updating
+            the panel and panel_name to find the slicer 's panel concerned.
+        """
+        event.panel_name
+        for item in self.parent.panels:
+            name = event.panel_name
+            if self.parent.panels[item].window_caption.startswith(name):
+                self.parent.panels[item].set_slicer(event.type, event.params)
+                
+        #self.parent._mgr.Update()
+   
+    def _closed_fitpage(self, event):
+        """
+        request fitpanel to close a given page when its unique data is removed
+        from the plot. close fitpage only when the a loaded data is removed
+        """
+        if event is None or event.data is None:
+            return
+        if hasattr(event.data, "is_data"):
+            if not event.data.is_data or \
+                event.data.__class__.__name__ == "Data1D":
+                self.fit_panel.close_page_with_data(event.data)
+  
+    def _reset_schedule_problem(self, value=0, uid=None):
+        """
+        unschedule or schedule all fitproblem to be fit
+        """
+        # case that uid is not specified
+        if uid == None:
+            for page_id in self.page_finder.keys():
+                self.page_finder[page_id].schedule_tofit(value)
+        # when uid is given
+        else:
+            if uid in self.page_finder.keys():
+                self.page_finder[uid].schedule_tofit(value)
+                
+    def _add_problem_to_fit(self, fitproblem, pars, fitter, fit_id):
+        """
+        Create and set fit engine with series of data and model
+        :param pars: list of fittable parameters
+        :param fitter_list: list of fit engine
+        :param value:  structure storing data mapped to their model, range etc.
+        """
+        data = fitproblem.get_fit_data()
+        model = fitproblem.get_model()
+        smearer = fitproblem.get_smearer()
+        qmin, qmax = fitproblem.get_range()
+
+        #Extra list of parameters and their constraints
+        listOfConstraint = []
+        param = fitproblem.get_model_param()
+        if len(param) > 0:
+            for item in param:
+                ## check if constraint
+                if item[0] != None and item[1] != None:
+                    listOfConstraint.append((item[0], item[1]))
+        new_model = model
+        fitter.set_model(new_model, fit_id, pars, data=data,
+                         constraints=listOfConstraint)
+        fitter.set_data(data=data, id=fit_id, smearer=smearer, qmin=qmin,
+                        qmax=qmax)
+        fitter.select_problem_for_fit(id=fit_id, value=1)
+       
+    def _onSelect(self, event):
+        """
+        when Select data to fit a new page is created .Its reference is
+        added to self.page_finder
+        """
+        panel = self.plot_panel
+        if panel == None:
+            raise ValueError, "Fitting:_onSelect: NonType panel"
+        Plugin.on_perspective(self, event=event)
+        self.select_data(panel)
+        
+    def select_data(self, panel):
+        """
+        """
+        for plottable in panel.graph.plottables:
+            if plottable.__class__.__name__ in ["Data1D", "Theory1D"]:
+                data_id = panel.graph.selected_plottable
+                if plottable == panel.plots[data_id]:
+                    data = plottable
+                    self.add_fit_page(data=[data])
+                    return
+            else:
+                data = plottable
+                self.add_fit_page(data=[data])
+            
+    def update_fit(self, result=None, msg=""):
+        """
+        """
+        print "update_fit result", result
+        
+    def _batch_fit_complete(self, result, pars, page_id,
+                            batch_outputs, batch_inputs, elapsed=None):
+        """
+        Display fit result in batch
+        :param result: list of objects received fromt fit engines
+        :param pars: list of  fitted parameters names
+        :param page_id: list of page ids which called fit function
+        :param elapsed: time spent at the fitting level
+        """
+        self._mac_sleep(0.2)
+        uid = page_id[0]
+        if uid in self.fit_thread_list.keys():
+            del self.fit_thread_list[uid]
+          
+        self._update_fit_button(page_id)
+        t1 = time.time()
+        str_time = time.strftime("%a, %d %b %Y %H:%M:%S ", time.localtime(t1))
+        msg = "Fit completed on %s \n" % str_time
+        msg += "Duration time: %s s.\n" % str(elapsed)
+        wx.PostEvent(self.parent, StatusEvent(status=msg, info="info",
+                                              type="stop"))
+       
+        if batch_outputs is None:
+            batch_outputs = {}
+        
+        # format batch_outputs
+        batch_outputs["Chi2"] = []
+        #Don't like these loops
+        # Need to create dictionary of all fitted parameters
+        # since the number of parameters can differ between each fit result
+        for list_res in result:
+            for res in list_res:
+                model, data = res.inputs[0]
+                if model is not None and hasattr(model, "model"):
+                    model = model.model
+                #get all fittable parameters of the current model
+                for param in  model.getParamList():
+                    if param  not in batch_outputs.keys():
+                        batch_outputs[param] = []
+                for param in model.getDispParamList():
+                    if not model.is_fittable(param) and \
+                        param in batch_outputs.keys():
+                        del batch_outputs[param]
+                # Add fitted parameters and their error
+                for param in res.param_list:
+                    if param not in batch_outputs.keys():
+                        batch_outputs[param] = []
+                    err_param = "error on %s" % str(param)
+                    if err_param not in batch_inputs.keys():
+                        batch_inputs[err_param] = []
+        msg = ""
+        for list_res in result:
+            for res in list_res:
+                pid = res.fitter_id
+                model, data = res.inputs[0]
+                correct_result = False
+                if model is not None and hasattr(model, "model"):
+                    model = model.model
+                if data is not None and hasattr(data, "sans_data"):
+                    data = data.sans_data
+                
+                is_data2d = issubclass(data.__class__, Data2D)
+                #check consistency of arrays
+                if not is_data2d:
+                    if len(res.theory) == len(res.index[res.index]) and \
+                        len(res.index) == len(data.y):
+                        correct_result = True
+                else:
+                    copy_data = deepcopy(data)
+                    new_theory = copy_data.data
+                    new_theory[res.index] = res.theory
+                    new_theory[res.index == False] = numpy.nan
+                    correct_result = True
+                #get all fittable parameters of the current model
+                param_list = model.getParamList()
+                for param in model.getDispParamList():
+                    if not model.is_fittable(param) and \
+                        param in param_list:
+                        param_list.remove(param)
+                if not correct_result or res.fitness is None or \
+                    not numpy.isfinite(res.fitness) or \
+                    numpy.any(res.pvec == None) or not \
+                    numpy.all(numpy.isfinite(res.pvec)):
+                    data_name = str(None)
+                    if data is not None:
+                        data_name = str(data.name)
+                    model_name = str(None)
+                    if model is not None:
+                        model_name = str(model.name)
+                    msg += "Data %s and Model %s did not fit.\n" % (data_name,
+                                                                    model_name)
+                    ERROR = numpy.NAN
+                    cell = BatchCell()
+                    cell.label = res.fitness
+                    cell.value = res.fitness
+                    batch_outputs["Chi2"].append(ERROR)
+                    for param in param_list:
+                        # save value of  fixed parameters
+                        if param not in res.param_list:
+                            batch_outputs[str(param)].append(ERROR)
+                        else:
+                            #save only fitted values
+                            batch_outputs[param].append(ERROR)
+                            batch_inputs["error on %s" % str(param)].append(ERROR)
+                else:
+                    # ToDo: Why sometimes res.pvec comes with numpy.float64?
+                    # Need to fix it within ScipyEngine
+                    if res.pvec.__class__ == numpy.float64:
+                        res.pvec = [res.pvec]
+                        
+                    cell = BatchCell()
+                    cell.label = res.fitness
+                    cell.value = res.fitness
+                    batch_outputs["Chi2"].append(cell)
+                    # add parameters to batch_results
+                    for param in param_list:
+                        # save value of  fixed parameters
+                        if param not in res.param_list:
+                            batch_outputs[str(param)].append(model.getParam(param))
+                        else:
+                            index = res.param_list.index(param)
+                            #save only fitted values
+                            batch_outputs[param].append(res.pvec[index])
+                            if res.stderr is not None and \
+                                len(res.stderr) == len(res.param_list):
+                                item = res.stderr[index]
+                                batch_inputs["error on %s" % param].append(item)
+                            else:
+                                batch_inputs["error on %s" % param].append('-')
+                            model.setParam(param, res.pvec[index])
+                #fill the batch result with emtpy value if not in the current
+                #model
+                EMPTY = "-"
+                for key in batch_outputs.keys():
+                    if key not in param_list and key not in ["Chi2", "Data"]:
+                        batch_outputs[key].append(EMPTY)
+                                
+                self.page_finder[pid].set_batch_result(batch_inputs=batch_inputs,
+                                                       batch_outputs=batch_outputs)
+               
+                cpage = self.fit_panel.get_page_by_id(pid)
+                cpage._on_fit_complete()
+                self.page_finder[pid][data.id].set_result(res)
+                fitproblem = self.page_finder[pid][data.id]
+                qmin, qmax = fitproblem.get_range()
+                plot_result = False
+                if correct_result:
+                    if not is_data2d:
+                        self._complete1D(x=data.x[res.index], y=res.theory, page_id=pid,
+                                         elapsed=None,
+                                         index=res.index, model=model,
+                                         weight=None, fid=data.id,
+                                         toggle_mode_on=False, state=None,
+                                         data=data, update_chisqr=False,
+                                         source='fit', plot_result=plot_result)
+                    else:
+                        self._complete2D(image=new_theory, data=data,
+                                         model=model,
+                                         page_id=pid, elapsed=None,
+                                         index=res.index,
+                                         qmin=qmin,
+                                         qmax=qmax, fid=data.id, weight=None,
+                                         toggle_mode_on=False, state=None,
+                                         update_chisqr=False,
+                                         source='fit', plot_result=plot_result)
+                self.on_set_batch_result(page_id=pid,
+                                         fid=data.id,
+                                         batch_outputs=batch_outputs,
+                                         batch_inputs=batch_inputs)
+        
+        wx.PostEvent(self.parent, StatusEvent(status=msg, error="info",
+                                              type="stop"))
+        # Remove parameters that are not shown
+        cpage = self.fit_panel.get_page_by_id(uid)
+        tbatch_outputs = {}
+        shownkeystr = cpage.get_copy_params()
+        for key in batch_outputs.keys():
+            if key in ["Chi2", "Data"] or shownkeystr.count(key) > 0:
+                tbatch_outputs[key] = batch_outputs[key]
+                
+        wx.CallAfter(self.parent.on_set_batch_result, tbatch_outputs,
+                     batch_inputs, self.sub_menu)
+        
+    def on_set_batch_result(self, page_id, fid, batch_outputs, batch_inputs):
+        """
+        """
+        pid = page_id
+        if fid not in self.page_finder[pid]:
+            return
+        fitproblem = self.page_finder[pid][fid]
+        index = self.page_finder[pid].nbr_residuals_computed - 1
+        residuals = fitproblem.get_residuals()
+        theory_data = fitproblem.get_theory_data()
+        data = fitproblem.get_fit_data()
+        model = fitproblem.get_model()
+        #fill batch result information
+        if "Data" not in batch_outputs.keys():
+            batch_outputs["Data"] = []
+        from sas.guiframe.data_processor import BatchCell
+        cell = BatchCell()
+        cell.label = data.name
+        cell.value = index
+        
+        if theory_data != None:
+            #Suucessful fit
+            theory_data.id = wx.NewId()
+            theory_data.name = model.name + "[%s]" % str(data.name)
+            if issubclass(theory_data.__class__, Data2D):
+                group_id = wx.NewId()
+                theory_data.group_id = group_id
+                if group_id not in theory_data.list_group_id:
+                    theory_data.list_group_id.append(group_id)
+                
+            try:
+                # associate residuals plot
+                if issubclass(residuals.__class__, Data2D):
+                    group_id = wx.NewId()
+                    residuals.group_id = group_id
+                    if group_id not in residuals.list_group_id:
+                        residuals.list_group_id.append(group_id)
+                batch_outputs["Chi2"][index].object = [residuals]
+            except:
+                pass
+
+        cell.object = [data, theory_data]
+        batch_outputs["Data"].append(cell)
+        for key, value in data.meta_data.iteritems():
+            if key not in batch_inputs.keys():
+                batch_inputs[key] = []
+            #if key.lower().strip() != "loader":
+            batch_inputs[key].append(value)
+        param = "temperature"
+        if hasattr(data.sample, param):
+            if param not in  batch_inputs.keys():
+                batch_inputs[param] = []
+            batch_inputs[param].append(data.sample.temperature)
+        
+    def _fit_completed(self, result, page_id, batch_outputs,
+                       batch_inputs=None, pars=None, elapsed=None):
+        """
+        Display result of the fit on related panel(s).
+        :param result: list of object generated when fit ends
+        :param pars: list of names of parameters fitted
+        :param page_id: list of page ids which called fit function
+        :param elapsed: time spent at the fitting level
+        """
+        t1 = time.time()
+        str_time = time.strftime("%a, %d %b %Y %H:%M:%S ", time.localtime(t1))
+        msg = "Fit completed on %s \n" % str_time
+        msg += "Duration time: %s s.\n" % str(elapsed)
+        wx.PostEvent(self.parent, StatusEvent(status=msg, info="info",
+                                                      type="stop"))
+        wx.PostEvent(self.result_panel, PlotResultEvent(result=result))
+        # reset fit_engine if changed by simul_fit
+        if self._fit_engine != self._gui_engine:
+            self._on_change_engine(self._gui_engine)
+        self._update_fit_button(page_id)
+        result = result[0]
+        self.fit_thread_list = {}
+        if page_id is None:
+            page_id = []
+        ## fit more than 1 model at the same time
+        self._mac_sleep(0.2)
+        try:
+            index = 0
+            for uid in page_id:
+                res = result[index]
+                if res.fitness is None or \
+                    not numpy.isfinite(res.fitness) or \
+                    numpy.any(res.pvec == None) or \
+                    not numpy.all(numpy.isfinite(res.pvec)):
+                    msg = "Fitting did not converge!!!"
+                    wx.PostEvent(self.parent,
+                             StatusEvent(status=msg,
+                                         info="warning",
+                                         type="stop"))
+                    self._update_fit_button(page_id)
+                else:
+                    #set the panel when fit result are float not list
+                    if res.pvec.__class__ == numpy.float64:
+                        pvec = [res.pvec]
+                    else:
+                        pvec = res.pvec
+                    if res.stderr.__class__ == numpy.float64:
+                        stderr = [res.stderr]
+                    else:
+                        stderr = res.stderr
+                    cpage = self.fit_panel.get_page_by_id(uid)
+                    # Make sure we got all results
+                    #(CallAfter is important to MAC)
+                    try:
+                        #if res != None:
+                        wx.CallAfter(cpage.onsetValues, res.fitness,
+                                     res.param_list,
+                                     pvec, stderr)
+                        index += 1
+                        wx.CallAfter(cpage._on_fit_complete)
+                    except KeyboardInterrupt:
+                        msg = "Singular point: Fitting Stoped."
+                        wx.PostEvent(self.parent, StatusEvent(status=msg,
+                                                              info="info",
+                                                              type="stop"))
+                    except:
+                        msg = "Singular point: Fitting Error occurred."
+                        wx.PostEvent(self.parent, StatusEvent(status=msg,
+                                                              info="error",
+                                                              type="stop"))
+                    
+        except:
+            msg = "Fit completed but Following"
+            msg += " warning occurred: %s" % sys.exc_value
+            wx.PostEvent(self.parent, StatusEvent(status=msg, info="warning",
+                                                  type="stop"))
+           
+    def _update_fit_button(self, page_id):
+        """
+        Update Fit button when fit stopped
+        
+        : parameter page_id: fitpage where the button is
+        """
+        if page_id.__class__.__name__ != 'list':
+            page_id = [page_id]
+        for uid in page_id:
+            page = self.fit_panel.get_page_by_id(uid)
+            page._on_fit_complete()
+        
+    def _on_show_panel(self, event):
+        """
+        """
+        pass
+    
+    def on_reset_batch_flag(self, event):
+        """
+        Set batch_reset_flag
+        """
+        event.Skip()
+        if self.menu1 == None:
+            return
+        menu_item = self.menu1.FindItemById(self.id_reset_flag)
+        flag = menu_item.IsChecked()
+        if not flag:
+            menu_item.Check(False)
+            self.batch_reset_flag = True
+        else:
+            menu_item.Check(True)
+            self.batch_reset_flag = False
+        
+        ## post a message to status bar
+        msg = "Set Chain Fitting: %s" % str(not self.batch_reset_flag)
+        wx.PostEvent(self.parent,
+                     StatusEvent(status=msg))
+
+    def _onset_engine_park(self, event):
+        """ 
+        set engine to park
+        """
+        self._on_change_engine('park')
+       
+    def _onset_engine_scipy(self, event):
+        """ 
+        set engine to scipy
+        """
+        self._on_change_engine('scipy')
+       
+    def _onset_engine_bumps(self, event):
+        """ 
+        set engine to bumps
+        """
+        self._on_change_engine('bumps')
+       
+    def _on_slicer_event(self, event):
+        """
+        Receive a panel as event and send it to guiframe
+        
+        :param event: event containing a panel
+        
+        """
+        if event.panel is not None:
+            new_panel = event.panel
+            self.slicer_panels.append(event.panel)
+            # Set group ID if available
+            event_id = self.parent.popup_panel(event.panel)
+            event.panel.uid = event_id
+            self.mypanels.append(event.panel)
+       
+    def _onclearslicer(self, event):
+        """
+        Clear the boxslicer when close the panel associate with this slicer
+        """
+        name = event.GetEventObject().frame.GetTitle()
+        print "name", name
+        for panel in self.slicer_panels:
+            if panel.window_caption == name:
+                
+                for item in self.parent.panels:
+                    if hasattr(self.parent.panels[item], "uid"):
+                        if self.parent.panels[item].uid == panel.base.uid:
+                            self.parent.panels[item].onClearSlicer(event)
+                            #self.parent._mgr.Update()
+                            break
+                break
+    
+    def _on_change_engine(self, engine='park'):
+        """
+        Allow to select the type of engine to perform fit
+        
+        :param engine: the key work of the engine
+        
+        """
+        ## saving fit engine name
+        self._fit_engine = engine
+        ## change menu item state
+        if engine == "park":
+            self.menu1.FindItemById(self.park_id).Check(True)
+            self.menu1.FindItemById(self.scipy_id).Check(False)
+            self.menu1.FindItemById(self.bumps_id).Check(False)
+        elif engine == "scipy":
+            self.menu1.FindItemById(self.park_id).Check(False)
+            self.menu1.FindItemById(self.scipy_id).Check(True)
+            self.menu1.FindItemById(self.bumps_id).Check(False)
+        else:
+            self.menu1.FindItemById(self.park_id).Check(False)
+            self.menu1.FindItemById(self.scipy_id).Check(False)
+            self.menu1.FindItemById(self.bumps_id).Check(True)
+        ## post a message to status bar
+        msg = "Engine set to: %s" % self._fit_engine
+        wx.PostEvent(self.parent,
+                     StatusEvent(status=msg))
+        ## send the current engine type to fitpanel
+        self.fit_panel._on_engine_change(name=self._fit_engine)
+
+    def _on_model_panel(self, evt):
+        """
+        react to model selection on any combo box or model menu.plot the model  
+        
+        :param evt: wx.combobox event
+        
+        """
+        model = evt.model
+        uid = evt.uid
+        qmin = evt.qmin
+        qmax = evt.qmax
+        caption = evt.caption
+        enable_smearer = evt.enable_smearer
+        if model == None:
+            return
+        if uid not in self.page_finder.keys():
+            return
+        # save the name containing the data name with the appropriate model
+        self.page_finder[uid].set_model(model)
+        self.page_finder[uid].enable_smearing(enable_smearer)
+        self.page_finder[uid].set_range(qmin=qmin, qmax=qmax)
+        self.page_finder[uid].set_fit_tab_caption(caption=caption)
+        if self.sim_page is not None and not self.batch_on:
+            self.sim_page.draw_page()
+        if self.batch_page is not None and self.batch_on:
+            self.batch_page.draw_page()
+        
+    def _update1D(self, x, output):
+        """
+        Update the output of plotting model 1D
+        """
+        msg = "Plot updating ... "
+        wx.PostEvent(self.parent, StatusEvent(status=msg, type="update"))
+        
+    def _complete1D(self, x, y, page_id, elapsed, index, model,
+                    weight=None, fid=None,
+                    toggle_mode_on=False, state=None,
+                    data=None, update_chisqr=True,
+                    source='model', plot_result=True):
+        """
+        Complete plotting 1D data
+        """
+        try:
+            numpy.nan_to_num(y)
+            
+            new_plot = Data1D(x=x, y=y)
+            new_plot.is_data = False
+            new_plot.dy = numpy.zeros(len(y))
+            new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
+            _yaxis, _yunit = data.get_yaxis()
+            _xaxis, _xunit = data.get_xaxis()
+            new_plot.title = data.name
+
+            new_plot.group_id = data.group_id
+            if new_plot.group_id == None:
+                new_plot.group_id = data.group_id
+            new_plot.id = str(page_id) + " " + data.name
+            #if new_plot.id in self.color_dict:
+            #    new_plot.custom_color = self.color_dict[new_plot.id]
+            #find if this theory was already plotted and replace that plot given
+            #the same id
+            theory_data = self.page_finder[page_id].get_theory_data(fid=data.id)
+            
+            if data.is_data:
+                data_name = str(data.name)
+            else:
+                data_name = str(model.__class__.__name__)
+            
+            new_plot.name = model.name + " [" + data_name +"]"
+            new_plot.xaxis(_xaxis, _xunit)
+            new_plot.yaxis(_yaxis, _yunit)
+            self.page_finder[page_id].set_theory_data(data=new_plot,
+                                                      fid=data.id)
+            self.parent.update_theory(data_id=data.id, theory=new_plot,
+                                       state=state)
+            current_pg = self.fit_panel.get_page_by_id(page_id)
+            title = new_plot.title
+            batch_on = self.fit_panel.get_page_by_id(page_id).batch_on
+            if not batch_on:
+                wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
+                                            title=str(title)))
+            elif plot_result:
+                top_data_id = self.fit_panel.get_page_by_id(page_id).data.id
+                if data.id == top_data_id:
+                    wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
+                                            title=str(title)))
+            caption = current_pg.window_caption
+            self.page_finder[page_id].set_fit_tab_caption(caption=caption)
+           
+            self.page_finder[page_id].set_theory_data(data=new_plot,
+                                                      fid=data.id)
+            if toggle_mode_on:
+                wx.PostEvent(self.parent,
+                             NewPlotEvent(group_id=str(page_id) + " Model2D",
+                                          action="Hide"))
+            else:
+                if update_chisqr:
+                    wx.PostEvent(current_pg,
+                                 Chi2UpdateEvent(output=self._cal_chisqr(
+                                                                data=data,
+                                                                fid=fid,
+                                                                weight=weight,
+                                                            page_id=page_id,
+                                                            index=index)))
+                else:
+                    self._plot_residuals(page_id=page_id, data=data, fid=fid,
+                                         index=index, weight=weight)
+
+            msg = "Computation  completed!"
+            wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
+        except:
+            raise
+   
+    def _update2D(self, output, time=None):
+        """
+        Update the output of plotting model
+        """
+        wx.PostEvent(self.parent, StatusEvent(status="Plot \
+        #updating ... ", type="update"))
+        #self.ready_fit()
+  
+    def _complete2D(self, image, data, model, page_id, elapsed, index, qmin,
+                qmax, fid=None, weight=None, toggle_mode_on=False, state=None,
+                     update_chisqr=True, source='model', plot_result=True):
+        """
+        Complete get the result of modelthread and create model 2D
+        that can be plot.
+        """
+        numpy.nan_to_num(image)
+        new_plot = Data2D(image=image, err_image=data.err_data)
+        new_plot.name = model.name + '2d'
+        new_plot.title = "Analytical model 2D "
+        new_plot.id = str(page_id) + " " + data.name
+        new_plot.group_id = str(page_id) + " Model2D"
+        new_plot.detector = data.detector
+        new_plot.source = data.source
+        new_plot.is_data = False
+        new_plot.qx_data = data.qx_data
+        new_plot.qy_data = data.qy_data
+        new_plot.q_data = data.q_data
+        new_plot.mask = data.mask
+        ## plot boundaries
+        new_plot.ymin = data.ymin
+        new_plot.ymax = data.ymax
+        new_plot.xmin = data.xmin
+        new_plot.xmax = data.xmax
+        title = data.title
+        
+        new_plot.is_data = False
+        if data.is_data:
+            data_name = str(data.name)
+        else:
+            data_name = str(model.__class__.__name__) + '2d'
+
+        if len(title) > 1:
+            new_plot.title = "Model2D for %s "% model.name + data_name
+        new_plot.name = model.name + " [" + \
+                                    data_name + "]"
+        theory_data = deepcopy(new_plot)
+        
+        self.page_finder[page_id].set_theory_data(data=theory_data,
+                                                  fid=data.id)
+        self.parent.update_theory(data_id=data.id,
+                                       theory=new_plot,
+                                       state=state)
+        current_pg = self.fit_panel.get_page_by_id(page_id)
+        title = new_plot.title
+        if not source == 'fit' and plot_result:
+            wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
+                                               title=title))
+        if toggle_mode_on:
+            wx.PostEvent(self.parent,
+                             NewPlotEvent(group_id=str(page_id) + " Model1D",
+                                               action="Hide"))
+        else:
+            # Chisqr in fitpage
+            if update_chisqr:
+                wx.PostEvent(current_pg,
+                             Chi2UpdateEvent(output=self._cal_chisqr(data=data,
+                                                                    weight=weight,
+                                                                    fid=fid,
+                                                         page_id=page_id,
+                                                         index=index)))
+            else:
+                self._plot_residuals(page_id=page_id, data=data, fid=fid,
+                                      index=index, weight=weight)
+        msg = "Computation  completed!"
+        wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
+    
+    def _draw_model2D(self, model, page_id, qmin,
+                      qmax,
+                      data=None, smearer=None,
+                      description=None, enable2D=False,
+                      state=None,
+                      fid=None,
+                      weight=None,
+                      toggle_mode_on=False,
+                       update_chisqr=True, source='model'):
+        """
+        draw model in 2D
+        
+        :param model: instance of the model to draw
+        :param description: the description of the model
+        :param enable2D: when True allows to draw model 2D
+        :param qmin: the minimum value to  draw model 2D
+        :param qmax: the maximum value to draw model 2D
+        :param qstep: the number of division of Qx and Qy of the model to draw
+            
+        """
+        if not enable2D:
+            return None
+        try:
+            from model_thread import Calc2D
+            ## If a thread is already started, stop it
+            if (self.calc_2D is not None) and self.calc_2D.isrunning():
+                self.calc_2D.stop()
+                ## stop just raises a flag to tell the thread to kill
+                ## itself -- see the fix in Calc1D implemented to fix
+                ## an actual problem.  Seems the fix should also go here
+                ## and may be the cause of other noted instabilities
+                ##
+                ##    -PDB August 12, 2014 
+                while self.calc_2D.isrunning():
+                    time.sleep(0.1)
+            self.calc_2D = Calc2D(model=model,
+                                    data=data,
+                                    page_id=page_id,
+                                    smearer=smearer,
+                                    qmin=qmin,
+                                    qmax=qmax,
+                                    weight=weight, 
+                                    fid=fid,
+                                    toggle_mode_on=toggle_mode_on,
+                                    state=state,
+                                    completefn=self._complete2D,
+                                    update_chisqr=update_chisqr, source=source)
+            self.calc_2D.queue()
+        except:
+            raise
+
+    def _draw_model1D(self, model, page_id, data,
+                      qmin, qmax, smearer=None,
+                state=None,
+                weight=None,
+                fid=None,
+                toggle_mode_on=False, update_chisqr=True, source='model',
+                enable1D=True):
+        """
+        Draw model 1D from loaded data1D
+        
+        :param data: loaded data
+        :param model: the model to plot
+        
+        """
+        if not enable1D:
+            return
+        try:
+            from model_thread import Calc1D
+            ## If a thread is already started, stop it
+            if (self.calc_1D is not None) and self.calc_1D.isrunning():
+                self.calc_1D.stop()
+                ## stop just raises the flag -- the thread is supposed to 
+                ## then kill itself but cannot.  Paul Kienzle came up with
+                ## this fix to prevent threads from stepping on each other
+                ## which was causing a simple custom model to crash Sasview.
+                ## We still don't know why the fit sometimes lauched a second
+                ## thread -- something which should also be investigated.
+                ## The thread approach was implemented in order to be able
+                ## to lauch a computation in a separate thread from the GUI so
+                ## that the GUI can still respond to user input including
+                ## a request to stop the computation.
+                ## It seems thus that the whole thread approach used here
+                ## May need rethinking  
+                ##
+                ##    -PDB August 12, 2014                  
+                while self.calc_1D.isrunning():
+                    time.sleep(0.1)
+            self.calc_1D = Calc1D(data=data,
+                                  model=model,
+                                  page_id=page_id, 
+                                  qmin=qmin,
+                                  qmax=qmax,
+                                  smearer=smearer,
+                                  state=state,
+                                  weight=weight,
+                                  fid=fid,
+                                  toggle_mode_on=toggle_mode_on,
+                                  completefn=self._complete1D,
+                                  #updatefn = self._update1D,
+                                  update_chisqr=update_chisqr,
+                                  source=source)
+            self.calc_1D.queue()
+        except:
+            msg = " Error occurred when drawing %s Model 1D: " % model.name
+            msg += " %s" % sys.exc_value
+            wx.PostEvent(self.parent, StatusEvent(status=msg))
+    
+    def _cal_chisqr(self, page_id, data, weight, fid=None, index=None):
+        """
+        Get handy Chisqr using the output from draw1D and 2D, 
+        instead of calling expansive CalcChisqr in guithread
+        """
+        try:
+            data_copy = deepcopy(data)
+        except:
+            return
+        # default chisqr
+        chisqr = None
+        #to compute chisq make sure data has valid data
+        # return None if data == None
+        if not check_data_validity(data_copy) or data_copy == None:
+            return chisqr
+
+        # Get data: data I, theory I, and data dI in order
+        if data_copy.__class__.__name__ == "Data2D":
+            if index == None:
+                index = numpy.ones(len(data_copy.data), dtype=bool)
+            if weight != None:
+                data_copy.err_data = weight
+            # get rid of zero error points
+            index = index & (data_copy.err_data != 0)
+            index = index & (numpy.isfinite(data_copy.data))
+            fn = data_copy.data[index]
+            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
+            if theory_data == None:
+                return chisqr
+            gn = theory_data.data[index]
+            en = data_copy.err_data[index]
+        else:
+            # 1 d theory from model_thread is only in the range of index
+            if index == None: 
+                index = numpy.ones(len(data_copy.y), dtype=bool)
+            if weight != None:
+                data_copy.dy = weight
+            if data_copy.dy == None or data_copy.dy == []:
+                dy = numpy.ones(len(data_copy.y))
+            else:
+                ## Set consitently w/AbstractFitengine:
+                # But this should be corrected later.
+                dy = deepcopy(data_copy.dy)
+                dy[dy == 0] = 1
+            fn = data_copy.y[index]
+            
+            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
+            if theory_data == None:
+                return chisqr
+            gn = theory_data.y
+            en = dy[index]
+        
+        # residual
+        try:
+            res = (fn - gn) / en
+        except ValueError:
+            print "Unmatch lengths %s, %s, %s"% (len(fn), len(gn), len(en))
+            return
+        
+        residuals = res[numpy.isfinite(res)]
+        # get chisqr only w/finite
+        chisqr = numpy.average(residuals * residuals)
+        
+        self._plot_residuals(page_id=page_id, data=data_copy,
+                             fid=fid,
+                             weight=weight, index=index)
+        
+        return chisqr
+    
+    def _plot_residuals(self, page_id, weight, fid=None,
+                        data=None, index=None):
+        """
+        Plot the residuals
+        
+        :param data: data
+        :param index: index array (bool)
+        : Note: this is different from the residuals in cal_chisqr()
+        """
+        data_copy = deepcopy(data)
+        # Get data: data I, theory I, and data dI in order
+        if data_copy.__class__.__name__ == "Data2D":
+            # build residuals
+            residuals = Data2D()
+            #residuals.copy_from_datainfo(data)
+            # Not for trunk the line below, instead use the line above
+            data_copy.clone_without_data(len(data_copy.data), residuals)
+            residuals.data = None
+            fn = data_copy.data
+            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
+            gn = theory_data.data
+            if weight == None:
+                en = data_copy.err_data
+            else:
+                en = weight
+            residuals.data = (fn - gn) / en
+            residuals.qx_data = data_copy.qx_data
+            residuals.qy_data = data_copy.qy_data
+            residuals.q_data = data_copy.q_data
+            residuals.err_data = numpy.ones(len(residuals.data))
+            residuals.xmin = min(residuals.qx_data)
+            residuals.xmax = max(residuals.qx_data)
+            residuals.ymin = min(residuals.qy_data)
+            residuals.ymax = max(residuals.qy_data)
+            residuals.q_data = data_copy.q_data
+            residuals.mask = data_copy.mask
+            residuals.scale = 'linear'
+            # check the lengths
+            if len(residuals.data) != len(residuals.q_data):
+                return
+        else:
+            # 1 d theory from model_thread is only in the range of index
+            if data_copy.dy == None or data_copy.dy == []:
+                dy = numpy.ones(len(data_copy.y))
+            else:
+                if weight == None:
+                    dy = numpy.ones(len(data_copy.y))
+                ## Set consitently w/AbstractFitengine:
+                ## But this should be corrected later.
+                else:
+                    dy = weight
+                dy[dy == 0] = 1
+            fn = data_copy.y[index]
+            theory_data = self.page_finder[page_id].get_theory_data(fid=data_copy.id)
+            gn = theory_data.y
+            en = dy[index]
+            # build residuals
+            residuals = Data1D()
+            try:
+                residuals.y = (fn - gn) / en
+            except:
+                msg = "ResidualPlot Error: different # of data points in theory"
+                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error"))
+                residuals.y = (fn - gn[index]) / en
+            residuals.x = data_copy.x[index]
+            residuals.dy = numpy.ones(len(residuals.y))
+            residuals.dx = None
+            residuals.dxl = None
+            residuals.dxw = None
+            residuals.ytransform = 'y'
+            # For latter scale changes 
+            residuals.xaxis('\\rm{Q} ', 'A^{-1}')
+            residuals.yaxis('\\rm{Residuals} ', 'normalized')
+        theory_name = str(theory_data.name.split()[0])
+        new_plot = residuals
+        new_plot.name = "Residuals for " + str(theory_name) + "[" +\
+                        str(data.name) +"]"
+        ## allow to highlight data when plotted
+        new_plot.interactive = True
+        ## when 2 data have the same id override the 1 st plotted
+        new_plot.id = "res" + str(data_copy.id) + str(theory_name)
+        ##group_id specify on which panel to plot this data
+        group_id = self.page_finder[page_id].get_graph_id()
+        if group_id == None:
+            group_id = data.group_id
+        new_plot.group_id = "res" + str(group_id)
+        #new_plot.is_data = True
+        ##post data to plot
+        title = new_plot.name
+        self.page_finder[page_id].set_residuals(residuals=new_plot,
+                                                fid=data.id)
+        self.parent.update_theory(data_id=data.id, theory=new_plot)
+        batch_on = self.fit_panel.get_page_by_id(page_id).batch_on
+        if not batch_on:
+            wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title=title))
Index: src/sas/perspectives/fitting/fitting_widgets.py
===================================================================
--- src/sas/perspectives/fitting/fitting_widgets.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/fitting/fitting_widgets.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,188 @@
+
+################################################################################
+#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 2009, University of Tennessee
+################################################################################
+import wx
+from wx.lib.scrolledpanel import ScrolledPanel
+
+MAX_NBR_DATA = 4
+WIDTH = 430
+HEIGHT = 350
+
+
+class DialogPanel(ScrolledPanel):
+    def __init__(self, *args, **kwds):
+        ScrolledPanel.__init__(self, *args, **kwds)
+        self.SetupScrolling()
+        
+        
+class BatchDataDialog(wx.Dialog):
+    """
+    The current design of Batch  fit allows only of type of data in the data
+    set. This allows the user to make a quick selection of the type of data
+    to use in fit tab.
+    """
+    def __init__(self, parent=None, *args, **kwds):
+        wx.Dialog.__init__(self, parent, *args, **kwds)
+        self.SetSize((WIDTH, HEIGHT))
+        self.data_1d_selected = None
+        self.data_2d_selected = None
+        self._do_layout()
+   
+    def _do_layout(self):
+        """
+        Draw the content of the current dialog window
+        """
+        vbox = wx.BoxSizer(wx.VERTICAL)
+        box_description = wx.StaticBox(self, -1, str("Hint"))
+        hint_sizer = wx.StaticBoxSizer(box_description, wx.VERTICAL)
+        selection_sizer = wx.GridBagSizer(5,5)
+        button_sizer = wx.BoxSizer(wx.HORIZONTAL)
+        self.data_1d_selected = wx.RadioButton(self, -1, 'Data1D',
+                                                style=wx.RB_GROUP)
+        self.data_2d_selected = wx.RadioButton(self, -1, 'Data2D')
+        self.data_1d_selected.SetValue(True)
+        self.data_2d_selected.SetValue(False)
+        button_cancel = wx.Button(self, wx.ID_CANCEL, "Cancel")
+        button_OK = wx.Button(self, wx.ID_OK, "Ok")
+        button_OK.SetFocus()
+        hint = "Selected Data set contains both 1D and 2D Data.\n"
+        hint += "Please select on type of analysis before proceeding.\n"
+        hint_sizer.Add(wx.StaticText(self, -1, hint))
+        #draw area containing radio buttons
+        ix = 0
+        iy = 0
+        selection_sizer.Add(self.data_1d_selected, (iy, ix),
+                           (1, 1), wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
+        iy += 1
+        selection_sizer.Add(self.data_2d_selected, (iy, ix),
+                           (1, 1), wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
+        #contruction the sizer contaning button
+        button_sizer.Add((20, 20), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+        button_sizer.Add(button_cancel, 0,
+                         wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 10)
+        button_sizer.Add(button_OK, 0,
+                                wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 10)
+        vbox.Add(hint_sizer, 0, wx.EXPAND|wx.ALL, 10)
+        vbox.Add(selection_sizer, 0, wx.TOP|wx.BOTTOM, 10)
+        vbox.Add(wx.StaticLine(self, -1),  0, wx.EXPAND, 0)
+        vbox.Add(button_sizer, 0, wx.TOP|wx.BOTTOM, 10)
+        self.SetSizer(vbox)
+        self.Layout()
+        
+    def get_data(self):
+        """
+        return 1 if  user requested Data1D , 2 if user requested Data2D
+        """
+        if self.data_1d_selected.GetValue():
+            return 1
+        else:
+            return 2
+
+
+class DataDialog(wx.Dialog):
+    """
+    Allow file selection at loading time
+    """
+    def __init__(self, data_list, parent=None, text='',
+                 nb_data=MAX_NBR_DATA, *args, **kwds):
+        wx.Dialog.__init__(self, parent, *args, **kwds)
+        self.SetTitle("Data Selection")
+        self._max_data = nb_data
+        self._nb_selected_data = nb_data
+        
+        self.SetSize((WIDTH, HEIGHT))
+        self.list_of_ctrl = []
+        if not data_list:
+            return
+        select_data_text = " %s Data selected.\n" % str(self._nb_selected_data)
+        self._data_text_ctrl = wx.StaticText(self, -1, str(select_data_text))
+                               
+        self._data_text_ctrl.SetForegroundColour('blue')
+        self._sizer_main = wx.BoxSizer(wx.VERTICAL)
+        self._sizer_txt = wx.BoxSizer(wx.VERTICAL)
+        self._sizer_button = wx.BoxSizer(wx.HORIZONTAL)
+        self._choice_sizer = wx.GridBagSizer(5, 5)
+        self._panel = DialogPanel(self, style=wx.RAISED_BORDER,
+                               size=(WIDTH-20, HEIGHT/3))
+        self.__do_layout(data_list, text=text)
+        
+    def __do_layout(self, data_list, text=''):
+        """
+        layout the dialog
+        """
+        if not data_list or len(data_list) <= 1:
+            return
+        #add text
+        if text.strip() == "":
+            text = "Fitting: We recommend that you selected"
+            text += " no more than '%s' data\n" % str(self._max_data)
+            text += "for adequate plot display size. \n"
+            text += "unchecked data won't be send to fitting . \n"
+        text_ctrl = wx.StaticText(self, -1, str(text))
+        self._sizer_txt.Add(text_ctrl)
+        iy = 0
+        ix = 0
+        data_count = 0
+        for i in range(len(data_list)):
+            data_count += 1
+            cb = wx.CheckBox(self._panel, -1, str(data_list[i].name), (10, 10))
+            wx.EVT_CHECKBOX(self, cb.GetId(), self._count_selected_data)
+            if data_count <= MAX_NBR_DATA:
+                cb.SetValue(True)
+            else:
+                cb.SetValue(False)
+            self.list_of_ctrl.append((cb, data_list[i]))
+            self._choice_sizer.Add(cb, (iy, ix),
+                           (1, 1), wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
+            iy += 1
+        self._panel.SetSizer(self._choice_sizer)
+        #add sizer
+        self._sizer_button.Add((20, 20), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+        button_cancel = wx.Button(self, wx.ID_CANCEL, "Cancel")
+        self._sizer_button.Add(button_cancel, 0,
+                          wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 10)
+        button_OK = wx.Button(self, wx.ID_OK, "Ok")
+        button_OK.SetFocus()
+        self._sizer_button.Add(button_OK, 0,
+                                wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 10)
+        static_line = wx.StaticLine(self, -1)
+        self._sizer_txt.Add(self._panel, 0, wx.EXPAND|wx.ALL, 10)
+        self._sizer_main.Add(self._sizer_txt, 0, wx.EXPAND|wx.ALL, 10)
+        self._sizer_main.Add(self._data_text_ctrl, 0,  wx.EXPAND|wx.ALL, 10)
+        self._sizer_main.Add(static_line, 0, wx.EXPAND, 0)
+        self._sizer_main.Add(self._sizer_button, 0, wx.EXPAND|wx.ALL, 10)
+        self.SetSizer(self._sizer_main)
+        self.Layout()
+        
+    def get_data(self):
+        """
+        return the selected data
+        """
+        temp = []
+        for item in self.list_of_ctrl:
+            cb, data = item
+            if cb.GetValue():
+                temp.append(data)
+        return temp
+    
+    def _count_selected_data(self, event):
+        """
+        count selected data
+        """
+        if event.GetEventObject().GetValue():
+            self._nb_selected_data += 1
+        else:
+            self._nb_selected_data -= 1
+        select_data_text = " %s Data selected.\n" % str(self._nb_selected_data)
+        self._data_text_ctrl.SetLabel(select_data_text)
+        if self._nb_selected_data <= self._max_data:
+            self._data_text_ctrl.SetForegroundColour('blue')
+        else:
+            self._data_text_ctrl.SetForegroundColour('red')
Index: src/sas/perspectives/fitting/ftol_dialog.py
===================================================================
--- src/sas/perspectives/fitting/ftol_dialog.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/fitting/ftol_dialog.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,104 @@
+"""
+Dialog to set ftol for Scipy
+
+    ftol(float): Relative error desired in the sum of squares.
+"""
+################################################################################
+#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 2009, University of Tennessee
+################################################################################
+import wx
+import sys
+# default ftol
+F_TOL = 1.49012e-08
+SANS_F_TOL = 5e-05
+
+PANEL_WIDTH = 270
+FONT_VARIANT = 0
+if sys.platform.count("win32") > 0:
+    PANEL_HEIGHT = 265
+else:
+    PANEL_HEIGHT = 245
+    
+class ChangeFtol(wx.Dialog):
+    """
+    Dialog to select ftol
+    """
+    def __init__(self, parent, base, id=-1, title="FTolerance"):
+        wx.Dialog.__init__(self, parent, id, title,
+                           size=(PANEL_WIDTH, PANEL_HEIGHT))
+        # font size
+        self.SetWindowVariant(variant=FONT_VARIANT)
+        # build layout
+        vbox = wx.BoxSizer(wx.VERTICAL)
+        title_text = wx.StaticText(self, id=wx.NewId(), label='FTol selection (Leastsq)')
+        self.default_bt = wx.RadioButton(self, -1, 'SansView Default (5e-05)', (15, 30), style=wx.RB_GROUP)
+        self.default_bt.SetValue(True)
+        self.default_bt.Bind(wx.EVT_RADIOBUTTON, self.OnFtolSelection)
+        self.sci_default_bt = wx.RadioButton(self, -1, 'Scipy Default (1.49012e-08)', (15, 55))
+        self.sci_default_bt.SetValue(False)
+        self.sci_default_bt.Bind(wx.EVT_RADIOBUTTON, self.OnFtolSelection)
+        self.high_bt = wx.RadioButton(self, -1, '1e-06', (15, 80))
+        self.high_bt.SetValue(False)
+        self.high_bt.Bind(wx.EVT_RADIOBUTTON, self.OnFtolSelection)
+        self.mid_bt = wx.RadioButton(self, -1, '1e-05', (15, 105))
+        self.mid_bt.SetValue(False)
+        self.mid_bt.Bind(wx.EVT_RADIOBUTTON, self.OnFtolSelection)
+        self.low_bt = wx.RadioButton(self, -1, '1e-04', (15, 130))
+        self.low_bt.SetValue(False)
+        self.low_bt.Bind(wx.EVT_RADIOBUTTON, self.OnFtolSelection)
+        self.custom_bt = wx.RadioButton(self, -1, 'Custom', (15, 155))
+        self.custom_bt.SetValue(False)
+        self.custom_bt.Bind(wx.EVT_RADIOBUTTON, self.OnFtolSelection)
+        self.custombox = wx.TextCtrl(self, -1, '', (95, 155))
+        self.custombox.Disable()
+        
+        hbox = wx.BoxSizer(wx.HORIZONTAL)
+        okButton = wx.Button(self, wx.ID_OK, 'Set', size=(70, 30))
+        closeButton = wx.Button(self,wx.ID_CANCEL, 'Cancel', size=(70, 30))
+        hbox.Add(okButton, 1, wx.RIGHT, 5)
+        hbox.Add(closeButton, 1, wx.RIGHT, 5)
+        
+        vbox.Add(title_text, 0, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 10)
+        vbox.Add(self.default_bt, 0, wx.LEFT, 20)
+        vbox.Add(self.sci_default_bt, 0, wx.LEFT, 20)
+        vbox.Add(self.high_bt, 0, wx.LEFT, 20)
+        vbox.Add(self.mid_bt, 0, wx.LEFT, 20)
+        vbox.Add(self.low_bt, 0, wx.LEFT, 20)
+        vbox.Add(self.custom_bt, 0, wx.LEFT, 20)
+        vbox.Add(self.custombox, 0, wx.LEFT, 60)
+        vbox.Add(hbox, 0, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 10)
+        
+        self.SetSizer(vbox)
+
+    def OnFtolSelection(self, event=None):
+        """
+           Changes the ftol on selection of the radio button
+        """
+        self.custombox.Enable(self.custom_bt.GetValue())
+        
+    def get_ftol(self):
+        """
+            Get the ftol value
+        """
+        if self.default_bt.GetValue():
+            return SANS_F_TOL
+        elif self.sci_default_bt.GetValue():
+            return F_TOL
+        elif self.low_bt.GetValue():
+            return 1.0e-4
+        elif self.mid_bt.GetValue():
+            return 1.0e-5
+        elif self.high_bt.GetValue():
+            return 1.0e-6
+        if self.custom_bt.GetValue():
+            try:
+                return float(self.custombox.GetValue())
+            except:
+                return None
+        return SANS_F_TOL
Index: src/sas/perspectives/fitting/help_panel.py
===================================================================
--- src/sas/perspectives/fitting/help_panel.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/fitting/help_panel.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,159 @@
+"""
+    Help dialog
+"""
+import wx
+import wx.html as html
+from wx.lib.splitter import MultiSplitterWindow
+import os
+from wx.lib.scrolledpanel import ScrolledPanel
+
+class HelpPanel(ScrolledPanel):
+    def __init__(self, parent, **kwargs):
+        ScrolledPanel.__init__(self, parent, **kwargs)
+        self.SetupScrolling()
+
+
+class HelpWindow(wx.Frame):
+    """
+    """
+    def __init__(self, parent, id, title='Fitting Help', pageToOpen=None, size=(850, 540)):
+        wx.Frame.__init__(self, parent, id, title, size=size)
+        """
+        contains help info
+        """
+        self.Show(False)
+        self.SetTitle(title) 
+        from sas.perspectives.fitting import get_data_path as fit_path
+        fitting_path = fit_path(media='media')
+        ico_file = os.path.join(fitting_path, 'ball.ico')
+        if os.path.isfile(ico_file):
+            self.SetIcon(wx.Icon(ico_file, wx.BITMAP_TYPE_ICO))
+        splitter = MultiSplitterWindow(self, style=wx.SP_LIVE_UPDATE)
+        self.rpanel = wx.Panel(splitter, -1)
+        self.lpanel = wx.Panel(splitter, -1, style=wx.BORDER_SUNKEN)
+        
+        vbox = wx.BoxSizer(wx.VERTICAL)
+        header = wx.Panel(self.rpanel, -1)
+        header.SetBackgroundColour('#6666FF')
+        header.SetForegroundColour('WHITE')
+        hbox = wx.BoxSizer(wx.HORIZONTAL)
+        st = wx.StaticText(header, -1, 'Contents', (5, 5))
+        font = st.GetFont()
+        font.SetPointSize(10)
+        st.SetFont(font)
+        hbox.Add(st, 1, wx.TOP | wx.BOTTOM | wx.LEFT, 5)
+        header.SetSizer(hbox)
+        vbox.Add(header, 0, wx.EXPAND)
+       
+        vboxl= wx.BoxSizer(wx.VERTICAL)
+        headerl = wx.Panel(self.lpanel, -1, size=(-1, 20))
+       
+        headerl.SetBackgroundColour('#6666FF')
+        headerl.SetForegroundColour('WHITE')
+        hboxl = wx.BoxSizer(wx.HORIZONTAL)
+        lst = wx.StaticText(headerl, -1, 'Menu', (5, 5))
+        fontl = lst.GetFont()
+        fontl.SetPointSize(10)
+        lst.SetFont(fontl)
+        hboxl.Add(lst, 1, wx.TOP | wx.BOTTOM | wx.LEFT, 5)
+        headerl.SetSizer(hboxl)
+        vboxl.Add(headerl, 0, wx.EXPAND)
+        self.lhelp = html.HtmlWindow(self.lpanel, -1, style=wx.NO_BORDER)
+        self.rhelp = html.HtmlWindow(self.rpanel, -1, style=wx.NO_BORDER, 
+                                     size=(500, -1))
+
+        # get the media path
+        if pageToOpen != None:
+            path = os.path.dirname(pageToOpen)
+        else:
+            from sas.models import get_data_path as model_path
+            # Get models help model_function path
+            path = model_path(media='media')
+
+        self.path = os.path.join(path, "model_functions.html")
+        self.path_pd = os.path.join(path, "pd_help.html")
+        self.path_sm = os.path.join(path, "smear_computation.html")
+        self.path_mag = os.path.join(path, "polar_mag_help.html")
+        
+        _html_file = [("load_data_help.html", "Load a File"),
+                      ("single_fit_help.html", "Single Fit"),
+                      ("simultaneous_fit_help.html", "Simultaneous Fit"),
+                      ("batch_help.html", "Batch Fit"),
+                      ("model_use_help.html", "Model Selection"),
+                      ("category_manager_help.html", "Model Category Manager"),
+                      ("%s" % self.path, "Model Functions"),
+                      ("model_editor_help.html", "Custom Model Editor"),
+                      ("%s" % self.path_pd, "Polydispersion Distributions"),
+                      ("%s" % self.path_sm, "Smear Computation"),
+                      ("%s" % self.path_mag, "Polarization/Magnetic Scattering"),
+                      ("key_help.html", "Key Combination"),
+                      ("status_bar_help.html", "Status Bar Help"),
+                      ]
+ 
+        page1 = """<html>
+            <body>
+             <p>Select topic on Menu</p>
+            </body>
+            </html>"""
+            
+        page = """<html>
+            <body>
+            <ul>
+            """
+            
+        for (p, title) in _html_file:
+            pp = os.path.join(fitting_path, p)
+            page += """<li><a href ="%s" target="showframe">%s</a><br></li>""" % (pp, title)
+        page += """</ul>
+                    </body>
+                    </html>
+                """
+                
+        self.rhelp.SetPage(page1)
+        self.lhelp.SetPage(page)
+        self.lhelp.Bind(wx.html.EVT_HTML_LINK_CLICKED, self.OnLinkClicked )
+        
+        #open the help frame a the current page
+        if  pageToOpen != None:
+            self.rhelp.LoadPage(str(pageToOpen))
+            
+        vbox.Add(self.rhelp, 1, wx.EXPAND)
+        vboxl.Add(self.lhelp, 1, wx.EXPAND)
+        self.rpanel.SetSizer(vbox)
+        self.lpanel.SetSizer(vboxl)
+        self.lpanel.SetFocus()
+        
+        vbox1 = wx.BoxSizer(wx.HORIZONTAL)
+        vbox1.Add(splitter, 1, wx.EXPAND)
+        splitter.AppendWindow(self.lpanel, 200)
+        splitter.AppendWindow(self.rpanel)
+        self.SetSizer(vbox1)
+
+        self.splitter = splitter
+        self.Centre()
+        self.Bind(wx.EVT_SIZE, self.on_Size)
+        
+    def OnButtonClicked(self, event):
+        """
+        Function to diplay Model html page related to the hyperlinktext selected
+        """
+        self.rhelp.LoadPage(self.path)
+        
+    def OnLinkClicked(self, event):
+        """
+        Function to diplay html page related to the hyperlinktext selected
+        """
+        link = event.GetLinkInfo().GetHref()
+        
+        self.rhelp.LoadPage(os.path.abspath(link))
+        
+    def on_Size(self, event):
+        """
+        Recover the scroll position On Size
+        """
+        pos = self.rhelp.GetScrollPos(wx.VERTICAL)
+        size = self.GetClientSize()
+        self.splitter.SetSize(size)
+        self.rhelp.Show(False)
+        self.rhelp.ScrollLines(pos)
+        self.rhelp.Show(True)
Index: src/sas/perspectives/fitting/hint_fitpage.py
===================================================================
--- src/sas/perspectives/fitting/hint_fitpage.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/fitting/hint_fitpage.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,55 @@
+"""
+    This class provide general structure of  fitpanel page
+"""
+import wx
+from sas.guiframe.panel_base import PanelBase
+
+class HintFitPage(wx.ScrolledWindow, PanelBase):
+    """
+        This class provide general structure of  fitpanel page
+    """
+    ## Internal name for the AUI manager
+    window_name = "Hint Page"
+    ## Title to appear on top of the window
+    window_caption = "Hint page "
+    
+    def __init__(self, parent):
+        wx.ScrolledWindow.__init__(self, parent,
+                 style = wx.FULL_REPAINT_ON_RESIZE)
+        PanelBase.__init__(self, parent)
+        msg = "right click on the data when it is highlighted "
+        msg += "the select option to fit for futher options"
+        self.do_layout()
+        
+    def do_layout(self):
+        """
+            Draw the page
+        """
+        name = "Hint"
+        box_description= wx.StaticBox(self, -1, name)
+        boxsizer = wx.StaticBoxSizer(box_description, wx.VERTICAL)
+        msg = "    How to link data to the control panel: \n \n"
+        msg += "    First load data file from 'File' menu. \n"
+        msg += "    Then Highlight and right click on the data plot. \n"
+        msg += "    Finally, select 'Select data for fitting' in the pop-up menu. \n"
+        self.hint_txt = wx.StaticText(self, -1, msg, style=wx.ALIGN_LEFT)
+        boxsizer.Add(self.hint_txt, wx.ALL|wx.EXPAND, 20)
+        self.vbox = wx.BoxSizer(wx.VERTICAL)
+        self.vbox.Add(boxsizer)
+        self.vbox.Layout()
+        self.vbox.Fit(self)
+        self.SetSizer(self.vbox)
+        self.SetScrollbars(20, 20, 25, 65)
+        self.Layout()
+        
+    def createMemento(self):
+        return
+        
+        
+class HelpWindow(wx.Frame):
+    def __init__(self, parent, id, title):
+        wx.Frame.__init__(self, parent, id, title, size=(570, 400))
+       
+        self.page = HintFitPage(self)
+        self.Centre()
+        self.Show(True)
Index: src/sas/perspectives/fitting/media/batch_help.html
===================================================================
--- src/sas/perspectives/fitting/media/batch_help.html	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/fitting/media/batch_help.html	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,177 @@
+<html>
+<head>
+</head>
+
+
+<body>
+<h4> Batch Fit </h4>
+
+<ul>
+<li><a href="#fitpage">Batch Fit</a></li>
+<li><a href="#batchwindow">Batch Window</a></li>
+</ul>
+
+<h5><font color="blue"><a name="fitpage">Batch Fit</a></font></h5>
+<p>
+Create <b>Batch Page </b> by selecting <b>Batch </b> radio button on 
+Data Explorer<font color="blue"><a href="#batch_button">(see figure below)</a></font>
+and for a new control page select 'New FitPage' in the 'Fitting' menubar.<br />
+</p>
+<br /><img src="batch_button_area.bmp" /><br /><br />
+<a name="figure1"><u>Figure 1: MenuBar</u></a><br />
+<br />
+Load Data to Data Explorer if not loaded. <br/>
+Select one or more data sets by checking the check boxes, and then make sure 
+that "Fitting" is selected in the dropdown menu next to the "Send To" button.
+Once ready, click the 'Send To' button to set data to a BatchPage. If already an empty batch page exists, it will be set there.
+Otherwise it will create a new Batch Page.
+Set up the model and the parameter values as same as a single fitting <font color="blue"><a href="single_fit_help.html">(see single fit help)</a></font>.
+The use 'Fit' button to perform the fitting.
+<br />Unlike a single fit, the results of the fittings will not return to the BatchPage'. Instead, a Grid window will be provided once the fitting is completed. 
+The Grid window is also accessible from the 'View' menu <font color="blue"><a href="#figure2">(see Figure 2)</a></font>. 
+<br /> Note that only one model is used for all the data. 
+The initial parameter values given in the control page will be used all the data fittings. If one wants the FitEngine to use the initial values 
+from the results of the previous data fitting (if any), choose the 'Chain Fitting' in the Fitting menubar, which will speed up the fitting 
+especially when you have lots of, and similar, data sets.
+</p>
+
+<h5><font color="blue"><a name="batchwindow">Batch Window</a></font></h5>
+<p>
+Batch Window provides an easy way to view the fit results, i.e., plot data, fits, and residuals.
+Batch window will be automatically shown after a batch fit is finished. <br />
+Once closed, 
+it can be opened anytime from the "View" menubar item <font color="blue"><a href="#figure2">(see Figure 2)</a></font>.<br />
+</p>
+<br /><br /><img src="restore_batch_window.bmp" /><br /><br />
+<a name="#figure2"><u>Figure 2: Edit Menu</u></a><br />
+<br />
+  <ul>
+	<li> <a href="#edit_grid"> Edit Grid </a></li>
+	<li><a href="#save_grid"> Save Grid </a></li>
+	<li><a href="#load_batch"> Open Batch Results </a></li>
+	<li><a href="#plot"> Plot </a></li>
+	<li><a href="#view"> View Column/Cell(s) </a></li>
+   </ul>
+
+
+
+<h5><font color="blue"><a name="edit_grid"> Edit Grid </a></font></h5>
+<p>
+Once a batch fit is completed, all fitted and fixed model parameters are displayed to the current sheet of
+the batch window except the errors of the parameters. To view the errors, 
+click on a given column then under <b> Edit </b> menubar item, and insert the desired parameter by selecting 
+a menu item with the appropriated label. Empty column can be inserted in the same way.
+A column value can be customized by editing an existing empty column.<br />
+To Remove column from the grid, select it, choose edit menu, and click the <b> 'remove' </b>  menu item.
+Any removed column should reinserted whenever needed.<br />
+All above options are also available when right clicking on a given column label 
+<font color="blue"><a href="#figure3">(see Figure 3)</a></font>.
+<b>Note:</b> A column always needs to be selected in order to remove or insert a column in the grid.<br /><br />
+
+<img src="edit_menu.bmp" /><br /><br />
+<a name="figure3"><u>Figure 3: Edit Menu</u></a><br />
+</p>
+
+
+<h5><font color="blue"><a name="save_grid"> Save Grid </a></font></h5>
+<p>
+To save the current page on the batch window, select the <b> 'File' </b> menubar item 
+<font color="blue"><a href="#figure4">(see Figure 4)</a></font>, then choose the <b> 'Save as' </b>
+menu item to save it as a .csv file. <br />
+<b>Note: </b> The grid doesn't save the data array, fits, and the array residuals. As a result, the 'View (fit) Results' functionality 
+will be lost  when reloading the saved file.<br />
+
+<font color="red">Warning! To ensure accuracy of saved fit results, It is recommended to save the current grid 
+before modifying it . </font>
+</p>
+
+
+<h5><font color="blue"><a name="load_batch"> Open Batch Results </a></font></h5>
+<p>
+Any <b> csv </b> file can be opened in the grid by selecting the <b> 'Open' </b> 
+under the <b> 'File' </b> menu in the Grid Window <font color="blue"><a href="#figure4">(see Figure 4)</a></font>. 
+All columns in the file will be displayed but insertion will not available. 
+Insertion will be available only when at least one column will be
+removed from the grid.
+
+<br /><img src="file_menu.bmp" /><br /><br />
+<a name="figure4"><u>Figure 4: MenuBar</u></a><br />
+</p>
+
+
+<h5><font color="blue"><a name="plot"> Plot </a></font></h5>
+<p>
+To <b> plot </b> a column versus another,
+select one column at the time, click the <b> 'Add' </b> button next to the text control
+ of X/Y -axis <b> Selection Range </b>
+to plot the value of this column on the X/Y axis. Alternatively, all available range can be selected by clicking the column letter (eg. B).
+Repeat the same procedure the next axis. Finally, click the <b> 'Plot' </b> button. 
+When clicking on <b> Add </b> button, the grid
+will automatically fill the axis label, but  different labels and units can be 
+entered  in the correct controls before clicking 
+on the plot button.<br />
+<b>X/Y -Axis Selection Range </b> can be edited manually. These text controls 
+allows the following types of expression:<br />
+
+operation can be <font color="#00CC33"><b>+</b></font>,<font color="#00CC33">
+<b> -</b></font>, <font color="#00CC33"><b>*</b></font>, 
+<font color="#00CC33"><b>/</b></font>, or <font color="#00CC33">pow</font>.<br /> <br />
+
+1) if the current axis label range is a function of 1 or more columns,
+ write this type of expression: <br />
+constant1  * <font color="#CC9933">column_name1</font>
+<font color="blue">[minimum row index :  maximum  row index]</font> 
+<font color="#00CC33">operator</font>  constant2 <font color="#00CC33">
+<b>*</b></font> <font color="#CC9933">column_name2</font>
+<font color="blue">[minimum row index :  maximum  row index]</font> <br />
+Example : <font color="#CC9933">radius</font>[2 : 5]<font color="#00CC33"><big><
+b> -</b></big></font> 3 <font color="#00CC33"><b>*</b></font>
+ <font color="#CC9933">scale</font>[2 : 5] <br /><br />
+
+2) if only some values of a given column are need but the range between 
+the first row and the last row used is not continuous,<br />write
+the following expression in the text control: <br />
+<font color="#CC9933">column_name1</font>
+<font color="blue">[minimum row index1 :  maximum  row index1]</font>
+<font color="#00CC33"><big><b> , </b></big></font> 
+<font color="#CC9933">column_name1</font>
+<font color="blue">[minimum row index2 :  maximum  row index2]</font> <br />
+Example : <font color="#CC9933">radius </font><font color="blue"> [2 : 5] </font>
+<font color="#00CC33"><big><b> , </b></big></font><font color="#CC9933">radius </font>
+ <font color="blue">[10 : 25] </font><br /><br />
+
+
+<b>Note: </b>Both text controls ( X and Y-axis Selection Ranges) need to be filled 
+with valid entries for plotting to work (the dY-bar is optional) 
+<font color="blue"><a href="#figure5">(see Figure 5)</a></font>.
+
+<br /><br /><img src="plot_button.bmp" /><br /><br />
+<a name="figure5"><u>Figure 5: Plotting</u></a><br />
+
+</p>
+
+
+<h5><font color="blue"><a name="view"> View Column/Cell(s) </a></font></h5>
+<p>
+Select 1 or more cells from the same column, click the <b> 'View Fits' </b>
+button  to display available curves. <br />
+
+For example, select the cells of the <b> 'Chi2' </b> column, then click the <b> 'View Fits' </b> 
+button. The plots generates will represent the <b>residuals </b> plots. <br />
+ 
+
+If you select any cells of the <b> 'Data' </b> column and click the
+<b> 'View Fits' </b> button. It generates both
+<b> data and fits</b> in the graph <font color="blue"><a href="#figure6">(see Figure 6)</a></font>. <br />
+
+<b>Alternatively, just click the column letter (eg. B) 
+to choose all the available Data sets, then simply click the 'View Fits' button to plot the data and fits. 
+</b>
+<br />
+<br /><br /><img src="view_button.bmp" /><br /><br />
+<a name="figure6"><u>Figure 6: View Fits</u></a><br /><br />
+</p>
+
+
+</body>
+</html>
Index: src/sas/perspectives/fitting/media/category_manager_help.html
===================================================================
--- src/sas/perspectives/fitting/media/category_manager_help.html	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/fitting/media/category_manager_help.html	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,42 @@
+<html>
+<body>
+<h4>Model Category Manager</h4>
+<ul>
+<li><a href="#description">Description</a></li>
+
+</ul>
+<h5><a name="description">Description</a></h5>
+<p>
+
+Our SANS models are, by default, classified into 5 categories; shapes, shape-independent, 
+structure factor, and customized models, where these categories (except the customized models) can be reassigned, added, and removed using 'Category Manager'.
+Each models can also be enabled(shown)/disabled(hidden) from the category that they
+belong.
+The Category Manager panel is accessible from the model category 'Modify' button in the fitting panel or the 'View/Category Manager' menu in the menu bar (Fig. 1).
+<br>
+1) Enable/Disable models: Check/uncheck the check boxes to enable/disable the models (Fig. 2).
+<br>
+2) Change category: Highlight a model in the list by left-clicking and click the 'Modify' button. In the 'Change Category' panel, 
+one can create/use a category for the model, then click the 'Add' button. In order to delete a category, select a category name and click the 'Remove Selected' 
+button (Fig. 3). 
+<br>
+3) To apply the changes made, hit the OK button. Otherwise, click the 'Cancel' button (Fig. 2).
+</p>
+<br>
+<img src="cat_fig0.bmp"/>
+<br>
+Fig.1
+<br>
+<br>
+<img src="cat_fig1.bmp"/>
+<br>
+Fig.2
+<br>
+<br>
+<img src="cat_fig2.bmp"/>
+<br>
+Fig.3
+<br>
+</body>
+
+</html>
Index: src/sas/perspectives/fitting/media/glossaryhelp.html
===================================================================
--- src/sas/perspectives/fitting/media/glossaryhelp.html	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/fitting/media/glossaryhelp.html	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,19 @@
+<html>
+
+<body bgcolor="#ffffff">
+<h4>Table of Contents</h4>
+
+<ul>
+<li><a href="load_data_help.html">Load a File</a></li>
+<li><a href="single_fit_help.html">Single fit</a></li>
+<li><a href="simultaneous_fit_help.html">Simultaneous fit</a></li>
+<li><a href="change_scale_help.html">Change scale</a></li>
+<li><a href="reset_Graph_help.html">Reset graph</a></li>
+<li><a href="save_data_help.html">Save data</a></li>
+<li><a href="model_use_help.html">visualize model</a></li>
+<li><a href="sld_calculator_help.html">SLD Calculator</a></li>
+<li><a href ="doc/averaging_help.html">Data Averaging</a><br></li>
+</ul>
+
+</body>
+</html>
Index: src/sas/perspectives/fitting/media/help.html
===================================================================
--- src/sas/perspectives/fitting/media/help.html	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/fitting/media/help.html	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,11 @@
+<html>
+
+<frameset cols="120,*">
+
+<frame src="model_use_help.html">
+<frame src="load_data_help.html" name="showframe">
+
+</frameset>
+
+</html>
+
Index: src/sas/perspectives/fitting/media/key_help.html
===================================================================
--- src/sas/perspectives/fitting/media/key_help.html	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/fitting/media/key_help.html	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,47 @@
+<html>
+<body>
+
+<h4>Key Combination</h4>
+<ul>
+<li><a href="#Copy">Copy & Paste</a></li>
+<li><a href="#Bookmark">Bookmark</a></li>
+<li><a href="#Ftol">FTolerance (Scipy)</a></li>
+</ul>
+<h5><a name="Copy">Copy & Paste</a></h5>
+<p>
+<h6>To copy the parameter values in a Fit(Model)panel to the clipboard:</h6></p>
+<p> <b>Ctrl(Cmd on MAC) + Left(Mouse)Click</b> on the panel.</p>
+<p>
+<h6>To paste the parameter values to a Fit(Model)panel from the clipboard:</h6></p>
+<p> <b>Ctrl(Cmd on MAC) + Shift + Left(Mouse)Click</b> on the panel.</p>
+<p> If this operation is successful, it will say so in the info line 
+at the bottom of the SV window.<p>
+<p>
+
+<p>
+<h5><a name="Bookmark">Bookmark</a></h5>
+<p>
+<h6>Bookmark of a fit-panel or model-panel status:</h6></p>
+<p> <b>(Mouse)Right-Click</b> and select the bookmark in the popup list.</p>
+
+<p>
+<h5><a name="GraphMenu">Graph Context Menu</a></h5>
+<p>
+<h6>To get the graph context menu to print, copy, save data, (2D)average, etc.:</h6></p>
+<p> <b>Locate the mouse point on the plot to highlight and 
+<b>(Mouse) Right Click</b> to bring up the full menu.</p>
+
+
+<h5><a name="Ftol">FTolerance (Scipy)</a></h5>
+<p>
+<h6>To change the ftol value of the Scipy FitEngine (leastsq):</h6></p>
+<p>First, make sure that the Fit panel has data and a model selected.
+<p> <b>Ctrl(Cmd on MAC) +  Shift + Alt + Right(Mouse)Click</b> on the panel.</p>
+<p>Then, set up the value in the dialog panel.</p>
+<p> If this operation is successful, the new ftol value will be displayed 
+in the info line at the bottom of the SV window.<p>
+<p> Note that increasing the ftol value may cause for the fitting to terminate 
+with higher chisqr.</p>
+</body>
+</html>
+
Index: src/sas/perspectives/fitting/media/load_data_help.html
===================================================================
--- src/sas/perspectives/fitting/media/load_data_help.html	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/fitting/media/load_data_help.html	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,13 @@
+<html>
+<body>
+
+<p>
+<h4>Loading Data File/Folder</h4>
+From Menu go to <b>Data</b> -><b>Load Data File(or Folder)</b>. Select a file/folder from the menu bar and click
+on Open button.
+Data contained in the file will be displayed. To cancel the loading click on <b>cancel</b>.
+In case a file can not be loaded, an error message will be displayed on the statusbar.
+</p>
+
+</body>
+</html>
Index: src/sas/perspectives/fitting/media/model_editor_help.html
===================================================================
--- src/sas/perspectives/fitting/media/model_editor_help.html	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/fitting/media/model_editor_help.html	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,58 @@
+<html>
+<body>
+<h4>Edit Custom Model</h4>
+<ul>
+<li><a href="#description">Description</a></li>
+<li><a href="#new">New</a></li>
+<li><a href="#sum">Sum|Multi(p1,p2)</a></li>
+<li><a href="#advanced">Advanced</a></li>
+<li><a href="#delete">Delete</a></li>
+
+</ul>
+<h5><a name="description">Description</a></h5>
+<p>
+This menu (Fitting/Edit Custom Model in the menu bar) interface is to provide you an easy way to write your own custom models.
+The changes in a model function are effective after it is re-selected from the combo-box menu. 
+</p>
+<br/>
+<img src="edit_model_menu.bmp"/>
+<br/>
+<br/>
+<h5><a name="new">New</a></h5>
+<p>
+This option is used to make a new model. 
+A model code generated by this option can be viewed and further modified by the 'Advanced' option below.
+</p>
+<br/>
+<img src="new_model.bmp"/>
+<br/>
+<br/>
+<h5><a name="sum">Sum|Multi(p1,p2)</a></h5>
+<p>
+This option create a new sum (or multiplication) model. Fill up the (sum model function) name and the description. 
+The description will show up on details button in the application.
+Then select the p1 or p2 model for the sum/multi model, 
+select an operator as necessary and click the Apply button for activation. 
+Hit the 'Close' button when it's done.
+</p>
+<br/>
+<img src="sum_model.bmp"/>
+<br/>
+<br/>
+<h5><a name="advanced">Advanced</a></h5>
+<p>
+The menu option shows all the files in the plugin_models folder. 
+You can edit, modify, and save it.
+It is recommended to modify only the lines with arrow (## &#60-----). 
+In the end of edit, 'Compile' and 'Run' from the menu bar to activate or to see the model working properly.
+</p>
+
+
+<h5><a name="delete">Delete</a></h5>
+<p>
+The menu option is to delete the custom models. Just select the file name to delete.
+</p>
+
+</body>
+
+</html>
Index: src/sas/perspectives/fitting/media/model_use_help.html
===================================================================
--- src/sas/perspectives/fitting/media/model_use_help.html	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/fitting/media/model_use_help.html	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,37 @@
+<html>
+<body>
+
+<h4>Model Selection</h4>
+<ul>
+<li><a href="#Model type">Model type</a></li>
+<li><a href="#Visualize">Change Model's parameters</a></li>
+<li><a href="#Plugin"> Write your own model</a></li>
+</ul>
+
+<h5><a name="Model types">Model type</a></h5>
+<p> Models are grouped into three classes:
+	<ul>
+	<li><b> Shapes</b>: Regular  geometric shapes</li>
+	<li><b> Shape- Independent</b></li>
+	<li><b> Customized Models</b> : models written by the user</li>
+	<li><b> Structure Factor</b> </li>
+	</ul>
+</p>
+
+<h5><a name="Visualize">Change Model's parameters</a></h5>
+<p>
+	To visualize model in a different window, from menu click on <b>Model</b>.Select
+	a type of model and then the name of your model.A new window will appear with the plot 
+	of your model with default values. Change model's parameters on <b> model view</b> tab
+	and view the plotted model with its new parameters. 
+</p>
+
+<h5><a name="Plugin"> Write your own model</a></h5>
+<p>The custom model editors are provided from 'Fitting' menu in the menu bar. 
+	See 'Custom model editor' in the side menu on left.
+	Advanced users can write your own model and save it (in .py format) into <b>plugin_models</b> directory
+	in .sansview of your home directory (eg., username\.sansview>\plugin_models). You plugin
+	model will be added into "<>Customized Models</>"  on the next model selection.
+</p>
+</body>
+</html>
Index: src/sas/perspectives/fitting/media/simultaneous_fit_help.html
===================================================================
--- src/sas/perspectives/fitting/media/simultaneous_fit_help.html	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/fitting/media/simultaneous_fit_help.html	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,38 @@
+<html>
+<body>
+
+<h4>Simultaneous Fit</h4>
+<p>
+	This fitting option enables to set a number of the constraints between the parameters of fitting(s).
+	It requires one or more FitPages with a data and a model set for the fitting, 
+	and performs multiple fittings given by the FitPage(s).
+	The Complex (ParkMC) FitEngine will be used automatically.
+</p>
+<ul>
+<li><a href="#noconstraint">Simultaneous Fit without constraint</a></li>
+<li><a href="#constraint">Simultaneous Fit with constraint</a></li>
+</ul>
+
+<h5><a name="noconstraint">Simultaneous Fit without constraint</a></h5>
+<p> 
+	Assuming some FitPages are already set up, check the checkboxes of the model_data rows to fit.
+	And click the 'Fit' button. 
+	The results will return to each FitPages. 
+	<br/>Note that the chi2/Npts returned is the sum of the chi2/Npts of each fits. 
+	If one needs the chi2 value only for a page, click the 'Compute' button in the FitPage to recalculate.
+	
+</p>
+
+<h5><a name="constraint">Simultaneous Fit with constraint</a></h5>
+<p>
+	Enter constraint in the text control next to  <b>constraint fit</b> button.
+	Constraint should be of type model1 parameter name = f(model2 parameter name)for example, M0.radius=2*M1.radius.
+	Many constraints can be entered for a single fit.Each of them should be separated 
+	by a newline charater or ";"
+	The easy setup can generate many constraint inputs easily when the selected two models are the same type.
+	<br/>Note that the chi2/Npts returned is the sum of the chi2/Npts of each fits. 
+	If one needs the chi2 value only for one fit, click the 'Compute' button in the FitPage to recalculate.
+</p>
+
+</body>
+</html>
Index: src/sas/perspectives/fitting/media/single_fit_help.html
===================================================================
--- src/sas/perspectives/fitting/media/single_fit_help.html	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/fitting/media/single_fit_help.html	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,27 @@
+<html>
+<body>
+
+<h4>Single Fit</h4>
+<ul>
+<li><a href="#fitpage">Single Fit</a></li>
+
+</ul>
+
+<h5><a name="fitpage">Single Fit</a></h5>
+<p> 
+	One of two fit-engines can be chosen from the Fitting menu bar. The Simple Fit-engine uses Scipy's leasqr 
+	and the Complex Fit-Engine is a custom optimizer that provides a better chance 
+	to find the global minimum of the chi2 but that requires longer computation time.
+	In order to set a data to a control panel (FitPage), see the "DataLoader Help".
+	Once a data set to the FiPage, select a model from the combo box. 
+	The default parameters of the model will be display. Set initial parameters if need.
+	Check and uncheck parameters to fit/fix. Click the <b> 'Fit'</b> button.
+	When the fitting is finished, the resultant parameter values will be displayed with the errors. If a error is missing, it generally means 
+	that the corresponding parameter is not very depending on the model. The chisq/Npt_fit and the plot associated
+	with the fit operation will be also updated.
+	
+</p>
+
+
+</body>
+</html>
Index: src/sas/perspectives/fitting/media/status_bar_help.html
===================================================================
--- src/sas/perspectives/fitting/media/status_bar_help.html	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/fitting/media/status_bar_help.html	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,25 @@
+<html>
+<body>
+
+<h4>Status Bar Help</h4>
+<ul>
+<li><a href="#message">Message, Warning, Hint</a></li>
+<li><a href="#console">Console</a></li>
+</ul>
+
+<h5><a name="message">Message, Warning, Hint</a></h5>
+<p> 
+The status bar located at the bottom of the application frame, displays messages,
+hints, warnings and errors.
+</p>
+
+<h5><a name="console">Console</a></h5>
+<p>
+Select <b> light bulb/info icon </b> button in the status bar at the bottom of the application window 
+to display available history.
+During a long task, the console can also help users to understand the status in progressing.
+</p>
+
+
+</body>
+</html>
Index: src/sas/perspectives/fitting/model_thread.py
===================================================================
--- src/sas/perspectives/fitting/model_thread.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/fitting/model_thread.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,238 @@
+"""
+    Calculation thread for modeling
+"""
+
+import time
+import numpy
+import math
+from sas.data_util.calcthread import CalcThread
+
+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
+                 ):
+        CalcThread.__init__(self, completefn, updatefn, yieldtime, worktime)
+        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)
+            # Get necessary data from self.data and set the data for smearing
+            fn.get_data()
+            # 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)
+        
+
+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
+                 ):
+        """
+        """
+        CalcThread.__init__(self, completefn,
+                 updatefn,
+                 yieldtime,
+                 worktime)
+        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)
+     
+        ##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]
+            output[first_bin:last_bin] = self.model.evalDistribution(mask)
+            output = self.smearer(output, first_bin, last_bin)
+        else:
+            output[index] = self.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)
+        
+    def results(self):
+        """
+        Send resuts of the computation
+        """
+        return [self.out, self.index]
+
+"""
+Example: ::
+                     
+    class CalcCommandline:
+        def __init__(self, n=20000):
+            #print thread.get_ident()
+            from sas.models.CylinderModel import CylinderModel
+            
+            model = CylinderModel()
+            
+             
+            print model.runXY([0.01, 0.02])
+            
+            qmax = 0.01
+            qstep = 0.0001
+            self.done = False
+            
+            x = numpy.arange(-qmax, qmax+qstep*0.01, qstep)
+            y = numpy.arange(-qmax, qmax+qstep*0.01, qstep)
+        
+        
+            calc_thread_2D = Calc2D(x, y, None, model.clone(),None,
+                                    -qmax, qmax,qstep,
+                                            completefn=self.complete,
+                                            updatefn=self.update ,
+                                            yieldtime=0.0)
+         
+            calc_thread_2D.queue()
+            calc_thread_2D.ready(2.5)
+            
+            while not self.done:
+                time.sleep(1)
+    
+        def update(self,output):
+            print "update"
+    
+        def complete(self, image, data, model, elapsed, qmin, qmax,index, qstep ):
+            print "complete"
+            self.done = True
+    
+    if __name__ == "__main__":
+        CalcCommandline()
+"""
Index: src/sas/perspectives/fitting/models.py
===================================================================
--- src/sas/perspectives/fitting/models.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/fitting/models.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,1048 @@
+"""
+    Utilities to manage models
+"""
+import wx
+import imp
+import os
+import sys
+import math
+import os.path
+# Time is needed by the log method
+import time
+import logging
+import py_compile
+import shutil
+from sas.guiframe.events import StatusEvent
+# Explicitly import from the pluginmodel module so that py2exe
+# places it in the distribution. The Model1DPlugin class is used
+# as the base class of plug-in models.
+from sas.models.pluginmodel import Model1DPlugin
+from sas.models.BaseComponent import BaseComponent
+from sas.guiframe.CategoryInstaller import CategoryInstaller
+   
+PLUGIN_DIR = 'plugin_models'
+
+def get_model_python_path():
+    return os.path.dirname(__file__)
+
+
+def log(message):
+    """
+        Log a message in a file located in the user's home directory
+    """
+    dir = os.path.join(os.path.expanduser("~"), '.sasview', PLUGIN_DIR)
+    out = open(os.path.join(dir, "plugins.log"), 'a')
+    out.write("%10g:  %s\n" % (time.clock(), message))
+    out.close()
+
+
+def _check_plugin(model, name):
+    """
+    Do some checking before model adding plugins in the list
+    
+    :param model: class model to add into the plugin list
+    :param name:name of the module plugin
+    
+    :return model: model if valid model or None if not valid
+    
+    """
+    #Check if the plugin is of type Model1DPlugin
+    if not issubclass(model, Model1DPlugin):
+        msg = "Plugin %s must be of type Model1DPlugin \n" % str(name)
+        log(msg)
+        return None
+    if model.__name__ != "Model":
+        msg = "Plugin %s class name must be Model \n" % str(name)
+        log(msg)
+        return None
+    try:
+        new_instance = model()
+    except:
+        msg = "Plugin %s error in __init__ \n\t: %s %s\n" % (str(name),
+                                    str(sys.exc_type), sys.exc_value)
+        log(msg)
+        return None
+   
+    if hasattr(new_instance, "function"):
+        try:
+            value = new_instance.function()
+        except:
+            msg = "Plugin %s: error writing function \n\t :%s %s\n " % (str(name),
+                                    str(sys.exc_type), sys.exc_value)
+            log(msg)
+            return None
+    else:
+        msg = "Plugin  %s needs a method called function \n" % str(name)
+        log(msg)
+        return None
+    return model
+  
+  
+def find_plugins_dir():
+    """
+        Find path of the plugins directory.
+        The plugin directory is located in the user's home directory.
+    """
+    dir = os.path.join(os.path.expanduser("~"), '.sasview', PLUGIN_DIR)
+    
+    # If the plugin directory doesn't exist, create it
+    if not os.path.isdir(dir):
+        os.makedirs(dir)
+        
+    # Find paths needed
+    try:
+        # For source
+        if os.path.isdir(os.path.dirname(__file__)):
+            p_dir = os.path.join(os.path.dirname(__file__), PLUGIN_DIR)
+        else:
+            raise
+    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
+        p_dir = None
+        f_dir = os.path.join(os.path.dirname(__file__))
+        for i in range(n_dir):
+            if i > 1:
+                f_dir, _ = os.path.split(f_dir)
+            plugin_path = os.path.join(f_dir, PLUGIN_DIR)
+            if os.path.isdir(plugin_path):
+                p_dir = plugin_path
+                break
+        if not p_dir:
+            raise
+    # Place example user models as needed
+    if os.path.isdir(p_dir):
+        for file in os.listdir(p_dir):
+            file_path = os.path.join(p_dir, file)
+            if os.path.isfile(file_path):
+                if file.split(".")[-1] == 'py' and\
+                    file.split(".")[0] != '__init__':
+                    if not os.path.isfile(os.path.join(dir, file)):
+                        shutil.copy(file_path, dir)
+
+    return dir
+
+
+class ReportProblem:
+    def __nonzero__(self):
+        type, value, traceback = sys.exc_info()
+        if type is not None and issubclass(type, py_compile.PyCompileError):
+            print "Problem with", repr(value)
+            raise type, value, traceback
+        return 1
+    
+report_problem = ReportProblem()
+
+
+def compile_file(dir):
+    """
+    Compile a py file
+    """
+    try:
+        import compileall
+        compileall.compile_dir(dir=dir, ddir=dir, force=1,
+                               quiet=report_problem)
+    except:
+        type, value, traceback = sys.exc_info()
+        return value
+    return None
+
+
+def _findModels(dir):
+    """
+    """
+    # List of plugin objects
+    plugins = {}
+    # Go through files in plug-in directory
+    #always recompile the folder plugin
+    dir = find_plugins_dir()
+    if not os.path.isdir(dir):
+        msg = "SansView couldn't locate Model plugin folder."
+        msg += """ "%s" does not exist""" % dir
+        logging.warning(msg)
+        return plugins
+    else:
+        log("looking for models in: %s" % str(dir))
+        compile_file(dir)
+        logging.info("pluging model dir: %s\n" % str(dir))
+    try:
+        list = os.listdir(dir)
+        for item in list:
+            toks = os.path.splitext(os.path.basename(item))
+            if toks[1] == '.py' and not toks[0] == '__init__':
+                name = toks[0]
+            
+                path = [os.path.abspath(dir)]
+                file = None
+                try:
+                    (file, path, info) = imp.find_module(name, path)
+                    module = imp.load_module(name, file, item, info)
+                    if hasattr(module, "Model"):
+                        try:
+                            if _check_plugin(module.Model, name) != None:
+                                plugins[name] = module.Model
+                        except:
+                            msg = "Error accessing Model"
+                            msg += "in %s\n  %s %s\n" % (name,
+                                    str(sys.exc_type), sys.exc_value)
+                            log(msg)
+                except:
+                    msg = "Error accessing Model"
+                    msg += " in %s\n  %s %s \n" % (name,
+                                    str(sys.exc_type), sys.exc_value)
+                    log(msg)
+                finally:
+              
+                    if not file == None:
+                        file.close()
+    except:
+        # Don't deal with bad plug-in imports. Just skip.
+        msg = "Could not import model plugin: %s\n" % sys.exc_value
+        log(msg)
+        pass
+    return plugins
+
+
+class ModelList(object):
+    """
+    Contains dictionary of model and their type
+    """
+    def __init__(self):
+        """
+        """
+        self.mydict = {}
+        
+    def set_list(self, name, mylist):
+        """
+        :param name: the type of the list
+        :param mylist: the list to add
+        
+        """
+        if name not in self.mydict.keys():
+            self.reset_list(name, mylist)
+            
+    def reset_list(self, name, mylist):
+        """
+        :param name: the type of the list
+        :param mylist: the list to add
+        """
+        self.mydict[name] = mylist
+            
+    def get_list(self):
+        """
+        return all the list stored in a dictionary object
+        """
+        return self.mydict
+        
+        
+class ModelManagerBase:
+    """
+        Base class for the model manager
+    """
+    ## external dict for models
+    model_combobox = ModelList()
+    ## Dictionary of form factor models
+    form_factor_dict = {}
+    ## dictionary of structure factor models
+    struct_factor_dict = {}
+    ##list of shape models -- this is superseded by categories 
+#    shape_list = []
+    ## shape independent model list-- this is superseded by categories
+#    shape_indep_list = []
+    ##list of structure factors
+    struct_list = []
+    ##list of model allowing multiplication by a structure factor
+    multiplication_factor = []
+    ##list of multifunctional shapes (i.e. that have user defined number of levels
+    multi_func_list = []
+    ## list of added models -- currently python models found in the plugin dir.
+    plugins = []
+    ## Event owner (guiframe)
+    event_owner = None
+    last_time_dir_modified = 0
+    
+    def __init__(self):
+        """
+        """
+        self.model_dictionary = {}
+        self.stored_plugins = {}
+        self._getModelList()
+        
+    def findModels(self):
+        """
+        find  plugin model in directory of plugin .recompile all file
+        in the directory if file were modified
+        """
+        temp = {}
+        if self.is_changed():
+            return  _findModels(dir)
+        logging.info("pluging model : %s\n" % str(temp))
+        return temp
+        
+    def _getModelList(self):
+        """
+        List of models we want to make available by default
+        for this application
+    
+        :return: the next free event ID following the new menu events
+        
+        """
+
+        ## NOTE: as of April 26, 2014, as part of first pass on fixing categories,
+        ## all the appends to shape_list or shape_independent_list are 
+        ## commented out.  They should be possible to remove.  They are in 
+        ## fact a "category" of model whereas the other list are actually 
+        ## "attributes" of a model.  In other words is it a structure factor
+        ## that can be used against a form factor, is it a form factor that is
+        ## knows how to be multiplied by a structure factor, does it have user
+        ## defined number of parameters, etc.
+        ##
+        ## We hope this whole list will be superseded by the new C models
+        ## structure where each model will provide a method to interrogate it
+        ## about its "attributes" -- then this long list becomes a loop reading
+        ## each model in the category list to populate the "attribute"lists.  
+        ## We should also refactor the whole category vs attribute list 
+        ## structure when doing this as now the attribute lists think they are
+        ## also category lists.
+        ##
+        ##   -PDB  April 26, 2014
+
+        # regular model names only
+        self.model_name_list = []
+        from sas.models.SphereModel import SphereModel
+        self.model_dictionary[SphereModel.__name__] = SphereModel
+#        self.shape_list.append(SphereModel)
+        self.multiplication_factor.append(SphereModel)
+        self.model_name_list.append(SphereModel.__name__)
+        
+        from sas.models.BinaryHSModel import BinaryHSModel
+        self.model_dictionary[BinaryHSModel.__name__] = BinaryHSModel
+#        self.shape_list.append(BinaryHSModel)
+        self.model_name_list.append(BinaryHSModel.__name__)
+                        
+        from sas.models.FuzzySphereModel import FuzzySphereModel
+        self.model_dictionary[FuzzySphereModel.__name__] = FuzzySphereModel
+#        self.shape_list.append(FuzzySphereModel)
+        self.multiplication_factor.append(FuzzySphereModel)
+        self.model_name_list.append(FuzzySphereModel.__name__)
+                        
+        from sas.models.RaspBerryModel import RaspBerryModel
+        self.model_dictionary[RaspBerryModel.__name__] = RaspBerryModel
+#        self.shape_list.append(RaspBerryModel)
+        self.model_name_list.append(RaspBerryModel.__name__)
+                
+        from sas.models.CoreShellModel import CoreShellModel
+        self.model_dictionary[CoreShellModel.__name__] = CoreShellModel
+#        self.shape_list.append(CoreShellModel)
+        self.multiplication_factor.append(CoreShellModel)
+        self.model_name_list.append(CoreShellModel.__name__)
+        
+        from sas.models.Core2ndMomentModel import Core2ndMomentModel
+        self.model_dictionary[Core2ndMomentModel.__name__] = Core2ndMomentModel
+#        self.shape_list.append(Core2ndMomentModel)
+        self.model_name_list.append(Core2ndMomentModel.__name__)
+        
+        from sas.models.CoreMultiShellModel import CoreMultiShellModel
+        self.model_dictionary[CoreMultiShellModel.__name__] = CoreMultiShellModel
+#        self.shape_list.append(CoreMultiShellModel)
+        self.multiplication_factor.append(CoreMultiShellModel)
+        self.multi_func_list.append(CoreMultiShellModel)
+
+        from sas.models.VesicleModel import VesicleModel
+        self.model_dictionary[VesicleModel.__name__] = VesicleModel
+#        self.shape_list.append(VesicleModel)
+        self.multiplication_factor.append(VesicleModel)
+        self.model_name_list.append(VesicleModel.__name__)
+        
+        from sas.models.MultiShellModel import MultiShellModel
+        self.model_dictionary[MultiShellModel.__name__] = MultiShellModel
+#        self.shape_list.append(MultiShellModel)
+        self.multiplication_factor.append(MultiShellModel)
+        self.model_name_list.append(MultiShellModel.__name__)
+        
+        from sas.models.OnionExpShellModel import OnionExpShellModel
+        self.model_dictionary[OnionExpShellModel.__name__] = OnionExpShellModel
+#        self.shape_list.append(OnionExpShellModel)
+        self.multiplication_factor.append(OnionExpShellModel)
+        self.multi_func_list.append(OnionExpShellModel)
+                         
+        from sas.models.SphericalSLDModel import SphericalSLDModel
+        self.model_dictionary[SphericalSLDModel.__name__] = SphericalSLDModel
+#        self.shape_list.append(SphericalSLDModel)
+        self.multiplication_factor.append(SphericalSLDModel)
+        self.multi_func_list.append(SphericalSLDModel)
+
+        
+        from sas.models.LinearPearlsModel import LinearPearlsModel
+        self.model_dictionary[LinearPearlsModel.__name__] = LinearPearlsModel
+#        self.shape_list.append(LinearPearlsModel)
+        self.model_name_list.append(LinearPearlsModel.__name__)
+          
+        from sas.models.PearlNecklaceModel import PearlNecklaceModel
+        self.model_dictionary[PearlNecklaceModel.__name__] = PearlNecklaceModel
+#        self.shape_list.append(PearlNecklaceModel)
+        self.model_name_list.append(PearlNecklaceModel.__name__)
+        #self.multiplication_factor.append(PearlNecklaceModel)
+        
+        from sas.models.CylinderModel import CylinderModel
+        self.model_dictionary[CylinderModel.__name__] = CylinderModel
+#        self.shape_list.append(CylinderModel)
+        self.multiplication_factor.append(CylinderModel)
+        self.model_name_list.append(CylinderModel.__name__)
+        
+        from sas.models.CoreShellCylinderModel import CoreShellCylinderModel
+        self.model_dictionary[CoreShellCylinderModel.__name__] = CoreShellCylinderModel
+#        self.shape_list.append(CoreShellCylinderModel)
+        self.multiplication_factor.append(CoreShellCylinderModel)
+        self.model_name_list.append(CoreShellCylinderModel.__name__)
+        
+        from sas.models.CoreShellBicelleModel import CoreShellBicelleModel
+        self.model_dictionary[CoreShellBicelleModel.__name__] = CoreShellBicelleModel
+#        self.shape_list.append(CoreShellBicelleModel)
+        self.multiplication_factor.append(CoreShellBicelleModel)
+        self.model_name_list.append(CoreShellBicelleModel.__name__)
+                
+        from sas.models.HollowCylinderModel import HollowCylinderModel
+        self.model_dictionary[HollowCylinderModel.__name__] = HollowCylinderModel
+#        self.shape_list.append(HollowCylinderModel)
+        self.multiplication_factor.append(HollowCylinderModel)
+        self.model_name_list.append(HollowCylinderModel.__name__)
+              
+        from sas.models.FlexibleCylinderModel import FlexibleCylinderModel
+        self.model_dictionary[FlexibleCylinderModel.__name__] = FlexibleCylinderModel
+#        self.shape_list.append(FlexibleCylinderModel)
+        self.model_name_list.append(FlexibleCylinderModel.__name__)
+
+        from sas.models.FlexCylEllipXModel import FlexCylEllipXModel
+        self.model_dictionary[FlexCylEllipXModel.__name__] = FlexCylEllipXModel
+#        self.shape_list.append(FlexCylEllipXModel)
+        self.model_name_list.append(FlexCylEllipXModel.__name__)
+        
+        from sas.models.StackedDisksModel import StackedDisksModel
+        self.model_dictionary[StackedDisksModel.__name__] = StackedDisksModel
+#        self.shape_list.append(StackedDisksModel)
+        self.multiplication_factor.append(StackedDisksModel)
+        self.model_name_list.append(StackedDisksModel.__name__)
+        
+        from sas.models.ParallelepipedModel import ParallelepipedModel
+        self.model_dictionary[ParallelepipedModel.__name__] = ParallelepipedModel
+#        self.shape_list.append(ParallelepipedModel)
+        self.multiplication_factor.append(ParallelepipedModel)
+        self.model_name_list.append(ParallelepipedModel.__name__)
+        
+        from sas.models.CSParallelepipedModel import CSParallelepipedModel
+        self.model_dictionary[CSParallelepipedModel.__name__] = CSParallelepipedModel
+#        self.shape_list.append(CSParallelepipedModel)
+        self.multiplication_factor.append(CSParallelepipedModel)
+        self.model_name_list.append(CSParallelepipedModel.__name__)
+        
+        from sas.models.EllipticalCylinderModel import EllipticalCylinderModel
+        self.model_dictionary[EllipticalCylinderModel.__name__] = EllipticalCylinderModel
+#        self.shape_list.append(EllipticalCylinderModel)
+        self.multiplication_factor.append(EllipticalCylinderModel)
+        self.model_name_list.append(EllipticalCylinderModel.__name__)
+        
+        from sas.models.BarBellModel import BarBellModel
+        self.model_dictionary[BarBellModel.__name__] = BarBellModel
+#        self.shape_list.append(BarBellModel)
+        self.model_name_list.append(BarBellModel.__name__)
+        # not implemeted yet!
+        #self.multiplication_factor.append(BarBellModel)
+        
+        from sas.models.CappedCylinderModel import CappedCylinderModel
+        self.model_dictionary[CappedCylinderModel.__name__] = CappedCylinderModel
+ #       self.shape_list.append(CappedCylinderModel)
+        self.model_name_list.append(CappedCylinderModel.__name__)
+        # not implemeted yet!
+        #self.multiplication_factor.append(CappedCylinderModel)
+        
+        from sas.models.EllipsoidModel import EllipsoidModel
+        self.model_dictionary[EllipsoidModel.__name__] = EllipsoidModel
+#        self.shape_list.append(EllipsoidModel)
+        self.multiplication_factor.append(EllipsoidModel)
+        self.model_name_list.append(EllipsoidModel.__name__)
+      
+        from sas.models.CoreShellEllipsoidModel import CoreShellEllipsoidModel
+        self.model_dictionary[CoreShellEllipsoidModel.__name__] = CoreShellEllipsoidModel
+#        self.shape_list.append(CoreShellEllipsoidModel)
+        self.multiplication_factor.append(CoreShellEllipsoidModel)
+        self.model_name_list.append(CoreShellEllipsoidModel.__name__)
+
+        from sas.models.CoreShellEllipsoidXTModel import CoreShellEllipsoidXTModel
+        self.model_dictionary[CoreShellEllipsoidXTModel.__name__] = CoreShellEllipsoidXTModel
+#        self.shape_list.append(CoreShellEllipsoidXTModel)
+        self.multiplication_factor.append(CoreShellEllipsoidXTModel)
+        self.model_name_list.append(CoreShellEllipsoidXTModel.__name__)
+         
+        from sas.models.TriaxialEllipsoidModel import TriaxialEllipsoidModel
+        self.model_dictionary[TriaxialEllipsoidModel.__name__] = TriaxialEllipsoidModel
+#        self.shape_list.append(TriaxialEllipsoidModel)
+        self.multiplication_factor.append(TriaxialEllipsoidModel)
+        self.model_name_list.append(TriaxialEllipsoidModel.__name__)
+        
+        from sas.models.LamellarModel import LamellarModel
+        self.model_dictionary[LamellarModel.__name__] = LamellarModel
+#        self.shape_list.append(LamellarModel)
+        self.model_name_list.append(LamellarModel.__name__)
+        
+        from sas.models.LamellarFFHGModel import LamellarFFHGModel
+        self.model_dictionary[LamellarFFHGModel.__name__] = LamellarFFHGModel
+#        self.shape_list.append(LamellarFFHGModel)
+        self.model_name_list.append(LamellarFFHGModel.__name__)
+        
+        from sas.models.LamellarPSModel import LamellarPSModel
+        self.model_dictionary[LamellarPSModel.__name__] = LamellarPSModel
+#        self.shape_list.append(LamellarPSModel)
+        self.model_name_list.append(LamellarPSModel.__name__)
+     
+        from sas.models.LamellarPSHGModel import LamellarPSHGModel
+        self.model_dictionary[LamellarPSHGModel.__name__] = LamellarPSHGModel
+#        self.shape_list.append(LamellarPSHGModel)
+        self.model_name_list.append(LamellarPSHGModel.__name__)
+        
+        from sas.models.LamellarPCrystalModel import LamellarPCrystalModel
+        self.model_dictionary[LamellarPCrystalModel.__name__] = LamellarPCrystalModel
+#        self.shape_list.append(LamellarPCrystalModel)
+        self.model_name_list.append(LamellarPCrystalModel.__name__)
+        
+        from sas.models.SCCrystalModel import SCCrystalModel
+        self.model_dictionary[SCCrystalModel.__name__] = SCCrystalModel
+#        self.shape_list.append(SCCrystalModel)
+        self.model_name_list.append(SCCrystalModel.__name__)
+        
+        from sas.models.FCCrystalModel import FCCrystalModel
+        self.model_dictionary[FCCrystalModel.__name__] = FCCrystalModel
+#        self.shape_list.append(FCCrystalModel)
+        self.model_name_list.append(FCCrystalModel.__name__)
+        
+        from sas.models.BCCrystalModel import BCCrystalModel
+        self.model_dictionary[BCCrystalModel.__name__] = BCCrystalModel
+#        self.shape_list.append(BCCrystalModel)
+        self.model_name_list.append(BCCrystalModel.__name__)
+      
+        ## Structure factor
+        from sas.models.SquareWellStructure import SquareWellStructure
+        self.model_dictionary[SquareWellStructure.__name__] = SquareWellStructure
+        self.struct_list.append(SquareWellStructure)
+        self.model_name_list.append(SquareWellStructure.__name__)
+        
+        from sas.models.HardsphereStructure import HardsphereStructure
+        self.model_dictionary[HardsphereStructure.__name__] = HardsphereStructure
+        self.struct_list.append(HardsphereStructure)
+        self.model_name_list.append(HardsphereStructure.__name__)
+         
+        from sas.models.StickyHSStructure import StickyHSStructure
+        self.model_dictionary[StickyHSStructure.__name__] = StickyHSStructure
+        self.struct_list.append(StickyHSStructure)
+        self.model_name_list.append(StickyHSStructure.__name__)
+        
+        from sas.models.HayterMSAStructure import HayterMSAStructure
+        self.model_dictionary[HayterMSAStructure.__name__] = HayterMSAStructure
+        self.struct_list.append(HayterMSAStructure)
+        self.model_name_list.append(HayterMSAStructure.__name__)
+
+
+        ##shape-independent models
+        from sas.models.PowerLawAbsModel import PowerLawAbsModel
+        self.model_dictionary[PowerLawAbsModel.__name__] = PowerLawAbsModel
+#        self.shape_indep_list.append(PowerLawAbsModel)
+        self.model_name_list.append(PowerLawAbsModel.__name__)
+        
+        from sas.models.BEPolyelectrolyte import BEPolyelectrolyte
+        self.model_dictionary[BEPolyelectrolyte.__name__] = BEPolyelectrolyte
+#        self.shape_indep_list.append(BEPolyelectrolyte)
+        self.model_name_list.append(BEPolyelectrolyte.__name__)
+        self.form_factor_dict[str(wx.NewId())] =  [SphereModel]
+        
+        from sas.models.BroadPeakModel import BroadPeakModel
+        self.model_dictionary[BroadPeakModel.__name__] = BroadPeakModel
+#        self.shape_indep_list.append(BroadPeakModel)
+        self.model_name_list.append(BroadPeakModel.__name__)
+        
+        from sas.models.CorrLengthModel import CorrLengthModel
+        self.model_dictionary[CorrLengthModel.__name__] = CorrLengthModel
+#        self.shape_indep_list.append(CorrLengthModel)
+        self.model_name_list.append(CorrLengthModel.__name__)
+        
+        from sas.models.DABModel import DABModel
+        self.model_dictionary[DABModel.__name__] = DABModel
+#        self.shape_indep_list.append(DABModel)
+        self.model_name_list.append(DABModel.__name__)
+        
+        from sas.models.DebyeModel import DebyeModel
+        self.model_dictionary[DebyeModel.__name__] = DebyeModel
+#        self.shape_indep_list.append(DebyeModel)
+        self.model_name_list.append(DebyeModel.__name__)
+        
+        from sas.models.FractalModel import FractalModel
+        self.model_dictionary[FractalModel.__name__] = FractalModel
+#        self.shape_indep_list.append(FractalModel)
+        self.model_name_list.append(FractalModel.__name__)
+        
+        from sas.models.FractalCoreShellModel import FractalCoreShellModel
+        self.model_dictionary[FractalCoreShellModel.__name__] = FractalCoreShellModel
+#        self.shape_indep_list.append(FractalCoreShellModel)
+        self.model_name_list.append(FractalCoreShellModel.__name__)
+        
+        from sas.models.GaussLorentzGelModel import GaussLorentzGelModel
+        self.model_dictionary[GaussLorentzGelModel.__name__] = GaussLorentzGelModel
+#        self.shape_indep_list.append(GaussLorentzGelModel)
+        self.model_name_list.append(GaussLorentzGelModel.__name__)
+                
+        from sas.models.GuinierModel import GuinierModel
+        self.model_dictionary[GuinierModel.__name__] = GuinierModel
+#        self.shape_indep_list.append(GuinierModel)
+        self.model_name_list.append(GuinierModel.__name__)
+        
+        from sas.models.GuinierPorodModel import GuinierPorodModel
+        self.model_dictionary[GuinierPorodModel.__name__] = GuinierPorodModel
+#        self.shape_indep_list.append(GuinierPorodModel)
+        self.model_name_list.append(GuinierPorodModel.__name__)
+
+        from sas.models.LorentzModel import LorentzModel
+        self.model_dictionary[LorentzModel.__name__] = LorentzModel
+#        self.shape_indep_list.append(LorentzModel)
+        self.model_name_list.append(LorentzModel.__name__)
+
+        from sas.models.MassFractalModel import MassFractalModel
+        self.model_dictionary[MassFractalModel.__name__] = MassFractalModel
+#        self.shape_indep_list.append(MassFractalModel)
+        self.model_name_list.append(MassFractalModel.__name__)
+        
+        from sas.models.MassSurfaceFractal import MassSurfaceFractal
+        self.model_dictionary[MassSurfaceFractal.__name__] = MassSurfaceFractal
+#        self.shape_indep_list.append(MassSurfaceFractal)
+        self.model_name_list.append(MassSurfaceFractal.__name__)
+        
+        from sas.models.PeakGaussModel import PeakGaussModel
+        self.model_dictionary[PeakGaussModel.__name__] = PeakGaussModel
+#        self.shape_indep_list.append(PeakGaussModel)
+        self.model_name_list.append(PeakGaussModel.__name__)
+        
+        from sas.models.PeakLorentzModel import PeakLorentzModel
+        self.model_dictionary[PeakLorentzModel.__name__] = PeakLorentzModel
+#        self.shape_indep_list.append(PeakLorentzModel)
+        self.model_name_list.append(PeakLorentzModel.__name__)
+        
+        from sas.models.Poly_GaussCoil import Poly_GaussCoil
+        self.model_dictionary[Poly_GaussCoil.__name__] = Poly_GaussCoil
+#        self.shape_indep_list.append(Poly_GaussCoil)
+        self.model_name_list.append(Poly_GaussCoil.__name__)
+        
+        from sas.models.PolymerExclVolume import PolymerExclVolume
+        self.model_dictionary[PolymerExclVolume.__name__] = PolymerExclVolume
+#        self.shape_indep_list.append(PolymerExclVolume)
+        self.model_name_list.append(PolymerExclVolume.__name__)
+        
+        from sas.models.PorodModel import PorodModel
+        self.model_dictionary[PorodModel.__name__] = PorodModel
+#        self.shape_indep_list.append(PorodModel)
+        self.model_name_list.append(PorodModel.__name__)
+        
+        from sas.models.RPA10Model import RPA10Model
+        self.model_dictionary[RPA10Model.__name__] = RPA10Model
+#        self.shape_indep_list.append(RPA10Model)
+        self.multi_func_list.append(RPA10Model)
+
+        from sas.models.StarPolymer import StarPolymer
+        self.model_dictionary[StarPolymer.__name__] = StarPolymer
+#        self.shape_indep_list.append(StarPolymer)
+        self.model_name_list.append(StarPolymer.__name__)
+
+        from sas.models.SurfaceFractalModel import SurfaceFractalModel
+        self.model_dictionary[SurfaceFractalModel.__name__] = SurfaceFractalModel
+#        self.shape_indep_list.append(SurfaceFractalModel)
+        self.model_name_list.append(SurfaceFractalModel.__name__)
+        
+        from sas.models.TeubnerStreyModel import TeubnerStreyModel
+        self.model_dictionary[TeubnerStreyModel.__name__] = TeubnerStreyModel
+#        self.shape_indep_list.append(TeubnerStreyModel)
+        self.model_name_list.append(TeubnerStreyModel.__name__)
+
+        from sas.models.TwoLorentzianModel import TwoLorentzianModel
+        self.model_dictionary[TwoLorentzianModel.__name__] = TwoLorentzianModel
+#        self.shape_indep_list.append(TwoLorentzianModel)
+        self.model_name_list.append(TwoLorentzianModel.__name__)
+        
+        from sas.models.TwoPowerLawModel import TwoPowerLawModel
+        self.model_dictionary[TwoPowerLawModel.__name__] = TwoPowerLawModel
+#        self.shape_indep_list.append(TwoPowerLawModel)
+        self.model_name_list.append(TwoPowerLawModel.__name__)
+        
+        from sas.models.UnifiedPowerRgModel import UnifiedPowerRgModel
+        self.model_dictionary[UnifiedPowerRgModel.__name__] = UnifiedPowerRgModel
+#        self.shape_indep_list.append(UnifiedPowerRgModel)
+        self.multi_func_list.append(UnifiedPowerRgModel)
+
+        from sas.models.LineModel import LineModel
+        self.model_dictionary[LineModel.__name__] = LineModel
+#        self.shape_indep_list.append(LineModel)
+        self.model_name_list.append(LineModel.__name__)
+        
+        from sas.models.ReflectivityModel import ReflectivityModel
+        self.model_dictionary[ReflectivityModel.__name__] = ReflectivityModel
+#        self.shape_indep_list.append(ReflectivityModel)
+        self.multi_func_list.append(ReflectivityModel)
+        
+        from sas.models.ReflectivityIIModel import ReflectivityIIModel
+        self.model_dictionary[ReflectivityIIModel.__name__] = ReflectivityIIModel
+#        self.shape_indep_list.append(ReflectivityIIModel)
+        self.multi_func_list.append(ReflectivityIIModel)
+        
+        from sas.models.GelFitModel import GelFitModel
+        self.model_dictionary[GelFitModel.__name__] = GelFitModel
+#        self.shape_indep_list.append(GelFitModel)
+        self.model_name_list.append(GelFitModel.__name__)
+
+        from sas.models.PringlesModel import PringlesModel
+        self.model_dictionary[PringlesModel.__name__] = PringlesModel
+#        self.shape_indep_list.append(PringlesModel)
+        self.model_name_list.append(PringlesModel.__name__)
+
+        from sas.models.RectangularPrismModel import RectangularPrismModel
+        self.model_dictionary[RectangularPrismModel.__name__] = RectangularPrismModel
+#        self.shape_list.append(RectangularPrismModel)
+        self.multiplication_factor.append(RectangularPrismModel)
+        self.model_name_list.append(RectangularPrismModel.__name__)
+
+        from sas.models.RectangularHollowPrismInfThinWallsModel import RectangularHollowPrismInfThinWallsModel
+        self.model_dictionary[RectangularHollowPrismInfThinWallsModel.__name__] = RectangularHollowPrismInfThinWallsModel
+#        self.shape_list.append(RectangularHollowPrismInfThinWallsModel)
+        self.multiplication_factor.append(RectangularHollowPrismInfThinWallsModel)
+        self.model_name_list.append(RectangularHollowPrismInfThinWallsModel.__name__)
+
+        from sas.models.RectangularHollowPrismModel import RectangularHollowPrismModel
+        self.model_dictionary[RectangularHollowPrismModel.__name__] = RectangularHollowPrismModel
+#        self.shape_list.append(RectangularHollowPrismModel)
+        self.multiplication_factor.append(RectangularHollowPrismModel)
+        self.model_name_list.append(RectangularHollowPrismModel.__name__)
+
+        from sas.models.MicelleSphCoreModel import MicelleSphCoreModel
+        self.model_dictionary[MicelleSphCoreModel.__name__] = MicelleSphCoreModel
+#        self.shape_list.append(MicelleSphCoreModel)
+        self.multiplication_factor.append(MicelleSphCoreModel)
+        self.model_name_list.append(MicelleSphCoreModel.__name__)
+
+
+        #from sas.models.FractalO_Z import FractalO_Z
+        #self.model_dictionary[FractalO_Z.__name__] = FractalO_Z
+        #self.shape_indep_list.append(FractalO_Z)
+        #self.model_name_list.append(FractalO_Z.__name__)
+    
+        #Looking for plugins
+        self.stored_plugins = self.findModels()
+        self.plugins = self.stored_plugins.values()
+        for name, plug in self.stored_plugins.iteritems():
+            self.model_dictionary[name] = plug
+            
+        self._get_multifunc_models()
+       
+        return 0
+
+    def is_changed(self):
+        """
+        check the last time the plugin dir has changed and return true
+         is the directory was modified else return false
+        """
+        is_modified = False
+        plugin_dir = find_plugins_dir()
+        if os.path.isdir(plugin_dir):
+            temp = os.path.getmtime(plugin_dir)
+            if  self.last_time_dir_modified != temp:
+                is_modified = True
+                self.last_time_dir_modified = temp
+        
+        return is_modified
+    
+    def update(self):
+        """
+        return a dictionary of model if
+        new models were added else return empty dictionary
+        """
+        new_plugins = self.findModels()
+        if len(new_plugins) > 0:
+            for name, plug in  new_plugins.iteritems():
+                if name not in self.stored_plugins.keys():
+                    self.stored_plugins[name] = plug
+                    self.plugins.append(plug)
+                    self.model_dictionary[name] = plug
+            self.model_combobox.set_list("Customized Models", self.plugins)
+            return self.model_combobox.get_list()
+        else:
+            return {}
+    
+    def pulgins_reset(self):
+        """
+        return a dictionary of model
+        """
+        self.plugins = []
+        new_plugins = _findModels(dir)
+        for name, plug in  new_plugins.iteritems():
+            for stored_name, stored_plug in self.stored_plugins.iteritems():
+                if name == stored_name:
+                    del self.stored_plugins[name]
+                    del self.model_dictionary[name]
+                    break
+            self.stored_plugins[name] = plug
+            self.plugins.append(plug)
+            self.model_dictionary[name] = plug
+
+        self.model_combobox.reset_list("Customized Models", self.plugins)
+        return self.model_combobox.get_list()
+       
+##   I believe the next four methods are for the old form factor GUI
+##   where the dropdown showed a list of categories which then rolled out
+##   in a second dropdown to the side. Some testing shows they indeed no longer
+##   seem to be called.  If no problems are found during testing of release we
+##   can remove this huge chunck of stuff.
+##
+##   -PDB  April 26, 2014
+
+#   def populate_menu(self, modelmenu, event_owner):
+#       """
+#       Populate a menu with our models
+#       
+#       :param id: first menu event ID to use when binding the menu events
+#       :param modelmenu: wx.Menu object to populate
+#       :param event_owner: wx object to bind the menu events to
+#       
+#       :return: the next free event ID following the new menu events
+#       
+#       """
+# 
+        ## Fill model lists
+#        self._getModelList()
+        ## store reference to model menu of guiframe
+#        self.modelmenu = modelmenu
+        ## guiframe reference
+#        self.event_owner = event_owner
+        
+#        shape_submenu = wx.Menu()
+#        shape_indep_submenu = wx.Menu()
+#        structure_factor = wx.Menu()
+#        added_models = wx.Menu()
+#        multip_models = wx.Menu()
+        ## create menu with shape
+#        self._fill_simple_menu(menuinfo=["Shapes",
+#                                         shape_submenu,
+#                                         " simple shape"],
+#                         list1=self.shape_list)
+        
+#        self._fill_simple_menu(menuinfo=["Shape-Independent",
+#                                         shape_indep_submenu,
+#                                         "List of shape-independent models"],
+#                         list1=self.shape_indep_list)
+        
+#        self._fill_simple_menu(menuinfo=["Structure Factors",
+#                                         structure_factor,
+#                                         "List of Structure factors models"],
+#                                list1=self.struct_list)
+        
+#        self._fill_plugin_menu(menuinfo=["Customized Models", added_models,
+#                                            "List of additional models"],
+#                                 list1=self.plugins)
+        
+#        self._fill_menu(menuinfo=["P(Q)*S(Q)", multip_models,
+#                                  "mulplication of 2 models"],
+#                                   list1=self.multiplication_factor,
+#                                   list2=self.struct_list)
+#        return 0
+    
+#    def _fill_plugin_menu(self, menuinfo, list1):
+#        """
+#        fill the plugin menu with costumized models
+#        """
+#        print ("got to fill plugin menu")
+#        if len(list1) == 0:
+#            id = wx.NewId()
+#            msg = "No model available check plugins.log for errors to fix problem"
+#            menuinfo[1].Append(int(id), "Empty", msg)
+#        self._fill_simple_menu(menuinfo, list1)
+        
+#   def _fill_simple_menu(self, menuinfo, list1):
+#       """
+#       Fill the menu with list item
+#       
+#       :param modelmenu: the menu to fill
+#       :param menuinfo: submenu item for the first column of this modelmenu
+#                        with info.Should be a list :
+#                        [name(string) , menu(wx.menu), help(string)]
+#       :param list1: contains item (form factor )to fill modelmenu second column
+#       
+#       """
+#       if len(list1) > 0:
+#           self.model_combobox.set_list(menuinfo[0], list1)
+            
+#            for item in list1:
+#                try:
+#                    id = wx.NewId()
+#                    struct_factor = item()
+#                    struct_name = struct_factor.__class__.__name__
+#                    if hasattr(struct_factor, "name"):
+#                        struct_name = struct_factor.name
+#                        
+#                    menuinfo[1].Append(int(id), struct_name, struct_name)
+#                    if not  item in self.struct_factor_dict.itervalues():
+#                        self.struct_factor_dict[str(id)] = item
+#                    wx.EVT_MENU(self.event_owner, int(id), self._on_model)
+#                except:
+#                    msg = "Error Occured: %s" % sys.exc_value
+#                    wx.PostEvent(self.event_owner, StatusEvent(status=msg))
+#                
+#        id = wx.NewId()
+#        self.modelmenu.AppendMenu(id, menuinfo[0], menuinfo[1], menuinfo[2])
+#        
+#    def _fill_menu(self, menuinfo, list1, list2):
+#        """
+#        Fill the menu with list item
+#        
+#        :param menuinfo: submenu item for the first column of this modelmenu
+#                         with info.Should be a list :
+#                         [name(string) , menu(wx.menu), help(string)]
+#        :param list1: contains item (form factor )to fill modelmenu second column
+#        :param list2: contains item (Structure factor )to fill modelmenu
+#                third column
+#                
+#        """
+#        if len(list1) > 0:
+#            self.model_combobox.set_list(menuinfo[0], list1)
+#            
+#            for item in list1:
+#                form_factor = item()
+#                form_name = form_factor.__class__.__name__
+#                if hasattr(form_factor, "name"):
+#                    form_name = form_factor.name
+#                ### store form factor to return to other users
+#                newmenu = wx.Menu()
+#                if len(list2) > 0:
+#                    for model  in list2:
+#                        id = wx.NewId()
+#                        struct_factor = model()
+#                        name = struct_factor.__class__.__name__
+#                        if hasattr(struct_factor, "name"):
+#                            name = struct_factor.name
+#                        newmenu.Append(id, name, name)
+#                        wx.EVT_MENU(self.event_owner, int(id), self._on_model)
+#                        ## save form_fact and struct_fact
+#                        self.form_factor_dict[int(id)] = [form_factor,
+#                                                          struct_factor]
+#                        
+#                form_id = wx.NewId()
+#                menuinfo[1].AppendMenu(int(form_id), form_name,
+#                                       newmenu, menuinfo[2])
+#        id = wx.NewId()
+#        self.modelmenu.AppendMenu(id, menuinfo[0], menuinfo[1], menuinfo[2])
+        
+    def _on_model(self, evt):
+        """
+        React to a model menu event
+        
+        :param event: wx menu event
+        
+        """
+        if int(evt.GetId()) in self.form_factor_dict.keys():
+            from sas.models.MultiplicationModel import MultiplicationModel
+            self.model_dictionary[MultiplicationModel.__name__] = MultiplicationModel
+            model1, model2 = self.form_factor_dict[int(evt.GetId())]
+            model = MultiplicationModel(model1, model2)
+        else:
+            model = self.struct_factor_dict[str(evt.GetId())]()
+        
+        #TODO: investigate why the following two lines were left in the code
+        #      even though the ModelEvent class doesn't exist
+        #evt = ModelEvent(model=model)
+        #wx.PostEvent(self.event_owner, evt)
+        
+    def _get_multifunc_models(self):
+        """
+        Get the multifunctional models
+        """
+        for item in self.plugins:
+            try:
+                # check the multiplicity if any
+                if item.multiplicity_info[0] > 1:
+                    self.multi_func_list.append(item)
+            except:
+                # pass to other items
+                pass
+                    
+    def get_model_list(self):
+        """
+        return dictionary of models for fitpanel use
+        
+        """
+        ## Model_list now only contains attribute lists not category list.
+        ## Eventually this should be in one master list -- read in category
+        ## list then pull those models that exist and get attributes then add
+        ## to list ..and if model does not exist remove from list as now
+        ## and update json file.
+        ##
+        ## -PDB   April 26, 2014
+        
+#        self.model_combobox.set_list("Shapes", self.shape_list)
+#        self.model_combobox.set_list("Shape-Independent",
+#                                     self.shape_indep_list)
+        self.model_combobox.set_list("Structure Factors", self.struct_list)
+        self.model_combobox.set_list("Customized Models", self.plugins)
+        self.model_combobox.set_list("P(Q)*S(Q)", self.multiplication_factor)
+        self.model_combobox.set_list("multiplication",
+                                     self.multiplication_factor)
+        self.model_combobox.set_list("Multi-Functions", self.multi_func_list)
+        return self.model_combobox.get_list()
+    
+    def get_model_name_list(self):
+        """
+        return regular model name list
+        """
+        return self.model_name_list
+
+    def get_model_dictionary(self):
+        """
+        return dictionary linking model names to objects
+        """
+        return self.model_dictionary
+  
+        
+class ModelManager(object):
+    """
+    implement model
+    """
+    __modelmanager = ModelManagerBase()
+    cat_model_list = [model_name for model_name \
+                      in __modelmanager.model_dictionary.keys() \
+                      if model_name not in __modelmanager.stored_plugins.keys()]
+
+    CategoryInstaller.check_install(model_list=cat_model_list)
+    def findModels(self):
+        return self.__modelmanager.findModels()
+    
+    def _getModelList(self):
+        return self.__modelmanager._getModelList()
+    
+    def is_changed(self):
+        return self.__modelmanager.is_changed()
+    
+    def update(self):
+        return self.__modelmanager.update()
+    
+    def pulgins_reset(self):
+        return self.__modelmanager.pulgins_reset()
+    
+    def populate_menu(self, modelmenu, event_owner):
+        return self.__modelmanager.populate_menu(modelmenu, event_owner)
+    
+    def _on_model(self, evt):
+        return self.__modelmanager._on_model(evt)
+    
+    def _get_multifunc_models(self):
+        return self.__modelmanager._get_multifunc_models()
+    
+    def get_model_list(self):
+        return self.__modelmanager.get_model_list()
+    
+    def get_model_name_list(self):
+        return self.__modelmanager.get_model_name_list()
+
+    def get_model_dictionary(self):
+        return self.__modelmanager.get_model_dictionary()
Index: src/sas/perspectives/fitting/pagestate.py
===================================================================
--- src/sas/perspectives/fitting/pagestate.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/fitting/pagestate.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,1764 @@
+"""
+    Class that holds a fit page state
+"""
+################################################################################
+#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 2009, University of Tennessee
+################################################################################
+import time
+import os
+import sys
+import copy
+import logging
+import numpy
+import string
+
+import xml.dom.minidom
+from xml.dom.minidom import parseString
+from lxml import etree
+
+import sas.dataloader
+from sas.dataloader.readers.cansas_reader import Reader as CansasReader
+from sas.dataloader.readers.cansas_reader import get_content, write_node
+from sas.dataloader.data_info import Data2D
+from sas.dataloader.data_info import Collimation
+from sas.dataloader.data_info import Detector
+from sas.dataloader.data_info import Process
+from sas.dataloader.data_info import Aperture
+#Information to read/write state as xml
+FITTING_NODE_NAME = 'fitting_plug_in'
+CANSAS_NS = "cansas1d/1.0"
+
+list_of_data_attributes = [["is_data", "is_data", "bool"],
+                      ["group_id", "data_group_id", "string"],
+                      ["data_name", "data_name", "string"],
+                      ["data_id", "data_id", "string"],
+                      ["name", "name", "string"],
+                      ["data_name", "data_name", "string"]]
+list_of_state_attributes = [["engine_type", "engine_type", "string"],
+                       ["qmin", "qmin", "float"],
+                      ["qmax", "qmax", "float"],
+                      ["npts", "npts", "float"],
+                      #["shape_rbutton", "shape_rbutton", "bool"],
+                      #["shape_indep_rbutton", "shape_indep_rbutton", "bool"],
+                      #["plugin_rbutton", "plugin_rbutton", "bool"],
+                      #["struct_rbutton", "struct_rbutton", "bool"],
+                      ["categorycombobox", "categorycombobox", "string"],
+                      ["formfactorcombobox", "formfactorcombobox", "string"],
+                      ["structurecombobox", "structurecombobox", "string"],
+                      ["multi_factor","multi_factor","float"],
+                      ["magnetic_on","magnetic_on", "bool"],
+                      ["enable_smearer","enable_smearer","bool"],
+                      ["disable_smearer","disable_smearer","bool"],
+                      ["pinhole_smearer","pinhole_smearer","bool"],
+                      ["slit_smearer","slit_smearer","bool"],
+                      ["enable_disp","enable_disp","bool"],
+                      ["disable_disp","disable_disp","bool"],
+                      ["dI_noweight","dI_noweight","bool"],
+                      ["dI_didata","dI_didata","bool"],
+                      ["dI_sqrdata","dI_sqrdata","bool"],
+                      ["dI_idata","dI_idata","bool"],
+                      ["enable2D","enable2D","bool"],
+                      ["cb1","cb1","bool"],
+                      ["tcChi","tcChi","float"],
+                      ["smearer", "smearer", "float"],
+                      ["smear_type","smear_type", "string"],
+                      ["dq_l", "dq_l", "string"],
+                      ["dq_r","dq_r", "string"],
+                      ["dx_max", "dx_max", "float"],
+                      ["dx_min","dx_min", "float"],
+                      ["dxl", "dxl", "float"],
+                      ["dxw","dxw", "float"]]
+
+list_of_model_attributes = [["values", "values"],
+                            ["weights", "weights"]]
+
+list_of_obj_dic = [["disp_obj_dict", "_disp_obj_dict", "string"]]
+
+list_of_state_parameters = [["parameters", "parameters"],
+                            ["str_parameters", "str_parameters"],
+                            ["orientation_parameters", "orientation_params"],
+                            ["dispersity_parameters", "orientation_params_disp"],
+                            ["fixed_param", "fixed_param"],
+                            ["fittable_param", "fittable_param"]]
+list_of_data_2d_attr = [["xmin", "xmin", "float"],
+                        ["xmax", "xmax", "float"],
+                        ["ymin", "ymin", "float"],
+                        ["ymax", "ymax", "float"],
+                        ["_xaxis", "_xaxis", "string"],
+                        ["_xunit", "_xunit", "string"],
+                        ["_yaxis", "_yaxis", "string"],
+                        ["_yunit", "_yunit", "string"],
+                        ["_zaxis", "_zaxis", "string"],
+                        ["_zunit", "_zunit", "string"]]
+list_of_data2d_values = [["qx_data", "qx_data", "float"],
+                         ["qy_data", "qy_data", "float"],
+                         ["dqx_data", "dqx_data", "float"],
+                         ["dqy_data", "dqy_data", "float"],
+                         ["data", "data", "float"],
+                         ["q_data", "q_data", "float"],
+                         ["err_data", "err_data", "float"],
+                         ["mask", "mask", "bool"]]
+
+
+def parse_entry_helper(node, item):
+    """
+    Create a numpy list from value extrated from the node
+    
+    :param node: node from each the value is stored
+    :param item: list name of three strings.the two first are name of data
+        attribute and the third one is the type of the value of that
+        attribute. type can be string, float, bool, etc.
+    
+    : return: numpy array
+    """
+    if node is not None:
+        if item[2] == "string":
+            return str(node.get(item[0]).strip())
+        elif item[2] == "bool":
+            try:
+                return node.get(item[0]).strip() == "True"
+                
+            except:
+                return None
+        else:
+            try:
+                return float(node.get(item[0]))
+            except:
+                return None
+            
+            
+class PageState(object):
+    """
+    Contains information to reconstruct a page of the fitpanel.
+    """
+    def __init__(self, parent=None, model=None, data=None):
+        """
+        Initialize the current state
+        
+        :param model: a selected model within a page
+        :param data:
+        
+        """
+        self.file = None
+        #Time of state creation
+        self.timestamp = time.time()
+        ## Data member to store the dispersion object created
+        self._disp_obj_dict = {}
+        #------------------------
+        #Data used for fitting
+        self.data = data
+        # model data
+        self.theory_data = None
+        #Is 2D
+        self.is_2D = False
+        self.images = None
+        
+        #save additional information on data that dataloader.reader does not read
+        self.is_data = None
+        self.data_name = ""
+
+        if self.data is not None:
+            self.data_name = self.data.name
+        self.data_id = None
+        if self.data is not None and hasattr(self.data, "id"):
+            self.data_id = self.data.id
+        self.data_group_id = None
+        if self.data is not None and hasattr(self.data, "group_id"):
+            self.data_group_id = self.data.group_id
+        
+        ## reset True change the state of exsiting button
+        self.reset = False
+       
+        #engine type
+        self.engine_type = None
+        # flag to allow data2D plot
+        self.enable2D = False
+        # model on which the fit would be performed
+        self.model = model
+        self.m_name = None
+        #list of process done to model
+        self.process = []
+        #fit page manager
+        self.manager = None
+        #Store the parent of this panel parent
+        # For this application fitpanel is the parent
+        self.parent = parent
+        # Event_owner is the owner of model event
+        self.event_owner = None
+        ##page name
+        self.page_name = ""
+        # Contains link between model, all its parameters, and panel organization
+        self.parameters = []
+        # String parameter list that can not be fitted
+        self.str_parameters = []
+        # Contains list of parameters that cannot be fitted and reference to
+        #panel objects
+        self.fixed_param = []
+        # Contains list of parameters with dispersity and reference to
+        #panel objects
+        self.fittable_param = []
+        ## orientation parameters
+        self.orientation_params = []
+        ## orientation parameters for gaussian dispersity
+        self.orientation_params_disp = []
+        ## smearer info
+        self.smearer = None
+        self.smear_type = None
+        self.dq_l = None
+        self.dq_r = None
+        self.dx_max = None
+        self.dx_min = None
+        self.dxl = None
+        self.dxw = None
+        #list of dispersion parameters
+        self.disp_list = []
+        if self.model is not None:
+            self.disp_list = self.model.getDispParamList()
+
+        self.disp_cb_dict = {}
+        self.values = {}
+        self.weights = {}
+                    
+        #contains link between a model and selected parameters to fit
+        self.param_toFit = []
+        ##dictionary of model type and model class
+        self.model_list_box = None
+        ## save the state of the context menu
+        self.saved_states = {}
+        ## save selection of combobox
+        self.formfactorcombobox = None
+        self.categorycombobox = None
+        self.structurecombobox = None
+
+        ## radio box to select type of model
+        #self.shape_rbutton = False
+        #self.shape_indep_rbutton = False
+        #self.struct_rbutton = False
+        #self.plugin_rbutton = False
+        ## the indice of the current selection
+        self.disp_box = 0
+        ## Qrange
+        ## Q range
+        self.qmin = 0.001
+        self.qmax = 0.1
+        #reset data range
+        self.qmax_x = None
+        self.qmin_x = None
+        
+        self.npts = None
+        self.name = ""
+        self.multi_factor = None
+        self.magnetic_on = False
+        ## enable smearering state
+        self.enable_smearer = False
+        self.disable_smearer = True
+        self.pinhole_smearer = False
+        self.slit_smearer = False
+        # weighting options
+        self.dI_noweight = False
+        self.dI_didata = True
+        self.dI_sqrdata = False
+        self.dI_idata = False
+        ## disperity selection
+        self.enable_disp = False
+        self.disable_disp = True
+       
+        ## state of selected all check button
+        self.cb1 = False
+        ## store value of chisqr
+        self.tcChi = None
+    
+    def clone(self):
+        """
+        Create a new copy of the current object
+        """
+        model = None
+        if self.model is not None:
+            model = self.model.clone()
+            model.name = self.model.name
+        obj = PageState(self.parent, model=model)
+        obj.file = copy.deepcopy(self.file)
+        obj.data = copy.deepcopy(self.data)
+        if self.data is not None:
+            self.data_name = self.data.name
+        obj.data_name = self.data_name
+        obj.is_data = self.is_data
+        obj.model_list_box = copy.deepcopy(self.model_list_box)
+        obj.engine_type = copy.deepcopy(self.engine_type)
+        
+        obj.categorycombobox = self.categorycombobox
+        obj.formfactorcombobox = self.formfactorcombobox
+        obj.structurecombobox = self.structurecombobox
+        
+        #obj.shape_rbutton = self.shape_rbutton
+        #obj.shape_indep_rbutton = self.shape_indep_rbutton
+        #obj.struct_rbutton = self.struct_rbutton
+        #obj.plugin_rbutton = self.plugin_rbutton
+        
+        obj.manager = self.manager
+        obj.event_owner = self.event_owner
+        obj.disp_list = copy.deepcopy(self.disp_list)
+        
+        obj.enable2D = copy.deepcopy(self.enable2D)
+        obj.parameters = copy.deepcopy(self.parameters)
+        obj.str_parameters = copy.deepcopy(self.str_parameters)
+        obj.fixed_param = copy.deepcopy(self.fixed_param)
+        obj.fittable_param = copy.deepcopy(self.fittable_param)
+        obj.orientation_params = copy.deepcopy(self.orientation_params)
+        obj.orientation_params_disp = copy.deepcopy(self.orientation_params_disp)
+        obj.enable_disp = copy.deepcopy(self.enable_disp)
+        obj.disable_disp = copy.deepcopy(self.disable_disp)
+        obj.tcChi = self.tcChi
+  
+        if len(self._disp_obj_dict) > 0:
+            for k, v in self._disp_obj_dict.iteritems():
+                obj._disp_obj_dict[k] = v
+        if len(self.disp_cb_dict) > 0:
+            for k, v in self.disp_cb_dict.iteritems():
+                obj.disp_cb_dict[k] = v
+        if len(self.values) > 0:
+            for k, v in self.values.iteritems():
+                obj.values[k] = v
+        if len(self.weights) > 0:
+            for k, v in self.weights.iteritems():
+                obj.weights[k] = v
+        obj.enable_smearer = copy.deepcopy(self.enable_smearer)
+        obj.disable_smearer = copy.deepcopy(self.disable_smearer)
+        obj.pinhole_smearer = copy.deepcopy(self.pinhole_smearer)
+        obj.slit_smearer = copy.deepcopy(self.slit_smearer)
+        obj.smear_type = copy.deepcopy(self.smear_type)
+        obj.dI_noweight = copy.deepcopy(self.dI_noweight)
+        obj.dI_didata = copy.deepcopy(self.dI_didata)
+        obj.dI_sqrdata = copy.deepcopy(self.dI_sqrdata)
+        obj.dI_idata = copy.deepcopy(self.dI_idata)
+        obj.dq_l = copy.deepcopy(self.dq_l)
+        obj.dq_r = copy.deepcopy(self.dq_r)
+        obj.dx_max = copy.deepcopy(self.dx_max)
+        obj.dx_min = copy.deepcopy(self.dx_min)
+        obj.dxl = copy.deepcopy(self.dxl)
+        obj.dxw = copy.deepcopy(self.dxw)        
+        obj.disp_box = copy.deepcopy(self.disp_box)
+        obj.qmin = copy.deepcopy(self.qmin)
+        obj.qmax = copy.deepcopy(self.qmax)
+        obj.multi_factor = self.multi_factor
+        obj.magnetic_on = self.magnetic_on
+        obj.npts = copy.deepcopy(self.npts)
+        obj.cb1 = copy.deepcopy(self.cb1)
+        obj.smearer = copy.deepcopy(self.smearer)
+        
+        for name, state in self.saved_states.iteritems():
+            copy_name = copy.deepcopy(name)
+            copy_state = state.clone()
+            obj.saved_states[copy_name] = copy_state
+        return obj
+    
+    def _repr_helper(self, list, rep):
+        """
+        Helper method to print a state
+        """
+        for item in list:
+            rep += "parameter name: %s \n" % str(item[1])
+            rep += "value: %s\n" % str(item[2])
+            rep += "selected: %s\n" % str(item[0])
+            rep += "error displayed : %s \n" % str(item[4][0])
+            rep += "error value:%s \n" % str(item[4][1])
+            rep += "minimum displayed : %s \n" % str(item[5][0])
+            rep += "minimum value : %s \n" % str(item[5][1])
+            rep += "maximum displayed : %s \n" % str(item[6][0])
+            rep += "maximum value : %s \n" % str(item[6][1])
+            rep += "parameter unit: %s\n\n" % str(item[7])
+        return rep
+    
+    def __repr__(self):
+        """
+        output string for printing
+        """
+        rep = "\nState name: %s\n" % self.file
+        t = time.localtime(self.timestamp)
+        time_str = time.strftime("%b %d %Y %H;%M;%S ", t)
+
+        rep += "State created: %s\n" % time_str
+        rep += "State form factor combobox selection: %s\n" % self.formfactorcombobox
+        rep += "State structure factor combobox selection: %s\n" % self.structurecombobox
+        rep += "is data : %s\n" % self.is_data
+        rep += "data's name : %s\n" % self.data_name
+        rep += "data's id : %s\n" % self.data_id
+        if self.model != None:
+            m_name = self.model.__class__.__name__
+            if m_name == 'Model':
+                m_name = self.m_name
+            rep += "model name : %s\n" % m_name
+        else:
+            rep += "model name : None\n"
+        #rep += "model type (form factor) selected: %s\n" % self.shape_rbutton 
+        rep += "multi_factor : %s\n" % str(self.multi_factor)
+        rep += "magnetic_on : %s\n"% str(self.magnetic_on)
+        rep += "model type (Category) selected: %s\n" % self.categorycombobox
+        #rep += "model type (shape independent) selected: %s\n" % self.shape_indep_rbutton
+        #rep += "model type (structure factor) selected: %s\n" % self.struct_rbutton
+        #rep += "model type (plug-in ) selected: %s\n" % self.plugin_rbutton
+        rep += "data : %s\n" % str(self.data)
+        rep += "Plotting Range: min: %s, max: %s, steps: %s\n" % (str(self.qmin),
+                                                str(self.qmax), str(self.npts))
+        rep += "Dispersion selection : %s\n" % str(self.disp_box)
+        rep += "Smearing enable : %s\n" % str(self.enable_smearer)
+        rep += "Smearing disable : %s\n" % str(self.disable_smearer)
+        rep += "Pinhole smearer enable : %s\n" % str(self.pinhole_smearer)
+        rep += "Slit smearer enable : %s\n" % str(self.slit_smearer)
+        rep += "Dispersity enable : %s\n" % str(self.enable_disp)
+        rep += "Dispersity disable : %s\n" % str(self.disable_disp)
+        rep += "Slit smearer enable: %s\n" % str(self.slit_smearer)
+        
+        rep += "dI_noweight : %s\n" % str(self.dI_noweight)
+        rep += "dI_didata : %s\n" % str(self.dI_didata)
+        rep += "dI_sqrdata : %s\n" % str(self.dI_sqrdata)
+        rep += "dI_idata : %s\n" % str(self.dI_idata)
+        
+        rep += "2D enable : %s\n" % str(self.enable2D)
+        rep += "All parameters checkbox selected: %s\n" % (self.cb1)
+        rep += "Value of Chisqr : %s\n" % str(self.tcChi)
+        rep += "Smear object : %s\n" % str(self.smearer)
+        rep += "Smear type : %s\n" % (self.smear_type)
+        rep += "dq_l  : %s\n" % self.dq_l
+        rep += "dq_r  : %s\n" % self.dq_r
+        rep += "dx_max  : %s\n" % str(self.dx_max)
+        rep += "dx_min : %s\n" % str(self.dx_min)  
+        rep += "dxl  : %s\n" % str(self.dxl)
+        rep += "dxw : %s\n" % str(self.dxw)    
+        rep += "model  : %s\n\n" % str(self.model)
+        temp_parameters = []
+        temp_fittable_param = []
+        if self.data.__class__.__name__ == "Data2D":
+            self.is_2D = True
+        else:
+            self.is_2D = False
+        if self.data is not None:
+            if not self.is_2D:
+                for item in self.parameters:
+                    if not item in self.orientation_params:
+                        temp_parameters.append(item)
+                for item in self.fittable_param:
+                    if not item in self.orientation_params_disp:
+                        temp_fittable_param.append(item)
+            else:
+                temp_parameters = self.parameters
+                temp_fittable_param = self.fittable_param
+                
+            rep += "number parameters(self.parameters): %s\n" % len(temp_parameters)
+            rep = self._repr_helper(list=temp_parameters, rep=rep)
+            rep += "number str_parameters(self.str_parameters): %s\n" % len(self.str_parameters)
+            rep = self._repr_helper(list=self.str_parameters, rep=rep)
+            rep += "number fittable_param(self.fittable_param): %s\n" % len(temp_fittable_param)
+            rep = self._repr_helper(list=temp_fittable_param, rep=rep)
+            """
+            if is_2D:
+                rep += "number orientation parameters"
+                rep += "(self.orientation_params): %s\n"%len(self.orientation_params)
+                rep = self._repr_helper( list=self.orientation_params, rep=rep)
+                rep += "number dispersity parameters"
+                rep += "(self.orientation_params_disp): %s\n"%len(self.orientation_params_disp)
+                rep = self._repr_helper( list=self.orientation_params_disp, rep=rep)
+            """
+        return rep
+
+    def set_report_string(self):
+        """
+        Get the values (strings) from __str__ for report
+        """
+        # Dictionary of teh report strings
+        repo_time = ""
+        model_name = ""
+        title = ""
+        title_name = ""
+        file_name = ""
+        param_string = ""
+        paramval_string = ""
+        chi2_string = ""
+        multi_factor_string = ""
+        q_range = ""
+        strings = self.__repr__()
+        lines = strings.split('\n')
+
+        # get all string values from __str__()
+        for line in lines:
+            value = ""
+            content = line.split(":")
+            name = content[0]
+            try:
+                value = content[1]
+            except:
+                pass
+            if name.count("State created"):
+                repo_time = "" + value
+            if name.count("parameter name"):
+                val_name = value.split(".")
+                if len(val_name) > 1:
+                    if val_name[1].count("width"):
+                        param_string += value + ','
+                    else:
+                        continue
+                else:
+                    param_string += value + ','
+            if name == "value":
+                param_string += value + ','
+            if name == "error value":
+                param_string += value + ','
+            if name == "parameter unit":
+                param_string += value + ':'
+            if name == "Value of Chisqr ":
+                chi2 = ("Chi2/Npts = " + value)
+                chi2_string = CENTRE % chi2
+            if name == "multi_factor ":
+                muti_factor = ("muti_factor = " + value)
+                muti_factor_string = CENTRE % muti_factor
+            if name == "magentic_on ":
+                magentic_on = ("magentic_on = " + value)
+                if string(value) == 'True':
+                    magentic_on_string = CENTRE % magentic_on
+            if name == "Title":
+                if len(value.strip()) == 0:
+                    continue
+                title = value + " [" + repo_time + "]"
+                title_name = HEADER % title
+            if name == "data ":
+                try:
+                    file = ("File name:" + content[2])
+                    file_name = CENTRE % file
+                    if len(title) == 0:
+                        title = content[2] + " [" + repo_time + "]"
+                        title_name = HEADER % title
+                except:
+                    pass
+            if name == "model name ":
+                try:
+                    modelname = "Model name:" + content[1]
+                except:
+                    modelname = "Model name:" + " NAN"
+                model_name = CENTRE % modelname
+                 
+            if name == "Plotting Range":
+                try:
+                    q_range = content[1] + " = " + content[2] \
+                            + " = " + content[3].split(",")[0]
+                    q_name = ("Q Range:    " + q_range)
+                    q_range = CENTRE % q_name
+                except:
+                    pass
+        paramval = ""
+        for lines in param_string.split(":"):
+            line = lines.split(",")
+            if len(lines) > 0:
+                param = line[0]
+                param += " = " + line[1]
+                if len(line[2].split()) > 0 and not line[2].count("None"):
+                    param += " +- " + line[2]
+                if len(line[3].split()) > 0 and not line[3].count("None"):
+                    param += " " + line[3]
+                if not paramval.count(param):
+                    paramval += param + "\n"
+                    paramval_string += CENTRE % param + "\n"
+        
+        text_string = "\n\n\n" + title + "\n\n" + file + \
+                      "\n" + q_name + \
+                      "\n" + chi2 + \
+                      "\n\n" + paramval
+        
+        title_name = self._check_html_format(title_name)
+        file_name = self._check_html_format(file_name)
+        title = self._check_html_format(title)
+                                  
+        html_string = title_name + "\n" + file_name + \
+                                   "\n" + model_name + \
+                                   "\n" + q_range + \
+                                   "\n" + chi2_string + \
+                                   "\n" + ELINE + \
+                                   "\n" + paramval_string + \
+                                   "\n" + ELINE + \
+                                   "\n" + FEET_1 % title + \
+                                   "\n" + FEET_2
+                                                                      
+        return html_string, text_string, title
+    
+    def _check_html_format(self, name):
+        """
+        Check string '%' for html format
+        """
+        if name.count('%'):
+            name = name.replace('%', '&#37')
+        
+        return name
+    
+    def report(self, figs=None, canvases=None):
+        """
+        Invoke report dialog panel
+        
+        : param figs: list of pylab figures [list]
+        """
+        from sas.perspectives.fitting.report_dialog import ReportDialog
+        # get the strings for report
+        html_str, text_str, title = self.set_report_string()
+        # Allow 2 figures to append
+        if len(figs) == 1:
+            add_str = FEET_3
+        elif len(figs) == 2:
+            add_str = ELINE
+            add_str += FEET_2 % ("%s")
+            add_str += ELINE
+            add_str += FEET_3
+        elif len(figs) > 2:
+            add_str = ELINE
+            add_str += FEET_2 % ("%s")
+            add_str += ELINE
+            add_str += FEET_2 % ("%s")
+            add_str += ELINE
+            add_str += FEET_3
+        else:
+            add_str = ""
+
+        # final report html strings
+        report_str = html_str % ("%s") + add_str
+
+        # make plot image
+        images = self.set_plot_state(figs, canvases)
+        report_list = [report_str, text_str, images]
+        dialog = ReportDialog(report_list, None, -1, "")
+        dialog.ShowModal()
+        
+    def _toXML_helper(self, thelist, element, newdoc):
+        """
+        Helper method to create xml file for saving state
+        """
+        for item in thelist:
+            sub_element = newdoc.createElement('parameter')
+            sub_element.setAttribute('name', str(item[1]))
+            sub_element.setAttribute('value', str(item[2]))
+            sub_element.setAttribute('selected_to_fit', str(item[0]))
+            sub_element.setAttribute('error_displayed', str(item[4][0]))
+            sub_element.setAttribute('error_value', str(item[4][1]))
+            sub_element.setAttribute('minimum_displayed', str(item[5][0]))
+            sub_element.setAttribute('minimum_value', str(item[5][1]))
+            sub_element.setAttribute('maximum_displayed', str(item[6][0]))
+            sub_element.setAttribute('maximum_value', str(item[6][1]))
+            sub_element.setAttribute('unit', str(item[7]))
+            element.appendChild(sub_element)
+        
+    def toXML(self, file="fitting_state.fitv", doc=None, entry_node=None):
+        """
+        Writes the state of the InversionControl panel to file, as XML.
+        
+        Compatible with standalone writing, or appending to an
+        already existing XML document. In that case, the XML document
+        is required. An optional entry node in the XML document may also be given.
+        
+        :param file: file to write to
+        :param doc: XML document object [optional]
+        :param entry_node: XML node within the XML document at which we will append the data [optional]
+        
+        """
+        from xml.dom.minidom import getDOMImplementation
+
+        # Check whether we have to write a standalone XML file
+        if doc is None:
+            impl = getDOMImplementation()
+            doc_type = impl.createDocumentType(FITTING_NODE_NAME, "1.0", "1.0")
+            newdoc = impl.createDocument(None, FITTING_NODE_NAME, doc_type)
+            top_element = newdoc.documentElement
+        else:
+            # We are appending to an existing document
+            newdoc = doc
+            try:
+                top_element = newdoc.createElement(FITTING_NODE_NAME)
+            except:
+                string = etree.tostring(doc, pretty_print=True)
+                newdoc = parseString(string)
+                top_element = newdoc.createElement(FITTING_NODE_NAME)
+            if entry_node is None:
+                newdoc.documentElement.appendChild(top_element)
+            else:
+                try:
+                    entry_node.appendChild(top_element)
+                except:
+                    node_name = entry_node.tag
+                    node_list = newdoc.getElementsByTagName(node_name)
+                    entry_node = node_list.item(0)
+                    entry_node.appendChild(top_element)
+                    
+        attr = newdoc.createAttribute("version")
+        attr.nodeValue = '1.0'
+        top_element.setAttributeNode(attr)
+        
+        # File name
+        element = newdoc.createElement("filename")
+        if self.file is not None:
+            element.appendChild(newdoc.createTextNode(str(self.file)))
+        else:
+            element.appendChild(newdoc.createTextNode(str(file)))
+        top_element.appendChild(element)
+        
+        element = newdoc.createElement("timestamp")
+        element.appendChild(newdoc.createTextNode(time.ctime(self.timestamp)))
+        attr = newdoc.createAttribute("epoch")
+        attr.nodeValue = str(self.timestamp)
+        element.setAttributeNode(attr)
+        top_element.appendChild(element)
+        # Inputs
+        inputs = newdoc.createElement("Attributes")
+        top_element.appendChild(inputs)
+       
+        if self.data is not None and hasattr(self.data, "group_id"):
+            self.data_group_id = self.data.group_id
+        if self.data is not None and hasattr(self.data, "is_data"):
+            self.is_data = self.data.is_data
+        if self.data is not None:
+            self.data_name = self.data.name
+        if self.data is not None and hasattr(self.data, "id"):
+            self.data_id = self.data.id
+       
+        for item in list_of_data_attributes:
+            element = newdoc.createElement(item[0])
+            exec "element.setAttribute(item[0], str(self.%s))" % (item[1])
+            inputs.appendChild(element)
+        
+        for item in list_of_state_attributes:
+            element = newdoc.createElement(item[0])
+            exec "element.setAttribute(item[0], str(self.%s))" % (item[1])
+            inputs.appendChild(element)
+            
+        # For self.values ={ disp_param_name: [vals,...],...}
+        # and for self.weights ={ disp_param_name: [weights,...],...}
+        list = {}
+        for item in list_of_model_attributes:
+            element = newdoc.createElement(item[0])
+            exec "list = self.%s" % item[1]
+            for key, value in list.iteritems():
+                sub_element = newdoc.createElement(key)
+                sub_element.setAttribute('name', str(key))
+                for val in value:
+                    com = "sub_element.appendChild"
+                    com += "(newdoc.createTextNode(str(%s)))"
+                    exec com % val
+                   
+                element.appendChild(sub_element)
+            inputs.appendChild(element)
+        
+        # Create doc for the dictionary of self._disp_obj_dic
+        for item in list_of_obj_dic:
+            element = newdoc.createElement(item[0])
+            exec "list = self.%s" % item[1]
+            for key, val in list.iteritems():
+                value = repr(val)
+                sub_element = newdoc.createElement(key)
+                sub_element.setAttribute('name', str(key))
+                sub_element.setAttribute('value', str(value))
+                element.appendChild(sub_element)
+            inputs.appendChild(element)
+                 
+        for item in list_of_state_parameters:
+            element = newdoc.createElement(item[0])
+            com = "self._toXML_helper(thelist=self.%s,"
+            com += " element=element, newdoc=newdoc)"
+            exec com % item[1]
+            inputs.appendChild(element)
+        
+        # Save the file
+        if doc is None:
+            fd = open(file, 'w')
+            fd.write(newdoc.toprettyxml())
+            fd.close()
+            return None
+        else:
+            return newdoc
+        
+    def _fromXML_helper(self, node, list):
+        """
+        Helper function to write state to xml
+        """
+        for item in node:
+            try:
+                name = item.get('name')
+            except:
+                name = None
+            try:
+                value = item.get('value')
+            except:
+                value = None
+            try:
+                selected_to_fit = (item.get('selected_to_fit') == "True")
+            except:
+                selected_to_fit = None
+            try:
+                error_displayed = (item.get('error_displayed') == "True")
+            except:
+                error_displayed = None
+            try:
+                error_value = item.get('error_value')
+            except:
+                error_value = None
+            try:
+                minimum_displayed = (item.get('minimum_displayed') == "True")
+            except:
+                minimum_displayed = None
+            try:
+                minimum_value = item.get('minimum_value')
+            except:
+                minimum_value = None
+            try:
+                maximum_displayed = (item.get('maximum_displayed') == "True")
+            except:
+                maximum_displayed = None
+            try:
+                maximum_value = item.get('maximum_value')
+            except:
+                maximum_value = None
+            try:
+                unit = item.get('unit')
+            except:
+                unit = None
+            list.append([selected_to_fit, name, value, "+/-",
+                         [error_displayed, error_value],
+                         [minimum_displayed, minimum_value],
+                         [maximum_displayed, maximum_value], unit])
+       
+    def fromXML(self, file=None, node=None):
+        """
+        Load fitting state from a file
+        
+        :param file: .fitv file
+        :param node: node of a XML document to read from
+        
+        """
+        if file is not None:
+            msg = "PageState no longer supports non-CanSAS"
+            msg += " format for fitting files"
+            raise RuntimeError, msg
+            
+        if node.get('version')and node.get('version') == '1.0':
+            
+            # Get file name
+            entry = get_content('ns:filename', node)
+            if entry is not None:
+                self.file = entry.text.strip()
+                
+            # Get time stamp
+            entry = get_content('ns:timestamp', node)
+            if entry is not None and entry.get('epoch'):
+                try:
+                    self.timestamp = float(entry.get('epoch'))
+                except:
+                    msg = "PageState.fromXML: Could not"
+                    msg += " read timestamp\n %s" % sys.exc_value
+                    logging.error(msg)
+            
+            # Parse fitting attributes
+            entry = get_content('ns:Attributes', node)
+            for item in list_of_data_attributes:
+                node = get_content('ns:%s' % item[0], entry)
+                try:
+                    exec "self.%s = parse_entry_helper(node, item)" % item[0]
+                    
+                except:
+                    raise
+
+            if entry is not None:
+                
+                for item in list_of_state_attributes:
+                    node = get_content('ns:%s' % item[0], entry)
+                    try:
+                        exec "self.%s = parse_entry_helper(node, item)" % \
+                                                                str(item[0])
+                    except:
+                        raise
+                    
+                for item in list_of_state_parameters:
+                    node = get_content("ns:%s" % item[0], entry)
+                    exec "self._fromXML_helper(node=node, list=self.%s)" % \
+                                                                    item[1]
+                
+                # Recover _disp_obj_dict from xml file
+                self._disp_obj_dict = {}
+                disp_model = None
+                for item in list_of_obj_dic:
+                    # Get node
+                    node = get_content("ns:%s" % item[0], entry)
+                    for attr in node:
+                        name = attr.get('name')
+                        val = attr.get('value')
+                        value = val.split(" instance")[0]
+                        disp_name = value.split("<")[1]
+                        try:
+                            # Try to recover disp_model object from strings
+                            com = "from sas.models.dispersion_models "
+                            com += "import %s as disp"
+                            com_name = disp_name.split(".")[3]
+                            exec com % com_name
+                            disp_model = disp()
+                            exec "self.%s['%s'] = com_name" % (item[1], name)
+                        except:
+                            pass
+                        
+                # get self.values and self.weights dic. if exists
+                for item in list_of_model_attributes:
+                    node = get_content("ns:%s" % item[0], entry)
+                    dic = {}
+                    list = []
+                    for par in node:
+                        name = par.get('name')
+                        values = par.text.split('\n')
+                        # Get lines only with numbers
+                        for line in values:
+                            try:
+                                val = float(line)
+                                list.append(val)
+                            except:
+                                # pass if line is empty (it happens)
+                                pass
+                    dic[name] = numpy.array(list)
+                    exec "self.%s = dic" % item[1]
+                    
+    def set_plot_state(self, figs, canvases):
+        """
+        Build image state that wx.html understand
+        by plotting, putting it into wx.FileSystem image object
+
+        """
+        images = []
+        # some imports
+        import wx
+
+        # Reset memory
+        self.imgRAM = None
+        wx.MemoryFSHandler()
+        
+        # For no figures in the list, prepare empty plot
+        if figs == None or len(figs) == 0:
+            figs = [None]
+            
+        # Loop over the list of figures
+        # use wx.MemoryFSHandler
+        self.imgRAM = wx.MemoryFSHandler()
+        for fig in figs:
+            if figs != None:
+                ind = figs.index(fig)
+                canvas = canvases[ind]
+                
+            #store the image in wx.FileSystem Object 
+            wx.FileSystem.AddHandler(wx.MemoryFSHandler())
+            
+            # index of the fig
+            ind = figs.index(fig)
+            
+            #AddFile, image can be retrieved with 'memory:filename'
+            self.imgRAM.AddFile('img_fit%s.png' % ind,
+                                canvas.bitmap, wx.BITMAP_TYPE_PNG)
+            
+            #append figs
+            images.append(fig)
+            
+        return images
+
+
+class Reader(CansasReader):
+    """
+    Class to load a .fitv fitting file
+    """
+    ## File type
+    type_name = "Fitting"
+    
+    ## Wildcards
+    type = ["Fitting files (*.fitv)|*.fitv"
+            "SANSView file (*.svs)|*.svs"]
+    ## List of allowed extensions
+    ext = ['.fitv', '.FITV', '.svs', 'SVS']
+    
+    def __init__(self, call_back=None, cansas=True):
+        CansasReader.__init__(self)
+        """
+        Initialize the call-back method to be called
+        after we load a file
+        
+        :param call_back: call-back method
+        :param cansas:  True = files will be written/read in CanSAS format
+                        False = write CanSAS format
+            
+        """
+        ## Call back method to be executed after a file is read
+        self.call_back = call_back
+        ## CanSAS format flag
+        self.cansas = cansas
+        self.state = None
+        
+    def get_state(self):
+        return self.state
+        
+    def read(self, path):
+        """
+        Load a new P(r) inversion state from file
+        
+        :param path: file path
+        
+        """
+        if self.cansas == True:
+            return self._read_cansas(path)
+     
+    def _data2d_to_xml_doc(self, datainfo):
+        """
+        Create an XML document to contain the content of a Data2D
+        
+        :param datainfo: Data2D object
+        
+        """
+        if not issubclass(datainfo.__class__, Data2D):
+            raise RuntimeError, "The cansas writer expects a Data2D instance"
+        
+        doc = xml.dom.minidom.Document()
+        main_node = doc.createElement("SASroot")
+        main_node.setAttribute("version", self.version)
+        main_node.setAttribute("xmlns", "cansas1d/%s" % self.version)
+        main_node.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance")
+        main_node.setAttribute("xsi:schemaLocation", "cansas1d/%s http://svn.smallangles.net/svn/canSAS/1dwg/trunk/cansas1d.xsd" % self.version)
+        
+        doc.appendChild(main_node)
+        
+        entry_node = doc.createElement("SASentry")
+        main_node.appendChild(entry_node)
+       
+        write_node(doc, entry_node, "Title", datainfo.title)
+        if datainfo is not None:
+            write_node(doc, entry_node, "data_class", datainfo.__class__.__name__)
+        for item in datainfo.run:
+            runname = {}
+            if datainfo.run_name.has_key(item) and len(str(datainfo.run_name[item]))>1:
+                runname = {'name': datainfo.run_name[item] }
+            write_node(doc, entry_node, "Run", item, runname)
+        # Data info
+        new_node = doc.createElement("SASdata")
+        entry_node.appendChild(new_node)
+        for item in list_of_data_2d_attr:
+            element = doc.createElement(item[0])
+            exec "element.setAttribute(item[0], str(datainfo.%s))" % (item[1])
+            new_node.appendChild(element)
+            
+        for item in list_of_data2d_values:
+            root_node = doc.createElement(item[0])
+            new_node.appendChild(root_node)
+            temp_list = None
+            exec "temp_list = datainfo.%s" % item[1]
+
+            if temp_list is None or len(temp_list)== 0:
+                element = doc.createElement(item[0])
+                exec "element.appendChild(doc.createTextNode(str(%s)))" % temp_list
+                root_node.appendChild(element)
+            else:
+                for value in temp_list:
+                    element = doc.createElement(item[0])
+                    exec "element.setAttribute(item[0], str(%s))" % value
+                    root_node.appendChild(element)
+       
+        # Sample info
+        sample = doc.createElement("SASsample")
+        if datainfo.sample.name is not None:
+            sample.setAttribute("name", str(datainfo.sample.name))
+        entry_node.appendChild(sample)
+        write_node(doc, sample, "ID", str(datainfo.sample.ID))
+        write_node(doc, sample, "thickness", datainfo.sample.thickness,
+                   {"unit": datainfo.sample.thickness_unit})
+        write_node(doc, sample, "transmission", datainfo.sample.transmission)
+        write_node(doc, sample, "temperature", datainfo.sample.temperature,
+                   {"unit": datainfo.sample.temperature_unit})
+        
+        for item in datainfo.sample.details:
+            write_node(doc, sample, "details", item)
+        
+        pos = doc.createElement("position")
+        written = write_node(doc, pos, "x", datainfo.sample.position.x,
+                             {"unit": datainfo.sample.position_unit})
+        written = written | write_node(doc, pos, "y",
+                                       datainfo.sample.position.y,
+                                       {"unit": datainfo.sample.position_unit})
+        written = written | write_node(doc, pos, "z",
+                                       datainfo.sample.position.z,
+                                       {"unit": datainfo.sample.position_unit})
+        if written == True:
+            sample.appendChild(pos)
+        
+        ori = doc.createElement("orientation")
+        written = write_node(doc, ori, "roll", datainfo.sample.orientation.x,
+                             {"unit": datainfo.sample.orientation_unit})
+        written = written | write_node(doc, ori, "pitch",
+                                       datainfo.sample.orientation.y,
+                                       {"unit": datainfo.sample.orientation_unit})
+        written = written | write_node(doc, ori, "yaw",
+                                       datainfo.sample.orientation.z,
+                                       {"unit": datainfo.sample.orientation_unit})
+        if written == True:
+            sample.appendChild(ori)
+        
+        # Instrument info
+        instr = doc.createElement("SASinstrument")
+        entry_node.appendChild(instr)
+        
+        write_node(doc, instr, "name", datainfo.instrument)
+        
+        #   Source
+        source = doc.createElement("SASsource")
+        if datainfo.source.name is not None:
+            source.setAttribute("name", str(datainfo.source.name))
+        instr.appendChild(source)
+        
+        write_node(doc, source, "radiation", datainfo.source.radiation)
+        write_node(doc, source, "beam_shape", datainfo.source.beam_shape)
+        size = doc.createElement("beam_size")
+        if datainfo.source.beam_size_name is not None:
+            size.setAttribute("name", str(datainfo.source.beam_size_name))
+        written = write_node(doc, size, "x", datainfo.source.beam_size.x,
+                             {"unit": datainfo.source.beam_size_unit})
+        written = written | write_node(doc, size, "y",
+                                       datainfo.source.beam_size.y,
+                                       {"unit": datainfo.source.beam_size_unit})
+        written = written | write_node(doc, size, "z",
+                                       datainfo.source.beam_size.z,
+                                       {"unit": datainfo.source.beam_size_unit})
+        if written == True:
+            source.appendChild(size)
+            
+        write_node(doc, source, "wavelength", datainfo.source.wavelength,
+                   {"unit": datainfo.source.wavelength_unit})
+        write_node(doc, source, "wavelength_min",
+                   datainfo.source.wavelength_min,
+                   {"unit": datainfo.source.wavelength_min_unit})
+        write_node(doc, source, "wavelength_max",
+                   datainfo.source.wavelength_max,
+                   {"unit": datainfo.source.wavelength_max_unit})
+        write_node(doc, source, "wavelength_spread",
+                   datainfo.source.wavelength_spread,
+                   {"unit": datainfo.source.wavelength_spread_unit})
+        
+        #   Collimation
+        for item in datainfo.collimation:
+            coll = doc.createElement("SAScollimation")
+            if item.name is not None:
+                coll.setAttribute("name", str(item.name))
+            instr.appendChild(coll)
+            
+            write_node(doc, coll, "length", item.length,
+                       {"unit": item.length_unit})
+            
+            for apert in item.aperture:
+                ap = doc.createElement("aperture")
+                if apert.name is not None:
+                    ap.setAttribute("name", str(apert.name))
+                if apert.type is not None:
+                    ap.setAttribute("type", str(apert.type))
+                coll.appendChild(ap)
+                
+                write_node(doc, ap, "distance", apert.distance,
+                           {"unit": apert.distance_unit})
+                
+                size = doc.createElement("size")
+                if apert.size_name is not None:
+                    size.setAttribute("name", str(apert.size_name))
+                written = write_node(doc, size, "x", apert.size.x,
+                                     {"unit": apert.size_unit})
+                written = written | write_node(doc, size, "y", apert.size.y,
+                                               {"unit": apert.size_unit})
+                written = written | write_node(doc, size, "z", apert.size.z,
+                                               {"unit": apert.size_unit})
+                if written == True:
+                    ap.appendChild(size)
+
+        #   Detectors
+        for item in datainfo.detector:
+            det = doc.createElement("SASdetector")
+            written = write_node(doc, det, "name", item.name)
+            written = written | write_node(doc, det, "SDD", item.distance,
+                                           {"unit": item.distance_unit})
+            written = written | write_node(doc, det, "slit_length",
+                                           item.slit_length,
+                                           {"unit": item.slit_length_unit})
+            if written == True:
+                instr.appendChild(det)
+            
+            off = doc.createElement("offset")
+            written = write_node(doc, off, "x", item.offset.x,
+                                 {"unit": item.offset_unit})
+            written = written | write_node(doc, off, "y", item.offset.y,
+                                           {"unit": item.offset_unit})
+            written = written | write_node(doc, off, "z", item.offset.z,
+                                           {"unit": item.offset_unit})
+            if written == True:
+                det.appendChild(off)
+            
+            center = doc.createElement("beam_center")
+            written = write_node(doc, center, "x", item.beam_center.x,
+                                 {"unit": item.beam_center_unit})
+            written = written | write_node(doc, center, "y",
+                                           item.beam_center.y,
+                                           {"unit": item.beam_center_unit})
+            written = written | write_node(doc, center, "z",
+                                           item.beam_center.z,
+                                           {"unit": item.beam_center_unit})
+            if written == True:
+                det.appendChild(center)
+                
+            pix = doc.createElement("pixel_size")
+            written = write_node(doc, pix, "x", item.pixel_size.x,
+                                 {"unit": item.pixel_size_unit})
+            written = written | write_node(doc, pix, "y", item.pixel_size.y,
+                                           {"unit": item.pixel_size_unit})
+            written = written | write_node(doc, pix, "z", item.pixel_size.z,
+                                           {"unit": item.pixel_size_unit})
+            if written == True:
+                det.appendChild(pix)
+                
+            ori = doc.createElement("orientation")
+            written = write_node(doc, ori, "roll", item.orientation.x,
+                                 {"unit": item.orientation_unit})
+            written = written | write_node(doc, ori, "pitch",
+                                           item.orientation.y,
+                                           {"unit": item.orientation_unit})
+            written = written | write_node(doc, ori, "yaw", item.orientation.z,
+                                           {"unit": item.orientation_unit})
+            if written == True:
+                det.appendChild(ori)
+                
+        # Processes info
+        for item in datainfo.process:
+            node = doc.createElement("SASprocess")
+            entry_node.appendChild(node)
+
+            write_node(doc, node, "name", item.name)
+            write_node(doc, node, "date", item.date)
+            write_node(doc, node, "description", item.description)
+            for term in item.term:
+                value = term['value']
+                del term['value']
+                write_node(doc, node, "term", value, term)
+            for note in item.notes:
+                write_node(doc, node, "SASprocessnote", note)
+        # Return the document, and the SASentry node associated with
+        # the data we just wrote
+        return doc, entry_node
+   
+    def _parse_state(self, entry):
+        """
+        Read a fit result from an XML node
+        
+        :param entry: XML node to read from
+        
+        :return: PageState object
+        """
+        # Create an empty state
+        state = None   
+        # Locate the P(r) node
+        try:
+            nodes = entry.xpath('ns:%s' % FITTING_NODE_NAME,
+                                namespaces={'ns': CANSAS_NS})
+            if nodes != []:
+                # Create an empty state
+                state = PageState()
+                state.fromXML(node=nodes[0])
+                
+        except:
+            logging.info("XML document does not contain fitting information.\n %s" % sys.exc_value)
+            
+        return state
+      
+    def _parse_save_state_entry(self, dom):
+        """
+        Parse a SASentry
+        
+        :param node: SASentry node
+        
+        :return: Data1D/Data2D object
+        
+        """
+        node = dom.xpath('ns:data_class', namespaces={'ns': CANSAS_NS})
+        if not node or node[0].text.lstrip().rstrip() != "Data2D":
+            return_value, _ = self._parse_entry(dom)
+            numpy.trim_zeros(return_value.x)
+            numpy.trim_zeros(return_value.y)
+            numpy.trim_zeros(return_value.dy)
+            size_dx = return_value.dx.size
+            size_dxl = return_value.dxl.size
+            size_dxw = return_value.dxw.size
+            if size_dxl == 0 and size_dxw == 0:
+                return_value.dxl = None
+                return_value.dxw = None
+                numpy.trim_zeros(return_value.dx)
+            elif size_dx == 0:
+                return_value.dx = None
+                size_dx = size_dxl
+                numpy.trim_zeros(return_value.dxl)
+                numpy.trim_zeros(return_value.dxw)
+                            
+            return return_value, _
+        
+        #Parse 2D
+        data_info = Data2D()
+        
+        # Look up title
+        self._store_content('ns:Title', dom, 'title', data_info)
+        
+        # Look up run number
+        nodes = dom.xpath('ns:Run', namespaces={'ns': CANSAS_NS})
+        for item in nodes:
+            if item.text is not None:
+                value = item.text.strip()
+                if len(value) > 0:
+                    data_info.run.append(value)
+                    if item.get('name') is not None:
+                        data_info.run_name[value] = item.get('name')
+                           
+        # Look up instrument name
+        self._store_content('ns:SASinstrument/ns:name', dom,
+                            'instrument', data_info)
+
+        # Notes
+        note_list = dom.xpath('ns:SASnote', namespaces={'ns': CANSAS_NS})
+        for note in note_list:
+            try:
+                if note.text is not None:
+                    note_value = note.text.strip()
+                    if len(note_value) > 0:
+                        data_info.notes.append(note_value)
+            except:
+                err_mess = "cansas_reader.read: error processing entry notes\n  %s" % sys.exc_value
+                self.errors.append(err_mess)
+                logging.error(err_mess)
+        
+        # Sample info ###################
+        entry = get_content('ns:SASsample', dom)
+        if entry is not None:
+            data_info.sample.name = entry.get('name')
+            
+        self._store_content('ns:SASsample/ns:ID',
+                     dom, 'ID', data_info.sample)
+        self._store_float('ns:SASsample/ns:thickness',
+                     dom, 'thickness', data_info.sample)
+        self._store_float('ns:SASsample/ns:transmission',
+                     dom, 'transmission', data_info.sample)
+        self._store_float('ns:SASsample/ns:temperature',
+                     dom, 'temperature', data_info.sample)
+        
+        nodes = dom.xpath('ns:SASsample/ns:details',
+                          namespaces={'ns': CANSAS_NS})
+        for item in nodes:
+            try:
+                if item.text is not None:
+                    detail_value = item.text.strip()
+                    if len(detail_value) > 0:
+                        data_info.sample.details.append(detail_value)
+            except:
+                err_mess = "cansas_reader.read: error processing sample details\n  %s" % sys.exc_value
+                self.errors.append(err_mess)
+                logging.error(err_mess)
+        
+        # Position (as a vector)
+        self._store_float('ns:SASsample/ns:position/ns:x',
+                     dom, 'position.x', data_info.sample)
+        self._store_float('ns:SASsample/ns:position/ns:y',
+                     dom, 'position.y', data_info.sample)
+        self._store_float('ns:SASsample/ns:position/ns:z',
+                     dom, 'position.z', data_info.sample)
+        
+        # Orientation (as a vector)
+        self._store_float('ns:SASsample/ns:orientation/ns:roll',
+                     dom, 'orientation.x', data_info.sample)
+        self._store_float('ns:SASsample/ns:orientation/ns:pitch',
+                     dom, 'orientation.y', data_info.sample)
+        self._store_float('ns:SASsample/ns:orientation/ns:yaw',
+                     dom, 'orientation.z', data_info.sample)
+       
+        # Source info ###################
+        entry = get_content('ns:SASinstrument/ns:SASsource', dom)
+        if entry is not None:
+            data_info.source.name = entry.get('name')
+        
+        self._store_content('ns:SASinstrument/ns:SASsource/ns:radiation',
+                     dom, 'radiation', data_info.source)
+        self._store_content('ns:SASinstrument/ns:SASsource/ns:beam_shape',
+                     dom, 'beam_shape', data_info.source)
+        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength',
+                     dom, 'wavelength', data_info.source)
+        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_min',
+                     dom, 'wavelength_min', data_info.source)
+        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_max',
+                     dom, 'wavelength_max', data_info.source)
+        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_spread',
+                     dom, 'wavelength_spread', data_info.source)
+        
+        # Beam size (as a vector)
+        entry = get_content('ns:SASinstrument/ns:SASsource/ns:beam_size', dom)
+        if entry is not None:
+            data_info.source.beam_size_name = entry.get('name')
+            
+        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:x',
+                     dom, 'beam_size.x', data_info.source)
+        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:y',
+                     dom, 'beam_size.y', data_info.source)
+        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:z',
+                     dom, 'beam_size.z', data_info.source)
+        
+        # Collimation info ###################
+        nodes = dom.xpath('ns:SASinstrument/ns:SAScollimation',
+                          namespaces={'ns': CANSAS_NS})
+        for item in nodes:
+            collim = Collimation()
+            if item.get('name') is not None:
+                collim.name = item.get('name')
+            self._store_float('ns:length', item, 'length', collim)
+            
+            # Look for apertures
+            apert_list = item.xpath('ns:aperture',
+                                    namespaces={'ns': CANSAS_NS})
+            for apert in apert_list:
+                aperture = Aperture()
+                
+                # Get the name and type of the aperture
+                aperture.name = apert.get('name')
+                aperture.type = apert.get('type')
+                    
+                self._store_float('ns:distance', apert, 'distance', aperture)
+                
+                entry = get_content('ns:size', apert)
+                if entry is not None:
+                    aperture.size_name = entry.get('name')
+                
+                self._store_float('ns:size/ns:x', apert, 'size.x', aperture)
+                self._store_float('ns:size/ns:y', apert, 'size.y', aperture)
+                self._store_float('ns:size/ns:z', apert, 'size.z', aperture)
+                
+                collim.aperture.append(aperture)
+                
+            data_info.collimation.append(collim)
+        
+        # Detector info ######################
+        nodes = dom.xpath('ns:SASinstrument/ns:SASdetector',
+                          namespaces={'ns': CANSAS_NS})
+        for item in nodes:
+            
+            detector = Detector()
+            
+            self._store_content('ns:name', item, 'name', detector)
+            self._store_float('ns:SDD', item, 'distance', detector)
+            
+            # Detector offset (as a vector)
+            self._store_float('ns:offset/ns:x', item, 'offset.x', detector)
+            self._store_float('ns:offset/ns:y', item, 'offset.y', detector)
+            self._store_float('ns:offset/ns:z', item, 'offset.z', detector)
+            
+            # Detector orientation (as a vector)
+            self._store_float('ns:orientation/ns:roll', item,
+                              'orientation.x', detector)
+            self._store_float('ns:orientation/ns:pitch', item,
+                              'orientation.y', detector)
+            self._store_float('ns:orientation/ns:yaw', item,
+                              'orientation.z', detector)
+            
+            # Beam center (as a vector)
+            self._store_float('ns:beam_center/ns:x', item,
+                              'beam_center.x', detector)
+            self._store_float('ns:beam_center/ns:y', item,
+                              'beam_center.y', detector)
+            self._store_float('ns:beam_center/ns:z', item,
+                              'beam_center.z', detector)
+            
+            # Pixel size (as a vector)
+            self._store_float('ns:pixel_size/ns:x', item,
+                              'pixel_size.x', detector)
+            self._store_float('ns:pixel_size/ns:y', item,
+                              'pixel_size.y', detector)
+            self._store_float('ns:pixel_size/ns:z', item,
+                              'pixel_size.z', detector)
+            
+            self._store_float('ns:slit_length', item, 'slit_length', detector)
+            
+            data_info.detector.append(detector)
+
+        # Processes info ######################
+        nodes = dom.xpath('ns:SASprocess', namespaces={'ns': CANSAS_NS})
+        for item in nodes:
+            process = Process()
+            self._store_content('ns:name', item, 'name', process)
+            self._store_content('ns:date', item, 'date', process)
+            self._store_content('ns:description', item, 'description', process)
+            
+            term_list = item.xpath('ns:term', namespaces={'ns': CANSAS_NS})
+            for term in term_list:
+                try:
+                    term_attr = {}
+                    for attr in term.keys():
+                        term_attr[attr] = term.get(attr).strip()
+                    if term.text is not None:
+                        term_attr['value'] = term.text.strip()
+                        process.term.append(term_attr)
+                except:
+                    err_mess = "cansas_reader.read: error processing process term\n  %s" % sys.exc_value
+                    self.errors.append(err_mess)
+                    logging.error(err_mess)
+            
+            note_list = item.xpath('ns:SASprocessnote',
+                                   namespaces={'ns': CANSAS_NS})
+            for note in note_list:
+                if note.text is not None:
+                    process.notes.append(note.text.strip())
+            
+            data_info.process.append(process)
+            
+        # Data info ######################
+        nodes = dom.xpath('ns:SASdata', namespaces={'ns': CANSAS_NS})
+        if len(nodes) > 1:
+            raise RuntimeError, "CanSAS reader is not compatible with multiple SASdata entries"
+       
+        for entry in nodes:
+            for item in list_of_data_2d_attr:
+                #get node
+                node = get_content('ns:%s' % item[0], entry)
+                exec "data_info.%s = parse_entry_helper(node, item)" % item[1]
+                    
+            for item in list_of_data2d_values:
+                field = get_content('ns:%s' % item[0], entry)
+                list = []
+                if field is not None:
+                    list = [parse_entry_helper(node, item) for node in field]
+                if len(list) < 2:
+                    exec "data_info.%s = None" % item[0]
+                else:
+                    exec "data_info.%s = numpy.array(list)" % item[0]
+        
+        return data_info
+
+    def _read_cansas(self, path):
+        """
+        Load data and P(r) information from a CanSAS XML file.
+        
+        :param path: file path
+        
+        :return: Data1D object if a single SASentry was found,
+                    or a list of Data1D objects if multiple entries were found,
+                    or None of nothing was found
+                    
+        :raise RuntimeError: when the file can't be opened
+        :raise ValueError: when the length of the data vectors are inconsistent
+        
+        """
+        output = []
+        basename = os.path.basename(path)
+        root, extension = os.path.splitext(basename)
+        ext = extension.lower()
+        try:
+            if os.path.isfile(path):
+                
+                #TODO: eventually remove the check for .xml once
+                # the P(r) writer/reader is truly complete.
+                if  ext in self.ext or \
+                    ext == '.xml':
+                    
+                    tree = etree.parse(path, parser=etree.ETCompatXMLParser())
+                    # Check the format version number
+                    # Specifying the namespace will take care of the file format version
+                    root = tree.getroot()
+                    entry_list = root.xpath('ns:SASentry',
+                                            namespaces={'ns': CANSAS_NS})
+                    for entry in entry_list:
+                        try:
+                            sas_entry, _ = self._parse_save_state_entry(entry)
+                        except:
+                            raise
+                        fitstate = self._parse_state(entry)
+                        
+                        #state could be None when .svs file is loaded
+                        #in this case, skip appending to output
+                        if fitstate != None:
+                            sas_entry.meta_data['fitstate'] = fitstate
+                            sas_entry.filename = fitstate.file
+                            output.append(sas_entry)
+            else:
+                self.call_back(format=ext)
+                raise RuntimeError, "%s is not a file" % path
+
+            # Return output consistent with the loader's api
+            if len(output) == 0:
+                self.call_back(state=None, datainfo=None, format=ext)
+                return None
+            else:
+                for ind in range(len(output)):
+                    # Call back to post the new state
+                    state = output[ind].meta_data['fitstate']
+                    t = time.localtime(state.timestamp)
+                    time_str = time.strftime("%b %d %H:%M", t)
+                    # Check that no time stamp is already appended
+                    max_char = state.file.find("[")
+                    if max_char < 0:
+                        max_char = len(state.file)
+                    original_fname = state.file[0:max_char]
+                    state.file = original_fname + ' [' + time_str + ']'
+                     
+                    if state is not None and state.is_data is not None:
+                        exec 'output[%d].is_data = state.is_data' % ind
+                     
+                    output[ind].filename = state.file
+                    state.data = output[ind]
+                    state.data.name = output[ind].filename  # state.data_name
+                    state.data.id = state.data_id
+                    if state.is_data is not None:
+                        state.data.is_data = state.is_data
+                    if output[ind].run_name is not None\
+                        and len(output[ind].run_name) != 0:
+                        name = output[ind].run_name
+                    else:
+                        name = original_fname
+                    state.data.group_id = name
+                    #store state in fitting
+                    self.call_back(state=state,
+                                   datainfo=output[ind], format=ext)
+                    self.state = state
+                return output
+              
+        except:
+            self.call_back(format=ext)
+            #self.state= state
+            raise
+           
+    def write(self, filename, datainfo=None, fitstate=None):
+        """
+        Write the content of a Data1D as a CanSAS XML file only for standalone
+        
+        :param filename: name of the file to write
+        :param datainfo: Data1D object
+        :param fitstate: PageState object
+        
+        """
+        # Sanity check
+        if self.cansas == True:
+            # Add fitting information to the XML document
+            doc = self.write_toXML(datainfo, fitstate)
+            # Write the XML document
+        else:
+            doc = fitstate.toXML(file=filename)
+        
+        # Save the document no matter the type
+        fd = open(filename, 'w')
+        fd.write(doc.toprettyxml())
+        fd.close()
+        
+    def write_toXML(self, datainfo=None, state=None):
+        """
+        Write toXML, a helper for write(),
+        could be used by guimanager._on_save()
+        
+        : return: xml doc
+        """
+
+        if state.data is None:
+            data = sas.dataloader.data_info.Data1D(x=[], y=[])
+            return None
+        elif not state.data.is_data:
+            return None
+        else:
+            #make sure title and data run is filled up.
+            if state.data.title == None or state.data.title == '':
+                state.data.title = state.data.name
+            if state.data.run_name == None or state.data.run_name == {}:
+                state.data.run = [str(state.data.name)]
+                state.data.run_name[0] = state.data.name
+            
+            if issubclass(state.data.__class__,
+                          sas.dataloader.data_info.Data1D):
+                data = state.data
+                doc, sasentry = self._to_xml_doc(data)
+            else:
+                data = state.data
+                doc, sasentry = self._data2d_to_xml_doc(data)
+            
+        if state is not None:
+            doc = state.toXML(doc=doc, file=data.filename, entry_node=sasentry)
+        
+        return doc
+    
+# Simple html report templet
+HEADER = "<html>\n"
+HEADER += "<head>\n"
+HEADER += "<meta http-equiv=Content-Type content='text/html; "
+HEADER += "charset=windows-1252'> \n"
+HEADER += "<meta name=Generator >\n"
+HEADER += "</head>\n"
+HEADER += "<body lang=EN-US>\n"
+HEADER += "<div class=WordSection1>\n"
+HEADER += "<p class=MsoNormal><b><span ><center><font size='4' >"
+HEADER += "%s</font></center></span></center></b></p>"
+HEADER += "<p class=MsoNormal>&nbsp;</p>"
+PARA = "<p class=MsoNormal><font size='4' > %s \n"
+PARA += "</font></p>"
+CENTRE = "<p class=MsoNormal><center><font size='4' > %s \n"
+CENTRE += "</font></center></p>"
+FEET_1 = \
+"""
+<p class=MsoNormal>&nbsp;</p>
+<br>
+<p class=MsoNormal><b><span ><center> <font size='4' > Graph
+</font></span></center></b></p>
+<p class=MsoNormal>&nbsp;</p>
+<center>
+<br><font size='4' >Model Computation</font>
+<br><font size='4' >Data: "%s"</font><br>
+"""
+FEET_2 = \
+"""
+<img src="%s" >
+</img>
+"""
+FEET_3 = \
+"""
+</center>
+</div>
+</body>
+</html>
+"""
+ELINE = "<p class=MsoNormal>&nbsp;</p>"
+
+if __name__ == "__main__":
+    state = PageState(parent=None)
+    #state.toXML()
+    """
+    
+    file = open("test_state", "w")
+    pickle.dump(state, file)
+    print pickle.dumps(state)
+    state.data_name = "hello---->"
+    pickle.dump(state, file)
+    file = open("test_state", "r")
+    new_state= pickle.load(file)
+    print "new state", new_state
+    new_state= pickle.load(file)
+    print "new state", new_state
+    #print "state", state
+    """
+    import bsddb
+    import pickle
+    db = bsddb.btopen('file_state.db', 'c')
+    val = (pickle.dumps(state), "hello", "hi")
+    db['state1'] = pickle.dumps(val)
+    print pickle.loads(db['state1'])
+    state.data_name = "hello---->22"
+    db['state2'] = pickle.dumps(state)
+    state.data_name = "hello---->2"
+    db['state3'] = pickle.dumps(state)
+    del db['state3']
+    state.data_name = "hello---->3"
+    db['state4'] = pickle.dumps(state)
+    new_state = pickle.loads(db['state1'])
+    #print db.last()
+    db.set_location('state2')
+    state.data_name = "hello---->5"
+    db['aastate5'] = pickle.dumps(state)
+    db.keys().sort()
+    print pickle.loads(db['state2'])
+  
+    db.close()
Index: src/sas/perspectives/fitting/plugin_models/polynomial5.py
===================================================================
--- src/sas/perspectives/fitting/plugin_models/polynomial5.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/fitting/plugin_models/polynomial5.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,117 @@
+"""
+Test plug-in model
+These are links of available functions:
+
+http://docs.python.org/library/math.html
+http://www.scipy.org/Numpy_Functions_by_Category
+"""
+
+"""
+## *****************************************************************************
+Please select the 'Compile' from the menubar after the modification and saving.
+Note that we recommend to save the file as a different file name.
+Otherwise, it could be removed in the future on re-installation of the SansView.
+## *****************************************************************************
+"""
+from sas.models.pluginmodel import Model1DPlugin  ##DO NOT CHANGE THIS LINE!!!
+
+## FOR MORE INFORMATION CHECK http://docs.python.org/library/math.html      
+## AND http://www.scipy.org/Numpy_Functions_by_Category
+import math                    ##DO NOT CHANGE THIS LINE!!!
+import numpy                   ##DO NOT CHANGE THIS LINE!!!
+import os
+import sys
+## <-----  SIGN DEFINES WHERE YOU CAN MODIFY THE CODE
+
+class Model(Model1DPlugin): ##DO NOT CHANGE THIS LINE!!!
+    """
+    ##YOU CAN BE MODIFY ANYTHING BETWEEN """ """
+    ##DESCRIPTION OF MODEL PLUG-IN GOES HERE
+    
+    ##EXAMPLE: Class that evaluates a polynomial model. 
+    """
+    name = ""  
+                                
+    def __init__(self):      ##DO NOT CHANGE THIS LINE!!!
+        """
+        Initialization
+        """
+        Model1DPlugin.__init__(self, name=self.name) ##DO NOT CHANGE THIS LINE!!!
+        
+        ## HERE WE DEFINE THE PARAM NAME AND ITS INITIAL VALUE 
+        ## YOU CAN MODIFY THE LINE BELLOW.CHANGE WORD BETWEEN ' ' AND NUMBER                                                      
+        self.params['A'] = 0.1       ## <-----                
+        self.params['B'] = 10.0      ## <-----                 
+        self.params['C'] = 0.0       ## <-----                
+        self.params['D'] = 0.0       ## <-----                    
+        self.params['E'] = 0.0       ## <-----                  
+        self.params['F'] = 0.0       ## <-----   
+        # Set the name same as the file name
+        self.name = self.get_fname()     ##DO NOT CHANGE THIS LINE!!!
+        ## YOU CAN MODIFY THE LINE BELLOW.MODIFY WORDS BETWEEN """   """  ONLY!!!!
+        self.description = """
+            a + b * x + c * math.pow(x,2) + d * math.pow(x,3) \n
+             + e * math.pow(x,4) + f * math.pow(x,5)  
+        """                        ## <-----   
+        ## DEFINE DEFAULT DETAILS
+        self.set_details()      ##DO NOT DELETE OR CHANGE THIS LINE!!!
+        
+        ## IN THIS EXAMPLE THE FUNTION IS:
+        ## F(x)=A+B*x+C*x^2+D*x^3+E*x^4+F*x^5             
+   
+    def function(self, x = 0.0): ##DO NOT CHANGE THIS LINE!!!
+        """ 
+        Evaluate the model
+        :param x: input x
+        :return: function value
+        """
+        ## DEFINE YOUR FUNCTION HERE.
+        ## YOU CAN ERASE EVERYTHING BELLOW FOR YOUR OWN FUNCTION
+        #Redefine parameters as local parameters, or skip and use long name
+        a = self.params['A']       ## <-----   
+        b = self.params['B']       ## <-----   
+        c = self.params['C']       ## <-----   
+        d = self.params['D']       ## <-----   
+        e = self.params['E']       ## <-----   
+        f = self.params['F']       ## <-----   
+ 
+        ##THIS OUR FUNCTION TEMPLATE
+        poly = a + b * x + c * math.pow(x,2) + d * math.pow(x,3) \
+                + e * math.pow(x,4) + f * math.pow(x,5)              ## <-----                
+        
+        #(Just note: In Python, indentation defines the belongings 
+        # of 'if', 'for', and so on..)
+        #(lim x --> 0) 
+        if x == 0:                          ## <-----   
+            result = a                      ## <-----   
+        else:                               ## <-----   
+            result = poly                   ## <-----    
+
+        return result       ## MODIFY ONLY RESULT. DON'T DELETE RETURN!!!!
+    
+    ## DO NOT MODIFY THE FOLLOWING LINES!!!!!!!!!!!!!!!!       
+    def get_fname(self):
+        """
+        Get the model name same as the file name
+        """
+        path = sys._getframe().f_code.co_filename
+        basename  = os.path.basename(path)
+        name, _ = os.path.splitext(basename)
+        return name
+            
+###############################################################################
+## THIS IS FOR TEST. DO NOT MODIFY THE FOLLOWING LINES!!!!!!!!!!!!!!!!       
+if __name__ == "__main__": 
+    m= Model() 
+    out1 = m.runXY(0.0)
+    out2 = m.runXY(0.01)
+    isfine1 = numpy.isfinite(out1)
+    isfine2 = numpy.isfinite(out2)
+    print "Testing the value at Q = 0.0:"
+    print out1, " : finite? ", isfine1
+    print "Testing the value at Q = 0.01:"
+    print out2, " : finite? ", isfine2
+    if isfine1 and isfine2:
+        print "===> Simple Test: Passed!"
+    else:
+        print "===> Simple Test: Failed!"
Index: src/sas/perspectives/fitting/plugin_models/sph_bessel_jn.py
===================================================================
--- src/sas/perspectives/fitting/plugin_models/sph_bessel_jn.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/fitting/plugin_models/sph_bessel_jn.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,140 @@
+"""
+Test plug-in model
+These are links of available functions:
+
+http://docs.python.org/library/math.html
+http://www.scipy.org/Numpy_Functions_by_Category
+"""
+
+"""
+## *****************************************************************************
+Please select the 'Compile' from the menubar after the modification and saving.
+Note that we recommend to save the file as a different file name.
+Otherwise, it could be removed in the future on re-installation of the SansView.
+## *****************************************************************************
+"""
+
+from sas.models.pluginmodel import Model1DPlugin  ##DO NOT CHANGE THIS LINE!!!
+import math                  ##DO NOT CHANGE THIS LINE!!!
+import numpy                 ##DO NOT CHANGE THIS LINE!!!
+import scipy.special            ##CHANGE THIS LINE WITH CAUTION!!!
+import os
+import sys
+##PLEASE READ COMMENTS CAREFULLY !!! COMMENT ARE IN CAPITAL LETTERS AND AFTER ##
+## THESE COMMENTS ARE THERE TO GUIDE YOU. YOU CAN REMOVE THEM ONLY WHEN YOU ARE
+## CONFORTABLE ENOUGH WITH OUR MODEL PLUGIN OPTION
+
+
+## <-----  SIGN DEFINES WHERE YOU CAN MODIFY THE CODE
+
+class Model(Model1DPlugin): ##DO NOT CHANGE THIS LINE!!!
+    """
+    ##YOU CAN BE MODIFY ANYTHING BETWEEN """ """
+    ##DESCRIPTION OF MODEL PLUG-IN GOES HERE
+    
+    ##EXAMPLE: Class that evaluates a polynomial model. 
+    """
+    name = "" 
+                                
+    def __init__(self):      ##DO NOT CHANGE THIS LINE!!!
+        """
+        Initialization
+        """
+        Model1DPlugin.__init__(self, name=self.name) ##DO NOT CHANGE THIS LINE!!!
+        
+        ## EDIT PARAMETERS' NAMES AND VALUE
+        ## DELETE MODIFIABLE LINE HERE WILL REDUCE THE NUMBER OF PARAMETERS
+        self.params = {}                ##DO NOT CHANGE THIS LINE!!!
+        # Set the name same as the file name
+        self.name = self.get_fname()     ##DO NOT CHANGE THIS LINE!!!
+        ## YOU CAN MODIFY THE LINE BELLOW.CHANGE WORD BETWEEN ' ' AND NUMBER 
+        ## YOU CAN ALSO DELETE THIS LINE
+        self.params['C'] = 1.0   ## <-----   
+        ## YOU CAN MODIFY THE LINE BELLOW.CHANGE WORD BETWEEN ' ' AND NUMBER 
+        ## YOU CAN ALSO DELETE THIS LINE                                                      
+        self.params['A'] = 1.0       ## <-----   
+        ## YOU CAN MODIFY THE LINE BELLOW.CHANGE WORD BETWEEN ' ' AND NUMBER 
+        ## YOU CAN ALSO DELETE THIS LINE                 
+        self.params['B'] = 0.0      ## <-----   
+        ## YOU CAN MODIFY THE LINE BELLOW.CHANGE WORD BETWEEN ' ' AND NUMBER 
+        ## YOU CAN ALSO DELETE THIS LINE                 
+        self.params['D'] = 0.0      ## <-----   
+        ## YOU CAN ALSO DELETE THIS LINE                 
+        self.params['n'] = 1      ## <----- 
+ 
+        ## DEFINE DEFAULT DETAILS
+        self.set_details()      ##DO NOT DELETE OR CHANGE THIS LINE!!!
+        
+        ## YOU CAN MODIFY THE LINE BELLOW.MODIFY WORDS BETWEEN """   """  ONLY!!!!
+        self.description = """
+            Spherical Bessel Function: C*sph_jn(n, Ax+B)+D
+                 """                        ## <-----   
+   
+    def function(self, x = 0.0): ##DO NOT CHANGE THIS LINE!!!
+        """ 
+        Evaluate the model
+        
+        :param x: input x
+        
+        :return: function value
+        
+        """
+        ## ADD YOUR FUNCTION HERE.
+        ## REUSE THE PARAMETERS DEFINED PREVIOUSLY TO WRITE YOUR FUNCTION.
+        
+        ## IN THIS EXAMPLE THE FUNTION IS:
+        ## C*sph_jn(Ax+B)+D
+         
+        ## NOTE: pow IS A FUNCTION IMPORTED FROM PYTHON MATH LIBRARY
+        ## FOR MORE INFORMATION CHECK http://docs.python.org/library/math.html      
+        ## OTHER FUNCTIONS ARE ALSO 
+        ###  AVAILABLE http://www.scipy.org/Numpy_Functions_by_Category
+        ## numpy FUNCTIONS ARE FOR EXPERT USER
+        
+        ## YOU CAN ERASE EVERYTHING BELLOW FOR YOUR OWN FUNCTION
+        #Redefine parameters as local parameters
+        a = self.params['A']       ## <-----   
+        b = self.params['B']       ## <-----   
+        c = self.params['C']       ## <-----   
+        d = self.params['D']       ## <-----   
+        # Take integer value only
+        n = int(self.params['n'])      ## <-----   
+
+        ##THIS OUR FUNCTION TEMPLATE
+        #Remove a singular point (lim poly --> 0) for sin(poly)/poly
+        #(Just note: In Python, indentation defines the belongings of 'if', 'for', and so on..)
+        input = a * x + b
+        # sph_out and _ are in array types from scipy upto n'th order
+        # where _ is not used for this function.
+        sph_out, _ = scipy.special.sph_jn(n, input)
+        # Take only n'th value
+        result = c * sph_out[n] + d      ## <-----                
+
+        return result ## MODIFY ONLY RESULT. DON'T DELETE RETURN!!!!
+    
+    ## DO NOT MODIFY THE FOLLOWING LINES!!!!!!!!!!!!!!!!      
+    def get_fname(self):
+        """
+        Get the model name same as the file name
+        """
+        path = sys._getframe().f_code.co_filename
+        basename  = os.path.basename(path)
+        name, _ = os.path.splitext(basename)
+        return name
+            
+###############################################################################   
+## DO NOT MODIFY THE FOLLOWING LINES!!!!!!!!!!!!!!!!       
+if __name__ == "__main__": 
+    m= Model() 
+    out1 = m.runXY(0.0)
+    out2 = m.runXY(0.01)
+    isfine1 = numpy.isfinite(out1)
+    isfine2 = numpy.isfinite(out2)
+    print "Testing the value at Q = 0.0:"
+    print out1, " : finite? ", isfine1
+    print "Testing the value at Q = 0.01:"
+    print out2, " : finite? ", isfine2
+    if isfine1 and isfine2:
+        print "===> Simple Test: Passed!"
+    else:
+        print "===> Simple Test: Failed!"
Index: src/sas/perspectives/fitting/plugin_models/sum_Ap1_1_Ap2.py
===================================================================
--- src/sas/perspectives/fitting/plugin_models/sum_Ap1_1_Ap2.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/fitting/plugin_models/sum_Ap1_1_Ap2.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,422 @@
+# A sample of an experimental model function for Sum(APmodel1,(1-A)Pmodel2)
+import copy
+from sas.models.pluginmodel import Model1DPlugin
+import os
+import sys
+"""
+## *****************************************************************************
+Please select the 'Compile' from the menubar after the modification and saving.
+Note that we recommend to save the file as a different file name.
+Otherwise, it could be removed in the future on re-installation of the SansView.
+## *****************************************************************************
+"""
+
+# Available model names for this sum model
+"""
+BCCrystalModel, BEPolyelectrolyte, BarBellModel, BinaryHSModel, BroadPeakModel,
+CSParallelepipedModel, CappedCylinderModel, CoreShellCylinderModel,
+CoreShellEllipsoidModel, CoreShellModel, CorrLengthModel, CylinderModel, 
+DABModel, DebyeModel, EllipsoidModel, EllipticalCylinderModel, FCCrystalModel,
+FlexCylEllipXModel, FlexibleCylinderModel, FractalCoreShellModel, FractalModel,
+FuzzySphereModel, GaussLorentzGelModel, GuinierModel, GuinierPorodModel,
+HardsphereStructure, HayterMSAStructure, HollowCylinderModel, LamellarFFHGModel,
+LamellarModel, LamellarPCrystalModel, LamellarPSHGModel, LamellarPSModel,
+LineModel, LorentzModel, MultiShellModel, ParallelepipedModel, PeakGaussModel,
+PeakLorentzModel, PearlNecklaceModel, Poly_GaussCoil, PolymerExclVolume,
+PorodModel, PowerLawAbsModel, SCCrystalModel, SphereModel, SquareWellStructure,
+StackedDisksModel, StickyHSStructure, TeubnerStreyModel, TriaxialEllipsoidModel,
+TwoLorentzianModel, TwoPowerLawModel, VesicleModel
+"""
+## This model is DIFFERENT from the Easy Custom Sum(p1 + p2) 
+## by definition of the scale factor *****************************************
+#
+#     Custom model = scale_factor * P1 + (1 - scale_factor) * P2
+#
+## User can REPLACE model names below arrowed two lines (twice per line)
+from sas.models.CylinderModel import CylinderModel as P1          #<========
+from sas.models.PolymerExclVolume import PolymerExclVolume as P2  #<========
+
+#####DO NOT CHANGE ANYTHING BELOW THIS LINE 
+#####---------------------------------------------------------------------------
+class Model(Model1DPlugin):
+    """
+    Use for A*p1(Q)+(1-A)*p2(Q); 
+    Note: P(Q) refers to 'form factor' model.
+    """
+    name = ""  
+    def __init__(self):
+        Model1DPlugin.__init__(self, name=self.name)
+        """
+        :param p_model1: a form factor, P(Q)
+        :param p_model2: another form factor, P(Q)
+        """
+        p_model1 = P1()
+        p_model2 = P2()
+        ## Setting  model name model description
+        self.description = ""
+        self.fill_description(p_model1, p_model2)
+        # Set the name same as the file name
+        self.name = self.get_fname()     ##DO NOT CHANGE THIS LINE!!!
+        ## Define parameters
+        self.params = {}
+
+        ## Parameter details [units, min, max]
+        self.details = {}
+        ## Magnetic Panrameters
+        self.magnetic_params = []
+        
+        # non-fittable parameters
+        self.non_fittable = p_model1.non_fittable  
+        self.non_fittable += p_model2.non_fittable  
+            
+        ##models 
+        self.p_model1= p_model1
+        self.p_model2= p_model2
+        
+       
+        ## dispersion
+        self._set_dispersion()
+        ## Define parameters
+        self._set_params()
+        ## New parameter:Scaling factor
+        self.params['scale_factor'] = 0.5
+        
+        ## Parameter details [units, min, max]
+        self._set_details()
+        self.details['scale_factor'] = ['',     None, None]
+
+        
+        #list of parameter that can be fitted
+        self._set_fixed_params()  
+        ## parameters with orientation
+        for item in self.p_model1.orientation_params:
+            new_item = "p1_" + item
+            if not new_item in self.orientation_params:
+                self.orientation_params.append(new_item)
+            
+        for item in self.p_model2.orientation_params:
+            new_item = "p2_" + item
+            if not new_item in self.orientation_params:
+                self.orientation_params.append(new_item)
+        ## magnetic params
+        for item in self.p_model1.magnetic_params:
+            new_item = "p1_" + item
+            if not new_item in self.magnetic_params:
+                self.magnetic_params.append(new_item)
+            
+        for item in self.p_model2.magnetic_params:
+            new_item = "p2_" + item
+            if not new_item in self.magnetic_params:
+                self.magnetic_params.append(new_item)
+        # set multiplicity 1: muti_func Not supported.
+        multiplicity1 = 1
+        multiplicity2 = 1
+        ## functional multiplicity of the model
+        self.multiplicity1 = multiplicity1  
+        self.multiplicity2 = multiplicity2    
+        self.multiplicity_info = []   
+        
+    def _clone(self, obj):
+        """
+            Internal utility function to copy the internal
+            data members to a fresh copy.
+        """
+        obj.params     = copy.deepcopy(self.params)
+        obj.description     = copy.deepcopy(self.description)
+        obj.details    = copy.deepcopy(self.details)
+        obj.dispersion = copy.deepcopy(self.dispersion)
+        obj.p_model1  = self.p_model1.clone()
+        obj.p_model2  = self.p_model2.clone()
+        #obj = copy.deepcopy(self)
+        return obj
+    
+    def _get_name(self, name1, name2):
+        """
+        Get combined name from two model names
+        """
+        name = "A*"
+        p1_name = self._get_upper_name(name1)
+        if not p1_name:
+            p1_name = name1
+        name += p1_name
+        name += "+(1-A)*"
+        p2_name = self._get_upper_name(name2)
+        if not p2_name:
+            p2_name = name2
+        name += p2_name
+        return name
+    
+    def _get_upper_name(self, name=None):
+        """
+        Get uppercase string from model name
+        """
+        if name == None:
+            return ""
+        upper_name = ""
+        str_name = str(name)
+        for index in range(len(str_name)):
+            if str_name[index].isupper():
+                upper_name += str_name[index]
+        return upper_name
+        
+    def _set_dispersion(self):
+        """
+           combined the two models dispersions
+           Polydispersion should not be applied to s_model
+        """
+        ##set dispersion only from p_model 
+        for name , value in self.p_model1.dispersion.iteritems():
+            #if name.lower() not in self.p_model1.orientation_params:
+            new_name = "p1_" + name
+            self.dispersion[new_name]= value 
+        for name , value in self.p_model2.dispersion.iteritems():
+            #if name.lower() not in self.p_model2.orientation_params:
+            new_name = "p2_" + name
+            self.dispersion[new_name]= value 
+            
+    def function(self, x=0.0): 
+        """
+        """
+        return 0
+                               
+    def getProfile(self):
+        """
+        Get SLD profile of p_model if exists
+        
+        : return: (r, beta) where r is a list of radius of the transition points
+                beta is a list of the corresponding SLD values 
+        : Note: This works only for func_shell# = 2 (exp function)
+                and is not supporting for p2
+        """
+        try:
+            x,y = self.p_model1.getProfile()
+        except:
+            x = None
+            y = None
+            
+        return x, y
+    
+    def _set_params(self):
+        """
+            Concatenate the parameters of the two models to create
+            this model parameters 
+        """
+
+        for name , value in self.p_model1.params.iteritems():
+            # No 2D-supported
+            #if name not in self.p_model1.orientation_params:
+            new_name = "p1_" + name
+            self.params[new_name]= value
+            
+        for name , value in self.p_model2.params.iteritems():
+            # No 2D-supported
+            #if name not in self.p_model2.orientation_params:
+            new_name = "p2_" + name
+            self.params[new_name]= value
+                
+        # Set "scale" as initializing
+        self._set_scale_factor()
+      
+            
+    def _set_details(self):
+        """
+            Concatenate details of the two models to create
+            this model details 
+        """
+        for name ,detail in self.p_model1.details.iteritems():
+            new_name = "p1_" + name
+            #if new_name not in self.orientation_params:
+            self.details[new_name]= detail
+            
+        for name ,detail in self.p_model2.details.iteritems():
+            new_name = "p2_" + name
+            #if new_name not in self.orientation_params:
+            self.details[new_name]= detail
+    
+    def _set_scale_factor(self):
+        """
+        Not implemented
+        """
+        pass
+        
+                
+    def setParam(self, name, value):
+        """ 
+        Set the value of a model parameter
+    
+        :param name: name of the parameter
+        :param value: value of the parameter
+        """
+        # set param to p1+p2 model
+        self._setParamHelper(name, value)
+        
+        ## setParam to p model 
+        model_pre = name.split('_', 1)[0]
+        new_name = name.split('_', 1)[1]
+        if model_pre == "p1":
+            if new_name in self.p_model1.getParamList():
+                self.p_model1.setParam(new_name, value)
+        elif model_pre == "p2":
+             if new_name in self.p_model2.getParamList():
+                self.p_model2.setParam(new_name, value)
+        elif name.lower() == 'scale_factor':
+            self.params['scale_factor'] = value
+        else:
+            raise ValueError, "Model does not contain parameter %s" % name
+            
+    def getParam(self, name):
+        """ 
+        Set the value of a model parameter
+
+        :param name: name of the parameter
+        
+        """
+        # Look for dispersion parameters
+        toks = name.split('.')
+        if len(toks)==2:
+            for item in self.dispersion.keys():
+                # 2D not supported
+                if item.lower()==toks[0].lower():# and \
+                            #item.lower() not in self.orientation_params \
+                            #and toks[0].lower() not in self.orientation_params:
+                    for par in self.dispersion[item]:
+                        if par.lower() == toks[1].lower():
+                            return self.dispersion[item][par]
+        else:
+            # Look for standard parameter
+            for item in self.params.keys():
+                if item.lower()==name.lower():#and \
+                            #item.lower() not in self.orientation_params \
+                            #and toks[0].lower() not in self.orientation_params:
+                    return self.params[item]
+        return  
+        #raise ValueError, "Model does not contain parameter %s" % name
+       
+    def _setParamHelper(self, name, value):
+        """
+        Helper function to setparam
+        """
+        # Look for dispersion parameters
+        toks = name.split('.')
+        if len(toks)== 2:
+            for item in self.dispersion.keys():
+                if item.lower()== toks[0].lower():# and \
+                            #item.lower() not in self.orientation_params:
+                    for par in self.dispersion[item]:
+                        if par.lower() == toks[1].lower():#and \
+                                #item.lower() not in self.orientation_params:
+                            self.dispersion[item][par] = value
+                            return
+        else:
+            # Look for standard parameter
+            for item in self.params.keys():
+                if item.lower()== name.lower():#and \
+                            #item.lower() not in self.orientation_params:
+                    self.params[item] = value
+                    return
+            
+        raise ValueError, "Model does not contain parameter %s" % name
+             
+   
+    def _set_fixed_params(self):
+        """
+             fill the self.fixed list with the p_model fixed list
+        """
+        for item in self.p_model1.fixed:
+            new_item = "p1" + item
+            self.fixed.append(new_item)
+        for item in self.p_model2.fixed:
+            new_item = "p2" + item
+            self.fixed.append(new_item)
+
+        self.fixed.sort()
+                
+                    
+    def run(self, x = 0.0):
+        """ 
+        Evaluate the model
+        
+        :param x: input q-value (float or [float, float] as [r, theta])
+        :return: (scattering function value)
+        """
+        self._set_scale_factor()
+        return (self.params['scale_factor'] * self.p_model1.run(x) + \
+                (1 - self.params['scale_factor']) * self.p_model2.run(x))
+    
+    def runXY(self, x = 0.0):
+        """ 
+        Evaluate the model
+        
+        :param x: input q-value (float or [float, float] as [qx, qy])
+        :return: scattering function value
+        """  
+        self._set_scale_factor()
+        return (self.params['scale_factor'] * self.p_model1.runXY(x) + \
+                (1 - self.params['scale_factor']) * self.p_model2.runXY(x))
+    
+    ## Now (May27,10) directly uses the model eval function 
+    ## instead of the for-loop in Base Component.
+    def evalDistribution(self, x = []):
+        """ 
+        Evaluate the model in cartesian coordinates
+        
+        :param x: input q[], or [qx[], qy[]]
+        :return: scattering function P(q[])
+        """
+        self._set_scale_factor()
+        return (self.params['scale_factor'] * self.p_model1.evalDistribution(x) + \
+                (1 - self.params['scale_factor']) * self.p_model2.evalDistribution(x))
+
+    def set_dispersion(self, parameter, dispersion):
+        """
+        Set the dispersion object for a model parameter
+        
+        :param parameter: name of the parameter [string]
+        :dispersion: dispersion object of type DispersionModel
+        """
+        value= None
+        new_pre = parameter.split("_", 1)[0]
+        new_parameter = parameter.split("_", 1)[1]
+        try:
+            if new_pre == 'p1' and \
+                            new_parameter in self.p_model1.dispersion.keys():
+                value= self.p_model1.set_dispersion(new_parameter, dispersion)
+            if new_pre == 'p2' and \
+                             new_parameter in self.p_model2.dispersion.keys():
+                value= self.p_model2.set_dispersion(new_parameter, dispersion)
+            self._set_dispersion()
+            return value
+        except:
+            raise 
+
+    def fill_description(self, p_model1, p_model2):
+        """
+        Fill the description for P(Q)+P(Q)
+        """
+        description = ""
+        description +="This model gives the summation of (A*%s) and ((1-A)*%s).\n"% \
+                                        ( p_model1.name, p_model2.name )
+        self.description += description
+    
+    ## DO NOT MODIFY THE FOLLOWING LINES!!!!!!!!!!!!!!!!       
+    def get_fname(self):
+        """
+        Get the model name same as the file name
+        """
+        path = sys._getframe().f_code.co_filename
+        basename  = os.path.basename(path)
+        name, _ = os.path.splitext(basename)
+        return name
+            
+### FOR TEST                
+if __name__ == "__main__": 
+    m1= Model() 
+    out1 = m1.runXY(0.01)
+    m2= Model()
+    out2 = 0.5 * m2.p_model1.runXY(0.01) + 0.5 * m2.p_model2.runXY(0.01)
+    print "Testing at Q = 0.01:"
+    print out1, " = ", out2
+    if out1 == out2:
+        print "===> Simple Test: Passed!"
+    else:
+        print "===> Simple Test: Failed!"
Index: src/sas/perspectives/fitting/plugin_models/sum_p1_p2.py
===================================================================
--- src/sas/perspectives/fitting/plugin_models/sum_p1_p2.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/fitting/plugin_models/sum_p1_p2.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,437 @@
+# A sample of an experimental model function for Sum(Pmodel1,Pmodel2)
+import copy
+from sas.models.pluginmodel import Model1DPlugin
+import os
+import sys
+"""
+## *****************************************************************************
+Please select the 'Compile' from the menubar after the modification and saving.
+Note that we recommend to save the file as a different file name.
+Otherwise, it could be removed in the future on re-installation of the SansView.
+## *****************************************************************************
+"""
+
+# Available model names for this sum model
+"""
+BCCrystalModel, BEPolyelectrolyte, BarBellModel, BinaryHSModel, BroadPeakModel,
+CSParallelepipedModel, CappedCylinderModel, CoreShellCylinderModel,
+CoreShellEllipsoidModel, CoreShellModel, CorrLengthModel, CylinderModel, 
+DABModel, DebyeModel, EllipsoidModel, EllipticalCylinderModel, FCCrystalModel,
+FlexCylEllipXModel, FlexibleCylinderModel, FractalCoreShellModel, FractalModel,
+FuzzySphereModel, GaussLorentzGelModel, GuinierModel, GuinierPorodModel,
+HardsphereStructure, HayterMSAStructure, HollowCylinderModel, LamellarFFHGModel,
+LamellarModel, LamellarPCrystalModel, LamellarPSHGModel, LamellarPSModel,
+LineModel, LorentzModel, MultiShellModel, ParallelepipedModel, PeakGaussModel,
+PeakLorentzModel, PearlNecklaceModel, Poly_GaussCoil, PolymerExclVolume,
+PorodModel, PowerLawAbsModel, SCCrystalModel, SphereModel, SquareWellStructure,
+StackedDisksModel, StickyHSStructure, TeubnerStreyModel, TriaxialEllipsoidModel,
+TwoLorentzianModel, TwoPowerLawModel, VesicleModel
+"""
+## This is same as the Easy Custom Sum(p1 + p2) 
+#
+#     Custom model = scale_factor * (P1 + P2)
+#
+## User can REPLACE model names below two arrowed lines (two names per line)
+from sas.models.CylinderModel import CylinderModel as P1          #<========
+from sas.models.PolymerExclVolume import PolymerExclVolume as P2  #<========
+
+# If you want to add your custom model, change the filename to your custom model
+# FILE NAME without the extension(.py). And un-comment the line(ie., remove '#')
+
+# from filename import Model as P2      #<========
+
+
+#####DO NOT CHANGE ANYTHING BELOW THIS LINE 
+#####---------------------------------------------------------------------------
+class Model(Model1DPlugin):
+    """
+    Use for p1(Q)+p2(Q); 
+    Note: P(Q) refers to 'form factor' model.
+    """
+    name = "" 
+    def __init__(self):
+        Model1DPlugin.__init__(self, name=self.name)
+        """
+        :param p_model1: a form factor, P(Q)
+        :param p_model2: another form factor, P(Q)
+        """
+        p_model1 = P1()
+        p_model2 = P2()
+        ## Setting  model name model description
+        self.description=""
+        
+        self.description = p_model1.name+"\n"
+        self.description += p_model2.name+"\n"
+        self.fill_description(p_model1, p_model2)
+        # Set the name same as the file name
+        self.name = self.get_fname()     ##DO NOT CHANGE THIS LINE!!!
+        ## Define parameters
+        self.params = {}
+
+        ## Parameter details [units, min, max]
+        self.details = {}
+        
+        # non-fittable parameters
+        self.non_fittable = p_model1.non_fittable  
+        self.non_fittable += p_model2.non_fittable  
+            
+        ##models 
+        self.p_model1= p_model1
+        self.p_model2= p_model2
+        
+       
+        ## dispersion
+        self._set_dispersion()
+        ## Define parameters
+        self._set_params()
+        ## New parameter:Scaling factor
+        self.params['scale_factor'] = 1
+        
+        ## Parameter details [units, min, max]
+        self._set_details()
+        self.details['scale_factor'] = ['',  None, None]
+        ## Magnetic Panrameters
+        self.magnetic_params = []
+        
+        #list of parameter that can be fitted
+        self._set_fixed_params()  
+        ## parameters with orientation
+        for item in self.p_model1.orientation_params:
+            new_item = "p1_" + item
+            if not new_item in self.orientation_params:
+                self.orientation_params.append(new_item)
+            
+        for item in self.p_model2.orientation_params:
+            new_item = "p2_" + item
+            if not new_item in self.orientation_params:
+                self.orientation_params.append(new_item)
+        ## magnetic params
+        for item in self.p_model1.magnetic_params:
+            new_item = "p1_" + item
+            if not new_item in self.magnetic_params:
+                self.magnetic_params.append(new_item)
+            
+        for item in self.p_model2.magnetic_params:
+            new_item = "p2_" + item
+            if not new_item in self.magnetic_params:
+                self.magnetic_params.append(new_item)
+        # set multiplicity 1: muti_func Not supported.
+        multiplicity1 = 1
+        multiplicity2 = 1
+        ## functional multiplicity of the model
+        self.multiplicity1 = multiplicity1  
+        self.multiplicity2 = multiplicity2    
+        self.multiplicity_info = []   
+        
+    def _clone(self, obj):
+        """
+            Internal utility function to copy the internal
+            data members to a fresh copy.
+        """
+        obj.params     = copy.deepcopy(self.params)
+        obj.description     = copy.deepcopy(self.description)
+        obj.details    = copy.deepcopy(self.details)
+        obj.dispersion = copy.deepcopy(self.dispersion)
+        obj.p_model1  = self.p_model1.clone()
+        obj.p_model2  = self.p_model2.clone()
+        #obj = copy.deepcopy(self)
+        return obj
+    
+    def _get_name(self, name1, name2):
+        """
+        Get combined name from two model names
+        """
+        p1_name = self._get_upper_name(name1)
+        if not p1_name:
+            p1_name = name1
+        name = p1_name
+        name += "+"
+        p2_name = self._get_upper_name(name2)
+        if not p2_name:
+            p2_name = name2
+        name += p2_name
+        return name
+    
+    def _get_upper_name(self, name=None):
+        """
+        Get uppercase string from model name
+        """
+        if name == None:
+            return ""
+        upper_name = ""
+        str_name = str(name)
+        for index in range(len(str_name)):
+            if str_name[index].isupper():
+                upper_name += str_name[index]
+        return upper_name
+        
+    def _set_dispersion(self):
+        """
+           combined the two models dispersions
+           Polydispersion should not be applied to s_model
+        """
+        ##set dispersion only from p_model 
+        for name , value in self.p_model1.dispersion.iteritems():
+            #if name.lower() not in self.p_model1.orientation_params:
+            new_name = "p1_" + name
+            self.dispersion[new_name]= value 
+        for name , value in self.p_model2.dispersion.iteritems():
+            #if name.lower() not in self.p_model2.orientation_params:
+            new_name = "p2_" + name
+            self.dispersion[new_name]= value 
+            
+    def function(self, x=0.0): 
+        """
+        """
+        return 0
+                               
+    def getProfile(self):
+        """
+        Get SLD profile of p_model if exists
+        
+        : return: (r, beta) where r is a list of radius of the transition points
+                beta is a list of the corresponding SLD values 
+        : Note: This works only for func_shell# = 2 (exp function)
+                and is not supporting for p2
+        """
+        try:
+            x,y = self.p_model1.getProfile()
+        except:
+            x = None
+            y = None
+            
+        return x, y
+    
+    def _set_params(self):
+        """
+            Concatenate the parameters of the two models to create
+            this model parameters 
+        """
+
+        for name , value in self.p_model1.params.iteritems():
+            # No 2D-supported
+            #if name not in self.p_model1.orientation_params:
+            new_name = "p1_" + name
+            self.params[new_name]= value
+            
+        for name , value in self.p_model2.params.iteritems():
+            # No 2D-supported
+            #if name not in self.p_model2.orientation_params:
+            new_name = "p2_" + name
+            self.params[new_name]= value
+                
+        # Set "scale" as initializing
+        self._set_scale_factor()
+      
+            
+    def _set_details(self):
+        """
+            Concatenate details of the two models to create
+            this model details 
+        """
+        for name ,detail in self.p_model1.details.iteritems():
+            new_name = "p1_" + name
+            #if new_name not in self.orientation_params:
+            self.details[new_name]= detail
+            
+        for name ,detail in self.p_model2.details.iteritems():
+            new_name = "p2_" + name
+            #if new_name not in self.orientation_params:
+            self.details[new_name]= detail
+    
+    def _set_scale_factor(self):
+        """
+        Not implemented
+        """
+        pass
+        
+                
+    def setParam(self, name, value):
+        """ 
+        Set the value of a model parameter
+    
+        :param name: name of the parameter
+        :param value: value of the parameter
+        """
+        # set param to p1+p2 model
+        self._setParamHelper(name, value)
+        
+        ## setParam to p model 
+        model_pre = name.split('_', 1)[0]
+        new_name = name.split('_', 1)[1]
+        if model_pre == "p1":
+            if new_name in self.p_model1.getParamList():
+                self.p_model1.setParam(new_name, value)
+        elif model_pre == "p2":
+             if new_name in self.p_model2.getParamList():
+                self.p_model2.setParam(new_name, value)
+        elif name.lower() == 'scale_factor':
+            self.params['scale_factor'] = value
+        else:
+            raise ValueError, "Model does not contain parameter %s" % name
+            
+    def getParam(self, name):
+        """ 
+        Set the value of a model parameter
+
+        :param name: name of the parameter
+        
+        """
+        # Look for dispersion parameters
+        toks = name.split('.')
+        if len(toks)==2:
+            for item in self.dispersion.keys():
+                # 2D not supported
+                if item.lower()==toks[0].lower():# and \
+                            #item.lower() not in self.orientation_params \
+                            #and toks[0].lower() not in self.orientation_params:
+                    for par in self.dispersion[item]:
+                        if par.lower() == toks[1].lower():
+                            return self.dispersion[item][par]
+        else:
+            # Look for standard parameter
+            for item in self.params.keys():
+                if item.lower()==name.lower():#and \
+                            #item.lower() not in self.orientation_params \
+                            #and toks[0].lower() not in self.orientation_params:
+                    return self.params[item]
+        return  
+        #raise ValueError, "Model does not contain parameter %s" % name
+       
+    def _setParamHelper(self, name, value):
+        """
+        Helper function to setparam
+        """
+        # Look for dispersion parameters
+        toks = name.split('.')
+        if len(toks)== 2:
+            for item in self.dispersion.keys():
+                if item.lower()== toks[0].lower():# and \
+                            #item.lower() not in self.orientation_params:
+                    for par in self.dispersion[item]:
+                        if par.lower() == toks[1].lower():#and \
+                                #item.lower() not in self.orientation_params:
+                            self.dispersion[item][par] = value
+                            return
+        else:
+            # Look for standard parameter
+            for item in self.params.keys():
+                if item.lower()== name.lower():#and \
+                            #item.lower() not in self.orientation_params:
+                    self.params[item] = value
+                    return
+            
+        raise ValueError, "Model does not contain parameter %s" % name
+             
+   
+    def _set_fixed_params(self):
+        """
+             fill the self.fixed list with the p_model fixed list
+        """
+        for item in self.p_model1.fixed:
+            new_item = "p1" + item
+            self.fixed.append(new_item)
+        for item in self.p_model2.fixed:
+            new_item = "p2" + item
+            self.fixed.append(new_item)
+
+        self.fixed.sort()
+                
+                    
+    def run(self, x = 0.0):
+        """ 
+        Evaluate the model
+        
+        :param x: input q-value (float or [float, float] as [r, theta])
+        :return: (scattering function value)
+        """
+        self._set_scale_factor()
+        return self.params['scale_factor'] * \
+                (self.p_model1.run(x) + self.p_model2.run(x))
+    
+    def runXY(self, x = 0.0):
+        """ 
+        Evaluate the model
+        
+        :param x: input q-value (float or [float, float] as [qx, qy])
+        :return: scattering function value
+        """  
+        self._set_scale_factor()
+        return self.params['scale_factor'] * \
+                (self.p_model1.runXY(x) + self.p_model2.runXY(x))
+    
+    ## Now (May27,10) directly uses the model eval function 
+    ## instead of the for-loop in Base Component.
+    def evalDistribution(self, x = []):
+        """ 
+        Evaluate the model in cartesian coordinates
+        
+        :param x: input q[], or [qx[], qy[]]
+        :return: scattering function P(q[])
+        """
+        self._set_scale_factor()
+        return self.params['scale_factor'] * \
+                    (self.p_model1.evalDistribution(x) + \
+                                 self.p_model2.evalDistribution(x))
+
+    def set_dispersion(self, parameter, dispersion):
+        """
+        Set the dispersion object for a model parameter
+        
+        :param parameter: name of the parameter [string]
+        :dispersion: dispersion object of type DispersionModel
+        """
+        value= None
+        new_pre = parameter.split("_", 1)[0]
+        new_parameter = parameter.split("_", 1)[1]
+        try:
+            if new_pre == 'p1' and \
+                            new_parameter in self.p_model1.dispersion.keys():
+                value= self.p_model1.set_dispersion(new_parameter, dispersion)
+            if new_pre == 'p2' and \
+                             new_parameter in self.p_model2.dispersion.keys():
+                value= self.p_model2.set_dispersion(new_parameter, dispersion)
+            self._set_dispersion()
+            return value
+        except:
+            raise 
+
+    def fill_description(self, p_model1, p_model2):
+        """
+        Fill the description for P(Q)+P(Q)
+        """
+        description = ""
+        description +="This model gives the summation of  %s and %s.\n"% \
+                                        ( p_model1.name, p_model2.name )
+        self.description += description
+    
+    ## DO NOT MODIFY THE FOLLOWING LINES!!!!!!!!!!!!!!!!       
+    def get_fname(self):
+        """
+        Get the model name same as the file name
+        """
+        path = sys._getframe().f_code.co_filename
+        basename  = os.path.basename(path)
+        name, _ = os.path.splitext(basename)
+        return name
+            
+if __name__ == "__main__": 
+    m1= Model() 
+    #m1.setParam("p1_scale", 25)  
+    #m1.setParam("p1_length", 1000)
+    #m1.setParam("p2_scale", 100) 
+    #m1.setParam("p2_rg", 100) 
+    out1 = m1.runXY(0.01)
+
+    m2= Model()
+    #m2.p_model1.setParam("scale", 25) 
+    #m2.p_model1.setParam("length", 1000) 
+    #m2.p_model2.setParam("scale", 100)
+    #m2.p_model2.setParam("rg", 100)
+    out2 = m2.p_model1.runXY(0.01) + m2.p_model2.runXY(0.01)
+    print "Testing at Q = 0.01:"
+    print out1, " = ", out2
+    if out1 == out2:
+        print "===> Simple Test: Passed!"
+    else:
+        print "===> Simple Test: Failed!"
Index: src/sas/perspectives/fitting/plugin_models/testmodel.py
===================================================================
--- src/sas/perspectives/fitting/plugin_models/testmodel.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/fitting/plugin_models/testmodel.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,112 @@
+"""
+Test plug-in model
+These are links of available functions:
+
+http://docs.python.org/library/math.html
+http://www.scipy.org/Numpy_Functions_by_Category
+"""
+
+"""
+## *****************************************************************************
+Please select the 'Compile' from the menubar after the modification and saving.
+Note that we recommend to save the file as a different file name.
+Otherwise, it could be removed in the future on re-installation of the SansView.
+## *****************************************************************************
+"""
+
+from sas.models.pluginmodel import Model1DPlugin  ##DO NOT CHANGE THIS LINE!!!
+import math                  ##DO NOT CHANGE THIS LINE!!!
+import numpy               ##DO NOT CHANGE THIS LINE!!!
+import os
+import sys
+##PLEASE READ COMMENTS CAREFULLY !!! COMMENT ARE IN CAPITAL LETTERS AND AFTER ##
+## THESE COMMENTS ARE THERE TO GUIDE YOU. YOU CAN REMOVE THEM ONLY WHEN YOU ARE
+## CONFORTABLE ENOUGH WITH OUR MODEL PLUGIN OPTION
+
+
+## <-----  SIGN DEFINES WHERE YOU CAN MODIFY THE CODE
+
+class Model(Model1DPlugin):##DO NOT CHANGE THIS LINE!!!
+    """
+    ##YOU CAN BE MODIFY ANYTHING BETWEEN """ """
+    ##DESCRIPTION OF MODEL PLUG-IN GOES HERE
+    
+    ##EXAMPLE:Class that evaluates a cos(x) model. 
+    """
+    name = ""     
+    
+    def __init__(self):
+        """
+        Initialization
+        """
+        Model1DPlugin.__init__(self, name= self.name)##DO NOT CHANGE THIS LINE!!!
+        
+        ## EDIT PARAMETERS' NAMES AND VALUE
+        ## DELETE MODIFIABLE LINE HERE WILL REDUCE THE NUMBER OF PARAMETERS
+        self.params = {}                        ##DO NOT CHANGE THIS LINE!!!
+        # Set the name same as the file name
+        self.name = self.get_fname()     ##DO NOT CHANGE THIS LINE!!!
+        ## YOU CAN MODIFY THE LINE BELLOW.CHANGE WORD BETWEEN ' ' AND NUMBER 
+        ## YOU CAN ALSO DELETE THIS LINE
+        self.params['A'] = 1.0       ## <-----  
+        ## YOU CAN MODIFY THE LINE BELLOW.CHANGE WORD BETWEEN ' ' AND NUMBER 
+        ## YOU CAN ALSO DELETE THIS LINE   
+        self.params['B'] = 1.0       ## <----- 
+        ## YOU CAN MODIFY THE LINE BELLOW.CHANGE WORD BETWEEN ' ' AND NUMBER 
+        ## YOU CAN ALSO DELETE THIS LINE    
+        self.params['C'] = 10.0      ## <-----  
+
+        ## DEFINE DEFAULT DETAILS
+        self.set_details()      ##DO NOT DELETE OR CHANGE THIS LINE!!!
+        
+        ## YOU CAN MODIFY THE LINE BELLOW.MODIFY WORDS BETWEEN """   """  ONLY!!!!
+        self.description = "F(x)=A+Bcos(2x)+Csin(2x) "     ## <----- 
+   
+    def function(self, x = 0.0):  ##DO NOT CHANGE THIS LINE!!!
+        """
+        Evaluate the model
+        
+        :param x: input x
+        
+        :return: function value
+        
+        """
+        ## ADD YOUR FUNCTION HERE.
+        ## REUSE THE PARAMETERS DEFINED PREVIOUSLY TO WRITE YOUR FUNCTION.
+        
+        ## IN THIS EXAMPLE THE FUNTION IS:
+        ## A+Bcos(2x)+Csin(2x)
+        ## YOU CAN USE math.sin or sin directly
+        ## NOTE: sin, cos ARE FUNCTIONS  IMPORTED FROM PYTHON MATH LIBRARY
+        ## FOR MORE INFORMATION CHECK http://docs.python.org/library/math.html      
+        ## OTHER FUNCTIONS ARE ALSO 
+        ##  AVAILABLE http://www.scipy.org/Numpy_Functions_by_Category
+        ## numpy FUNCTIONS ARE FOR EXPERT USER
+        
+        return self.params['A']+self.params['B']*math.cos(2.0*x)+self.params['C']*math.sin(2.0*x)
+    
+    ## DO NOT MODIFY THE FOLLOWING LINES!!!!!!!!!!!!!!!!   
+    def get_fname(self):
+        """
+        Get the model name same as the file name
+        """
+        path = sys._getframe().f_code.co_filename
+        basename  = os.path.basename(path)
+        name, _ = os.path.splitext(basename)
+        return name
+    
+## DO NOT MODIFY THE FOLLOWING LINES!!!!!!!!!!!!!!!!       
+if __name__ == "__main__": 
+    m= Model() 
+    out1 = m.runXY(0.0)
+    out2 = m.runXY(0.01)
+    isfine1 = numpy.isfinite(out1)
+    isfine2 = numpy.isfinite(out2)
+    print "Testing the value at Q = 0.0:"
+    print out1, " : finite? ", isfine1
+    print "Testing the value at Q = 0.01:"
+    print out2, " : finite? ", isfine2
+    if isfine1 and isfine2:
+        print "===> Simple Test: Passed!"
+    else:
+        print "===> Simple Test: Failed!"
Index: src/sas/perspectives/fitting/plugin_models/testmodel_2.py
===================================================================
--- src/sas/perspectives/fitting/plugin_models/testmodel_2.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/fitting/plugin_models/testmodel_2.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,175 @@
+"""
+Test plug-in model
+These are links of available functions:
+
+http://docs.python.org/library/math.html
+http://www.scipy.org/Numpy_Functions_by_Category
+"""
+
+"""
+## *****************************************************************************
+Please select the 'Compile' from the menubar after the modification and saving.
+Note that we recommend to save the file as a different file name.
+Otherwise, it could be removed in the future on re-installation of the SansView.
+## *****************************************************************************
+"""
+
+from sas.models.pluginmodel import Model1DPlugin  ##DO NOT CHANGE THIS LINE!!!
+import math                     ##DO NOT CHANGE THIS LINE!!!
+import numpy                    ##DO NOT CHANGE THIS LINE!!!
+import os
+import sys
+##PLEASE READ COMMENTS CAREFULLY !!! COMMENT ARE IN CAPITAL LETTERS AND AFTER ##
+## THESE COMMENTS ARE THERE TO GUIDE YOU. YOU CAN REMOVE THEM ONLY WHEN YOU ARE
+## CONFORTABLE ENOUGH WITH OUR MODEL PLUGIN OPTION
+
+
+## <-----  SIGN DEFINES WHERE YOU CAN MODIFY THE CODE
+
+class Model(Model1DPlugin): ##DO NOT CHANGE THIS LINE!!!
+    """
+    ##YOU CAN BE MODIFY ANYTHING BETWEEN """ """
+    ##DESCRIPTION OF MODEL PLUG-IN GOES HERE
+    
+    ##EXAMPLE: Class that evaluates a polynomial model. 
+    """
+    name = "" 
+                                
+    def __init__(self):      ##DO NOT CHANGE THIS LINE!!!
+        """
+        Initialization
+        """
+        Model1DPlugin.__init__(self, name=self.name) ##DO NOT CHANGE THIS LINE!!!
+        
+        ## EDIT PARAMETERS' NAMES AND VALUE
+        ## DELETE MODIFIABLE LINE HERE WILL REDUCE THE NUMBER OF PARAMETERS
+        self.params = {}                ##DO NOT CHANGE THIS LINE!!!
+        # Set the name same as the file name
+        self.name = self.get_fname()     ##DO NOT CHANGE THIS LINE!!!
+        ## YOU CAN MODIFY THE LINE BELLOW.CHANGE WORD BETWEEN ' ' AND NUMBER 
+        ## YOU CAN ALSO DELETE THIS LINE
+        self.params['scale'] = 1.0   ## <-----   
+        ## YOU CAN MODIFY THE LINE BELLOW.CHANGE WORD BETWEEN ' ' AND NUMBER 
+        ## YOU CAN ALSO DELETE THIS LINE                                                      
+        self.params['A'] = 0.0       ## <-----   
+        ## YOU CAN MODIFY THE LINE BELLOW.CHANGE WORD BETWEEN ' ' AND NUMBER 
+        ## YOU CAN ALSO DELETE THIS LINE                 
+        self.params['B'] = 10.0      ## <-----   
+        ## YOU CAN MODIFY THE LINE BELLOW.CHANGE WORD BETWEEN ' ' AND NUMBER 
+        ## YOU CAN ALSO DELETE THIS LINE                  
+        self.params['C'] = 0.0       ## <-----   
+        ## YOU CAN MODIFY THE LINE BELLOW.CHANGE WORD BETWEEN ' ' AND NUMBER 
+        ## YOU CAN ALSO DELETE THIS LINE                
+        self.params['D'] = 0.0     ## <-----   
+        ## YOU CAN MODIFY THE LINE BELLOW.CHANGE WORD BETWEEN ' ' AND NUMBER 
+        ## YOU CAN ALSO DELETE THIS LINE                   
+        self.params['E'] = 0.0     ## <-----   
+        ## YOU CAN MODIFY THELINE BELLOW.CHANGE WORD BETWEEN ' ' AND NUMBER 
+        ## YOU CAN ALSO DELETE THIS LINE                  
+        self.params['F'] = 0.0     ## <-----          
+
+        ## STORING PARAMETERS  [UNIT, MINIMUM VALUE, MAXIMUM VALUE]
+        self.details = {}    ##DO NOT CHANGE THIS LINE!!!
+        ## YOU CAN MODIFY THE LINE BELLOW.CHANGE WORD BETWEEN ' ',WORD BETWEEN
+        ## ' ', TWO OTHER NUMBESR TO NEW VALUE OR YOU CAN ALSO DELETE TH LINE      
+        self.details['scale'] = ['',None, None]    ## <-----   
+        ## YOU CAN MODIFY THE LINE BELLOW.CHANGE WORD BETWEEN ' ',WORD BETWEEN
+        ## ' ', TWO OTHER NUMBERS TO NEW VALUE OR YOU CAN ALSO DELETE TH LINE     
+        self.details['A'] = ['', None, None]        ## <-----   
+        ## YOU CAN MODIFY THE LINE BELLOW.CHANGE WORD BETWEEN ' ',WORD BETWEEN
+        ## ' ', TWO OTHER NUMBERS TO NEW VALUE OR YOU CAN ALSO DELETE TH LINE     
+        self.details['B'] = ['', None, None]        ## <-----   
+        ## YOU CAN MODIFY THE LINE BELLOW.CHANGE WORD BETWEEN ' ',WORD BETWEEN
+        ## ' ', TWO OTHER NUMBERS TO NEW VALUE OR YOU CAN ALSO DELETE TH LINE     
+        self.details['C'] = ['', None, None]        ## <-----   
+        ## YOU CAN MODIFY THE LINE BELLOW.CHANGE WORD BETWEEN ' ',WORD BETWEEN
+        ## ' ', TWO OTHER NUMBERS TO NEW VALUE OR YOU CAN ALSO DELETE TH LINE     
+        self.details['D'] = ['', None, None]        ## <-----   
+        ## YOU CAN MODIFY THE LINE BELLOW.CHANGE WORD BETWEEN ' ',WORD BETWEEN
+        ## ' ', TWO OTHER NUMBERS TO NEW VALUE OR YOU CAN ALSO DELETE TH LINE     
+        self.details['E'] = ['', None, None]        ## <-----   
+        ## YOU CAN MODIFY THE LINE BELLOW.CHANGE WORD BETWEEN ' ',WORD BETWEEN
+        ## ' ', TWO OTHER NUMBERS TO NEW VALUE OR YOU CAN ALSO DELETE TH LINE     
+        self.details['F'] = ['', 0, 1e16]           ## <-----   
+        ## YOU CAN MODIFY THE LINE BELLOW.MODIFY WORDS BETWEEN """   """  ONLY!!!!
+        self.description = """
+            scale * sin(F(x)/F(x)) \n where F(x)=A+B*x+C*x^2+D*x^3+E*x^4+F*x^5
+                 """                        ## <-----   
+   
+    def function(self, x = 0.0): ##DO NOT CHANGE THIS LINE!!!
+        """ 
+        Evaluate the model
+        
+        :param x: input x
+        
+        :return: function value
+        
+        """
+        ## ADD YOUR FUNCTION HERE.
+        ## REUSE THE PARAMETERS DEFINED PREVIOUSLY TO WRITE YOUR FUNCTION.
+        
+        ## IN THIS EXAMPLE THE FUNTION IS:
+        ## scale * sin(F(x)/F(x)) \n where F(x)=A+B*x+C*x^2+D*x^3+E*x^4+F*x^5
+        
+        ## YOU CAN REWRITE OUR EXAMPLE WITH EVERYTING INSIDE " " :
+        ## "RETURN self.params['scale']* (self.params['A'] + self.params['B']*x + \
+        ##   self.params['C']* pow(x,2) + self.params['D']*pow(x,3)+\
+        ## self.params['E']*pow(x,4) +self.params['F']*pow(x,5)  ) "  
+         
+        ## NOTE: pow IS A FUNCTION IMPORTED FROM PYTHON MATH LIBRARY
+        ## FOR MORE INFORMATION CHECK http://docs.python.org/library/math.html      
+        ## OTHER FUNCTIONS ARE ALSO 
+        ###  AVAILABLE http://www.scipy.org/Numpy_Functions_by_Category
+        ## numpy FUNCTIONS ARE FOR EXPERT USER
+        
+         
+        ## YOU CAN ERASE EVERYTHING BELLOW FOR YOUR OWN FUNCTION
+        #Redefine parameters as local parameters
+        a = self.params['A']       ## <-----   
+        b = self.params['B']       ## <-----   
+        c = self.params['C']       ## <-----   
+        d = self.params['D']       ## <-----   
+        e = self.params['E']       ## <-----   
+        f = self.params['F']        ## <-----   
+        scl = self.params['scale']   ## <-----   
+        ##THIS OUR FUNCTION TEMPLATE
+        poly = a + b*x + c*math.pow(x,2) + d*math.pow(x,3) \
+             + e*math.pow(x,4) +f*math.pow(x,5)      ## <-----                
+        
+        #Remove a singular point (lim poly --> 0) for sin(poly)/poly
+        #(Just note: In Python, indentation defines the belongings of 'if', 'for', and so on..)
+        if poly == 0:                       ## <-----   
+            result = 1                      ## <-----   
+        else:                               ## <-----   
+            result = math.sin(poly)/poly    ## <-----   
+            
+        #Re-scale                          ## <-----   
+        result *=scl                       ## <-----   
+
+        return result ## MODIFY ONLY RESULT. DON'T DELETE RETURN!!!!
+
+    ## DO NOT MODIFY THE FOLLOWING LINES!!!!!!!!!!!!!!!!   
+    def get_fname(self):
+        """
+        Get the model name same as the file name
+        """
+        path = sys._getframe().f_code.co_filename
+        basename  = os.path.basename(path)
+        name, _ = os.path.splitext(basename)
+        return name
+                
+## DO NOT MODIFY THE FOLLOWING LINES!!!!!!!!!!!!!!!!       
+if __name__ == "__main__": 
+    m= Model() 
+    out1 = m.runXY(0.0)
+    out2 = m.runXY(0.01)
+    isfine1 = numpy.isfinite(out1)
+    isfine2 = numpy.isfinite(out2)
+    print "Testing the value at Q = 0.0:"
+    print out1, " : finite? ", isfine1
+    print "Testing the value at Q = 0.01:"
+    print out2, " : finite? ", isfine2
+    if isfine1 and isfine2:
+        print "===> Simple Test: Passed!"
+    else:
+        print "===> Simple Test: Failed!"
Index: src/sas/perspectives/fitting/report_dialog.py
===================================================================
--- src/sas/perspectives/fitting/report_dialog.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/fitting/report_dialog.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,312 @@
+"""
+Dialog report panel to show and summarize the results of 
+the invariant calculation.
+"""
+################################################################################
+#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 2009, University of Tennessee
+################################################################################
+
+import wx
+import sys
+import os
+import wx.html as html
+
+_STATICBOX_WIDTH = 480
+PANEL_WIDTH = 530
+PANEL_HEIGHT = 700
+FONT_VARIANT = 1
+ISMAC = False
+ISPDF = False
+if sys.platform == "win32":
+    _STATICBOX_WIDTH = 450
+    PANEL_WIDTH = 500
+    PANEL_HEIGHT = 700
+    FONT_VARIANT = 0
+    ISMAC = False
+    ISPDF = True
+elif sys.platform == "darwin":
+    ISMAC = True
+    ISPDF = True
+
+        
+class ReportDialog(wx.Dialog):
+    """
+    The report dialog box.
+    """
+    
+    def __init__(self, list, *args, **kwds):
+        """
+        Initialization. The parameters added to Dialog are:
+        
+        :param list: report_list (list of html_str, text_str, image)
+        from invariant_state
+        """
+        kwds["style"] = wx.RESIZE_BORDER|wx.DEFAULT_DIALOG_STYLE
+        wx.Dialog.__init__(self, *args, **kwds)
+        kwds["image"] = 'Dynamic Image'
+        # title
+        self.SetTitle("Report: Fitting")
+        # size
+        self.SetSize((720, 650))
+        # font size
+        self.SetWindowVariant(variant=FONT_VARIANT)
+        # check if tit is MAC
+        self.is_pdf = ISPDF
+        # report string
+        self.report_list = list
+        # number of images of plot
+        self.nimages = len(list[2])
+        
+        if list[2] != None:
+            # put image path in the report string
+            if len(list[2]) == 1:
+                self.report_html = self.report_list[0] % \
+                                    "memory:img_fit0.png"
+            elif len(list[2]) == 2:
+                self.report_html = self.report_list[0] % \
+                                    ("memory:img_fit0.png",
+                                     "memory:img_fit1.png")
+            # allows up to three images
+            else:
+                self.report_html = self.report_list[0] % \
+                                    ("memory:img_fit0.png",
+                                     "memory:img_fit1.png",
+                                     "memory:img_fit2.png")
+        else:
+            self.report_html = self.report_list[0]
+        # layout
+        self._setup_layout()
+        
+    def _setup_layout(self):
+        """
+        Set up layout
+        """
+        hbox = wx.BoxSizer(wx.HORIZONTAL)
+        
+        # buttons
+        id = wx.ID_OK
+        button_close = wx.Button(self, id, "Close")
+        button_close.SetToolTipString("Close this report window.")
+        #hbox.Add((5,10), 1 , wx.EXPAND|wx.ADJUST_MINSIZE,0)
+        hbox.Add(button_close)
+        button_close.SetFocus()
+
+        id = wx.NewId()
+        button_preview = wx.Button(self, id, "Preview")
+        button_preview.SetToolTipString("Print preview this report.")
+        button_preview.Bind(wx.EVT_BUTTON, self.onPreview,
+                            id=button_preview.GetId()) 
+        hbox.Add(button_preview)
+
+        id = wx.NewId()
+        button_print = wx.Button(self, id, "Print")
+        button_print.SetToolTipString("Print this report.")
+        button_print.Bind(wx.EVT_BUTTON, self.onPrint,
+                          id=button_print.GetId())
+        hbox.Add(button_print)
+        
+        id = wx.NewId()
+        button_save = wx.Button(self, id, "Save")
+        button_save.SetToolTipString("Save this report.")
+        button_save.Bind(wx.EVT_BUTTON, self.onSave, id=button_save.GetId())
+        hbox.Add(button_save)
+        
+        # panel for report page
+        #panel = wx.Panel(self, -1)
+        vbox = wx.BoxSizer(wx.VERTICAL)
+        # html window
+        self.hwindow = html.HtmlWindow(self, style=wx.BORDER)
+        # set the html page with the report string
+        self.hwindow.SetPage(self.report_html)
+        
+        # add panels to boxsizers
+        vbox.Add(hbox)
+        vbox.Add(self.hwindow, 1, wx.EXPAND|wx.ALL,0)
+
+        self.SetSizer(vbox)
+        self.Centre()
+        self.Show(True)
+
+    def onSave(self, event=None):
+        """
+        Save
+        """
+        # pdf supporting only on MAC, not on exe
+        if self.is_pdf:
+            wild_card = ' PDF files (*.pdf)|*.pdf|'
+            ind_cor = 0
+        else:
+            wild_card = ''
+            ind_cor = 1
+        wild_card += 'HTML files (*.html)|*.html|'
+        wild_card += 'Text files (*.txt)|*.txt'
+
+        #todo: complete saving fig file and as a txt file
+        dlg = wx.FileDialog(self, "Choose a file",
+                            wildcard=wild_card,
+                            style=wx.SAVE|wx.OVERWRITE_PROMPT|wx.CHANGE_DIR)
+        dlg.SetFilterIndex(0)  # Set .html files to be default
+
+        if dlg.ShowModal() != wx.ID_OK:
+            dlg.Destroy()
+            return
+        
+        fName = dlg.GetPath()
+        ext_num = dlg.GetFilterIndex()
+
+        #set file extensions
+        img_ext = []
+        pic_fname = []
+        #PDF
+        if ext_num == (0 + 2 * ind_cor):
+            # TODO: Sort this case out
+            ext = '.pdf'
+            
+            fName = os.path.splitext(fName)[0] + ext
+            dlg.Destroy()
+            #pic (png) file path/name
+            for num in range(self.nimages):
+                im_ext = '_img%s.png' % num
+                #img_ext.append(im_ext)
+                pic_name = os.path.splitext(fName)[0] + im_ext
+                pic_fname.append(pic_name)
+                # save the image for use with pdf writer
+                self.report_list[2][num].savefig(pic_name)
+
+            #put the image path in html string
+            report_frame = self.report_list[0]
+            #put image name strings into the html file
+            #Note:The str for pic_fname shouldn't be removed.
+            if self.nimages == 1:
+                html = report_frame % str(pic_fname[0])
+            elif self.nimages == 2:
+                html = report_frame % (str(pic_fname[0]), str(pic_fname[1]))
+            elif self.nimages == 3:
+                html = report_frame % (str(pic_fname[0]), str(pic_fname[1]),
+                                          str(pic_fname[2]))
+
+            # make/open file in case of absence
+            f = open(fName, 'w')
+            f.close()
+            # write pdf as a pdf file
+            pdf = self.HTML2PDF(data=html, filename=fName)
+            
+            #open pdf
+            if pdf:
+                try:
+                    #Windows
+                    os.startfile(str(fName))
+                except:
+                    try:
+                        #Mac
+                        os.system("open %s"% fName)
+                    except:
+                        #DO not open
+                        pass
+            #delete image file
+            for num in range(self.nimages):
+                os.remove(pic_fname[num])
+            return
+        #HTML + png(graph)
+        elif ext_num == (1 - ind_cor):
+            ext = '.html'
+            for num in range(self.nimages):
+                img_ext.append('_img4html%s.png' % num)
+            report_frame = self.report_list[0]
+        #TEXT + pdf(graph)
+        elif ext_num == (2 - ind_cor):
+            ext = '.txt'
+            # changing the image extension actually changes the image
+            # format on saving
+            for num in range(self.nimages):
+                img_ext.append('_img4txt%s.pdf' % num)
+            report = self.report_list[1]
+        else:
+            return
+        
+        #file name
+        fName = os.path.splitext(fName)[0] + ext
+        dlg.Destroy()
+        
+        #pic (png) file path/name
+        for num in range(self.nimages):
+            pic_name = os.path.splitext(fName)[0] + img_ext[num]
+            pic_fname.append(pic_name)
+        #put the image path in html string
+        if ext_num == (1 - ind_cor):
+            if self.nimages == 1:
+                report = report_frame % os.path.basename(pic_fname[0])
+            elif self.nimages == 2:
+                report = report_frame % (os.path.basename(pic_fname[0]),
+                                         os.path.basename(pic_fname[1]))
+            elif self.nimages == 3:
+                report = report_frame % (os.path.basename(pic_fname[0]),
+                                         os.path.basename(pic_fname[1]),
+                                         os.path.basename(pic_fname[2]))
+        f = open(fName, 'w')
+        f.write(report)
+        f.close()
+        self.Update()
+        #save png file using pic_fname
+        for num in range(self.nimages):
+            self.report_list[2][num].savefig(pic_fname[num])
+        
+    def onPreview(self, event=None):
+        """
+        Preview
+        
+        : event: Preview button event
+        """
+        previewh = html.HtmlEasyPrinting(name="Printing", parentWindow=self)
+        previewh.PreviewText(self.report_html)
+        if event is not None:
+            event.Skip()
+        self.Update()
+    
+    def onPrint(self, event=None):
+        """
+        Print
+        
+        : event: Print button event
+        """
+        printh = html.HtmlEasyPrinting(name="Printing", parentWindow=self)
+        printh.PrintText(self.report_html)
+        if event is not None:
+            event.Skip()
+        self.Update()
+        
+    def OnClose(self, event=None):
+        """
+        Close the Dialog
+        
+        : event: Close button event
+        """
+        self.Close()
+        # Reset memory
+        #wx.MemoryFSHandler()
+        if event is not None:
+            event.Skip()
+    
+    def HTML2PDF(self, data, filename):
+        """
+        Create a PDF file from html source string.
+        
+        : data: html string
+        : filename: name of file to be saved
+        """
+        import ho.pisa as pisa
+        f = file(filename, "wb")
+        # pisa requires some extra packages, see their web-site
+        pdf = pisa.CreatePDF(data, f)
+
+        # close the file here otherwise it will be open until quitting
+        #the application.
+        f.close()
+        self.Update()
+        return not pdf.err
Index: src/sas/perspectives/fitting/resultpanel.py
===================================================================
--- src/sas/perspectives/fitting/resultpanel.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/fitting/resultpanel.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,86 @@
+"""
+FitPanel class contains fields allowing to fit  models and  data
+
+:note: For Fit to be performed the user should check at least one parameter
+    on fit Panel window.
+   
+"""
+import wx
+import wx.lib.newevent
+from wx.aui import AuiNotebook as Notebook
+
+from sas.guiframe.panel_base import PanelBase
+from sas.guiframe.events import StatusEvent
+
+(PlotResultEvent, EVT_PLOT_RESULT) = wx.lib.newevent.NewEvent()
+
+
+class ResultPanel(Notebook, PanelBase):
+    """
+    FitPanel class contains fields allowing to fit  models and  data
+    
+    :note: For Fit to be performed the user should check at least one parameter
+        on fit Panel window.
+       
+    """
+    ## Internal name for the AUI manager
+    window_name = "Result panel"
+    ## Title to appear on top of the window
+    window_caption = "Result Panel"
+    CENTER_PANE = True
+    
+    def __init__(self, parent, manager=None, *args, **kwargs):
+        """
+        """
+        style = ((wx.aui.AUI_NB_WINDOWLIST_BUTTON
+                 |wx.aui.AUI_NB_DEFAULT_STYLE
+                 |wx.CLIP_CHILDREN)
+                 & ~wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB)
+        Notebook.__init__(self, parent, -1, style = style)
+        PanelBase.__init__(self, parent)
+        self.frame = parent
+        self.Bind(EVT_PLOT_RESULT, self.on_plot_results)
+        self.frame.Bind(wx.EVT_CLOSE, self.on_close)
+        self._manager = None
+
+    def on_close(self, event):
+        if event.CanVeto():
+            self.frame.Hide()
+            event.Veto()
+        else:
+            event.Skip()
+
+    def on_plot_results(self, event):
+        self.frame.Show()
+        result = event.result[0][0]
+        if hasattr(result, 'convergence'):
+            from bumps.gui.convergence_view import ConvergenceView
+            best, pop = result.convergence[:,0], result.convergence[:,1:]
+            self.get_panel(ConvergenceView).update(best, pop)
+        if hasattr(result, 'uncertainty_state'):
+            from bumps.gui.uncertainty_view import UncertaintyView, CorrelationView, TraceView
+            from bumps.dream.stats import var_stats,format_vars
+            stats = var_stats(result.uncertainty_state.draw())
+            msg = format_vars(stats)
+            self.get_panel(CorrelationView).update(result.uncertainty_state)
+            self.get_panel(UncertaintyView).update((result.uncertainty_state,stats))
+            self.get_panel(TraceView).update(result.uncertainty_state)
+            # TODO: stats should be stored in result rather than computed in bumps UncertaintyView
+            wx.PostEvent(self.frame.parent,
+                         StatusEvent(status=msg, info="info"))
+            print
+
+    def get_frame(self):
+        return self.frame
+
+    def add_panel(self, panel):
+        self.AddPage(panel, panel.title)
+
+    def get_panel(self, panel_class):
+        for idx in range(self.PageCount):
+            if self.GetPageText(idx) == panel_class.title:
+                return self.GetPage(idx)
+        else:
+            panel = panel_class(self)
+            self.add_panel(panel)
+            return panel
Index: src/sas/perspectives/fitting/simfitpage.py
===================================================================
--- src/sas/perspectives/fitting/simfitpage.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/fitting/simfitpage.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,931 @@
+"""
+    Simultaneous fit page
+"""
+import sys,re,string, wx
+import wx.lib.newevent
+from sas.guiframe.events import StatusEvent 
+from sas.guiframe.panel_base import PanelBase
+from wx.lib.scrolledpanel import ScrolledPanel
+from sas.guiframe.events import PanelOnFocusEvent
+#Control panel width 
+if sys.platform.count("darwin") == 0:
+    PANEL_WID = 420
+    FONT_VARIANT = 0
+else:
+    PANEL_WID = 490
+    FONT_VARIANT = 1
+       
+            
+def get_fittableParam(model):
+    """
+    return list of fittable parameters name of a model
+    
+    :param model: the model used
+    
+    """
+    fittable_param = []
+    for item in model.getParamList():
+        if not item  in model.getDispParamList():
+            if not item in model.non_fittable:
+                fittable_param.append(item)
+            
+    for item in model.fixed:
+        fittable_param.append(item)
+        
+    return fittable_param
+
+
+class SimultaneousFitPage(ScrolledPanel, PanelBase):
+    """
+    Simultaneous fitting panel
+    All that needs to be defined are the
+    two data members window_name and window_caption
+    """
+    ## Internal name for the AUI manager
+    window_name = "simultaneous Fit page"
+    ## Title to appear on top of the window
+    window_caption = "Simultaneous Fit Page"
+    
+    def __init__(self, parent, page_finder={}, id=-1, batch_on=False,
+                     *args, **kwargs):
+        ScrolledPanel.__init__(self, parent, id=id,
+                               style=wx.FULL_REPAINT_ON_RESIZE,
+                               *args, **kwargs)
+        PanelBase.__init__(self, parent)
+        """
+        Simultaneous page display
+        """
+        self.SetupScrolling()
+        ##Font size
+        self.SetWindowVariant(variant=FONT_VARIANT)
+        self.uid = wx.NewId()
+        self.parent = parent
+        self.batch_on = batch_on
+        ## store page_finder
+        self.page_finder = page_finder
+        ## list contaning info to set constraint
+        ## look like self.constraint_dict[page_id]= page
+        self.constraint_dict = {}
+        ## item list
+        # self.constraints_list=[combobox1, combobox2,=,textcrtl, button ]
+        self.constraints_list = []
+        ## list of current model
+        self.model_list = []
+        ## selected mdoel to fit
+        self.model_toFit = []
+        ## number of constraint
+        self.nb_constraint = 0
+        self.model_cbox_left = None
+        self.model_cbox_right = None
+        self.uid = wx.NewId()
+        ## draw page
+        self.define_page_structure()
+        self.draw_page()
+        self.set_layout()
+        self._set_save_flag(False)
+        
+    def define_page_structure(self):
+        """
+        Create empty sizer for a panel
+        """
+        self.vbox = wx.BoxSizer(wx.VERTICAL)
+        self.sizer1 = wx.BoxSizer(wx.VERTICAL)
+        self.sizer2 = wx.BoxSizer(wx.VERTICAL)
+        self.sizer3 = wx.BoxSizer(wx.VERTICAL)
+
+        self.sizer1.SetMinSize((PANEL_WID, -1))
+        self.sizer2.SetMinSize((PANEL_WID, -1))
+        self.sizer3.SetMinSize((PANEL_WID, -1))
+        self.vbox.Add(self.sizer1)
+        self.vbox.Add(self.sizer2)
+        self.vbox.Add(self.sizer3)
+        
+    def set_scroll(self):
+        """
+        """
+        self.Layout()
+         
+    def set_layout(self):
+        """
+        layout
+        """
+        self.vbox.Layout()
+        self.vbox.Fit(self)
+        self.SetSizer(self.vbox)
+        self.set_scroll()
+        self.Centre()
+        
+    def onRemove(self, event):
+        """
+        Remove constraint fields
+        """
+        if len(self.constraints_list) == 1:
+            self.hide_constraint.SetValue(True)
+            self._hide_constraint()
+            return
+        if len(self.constraints_list) == 0:
+            return
+        for item in self.constraints_list:
+            length = len(item)
+            if event.GetId() == item[length - 2].GetId():
+                sizer = item[length - 1]
+                sizer.Clear(True)
+                self.sizer_constraints.Remove(sizer)
+                #self.SetScrollbars(20,20,25,65)
+                self.constraints_list.remove(item)
+                self.nb_constraint -= 1
+                self.sizer2.Layout()
+                self.Layout()
+                break
+
+        #self._onAdd_constraint(None)
+             
+    def onFit(self, event):
+        """
+        signal for fitting
+        
+        """
+        flag = False
+        # check if the current page a simultaneous fit page or a batch page
+        if self == self._manager.sim_page:
+            flag = (self._manager.sim_page.uid == self.uid)
+
+        ## making sure all parameters content a constraint
+        ## validity of the constraint expression is own by fit engine
+        if self.parent._manager._fit_engine not in ("park","bumps") and flag:
+            msg = "The FitEnging will be set to 'Park' fit engine\n"
+            msg += " for the simultaneous fit..."
+            #wx.MessageBox(msg, 'Info')
+            wx.PostEvent(self._manager.parent, StatusEvent(status=\
+                            "Fitting: %s" % msg, info="info"))
+        if not self.batch_on and self.show_constraint.GetValue():
+            if not self._set_constraint():
+                return
+        ## model was actually selected from this page to be fit
+        if len(self.model_toFit) >= 1:
+            self.manager._reset_schedule_problem(value=0)
+            for item in self.model_list:
+                if item[0].GetValue():
+                    self.manager.schedule_for_fit(value=1, uid=item[2]) 
+            try:
+                if not self.manager.onFit(uid=self.uid):
+                    return
+            except:
+                msg = "Select at least one parameter to fit in the FitPages."
+                wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
+        else:
+            msg = "Select at least one model check box to fit "
+            wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
+           
+    def set_manager(self, manager):
+        """
+        set panel manager
+        
+        :param manager: instance of plugin fitting
+        
+        """
+        self.manager = manager
+       
+    def check_all_model_name(self, event=None):
+        """
+        check all models names
+        """
+        self.model_toFit = []
+        if self.cb1.GetValue() == True:
+            for item in self.model_list:
+                if item[0].IsEnabled():
+                    item[0].SetValue(True)
+                    self.model_toFit.append(item)
+                
+            ## constraint info
+            self._store_model()
+            if not self.batch_on:
+                ## display constraint fields
+                if self.show_constraint.GetValue() and\
+                                 len(self.constraints_list) == 0:
+                    self._show_all_constraint() 
+                    self._show_constraint()
+        else:
+            for item in self.model_list:
+                item[0].SetValue(False) 
+                
+            self.model_toFit = []
+            if not self.batch_on:
+                ##constraint info
+                self._hide_constraint()
+            
+        self._update_easy_setup_cb()
+        self.Layout()
+        self.Refresh()
+        
+    def check_model_name(self, event):
+        """
+        Save information related to checkbox and their states
+        """
+        self.model_toFit = []
+        cbox = event.GetEventObject()
+        for item in self.model_list:
+            if item[0].GetValue() == True:
+                self.model_toFit.append(item)
+            else:
+                if item in self.model_toFit:
+                    self.model_toFit.remove(item)
+                    self.cb1.SetValue(False)
+        
+        ## display constraint fields
+        if len(self.model_toFit) >= 1:
+            self._store_model()
+            if not self.batch_on and self.show_constraint.GetValue() and\
+                             len(self.constraints_list) == 0:
+                self._show_all_constraint()
+                self._show_constraint()
+
+        elif len(self.model_toFit) < 1:
+            ##constraint info
+            self._hide_constraint()
+                        
+        self._update_easy_setup_cb()
+        ## set the value of the main check button
+        if len(self.model_list) == len(self.model_toFit):
+            self.cb1.SetValue(True)
+            self.Layout()
+            return
+        else:
+            self.cb1.SetValue(False)
+            self.Layout()
+            
+    def _update_easy_setup_cb(self):
+        """
+        Update easy setup combobox on selecting a model
+        """
+        if self.model_cbox_left != None and self.model_cbox_right != None:
+            try:
+                # when there is something
+                self.model_cbox_left.Clear()
+                self.model_cbox_right.Clear()
+                self.model_cbox.Clear()
+            except:
+                # when there is nothing
+                pass
+            #for id, model in self.constraint_dict.iteritems():
+            for item in self.model_toFit:
+                model = item[3]
+                ## check if all parameters have been selected for constraint
+                ## then do not allow add constraint on parameters
+                if str(model.name) not in self.model_cbox_left.GetItems():
+                    self.model_cbox_left.Append(str(model.name), model)
+                if str(model.name) not in self.model_cbox_right.GetItems():
+                    self.model_cbox_right.Append(str(model.name), model)
+                if str(model.name) not in self.model_cbox.GetItems():
+                    self.model_cbox.Append(str(model.name), model)
+            self.model_cbox_left.SetSelection(0)
+            self.sizer2.Layout()
+            self.sizer3.Layout()
+        
+    def draw_page(self):
+        """
+        Draw a sizer containing couples of data and model 
+        """
+        self.model_list = []
+        self.model_toFit = []
+        self.constraints_list = []
+        self.constraint_dict = {}
+        self.nb_constraint = 0
+        self.model_cbox_left = None
+        self.model_cbox_right = None
+        
+        if len(self.model_list) > 0:
+            for item in self.model_list:
+                item[0].SetValue(False)
+                self.manager.schedule_for_fit(value=0, uid=item[2])
+                
+        self.sizer1.Clear(True)
+        box_description = wx.StaticBox(self, -1, "Fit Combinations")
+        boxsizer1 = wx.StaticBoxSizer(box_description, wx.VERTICAL)
+        sizer_title = wx.BoxSizer(wx.HORIZONTAL)
+        sizer_couples = wx.GridBagSizer(5, 5)
+        #------------------------------------------------------
+        if len(self.page_finder) == 0:
+            msg = " No fit combinations are found! \n\n"
+            msg += " Please load data and set up "
+            msg += "at least two fit panels first..."
+            sizer_title.Add(wx.StaticText(self, -1, msg))
+        else:
+            ## store model
+            self._store_model()
+        
+            self.cb1 = wx.CheckBox(self, -1, 'Select all')
+            self.cb1.SetValue(False)
+            
+            wx.EVT_CHECKBOX(self, self.cb1.GetId(), self.check_all_model_name)
+            
+            sizer_title.Add((10, 10), 0,
+                wx.TOP|wx.BOTTOM|wx.EXPAND|wx.ADJUST_MINSIZE, border=5)
+            sizer_title.Add(self.cb1, 0,
+                wx.TOP|wx.BOTTOM|wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, border=5)
+            
+            ## draw list of model and data name
+            self._fill_sizer_model_list(sizer_couples)
+            ## draw the sizer containing constraint info
+            if not self.batch_on:
+                self._fill_sizer_constraint()
+            ## draw fit button
+            self._fill_sizer_fit()
+        #--------------------------------------------------------
+        boxsizer1.Add(sizer_title, flag = wx.TOP|wx.BOTTOM, border=5) 
+        boxsizer1.Add(sizer_couples, 1, flag = wx.TOP|wx.BOTTOM, border=5)
+       
+        self.sizer1.Add(boxsizer1, 1, wx.EXPAND | wx.ALL, 10)
+        self.sizer1.Layout()
+        #self.SetScrollbars(20,20,25,65)
+        self.AdjustScrollbars()
+        self.Layout()
+        
+    def _store_model(self):
+        """
+         Store selected model
+        """
+        if len(self.model_toFit) < 1:
+            return
+        for item in self.model_toFit:
+            model = item[3]
+            page_id = item[2]
+            self.constraint_dict[page_id] = model
+                   
+    def _display_constraint(self, event):
+        """
+        Show fields to add constraint
+        """
+        if len(self.model_toFit) < 1:
+            msg = "Select at least 1 model to add constraint "
+            wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
+            ## hide button
+            self._hide_constraint()
+            return
+        if self.show_constraint.GetValue():
+            self._show_all_constraint()
+            self._show_constraint()
+            self.Layout()
+            return
+        else:
+            self._hide_constraint()
+            self.Layout()
+            return
+       
+    def _show_all_constraint(self):
+        """
+        Show constraint fields
+        """
+        box_description = wx.StaticBox(self, -1,"Easy Setup ")
+        boxsizer = wx.StaticBoxSizer(box_description, wx.HORIZONTAL)     
+        sizer_constraint = wx.BoxSizer(wx.HORIZONTAL)
+        self.model_cbox_left = wx.ComboBox(self, -1, style=wx.CB_READONLY)
+        self.model_cbox_left.Clear()
+        self.model_cbox_right = wx.ComboBox(self, -1, style=wx.CB_READONLY)
+        self.model_cbox_right.Clear()
+        wx.EVT_COMBOBOX(self.model_cbox_left, -1, self._on_select_modelcb)
+        wx.EVT_COMBOBOX(self.model_cbox_right, -1, self._on_select_modelcb)
+        egal_txt = wx.StaticText(self, -1, " = ")
+        self.set_button = wx.Button(self, wx.NewId(), 'Set All')
+        self.set_button.Bind(wx.EVT_BUTTON, self._on_set_all_equal,
+                             id=self.set_button.GetId())
+        set_tip = "Add constraints for all the adjustable parameters "
+        set_tip += "(checked in FitPages) if exist."
+        self.set_button.SetToolTipString(set_tip)
+        self.set_button.Disable()
+        
+        for id, model in self.constraint_dict.iteritems():
+            ## check if all parameters have been selected for constraint
+            ## then do not allow add constraint on parameters
+            self.model_cbox_left.Append(str(model.name), model)
+        self.model_cbox_left.Select(0)
+        for id, model in self.constraint_dict.iteritems():
+            ## check if all parameters have been selected for constraint
+            ## then do not allow add constraint on parameters
+            self.model_cbox_right.Append(str(model.name), model)
+        boxsizer.Add(self.model_cbox_left,
+                             flag=wx.RIGHT|wx.EXPAND, border=10)
+        boxsizer.Add(wx.StaticText(self, -1, ".parameters"),
+                             flag=wx.RIGHT|wx.EXPAND, border=5)
+        boxsizer.Add(egal_txt, flag= wx.RIGHT|wx.EXPAND, border=5)
+        boxsizer.Add(self.model_cbox_right,
+                             flag=wx.RIGHT|wx.EXPAND, border=10)
+        boxsizer.Add(wx.StaticText(self, -1, ".parameters"),
+                             flag=wx.RIGHT|wx.EXPAND,border=5)
+        boxsizer.Add((20, -1))
+        boxsizer.Add(self.set_button, flag=wx.RIGHT|wx.EXPAND, border=5)
+        sizer_constraint.Add(boxsizer, flag=wx.RIGHT|wx.EXPAND, border=5)
+        self.sizer_all_constraints.Insert(before=0,
+                             item=sizer_constraint,
+                             flag=wx.TOP|wx.BOTTOM|wx.EXPAND, border=5)
+
+        self.sizer_all_constraints.Layout()
+        self.sizer2.Layout()
+        #self.SetScrollbars(20,20,25,65)
+    
+    def _on_select_modelcb(self, event):
+        """
+        On select model left or right combobox
+        """
+        event.Skip()
+        flag = True
+        if self.model_cbox_left.GetValue().strip() == '':
+            flag = False
+        if self.model_cbox_right.GetValue().strip() == '':
+            flag = False
+        if self.model_cbox_left.GetValue() ==\
+                self.model_cbox_right.GetValue():
+            flag = False
+        self.set_button.Enable(flag)
+        
+    def _on_set_all_equal(self, event):
+        """
+        On set button
+        """
+        event.Skip()
+        length = len(self.constraints_list)
+        if length < 1:
+            return
+        param_list = []
+        param_listB = []
+        selection = self.model_cbox_left.GetCurrentSelection()
+        model_left = self.model_cbox_left.GetValue()
+        model = self.model_cbox_left.GetClientData(selection)
+        selectionB = self.model_cbox_right.GetCurrentSelection()
+        model_right = self.model_cbox_right.GetValue()
+        modelB = self.model_cbox_right.GetClientData(selectionB)
+        for id, dic_model in self.constraint_dict.iteritems():
+            if model == dic_model:
+                param_list = self.page_finder[id].get_param2fit()
+            if modelB == dic_model:
+                param_listB = self.page_finder[id].get_param2fit()
+            if len(param_list) > 0 and len(param_listB) > 0:
+                break
+        num_cbox = 0
+        has_param = False
+        for param in param_list:
+            num_cbox += 1
+            if param in param_listB:
+                self.model_cbox.SetStringSelection(model_left)
+                self._on_select_model(None)
+                self.param_cbox.Clear()
+                self.param_cbox.Append(str(param), model)
+                self.param_cbox.SetStringSelection(str(param))
+                self.ctl2.SetValue(str(model_right + "." + str(param)))
+                has_param = True
+                if num_cbox == (len(param_list) + 1):
+                    break
+                self._show_constraint()
+        
+        self.sizer_constraints.Layout()
+        self.sizer2.Layout()
+        self.SetScrollbars(20, 20, 25, 65)
+        self.Layout()
+        if not has_param:
+            msg = " There is no adjustable parameter (checked to fit)"
+            msg += " either one of the models."
+            wx.PostEvent(self.parent.parent, StatusEvent(info="warning",
+                                                         status=msg))
+        else:
+            msg = " The constraints are added."
+            wx.PostEvent(self.parent.parent, StatusEvent(info="info",
+                                                         status=msg))
+
+    def _show_constraint(self):
+        """
+        Show constraint fields
+        """
+        self.btAdd.Show(True)
+        if len(self.constraints_list) != 0:
+            nb_fit_param = 0
+            for id, model in self.constraint_dict.iteritems():
+                nb_fit_param += len(self.page_finder[id].get_param2fit())
+            ##Don't add anymore
+            if len(self.constraints_list) == nb_fit_param:
+                msg = "Cannot add another constraint .Maximum of number "
+                msg += "Parameters name reached %s" % str(nb_fit_param)
+                wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
+                self.sizer_constraints.Layout()
+                self.sizer2.Layout()
+                return
+        if len(self.model_toFit) < 1:
+            msg = "Select at least 1 model to add constraint "
+            wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
+            self.sizer_constraints.Layout()
+            self.sizer2.Layout()
+            return
+            
+        sizer_constraint = wx.BoxSizer(wx.HORIZONTAL)
+        model_cbox = wx.ComboBox(self, -1, style=wx.CB_READONLY)
+        model_cbox.Clear()
+        param_cbox = wx.ComboBox(self, -1,style=wx.CB_READONLY, size=(100, -1), )
+        param_cbox.Hide()
+        
+        #This is for GetCLientData() _on_select_param: Was None return on MAC.
+        self.param_cbox = param_cbox
+        
+        wx.EVT_COMBOBOX(param_cbox,-1, self._on_select_param)
+        self.ctl2 = wx.TextCtrl(self, -1)
+        egal_txt = wx.StaticText(self, -1, " = ")
+        self.btRemove = wx.Button(self,wx.NewId(),'Remove')
+        self.btRemove.Bind(wx.EVT_BUTTON, self.onRemove, 
+                           id=self.btRemove.GetId())
+        self.btRemove.SetToolTipString("Remove constraint.")
+        self.btRemove.Hide()
+        if hasattr(self, "btAdd"):
+            self.btAdd.Hide()
+        for id, model in self.constraint_dict.iteritems():
+            ## check if all parameters have been selected for constraint
+            ## then do not allow add constraint on parameters
+            model_cbox.Append(str(model.name), model)
+            
+        #This is for GetCLientData() passing to self._on_select_param: Was None return on MAC.
+        self.model_cbox = model_cbox
+           
+        wx.EVT_COMBOBOX(model_cbox, -1, self._on_select_model)
+        sizer_constraint.Add((5, -1))
+        sizer_constraint.Add(model_cbox, flag=wx.RIGHT|wx.EXPAND, border=10)
+        sizer_constraint.Add(param_cbox, flag=wx.RIGHT|wx.EXPAND, border=5)
+        sizer_constraint.Add(egal_txt, flag=wx.RIGHT|wx.EXPAND, border=5)
+        sizer_constraint.Add(self.ctl2, flag=wx.RIGHT|wx.EXPAND, border=10)
+        sizer_constraint.Add(self.btRemove, flag=wx.RIGHT|wx.EXPAND, border=10)
+      
+        self.sizer_constraints.Insert(before=self.nb_constraint,
+                        item=sizer_constraint, flag=wx.TOP|wx.BOTTOM|wx.EXPAND,
+                        border=5)
+        self.constraints_list.append([model_cbox, param_cbox, egal_txt,
+                                    self.ctl2, self.btRemove, sizer_constraint])
+    
+        self.nb_constraint += 1
+        self.sizer_constraints.Layout()
+        self.sizer2.Layout()
+        
+    def _hide_constraint(self):
+        """
+        hide buttons related constraint
+        """
+        for id in  self.page_finder.iterkeys():
+            self.page_finder[id].clear_model_param()
+               
+        self.nb_constraint = 0
+        self.constraint_dict = {}
+        if hasattr(self, "btAdd"):
+            self.btAdd.Hide()
+        self._store_model()
+        if self.model_cbox_left != None:
+            try:
+                self.model_cbox_left.Clear()
+            except:
+                pass
+            self.model_cbox_left = None
+        if self.model_cbox_right != None:
+            try:
+                self.model_cbox_right.Clear()
+            except:
+                pass
+            self.model_cbox_right = None
+        self.constraints_list = []
+        self.sizer_all_constraints.Clear(True)
+        self.sizer_all_constraints.Layout()
+        self.sizer_constraints.Clear(True)
+        self.sizer_constraints.Layout()
+        self.sizer2.Layout()
+            
+    def _on_select_model(self, event):
+        """
+        fill combox box with list of parameters
+        """
+        param_list = []
+        ##This way PC/MAC both work, instead of using event.GetClientData().
+        n = self.model_cbox.GetCurrentSelection()
+        model = self.model_cbox.GetClientData(n)
+        for id, dic_model in self.constraint_dict.iteritems():
+            if model == dic_model:
+                param_list = self.page_finder[id].get_param2fit()
+                #break
+        length = len(self.constraints_list)
+        if length < 1:
+            return
+        param_cbox = self.constraints_list[length - 1][1]
+        param_cbox.Clear()
+        ## insert only fittable paramaters
+        for param in param_list:
+            param_cbox.Append(str(param), model)
+
+        param_cbox.Show(True)
+        self.btRemove.Show(True)
+        self.btAdd.Show(True)
+        self.sizer2.Layout()
+        
+    def _on_select_param(self, event):
+        """
+        Store the appropriate constraint in the page_finder
+        """
+        ##This way PC/MAC both work, instead of using event.GetClientData().
+        #n = self.param_cbox.GetCurrentSelection()
+        #model = self.param_cbox.GetClientData(n)
+        #param = event.GetString()
+      
+        length = len(self.constraints_list)
+        if length < 1:
+            return
+        egal_txt = self.constraints_list[length - 1][2]
+        egal_txt.Show(True)
+        
+        ctl2 = self.constraints_list[length - 1][3]
+        ctl2.Show(True)
+        
+    def _onAdd_constraint(self, event):
+        """
+        Add another line for constraint
+        """
+        if not self.show_constraint.GetValue():
+            msg = " Select Yes to add Constraint "
+            wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
+            return
+        ## check that a constraint is added
+        # before allow to add another constraint
+        for item in self.constraints_list:
+            model_cbox = item[0]
+            if model_cbox.GetString(0) == "":
+                msg = " Select a model Name! "
+                wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
+                return 
+            param_cbox = item[1]
+            if param_cbox.GetString(0) == "":
+                msg = " Select a parameter Name! "
+                wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
+                return 
+            ctl2 = item[3]
+            if ctl2.GetValue().lstrip().rstrip() == "":
+                model = param_cbox.GetClientData(\
+                                            param_cbox.GetCurrentSelection())
+                if model != None:
+                    msg = " Enter a constraint for %s.%s! "%(model.name, 
+                                                        param_cbox.GetString(0))
+                else:
+                     msg = " Enter a constraint"
+                wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
+                return
+        ## some model or parameters can be constrained
+        self._show_constraint()
+        self.sizer3.Layout()
+        self.Layout()
+        self.Refresh()
+        
+    def _fill_sizer_fit(self):
+        """
+        Draw fit button
+        """
+        self.sizer3.Clear(True)
+        box_description= wx.StaticBox(self, -1, "Fit ")
+        boxsizer1 = wx.StaticBoxSizer(box_description, wx.VERTICAL)
+        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
+          
+        self.btFit = wx.Button(self, wx.NewId(), 'Fit', size=wx.DefaultSize)
+        self.btFit.Bind(wx.EVT_BUTTON, self.onFit, id=self.btFit.GetId())
+        self.btFit.SetToolTipString("Perform fit.")
+        if self.batch_on:
+            text = " Fit in Parallel all Data set and model selected.\n"
+        else:
+            text = "     Note: Park fitting engine will be used automatically. \n"
+            text += "     This page requires at least one FitPage with a data \n"
+            text += "       and a model set for fitting."
+            #text+= "automatically for more than 2 combinations checked"
+        text_hint = wx.StaticText(self, -1, text)
+        
+        sizer_button.Add(text_hint, wx.RIGHT|wx.EXPAND, 10)
+        sizer_button.Add(self.btFit, 0, wx.LEFT|wx.ADJUST_MINSIZE, 10)
+        
+        boxsizer1.Add(sizer_button, flag= wx.TOP|wx.BOTTOM,border=10)
+        self.sizer3.Add(boxsizer1, 0, wx.EXPAND | wx.ALL, 10)
+        self.sizer3.Layout()
+        
+    def _fill_sizer_constraint(self):
+        """
+        Fill sizer containing constraint info
+        """
+        msg = "Select at least 2 model to add constraint "
+        wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
+        
+        self.sizer2.Clear(True)
+        if self.batch_on:
+            if self.sizer2.IsShown():
+                self.sizer2.Show(False)
+            return
+        box_description= wx.StaticBox(self, -1, "Fit Constraints")
+        boxsizer1 = wx.StaticBoxSizer(box_description, wx.VERTICAL)
+        sizer_title = wx.BoxSizer(wx.HORIZONTAL)
+        self.sizer_all_constraints = wx.BoxSizer(wx.HORIZONTAL)
+        self.sizer_constraints = wx.BoxSizer(wx.VERTICAL)
+        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
+        
+        self.hide_constraint = wx.RadioButton(self, -1, 'No', (10, 10),
+                                              style=wx.RB_GROUP)
+        self.show_constraint = wx.RadioButton(self, -1, 'Yes', (10, 30))
+        self.Bind(wx.EVT_RADIOBUTTON, self._display_constraint,
+                  id=self.hide_constraint.GetId())
+        self.Bind(wx.EVT_RADIOBUTTON, self._display_constraint,
+                  id=self.show_constraint.GetId())
+        if self.batch_on:
+            self.hide_constraint.Enable(False)
+            self.show_constraint.Enable(False)
+        self.hide_constraint.SetValue(True)
+        self.show_constraint.SetValue(False)
+        
+        sizer_title.Add(wx.StaticText(self, -1, " Model"))
+        sizer_title.Add((10, 10))
+        sizer_title.Add(wx.StaticText(self, -1, " Parameter"))
+        sizer_title.Add((10, 10))
+        sizer_title.Add( wx.StaticText(self,-1," Add Constraint?") )
+        sizer_title.Add((10, 10))
+        sizer_title.Add(self.show_constraint)
+        sizer_title.Add(self.hide_constraint)
+        sizer_title.Add((10, 10))
+        
+        self.btAdd = wx.Button(self, wx.NewId(), 'Add')
+        self.btAdd.Bind(wx.EVT_BUTTON, self._onAdd_constraint,
+                        id=self.btAdd.GetId())
+        self.btAdd.SetToolTipString("Add another constraint?")
+        self.btAdd.Hide()
+     
+        text_hint = wx.StaticText(self, -1,
+                                  "Example: [M0][paramter] = M1.parameter")
+        sizer_button.Add(text_hint, 0 , wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 10)
+        sizer_button.Add(self.btAdd, 0, wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 10)
+       
+        boxsizer1.Add(sizer_title, flag=wx.TOP|wx.BOTTOM,border=10)
+        boxsizer1.Add(self.sizer_all_constraints, flag=wx.TOP|wx.BOTTOM,
+                      border=10)
+        boxsizer1.Add(self.sizer_constraints, flag=wx.TOP|wx.BOTTOM,
+                      border=10)
+        boxsizer1.Add(sizer_button, flag=wx.TOP|wx.BOTTOM, border=10)
+        
+        self.sizer2.Add(boxsizer1, 0, wx.EXPAND | wx.ALL, 10)
+        self.sizer2.Layout()
+       
+        #self.SetScrollbars(20,20,25,65)
+    
+    def _set_constraint(self):
+        """
+        get values from the constrainst textcrtl ,parses them into model name
+        parameter name and parameters values.
+        store them in a list self.params .when when params is not empty
+        set_model uses it to reset the appropriate model
+        and its appropriates parameters
+        """
+        for item in self.constraints_list:
+            select0 = item[0].GetSelection()
+            if select0 == wx.NOT_FOUND:
+                continue
+            model = item[0].GetClientData(select0)
+            select1 = item[1].GetSelection()
+            if select1 == wx.NOT_FOUND:
+                continue
+            param = item[1].GetString(select1)
+            constraint = item[3].GetValue().lstrip().rstrip()
+            if param.lstrip().rstrip() == "":
+                param = None
+                msg = " Constraint will be ignored!. missing parameters"
+                msg += " in combobox to set constraint! "
+                wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
+            for id, value in self.constraint_dict.iteritems():
+                if model == value:
+                    if constraint == "":
+                        msg = " Constraint will be ignored!. missing value"
+                        msg += " in textcrtl to set constraint! "
+                        wx.PostEvent(self.parent.parent,
+                                     StatusEvent(status=msg))
+                        constraint = None
+                    if str(param) in self.page_finder[id].get_param2fit():
+                        msg = " Checking constraint for parameter: %s ", param
+                        wx.PostEvent(self.parent.parent,
+                                     StatusEvent(info="info", status=msg))
+                    else:
+                        model_name = item[0].GetLabel()
+                        fitpage = self.page_finder[id].get_fit_tab_caption()
+                        msg = "All constrainted parameters must be set "
+                        msg += " adjustable: '%s.%s' " % (model_name, param)
+                        msg += "is NOT checked in '%s'. " % fitpage
+                        msg += " Please check it to fit or"
+                        msg += " remove the line of the constraint."
+                        wx.PostEvent(self.parent.parent,
+                                StatusEvent(info="error", status=msg))
+                        return False
+                        
+                    for fid in self.page_finder[id].iterkeys():
+                        # wrap in param/constraint in str() to remove unicode
+                        self.page_finder[id].set_model_param(str(param),
+                                                        str(constraint), fid=fid)
+                    break
+        return True
+    
+    def _fill_sizer_model_list(self, sizer):
+        """
+        Receive a dictionary containing information to display model name
+        """
+        ix = 0
+        iy = 0
+        list = []
+        sizer.Clear(True)
+        
+        new_name = wx.StaticText(self, -1, '  Model Title ',
+                                 style=wx.ALIGN_CENTER)
+        new_name.SetBackgroundColour('orange')
+        new_name.SetForegroundColour(wx.WHITE)
+        sizer.Add(new_name,(iy, ix),(1,1),
+                            wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
+        ix += 2
+        model_type = wx.StaticText(self, -1, '  Model ')
+        model_type.SetBackgroundColour('grey')
+        model_type.SetForegroundColour(wx.WHITE)
+        sizer.Add(model_type, (iy, ix), (1, 1),
+                            wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+        ix += 1
+        data_used = wx.StaticText(self, -1, '  Data ')
+        data_used.SetBackgroundColour('grey')
+        data_used.SetForegroundColour(wx.WHITE)
+        sizer.Add(data_used, (iy, ix), (1, 1),
+                            wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+        ix += 1
+        tab_used = wx.StaticText(self, -1, '  FitPage ')
+        tab_used.SetBackgroundColour('grey')
+        tab_used.SetForegroundColour(wx.WHITE)
+        sizer.Add(tab_used, (iy, ix), (1, 1),
+                            wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+        for id, value in self.page_finder.iteritems():
+            if id not in self.parent.opened_pages:
+                continue
+
+            if self.batch_on != self.parent.get_page_by_id(id).batch_on:
+                continue
+            
+            data_list = []
+            model_list = []
+            # get data name and model objetta
+            for fitproblem in value.get_fit_problem():
+                
+                data = fitproblem.get_fit_data()
+                if not data.is_data:
+                    continue
+                name = '-'
+                if data is not None and data.is_data:
+                    name = str(data.name)
+                data_list.append(name)
+                    
+                model = fitproblem.get_model()
+                if model is None:
+                    continue
+                model_list.append(model)
+           
+            if len(model_list) == 0:
+                continue
+            # Draw sizer
+            ix = 0
+            iy += 1
+            model = model_list[0]
+            name = '_'
+            if model is not None:
+                name = str(model.name)
+            cb = wx.CheckBox(self, -1, name)
+            cb.SetValue(False)
+            cb.Enable(model is not None and data.is_data)
+            sizer.Add(cb, (iy, ix), (1, 1), 
+                       wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
+            wx.EVT_CHECKBOX(self, cb.GetId(), self.check_model_name)
+            ix += 2 
+            type = model.__class__.__name__
+            model_type = wx.StaticText(self, -1, str(type))
+            sizer.Add(model_type, (iy, ix), (1, 1),
+                      wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+            if self.batch_on:
+                data_used = wx.ComboBox(self, -1, style=wx.CB_READONLY)
+                data_used.AppendItems(data_list)
+                data_used.SetSelection(0)
+            else:
+                data_used = wx.StaticText(self, -1, data_list[0])
+            
+            ix += 1
+            sizer.Add(data_used, (iy, ix), (1, 1),
+                      wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+            ix += 1
+            caption = value.get_fit_tab_caption()
+            tab_caption_used = wx.StaticText(self, -1, str(caption))
+            sizer.Add(tab_caption_used, (iy, ix), (1, 1),
+                      wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+            
+            self.model_list.append([cb, value, id, model])
+            
+        iy += 1
+        sizer.Add((20, 20), (iy, ix), (1, 1),
+                  wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
+        sizer.Layout()
+    
+    def on_set_focus(self, event=None):
+        """
+        The  derivative class is on focus if implemented
+        """
+        if self.parent is not None:
+            if self.parent.parent is not None:
+                wx.PostEvent(self.parent.parent, PanelOnFocusEvent(panel=self))
+            self.page_finder = self.parent._manager.get_page_finder()
Index: src/sas/perspectives/fitting/utils.py
===================================================================
--- src/sas/perspectives/fitting/utils.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/fitting/utils.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
@@ -0,0 +1,28 @@
+"""
+Module contains functions frequently used in this package
+"""
+import numpy
+
+
+def get_weight(data, is2d, flag=None):
+    """
+    Received flag and compute error on data.
+    :param flag: flag to transform error of data.
+    :param is2d: flag to distinguish 1D to 2D 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
