Index: sansview/perspectives/fitting/basepage.py
===================================================================
--- sansview/perspectives/fitting/basepage.py	(revision 70468a577138cf2f8bf7f68a0d27f17af0fac52a)
+++ sansview/perspectives/fitting/basepage.py	(revision cc316087447594027dc1ee6e8fb72cb75f171858)
@@ -64,4 +64,6 @@
         ## data
         self.data = None
+        #list of available data
+        self.data_list = []
         self.mask = None
         self.uid = None
@@ -364,4 +366,5 @@
         """
         return self.state
+    
     def get_data(self):
         """
@@ -369,4 +372,10 @@
         """
         return self.data  
+    
+    def get_data_list(self):
+        """
+        return the current data 
+        """
+        return self.data_list  
     
     def set_manager(self, manager):
Index: sansview/perspectives/fitting/batchfitpage.py
===================================================================
--- sansview/perspectives/fitting/batchfitpage.py	(revision cc316087447594027dc1ee6e8fb72cb75f171858)
+++ sansview/perspectives/fitting/batchfitpage.py	(revision cc316087447594027dc1ee6e8fb72cb75f171858)
@@ -0,0 +1,661 @@
+
+
+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))
+            #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: sansview/perspectives/fitting/fit_thread.py
===================================================================
--- sansview/perspectives/fitting/fit_thread.py	(revision 40953a960cc209a729ff8657fde524c8ed336d64)
+++ sansview/perspectives/fitting/fit_thread.py	(revision cc316087447594027dc1ee6e8fb72cb75f171858)
@@ -3,4 +3,13 @@
 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):
@@ -45,10 +54,31 @@
         """
         msg = ""
-        try: 
+        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)
+            print inputs
+            result =  Pool(1).map(func=map_apply, 
+                               iterable=inputs)
             #self.handler.starting_fit()
             #Result from the fit
-            result = self.fitter.fit(handler=self.handler, 
-                                     curr_thread=self,
-                                     ftol=self.ftol)
+            """
+                result = self.fitter.fit(handler=self.handler, 
+                                         curr_thread=self,
+                                         ftol=self.ftol)
+            """
+            print "fithread result", result
             self.complete(result= result,
                           page_id=self.page_id,
Index: sansview/perspectives/fitting/fitpage.py
===================================================================
--- sansview/perspectives/fitting/fitpage.py	(revision 515bf4f80d391d93c92fa63af03d845711421d41)
+++ sansview/perspectives/fitting/fitpage.py	(revision cc316087447594027dc1ee6e8fb72cb75f171858)
@@ -10,5 +10,6 @@
 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 StatusEvent 
+from sans.guiframe.events import NewPlotEvent  
 from sans.guiframe.utils import format_number,check_float
 
@@ -41,5 +42,5 @@
         
         ## draw sizer
-        self._fill_datainfo_sizer()
+        self._fill_data_sizer()
         self.is_2D = None
         self.fit_started = False
@@ -59,5 +60,59 @@
         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()
+        for data in self.data_list:
+            if data is not None:
+                self.dataSource.Append(str(data.name), clientData=data)
+        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):
@@ -293,5 +348,5 @@
         sizer_chi2.Add(sizer_smearer_box)
         sizer_chi2.Add((-1,5))
-
+        
         # hide all smear messages and textctrl
         self._hide_all_smear_info()
@@ -386,65 +441,4 @@
         self.sizer5.Layout()
 
-    def _fill_datainfo_sizer(self):
-        """
-        fill sizer 0 with data info
-        """
-        ## no loaded data , don't fill the sizer
-        if self.data is None:
-            data_min = ""
-            data_max = ""
-            data_name = " None"
-        else:
-            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)
-            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)
-            ## set q range to plot
-            self.qmin_x = data_min
-            self.qmax_x = data_max
-        box_description= wx.StaticBox(self, -1, 'I(q) Data Source')
-        boxsizer1 = wx.StaticBoxSizer(box_description, wx.VERTICAL)
-        #----------------------------------------------------------
-        sizer_data = wx.BoxSizer(wx.HORIZONTAL)
-        #Filling the sizer containing data related fields
-        #self.dataSource = wx.StaticText(self, -1,str(self.data.name))
-        self.dataSource = BGTextCtrl(self, -1)
-        self.dataSource.SetValue(str(data_name))
-        #self.dataSource.SetEditable(False)
-        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) )
-        """
-        #---------sizer 2 draw--------------------------------
-        text4_3 = wx.StaticText(self, -1, 'Total Q Range (1/A)',
-                                 style=wx.ALIGN_LEFT)
-        sizer_range = wx.BoxSizer(wx.HORIZONTAL)
-        sizer_range.Add( text4_3 ,0, wx.RIGHT, 10)
-        self.minimum_q = BGTextCtrl(self, -1, size=(_BOX_WIDTH,20))
-        self.minimum_q.SetValue(str(data_min))
-        self.maximum_q = BGTextCtrl(self, -1,size=(_BOX_WIDTH,20))
-        self.maximum_q.SetValue(str(data_max))
-        sizer_range.Add(wx.StaticText(self, -1, "Min: "),0, wx.LEFT, 10)
-        sizer_range.Add(self.minimum_q,0, wx.LEFT, 10)
-        sizer_range.Add(wx.StaticText(self, -1, "Max: "),0, wx.LEFT, 10)
-        sizer_range.Add(self.maximum_q,0, wx.LEFT, 10)
-        """
-        boxsizer1.Add(sizer_data,0, wx.ALL, 10)
-        #boxsizer1.Add(sizer_range, 0 , wx.LEFT, 10)
-        #------------------------------------------------------------
-        
-        self.sizer0.Add(boxsizer1,0, wx.EXPAND | wx.ALL, 10)
-        self.sizer0.Layout()
        
     def _fill_model_sizer(self, sizer):
