Index: fittingview/src/sans/perspectives/fitting/__init__.py
===================================================================
--- fittingview/src/sans/perspectives/fitting/__init__.py	(revision 49c69de79aac6bbde0711203fe50ff3af1f0bb81)
+++ fittingview/src/sans/perspectives/fitting/__init__.py	(revision 49c69de79aac6bbde0711203fe50ff3af1f0bb81)
@@ -0,0 +1,2 @@
+PLUGIN_ID = "Fitting plug-in 1.0"
+from fitting import *
Index: fittingview/src/sans/perspectives/fitting/basepage.py
===================================================================
--- fittingview/src/sans/perspectives/fitting/basepage.py	(revision 49c69de79aac6bbde0711203fe50ff3af1f0bb81)
+++ fittingview/src/sans/perspectives/fitting/basepage.py	(revision 49c69de79aac6bbde0711203fe50ff3af1f0bb81)
@@ -0,0 +1,2809 @@
+
+import sys
+import os
+import wx
+import numpy
+import time
+import copy 
+import math
+import string
+from sans.guiframe.panel_base import PanelBase
+from wx.lib.scrolledpanel import ScrolledPanel
+from sans.guiframe.utils import format_number,check_float
+from sans.guiframe.events import PanelOnFocusEvent
+from sans.guiframe.events import StatusEvent
+from sans.guiframe.events import AppendBookmarkEvent
+import pagestate
+from pagestate import PageState
+(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("darwin")==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
+        ## data
+        self.data = None
+        #list of available data
+        self.data_list = []
+        self.mask = None
+        self.uid = None
+        #Q range for data set
+        self.qmin_data_set = None
+        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
+        ## default fitengine type
+        self.engine_type = 'scipy'
+        ## smear default
+        self.smearer = None
+        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
+       
+        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.smearer = None
+        self.enable2D = False
+        self.is_mac = ON_MAC
+        
+        ##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 = []
+        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 on_set_focus(self, event):
+        """
+        """
+        if self._manager is not None:
+            wx.PostEvent(self._manager.parent, PanelOnFocusEvent(panel=self))
+        
+    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 the 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
+        """
+       ##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(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, dict):
+        """
+        Store list of model
+        
+        :param dict: dictionary containing list of models
+        
+        """
+        self.model_list_box = dict
+        self.state.model_list_box = self.model_list_box
+        self.initialize_combox()
+        
+    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:
+            self._populate_box(self.formfactorbox,
+                               self.model_list_box["Shapes"])
+       
+        if len(self.model_list_box) > 0:
+            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)
+        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)
+        #boxsizer1.Add( (10,10) )
+        #boxsizer1.Add( sizer_select_dispers )
+        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.parent.parent, StatusEvent(status=msg))
+        
+        name = menu.GetLabel(event.GetId())
+        self._on_select_model_helper()
+        
+        if name in self.saved_states.keys():
+            previous_state = self.saved_states[name]
+            ## reset state of checkbox,textcrtl  and  regular parameters value
+            self.Show(False)
+            self.reset_page(previous_state)   
+            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
+        dlg = wx.FileDialog(self, "Choose a file", self._default_save_location,
+                                                 "", "*.fitv", wx.SAVE)
+        if dlg.ShowModal() == wx.ID_OK:
+            path = dlg.GetPath()
+            self._default_save_location = os.path.dirname(path)
+        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
+        wx.CallAfter(self.get_copy)
+        
+        # messages depending on the flag
+        self._copy_info(None)
+        
+    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 was occured "
+            msg += ": No valid parameter values to paste from the clipboard..."
+            infor = "error"
+        # inform msg to wx
+        wx.PostEvent( self.parent.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,tda,ty,tm_isdst= 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.parent.parent, StatusEvent(status = msg ))
+        
+        id = wx.NewId()
+        self.popUpMenu.Append(id,name,str(msg))
+        wx.EVT_MENU(self, id, self.onResetModel)
+        wx.PostEvent(self.parent.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)
+    def old_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 
+        if hasattr(self,"enable_disp"):
+            self.state.enable_disp = copy.deepcopy(self.enable_disp.GetValue())
+        if hasattr(self, "disp_box"):
+            self.state.disp_box = copy.deepcopy(self.disp_box.GetSelection())
+
+        self.state.model.name= self.model.name
+        
+        #Remember fit engine_type for fit panel
+        if self.engine_type == None: 
+            self.engine_type = "scipy"
+        if self._manager !=None:
+            self._manager._on_change_engine(engine=self.engine_type)
+        
+            self.state.engine_type = self.engine_type
+
+        new_state = self.state.clone()
+        new_state.model.name = self.state.model.name
+        
+        new_state.enable2D = copy.deepcopy(self.enable2D)
+        ##Add model state on context menu
+        self.number_saved_state += 1
+        #name= self.model.name+"[%g]"%self.number_saved_state 
+        name= self.model.__class__.__name__+"[%g]"%self.number_saved_state 
+        self.saved_states[name]= new_state
+        
+        ## Add item in the context menu
+        
+        year, month, day,hour,minute,second,tda,ty,tm_isdst= time.localtime()
+        my_time= str(hour)+" : "+str(minute)+" : "+str(second)+" "
+        date= str( month)+"|"+str(day)+"|"+str(year)
+        msg=  "Model saved at %s on %s"%(my_time, date)
+         ## post help message for the selected model 
+        msg +=" Saved! right click on this page to retrieve this model"
+        wx.PostEvent(self.parent.parent, StatusEvent(status = msg ))
+        
+        id = wx.NewId()
+        self.popUpMenu.Append(id,name,str(msg))
+        wx.EVT_MENU(self, id, self.onResetModel)
+        
+    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.parent.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')
+            
+            angles = []
+            weights=[]
+            for line in lines:
+                toks = line.split()
+                try:
+                    angle = float(toks[0])
+                    weight = float(toks[1])
+                except:
+                    # Skip non-data lines
+                    pass
+                angles.append(angle)
+                weights.append(weight)
+            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()
+        #save radiobutton state for model selection
+        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()
+        #model combobox
+        self.state.structurebox = self.structurebox.GetSelection()
+        self.state.formfactorbox = self.formfactorbox.GetSelection()
+        
+        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
+      
+        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.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())  
+                  
+        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.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())  
+            
+        if hasattr(self,"disp_box"):
+            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
+        """ 
+        flag = False
+        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
+        self.formfactorbox.Select(int(state.formfactorcombobox))
+        self.structurebox.SetSelection(state.structurecombobox )
+        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"):
+            
+            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 sans.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 self.model.details.has_key(item):
+                        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)
+         ## 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)
+        ## 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:
+            #self._undo.Enable(False)
+            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:
+        
+        if data == None:
+            data_min = state.qmin
+            data_max = state.qmax
+            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.state.data = data
+            self.state.qmin = self.qmin_x
+            self.state.qmax = self.qmax_x
+        else:
+            self.set_data(data)
+            
+        self.enable2D= state.enable2D
+        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
+        self.formfactorbox.Select(int(state.formfactorcombobox))
+        self.structurebox.SetSelection(state.structurecombobox )
+        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:
+            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)
+        ## 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()
+        #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() 
+    
+    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 self.model.details.has_key(item):
+                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':
+                com_str  = "from sans.models.dispersion_models "
+                com_str += "import %s as disp_func"
+                exec com_str % disp
+                disp_model = disp_func()
+            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 
+        """
+        import os
+        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, state 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
+        """
+        # if self.check_invalid_panel():
+        #    return
+        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.structurebox= self.structurebox.GetCurrentSelection()
+        self.state.formfactorbox = self.formfactorbox.GetCurrentSelection()
+       
+        #self._undo.Enable(True)
+        ## 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
+            #if is_modified:
+
+            # 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 any value is modify draw model with new value
+            if not self.fitrange:
+                #self.btFit.Disable()
+                if is_2Ddata: self.btEditMask.Disable()
+            else:
+                #self.btFit.Enable(True)
+                if is_2Ddata: self.btEditMask.Enable(True)
+            if is_modified and self.fitrange:
+                if self.data == None:
+                    # Theory case: need to get npts value to draw
+                    self.npts_x = float(self.Npts_total.GetValue())
+                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:           
+            ##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,
+                                                     qmin=float(self.qmin_x),
+                                                      qmax=float(self.qmax_x),
+                                                      draw=False)
+                elif not self._is_2D():
+                    self._manager.set_smearer(smearer=temp_smearer,
+                                              qmin=float(self.qmin_x),
+                                              uid=self.uid, 
+                                                 qmax= float(self.qmax_x),
+                                                 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:
+            #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 _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 self.model.fun_list.has_key(item_page_info[2]):
+                       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 self.model.fun_list.has_key(value):
+                    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])
+           
+    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):
+        """
+        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
+            toggle_mode_on = self.model_view.IsEnabled()
+            self._manager.draw_model(self.model, 
+                                    data=self.data,
+                                    smearer= temp_smear,
+                                    qmin=float(self.qmin_x), 
+                                    qmax=float(self.qmax_x),
+                                    qstep= float(self.npts_x),
+                                    page_id=self.uid,
+                                    toggle_mode_on=toggle_mode_on, 
+                                    state = self.state,
+                                    enable2D=self.enable2D,
+                                    update_chisqr=update_chisqr)
+        
+       
+    def _on_show_sld(self, event=None):
+        """
+        Plot SLD profile
+        """
+        # get profile data
+        x,y=self.model.getProfile()
+
+        from danse.common.plottools import Data1D
+        #from sans.perspectives.theory.profile_dialog import SLDPanel
+        from sans.guiframe.local_perspectives.plotting.profile_dialog \
+        import SLDPanel
+        sld_data = 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.multifactorbox.SetSelection(1) 
+        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 _show_combox_helper(self):
+        """
+        Fill panel's combo box according to the type of model selected
+        """
+        if self.shape_rbutton.GetValue():
+            ##fill the combobox with form factor list
+            self.structurebox.SetSelection(0)
+            self.structurebox.Disable()
+            self.formfactorbox.Clear()
+            self._populate_box( self.formfactorbox,self.model_list_box["Shapes"])
+        if self.shape_indep_rbutton.GetValue():
+            ##fill the combobox with shape independent  factor list
+            self.structurebox.SetSelection(0)
+            self.structurebox.Disable()
+            self.formfactorbox.Clear()
+            self._populate_box( self.formfactorbox,
+                                self.model_list_box["Shape-Independent"])
+        if self.struct_rbutton.GetValue():
+            ##fill the combobox with structure factor list
+            self.structurebox.SetSelection(0)
+            self.structurebox.Disable()
+            self.formfactorbox.Clear()
+            self._populate_box( self.formfactorbox,
+                                self.model_list_box["Structure Factors"])
+        if self.plugin_rbutton.GetValue():
+            ##fill the combobox with form factor list
+            self.structurebox.Disable()
+            self.formfactorbox.Clear()
+            self._populate_box( self.formfactorbox,
+                                self.model_list_box["Customized Models"])
+        
+    def _show_combox(self, event=None):
+        """
+        Show combox box associate with type of model selected
+        """
+        #if self.check_invalid_panel():
+        #    self.shape_rbutton.SetValue(True)
+        #    return
+
+        self._show_combox_helper()
+        self._on_select_model(event=None)
+        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
+        """
+        for models in list:
+            model= models()
+            name = model.__class__.__name__
+            if models.__name__!="NoStructure":
+                if hasattr(model, "name"):
+                    name = model.name
+                combobox.Append(name,models)
+        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:
+                value = 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 npts in that Qrange!!!  "
+                    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._undo.Enable(True)
+        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._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.parent.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 npts in that Qrange!!!  "
+                    wx.PostEvent(self.parent.parent, StatusEvent(status = msg ))
+        else:
+           tcrtl.SetBackgroundColour("pink")
+           msg = "Model Error:wrong value entered!!!"
+           wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
+        #self._undo.Enable(True)
+        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._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.mutifactor_text.SetLabel(form_factor().details[])
+                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.Clear()
+                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 sans.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
+            
+
+        ## 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.parent.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
+
+        # q value from qx and qy
+        radius= numpy.sqrt( self.data.qx_data * self.data.qx_data + 
+                            self.data.qy_data * self.data.qy_data )
+        #get unmasked index
+        index_data = (float(self.qmin.GetValue()) <= radius) & \
+                        (radius <= float(self.qmax.GetValue()))
+        index_data = (index_data) & (self.data.mask) 
+        index_data = (index_data) & (numpy.isfinite(self.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= "Cannot Plot :No or too little npts in that data range!!!  "
+            wx.PostEvent(self.parent.parent, StatusEvent(status = msg ))
+            self.fitrange = False
+            flag = False
+        else:
+            self.Npts_fit.SetValue(str(len(self.data.mask[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:#self.data.__class__.__name__ !="Data2D":
+                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.parent.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.parent.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.parent.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.parent.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)
+        """
+        #if self.check_invalid_panel():
+        #    return 
+        ## 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:
+            #self._undo.Enable(True)
+            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"
+        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 sans.models.dispersion_models import GaussianDispersion, ArrayDispersion
+        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[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)
+
+            # 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.parent.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)
+
+        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.parent.parent, StatusEvent(status=msg))  
+
+        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.
+        #TODO: total hack - fix this
+        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]
+        return basename
+    
+    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.:ayout 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
+        #if self.check_invalid_panel():
+        #    return
+        ##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)
+            # check smearing
+            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.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():
+                temp_smearer= self.current_smearer
+                ## 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.parent.parent, StatusEvent(status = msg ))
+        else:
+            # set relative text ctrs.
+            self.qmin.SetValue(str(self.qmin_x))
+            self.qmax.SetValue(str(self.qmax_x))
+            self.set_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.compute_chisqr(smearer=self.current_smearer)
+        #Re draw plot
+        self._draw_model()
+        
+    def get_images(self):
+        """
+        Get the images of the plots corresponding this panel for report
+        
+        : return graphs: list of figures
+        : TODO: Move to guiframe
+        """
+        # set list of graphs
+        graphs = []
+        canvases = []
+        # call gui_manager
+        gui_manager = self.parent.parent
+        # loops through the panels [dic]
+        for item1, item2 in gui_manager.panels.iteritems():
+             data_title = self.data.group_id
+             data_name = str(self.data.name).split(" [")[0]
+            # try to get all plots belonging to this control panel
+             try:
+                 title = ''
+                 # check titles (main plot) 
+                 if hasattr(item2,"data2D"):
+                     title = item2.data2D.title
+                 # and data_names (model plot[2D], and residuals)
+                 if item2.group_id == data_title or \
+                                title.count(data_name) or \
+                                item2.window_name.count(data_name) or \
+                                item2.window_caption.count(data_name):
+                     #panel = gui_manager._mgr.GetPane(item2.window_name)
+                     # append to the list
+                     graphs.append(item2.figure) 
+                     canvases.append(item2.canvas)     
+             except:
+                 # Not for control panels
+                 pass
+        # return the list of graphs
+        return graphs, canvases
+
+    def on_model_help_clicked(self,event):
+        """
+        on 'More details' button
+        """
+        from help_panel import  HelpWindow
+        import sans.models as models 
+        
+        # Get models help model_function path
+        path = models.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()
+            #name = self.model.__class__.__name__
+        frame = HelpWindow(None, -1,  pageToOpen=model_path)    
+        frame.Show(True)
+        if frame.rhelp.HasAnchor(name):
+            frame.rhelp.ScrollToAnchor(name)
+        else:
+           msg= "Model does not contains an available description "
+           msg +="Please try searching in the Help window"
+           wx.PostEvent(self.parent.parent, StatusEvent(status = msg ))      
+    
+    def on_pd_help_clicked(self, event):
+        """
+        Button event for PD help
+        """
+        from help_panel import  HelpWindow
+        import sans.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():
+            flag = self.get_paste()
+        elif event.CmdDown():
+            flag = self.get_copy()
+        else:
+            event.Skip()
+            return
+        # make event free
+        event.Skip()
+        
+            
+    def get_copy(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 
+            string = self._get_copy_helper(self.parameters, 
+                                           self.orientation_params)
+            content += string
+            
+            # go through the fittables
+            string = self._get_copy_helper(self.fittable_param, 
+                                           self.orientation_params_disp)
+            content += string
+
+            # go through the fixed params
+            string = self._get_copy_helper(self.fixed_param, 
+                                           self.orientation_params_disp)
+            content += string
+                
+            # go through the str params
+            string = self._get_copy_helper(self.str_parameters, 
+                                           self.orientation_params)
+            content += string
+
+        if wx.TheClipboard.Open():
+            wx.TheClipboard.SetData(wx.TextDataObject(str(content)))
+            data = wx.TextDataObject()
+            success = wx.TheClipboard.GetData(data)
+            text = data.GetText()
+            wx.TheClipboard.Close()
+    
+        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: 
+            # 2D
+            if self.data.__class__.__name__== "Data2D":
+                name = item[1]
+                value = item[2].GetValue()
+            # 1D
+            else:
+                ## for 1D all parameters except orientation
+                if not item[1] in orient_param:
+                    name = item[1]
+                    value = item[2].GetValue()
+            # add to the content
+            content += name + ',' + value + ':'
+            
+        return content
+    
+    def get_paste(self): 
+        """
+        Get the string copies of the param names and values in the tap
+        """  
+        context = {}
+        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
+                text = data.GetText()
+            # close clipboard
+            wx.TheClipboard.Close()
+            
+        # 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(',')
+                name = item[0]
+                value = item[1]
+                # Transfer the text to content[dictionary]
+                context[name] = value
+        
+        # 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)
+        self._copy_info(True)
+        return True
+    
+    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():
+                    item[2].SetValue(content[name])
+            # 1D
+            else:
+                ## for 1D all parameters except orientation
+                if not item[1] in orient_param:
+                    name = item[1]
+                    if name in content.keys():
+                        # Avoid changing combox content which needs special care
+                        value = content[name]
+                        item[2].SetValue(value)
+                        if item[2].__class__.__name__ == "ComboBox":
+                            if self.model.fun_list.has_key(value):
+                                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)
+                
Index: fittingview/src/sans/perspectives/fitting/batchfitpage.py
===================================================================
--- fittingview/src/sans/perspectives/fitting/batchfitpage.py	(revision 49c69de79aac6bbde0711203fe50ff3af1f0bb81)
+++ fittingview/src/sans/perspectives/fitting/batchfitpage.py	(revision 49c69de79aac6bbde0711203fe50ff3af1f0bb81)
@@ -0,0 +1,674 @@
+
+
+import sys
+import wx
+import wx.lib.newevent
+import numpy
+import copy
+import math
+import time
+from sans.models.dispersion_models import ArrayDispersion, GaussianDispersion
+from DataLoader.data_info import Data1D
+from sans.guiframe.events import StatusEvent    
+from sans.guiframe.events import NewPlotEvent  
+from sans.guiframe.utils import format_number,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
+
+import basepage
+from basepage import BasicPage
+from basepage import PageInfoEvent
+from sans.models.qsmearing import smear_selection
+from fitpage import FitPage
+
+class BatchFitPage(FitPage):
+    window_name = "BatchFit"
+    window_caption  = "BatchFit"
+
+    def __init__(self,parent, color='rand'):
+        """ 
+        Initialization of the Panel
+        """
+        FitPage.__init__(self, parent, color=color)
+        self.window_name = "BatchFit"
+        self.window_caption  = "BatchFit"
+        
+    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))
+        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,85))
+        """
+        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
+        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.")
+        """
+        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")
+        #box_description_4= wx.StaticText(self, -1,' ')
+        """
+        
+        """
+        sizer_fit.Add(box_description_1,0,0)
+        sizer_fit.Add(box_description_2,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) 
+        """
+        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 
+        #boxsizer1.Add( self.tcChi )  
+        """
+        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))
+        """
+        # 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.smear_description_2d.Show(True) 
+                #self.pinhole_smearer.Disable() 
+                self.slit_smearer.Disable()   
+                #self.enable_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,
+                                    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(wx.StaticText(self, -1, ''))
+        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 _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.btFit.SetFocus() 
+        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.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(True)
+                self._keep.Enable(True)
+                
+            temp_smear = None
+            """
+            #self._set_save_flag(True)
+            # Reset smearer, model and data
+            self._set_smear(self.data)
+            try:
+                # update smearer sizer
+                self.onSmear(None)
+                temp_smear = None
+                if self.enable_smearer.GetValue():
+                    # Set the smearer environments
+                    temp_smear = self.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, 
+                                        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
+
+        #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:           
+            ##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,
+                                                     qmin=float(self.qmin_x),
+                                                      qmax=float(self.qmax_x),
+                                                      draw=False)
+                elif not self._is_2D():
+                    self._manager.set_smearer(smearer=temp_smearer,
+                                              qmin=float(self.qmin_x),
+                                              uid=self.uid, 
+                                                 qmax= float(self.qmax_x),
+                                                 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:
+            #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(True)
+                self._keep.Enable(True)
+            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)
+            else:
+                self.slit_smearer.Enable(True) 
+                self.pinhole_smearer.Enable(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)
+                """
+                #number of total data points
+                self.Npts_total.SetValue(str(len(self.data.x)))
+                #default:number of data points selected to fit
+                self.Npts_fit.SetValue(str(len(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)
+                """
+                #number of total data points
+                self.Npts_total.SetValue(str(len(self.data.data)))
+                #default:number of data points selected to fit
+                self.Npts_fit.SetValue(str(len(self.data.data)))
+                """
+                self.btEditMask.Enable()  
+                self.EditMask_title.Enable() 
+        """
+        self.Npts_total.SetEditable(False)
+        self.Npts_total.SetBackgroundColour(\
+                                    self.GetParent().GetBackgroundColour())
+        
+        self.Npts_total.Bind(wx.EVT_MOUSE_EVENTS, self._npts_click)
+        #self.Npts_total.Disable()
+        """
+        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()
+            
+            #replace data plot on combo box selection
+            #by removing the previous selected data
+            #wx.PostEvent(self._manager.parent, NewPlotEvent(action="remove",
+            #                                        group_id=group_id, id=id))
+            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: fittingview/src/sans/perspectives/fitting/console.py
===================================================================
--- fittingview/src/sans/perspectives/fitting/console.py	(revision 49c69de79aac6bbde0711203fe50ff3af1f0bb81)
+++ fittingview/src/sans/perspectives/fitting/console.py	(revision 49c69de79aac6bbde0711203fe50ff3af1f0bb81)
@@ -0,0 +1,139 @@
+
+
+
+from sans.guiframe.events import StatusEvent 
+import time
+import wx
+import park
+from park.fitresult 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 = self.progress_time
+        
+    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.isbetter = False
+            self.improvement_time = t
+            
+            wx.PostEvent(self.parent, StatusEvent(status=\
+             "%d%% complete ..."%(p),type="progress"))
+       
+        # 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
+                wx.PostEvent(self.parent, StatusEvent(status=\
+                                                      "%d%% complete ..."%(p),
+                                                      type="progress"))
+       
+            elif 2 < dp <= 5:
+                if p//5 != self.progress_percent//5:
+                    wx.PostEvent(self.parent, StatusEvent(status=\
+                        "%d%% complete ..."%(5*(p//5)),type="progress"))
+                    self.progress_percent = p
+                    self.progress_time = t
+            else:
+                if p//10 != self.progress_percent//10:
+                    self.progress_percent = p
+                    self.progress_time = t
+                    wx.PostEvent(self.parent, StatusEvent(status=\
+                   "%d%% complete ..."%(10*(p//10)),type="progress"))
+        
+    def improvement(self):
+        """
+        Called when a result is observed which is better than previous
+        results from the fit.
+        """
+        self.isbetter = True
+    
+    def error(self, msg):
+        """
+        Model had an error; print traceback
+        """
+        if self.isbetter:
+            self.result.print_summary()
+        message = "Fit Abort:"
+        message = str(msg)+ " \n %s"%self.result.__str__()
+        wx.PostEvent(self.parent, StatusEvent(status=message,
+                                   info="error", type="stop"))
+    def finalize(self):
+        """
+        """
+        if self.isbetter:
+            self.result.print_summary()
+
+    def abort(self):
+        """
+        """
+        if self.isbetter:
+            self.result.print_summary()
+            
+        
+    def update_fit(self, msg=""):
+        """
+        """
+        self.elapsed_time = time.time() - self.elapsed_time
+        msg = " Updating fit ...\n result:\n %s \n"%self.result.__str__()
+        wx.PostEvent(self.parent, StatusEvent(status=msg, info="info",
+                                              type="progress"))
+        time.sleep(0.01)
+        
+    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: fittingview/src/sans/perspectives/fitting/fit_thread.py
===================================================================
--- fittingview/src/sans/perspectives/fitting/fit_thread.py	(revision 49c69de79aac6bbde0711203fe50ff3af1f0bb81)
+++ fittingview/src/sans/perspectives/fitting/fit_thread.py	(revision 49c69de79aac6bbde0711203fe50ff3af1f0bb81)
@@ -0,0 +1,95 @@
+
+import sys
+from 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,
+                  pars=None,
+                 completefn = None,
+                 updatefn   = None,
+                 yieldtime  = 0.01,
+                 worktime   = 0.01,
+                 ftol       = None):
+        CalcThread.__init__(self,completefn,
+                 updatefn,
+                 yieldtime,
+                 worktime)
+        self.handler = handler
+        self.fitter = fn
+        self.pars = pars
+        self.page_id = page_id
+        self.starttime = 0
+        self.updatefn = updatefn
+        #Relative error desired in the sum of squares.
+        self.ftol = ftol
+   
+    def isquit(self):
+        """
+        :raise KeyboardInterrupt: when the thread is interrupted
+        
+        """
+        try:
+            CalcThread.isquit(self)
+        except KeyboardInterrupt:
+            raise KeyboardInterrupt
+       
+    def compute(self):
+        """
+        Perform a fit 
+        """
+        msg = ""
+        try:
+            list_handler = []
+            list_curr_thread = [] 
+            list_ftol = []
+            list_map_get_attr = []
+            list_fit_function = []
+            list_q = []
+            for i in range(len(self.fitter)):
+                list_handler.append(None)
+                list_q.append(None)
+                list_curr_thread.append(None)
+                list_ftol.append(self.ftol)
+                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_handler, list_q, list_curr_thread,list_ftol)
+            result =  Pool(1).map(func=map_apply, 
+                               iterable=inputs)
+            #self.handler.starting_fit()
+            self.complete(result= result,
+                          page_id=self.page_id,
+                          pars = self.pars)
+           
+        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.
+            raise
+            #if self.handler is not None:
+            #    self.handler.error(msg=msg)
+        except:
+            raise
+            #if self.handler is not None:
+            #    self.handler.error(msg=str(sys.exc_value))
+           
+        
+    
Index: fittingview/src/sans/perspectives/fitting/fitpage.py
===================================================================
--- fittingview/src/sans/perspectives/fitting/fitpage.py	(revision 49c69de79aac6bbde0711203fe50ff3af1f0bb81)
+++ fittingview/src/sans/perspectives/fitting/fitpage.py	(revision 49c69de79aac6bbde0711203fe50ff3af1f0bb81)
@@ -0,0 +1,2990 @@
+
+
+import sys
+import wx
+import wx.lib.newevent
+import numpy
+import copy
+import math
+import time
+from sans.models.dispersion_models import ArrayDispersion, GaussianDispersion
+from DataLoader.data_info import Data1D
+from sans.guiframe.events import StatusEvent 
+from sans.guiframe.events import NewPlotEvent  
+from sans.guiframe.utils import format_number,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
+
+import basepage
+from basepage import BasicPage
+from basepage import PageInfoEvent
+from sans.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='rand'):
+        """ 
+        Initialization of the Panel
+        """
+        BasicPage.__init__(self, parent, color=color)
+        
+        ## draw sizer
+        self._fill_data_sizer()
+        self.is_2D = None
+        self.fit_started = False
+        # 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.fill_data_combobox(data_list=self.data_list)
+    
+    
+    def _fill_data_sizer(self):
+        """
+        fill sizer 0 with data info
+        """
+        box_description= wx.StaticBox(self, -1, 'I(q) Data Source')
+        boxsizer1 = wx.StaticBoxSizer(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 data_list:
+            qmin, qmax, npts = self.compute_data_range(data_list[0])
+            self.qmin_data_set, self.qmax_data_set = qmin, qmax
+            self.npts_data_set = npts
+        for data in self.data_list:
+            if data is not None:
+                self.compute_data_set_range(data)
+                self.dataSource.Append(str(data.name), clientData=data)
+                
+        print "fill_data_combox", self.qmin_data_set, self.qmax_data_set
+        self.dataSource.SetSelection(0)
+        self.on_select_data(event=None)
+                
+    def on_select_data(self, event=None):
+        """
+        """
+        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.btFit.SetLabel("Fit")
+        self.bind_fit_button()
+        
+    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))
+        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,85))
+        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
+        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.")
+        
+        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")
+        #box_description_4= wx.StaticText(self, -1,' ')
+        
+        
+        sizer_fit.Add(box_description_1,0,0)
+        sizer_fit.Add(box_description_2,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.tcChi    =  wx.StaticText(self, -1, "-", style=wx.ALIGN_LEFT)
+        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 
+        #boxsizer1.Add( self.tcChi )  
+        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))
+        
+        # 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.smear_description_2d.Show(True) 
+                #self.pinhole_smearer.Disable() 
+                self.slit_smearer.Disable()   
+                #self.enable_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,
+                                    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.")
+        """
+        self.theory_npts_tcrtl  = self.ModelTextCtrl(self, -1, size=(_BOX_WIDTH, 20), 
+                        style=wx.TE_PROCESS_ENTER, 
+                        text_enter_callback=self._onQrangeEnter)
+        self.theory_npts_tcrtl.SetValue(format_number(self.npts_x))
+        self.theory_npts_tcrtl.SetToolTipString("Number of point to plot.")
+        """
+        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(wx.StaticText(self, -1, ''))
+        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 _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)
+        
+        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,"1D Mode", 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)
+      
+        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, "Customized Models")
+                
+        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
+        self.shape_rbutton.SetValue(True)
+      
+        sizer_radiobutton = wx.GridSizer(2, 3, 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.LEFT, 20)
+        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.LEFT, 20)
+        
+        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)
+        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:
+            self._set_model_sizer_selection(self.model)
+        
+        sizer_selection.Add(self.text1)
+        sizer_selection.Add((5, 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((10,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_radiobutton)
+        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 _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
+        #sizer_select_dispers = wx.BoxSizer(wx.HORIZONTAL)  
+        model_disp = wx.StaticText(self, -1, 'Function')
+            
+        import sans.models.dispersion_models 
+        self.polydisp= sans.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) 
+        
+        if self.engine_type=="park":
+            self.text_disp_max.Show(True)
+            self.text_disp_min.Show(True)
+
+        for item in self.model.dispersion.keys():
+            if not item in self.model.orientation_params:
+                if not self.disp_cb_dict.has_key(item):
+                    self.disp_cb_dict[item]= None
+                name0="Distribution of " + item
+                name1=item+".width"
+                name2=item+".npts"
+                name3=item+".nsigmas"
+                if not self.model.details.has_key(name1):
+                    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.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)
+
+                        
+                        if self.engine_type=="park":
+                            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)
+                            #ix +=1
+                            #self.sizer4_4.Add((20,20), (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  item in self.model.orientation_params:
+                if not self.disp_cb_dict.has_key(item):
+                    self.disp_cb_dict[item]= None
+                name0="Distribution of " + item
+                name1=item+".width"
+                name2=item+".npts"
+                name3=item+".nsigmas"
+                
+                if not self.model.details.has_key(name1):
+                    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.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.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
+        flag = self._update_paramv_on_fit()         
+        self._onparamEnter_helper()
+        if not flag:
+            msg= "The parameters are invalid"
+            wx.PostEvent(self.parent.parent, StatusEvent(status= msg ))
+            return 
+        
+
+    def _onFit(self, event):     
+        """
+        Allow to fit
+        """
+        if event != None:
+            event.Skip()
+        if len(self.parent._manager.fit_thread_list)>0 and\
+                    self.parent._manager._fit_engine != "park":
+            msg = "The FitEnging will be set to 'ParkMC'\n"
+            msg += " to fit with more than one data set..."
+            wx.MessageBox(msg, 'Info')
+            #wx.PostEvent(self._manager.parent, StatusEvent(status=\
+            #                "Fitting: %s"%msg))
+            
+        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 not flag:
+            msg= "Fitting range or parameters are invalid"
+            wx.PostEvent(self.parent.parent, StatusEvent(status= msg, 
+                                                         type="stop"))
+            return 
+              
+        self.select_param(event =None)
+        
+        #Clear errors if exist from previous fitting
+        #self._clear_Err_on_Fit() 
+
+        # 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, fitproblem=None) 
+        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.btFit.SetLabel("Stop")
+        self.bind_fit_button()
+           
+   
+    def bind_fit_button(self):
+        """
+        bind the fit button to either fit handler or stop fit handler
+        """
+        self.btFit.Unbind(event=wx.EVT_BUTTON, id= self.btFit.GetId())
+        if self.btFit.GetLabel().lower() == "stop":
+            self.fit_started = True
+            self.btFit.SetForegroundColour('red')
+            self.btFit.Bind(event=wx.EVT_BUTTON, handler=self._StopFit,
+                             id=self.btFit.GetId())
+        elif self.btFit.GetLabel().lower() == "fit":
+            self.fit_started = False
+            self.btFit.SetDefault()
+            self.btFit.SetForegroundColour('black')
+            #self.btFit.SetBackgroundColour(self.default_bt_colour)
+            self.btFit.Bind(event=wx.EVT_BUTTON, handler=self._onFit, 
+                            id=self.btFit.GetId())
+        else:
+            msg = "FitPage: fit button has unknown label"
+            raise ValuerError, msg
+        self._manager._reset_schedule_problem(value=0)
+          
+    def is_fitting(self):
+        if self.fit_started:
+            self._StopFit(event=None)
+            
+    def _StopFit(self, event=None):
+        """
+        Stop fit 
+        """
+        #time.sleep(0.1)
+        if event != None:
+            event.Skip()
+        #if self.engine_type=="scipy":
+        self._manager.stop_fit(self.uid)
+        self._manager._reset_schedule_problem(value=0)
+        self._on_fit_complete()
+         
+    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.btFit.SetFocus() 
+        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.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(True)
+                self._keep.Enable(True)
+            #self._set_save_flag(True)
+            # Reset smearer, model and data
+            self._set_smear(self.data)
+            try:
+                # update smearer sizer
+                self.onSmear(None)
+                temp_smear = None
+                if self.enable_smearer.GetValue():
+                    # Set the smearer environments
+                    temp_smear = self.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, 
+                                        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 _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')
+            #wx.PostEvent(self._manager.parent, StatusEvent(status=\
+            #                "Parameters: %s"%msg))
+            return
+
+        #default flag
+        flag = False
+        self.fitrange = True
+        #get event object
+        tcrtl = event.GetEventObject()
+        
+        #wx.PostEvent(self._manager.parent, StatusEvent(status=" \
+        #                        updating ... ",type="update"))
+        #Clear msg if previously shown.
+        msg= ""
+        wx.PostEvent(self.parent.parent, StatusEvent(status = msg ))
+
+        if check_float(tcrtl):
+            flag = self._onparamEnter_helper() 
+            self.set_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, 
+                                              enable2D=self.enable2D,
+                                              uid=self.uid,
+                                             qmin= float(self.qmin_x),
+                                            qmax= float(self.qmax_x),
+                                            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.parent.parent, StatusEvent(status = msg ))
+             
+        self.save_current_state() 
+        return 
+   
+    def _onparamRangeEnter(self, event):
+        """
+        Check validity of value enter in the parameters range field
+        """
+        #if self.check_invalid_panel():
+        #    return
+        tcrtl= event.GetEventObject()
+        #Clear msg if previously shown.
+        msg= ""
+        wx.PostEvent(self.parent.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)
+                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.parent.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 _onQrangeEnter(self, event):
+        """
+        Check validity of value enter in the Q range field
+        """
+        #if self.check_invalid_panel():
+        #    return
+        tcrtl= event.GetEventObject()
+        #Clear msg if previously shown.
+        msg= ""
+        wx.PostEvent(self.parent.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.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.parent, StatusEvent(status = msg ))
+                    return 
+                
+            except:
+                tcrtl.SetBackgroundColour("pink")
+                msg= "Model Error:wrong value entered : %s"% sys.exc_value
+                wx.PostEvent(self.parent.parent, StatusEvent(status = msg ))
+                return 
+            #Check if # of points for theory model are valid(>0).
+            # check for 2d
+            if self.data is not None:
+                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.parent.parent, 
+                                     StatusEvent(status=msg))
+                        return
+                    else:
+                        self.data.mask = index_data
+                        #self.Npts_fit.SetValue(str(len(self.data.mask)))
+                        self.set_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])))
+            else:
+                self.npts_x = self.Npts_total.GetValue()
+                self._save_plotting_range()
+
+        else:
+           tcrtl.SetBackgroundColour("pink")
+           msg= "Model Error:wrong value entered!!!"
+           wx.PostEvent(self.parent.parent, StatusEvent(status = msg ))
+        
+        self._draw_model()
+        ##update chi2
+        #self.compute_chisqr(smearer= temp_smearer)
+        #self._undo.Enable(True)
+        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:
+                #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:
+                #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.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 self.model.fun_list.has_key(value):
+            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 sans.guiframe.local_perspectives.plotting.masking \
+        import MaskPanel as MaskDialog
+        
+        self.panel = MaskDialog(base=self, data=self.data, id=wx.NewId())
+        #self.panel.Bind(wx.EVT_CLOSE, self._draw_masked_model)
+        self.panel.ShowModal()
+        #wx.PostEvent(self.parent, event)
+        
+    def _draw_masked_model(self, event):
+        """
+        Draw model image w/mask
+        """
+        #event.Skip()
+        is_valid_qrange = self._update_paramv_on_fit()
+
+        if is_valid_qrange and self.model != None:
+            #self.panel.Show(0)
+            #self.panel.Destroy() # frame
+            self.panel.MakeModal(False)
+            event.Skip() 
+            # try re draw the model plot if it exists
+            self._draw_model()
+            self.set_npts2fit()
+        elif self.model == None:
+            #self.panel.Show(0)
+            #self.panel.Destroy() # frame
+            self.panel.MakeModal(False)
+            event.Skip()
+            self.set_npts2fit()
+            msg= "No model is found on updating MASK in the model plot... "
+            wx.PostEvent(self.parent.parent, StatusEvent(status = msg ))
+        else:
+            event.Skip()
+            msg = ' Please consider your Q range, too.'
+            self.panel.ShowMessage(msg)
+
+        
+    def _set_smear(self, data):
+        """
+        """
+        if data is None:
+            return
+        self.smearer = smear_selection(data, self.model)
+        self.disable_smearer.SetValue(True)
+        if self.smearer == None:
+            self.enable_smearer.Disable()
+        else:
+            self.enable_smearer.Enable()
+
+    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):
+        """
+        find the range that include all data  in the set
+        return the minimum and the maximum values
+        """
+        if data is not None:
+            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
+        
+    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"):
+                qmin = min(data.x)
+                # Maximum value of data  
+                qmax = max(data.x)
+                npts = len(data.x)
+            else:
+                qmin = 0
+                x = max(math.fabs(data.xmin), math.fabs(data.xmax)) 
+                y = max(math.fabs(data.ymin), math.fabs(data.ymax))
+                ## Maximum value of data  
+                qmax = math.sqrt(x*x + y*y)
+                npts = len(data.data)
+        print "compute single data range", qmin, qmax
+        return qmin, qmax, npts
+            
+    
+    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(True)
+                self._keep.Enable(True)
+            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)
+            else:
+                self.slit_smearer.Enable(True) 
+                self.pinhole_smearer.Enable(True)      
+                
+            self.formfactorbox.Enable()
+            self.structurebox.Enable()
+            data_name = self.data.name
+            #data_min, data_max, npts = self.compute_data_range(self.data)
+            data_min, data_max = self.qmin_data_set, self.qmax_data_set
+            npts =  self.npts_data_set
+            #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.Npts_total.Disable()
+        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))
+        if data_min is None:
+            data_min = ""
+        if data_max is None:
+            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()
+            
+            #replace data plot on combo box selection
+            #by removing the previous selected data
+            wx.PostEvent(self._manager.parent, NewPlotEvent(action="remove",
+                                                    group_id=group_id, id=id))
+            #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()
+    
+    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
+        """
+        self.reset_page_helper(state)
+        #import sans.guiframe.gui_manager
+        #evt = ModelEventbox(model=state.model)
+        #wx.PostEvent(self.event_owner, evt)  
+   
+        if self.engine_type != None:
+            self._manager._on_change_engine(engine=self.engine_type)
+
+        self.select_param(event = None) 
+        #Save state_fit
+        self.save_current_state_fit()
+        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 set_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 get_param_list(self):
+        """
+        :return self.param_toFit: list containing  references to TextCtrl
+            checked.Theses TextCtrl will allow reference to parameters to fit.
+        
+        :raise: if return an empty list of parameter fit will nnote work 
+            properly so raise ValueError,"missing parameter to fit"
+        """
+        if self.param_toFit !=[]:
+            return self.param_toFit
+        else:
+            msg = "missing parameters to fit"  
+            wx.MessageBox(msg, 'warning')
+            return False
+            #raise ValueError,"missing parameters to fit"    
+      
+    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
+   
+        """
+        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  
+            if self.engine_type == "park":  
+                npt_fit = float(self.get_npts2fit()) 
+                if npt_fit > 0:
+                    chisqr =chisqr/npt_fit    
+            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
+        #set the panel when fit result are float not list
+        if out.__class__== numpy.float64:
+            self.param_toFit[0][2].SetValue(format_number(out, True))
+            
+            if self.param_toFit[0][4].IsShown and not self.is_mac:
+                self.param_toFit[0][4].Hide()
+            if cov !=None :
+                self.text2_3.Show(True)
+                try:
+                    if self.enable_disp.GetValue():
+                        if hasattr(self,"text_disp_1" ):
+                            if self.text_disp_1 !=None:
+                                self.text_disp_1.Show(True)
+                except:
+                    pass
+
+                if cov[0]==None or  not numpy.isfinite(cov[0]): 
+                    if self.param_toFit[0][3].IsShown and not self.is_mac:
+                        self.param_toFit[0][3].Hide()
+                else:                    
+                    self.param_toFit[0][3].Show(True)               
+                    self.param_toFit[0][4].Show(True)
+                    self.param_toFit[0][4].SetValue(format_number(cov[0], True))
+                    has_error = True
+        else:
+
+            i = 0
+            #Set the panel when fit result are list
+            for item in self.param_toFit:     
+                if len(item)>5 and item != None:     
+                    ## 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)<=len(self.param_toFit) and out[ind] !=None:   
+                        val_out = format_number(out[ind], True)                  
+                        item[2].SetValue(val_out)
+
+
+                    if(cov !=None):
+                        
+                        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         
+        #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()          
+        
+        #self._lay_out() 
+        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)    
+        #PostStatusEvent     
+        #msg = "Fit completed!dddd "
+        #wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
+
+    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.check_invalid_panel():
+        #    return
+        if self.model ==None:
+            self.disable_smearer.SetValue(True)
+            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()
+        
+        ##Calculate chi2
+        temp_smearer = self.current_smearer
+        #self.compute_chisqr(smearer= temp_smearer)
+            
+        # 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,
+                                 enable2D=self.enable2D,
+                                 qmin=float(self.qmin_x),
+                                 qmax= float(self.qmax_x),
+                                 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()           
+        #self._undo.Enable(True)
+        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.check_invalid_panel():
+        #    return
+
+        if self.model ==None:
+            self.disable_smearer.SetValue(True)
+            
+            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()        
+        ##Calculate chi2
+        #self.compute_chisqr(smearer= self.current_smearer)  
+        # 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)
+        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
+        temp_smearer = None
+        # 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: 
+            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: 
+            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)
+        #temp_smearer = self.current_smearer
+        ## set smearing value whether or not the data contain the smearing info
+        self._manager.set_smearer(smearer=self.current_smearer, 
+                                 enable2D=self.enable2D,
+                                 qmin=float(self.qmin_x), 
+                                 qmax= float(self.qmax_x),
+                                 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)
+            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()
+        
+        temp_smearer = None
+        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
+        self.current_smearer = smear_selection(data, self.model)
+        
+        if self.enable_smearer.GetValue():
+            if hasattr(self.data,"dxl"):
+                
+                msg= ": Resolution smearing parameters"
+            if hasattr(self.data,"dxw"):
+                msg= ": Slit smearing parameters"
+            if self.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_description_dqdata.Show(True)
+            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)
+            
+        self._show_smear_sizer()
+        
+        self.sizer_set_smearer.Layout()
+        self.Layout()
+        ## set smearing value whether or not the data contain the smearing info
+        self._manager.set_smearer(uid=self.uid, smearer=temp_smearer,
+                        enable2D=self.enable2D,
+                        qmin= float(self.qmin_x),
+                        qmax= float(self.qmax_x), 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_complete_chisqr(self, event):  
+        """
+        print result chisqr 
+        
+        :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 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:
+                    ## 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 )
+                #if len(self.fittable_param)>0:
+                for item in self.fittable_param:
+                    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:
+                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) 
+     
+    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():
+                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():
+                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:
+            #self._undo.Enable(True)
+            ## post state to fit panel
+            event = PageInfoEvent(page = self)
+            wx.PostEvent(self.parent, event) 
+    
+    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()
+        
+        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 self.model.details.has_key(item):
+                    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.")
+                        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.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.")
+                    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 self.model.details.has_key(item):
+                        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)
+                        
+                    ##[cb state, name, value, "+/-", error of fit, min, max , units]
+                    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]:')
+                sizer.Add(orient_angle,(iy, ix),(1,1), 
+                          wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15) 
+                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 item in self.disp_list:
+                    ##prepare a spot to store min max
+                    if not self.model.details.has_key(item):
+                        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(False)
+                    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.")
+                    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 self.model.details.has_key(item):
+                        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)
+                                          
+                    ##[cb state, name, value, "+/-", error of fit,min,max,units]
+                    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
+        
+        #Display units text on panel
+        for item in keys:   
+            if self.model.details.has_key(item):
+                self.text2_4.Show()
+        #Fill the list of fittable parameters
+        self.select_all_param(event=None)
+
+        self.save_current_state_fit()
+        boxsizer1.Add(sizer)
+        self.sizer3.Add(boxsizer1,0, wx.EXPAND | wx.ALL, 10)
+        self.sizer3.Layout()
+        self.Layout()
+        #self.Refresh()
+
+    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.parent.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() == "1D Mode":
+            self.model_view.SetLabel("2D Mode")
+            self.enable2D = True
+              
+        else:
+            self.model_view.SetLabel("1D Mode")
+            self.enable2D = False
+
+        self.set_model_param_sizer(self.model)
+        self._set_sizer_dispersion()  
+        self._draw_model()
+        
+        
+        self.state.enable2D =  copy.deepcopy(self.enable2D)
+        
+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: fittingview/src/sans/perspectives/fitting/fitpanel.py
===================================================================
--- fittingview/src/sans/perspectives/fitting/fitpanel.py	(revision 49c69de79aac6bbde0711203fe50ff3af1f0bb81)
+++ fittingview/src/sans/perspectives/fitting/fitpanel.py	(revision 49c69de79aac6bbde0711203fe50ff3af1f0bb81)
@@ -0,0 +1,572 @@
+
+import numpy
+import string 
+import wx
+import sys
+#from wx.lib.flatnotebook import FlatNotebook as nb
+from wx.aui import AuiNotebook as nb
+
+from sans.guiframe.panel_base import PanelBase
+from sans.guiframe.events import PanelOnFocusEvent
+from sans.guiframe.events import StatusEvent
+
+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.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.fit_engine_type = "scipy"
+        ## 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
+        """
+        for uid, page in self.opened_pages.iteritems():
+            data = page.get_data()
+            # state must be cloned
+            state = page.get_state().clone()
+            if data 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 
+        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 != 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 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.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.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(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:
+                panel = self._manager.add_fit_page(data=state.data)
+                # add data associated to the page created
+                if panel is not None:  
+                    self._manager.store_data(uid=panel.uid, 
+                                             data=state.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 dict: dictionnary made of model name as key and model class
+             as value
+         """
+         self.model_list_box = dict
+        
+    def get_current_page(self):
+        """
+        :return: the current page selected
+        
+        """
+        return self.GetPage(self.GetSelection())
+    
+    def add_sim_page(self):
+        """
+        Add the simultaneous fit page
+        """
+        from simfitpage import SimultaneousFitPage
+        page_finder= self._manager.get_page_finder()
+        self.sim_page = SimultaneousFitPage(self,page_finder=page_finder, id=-1)
+        self.sim_page.uid = wx.NewId()
+        self.AddPage(self.sim_page,"Simultaneous Fit", True)
+        self.sim_page.set_manager(self._manager)
+        self.enable_close_button()
+        return self.sim_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)
+        #Increment index of fit page
+        self.fit_page_index += 1
+        index = self.fit_page_index
+        panel.uid = wx.NewId()
+        panel.populate_box(dict=self.model_list_box)
+        panel.set_manager(self._manager)
+        caption = str(panel.window_caption) + " " + str(index)
+        self.AddPage(panel, caption, select=True)
+        self.opened_pages[panel.uid] = panel
+        self.set_engine_helper(panel=panel)
+        self.enable_close_button()
+        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()
+                #stop the fitting before deleting the page
+                page.is_fitting()
+                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 = NewId()
+                data_2d_list.append(data)
+        page = None
+        for p in self.opened_pages.values():
+            #check if the selected data existing in the fitpanel
+            pos = self.GetPageIndex(page)
+            if p.get_data() is None:
+                page = p
+                break
+               
+        if data_1d_list and data_2d_list:
+            # need to warning the user that this batch is a special case
+            from .fitting_widgets 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)
+            
+        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
+            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 page.get_data() is None:
+                #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.set_data(data)
+                    page.fill_data_combobox(data_list)
+                    self.SetPageText(pos, str(data.name))
+                    self.SetSelection(pos)
+                    return page
+                # Todo: Need to set different window name when has same data
+                # Todo: to catch page with same data even if it is not on the top.
+                """
+                elif page.get_data().id == data.id:
+                    msg = "Data already existing in the fitting panel"
+                    wx.PostEvent(self._manager.parent, 
+                                 StatusEvent(status=msg, info='warning')) 
+                    self.SetSelection(pos)
+                    return page
+                """
+        page = self.add_empty_page()
+        pos = self.GetPageIndex(page)
+        page.fill_data_combobox(data_list)
+        #page.set_data(data)
+        self.SetPageText(pos, str(data.name))
+        self.opened_pages[page.uid] = page
+        
+        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.__class__.__name__ == "FitPage":
+            self.fit_page_index -= 1
+        else:
+            self.batch_page_index -= 1
+        ## closing other pages
+        state = selected_page.createMemento()
+        page_name = selected_page.window_name
+        page_finder = self._manager.get_page_finder() 
+        fitproblem = None
+        ## 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]
+        ##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
+                
+        #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 
+      
+  
Index: fittingview/src/sans/perspectives/fitting/fitproblem.py
===================================================================
--- fittingview/src/sans/perspectives/fitting/fitproblem.py	(revision 49c69de79aac6bbde0711203fe50ff3af1f0bb81)
+++ fittingview/src/sans/perspectives/fitting/fitproblem.py	(revision 49c69de79aac6bbde0711203fe50ff3af1f0bb81)
@@ -0,0 +1,250 @@
+import copy 
+
+class FitProblem:
+    """  
+    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):
+        """
+        contains information about data and model to fit
+        """
+        ## data used for fitting
+        self.fit_data = None
+        #list of data  in case of batch mode
+        self.data_list = []
+        #dictionary of data id and location to corresponding fitproblem
+        self.fitproblem_pointers = {}
+        self.theory_data = None
+        ## the current model
+        self.model = None
+        self.model_index = 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 = None
+        self.fit_tab_caption = ''
+        ## fitting range
+        self.qmin = None
+        self.qmax = None
+        self.result = []
+        
+        # 1D or 2D
+        self.enable2D = False
+        
+    def clone(self):
+        """
+        copy fitproblem
+        """
+        
+        obj          = FitProblem()
+        model= None
+        if self.model!=None:
+            model = self.model.clone()
+        obj.model = model
+        obj.fit_data = copy.deepcopy(self.fit_data)
+        obj.theory_data = copy.deepcopy(self.theory_data)
+        obj.model = copy.deepcopy(self.model)
+        obj.schedule = copy.deepcopy(self.schedule)
+        obj.list_param = copy.deepcopy(self.list_param)
+        obj.smearer = copy.deepcopy(self.smearer)
+        obj.plotted_data = copy.deepcopy(self.plotted_data)
+        obj.qmin = copy.deepcopy(self.qmin)
+        obj.qmax = copy.deepcopy(self.qmax)
+        obj.enable2D = copy.deepcopy(self.enable2D)
+        return obj
+        
+    def set_smearer(self, smearer):
+        """
+        save reference of  smear object on fitdata
+        
+        :param smear: smear object from DataLoader
+        
+        """
+        self.smearer= smearer
+       
+    def get_smearer(self):
+        """
+        return smear object
+        """
+        return self.smearer
+    
+    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
+        
+    def get_model(self):
+        """
+        :return: saved model
+        
+        """
+        return self.model
+  
+    def set_index(self, index):
+        """
+        set index of the model name
+        """
+        self.model_index = index
+        
+    def get_index(self):
+        """
+        get index of the model name
+        """
+        return self.model_index
+    
+    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 set_enable2D(self, enable2D):
+        """
+        """
+        self.enable2D = enable2D
+         
+    def get_theory_data(self):
+        """
+        :return: list of data dList
+        
+        """
+        return self.theory_data
+
+    def set_fit_data(self,data):
+        """ 
+        save a copy of the data select to fit
+        
+        :param data: data selected
+        
+        """
+        self.fit_data = data
+            
+    def get_fit_data(self):
+        """
+        """
+        return self.fit_data
+    
+    def set_fit_data_list(self, data_list):
+        """ 
+        save a copy of a list of data
+        
+        :param data_list: list of data
+        
+        """
+        self.data_list = data_list
+            
+    def get_fit_data_list(self):
+        """
+        """
+        return self.data_list
+    
+    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 
+        """
+        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 get_enable2D(self):
+        """
+        """
+        return self.enable2D
+    
+    def get_pointer_to_fitproblem(self):
+        """
+        return dictionary of id of fitproblem
+        """
+        return self.fitproblem_pointers
+    
+    def set_pointer_to_fitproblem(self, data_id, page_id):
+        """
+        """
+        self.fitproblem_pointers[data_id] = page_id
+        
+    def set_result(self, result):
+        """
+        set a list of result
+        """
+        self.result = result
+        
+    def get_result(self):
+        """
+        get result 
+        """
+        return self.result
+        
+   
Index: fittingview/src/sans/perspectives/fitting/fitting.py
===================================================================
--- fittingview/src/sans/perspectives/fitting/fitting.py	(revision 49c69de79aac6bbde0711203fe50ff3af1f0bb81)
+++ fittingview/src/sans/perspectives/fitting/fitting.py	(revision 49c69de79aac6bbde0711203fe50ff3af1f0bb81)
@@ -0,0 +1,1822 @@
+
+
+################################################################################
+#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 wx
+import logging
+import numpy
+import string
+import time
+from copy import deepcopy
+import models
+import fitpage
+
+
+from DataLoader.loader import Loader
+from sans.guiframe.dataFitting import Data2D
+from sans.guiframe.dataFitting import Data1D
+from sans.guiframe.events import NewPlotEvent 
+from sans.guiframe.events import StatusEvent  
+from sans.guiframe.events import EVT_SLICER_PANEL
+from sans.guiframe.events import EVT_SLICER_PARS_UPDATE
+from sans.guiframe.gui_style import GUIFRAME_ID
+from sans.guiframe.plugin_base import PluginBase 
+
+from .console import ConsoleUpdate
+from .fitproblem import FitProblem
+from .fitpanel import FitPanel
+from .fit_thread import FitThread
+from .pagestate import Reader
+from .fitpage import Chi2UpdateEvent
+
+DEFAULT_BEAM = 0.005
+DEFAULT_QMIN = 0.001
+DEFAULT_QMAX = 0.13
+DEFAULT_NPTS = 50
+MAX_NBR_DATA = 4
+SANS_F_TOL = 5e-05
+
+(PageInfoEvent, EVT_PAGE_INFO)   = wx.lib.newevent.NewEvent()
+
+if sys.platform.count("darwin")==0:
+    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.fit_thread_list = {}
+        self.residuals = None
+        self.fit_panel = None
+        # Start with a good default
+        self.elapsed = 0.022
+        # the type of optimizer selected, park or scipy
+        self.fitter  = None
+        self.fit_panel = None
+        #let fit ready
+        self.fitproblem_count = 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 = 'scipy'
+        ## Relative error desired in the sum of squares (float); scipy only
+        self.ftol = SANS_F_TOL
+        #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.index_model = 0
+        #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.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") 
+        
+    def on_batch_selection(self, flag):
+        """
+        switch the the notebook of batch mode or not
+        """
+        self.batch_on = flag
+        if self.batch_on:
+            if self.fit_panel is not None:
+                self.fit_panel.batch_on = flag
+        
+    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()
+        id1 = wx.NewId()
+        simul_help = "Simultaneous Fit"
+        self.menu1.Append(id1, '&Simultaneous Fit',simul_help)
+        wx.EVT_MENU(owner, id1, self.on_add_sim_page)
+        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)
+        
+        self.menu1.FindItemById(self.scipy_id).Check(True)
+        self.menu1.FindItemById(self.park_id).Check(False)
+        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 [LeastSq Only]", 
+                                   ftol_help) 
+        wx.EVT_MENU(owner, self.id_tol,  self.show_ftol_dialog)
+        
+        
+        #create  menubar items
+        return [(self.menu1, self.sub_menu)]
+               
+    def on_add_sim_page(self, event):
+        """
+        Create a page to access simultaneous fit option
+        """
+        if self.sim_page != None:
+            msg= "Simultaneous Fit page already opened"
+            wx.PostEvent(self.parent, StatusEvent(status= msg))
+            return 
+        
+        self.sim_page= self.fit_panel.add_sim_page()
+        
+    def help(self, evt):
+        """
+        Show a general help dialog. 
+        """
+        from help_panel import  HelpWindow
+        frame = HelpWindow(None, -1, 'HelpWindow')    
+        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
+                
+        """
+        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 []   
+
+
+    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.fit_panel = FitPanel(parent=self.parent, manager=self)
+        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)
+       
+        #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)
+        #loader.associate_file_reader(".svs", self.state_reader)
+        #from sans.perspectives.calculator.sld_panel import SldPanel
+        #Send the fitting panel to guiframe
+        self.mypanels.append(self.fit_panel) 
+        #self.mypanels.append(SldPanel(parent=self.parent, base=self.parent))
+        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:
+                for data in selected_data_list:
+                    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_top_panel(self):
+        """
+        Close default (welcome) panel
+        """
+        if 'default' in self.parent.panels:
+            self.parent.on_close_welcome_panel()
+
+             
+    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.fit_panel.add_default_pages()
+                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_name
+                self.store_data(uid=page.uid, data=page.get_data(),
+                        data_list=page.get_data_list(), 
+                        caption=page.window_name)
+                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 save_fit_state(self, filepath, fitstate):  
+        """
+        save fit page state into file
+        """
+        self.state_reader.write(filename=filepath, fitstate=fitstate)
+        
+    def set_fit_range(self, uid, qmin, qmax):
+        """
+        Set the fitting range of a given page
+        """
+        self.page_finder[uid].set_range(qmin=qmin, qmax=qmax)
+                    
+    def schedule_for_fit(self,value=0, uid=None,fitproblem =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 fitproblem: fitproblem to schedule or not to fit
+        
+        """   
+        if fitproblem !=None:
+            fitproblem.schedule_tofit(value)
+        else:
+            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:
+                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 string.find(item,".")!=-1:
+            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..." 
+        self.menu1.SetHelpString(self.id_tol, ftol_help)
+        
+    def show_ftol_dialog(self, event=None):
+        """
+        Dialog to select ftol for Scipy
+        """
+        #if event != None:
+        #    event.Skip()
+        from ftol_dialog import ChangeFtol
+        panel = ChangeFtol(self.parent, self)
+        panel.ShowModal()
+                  
+    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
+        if  self.sim_page is not None and uid == self.sim_page.uid:
+            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, qmin=None, qmax=None, draw=True, 
+                    enable2D=False):
+        """
+        Get a smear object and store it to a fit problem
+        
+        :param smearer: smear object to allow smearing data
+        
+        """   
+        if uid not in self.page_finder.keys():
+            msg = "Cannot find ID: %s in page_finder" % str(uid)
+            raise ValueError, msg
+        self.page_finder[uid].set_smearer(smearer)
+        self.page_finder[uid].set_enable2D(enable2D)
+        if draw:
+            ## draw model 1D with smeared data
+            data =  self.page_finder[uid].get_fit_data()
+            model = self.page_finder[uid].get_model()
+            if model is None:
+                return
+            enable1D = True
+            enable2D = self.page_finder[uid].get_enable2D()
+            if enable2D:
+                enable1D = False
+
+            ## if user has already selected a model to plot
+            ## redraw the model with data smeared
+            smear = self.page_finder[uid].get_smearer()
+            self.draw_model(model=model, data=data, page_id=uid, smearer=smear,
+                enable1D=enable1D, enable2D=enable2D,
+                qmin=qmin, qmax=qmax)
+            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,
+                   toggle_mode_on=False,
+                   qmin=DEFAULT_QMIN, qmax=DEFAULT_QMAX, 
+                   qstep=DEFAULT_NPTS,
+                   update_chisqr=True):
+        """
+        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]
+             
+        """
+        if data.__class__.__name__ == "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, 
+                               toggle_mode_on=toggle_mode_on,
+                               state=state,
+                               qstep=qstep,
+                               update_chisqr=update_chisqr)
+        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,
+                                state=state,
+                                toggle_mode_on=toggle_mode_on,
+                                qstep=qstep,
+                                update_chisqr=update_chisqr)
+            
+    def onFit(self, uid=None):
+        """
+        perform fit 
+        """
+        ##  count the number of fitproblem schedule to fit 
+        fitproblem_count = 0
+        for value in self.page_finder.values():
+            if value.get_scheduled() == 1:
+                fitproblem_count += 1
+                
+        ## if simultaneous fit change automatically the engine to park
+        if fitproblem_count > 1:
+            self._on_change_engine(engine='park')
+            
+        self.fitproblem_count = fitproblem_count  
+          
+        from sans.fit.Fitting import Fit
+        fitter = Fit(self._fit_engine)
+        
+        if self._fit_engine == "park":
+            engineType = "Simultaneous Fit"
+        else:
+            engineType = "Single Fit"
+            
+        fitter_list = []
+        fproblemId = 0
+        self.current_pg = None
+        list_page_id = []
+        for page_id, value 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 engineType == "Single Fit":
+                if page_id != uid:
+                    continue
+            try:
+                if value.get_scheduled() == 1:
+                    #Get list of parameters name to fit
+                    pars = []
+                    templist = []
+                    
+                    page = self.fit_panel.get_page_by_id(page_id)
+                    templist = page.get_param_list()
+                    # missing fit parameters
+                    #if not templist:
+                    #    return
+                    # have the list
+                    for element in templist:
+                        name = str(element[1])
+                        pars.append(name)
+                    #Set Engine  (model , data) related to the page on 
+                    self._fit_helper(value=value, pars=pars,
+                                     fitter=fitter,
+                                     fitter_list=fitter_list,
+                                      fitproblem_id=fproblemId,
+                                      title=engineType) 
+                    list_page_id.append(page_id)
+                    fproblemId += 1 
+                    current_page_id = page_id
+            except:
+                #raise
+                msg= "%s error: %s" % (engineType, sys.exc_value)
+                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
+                                                      type="stop"))
+                return 
+        ## 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)
+        ## perform single fit
+        if fitproblem_count == 1:
+            calc_fit = FitThread(handler = handler,
+                                    fn=fitter_list,
+                                    pars=pars,
+                                    page_id=list_page_id,
+                                    completefn=self._single_fit_completed,
+                                    ftol=self.ftol)
+        else:
+            current_page_id = self.sim_page.uid
+            ## Perform more than 1 fit at the time
+            calc_fit = FitThread(handler=handler,
+                                    fn=fitter_list,
+                                    page_id=list_page_id,
+                                    updatefn=handler.update_fit,
+                                    completefn=self._simul_fit_completed,
+                                    ftol=self.ftol)
+        self.fit_thread_list[current_page_id] = calc_fit
+        calc_fit.queue()
+        msg = "Fitting is in progress..."
+        wx.PostEvent( self.parent, StatusEvent(status=msg, type="progress" ))
+        
+        self.ready_fit(calc_fit=calc_fit)
+        
+    def ready_fit(self, calc_fit):
+        """
+        Ready for another fit
+        """
+        if self.fitproblem_count != None and self.fitproblem_count > 1:
+            calc_fit.ready(2.5)
+            
+        else:
+            time.sleep(0.4)
+            
+    def remove_plot(self, uid, theory=False):
+        """
+        remove model plot when a fit page is closed
+        """
+        fitproblem = self.page_finder[uid]
+        data = fitproblem.get_fit_data()
+        model = fitproblem.get_model()
+        plot_id = None
+        if model is not None:
+            plot_id = data.id + name
+        if theory:
+            plot_id = data.id 
+        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=None, data_list=None, caption=None):
+        """
+        Helper to save page reference into the plug-in
+        
+        :param page: page to store
+        
+        """
+        if data_list is None:
+            data_list = []
+        #create a fitproblem storing all link to data,model,page creation
+        if not uid in self.page_finder.keys():
+            self.page_finder[uid] = FitProblem()
+        self.page_finder[uid].set_fit_data(data)
+        self.page_finder[uid].set_fit_data_list(data_list)
+        self.page_finder[uid].set_fit_tab_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()
+            page_caption = page.window_name
+            # add data associated to the page created
+            if page != None:  
+                self.store_data(uid=page.uid, caption=page_caption,
+                                data=page.get_data())
+                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"))
+            self.set_top_panel()
+        except:
+            raise
+            #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
+        """
+        page = self.fit_panel.set_data(data)
+        page_caption = page.window_name
+        #append Data1D to the panel containing its theory
+        #if theory already plotted
+        if page.uid in self.page_finder:
+            theory_data = self.page_finder[page.uid].get_theory_data()
+            data = page.get_data()
+            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=page.get_data(),
+                        data_list=page.get_data_list(), 
+                        caption=page.window_name)
+        if self.sim_page is not None:
+            self.sim_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.
+        """
+        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 _add_page_onmenu(self, name, fitproblem=None):
+        """
+        Add name of a closed page of fitpanel in a menu 
+        """
+        list = self.menu1.GetMenuItems()
+        for item in list:
+            if name == item.GetItemLabel():
+                self.closed_page_dict[name][1] = fitproblem
+                
+        if not name in self.closed_page_dict.keys():    
+            # Post paramters
+            event_id = wx.NewId()
+            self.menu1.Append(event_id, name, "Show %s fit panel" % name)
+            self.closed_page_dict[name]= [event_id, fitproblem]
+            wx.EVT_MENU(self.parent, event_id, self._open_closed_page)
+        
+    def _open_closed_page(self, event):    
+        """
+        reopen a closed page
+        """
+        for name, value in self.closed_page_dict.iteritems():
+            if event.GetId() in value:
+                uid,fitproblem = value
+                if name !="Model":
+                    data= fitproblem.get_fit_data()
+                    page = self.fit_panel.add_fit_page(data=data, reset=True)
+                    if fitproblem != None:
+                        self.page_finder[uid] = fitproblem
+                        if self.sim_page != None:
+                            self.sim_page.draw_page()
+                            
+                else:
+                    model = fitproblem
+                    self.fit_panel.add_model_page(model=model, topmenu=True,
+                                                  reset= True)
+                    break
+    
+    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:
+            self.page_finder[uid].schedule_tofit(value)
+                
+    def _fit_setter(self, data, value, fitter, fit_id, pars):
+        """
+        """
+        model = value.get_model()
+        smearer = value.get_smearer()
+        qmin, qmax = value.get_range()
+        print "fitter_setter", qmin, qmax
+        #Extra list of parameters and their constraints
+        listOfConstraint = []
+        
+        param = value.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]))
+               
+        #Do the single fit
+        fitter.set_model(deepcopy(model), fit_id,
+                               pars, 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)
+        value.clear_model_param()
+            
+            
+    def _fit_helper(self, pars, value, fitproblem_id, fitter_list,
+                    fitter=None, title="Single Fit " ):
+        """
+        helper for fitting
+        """
+        from sans.fit.Fitting import Fit
+        self.fit_id = 0
+        data_list = value.get_fit_data_list()
+        pointer_to_fitproblem = value.get_pointer_to_fitproblem()
+        #Create list of parameters for fitting used
+        templist = []
+        for single_data in data_list:
+            fitter = Fit(self._fit_engine)
+            try:
+                if single_data.id in pointer_to_fitproblem:
+                     v = self.page_finder[pointer_to_fitproblem[single_data.id]]
+                     self._fit_setter(data=single_data, value=v, 
+                                     fitter=fitter,
+                                     pars=pars,
+                                      fit_id=self.fit_id)
+                else:
+                    self._fit_setter(data=single_data, value=value, 
+                                     fitter=fitter,
+                                     pars=pars,
+                                      fit_id=self.fit_id)
+            except:
+                raise
+                #msg = title + " error: %s" % sys.exc_value
+                #wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
+            fitter_list.append(fitter)
+            self.fit_id += 1
+            
+    def _onSelect(self,event):
+        """ 
+        when Select data to fit a new page is created .Its reference is 
+        added to self.page_finder
+        """
+        self.panel = event.GetEventObject()
+        Plugin.on_perspective(self, event=event)
+        for plottable in self.panel.graph.plottables:
+            if plottable.__class__.__name__ in ["Data1D", "Theory1D"]:
+                data_id = self.panel.graph.selected_plottable
+                if plottable == self.panel.plots[data_id]:
+                    data = plottable
+                    self.add_fit_page(data=[data])
+                    return
+            else:
+                data = plottable
+                self.add_fit_page(data=[data])
+        self.set_top_panel()
+            
+    def update_fit(self, result=None, msg=""):
+        """
+        """
+        print "update_fit result", result
+        
+    def _batch_single_fit_complete_helper(self,result, pars, page_id, 
+                                          elapsed=None):
+        """
+        Fit result are display in batch mode
+        """
+        self._update_fit_button(page_id)
+        msg = "Single Fitting complete "
+        wx.PostEvent(self.parent, StatusEvent(status=msg, info="info",
+                                                      type="stop"))
+        if self.batch_on:
+            batch_result = {"Chi2":[]}
+            for index  in range(len(pars)):
+                    batch_result[pars[index]] = []
+                    batch_result["error on %s" % pars[index]] = []
+            for res in result:
+                batch_result["Chi2"].append(res.fitness)
+                for index  in range(len(pars)):
+                    batch_result[pars[index]].append(res.pvec[index])
+                    item = res.stderr[index]
+                    batch_result["error on %s" % pars[index]].append(item)
+              
+            pid = page_id[0]
+            self.page_finder[pid].set_result(result=batch_result)      
+            self.parent.on_set_batch_result(data=batch_result, 
+                                            name=self.sub_menu)
+            
+        
+    def _single_fit_completed(self, result, pars, page_id, elapsed=None):
+        """
+        Display fit result on one page of the notebook.
+        
+        :param result: result of fit 
+        :param pars: list of names of parameters fitted
+        :param current_pg: the page where information will be displayed
+        :param qmin: the minimum value of x to replot the model 
+        :param qmax: the maximum value of x to replot model
+          
+        """  
+        self._mac_sleep(0.2)
+        if page_id[0] in self.fit_thread_list.keys():
+            del self.fit_thread_list[page_id[0]] 
+        if self.batch_on:
+            wx.CallAfter(self._batch_single_fit_complete_helper,
+                          result, pars, page_id, elapsed=None)
+            return 
+        else:  
+            try:
+                result = result[0]
+                if result == None:
+                    self._update_fit_button(page_id)
+                    msg= "Single Fitting did not converge!!!"
+                    wx.PostEvent(self.parent, 
+                                 StatusEvent(status=msg, 
+                                             info="warning",
+                                             type="stop"))
+                    return
+                if not numpy.isfinite(result.fitness) or \
+                        numpy.any(result.pvec == None) or \
+                        not numpy.all(numpy.isfinite(result.pvec)):
+                    msg = "Single Fitting did not converge!!!"
+                    wx.PostEvent(self.parent, 
+                                 StatusEvent(status=msg, 
+                                             info="warning",
+                                             type="stop"))
+                    self._update_fit_button(page_id)
+                    return
+                
+                for uid in page_id:
+                    value = self.page_finder[uid]   
+                    model = value.get_model()
+                    page_id = uid
+                        
+                    param_name = []
+                    for name in pars:
+                        param_name.append(name)
+       
+                    cpage = self.fit_panel.get_page_by_id(uid)
+                    # Make sure we got all results 
+                    #(CallAfter is important to MAC)
+                    wx.CallAfter(cpage.onsetValues, result.fitness, 
+                                      param_name, result.pvec, result.stderr)
+                    cpage._on_fit_complete()
+                if result.stderr == None:
+                    msg = "Fit Abort: "
+                else:
+                    msg = "Fitting: "
+                msg += "Completed!!!"
+                wx.PostEvent(self.parent, StatusEvent(status=msg))
+                return
+            except ValueError:
+                self._update_fit_button(page_id)
+                msg = "Single Fitting did not converge!!!"
+                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
+                                                      type="stop"))
+                return  
+            except:
+                self._update_fit_button(page_id)
+                msg = "Single Fit completed but Following"
+                msg += " error occurred:%s" % sys.exc_value
+                wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
+                                                      type="stop"))
+                raise
+                return
+           
+    def _simul_fit_completed(self, result, page_id,pars=None, elapsed=None):
+        """
+        Parameter estimation completed, 
+        display the results to the user
+        
+        :param alpha: estimated best alpha
+        :param elapsed: computation time
+        
+        """
+        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:
+            msg = "" 
+            if result == None:
+                self._update_fit_button(page_id)
+                msg= "Complex Fitting did not converge!!!"
+                wx.PostEvent(self.parent, StatusEvent(status=msg,
+                                                      type="stop"))
+                return
+            if not numpy.isfinite(result.fitness) or \
+                numpy.any(result.pvec == None) or not \
+                numpy.all(numpy.isfinite(result.pvec)):
+                self._update_fit_button(page_id)
+                msg= "Simultaneous Fitting did not converge!!!"
+                wx.PostEvent(self.parent, StatusEvent(status=msg,type="stop"))
+                return
+              
+            for uid in page_id:   
+                value = self.page_finder[uid]
+                model = value.get_model()
+                data =  value.get_fit_data()
+                small_param_name = []
+                small_out = []
+                small_cov = []
+                #Separate result in to data corresponding to each page
+                for p in result.parameters:
+                    model_name, param_name = self.split_string(p.name)  
+                    if model.name == model_name:
+                        p_name= model.name+"."+param_name
+                        if p.name == p_name:      
+                            if p.value != None and numpy.isfinite(p.value):
+                                small_out.append(p.value)
+                                small_param_name.append(param_name)
+                                small_cov.append(p.stderr)
+                # Display result on each page 
+                cpage = self.fit_panel.get_page_by_id(uid)
+                wx.CallAfter(cpage.onsetValues, 
+                                    result.fitness,
+                                  small_param_name,
+                                  small_out,small_cov)
+                cpage._on_fit_complete()
+                msg = "Fit completed!"
+                wx.PostEvent(self.parent, StatusEvent(status=msg))
+        except Exception:
+            self._update_fit_button(page_id)
+            msg = "Complex Fitting did not converge!!!"
+            wx.PostEvent(self.parent, StatusEvent(status=msg, info="error",
+                                                  type="stop"))
+            return
+
+        except:
+            self._update_fit_button(page_id)
+            msg = "Simultaneous Fit completed"
+            msg += " but Following error occurred:%s" % sys.exc_value
+            wx.PostEvent(self.parent, StatusEvent(status=msg, 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 _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 _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(new_panel)
+            #self.menu3.Append(event_id, new_panel.window_caption, 
+            #                 "Show %s plot panel" % new_panel.window_caption)
+            # Set id to allow us to reference the panel later
+         
+            new_panel.uid = event_id
+            self.mypanels.append(new_panel) 
+       
+    def _onclearslicer(self, event):
+        """
+        Clear the boxslicer when close the panel associate with this slicer
+        """
+        name =event.GetPane().caption
+    
+        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 _return_engine_type(self):
+        """
+        return the current type of engine
+        """
+        return self._fit_engine
+     
+     
+    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)
+        else:
+            self.menu1.FindItemById(self.park_id).Check(False)
+            self.menu1.FindItemById(self.scipy_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
+        smearer = evt.smearer
+        
+        if model == None:
+            return
+       
+        if self.page_finder[uid].get_model() is None:
+            model.name = "M" + str(self.index_model)
+            self.index_model += 1  
+        else:
+            model.name = self.page_finder[uid].get_model().name
+        # save the name containing the data name with the appropriate model
+        self.page_finder[uid].set_model(model)
+        self.page_finder[uid].set_range(qmin=qmin, qmax=qmax)
+        if self.sim_page is not None:
+            self.sim_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"))
+        #self.ready_fit()
+        
+    
+    def _fill_default_model2D(self, theory, page_id, qmax,qstep, qmin=None):
+        """
+        fill Data2D with default value 
+        
+        :param theory: Data2D to fill
+        
+        """
+        from DataLoader.data_info import Detector, Source
+        
+        detector = Detector()
+        theory.detector.append(detector)         
+        theory.source= Source()
+        
+        ## Default values    
+        theory.detector[0].distance= 8000   # mm        
+        theory.source.wavelength= 6         # A      
+        theory.detector[0].pixel_size.x= 5  # mm
+        theory.detector[0].pixel_size.y= 5  # mm
+        
+        theory.detector[0].beam_center.x= qmax
+        theory.detector[0].beam_center.y= qmax
+    
+        ## create x_bins and y_bins of the model 2D
+        pixel_width_x = theory.detector[0].pixel_size.x
+        pixel_width_y = theory.detector[0].pixel_size.y
+        center_x      = theory.detector[0].beam_center.x/pixel_width_x
+        center_y      = theory.detector[0].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
+        
+        x=  numpy.linspace(start= -1*qmax,
+                               stop=qmax,
+                               num=qstep,
+                               endpoint=True)  
+        y = numpy.linspace(start=-1*qmax,
+                               stop= qmax,
+                               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)
+        
+        #theory.data = numpy.zeros(len(mask))
+        theory.err_data = numpy.ones(len(mask))
+        theory.qx_data = qx_data 
+        theory.qy_data = qy_data  
+        theory.q_data = q_data 
+        theory.mask = mask            
+        theory.x_bins = x_bins  
+        theory.y_bins = y_bins   
+        
+        # max and min taking account of the bin sizes
+        theory.xmin = xmin 
+        theory.xmax = xmax
+        theory.ymin = ymin 
+        theory.ymax = ymax 
+        theory.group_id = str(page_id) + " Model2D"
+        theory.id = str(page_id) + " Model2D"
+  
+    def _complete1D(self, x,y, page_id, elapsed,index,model,
+                    toggle_mode_on=False,state=None, 
+                    data=None, update_chisqr=True):
+        """
+        Complete plotting 1D data
+        """ 
+        try:
+            new_plot = Data1D(x=x, y=y)
+            new_plot.is_data = False
+            new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
+            if data != None:
+                #if model.output_name.lower().count("reflectivity") > 0:
+                #    _yaxis, _yunit = "\\rm{%s}"% model.output_name, \
+                #                                "%s"% model.output_unit
+                #else:
+                _yaxis, _yunit = data.get_yaxis() 
+                _xaxis, _xunit = data.get_xaxis() 
+                new_plot.title = data.name
+                #if the theory is already plotted use the same group id 
+                #to replot
+                if page_id in self.page_finder:
+                    theory_data = self.page_finder[page_id].get_theory_data()
+                    if theory_data is not None:
+                       data.group_id = theory_data.group_id
+                #data is plotted before the theory, then take its group_id
+                #assign to the new theory
+                new_plot.group_id = data.group_id
+               
+            else:
+                _xaxis, _xunit = "\\rm{%s}"% model.input_name, \
+                                                "%s"% model.input_unit
+                _yaxis, _yunit = "\\rm{%s}"% model.output_name, \
+                                                "%s"% model.output_unit
+                new_plot.title = "Analytical model 1D "
+                #find a group id to plot theory without data
+                new_plot.group_id =  str(page_id) + " Model1D"  
+            new_plot.id =  str(page_id) + " Model1D"  
+            
+            #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()
+            if theory_data is not None:
+                new_plot.id = theory_data.id
+             
+            new_plot.name = model.name + " ["+ str(model.__class__.__name__)+ "]"
+            new_plot.xaxis(_xaxis, _xunit)
+            new_plot.yaxis(_yaxis, _yunit)
+            if toggle_mode_on:
+                new_plot.id =  str(page_id) + " Model" 
+                wx.PostEvent(self.parent, 
+                             NewPlotEvent(group_id=str(page_id) + " Model2D",
+                                               action="Hide"))
+           
+            self.page_finder[page_id].set_theory_data(new_plot)
+            if data is None:
+                data_id = None
+            else:
+                data_id = 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
+
+            wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
+                                            title= str(title)))
+
+            self.page_finder[page_id].set_theory_data(new_plot)
+
+            if update_chisqr:
+                wx.PostEvent(current_pg,
+                             Chi2UpdateEvent(output=self._cal_chisqr(data=data,
+                                                        page_id=page_id,
+                                                        index=index)))
+            else:
+                self._plot_residuals(page_id, data, index)
+
+            msg = "Computation  completed!"
+            wx.PostEvent( self.parent, StatusEvent(status=msg, type="stop" ))
+
+            #self.current_pg.state.theory_data = deepcopy(self.theory_data)
+        except:
+            raise
+            #msg = " Error occurred when drawing %s Model 1D: " % new_plot.name
+            #msg += " %s"  % sys.exc_value
+            #wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
+   
+    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, toggle_mode_on=False,state=None,qstep=DEFAULT_NPTS, 
+                     update_chisqr=True):
+        """
+        Complete get the result of modelthread and create model 2D
+        that can be plot.
+        """
+        err_image = numpy.zeros(numpy.shape(image))
+       
+        new_plot= Data2D(image=image, err_image=err_image)
+        new_plot.name = model.name
+        new_plot.title = "Analytical model 2D "
+        if data is None:
+            self._fill_default_model2D(theory=new_plot, 
+                                       qmax=qmax, 
+                                       page_id=page_id,
+                                       qstep=qstep,
+                                        qmin= qmin)
+           
+        else:
+            new_plot.id = str(page_id) + " Model2D"
+            new_plot.group_id = str(page_id) + " Model2D"
+            new_plot.x_bins = data.x_bins
+            new_plot.y_bins = data.y_bins
+            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
+            #numpy.zeros(len(data.err_data))#data.err_data
+            new_plot.err_data = err_image
+            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
+            if len(title) > 1:
+                new_plot.title = "Model2D for " + data.name
+        new_plot.is_data = False
+        new_plot.name = model.name + " ["+ str(model.__class__.__name__)+ "]"
+
+        theory_data = deepcopy(new_plot)
+        theory_data.name = "Unknown"
+        if toggle_mode_on:
+            new_plot.id = str(page_id) + " Model"      
+            wx.PostEvent(self.parent, 
+                             NewPlotEvent(group_id=str(page_id) + " Model1D",
+                                               action="Hide"))
+        
+        self.page_finder[page_id].set_theory_data(new_plot)
+        if data is None:
+            data_id = None
+        else:
+            data_id = 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
+
+        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
+                                               title=title))
+        self.page_finder[page_id].set_theory_data(new_plot)
+        # Chisqr in fitpage
+        if update_chisqr:
+            wx.PostEvent(current_pg,
+                         Chi2UpdateEvent(output=\
+                                    self._cal_chisqr(data=data,
+                                                     page_id=page_id,
+                                                     index=index)))
+        else:
+            self._plot_residuals(page_id, data, index)
+        msg = "Computation  completed!"
+        wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
+    
+    def _draw_model2D(self, model, page_id, data=None, smearer=None,
+                      description=None, enable2D=False,
+                      state=None,
+                      toggle_mode_on=False,
+                      qmin=DEFAULT_QMIN, qmax=DEFAULT_QMAX,
+                      qstep=DEFAULT_NPTS,
+                      update_chisqr=True):
+        """
+        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
+            
+        """
+        x=  numpy.linspace(start=-1*qmax,
+                               stop=qmax,
+                               num=qstep,
+                               endpoint=True)  
+        y = numpy.linspace(start= -1*qmax,
+                               stop=qmax,
+                               num=qstep,
+                               endpoint=True)
+        if model is None:
+            msg = "Panel with ID: %s does not contained model" % str(page_id)
+            raise ValueError, msg
+        ## use data info instead
+        if data is not None:
+            ## check if data2D to plot
+            if hasattr(data, "x_bins"):
+                enable2D = True
+                x = data.x_bins
+                y = data.y_bins
+               
+        if not enable2D:
+            return None, 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()
+            self.calc_2D = Calc2D(x=x,
+                                    y=y,
+                                    model=model, 
+                                    data=data,
+                                    page_id=page_id,
+                                    smearer=smearer,
+                                    qmin=qmin,
+                                    qmax=qmax,
+                                    qstep=qstep,
+                                    toggle_mode_on=toggle_mode_on,
+                                    state=state,
+                                    completefn=self._complete2D,
+                                    #updatefn= self._update2D,
+                                    update_chisqr=update_chisqr)
+
+            self.calc_2D.queue()
+
+        except:
+            raise
+            #msg = " Error occurred when drawing %s Model 2D: " % model.name
+            #msg += " %s" % sys.exc_value
+            #wx.PostEvent(self.parent, StatusEvent(status=msg))
+
+    def _draw_model1D(self, model, page_id, data=None, smearer=None,
+                qmin=DEFAULT_QMIN, qmax=DEFAULT_QMAX, 
+                state=None,
+                toggle_mode_on=False,
+                qstep=DEFAULT_NPTS, update_chisqr=True, 
+                enable1D=True):
+        """
+        Draw model 1D from loaded data1D
+        
+        :param data: loaded data
+        :param model: the model to plot
+        
+        """
+        x=  numpy.linspace(start=qmin,
+                           stop=qmax,
+                           num=qstep,
+                           endpoint=True
+                           )
+        if data is not None:
+            ## check for data2D
+            if hasattr(data,"x_bins"):
+                return
+            x = data.x
+            if qmin == None :
+                qmin == DEFAULT_QMIN
+
+            if qmax == None:
+                qmax == DEFAULT_QMAX 
+        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()
+            self.calc_1D = Calc1D(x=x,
+                                  data=data,
+                                  model=model,
+                                  page_id=page_id, 
+                                  qmin=qmin,
+                                  qmax=qmax,
+                                  smearer=smearer,
+                                  state=state,
+                                  toggle_mode_on=toggle_mode_on,
+                                  completefn=self._complete1D,
+                                  #updatefn = self._update1D,
+                                  update_chisqr=update_chisqr)
+
+            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=None, index=None): 
+        """
+        Get handy Chisqr using the output from draw1D and 2D, 
+        instead of calling expansive CalcChisqr in guithread
+        """
+        # default chisqr
+        chisqr = None
+
+        # return None if data == None
+        if data == None: return chisqr
+        
+        # Get data: data I, theory I, and data dI in order
+        if data.__class__.__name__ == "Data2D":
+            if index == None: 
+                index = numpy.ones(len(data.data),ntype=bool)
+            # get rid of zero error points
+            index = index & (data.err_data != 0 )  
+            index = index & (numpy.isfinite(data.data)) 
+            fn = data.data[index] 
+            theory_data = self.page_finder[page_id].get_theory_data()
+            gn = theory_data.data[index]
+            en = data.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.y), ntype=bool)
+            if data.dy == None or data.dy == []:
+                dy = numpy.ones(len(data.y))
+            else:
+                ## Set consitently w/AbstractFitengine:
+                # But this should be corrected later.
+                dy = deepcopy(data.dy)
+                dy[dy==0] = 1  
+            fn = data.y[index] 
+            theory_data = self.page_finder[page_id].get_theory_data()
+            gn = theory_data.y
+            en = dy[index]
+
+        # residual
+        res = (fn - gn) / en
+        residuals = res[numpy.isfinite(res)]
+        # get chisqr only w/finite
+        chisqr = numpy.average(residuals * residuals)
+        
+        self._plot_residuals(page_id, data, index)
+        return chisqr
+    
+
+        
+    def _plot_residuals(self, page_id, 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()
+        """
+        if data == None: 
+            return 
+        
+        # Get data: data I, theory I, and data dI in order
+        if data.__class__.__name__ == "Data2D":
+            # build residuals
+            #print data
+            residuals = Data2D()
+            #residuals.copy_from_datainfo(data)
+            # Not for trunk the line below, instead use the line above
+            data.clone_without_data(len(data.data), residuals)
+            residuals.data = None
+            fn = data.data#[index] 
+            theory_data = self.page_finder[page_id].get_theory_data()
+            gn = theory_data.data#[index]
+            en = data.err_data#[index]
+            residuals.data = (fn - gn) / en 
+            residuals.qx_data = data.qx_data#[index]
+            residuals.qy_data = data.qy_data #[index]
+            residuals.q_data = data.q_data#[index]
+            residuals.err_data = numpy.ones(len(residuals.data))#[index]
+            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.q_data#[index]
+            residuals.mask = data.mask
+            residuals.scale = 'linear'
+            #print "print data",residuals
+            # 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.dy == None or data.dy == []:
+                dy = numpy.ones(len(data.y))
+            else:
+                ## Set consitently w/AbstractFitengine: 
+                ## But this should be corrected later.
+                dy = deepcopy(data.dy)
+                dy[dy==0] = 1  
+            fn = data.y[index] 
+            theory_data = self.page_finder[page_id].get_theory_data()
+            gn = theory_data.y
+            en = dy[index]
+            # build residuals
+            residuals = Data1D()
+            residuals.y = (fn - gn) / en
+            residuals.x = data.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')
+            
+        new_plot = residuals
+        if data.id == None:
+            data.id = data.name
+        name  = data.id
+        new_plot.name = "Residuals for " + 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 = new_plot.name#name + " residuals"
+        ##group_id specify on which panel to plot this data
+        new_plot.group_id = new_plot.id
+        #new_plot.is_data = True
+        ##post data to plot
+        title = new_plot.name 
+        
+        # plot data
+        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title=title))   
+        
+#def profile(fn, *args, **kw):
+#    import cProfile, pstats, os
+#    global call_result
+#    def call():
+#        global call_result
+#        call_result = fn(*args, **kw)
+#    cProfile.runctx('call()', dict(call=call), {}, 'profile.out')
+#    stats = pstats.Stats('profile.out')
+#    #stats.sort_stats('time')
+#    stats.sort_stats('calls')
+#    stats.print_stats()
+#    os.unlink('profile.out')
+#    return call_result
+if __name__ == "__main__":
+    i = Plugin()
+    
+    
+    
+    
Index: fittingview/src/sans/perspectives/fitting/fitting_widgets.py
===================================================================
--- fittingview/src/sans/perspectives/fitting/fitting_widgets.py	(revision 49c69de79aac6bbde0711203fe50ff3af1f0bb81)
+++ fittingview/src/sans/perspectives/fitting/fitting_widgets.py	(revision 49c69de79aac6bbde0711203fe50ff3af1f0bb81)
@@ -0,0 +1,187 @@
+
+################################################################################
+#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
+
+WIDTH = 400
+HEIGHT = 300
+MAX_NBR_DATA = 4
+
+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, 250))
+        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)
+        
+    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 = ScrolledPanel(self, style=wx.RAISED_BORDER,
+                               size=(WIDTH-20, HEIGHT-50))
+        self._panel.SetupScrolling()
+        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, 1, wx.EXPAND|wx.LEFT|wx.RIGHT, 5)
+        self._sizer_main.Add(self._sizer_txt, 1, wx.EXPAND|wx.ALL, 10)
+        self._sizer_main.Add(self._data_text_ctrl, 0, 
+                             wx.EXPAND|wx.LEFT|wx.RIGHT, 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: fittingview/src/sans/perspectives/fitting/ftol_dialog.py
===================================================================
--- fittingview/src/sans/perspectives/fitting/ftol_dialog.py	(revision 49c69de79aac6bbde0711203fe50ff3af1f0bb81)
+++ fittingview/src/sans/perspectives/fitting/ftol_dialog.py	(revision 49c69de79aac6bbde0711203fe50ff3af1f0bb81)
@@ -0,0 +1,131 @@
+
+################################################################################
+#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
+from sans.guiframe.events import StatusEvent  
+# default ftol
+F_TOL = 1.49012e-08 
+SANS_F_TOL = 5e-05
+
+if sys.platform.count("win32") > 0:
+    PANEL_WIDTH = 270 
+    PANEL_HEIGHT = 265
+    FONT_VARIANT = 0
+else:
+    PANEL_WIDTH = 285
+    PANEL_HEIGHT = 265
+    FONT_VARIANT = 1
+    
+"""
+Dialog to set ftol for Scipy
+
+    ftol(float): Relative error desired in the sum of squares.
+"""
+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))
+        # parent
+        self.parent = base
+        # default ftol
+        self.ftol = SANS_F_TOL
+                # font size 
+        self.SetWindowVariant(variant=FONT_VARIANT)
+        # build layout
+        panel = wx.Panel(self, -1)
+        vbox = wx.BoxSizer(wx.VERTICAL)
+        wx.StaticBox(panel, -1, 'FTol selection (Leastsq)', (5, 6),
+                      (PANEL_WIDTH*0.9, PANEL_HEIGHT*0.7))
+        default_bt = wx.RadioButton(panel, -1, 'SansView Default (5e-05)', 
+                                    (15, 30), 
+                                    style=wx.RB_GROUP)
+        default_bt.SetValue(True)
+        default_bt.Bind(wx.EVT_RADIOBUTTON, self.OnFtolSelection)
+        sci_default_bt = wx.RadioButton(panel, -1, 
+                                    'Scipy Default (1.49012e-08)', (15, 55))
+        sci_default_bt.SetValue(False)
+        sci_default_bt.Bind(wx.EVT_RADIOBUTTON, self.OnFtolSelection)
+        high_bt = wx.RadioButton(panel, -1, '1e-06', (15, 80))
+        high_bt.SetValue(False)
+        high_bt.Bind(wx.EVT_RADIOBUTTON, self.OnFtolSelection)
+        mid_bt = wx.RadioButton(panel, -1, '1e-05', (15, 105))
+        mid_bt.SetValue(False)
+        mid_bt.Bind(wx.EVT_RADIOBUTTON, self.OnFtolSelection)
+        low_bt = wx.RadioButton(panel, -1, '1e-04', (15, 130))
+        low_bt.SetValue(False)
+        low_bt.Bind(wx.EVT_RADIOBUTTON, self.OnFtolSelection)
+        self.custom_bt = wx.RadioButton(panel, -1, 'Custom', (15, 155))
+        self.custom_bt.SetValue(False)
+        self.custom_bt.Bind(wx.EVT_RADIOBUTTON, self.OnFtolSelection)
+        self.custombox = wx.TextCtrl(panel, -1, '', (95, 155))
+        self.custombox.Disable()
+        hbox = wx.BoxSizer(wx.HORIZONTAL)
+        okButton = wx.Button(self, -1, 'Set', size=(70, 30))
+        hbox.Add(okButton, 1, wx.RIGHT, 5)
+        okButton.Bind(wx.EVT_BUTTON, self.OnClose)
+        vbox.Add(panel)
+        vbox.Add(hbox, 1, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 10)
+        # set sizer
+        self.SetSizer(vbox)
+
+    def OnFtolSelection(self, event=None):
+        """
+        Changes the ftol on selection of the radio button
+        """
+        event.Skip()
+        # event object and selection
+        button = event.GetEventObject()
+        selection = button.GetLabel()
+        # get float value 
+        if selection.count('SansView') > 0:
+            ftol = SANS_F_TOL   
+            self.custombox.Disable() 
+        elif selection.count('Scipy') > 0:
+            ftol = F_TOL   
+            self.custombox.Disable()     
+        elif selection == 'Custom':
+            ftol = F_TOL 
+            self.custombox.Enable(True)
+        else:
+            ftol =  float(selection)
+            self.custombox.Disable()
+        self.ftol = ftol    
+        
+
+    def OnClose(self, event):
+        """
+        Close event
+        """
+        # clear event
+        event.Skip()
+        flag = True
+        # I case of the custom ftol
+        if self.custom_bt.GetValue():
+            try:
+                ftol = float(self.custombox.GetValue())
+                self.ftol = ftol
+            except:
+                flag = False
+        if flag:
+            # set ftol in fitting
+            self.parent.set_ftol(self.ftol) 
+            msg = "The ftol (LeastSq) is set to %s." % self.ftol
+        else:
+           msg = "Error in the selection... No changes in ftol."
+        # post event for info
+        wx.PostEvent( self.parent.parent, 
+                      StatusEvent(status= msg, info='warning')) 
+    
+        self.Destroy()
+
Index: fittingview/src/sans/perspectives/fitting/help_panel.py
===================================================================
--- fittingview/src/sans/perspectives/fitting/help_panel.py	(revision 49c69de79aac6bbde0711203fe50ff3af1f0bb81)
+++ fittingview/src/sans/perspectives/fitting/help_panel.py	(revision 49c69de79aac6bbde0711203fe50ff3af1f0bb81)
@@ -0,0 +1,133 @@
+#!/usr/bin/python
+
+import wx
+import wx.html as html
+from wx.lib.splitter import MultiSplitterWindow
+import os
+
+
+class HelpWindow(wx.Frame):
+    """
+    """
+    def __init__(self, parent, id, title= 'HelpWindow', pageToOpen=None):
+        wx.Frame.__init__(self, parent, id, title, size=(850, 530))
+        """
+        contains help info
+        """
+      
+        splitter = MultiSplitterWindow(self, style=wx.SP_LIVE_UPDATE)
+        rpanel = wx.Panel(splitter, -1)
+        lpanel = wx.Panel(splitter, -1,style=wx.BORDER_SUNKEN)
+        
+        vbox = wx.BoxSizer(wx.VERTICAL)
+        header = wx.Panel(rpanel, -1)
+        header.SetBackgroundColour('#6666FF')
+        header.SetForegroundColour('WHITE')
+        hbox = wx.BoxSizer(wx.HORIZONTAL)
+        st = wx.StaticText(header, -1, 'Contents', (5, 5))
+        font = st.GetFont()
+        font.SetPointSize(10)
+        st.SetFont(font)
+        hbox.Add(st, 1, wx.TOP | wx.BOTTOM | wx.LEFT, 5)
+        header.SetSizer(hbox)
+        vbox.Add(header, 0, wx.EXPAND)
+       
+        vboxl= wx.BoxSizer(wx.VERTICAL)
+        headerl = wx.Panel(lpanel, -1, size=(-1, 20))
+       
+        headerl.SetBackgroundColour('#6666FF')
+        headerl.SetForegroundColour('WHITE')
+        hboxl = wx.BoxSizer(wx.HORIZONTAL)
+        lst = wx.StaticText(headerl, -1, 'Menu', (5, 5))
+        fontl = lst.GetFont()
+        fontl.SetPointSize(10)
+        lst.SetFont(fontl)
+        hboxl.Add(lst, 1, wx.TOP | wx.BOTTOM | wx.LEFT, 5)
+        headerl.SetSizer(hboxl)
+        vboxl.Add(headerl, 0, wx.EXPAND)
+        self.lhelp = html.HtmlWindow(lpanel, -1, style=wx.NO_BORDER)
+        self.rhelp = html.HtmlWindow(rpanel, -1, style=wx.NO_BORDER, size=(500,-1))
+
+        import sans.models as models 
+        # get the media path
+        path = models.get_data_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")
+                    
+        page1="""<html>
+            <body>
+             <p>Select topic on Menu</p>
+            </body>
+            </html>"""
+        page="""<html>
+            <body>
+            <ul>
+            <li><a href ="media/status_bar_help.html" target ="showframe">Status Bar Help</a><br></li>
+            <li><a href ="media/load_data_help.html" target ="showframe">Load a File</a><br></li>
+            <li><a href ="media/simultaneous_fit_help.html" target ="showframe">Simultaneous Fit</a><br></li>
+            <li><a href ="media/single_fit_help.html" target ="showframe">Single Fit</a><br></li>
+            <li><a href ="media/model_use_help.html" target ="showframe">Model Selection</a><br></li>
+            <li><a href ="%s" target ="showframe">Model Functions</a><br></li>
+            <li><a href ="%s" target ="showframe">Polydispersion Distributions</a><br></li>
+            <li><a href ="%s" target ="showframe">Smear Computation</a><br></li>
+            <li><a href ="media/key_help.html" target ="showframe">Key Combination</a><br></li>
+            </ul>
+            </body>
+            </html>""" % (self.path, self.path_pd, self.path_sm)
+        
+        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)
+        rpanel.SetSizer(vbox)
+        lpanel.SetSizer(vboxl)
+        lpanel.SetFocus()
+        
+        vbox1 = wx.BoxSizer(wx.HORIZONTAL)
+        vbox1.Add(splitter,1,wx.EXPAND)
+        splitter.AppendWindow(lpanel, 200)
+        splitter.AppendWindow(rpanel)
+        self.SetSizer(vbox1)
+       
+        self.Centre()
+        self.Show(True)
+        
+    def 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(link)
+        
+"""
+Example: ::
+
+    class ViewApp(wx.App):
+        def OnInit(self):
+            frame = HelpWindow(None, -1, 'HelpWindow')    
+            frame.Show(True)
+            self.SetTopWindow(frame)
+            
+            return True
+            
+    
+    if __name__ == "__main__": 
+    app = ViewApp(0)
+    app.MainLoop()  
+
+"""   
Index: fittingview/src/sans/perspectives/fitting/hint_fitpage.py
===================================================================
--- fittingview/src/sans/perspectives/fitting/hint_fitpage.py	(revision 49c69de79aac6bbde0711203fe50ff3af1f0bb81)
+++ fittingview/src/sans/perspectives/fitting/hint_fitpage.py	(revision 49c69de79aac6bbde0711203fe50ff3af1f0bb81)
@@ -0,0 +1,57 @@
+import wx
+from sans.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)
+ 
+if __name__=="__main__":
+    app = wx.App()
+    HelpWindow(None, -1, 'HelpWindow')
+    app.MainLoop()
+                
Index: fittingview/src/sans/perspectives/fitting/model_thread.py
===================================================================
--- fittingview/src/sans/perspectives/fitting/model_thread.py	(revision 49c69de79aac6bbde0711203fe50ff3af1f0bb81)
+++ fittingview/src/sans/perspectives/fitting/model_thread.py	(revision 49c69de79aac6bbde0711203fe50ff3af1f0bb81)
@@ -0,0 +1,248 @@
+
+
+import time
+from data_util.calcthread import CalcThread
+import sys
+import numpy,math
+from sans.models.smearing_2d import Smearer2D
+
+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, x, y, data,model,smearer,qmin, qmax,qstep,
+                 page_id ,
+                 state=None,
+                 toggle_mode_on=False,
+                 completefn = None,
+                 updatefn   = None,
+                 update_chisqr=True,
+                 yieldtime  = 0.04,
+                 worktime   = 0.04
+                 ):
+        CalcThread.__init__(self,completefn,
+                 updatefn,
+                 yieldtime,
+                 worktime)
+        self.qmin= qmin
+        self.qmax= qmax
+        self.qstep= qstep
+        self.toggle_mode_on = toggle_mode_on
+        self.x = x
+        self.y = y
+        self.data= data
+        self.page_id = page_id
+        self.state = None
+        # the model on to calculate
+        self.model = model
+        self.smearer = smearer#(data=self.data,model=self.model)
+        self.starttime = 0  
+        self.update_chisqr = update_chisqr
+        
+    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 != None:
+            self.I_data = self.data.data
+            self.qx_data = self.data.qx_data
+            self.qy_data = self.data.qy_data
+            self.dqx_data = self.data.dqx_data
+            self.dqy_data = self.data.dqy_data
+            self.mask    = self.data.mask
+        else:          
+            xbin =  numpy.linspace(start= -1*self.qmax,
+                                   stop= self.qmax,
+                                   num= self.qstep,
+                                   endpoint=True )  
+            ybin = numpy.linspace(start= -1*self.qmax,
+                                   stop= self.qmax,
+                                   num= self.qstep,
+                                   endpoint=True )            
+            
+            new_xbin = numpy.tile(xbin, (len(ybin),1))
+            new_ybin = numpy.tile(ybin, (len(xbin),1))
+            new_ybin = new_ybin.swapaxes(0,1)
+            new_xbin = new_xbin.flatten()
+            new_ybin = new_ybin.flatten()
+            self.qy_data = new_ybin
+            self.qx_data = new_xbin
+            # fake data
+            self.I_data = numpy.ones(len(self.qx_data))
+           
+            self.mask = numpy.ones(len(self.qx_data),dtype=bool)
+            
+        # Define matrix where data will be plotted    
+        radius= numpy.sqrt( self.qx_data*self.qx_data + self.qy_data*self.qy_data )
+        index_data= (self.qmin<= radius)&(self.mask)
+
+        # 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.mask)
+        index_model = (index_model)&(numpy.isfinite(self.I_data))
+        if self.data ==None:
+            # Only qmin value will be consider for the detector
+            index_model = index_data  
+
+        if self.smearer != 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.qx_data[index_model],self.qy_data[index_model]])
+
+        output = numpy.zeros(len(self.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,
+                       qmin=self.qmin,
+                       qmax=self.qmax,
+                       qstep=self.qstep,
+                       update_chisqr = self.update_chisqr )
+        
+
+class Calc1D(CalcThread):
+    """
+    Compute 1D data
+    """
+    def __init__(self, x, model,
+                 page_id,
+                 data=None,
+                 qmin=None,
+                 qmax=None,
+                 smearer=None,
+                 toggle_mode_on=False,
+                 state=None,
+                 completefn = None,
+                 update_chisqr=True,
+                 updatefn   = None,
+                 yieldtime  = 0.01,
+                 worktime   = 0.01
+                 ):
+        """
+        """
+        CalcThread.__init__(self,completefn,
+                 updatefn,
+                 yieldtime,
+                 worktime)
+        self.x = numpy.array(x)
+        self.data = data
+        self.qmin = qmin
+        self.qmax = qmax
+        self.model = model
+        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
+        
+    def compute(self):
+        """
+        Compute model 1d value given qmin , qmax , x value 
+        """
+        self.starttime = time.time()
+        output = numpy.zeros((len(self.x)))
+        index= (self.qmin <= self.x)& (self.x <= self.qmax)
+     
+        ##smearer the ouput of the plot    
+        if self.smearer!=None:
+            first_bin, last_bin = self.smearer.get_bin_range(self.qmin, self.qmax)
+            output[first_bin:last_bin] = self.model.evalDistribution(self.x[first_bin:last_bin])
+            output = self.smearer(output, first_bin, last_bin) 
+        else:
+            output[index] = self.model.evalDistribution(self.x[index])
+         
+        elapsed = time.time() - self.starttime
+       
+        self.complete(x=self.x[index], y=output[index], 
+                      page_id=self.page_id,
+                      state=self.state,
+                      toggle_mode_on=self.toggle_mode_on,
+                      elapsed=elapsed,index=index, model=self.model,
+                      data=self.data, 
+                      update_chisqr = self.update_chisqr )
+        
+    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 sans.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: fittingview/src/sans/perspectives/fitting/models.py
===================================================================
--- fittingview/src/sans/perspectives/fitting/models.py	(revision 49c69de79aac6bbde0711203fe50ff3af1f0bb81)
+++ fittingview/src/sans/perspectives/fitting/models.py	(revision 49c69de79aac6bbde0711203fe50ff3af1f0bb81)
@@ -0,0 +1,666 @@
+
+import wx
+import wx.lib.newevent
+import imp
+import os
+import sys
+import math
+import os.path
+# Time is needed by the log method
+import time
+import logging
+
+from sans.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 sans.models.pluginmodel import Model1DPlugin
+   
+PLUGIN_DIR = 'plugins' 
+
+def log(message):
+    """
+    """
+    out = open("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 is 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
+   
+    new_instance= model() 
+    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 _findModels(dir):
+    """
+    """
+    # List of plugin objects
+    plugins = {}
+    # Go through files in plug-in directory
+    #always recompile the folder plugin
+    import compileall
+    dir = os.path.abspath(PLUGIN_DIR)
+    if not os.path.isdir(dir):
+        dir = os.path.join(os.getcwd(), PLUGIN_DIR)
+    if not os.path.isdir(dir):
+        dir = os.path.join(os.path.dirname(__file__), PLUGIN_DIR)
+    if not os.path.isdir(dir):
+        dir = os.path.join(os.path.dirname(os.path.sys.path[0]), PLUGIN_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))
+        compileall.compile_dir(dir=dir, ddir=dir, force=1, quiet=True)
+        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.mydict[name] = mylist
+            
+            
+    def get_list(self):
+        """
+        return all the list stored in a dictionary object
+        """
+        return self.mydict
+        
+class ModelManagerBase:
+    """
+    """
+    ## external dict for models
+    model_combobox = ModelList()
+    ## Dictionary of form models
+    form_factor_dict = {}
+    ## dictionary of other
+    struct_factor_dict = {}
+    ##list of form factors
+    shape_list = []
+    ## independent shape model list
+    shape_indep_list = []
+    ##list of structure factors 
+    struct_list = []
+    ##list of model allowing multiplication
+    multiplication_factor = []
+    ##list of multifunctional shapes
+    multi_func_list = []
+    ## list of added models
+    plugins = []
+    ## Event owner (guiframe)
+    event_owner = None
+    last_time_dir_modified = 0
+    def __init__(self):
+        """
+        """
+       
+        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
+        
+        """
+        from sans.models.SphereModel import SphereModel
+        self.shape_list.append(SphereModel)
+        self.multiplication_factor.append(SphereModel)
+        
+        from sans.models.BinaryHSModel import BinaryHSModel
+        self.shape_list.append(BinaryHSModel)
+                        
+        from sans.models.FuzzySphereModel import FuzzySphereModel
+        self.shape_list.append(FuzzySphereModel)
+        self.multiplication_factor.append(FuzzySphereModel)
+           
+        from sans.models.CoreShellModel import CoreShellModel
+        self.shape_list.append(CoreShellModel)
+        self.multiplication_factor.append(CoreShellModel)
+        
+        from sans.models.CoreMultiShellModel import CoreMultiShellModel
+        self.shape_list.append(CoreMultiShellModel)
+        self.multiplication_factor.append(CoreMultiShellModel)
+        self.multi_func_list.append(CoreMultiShellModel)
+
+        from sans.models.VesicleModel import VesicleModel
+        self.shape_list.append(VesicleModel)
+        self.multiplication_factor.append(VesicleModel)
+        
+        from sans.models.MultiShellModel import MultiShellModel
+        self.shape_list.append(MultiShellModel)
+        self.multiplication_factor.append(MultiShellModel)
+        
+        from sans.models.OnionExpShellModel import OnionExpShellModel
+        self.shape_list.append(OnionExpShellModel)
+        self.multiplication_factor.append(OnionExpShellModel)
+        self.multi_func_list.append(OnionExpShellModel)
+        
+        from sans.models.PearlNecklaceModel import PearlNecklaceModel
+        self.shape_list.append(PearlNecklaceModel)
+        #self.multiplication_factor.append(PearlNecklaceModel)
+                         
+        from sans.models.SphericalSLDModel import SphericalSLDModel
+        self.shape_list.append(SphericalSLDModel)
+        self.multiplication_factor.append(SphericalSLDModel)
+        self.multi_func_list.append(SphericalSLDModel)
+        
+        from sans.models.CylinderModel import CylinderModel
+        self.shape_list.append(CylinderModel)
+        self.multiplication_factor.append(CylinderModel)
+        
+        from sans.models.CoreShellCylinderModel import CoreShellCylinderModel
+        self.shape_list.append(CoreShellCylinderModel)
+        self.multiplication_factor.append(CoreShellCylinderModel)
+        
+        from sans.models.HollowCylinderModel import HollowCylinderModel
+        self.shape_list.append(HollowCylinderModel)
+        self.multiplication_factor.append(HollowCylinderModel)
+              
+        from sans.models.FlexibleCylinderModel import FlexibleCylinderModel
+        self.shape_list.append(FlexibleCylinderModel)
+
+        from sans.models.FlexCylEllipXModel import FlexCylEllipXModel
+        self.shape_list.append(FlexCylEllipXModel)
+        
+        from sans.models.StackedDisksModel import StackedDisksModel
+        self.shape_list.append(StackedDisksModel)
+        self.multiplication_factor.append(StackedDisksModel)
+        
+        from sans.models.ParallelepipedModel import ParallelepipedModel
+        self.shape_list.append(ParallelepipedModel)
+        self.multiplication_factor.append(ParallelepipedModel)
+        
+        from sans.models.CSParallelepipedModel import CSParallelepipedModel
+        self.shape_list.append(CSParallelepipedModel)
+        self.multiplication_factor.append(CSParallelepipedModel)
+        
+        from sans.models.EllipticalCylinderModel import EllipticalCylinderModel
+        self.shape_list.append(EllipticalCylinderModel)
+        self.multiplication_factor.append(EllipticalCylinderModel)
+        
+        from sans.models.BarBellModel import BarBellModel
+        self.shape_list.append(BarBellModel)
+        # not implemeted yet!
+        #self.multiplication_factor.append(BarBellModel)
+        
+        from sans.models.CappedCylinderModel import CappedCylinderModel
+        self.shape_list.append(CappedCylinderModel)
+        # not implemeted yet!
+        #self.multiplication_factor.append(CappedCylinderModel)
+        
+        from sans.models.EllipsoidModel import EllipsoidModel
+        self.shape_list.append(EllipsoidModel)
+        self.multiplication_factor.append(EllipsoidModel)
+      
+        from sans.models.CoreShellEllipsoidModel import CoreShellEllipsoidModel
+        self.shape_list.append(CoreShellEllipsoidModel)
+        self.multiplication_factor.append(CoreShellEllipsoidModel)
+         
+        from sans.models.TriaxialEllipsoidModel import TriaxialEllipsoidModel
+        self.shape_list.append(TriaxialEllipsoidModel)
+        self.multiplication_factor.append(TriaxialEllipsoidModel)
+        
+        from sans.models.LamellarModel import LamellarModel
+        self.shape_list.append(LamellarModel)
+        
+        from sans.models.LamellarFFHGModel import LamellarFFHGModel
+        self.shape_list.append(LamellarFFHGModel)
+        
+        from sans.models.LamellarPSModel import LamellarPSModel
+        self.shape_list.append(LamellarPSModel)
+     
+        from sans.models.LamellarPSHGModel import LamellarPSHGModel
+        self.shape_list.append(LamellarPSHGModel)
+        
+        from sans.models.LamellarPCrystalModel import LamellarPCrystalModel
+        self.shape_list.append(LamellarPCrystalModel)
+        
+        from sans.models.SCCrystalModel import SCCrystalModel
+        self.shape_list.append(SCCrystalModel)
+        
+        from sans.models.FCCrystalModel import FCCrystalModel
+        self.shape_list.append(FCCrystalModel)
+        
+        from sans.models.BCCrystalModel import BCCrystalModel
+        self.shape_list.append(BCCrystalModel)
+      
+        ## Structure factor 
+        from sans.models.SquareWellStructure import SquareWellStructure
+        self.struct_list.append(SquareWellStructure)
+        
+        from sans.models.HardsphereStructure import HardsphereStructure
+        self.struct_list.append(HardsphereStructure)
+         
+        from sans.models.StickyHSStructure import StickyHSStructure
+        self.struct_list.append(StickyHSStructure)
+        
+        from sans.models.HayterMSAStructure import HayterMSAStructure
+        self.struct_list.append(HayterMSAStructure)
+        
+        ##shape-independent models
+        from sans.models.PowerLawAbsModel import PowerLawAbsModel
+        self.shape_indep_list.append( PowerLawAbsModel )
+        
+        from sans.models.BEPolyelectrolyte import BEPolyelectrolyte
+        self.shape_indep_list.append(BEPolyelectrolyte )
+        self.form_factor_dict[str(wx.NewId())] =  [SphereModel]
+        
+        from sans.models.BroadPeakModel import BroadPeakModel
+        self.shape_indep_list.append(BroadPeakModel)
+        
+        from sans.models.CorrLengthModel import CorrLengthModel
+        self.shape_indep_list.append(CorrLengthModel)
+        
+        from sans.models.DABModel import DABModel
+        self.shape_indep_list.append(DABModel )
+        
+        from sans.models.DebyeModel import DebyeModel
+        self.shape_indep_list.append(DebyeModel )
+        
+        #FractalModel (a c-model)is now being used instead of FractalAbsModel.
+        from sans.models.FractalModel import FractalModel
+        self.shape_indep_list.append(FractalModel )
+        
+        from sans.models.FractalCoreShellModel import FractalCoreShellModel
+        self.shape_indep_list.append(FractalCoreShellModel )
+        
+        from sans.models.GaussLorentzGelModel import GaussLorentzGelModel
+        self.shape_indep_list.append(GaussLorentzGelModel) 
+                
+        from sans.models.GuinierModel import GuinierModel
+        self.shape_indep_list.append(GuinierModel )
+        
+        from sans.models.GuinierPorodModel import GuinierPorodModel
+        self.shape_indep_list.append(GuinierPorodModel )
+
+        from sans.models.LorentzModel import LorentzModel
+        self.shape_indep_list.append( LorentzModel) 
+        
+        from sans.models.PeakGaussModel import PeakGaussModel
+        self.shape_indep_list.append(PeakGaussModel)
+        
+        from sans.models.PeakLorentzModel import PeakLorentzModel
+        self.shape_indep_list.append(PeakLorentzModel)
+        
+        from sans.models.Poly_GaussCoil import Poly_GaussCoil
+        self.shape_indep_list.append(Poly_GaussCoil)
+        
+        from sans.models.PolymerExclVolume import PolymerExclVolume
+        self.shape_indep_list.append(PolymerExclVolume)
+        
+        from sans.models.PorodModel import PorodModel
+        self.shape_indep_list.append(PorodModel )      
+        
+        from sans.models.RPA10Model import RPA10Model
+        self.shape_indep_list.append(RPA10Model)
+        self.multi_func_list.append(RPA10Model)
+        
+        from sans.models.TeubnerStreyModel import TeubnerStreyModel
+        self.shape_indep_list.append(TeubnerStreyModel )
+        
+        from sans.models.TwoLorentzianModel import TwoLorentzianModel
+        self.shape_indep_list.append(TwoLorentzianModel )
+        
+        from sans.models.TwoPowerLawModel import TwoPowerLawModel
+        self.shape_indep_list.append(TwoPowerLawModel )
+        
+        from sans.models.UnifiedPowerRgModel import UnifiedPowerRgModel
+        self.shape_indep_list.append(UnifiedPowerRgModel )
+        self.multi_func_list.append(UnifiedPowerRgModel)
+        
+        from sans.models.LineModel import LineModel
+        self.shape_indep_list.append(LineModel)
+        
+        from sans.models.ReflectivityModel import ReflectivityModel
+        self.multi_func_list.append(ReflectivityModel)
+        
+        from sans.models.ReflectivityIIModel import ReflectivityIIModel
+        self.multi_func_list.append(ReflectivityIIModel)
+    
+        #Looking for plugins
+        self.stored_plugins = self.findModels()
+        self.plugins = self.stored_plugins.values()
+        self.plugins.append(ReflectivityModel)
+        self.plugins.append(ReflectivityIIModel)
+        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
+        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_combobox.set_list("Customized Models", self.plugins)
+            return self.model_combobox.get_list()
+        else:
+            return {}
+        
+    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
+        """
+        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 sans.models.MultiplicationModel import MultiplicationModel
+            model1, model2 = self.form_factor_dict[int(evt.GetId())]
+            model = MultiplicationModel(model1, model2)    
+        else:
+            model= self.struct_factor_dict[str(evt.GetId())]()
+        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 
+        
+        """
+        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()
+    
+  
+        
+class ModelManager(object):
+    """
+    implement model 
+    """
+    __modelmanager = ModelManagerBase()
+    
+    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 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()
+    
+    
+  
Index: fittingview/src/sans/perspectives/fitting/pagestate.py
===================================================================
--- fittingview/src/sans/perspectives/fitting/pagestate.py	(revision 49c69de79aac6bbde0711203fe50ff3af1f0bb81)
+++ fittingview/src/sans/perspectives/fitting/pagestate.py	(revision 49c69de79aac6bbde0711203fe50ff3af1f0bb81)
@@ -0,0 +1,1602 @@
+
+
+################################################################################
+#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 xml.dom.minidom
+from xml.dom.minidom import parse
+from lxml import etree
+
+import DataLoader
+from DataLoader.readers.cansas_reader import Reader as CansasReader
+from DataLoader.readers.cansas_reader import get_content, write_node
+from DataLoader.data_info import Data2D, Detector
+
+#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"],
+                      ["formfactorcombobox", "formfactorcombobox", "float"],
+                      ["structurecombobox", "structurecombobox", "float"],
+                      ["multi_factor","multi_factor","float"],
+                      ["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"],
+                      ["slit_smearer","slit_smearer","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"]]
+
+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
+        #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 parmaters for gaussian dispersity
+        self.orientation_params_disp = []
+        ## smearer info
+        self.smearer = None
+        self.smear_type = None
+        self.dq_l = None
+        self.dq_r = None
+
+        #list of dispersion paramaters
+        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.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
+        ## enable smearering state
+        self.enable_smearer = False
+        self.disable_smearer = True
+        self.pinhole_smearer = False
+        self.slit_smearer   = 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.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.dq_l = copy.deepcopy(self.dq_l)
+        obj.dq_r = copy.deepcopy(self.dq_r)
+
+        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.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 %H;%M of %Y", t)
+
+        rep += "State created on : %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
+        rep += "model type (form factor) selected: %s\n"%self.shape_rbutton 
+        rep += "multi_factor : %s\n"% str(self.multi_factor)
+        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 += "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 += "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 on"):
+                repo_time = "Created on " + 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 == "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 == "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" + 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 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, list, element, newdoc):
+        """
+        Helper method to create xml file for saving state
+        """
+        for item in list:
+            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
+            top_element = newdoc.createElement(FITTING_NODE_NAME)
+            if entry_node is None:
+                newdoc.documentElement.appendChild(top_element)
+            else:
+                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,...],...}  
+        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(list=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.toprettyxml()
+        
+    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 = {}    
+                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 sans.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:
+                #fig.set_facecolor('w')
+                ind = figs.index(fig)
+                canvas = canvases[ind]
+                
+            #bmp = wx.BitmapDataObject()
+            #bmp.SetBitmap(canvas.bitmap)
+            #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)
+            
+            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_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 CansasReader._parse_entry(self, dom)
+        
+        #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]
+                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_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
+            fd = open(filename, 'w')
+            fd.write(doc.toprettyxml())
+            fd.close()
+        else:
+            fitstate.toXML(file=filename)
+        
+    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 = DataLoader.data_info.Data1D(x=[], y=[])  
+        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__, 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:
+            state.toXML(doc=doc, file=data.name, 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>"
+HEADER += "%s</center></span></center></b></p>"
+HEADER += "<p class=MsoNormal>&nbsp;</p>"
+PARA = "<p class=MsoNormal> %s \n"
+PARA += "</p>"
+CENTRE = "<p class=MsoNormal><center> %s \n"
+CENTRE += "</center></p>"
+FEET_1 = \
+"""
+<p class=MsoNormal>&nbsp;</p>
+<p class=MsoNormal><b><span ><center> Graph</span></center></b></p> 
+<p class=MsoNormal>&nbsp;</p> 
+<center> 
+<br>Model Computation<br> 
+<br>Data: "%s"<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: fittingview/src/sans/perspectives/fitting/report_dialog.py
===================================================================
--- fittingview/src/sans/perspectives/fitting/report_dialog.py	(revision 49c69de79aac6bbde0711203fe50ff3af1f0bb81)
+++ fittingview/src/sans/perspectives/fitting/report_dialog.py	(revision 49c69de79aac6bbde0711203fe50ff3af1f0bb81)
@@ -0,0 +1,300 @@
+
+################################################################################
+#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
+################################################################################
+
+"""
+Dialog report panel to show and summarize the results of 
+the invariant calculation.
+"""
+import wx
+import sys
+import os
+import wx.html as html
+
+if sys.platform.count("win32") > 0:
+    _STATICBOX_WIDTH = 450
+    PANEL_WIDTH = 500 
+    PANEL_HEIGHT = 700
+    FONT_VARIANT = 0
+    ISMAC = False
+else:
+    _STATICBOX_WIDTH = 480
+    PANEL_WIDTH = 530
+    PANEL_HEIGHT = 700
+    FONT_VARIANT = 1
+    ISMAC = 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_mac = ISMAC
+        # 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_mac:
+            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:
+                os.startfile(str(fName))
+            #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 % pic_fname[0]
+            elif self.nimages == 2:
+                report = report_frame % (pic_fname[0], pic_fname[1])
+            elif self.nimages == 3:
+                report = report_frame % (pic_fname[0], pic_fname[1],
+                                          pic_fname[2])
+        f = open(fName, 'w')
+        f.write(report)
+        f.close()
+        #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()
+    
+    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()
+
+    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()
+
+        return not pdf.err
+
+        
+        
Index: fittingview/src/sans/perspectives/fitting/simfitpage.py
===================================================================
--- fittingview/src/sans/perspectives/fitting/simfitpage.py	(revision 49c69de79aac6bbde0711203fe50ff3af1f0bb81)
+++ fittingview/src/sans/perspectives/fitting/simfitpage.py	(revision 49c69de79aac6bbde0711203fe50ff3af1f0bb81)
@@ -0,0 +1,624 @@
+
+import sys,re,string, wx  
+import wx.lib.newevent 
+from sans.guiframe.events import StatusEvent 
+from sans.guiframe.panel_base import PanelBase
+from wx.lib.scrolledpanel import ScrolledPanel
+#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 ={}, *args, **kwargs):
+        ScrolledPanel.__init__(self, parent,style= wx.FULL_REPAINT_ON_RESIZE )
+        PanelBase.__init__(self, parent)
+        """
+        Simultaneous page display
+        """
+        ##Font size
+        self.SetWindowVariant(variant = FONT_VARIANT)
+        self.uid = None
+        self.parent = parent
+        ## 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.uid = wx.NewId()
+        ## draw page
+        self.define_page_structure()
+        self.draw_page()
+        self.set_layout()
+        
+    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.SetScrollbars(20,20,25,65)
+        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.sizer2.Layout()
+                self.SetScrollbars(20,20,25,65)
+                self.constraints_list.remove(item)
+                self.nb_constraint -= 1
+                break
+                
+    def onFit(self,event):
+        """
+        signal for fitting
+        
+        """
+        ## making sure all parameters content a constraint
+        ## validity of the constraint expression is own by fit engine
+        if self.show_constraint.GetValue():
+            self._set_constraint()
+        ## get the fit range of very fit problem        
+        for id, value in self.page_finder.iteritems():
+            qmin, qmax= self.page_finder[id].get_range()
+            value.set_range(qmin, qmax)
+        ## 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, fitproblem =item[1]) 
+            self.manager.onFit(None)
+        else:
+            msg= "Select at least one model 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):
+        """
+        check all models names
+        """
+        self.model_toFit = [] 
+        if self.cb1.GetValue()== True:
+            for item in self.model_list:
+                item[0].SetValue(True)
+                self.model_toFit.append(item)
+                
+            ## constraint info
+            self._store_model()
+            ## display constraint fields
+            if self.show_constraint.GetValue():
+                self._show_constraint()
+                return
+        else:
+            for item in self.model_list:
+                item[0].SetValue(False) 
+                
+            self.model_toFit=[]
+            ##constraint info
+            self._hide_constraint()
+        
+    def check_model_name(self,event):
+        """
+        Save information related to checkbox and their states
+        """
+        self.model_toFit=[]
+        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)==2:
+            self._store_model()
+            if self.show_constraint.GetValue() and len(self.constraints_list)==0:
+                self._show_constraint()
+        elif len(self.model_toFit)< 2:
+            ##constraint info
+            self._hide_constraint()              
+       
+        ## set the value of the main check button          
+        if len(self.model_list)==len(self.model_toFit):
+            self.cb1.SetValue(True)
+            return
+        else:
+            self.cb1.SetValue(False)
+        
+    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
+        
+        if len(self.model_list)>0:
+            for item in self.model_list:
+                item[0].SetValue(False) 
+                self.manager.schedule_for_fit( value=0,fitproblem =item[1])
+                
+        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 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
+            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, 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()
+        
+    def _store_model(self):
+        """
+         Store selected model
+        """
+        if len(self.model_toFit) < 2:
+            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)< 2:
+            msg= "Select at least 2 models to add constraint "
+            wx.PostEvent(self.parent.Parent, StatusEvent(status= msg ))
+            ## hide button
+            self._hide_constraint()
+            return
+        if self.show_constraint.GetValue():
+            self._show_constraint()
+            return
+        else:
+           self._hide_constraint()
+           return 
+            
+    def _show_constraint(self):
+        """
+        Show constraint fields
+        """
+        self.btAdd.Show(True)
+        if len(self.constraints_list)!= 0:
+            nb_fit_param = 0
+            for model in self.constraint_dict.values():
+                nb_fit_param += len(get_fittableParam(model))
+            ##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()
+                self.SetScrollbars(20,20,25,65)
+                return
+            
+        if len(self.model_toFit) < 2 :
+            msg= "Select at least 2 model to add constraint "
+            wx.PostEvent(self.parent.Parent, StatusEvent(status= msg ))
+            self.sizer_constraints.Layout()
+            self.sizer2.Layout()
+            self.SetScrollbars(20,20,25,65)
+            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)
+        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)
+        ctl2 = wx.TextCtrl(self, -1)
+        egal_txt= wx.StaticText(self,-1," = ")
+        btRemove = wx.Button(self,wx.NewId(),'Remove')
+        btRemove.Bind(wx.EVT_BUTTON, self.onRemove,id= btRemove.GetId())
+        btRemove.SetToolTipString("Remove constraint.")
+       
+        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(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(ctl2, flag= wx.RIGHT|wx.EXPAND,border=10)
+        sizer_constraint.Add(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)
+        ##[combobox1, combobox2,=,textcrtl, remove button ]
+        self.constraints_list.append([model_cbox, param_cbox, egal_txt, ctl2,btRemove,sizer_constraint])
+    
+        self.nb_constraint += 1
+        self.sizer_constraints.Layout()
+        self.sizer2.Layout()
+        self.SetScrollbars(20,20,25,65)
+        
+    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()
+        self.constraints_list=[]         
+        self.sizer_constraints.Clear(True) 
+        self.sizer_constraints.Layout()    
+        self.sizer2.Layout()
+        self.SetScrollbars(20,20,25,65)
+        self.AdjustScrollbars()    
+            
+    def _on_select_model(self, event):
+        """
+        fill combox box with list of parameters
+        """
+        ##This way PC/MAC both work, instead of using event.GetClientData().
+        n = self.model_cbox.GetCurrentSelection()
+        model = self.model_cbox.GetClientData(n)
+        
+        param_list= get_fittableParam(model)
+        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.sizer2.Layout()
+        self.SetScrollbars(20,20,25,65)
+        
+    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)
+        #self.sizer2.Layout()
+        self.SetScrollbars(20,20,25,65)
+        
+    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 cosntraint
+        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())
+                msg= " Enter a constraint for %s.%s! "%(model.name,param_cbox.GetString(0))           
+                wx.PostEvent(self.parent.Parent, StatusEvent(status= msg ))
+                return 
+        ## some model or parameters can be constrained
+        self._show_constraint()
+        
+    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')
+        self.btFit.Bind(wx.EVT_BUTTON, self.onFit,id= self.btFit.GetId())
+        self.btFit.SetToolTipString("Perform fit.")
+        
+        text= "Hint: Park fitting engine will be selected \n"
+        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.EXPAND|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()
+        self.SetScrollbars(20,20,25,65)
+        
+    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)
+        box_description= wx.StaticBox(self, -1,"Fit Constraints")
+        boxsizer1 = wx.StaticBoxSizer(box_description, wx.VERTICAL)
+        sizer_title = 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()    )
+        self.hide_constraint.SetValue(True)
+        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_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:
+            model = item[0].GetClientData(item[0].GetCurrentSelection())
+            param = item[1].GetString(item[1].GetCurrentSelection())
+            constraint = item[3].GetValue().lstrip().rstrip()
+            if param.lstrip().rstrip()=="":
+                param= None
+                msg= " Constraint will be ignored!. missing parameters in combobox"
+                msg+= " 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 in textcrtl"
+                        msg+= " to set constraint! "
+                        wx.PostEvent(self.parent.Parent, StatusEvent(status= msg ))
+                        constraint = None
+                    self.page_finder[id].set_model_param(param,constraint)
+                    break
+    
+    def _fill_sizer_model_list(self,sizer):
+        """
+        Receive a dictionary containing information to display model name
+        
+        :param page_finder: the dictionary containing models information
+        
+        """
+        ix = 0
+        iy = 0
+        list=[]
+        sizer.Clear(True)
+        
+        new_name = wx.StaticText(self, -1, 'New Model Name', style=wx.ALIGN_CENTER)
+        new_name.SetBackgroundColour('orange')
+        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 Type')
+        model_type.SetBackgroundColour('grey')
+        sizer.Add(model_type,(iy, ix),(1,1),
+                            wx.EXPAND|wx.ADJUST_MINSIZE, 0) 
+        ix += 1 
+        data_used = wx.StaticText(self, -1, '  Used Data')
+        data_used.SetBackgroundColour('grey')
+        sizer.Add(data_used,(iy, ix),(1,1),
+                            wx.EXPAND|wx.ADJUST_MINSIZE, 0) 
+        ix += 1 
+        tab_used = wx.StaticText(self, -1, '  Fit Tab')
+        tab_used.SetBackgroundColour('grey')
+        sizer.Add(tab_used,(iy, ix),(1,1),
+                            wx.EXPAND|wx.ADJUST_MINSIZE, 0) 
+        for id, value in self.page_finder.iteritems():
+            try:
+                ix = 0
+                iy += 1 
+                model = value.get_model()
+                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)
+                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)
+                data = value.get_fit_data()
+                name = '-'
+                if data is not None:
+                    name = str(data.name)
+                data_used = wx.StaticText(self, -1, name)
+                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])
+                
+            except:
+                raise
+                #pass
+        iy += 1
+        sizer.Add((20,20),( iy,ix),(1,1),  wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
+        sizer.Layout()    