@@ -1636,10 +1630,14 @@
         reset the current data 
         """
+        id = None
+        group_id = None
         flag = False
         if self.data is None and data is not None:
-            self.window_name = str(data.name)
-            ## Title to appear on top of the window
-            self.window_caption = str(data.name)
             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:
@@ -1727,4 +1725,15 @@
                 
             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()
     
Index: sansview/perspectives/fitting/fitpanel.py
===================================================================
--- sansview/perspectives/fitting/fitpanel.py	(revision 00ea2a77965ff4b41113952bf1e8ee860df59d95)
+++ sansview/perspectives/fitting/fitpanel.py	(revision cc316087447594027dc1ee6e8fb72cb75f171858)
@@ -54,4 +54,8 @@
         ## 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
@@ -309,12 +313,20 @@
         add an empty page
         """
-        from fitpage import FitPage
-        panel = FitPage(parent=self)
+        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)
-        index = self.GetPageCount()
-        #caption = str(panel.window_name) + " " + str(self._manager.index_model)
-        caption = str(panel.window_name) + " " + str(index)
+        caption = str(panel.window_caption) + " " + str(index)
         self.AddPage(panel, caption, select=True)
         self.opened_pages[panel.uid] = panel
@@ -360,5 +372,5 @@
                 self._manager.on_add_new_page(event=None)
         
-    def set_data(self, data):
+    def set_data(self, data_list):
         """ 
         Add a fitting page on the notebook contained by fitpanel
@@ -369,30 +381,47 @@
         
         """
-        if data is None:
+        if not data_list:
             return None
+        
+        if self.batch_on:
+            page = self.add_empty_page()
+            print  "batch on fitpanel", page.__class__.__name__, page.window_caption
+            pos = self.GetPageIndex(page)
+            page.fill_data_combobox(data_list)
+            self.opened_pages[page.uid] = page
+            return page
+        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.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
-            """
+                # 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.set_data(data)
+        page.fill_data_combobox(data_list)
+        #page.set_data(data)
         self.SetPageText(pos, str(data.name))
         self.opened_pages[page.uid] = page
@@ -447,5 +476,8 @@
             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()
Index: sansview/perspectives/fitting/fitproblem.py
===================================================================
--- sansview/perspectives/fitting/fitproblem.py	(revision ba1f0b24608060d5541e7447d9f19042c3fb21b4)
+++ sansview/perspectives/fitting/fitproblem.py	(revision cc316087447594027dc1ee6e8fb72cb75f171858)
@@ -13,4 +13,8 @@
         ## 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
@@ -28,4 +32,5 @@
         self.qmin = None
         self.qmax = None
+        self.result = []
         
         # 1D or 2D
@@ -143,4 +148,18 @@
         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):
         """ 
@@ -205,3 +224,27 @@
         """
         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: sansview/perspectives/fitting/fitting.py
===================================================================
--- sansview/perspectives/fitting/fitting.py	(revision 0145a254b5b74a5d4a57c65d6bc02f7140b14fa3)
+++ sansview/perspectives/fitting/fitting.py	(revision cc316087447594027dc1ee6e8fb72cb75f171858)
@@ -34,8 +34,8 @@
 from sans.guiframe.plugin_base import PluginBase 
 
-#from .console import ConsoleUpdate
+from .console import ConsoleUpdate
 from .fitproblem import FitProblem
 from .fitpanel import FitPanel
-#from .fit_thread import FitThread
+from .fit_thread import FitThread
 from .pagestate import Reader
 from .fitpage import Chi2UpdateEvent
@@ -108,4 +108,13 @@
         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):
         """
@@ -275,21 +284,22 @@
             data_list = []
         selected_data_list = []
-        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()
-            
+        if self.batch_on:
+            page = self.add_fit_page(data=data_list)
         else:
-            selected_data_list = data_list
-        try:
-            for data in selected_data_list:
-                page = self.add_fit_page(data=data)
-                wx.PostEvent(self.parent, NewPlotEvent(plot=data, 
-                                                       title=str(data.title)))
-        except:
-            msg = "Fitting Set_data: " + str(sys.exc_value)
-            wx.PostEvent(self.parent, StatusEvent(status=msg, info="error"))
+            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):
@@ -371,7 +381,9 @@
                 wx.PostEvent(self.parent, NewPlotEvent(plot=data,
                                         title=data.title))
-                page = self.add_fit_page(data)
+                page = self.add_fit_page([data])
                 caption = page.window_name
-                self.store_data(page=page.uid, data=data, caption=caption)
+                self.store_data(uid=page.uid, data=page.get_data(),
+                        data_list=page.get_data_list(), 
+                        caption=page.window_name)
                 self.mypanels.append(page) 
                 
@@ -602,12 +614,6 @@
         """
         perform fit 
-        
-        : param type: uid for single fit, None for simultaneous fit
-        """
-        from sans.fit.Fitting import Fit
-        from .fit_thread import FitThread
-        from .console import ConsoleUpdate
+        """
         ##  count the number of fitproblem schedule to fit 
-        #if uid == None or self._fit_engine == "park":
         fitproblem_count = 0
         for value in self.page_finder.values():
@@ -616,11 +622,12 @@
                 
         ## if simultaneous fit change automatically the engine to park
-        # scipy can not handle two thread running at the same time
-        if fitproblem_count > 1 or len(self.fit_thread_list)>0:
+        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"
@@ -628,4 +635,5 @@
             engineType = "Single Fit"
             
+        fitter_list = []
         fproblemId = 0
         self.current_pg = None
@@ -655,4 +663,5 @@
                     self._fit_helper(value=value, pars=pars,
                                      fitter=fitter,
+                                     fitter_list=fitter_list,
                                       fitproblem_id=fproblemId,
                                       title=engineType) 
@@ -679,8 +688,8 @@
         self._mac_sleep(0.2)
         ## perform single fit
-        
         if fitproblem_count == 1:
+            print "fitter_list", fitter_list
             calc_fit = FitThread(handler = handler,
-                                    fn=fitter,
+                                    fn=fitter_list,
                                     pars=pars,
                                     page_id=list_page_id,
@@ -691,16 +700,16 @@
             ## Perform more than 1 fit at the time
             calc_fit = FitThread(handler=handler,
-                                    fn=fitter,
+                                    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
-        self._mac_sleep(0.2)
-        
-        #self.ready_fit(calc_fit=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):
         """
@@ -730,5 +739,5 @@
                                                    action='remove'))
            
-    def store_data(self, uid, data=None, caption=None):
+    def store_data(self, uid, data=None, data_list=None, caption=None):
         """
         Helper to save page reference into the plug-in
@@ -737,8 +746,11 @@
         
         """
+        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)
         
@@ -778,4 +790,5 @@
         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()
@@ -795,5 +808,7 @@
                     self.parent.update_data(prev_data=theory_data, new_data=data)   
               
-        self.store_data(uid=page.uid, data=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)
         if self.sim_page is not None:
             self.sim_page.draw_page()
@@ -875,42 +890,63 @@
             self.page_finder[uid].schedule_tofit(value)
                 
-    def _fit_helper(self, pars, value, fitproblem_id,fitter, title="Single Fit " ):
-        """
-        helper for fitting
-        """
-        metadata = value.get_fit_data()
+    def _fit_setter(self, data, value, fitter, fit_id, pars):
+        """
+        """
         model = value.get_model()
         smearer = value.get_smearer()
         qmin, qmax = value.get_range()
-        self.fit_id = fitproblem_id
+        #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 = []
-       
-        try:
-            #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(model, self.fit_id,
-                                   pars, constraints=listOfConstraint)
-            
-            fitter.set_data(data=metadata, id=self.fit_id,
-                                 smearer=smearer, qmin=qmin, qmax=qmax)
-           
-            fitter.select_problem_for_fit(id=self.fit_id,
-                                               value=value.get_scheduled())
-            value.clear_model_param()
-        except:
-            raise
-            #msg = title + " error: %s" % sys.exc_value
-            #wx.PostEvent(self.parent, StatusEvent(status=msg, type="stop"))
-          
+        for single_data in data_list:
+            fitter = Fit(self._fit_engine)
+            try:
+                if single_data.id in pointer_to_fitproblem:
+                     new_value = self.page_finder[pointer_to_fitproblem[single_data.id]]
+                     self._fit_setter(data=single_data, value=new_value, 
+                                     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):
         """ 
@@ -925,9 +961,9 @@
                 if plottable == self.panel.plots[data_id]:
                     data = plottable
-                    self.add_fit_page(data=data)
+                    self.add_fit_page(data=[data])
                     return
             else:
                 data = plottable
-                self.add_fit_page(data=data)
+                self.add_fit_page(data=[data])
         self.set_top_panel()
             
@@ -936,4 +972,28 @@
         """
         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])
+                    batch_result["error on %s" % pars[index]].append(res.stderr[index])
+              
+            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):
@@ -950,61 +1010,66 @@
         self._mac_sleep(0.2)
         if page_id[0] in self.fit_thread_list.keys():
-            del self.fit_thread_list[page_id[0]]  
-        try:
-            if result == None:
+            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="warning",
-                                         type="stop"))
+                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
-            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):
         """
