Index: src/sas/sasgui/perspectives/calculator/calculator.py
===================================================================
--- src/sas/sasgui/perspectives/calculator/calculator.py	(revision d85c194ea2f78cb2b9b3de9c969e1b8ebea6b6d2)
+++ src/sas/sasgui/perspectives/calculator/calculator.py	(revision a0c1e1de9eb99665b31c85c4fddcfca740db719c)
@@ -80,5 +80,5 @@
                 ("Kiessig Thickness Calculator",
                         kiessig_help, self.on_calculate_kiessig),
-                          ("SAS Resolution Estimator",
+                          ("Q Resolution Estimator",
                         resolution_help, self.on_calculate_resoltuion),
                 ("Generic Scattering Calculator",
Index: src/sas/sasgui/perspectives/calculator/data_operator.py
===================================================================
--- src/sas/sasgui/perspectives/calculator/data_operator.py	(revision 0e760e9190d1a156409589ee9c50372130d80dbe)
+++ src/sas/sasgui/perspectives/calculator/data_operator.py	(revision 61780e3355265f8377c4d588db7b820d2c9989a7)
@@ -397,5 +397,5 @@
             self._set_textctrl_color(self.data1_cbox, 'pink')
             self._set_textctrl_color(self.data2_cbox, 'pink')
-            msg = "DataOperation: Data types must be same."
+            msg = "DataOperation: %s" % sys.exc_value
             self.send_warnings(msg, 'error')
             self.output = None
Index: src/sas/sasgui/perspectives/calculator/media/image_viewer_help.rst
===================================================================
--- src/sas/sasgui/perspectives/calculator/media/image_viewer_help.rst	(revision d85c194ea2f78cb2b9b3de9c969e1b8ebea6b6d2)
+++ src/sas/sasgui/perspectives/calculator/media/image_viewer_help.rst	(revision da456fba6ab06ddec7ae847f352aff18d1d855d1)
@@ -3,4 +3,6 @@
 .. This is a port of the original SasView html help file to ReSTructured text
 .. by S King, ISIS, during SasView CodeCamp-III in Feb 2015.
+
+.. _Image_Viewer_Tool:
 
 Image Viewer Tool
Index: src/sas/sasgui/perspectives/calculator/media/sas_calculator_help.rst
===================================================================
--- src/sas/sasgui/perspectives/calculator/media/sas_calculator_help.rst	(revision 780545820bf596bf3709c434f3eb26bcc29a1236)
+++ src/sas/sasgui/perspectives/calculator/media/sas_calculator_help.rst	(revision da456fba6ab06ddec7ae847f352aff18d1d855d1)
@@ -3,4 +3,6 @@
 .. This is a port of the original SasView html help file to ReSTructured text
 .. by S King, ISIS, during SasView CodeCamp-III in Feb 2015.
+
+.. _SANS_Calculator_Tool:
 
 Generic SANS Calculator Tool
Index: src/sas/sasgui/perspectives/calculator/pyconsole.py
===================================================================
--- src/sas/sasgui/perspectives/calculator/pyconsole.py	(revision f1cbae76f663f0b5d581265b20a168fdcaca6f90)
+++ src/sas/sasgui/perspectives/calculator/pyconsole.py	(revision 0912b405bd0067ba7456b60f02342b50c7f6671b)
@@ -231,4 +231,11 @@
         if result.path:
             self.bufferCreate(result.path)
+
+        # See if there is a corresponding C file
+        if result.path != None:
+            c_filename = os.path.splitext(result.path)[0] + ".c"
+            if os.path.isfile(c_filename):
+                self.bufferCreate(c_filename)
+
         cancel = False
         return cancel
Index: src/sas/sasgui/perspectives/calculator/resolution_calculator_panel.py
===================================================================
--- src/sas/sasgui/perspectives/calculator/resolution_calculator_panel.py	(revision d0248bd59e16e863c884f4be9af0a03b2a86b6e2)
+++ src/sas/sasgui/perspectives/calculator/resolution_calculator_panel.py	(revision a0c1e1de9eb99665b31c85c4fddcfca740db719c)
@@ -63,5 +63,5 @@
     """
     ## Internal nickname for the window, used by the AUI manager
-    window_name = "SAS Resolution Estimator"
+    window_name = "Q Resolution Estimator"
     ## Name to appear on the window title bar
     window_caption = ""
@@ -1329,5 +1329,5 @@
     """
     def __init__(self, parent=None, manager=None,
-                 title="SAS Resolution Estimator",
+                 title="Q Resolution Estimator",
                  size=(PANEL_WIDTH * 2, PANEL_HEIGHT), *args, **kwds):
         kwds['title'] = title
Index: src/sas/sasgui/perspectives/corfunc/media/corfunc_help.rst
===================================================================
--- src/sas/sasgui/perspectives/corfunc/media/corfunc_help.rst	(revision f56770ef50e9e3e1561054983f01b7b9ab68dab2)
+++ src/sas/sasgui/perspectives/corfunc/media/corfunc_help.rst	(revision da456fba6ab06ddec7ae847f352aff18d1d855d1)
@@ -1,3 +1,5 @@
 .. corfunc_help.rst
+
+.. _Correlation_Function_Analysis:
 
 Correlation Function Analysis
Index: src/sas/sasgui/perspectives/file_converter/converter_panel.py
===================================================================
--- src/sas/sasgui/perspectives/file_converter/converter_panel.py	(revision be880765648278fd07d2d8b28d75b9ca7b238527)
+++ src/sas/sasgui/perspectives/file_converter/converter_panel.py	(revision ed9f87285808f62d344f2b0a8c086bbce09ab338)
@@ -499,10 +499,13 @@
         vbox = wx.BoxSizer(wx.VERTICAL)
 
-        instructions = ("Select either single column ASCII files or 1D OTOKO "
-        "files containing the Q-Axis and Intensity-Axis data, or a 2D BSL file"
-        ", then chose where to save the converted file, and click Convert.\n"
-        "ASCII and OTOKO files can be converted to CanSAS XML, or NXcanSAS "
-        "HDF5 format. OTOKO files can only be converted to NXcanSAS.\nCanSAS "
-        "metadata can also be optionally input below.")
+        instructions = (
+        "Select linked single column 1D ASCII files containing the Q-axis and "
+        "Intensity-axis data, or 1D BSL/OTOKO files, or a 2D BSL/OTOKO file, "
+        "then choose where to save the converted file, and click Convert.\n"
+        "1D ASCII and BSL/OTOKO files can be converted to CanSAS (XML) or "
+        "NXcanSAS (HDF5) formats. 2D BSL/OTOKO files can only be converted to "
+        "the NXcanSAS format.\n"
+        "Metadata can be optionally added for the CanSAS XML format."
+        )
 
         instruction_label = wx.StaticText(self, -1, instructions,
@@ -527,5 +530,5 @@
         ascii_btn.Bind(wx.EVT_RADIOBUTTON, self.datatype_changed)
         radio_sizer.Add(ascii_btn)
-        otoko_btn = wx.RadioButton(self, -1, "OTOKO 1D", name="otoko")
+        otoko_btn = wx.RadioButton(self, -1, "BSL 1D", name="otoko")
         otoko_btn.Bind(wx.EVT_RADIOBUTTON, self.datatype_changed)
         radio_sizer.Add(otoko_btn)
Index: src/sas/sasgui/perspectives/file_converter/media/file_converter_help.rst
===================================================================
--- src/sas/sasgui/perspectives/file_converter/media/file_converter_help.rst	(revision 97e90b79c97359b5c5f64056127cb3772410daf3)
+++ src/sas/sasgui/perspectives/file_converter/media/file_converter_help.rst	(revision d73998cb7afc31b78a37055063aeb56b7c0d1036)
@@ -1,3 +1,5 @@
 .. file_converter_help.rst
+
+.. _File_Converter_Tool:
 
 File Converter Tool
@@ -7,24 +9,24 @@
 -----------
 
-This tool converts file formats with the Q data and intensity data in separate
-files, into a single CanSAS XML or NXcanSAS HDF5 file.
+This tool converts file formats with the Q data and Intensity data stored in separate
+files, into a single CanSAS (XML) or NXcanSAS (HDF5) file.
 
-It can also convert 2D BSL files into NXcanSAS HDF5 files.
+It can also convert 2D BSL/OTOKO files into a NXcanSAS file.
 
-Supported input file formats:
+Supported input file formats (examples may be found in the /test/convertible_files folder):
 
-*   Single column ASCII data, with lines that end with a digit (no delimiter),
-    comma or semi-colon.
-*   `One-Dimensional OTOKO formatted
+*   Single-column ASCII data, with lines that end without any delimiter,
+    or with a comma or semi-colon delimiter
+*   `1D BSL/OTOKO format
     <http://www.diamond.ac.uk/Beamlines/Soft-Condensed-Matter/small-angle/
-    SAXS-Software/CCP13/XOTOKO.html>`_ data.
-*   `Two-Dimensional BSL formatted
+    SAXS-Software/CCP13/BSL.html>`_ data
+*   `2D BSL/OTOKO format
     <http://www.diamond.ac.uk/Beamlines/Soft-Condensed-Matter/small-angle/
-    SAXS-Software/CCP13/BSL.html>`_ data.
+    SAXS-Software/CCP13/BSL.html>`_ data
 
-Supported output file formats@
+Supported output file formats:
 
-*   `CanSAS XML <http://www.cansas.org/formats/canSAS1d/1.1/doc/>`_
-*   `NXcanSAS HDF5 <http://download.nexusformat.org/sphinx/classes/contributed_definitions/NXcanSAS.html>`_
+*   `CanSAS <http://www.cansas.org/formats/canSAS1d/1.1/doc/>`_
+*   `NXcanSAS <http://download.nexusformat.org/sphinx/classes/contributed_definitions/NXcanSAS.html>`_
 
 .. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
@@ -33,18 +35,19 @@
 --------------
 
-1) Select the files containing your Q-Axis and Intensity-Axis data
-2) Chose whether the files are in ASCII, OTOKO or BSL format
-3) Chose where you would like to save the converted file
+1) Select the files containing your Q-axis and Intensity-axis data
+2) Choose whether the files are in ASCII, 1D BSL/OTOKO or 2D BSL/OTOKO format
+3) Choose where you would like to save the converted file
 4) Optionally, input some metadata such as sample size, detector name, etc
-5) Click *Convert* to save the converted file to disk
+5) Click *Convert* to save the converted file
 
 Files With Multiple Frames
 ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-If a BSL/OTOKO file with multiple frames is selected for the intensity-axis
+If a BSL/OTOKO file with multiple frames is selected for the Intensity-axis
 file, a dialog will appear asking which frames you would like converted. You
 may enter a start frame, end frame & increment, and all frames in that subset
-will be converted. For example: entering 0, 50 and 10 for the first frame, last
-frame, and increment respectively will convert frames 0, 10, 20, 30, 40 & 50.
+will be converted. For example, entering 0, 50 and 10 will convert frames 0, 
+10, 20, 30, 40 & 50.
+
 To convert a single frame, enter the same value for first frame & last frame,
 and 1 as the increment.
@@ -52,10 +55,13 @@
 CanSAS XML files can become quite large when exporting multiple frames to a
 single file, so there is an option in the *Select Frame* dialog to output each
-frame to it's own file. The single file option will produce one file with
-multiple `<SASdata>` elements. The multiple file option will output a file for
-each frame; each file will have one `<SASdata>` element, and the frame number
-will appended to the file name.
+frame to its own file. The single file option will produce one file with
+multiple `<SASdata>` elements. The multiple file option will output a separate 
+file with one `<SASdata>` element for each frame. The frame number will also be 
+appended to the file name.
 
-The multiple file option is not available when exporting to NXcanSAS. All
-frames will be written to the same file, since the HDF5 format is designed for
-handling large amounts of data.
+The multiple file option is not available when exporting to NXcanSAS because 
+the HDF5 format is more efficient at handling large amounts of data.
+
+
+.. note::
+    This help document was last changed by Steve King, 08Oct2016
Index: src/sas/sasgui/perspectives/fitting/basepage.py
===================================================================
--- src/sas/sasgui/perspectives/fitting/basepage.py	(revision 79885018e2047aef9517748c8de7eccc28d1305e)
+++ src/sas/sasgui/perspectives/fitting/basepage.py	(revision 79885018e2047aef9517748c8de7eccc28d1305e)
@@ -9,5 +9,4 @@
 import copy
 import math
-import string
 import json
 import logging
@@ -340,11 +339,4 @@
         self.data.detector[index].beam_center.x = qmax
         self.data.detector[index].beam_center.y = qmax
-        ## create x_bins and y_bins of the model 2D
-        #pixel_width_x = self.data.detector[index].pixel_size.x
-        #pixel_width_y = self.data.detector[index].pixel_size.y
-        #center_x = self.data.detector[index].beam_center.x/pixel_width_x
-        #center_y = self.data.detector[index].beam_center.y/pixel_width_y
-        # theory default: assume the beam
-        #center is located at the center of sqr detector
         xmax = qmax
         xmin = -qmax
@@ -365,14 +357,7 @@
         # set all True (standing for unmasked) as default
         mask = numpy.ones(len(qx_data), dtype=bool)
-        # calculate the range of qx and qy: this way,
-        # it is a little more independent
-        #x_size = xmax - xmin
-        #y_size = ymax - ymin
         # store x and y bin centers in q space
         x_bins = x
         y_bins = y
-        # bin size: x- & y-directions
-        #xstep = x_size / len(x_bins - 1)
-        #ystep = y_size / len(y_bins - 1)
 
         self.data.source = Source()
@@ -415,43 +400,10 @@
             batch_menu.Enable(self.batch_on and flag)
 
-    def set_page_info(self, page_info):
-        """
-        set some page important information at once
-        """
-#       THIS METHOD/FUNCTION NO LONGE APPEARS TO BE CALLED.  Started up program
-#       and started new fit window and PR and Invariant and a fit in fitting
-#       but never entered this routine which should be an initialization
-#       routine.  Leave for a while but probably something to clean up at
-#       some point?
-#
-#       PDB April 13 2014
-#
-        ##window_name
-        self.window_name = page_info.window_name
-        ##window_caption
-        self.window_caption = page_info.window_caption
-        ## manager is the fitting plugin
-        self._manager = page_info.manager
-        ## owner of the page (fitting plugin)
-        self.event_owner = page_info.event_owner
-        ## current model
-        self.model = page_info.model
-        ## data
-        self.data = page_info.data
-        ## dictionary containing list of models
-        self.model_list_box = page_info.model_list_box
-        ## Data member to store the dispersion object created
-        self.populate_box(model_dict=self.model_list_box)
-
     def onContextMenu(self, event):
         """
         Retrieve the state selected state
         """
-        # Skipping the save state functionality for release 0.9.0
-        #return
-
         pos = event.GetPosition()
         pos = self.ScreenToClient(pos)
-
         self.PopupMenu(self.popUpMenu, pos)
 
@@ -570,28 +522,7 @@
     def initialize_combox(self):
         """
-        put default value in the combobox
-        """
-        ## fill combox box
-        if self.model_list_box is None:
-            return
-        if len(self.model_list_box) > 0:
-        ## This is obsolete code since form factor box is no longer static.
-        ## It is now set dynamically through _show_combox and _show_combos_helper
-        ## These are called for first time by formfactor_combo_init
-        ## itself called from fitpanel only.  If we find that I'm wrong and
-        ## we DO need to initialize somehow here - do it by a call to
-        ## formfactor_combo_init
-        ## self.formfator_combo_init()
-        ## BUT NOT HERE -- make it last line of this
-        ## method so that structure box is populated before _show_comboox_helper
-        ## is called.  Otherwise wx will complain mightily:-)
-        ##
-        ## Also change the name to initiatlize_structurebox along with changes
-        ## to other combobox methods (_populate_listbox --> _populate_categorybox
-        ## etc )
-        ##
-        ##     PDB 4/26/2014
-#            self._populate_box(self.formfactorbox,
-#                               self.model_list_box["Shapes"])
+        put default value in the combo box
+        """
+        if self.model_list_box is not None and len(self.model_list_box) > 0:
             self._populate_box(self.structurebox,
                                self.model_list_box["Structure Factors"])
@@ -602,11 +533,4 @@
             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):
@@ -746,5 +670,4 @@
         else:
             wx.CallAfter(self.get_copy)
-
 
     def on_paste(self, event):
@@ -960,9 +883,9 @@
                                      self.state.orientation_params)
         self._copy_parameters_state(self.orientation_params_disp,
-                                     self.state.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.state.fittable_param)
         self._copy_parameters_state(self.fixed_param, self.state.fixed_param)
         #save chisqr
@@ -1052,10 +975,4 @@
         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
@@ -1886,6 +1803,5 @@
             if mod_cat == custom_model:
                 for model in self.model_list_box[mod_cat]:
-                    str_m = model.id if hasattr(model, 'id') else model.name
-                    m_list.append(self.model_dict[str_m])
+                    m_list.append(self.model_dict[model.name])
             else:
                 cat_dic = self.master_category_dict[mod_cat]
@@ -1893,8 +1809,4 @@
                     if enabled:
                         m_list.append(self.model_dict[model])
-                    #else:
-                    #    msg = "This model is disabled by Category Manager."
-                    #    wx.PostEvent(self.parent.parent,
-                    #                 StatusEvent(status=msg, info="error"))
         except Exception:
             msg = traceback.format_exc()
@@ -2067,5 +1979,5 @@
             form_factor = self.formfactorbox.GetClientData(f_id)
 
-        if not form_factor in  self.model_list_box["multiplication"]:
+        if form_factor is None or not form_factor.is_form_factor:
             self.structurebox.Hide()
             self.text2.Hide()
@@ -3474,18 +3386,8 @@
         self.by_model_dict = defaultdict(list)
         self.model_enabled_dict = defaultdict(bool)
-
-        try:
-            categorization_file = CategoryInstaller.get_user_file()
-            if not os.path.isfile(categorization_file):
-                categorization_file = CategoryInstaller.get_default_file()
-            cat_file = open(categorization_file, 'rb')
-            self.master_category_dict = json.load(cat_file)
-            self._regenerate_model_dict()
-            cat_file.close()
-        except IOError:
-            raise
-            print 'Problem reading in category file.'
-            print 'We even looked for it, made sure it was there.'
-            print 'An existential crisis if there ever was one.'
+        categorization_file = CategoryInstaller.get_user_file()
+        with open(categorization_file, 'rb') as f:
+            self.master_category_dict = json.load(f)
+        self._regenerate_model_dict()
 
     def _regenerate_model_dict(self):
Index: src/sas/sasgui/perspectives/fitting/fitpanel.py
===================================================================
--- src/sas/sasgui/perspectives/fitting/fitpanel.py	(revision 05228b001b88187176fb9a2d1bfd9098273fb854)
+++ src/sas/sasgui/perspectives/fitting/fitpanel.py	(revision e6de6b8b31744ebe652ab6206d60d0d20979c592)
@@ -10,7 +10,7 @@
 
 from sas.sasgui.guiframe.panel_base import PanelBase
-from sas.sasgui.guiframe.events import PanelOnFocusEvent
-from sas.sasgui.guiframe.events import StatusEvent
+from sas.sasgui.guiframe.events import PanelOnFocusEvent, StatusEvent
 from sas.sasgui.guiframe.dataFitting import check_data_validity
+from sas.sasgui.perspectives.fitting.simfitpage import SimultaneousFitPage
 
 import basepage
@@ -18,4 +18,5 @@
 _BOX_WIDTH = 80
 
+
 class FitPanel(nb, PanelBase):
     """
@@ -26,7 +27,7 @@
 
     """
-    ## Internal name for the AUI manager
+    # Internal name for the AUI manager
     window_name = "Fit panel"
-    ## Title to appear on top of the window
+    # Title to appear on top of the window
     window_caption = "Fit Panel "
     CENTER_PANE = True
@@ -40,28 +41,28 @@
                     wx.CLIP_CHILDREN)
         PanelBase.__init__(self, parent)
-        #self.SetWindowStyleFlag(style=nb.FNB_FANCY_TABS)
+        # 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}
+        # 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
+        # pageClosedEvent = nb.EVT_FLATNOTEBOOK_PAGE_CLOSING
         self.model_dictionary = self.menu_mng.get_model_dictionary()
         self.pageClosedEvent = wx.aui.EVT_AUINOTEBOOK_PAGE_CLOSE
 
         self.Bind(self.pageClosedEvent, self.on_close_page)
-        ## save the title of the last page tab added
+        # save the title of the last page tab added
         self.fit_page_name = {}
-        ## list of existing fit page
+        # list of existing fit page
         self.opened_pages = {}
-        #index of fit page
+        # index of fit page
         self.fit_page_index = 0
-        #index of batch page
+        # index of batch page
         self.batch_page_index = 0
-        #page of simultaneous fit
+        # page of simultaneous fit
         self.sim_page = None
         self.batch_page = None
-        ## get the state of a page
+        # get the state of a page
         self.Bind(basepage.EVT_PAGE_INFO, self._onGetstate)
         self.Bind(basepage.EVT_PREVIOUS_STATE, self._onUndo)
@@ -80,30 +81,25 @@
         """
         return an xml node containing state of the panel
-         that guiframe can write to file
-        """
-        msg = ""
+        that guiframe can write to file
+        """
+        # Iterate through all pages and check for batch fitting
+        batch_state = None
+        if self.sim_page is not None:
+            batch_state = self.sim_page.set_state()
+
         for uid, page in self.opened_pages.iteritems():
-            if page.batch_on:
-                pos = self.GetPageIndex(page)
-                if pos != -1 and page not in [self.sim_page, self.batch_page]:
-                    msg += "%s .\n" % str(self.GetPageText(pos))
-            else:
-                data = page.get_data()
-                # state must be cloned
-                state = page.get_state().clone()
-                if data is not None and page.model is not None:
-                    new_doc = self._manager.state_reader.write_toXML(data,
-                                                                     state)
-                    if doc != None and hasattr(doc, "firstChild"):
-                        child = new_doc.firstChild.firstChild
-                        doc.firstChild.appendChild(child)
-                    else:
-                        doc = new_doc
-        if msg.strip() != "":
-            temp = "Save Project is not supported for Batch page.\n"
-            temp += "The following pages will not be save:\n"
-            message = temp + msg
-            wx.PostEvent(self._manager.parent, StatusEvent(status=message,
-                                                            info="warning"))
+            data = page.get_data()
+            # state must be cloned
+            state = page.get_state().clone()
+            if data is not None or page.model is not None:
+                new_doc = self._manager.state_reader.write_toXML(data,
+                                                                 state,
+                                                                 batch_state)
+                if doc is not None and hasattr(doc, "firstChild"):
+                    child = new_doc.firstChild.firstChild
+                    doc.firstChild.appendChild(child)
+                else:
+                    doc = new_doc
+
         return doc
 
@@ -179,5 +175,5 @@
     def get_state(self):
         """
-         return the state of the current selected page
+        return the state of the current selected page
         """
         pos = self.GetSelection()
@@ -191,9 +187,9 @@
         """
 
-        #get number of pages
+        # get number of pages
         nop = self.GetPageCount()
-        #use while-loop, for-loop will not do the job well.
+        # use while-loop, for-loop will not do the job well.
         while (nop > 0):
-            #delete the first page until no page exists
+            # delete the first page until no page exists
             page = self.GetPage(0)
             if self._manager.parent.panel_on_focus == page:
@@ -201,10 +197,11 @@
             self._close_helper(selected_page=page)
             self.DeletePage(0)
-            nop = nop - 1
-
-        ## save the title of the last page tab added
+            nop = self.GetPageCount()
+        # Clear list of names
         self.fit_page_name = {}
-        ## list of existing fit page
+        # Clear list of opened pages
         self.opened_pages = {}
+        self.fit_page_index = 0
+        self.batch_page_index = 0
 
     def set_state(self, state):
@@ -215,5 +212,5 @@
         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
+                # 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
@@ -223,8 +220,8 @@
             if not page_is_opened:
                 if state.data.__class__.__name__ != 'list':
-                    #To support older state file format
+                    # To support older state file format
                     list_data = [state.data]
                 else:
-                    #Todo: need new file format for the list
+                    # Todo: need new file format for the list
                     list_data = state.data
                 panel = self._manager.add_fit_page(data=list_data)
@@ -241,8 +238,9 @@
         Clear and close all panels, used by guimanager
         """
-
-        #close all panels only when svs file opened
+        # close all panels only when svs file opened
         self.close_all()
         self._manager.mypanels = []
+        self.sim_page = None
+        self.batch_page = None
 
     def on_close_page(self, event=None):
@@ -253,5 +251,5 @@
         selected_page = self.GetPage(self.GetSelection())
         if nbr_page == 1:
-            if selected_page.get_data() == None:
+            if selected_page.get_data() is not None:
                 if event is not None:
                     event.Veto()
@@ -271,6 +269,6 @@
 
                 if data is None:
-                    #the fitpanel exists and only the initial fit page is open
-                    #with no selected data
+                    # the fitpanel exists and only the initial fit page is open
+                    # with no selected data
                     return
                 if data.id == deleted_data.id:
@@ -284,5 +282,4 @@
 
         :param manager: instance of plugin fitting
-
         """
         self._manager = manager
@@ -333,5 +330,5 @@
         else:
             self.batch_page = SimultaneousFitPage(self, batch_on=True,
-                                                   page_finder=page_finder)
+                                                  page_finder=page_finder)
             self.batch_page.window_caption = caption
             self.batch_page.window_name = caption
@@ -346,16 +343,6 @@
         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
-        from batchfitpage import BatchFitPage
-        if self.batch_on:
             panel = BatchFitPage(parent=self)
             self.batch_page_index += 1
@@ -363,5 +350,6 @@
             panel.set_index_model(self.batch_page_index)
         else:
-            #Increment index of fit page
+            # Increment index of fit page
+            from fitpage import FitPage
             panel = FitPage(parent=self)
             self.fit_page_index += 1
@@ -441,9 +429,9 @@
         page = None
         for p in self.opened_pages.values():
-            #check if there is an empty page to fill up
+            # check if there is an empty page to fill up
             if not check_data_validity(p.get_data()) and p.batch_on:
 
-                #make sure data get placed in 1D empty tab if data is 1D
-                #else data get place on 2D tab empty tab
+                # make sure data get placed in 1D empty tab if data is 1D
+                # else data get place on 2D tab empty tab
                 enable2D = p.get_view_mode()
                 if (data.__class__.__name__ == "Data2D" and enable2D)\
@@ -458,13 +446,13 @@
                 data_type = dlg.get_data()
                 dlg.Destroy()
-                if page  is None:
+                if page is None:
                     page = self.add_empty_page()
                 if data_type == 1:
-                    #user has selected only data1D
+                    # 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
+                # the batch analysis is canceled
                 dlg.Destroy()
                 return None
@@ -503,5 +491,5 @@
             try:
                 data = data_list[0]
-            except:
+            except Exception:
                 # for 'fitv' files
                 data_list = [data]
@@ -511,20 +499,20 @@
                 return None
         for page in self.opened_pages.values():
-            #check if the selected data existing in the fitpanel
+            # check if the selected data existing in the fitpanel
             pos = self.GetPageIndex(page)
             if not check_data_validity(page.get_data()) and not page.batch_on:
-                #make sure data get placed in 1D empty tab if data is 1D
-                #else data get place on 2D tab empty tab
+                # 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):
+                   or (data.__class__.__name__ == "Data1D" and not enable2D):
                     page.batch_on = self.batch_on
                     page._set_save_flag(not page.batch_on)
                     page.fill_data_combobox(data_list)
-                    #caption = "FitPage" + str(self.fit_page_index)
+                    # caption = "FitPage" + str(self.fit_page_index)
                     self.SetPageText(pos, page.window_caption)
                     self.SetSelection(pos)
                     return page
-        #create new page and add data
+        # create new page and add data
         page = self.add_empty_page()
         pos = self.GetPageIndex(page)
@@ -574,8 +562,8 @@
         Delete the given page from the notebook
         """
-        #remove hint page
-        #if selected_page == self.hint_page:
+        # remove hint page
+        # if selected_page == self.hint_page:
         #    return
-        ## removing sim_page
+        # removing sim_page
         if selected_page == self.sim_page:
             self._manager.sim_page = None
@@ -584,25 +572,17 @@
             self._manager.batch_page = None
             return
-        """
-        # The below is not working when delete #5 and still have #6.
-        if selected_page.__class__.__name__ == "FitPage":
-            self.fit_page_index -= 1
-        else:
-            self.batch_page_index -= 1
-        """
-        ## closing other pages
+        # closing other pages
         state = selected_page.createMemento()
         page_finder = self._manager.get_page_finder()
-        ## removing fit page
+        # removing fit page
         data = selected_page.get_data()
-        #Don' t remove plot for 2D
+        # 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
+            # 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
-
+                # 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)
@@ -610,21 +590,21 @@
             del page_finder[selected_page]
 
-        #Delete the name of the page into the list of open page
+        # Delete the name of the page into the list of open page
         for uid, list in self.opened_pages.iteritems():
-            #Don't return any panel is the exact same page is created
+            # Don't return any panel is the exact same page is created
             if selected_page.uid == uid:
                 del self.opened_pages[selected_page.uid]
                 break
-        ##remove the check box link to the model name of this page (selected_page)
+        # remove the check box link to the model name of the selected_page
         try:
             self.sim_page.draw_page()
         except:
-            ## that page is already deleted no need to remove check box on
-            ##non existing page
+            # that page is already deleted no need to remove check box on
+            # non existing page
             pass
         try:
             self.batch_page.draw_page()
         except:
-            ## that page is already deleted no need to remove check box on
-            ##non existing page
+            # that page is already deleted no need to remove check box on
+            # non existing page
             pass
Index: src/sas/sasgui/perspectives/fitting/fitting.py
===================================================================
--- src/sas/sasgui/perspectives/fitting/fitting.py	(revision ca4d9854c75a5344e770dec25221dfeb5a270915)
+++ src/sas/sasgui/perspectives/fitting/fitting.py	(revision 286c7571e8019b4c28a92d3d497cac7da825076c)
@@ -593,9 +593,11 @@
         : param datainfo: data
         """
-        #state = self.state_reader.get_state()
-        if state != None:
+        from pagestate import PageState
+        from simfitpage import SimFitPageState
+        if isinstance(state, PageState):
             state = state.clone()
-            # store fitting state in temp_state
             self.temp_state.append(state)
+        elif isinstance(state, SimFitPageState):
+            state.load_from_save_state(self)
         else:
             self.temp_state = []
@@ -1740,8 +1742,9 @@
                                       data_id=str(page_id) + " " + data.name + " unsmeared")
 
-                self.create_theory_1D(x, unsmeared_data, page_id, model, data, state,
-                                      data_description="Data unsmeared",
-                                      data_id="Data  " + data.name + " unsmeared",
-                                      dy=unsmeared_error)
+                if unsmeared_data is not None and unsmeared_error is not None:
+                    self.create_theory_1D(x, unsmeared_data, page_id, model, data, state,
+                                          data_description="Data unsmeared",
+                                          data_id="Data  " + data.name + " unsmeared",
+                                          dy=unsmeared_error)
                 
             if sq_model is not None and pq_model is not None:
Index: src/sas/sasgui/perspectives/fitting/model_thread.py
===================================================================
--- src/sas/sasgui/perspectives/fitting/model_thread.py	(revision ca4d9854c75a5344e770dec25221dfeb5a270915)
+++ src/sas/sasgui/perspectives/fitting/model_thread.py	(revision 286c7571e8019b4c28a92d3d497cac7da825076c)
@@ -179,17 +179,20 @@
             unsmeared_output[first_bin:last_bin+1] = self.model.evalDistribution(mask)
             output = self.smearer(unsmeared_output, first_bin, last_bin)
-            
+
             # Rescale data to unsmeared model
-            unsmeared_data = numpy.zeros((len(self.data.x)))
-            unsmeared_error = numpy.zeros((len(self.data.x)))
-            unsmeared_data[first_bin:last_bin+1] = self.data.y[first_bin:last_bin+1]\
-                                                    * unsmeared_output[first_bin:last_bin+1]\
-                                                    / output[first_bin:last_bin+1]
-            unsmeared_error[first_bin:last_bin+1] = self.data.dy[first_bin:last_bin+1]\
-                                                    * unsmeared_output[first_bin:last_bin+1]\
-                                                    / output[first_bin:last_bin+1]
-            unsmeared_output=unsmeared_output[index]
-            unsmeared_data=unsmeared_data[index]
-            unsmeared_error=unsmeared_error
+            # Check that the arrays are compatible. If we only have a model but no data,
+            # the length of data.y will be zero.
+            if isinstance(self.data.y, numpy.ndarray) and output.shape == self.data.y.shape:
+                unsmeared_data = numpy.zeros((len(self.data.x)))
+                unsmeared_error = numpy.zeros((len(self.data.x)))
+                unsmeared_data[first_bin:last_bin+1] = self.data.y[first_bin:last_bin+1]\
+                                                        * unsmeared_output[first_bin:last_bin+1]\
+                                                        / output[first_bin:last_bin+1]
+                unsmeared_error[first_bin:last_bin+1] = self.data.dy[first_bin:last_bin+1]\
+                                                        * unsmeared_output[first_bin:last_bin+1]\
+                                                        / output[first_bin:last_bin+1]
+                unsmeared_output=unsmeared_output[index]
+                unsmeared_data=unsmeared_data[index]
+                unsmeared_error=unsmeared_error
         else:
             output[index] = self.model.evalDistribution(self.data.x[index])
Index: src/sas/sasgui/perspectives/fitting/models.py
===================================================================
--- src/sas/sasgui/perspectives/fitting/models.py	(revision 6fb559d7e0b3729983ce407aeb8ba9c519a57907)
+++ src/sas/sasgui/perspectives/fitting/models.py	(revision 212bfc297ca102b648c949f030d3d06a4c569fa3)
@@ -412,5 +412,5 @@
     """
     __modelmanager = ModelManagerBase()
-    cat_model_list = [model_name for model_name \
+    cat_model_list = [__modelmanager.model_dictionary[model_name] for model_name \
                       in __modelmanager.model_dictionary.keys() \
                       if model_name not in __modelmanager.stored_plugins.keys()]
Index: src/sas/sasgui/perspectives/fitting/pagestate.py
===================================================================
--- src/sas/sasgui/perspectives/fitting/pagestate.py	(revision 6c382da7a67e14f0ad75317f0696ce771b802d39)
+++ src/sas/sasgui/perspectives/fitting/pagestate.py	(revision e6de6b8b31744ebe652ab6206d60d0d20979c592)
@@ -2,13 +2,13 @@
     Class that holds a fit page state
 """
-#TODO: Refactor code so we don't need to use getattr/setattr
+# TODO: Refactor code so we don't need to use getattr/setattr
 ################################################################################
-#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.
+# 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
+# See the license text in license.txt
 #
-#copyright 2009, University of Tennessee
+# copyright 2009, University of Tennessee
 ################################################################################
 import time
@@ -30,10 +30,7 @@
 from sas.sascalc.dataloader.readers.cansas_reader import Reader as CansasReader
 from sas.sascalc.dataloader.readers.cansas_reader import get_content, write_node
-from sas.sascalc.dataloader.data_info import Data2D
-from sas.sascalc.dataloader.data_info import Collimation
-from sas.sascalc.dataloader.data_info import Detector
-from sas.sascalc.dataloader.data_info import Process
-from sas.sascalc.dataloader.data_info import Aperture
-#Information to read/write state as xml
+from sas.sascalc.dataloader.data_info import Data2D, Collimation, Detector
+from sas.sascalc.dataloader.data_info import Process, Aperture
+# Information to read/write state as xml
 FITTING_NODE_NAME = 'fitting_plug_in'
 CANSAS_NS = "cansas1d/1.0"
@@ -123,5 +120,4 @@
             try:
                 return node.get(item[0]).strip() == "True"
-
             except:
                 return None
@@ -146,18 +142,19 @@
         """
         self.file = None
-        #Time of state creation
+        # Time of state creation
         self.timestamp = time.time()
-        ## Data member to store the dispersion object created
+        # Data member to store the dispersion object created
         self._disp_obj_dict = {}
-        #------------------------
-        #Data used for fitting
+        # ------------------------
+        # Data used for fitting
         self.data = data
         # model data
         self.theory_data = None
-        #Is 2D
+        # Is 2D
         self.is_2D = False
         self.images = None
 
-        #save additional information on data that dataloader.reader does not read
+        # save additional information on data that dataloader.reader
+        # does not read
         self.is_data = None
         self.data_name = ""
@@ -172,5 +169,5 @@
             self.data_group_id = self.data.group_id
 
-        ## reset True change the state of exsiting button
+        # reset True change the state of existing button
         self.reset = False
 
@@ -180,14 +177,14 @@
         self.model = model
         self.m_name = None
-        #list of process done to model
+        # list of process done to model
         self.process = []
-        #fit page manager
+        # fit page manager
         self.manager = None
-        #Store the parent of this panel parent
+        # 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
+        # page name
         self.page_name = ""
         # Contains link between model, all its parameters, and panel organization
@@ -196,14 +193,14 @@
         self.str_parameters = []
         # Contains list of parameters that cannot be fitted and reference to
-        #panel objects
+        # panel objects
         self.fixed_param = []
         # Contains list of parameters with dispersity and reference to
-        #panel objects
+        # panel objects
         self.fittable_param = []
-        ## orientation parameters
+        # orientation parameters
         self.orientation_params = []
-        ## orientation parameters for gaussian dispersity
+        # orientation parameters for gaussian dispersity
         self.orientation_params_disp = []
-        ## smearer info
+        # smearer info
         self.smearer = None
         self.smear_type = None
@@ -214,5 +211,5 @@
         self.dxl = None
         self.dxw = None
-        #list of dispersion parameters
+        # list of dispersion parameters
         self.disp_list = []
         if self.model is not None:
@@ -223,27 +220,27 @@
         self.weights = {}
 
-        #contains link between a model and selected parameters to fit
+        # contains link between a model and selected parameters to fit
         self.param_toFit = []
-        ##dictionary of model type and model class
+        # dictionary of model type and model class
         self.model_list_box = None
-        ## save the state of the context menu
+        # save the state of the context menu
         self.saved_states = {}
-        ## save selection of combobox
+        # save selection of combobox
         self.formfactorcombobox = None
         self.categorycombobox = None
         self.structurecombobox = None
 
-        ## radio box to select type of model
-        #self.shape_rbutton = False
-        #self.shape_indep_rbutton = False
-        #self.struct_rbutton = False
-        #self.plugin_rbutton = False
-        ## the indice of the current selection
+        # 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
+        # Qrange
+        # Q range
         self.qmin = 0.001
         self.qmax = 0.1
-        #reset data range
+        # reset data range
         self.qmax_x = None
         self.qmin_x = None
@@ -253,5 +250,5 @@
         self.multi_factor = None
         self.magnetic_on = False
-        ## enable smearering state
+        # enable smearering state
         self.enable_smearer = False
         self.disable_smearer = True
@@ -263,11 +260,11 @@
         self.dI_sqrdata = False
         self.dI_idata = False
-        ## disperity selection
+        # disperity selection
         self.enable_disp = False
         self.disable_disp = True
 
-        ## state of selected all check button
+        # state of selected all check button
         self.cb1 = False
-        ## store value of chisqr
+        # store value of chisqr
         self.tcChi = None
 
@@ -293,8 +290,8 @@
         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.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
@@ -381,10 +378,12 @@
 
         rep += "State created: %s\n" % time_str
-        rep += "State form factor combobox selection: %s\n" % self.formfactorcombobox
-        rep += "State structure factor combobox selection: %s\n" % self.structurecombobox
+        rep += "State form factor combobox selection: %s\n" % \
+               self.formfactorcombobox
+        rep += "State structure factor combobox selection: %s\n" % \
+               self.structurecombobox
         rep += "is data : %s\n" % self.is_data
         rep += "data's name : %s\n" % self.data_name
         rep += "data's id : %s\n" % self.data_id
-        if self.model != None:
+        if self.model is not None:
             m_name = self.model.__class__.__name__
             if m_name == 'Model':
@@ -397,6 +396,6 @@
         rep += "model type (Category) selected: %s\n" % self.categorycombobox
         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 += "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)
@@ -414,8 +413,8 @@
 
         rep += "2D enable : %s\n" % str(self.enable2D)
-        rep += "All parameters checkbox selected: %s\n" % (self.cb1)
+        rep += "All parameters checkbox selected: %s\n" % str(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 += "Smear type : %s\n" % str(self.smear_type)
         rep += "dq_l  : %s\n" % self.dq_l
         rep += "dq_r  : %s\n" % self.dq_r
@@ -434,8 +433,8 @@
             if not self.is_2D:
                 for item in self.parameters:
-                    if not item in self.orientation_params:
+                    if item not in self.orientation_params:
                         temp_parameters.append(item)
                 for item in self.fittable_param:
-                    if not item in self.orientation_params_disp:
+                    if item not in self.orientation_params_disp:
                         temp_fittable_param.append(item)
             else:
@@ -443,9 +442,12 @@
                 temp_fittable_param = self.fittable_param
 
-            rep += "number parameters(self.parameters): %s\n" % len(temp_parameters)
+            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 += "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 += "number fittable_param(self.fittable_param): %s\n" % \
+                   len(temp_fittable_param)
             rep = self._repr_helper(list=temp_fittable_param, rep=rep)
         return rep
@@ -551,5 +553,6 @@
                     paramval_string += CENTRE % param + "\n"
 
-        text_string = "\n\n\n%s\n\n%s\n%s\n%s\n\n%s" % (title, file, q_name, chi2, paramval)
+        text_string = "\n\n\n%s\n\n%s\n%s\n%s\n\n%s" % \
+                      (title, file, q_name, chi2, paramval)
 
         title_name = self._check_html_format(title_name)
@@ -632,16 +635,17 @@
             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.
+    def toXML(self, file="fitting_state.fitv", doc=None,
+              entry_node=None, batch_fit_state=None):
+        """
+        Writes the state of the fit 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.
+        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]
-
+        :param entry_node: XML node within the XML document at which we
+                           will append the data [optional]
         """
         from xml.dom.minidom import getDOMImplementation
@@ -691,4 +695,5 @@
         element.setAttributeNode(attr)
         top_element.appendChild(element)
+
         # Inputs
         inputs = newdoc.createElement("Attributes")
@@ -741,6 +746,56 @@
         for item in LIST_OF_STATE_PARAMETERS:
             element = newdoc.createElement(item[0])
-            self._toXML_helper(thelist=getattr(self, item[1]), element=element, newdoc=newdoc)
+            self._toXML_helper(thelist=getattr(self, item[1]),
+                               element=element, newdoc=newdoc)
             inputs.appendChild(element)
+
+        # Combined and Simultaneous Fit Parameters
+        if batch_fit_state is not None:
+            batch_combo = newdoc.createElement('simultaneous_fit')
+            top_element.appendChild(batch_combo)
+
+            # Simultaneous Fit Number For Linking Later
+            element = newdoc.createElement('sim_fit_number')
+            element.setAttribute('fit_number', str(batch_fit_state.fit_page_no))
+            batch_combo.appendChild(element)
+
+            # Save constraints
+            constraints = newdoc.createElement('constraints')
+            batch_combo.appendChild(constraints)
+            for constraint in batch_fit_state.constraints_list:
+                if constraint.model_cbox.GetValue() != "":
+                    # model_cbox, param_cbox, egal_txt, constraint, btRemove, sizer
+                    doc_cons = newdoc.createElement('constraint')
+                    doc_cons.setAttribute('model_cbox',
+                                          str(constraint.model_cbox.GetValue()))
+                    doc_cons.setAttribute('param_cbox',
+                                          str(constraint.param_cbox.GetValue()))
+                    doc_cons.setAttribute('egal_txt',
+                                          str(constraint.egal_txt.GetLabel()))
+                    doc_cons.setAttribute('constraint',
+                                          str(constraint.constraint.GetValue()))
+                    constraints.appendChild(doc_cons)
+
+            # Save all models
+            models = newdoc.createElement('model_list')
+            batch_combo.appendChild(models)
+            for model in batch_fit_state.model_list:
+                doc_model = newdoc.createElement('model_list_item')
+                doc_model.setAttribute('checked', str(model[0].GetValue()))
+                keys = model[1].keys()
+                doc_model.setAttribute('name', str(keys[0]))
+                values = model[1].get(keys[0])
+                doc_model.setAttribute('fit_number', str(model[2]))
+                doc_model.setAttribute('fit_page_source', str(model[3]))
+                doc_model.setAttribute('model_name', str(values.model.id))
+                models.appendChild(doc_model)
+
+            # Select All Checkbox
+            element = newdoc.createElement('select_all')
+            if batch_fit_state.select_all:
+                element.setAttribute('checked', 'True')
+            else:
+                element.setAttribute('checked', 'False')
+            batch_combo.appendChild(element)
 
         # Save the file
@@ -809,5 +864,4 @@
         :param file: .fitv file
         :param node: node of a XML document to read from
-
         """
         if file is not None:
@@ -816,5 +870,5 @@
             raise RuntimeError, msg
 
-        if node.get('version')and node.get('version') == '1.0':
+        if node.get('version') and node.get('version') == '1.0':
 
             # Get file name
@@ -862,6 +916,6 @@
                                 value = cls.type
                             except Exception:
-                                logging.error("unable to load distribution %r for %s"
-                                              % (value, parameter))
+                                base = "unable to load distribution %r for %s"
+                                logging.error(base % (value, parameter))
                                 continue
                         _disp_obj_dict = getattr(self, varname)
@@ -960,4 +1014,6 @@
         self.cansas = cansas
         self.state = None
+        # batch fitting params for saving
+        self.batchfit_params = []
 
     def get_state(self):
@@ -984,11 +1040,13 @@
             raise RuntimeError, "The cansas writer expects a Data2D instance"
 
+        title = "cansas1d/%s" % self.version
+        title += "http://svn.smallangles.net/svn/canSAS/1dwg/trunk/cansas1d.xsd"
         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)
+        main_node.setAttribute("xmlns:xsi",
+                               "http://www.w3.org/2001/XMLSchema-instance")
+        main_node.setAttribute("xsi:schemaLocation", title)
 
         doc.appendChild(main_node)
@@ -999,8 +1057,10 @@
         write_node(doc, entry_node, "Title", datainfo.title)
         if datainfo is not None:
-            write_node(doc, entry_node, "data_class", datainfo.__class__.__name__)
+            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:
+            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)
@@ -1219,5 +1279,4 @@
 
         :param entry: XML node to read from
-
         :return: PageState object
         """
@@ -1228,5 +1287,5 @@
             nodes = entry.xpath('ns:%s' % FITTING_NODE_NAME,
                                 namespaces={'ns': CANSAS_NS})
-            if nodes != []:
+            if nodes:
                 # Create an empty state
                 state = PageState()
@@ -1238,4 +1297,44 @@
 
         return state
+
+    def _parse_simfit_state(self, entry):
+        """
+        Parses the saved data for a simultaneous fit
+        :param entry: XML object to read from
+        :return: XML object for a simultaneous fit or None
+        """
+        nodes = entry.xpath('ns:%s' % FITTING_NODE_NAME,
+                            namespaces={'ns': CANSAS_NS})
+        if nodes:
+            simfitstate = nodes[0].xpath('ns:simultaneous_fit',
+                                         namespaces={'ns': CANSAS_NS})
+            if simfitstate:
+                from simfitpage import SimFitPageState
+                sim_fit_state = SimFitPageState()
+                simfitstate_0 = simfitstate[0]
+                all = simfitstate_0.xpath('ns:select_all',
+                                        namespaces={'ns': CANSAS_NS})
+                atts = all[0].attrib
+                checked = atts.get('checked')
+                sim_fit_state.select_all = bool(checked)
+                model_list = simfitstate_0.xpath('ns:model_list',
+                                               namespaces={'ns': CANSAS_NS})
+                model_list_items = model_list[0].xpath('ns:model_list_item',
+                                                   namespaces={'ns': CANSAS_NS})
+                for model in model_list_items:
+                    attrs = model.attrib
+                    sim_fit_state.model_list.append(attrs)
+
+                constraints = simfitstate_0.xpath('ns:constraints',
+                                                namespaces={'ns': CANSAS_NS})
+                constraint_list = constraints[0].xpath('ns:constraint',
+                                               namespaces={'ns': CANSAS_NS})
+                for constraint in constraint_list:
+                    attrs = constraint.attrib
+                    sim_fit_state.constraints_list.append(attrs)
+
+                return sim_fit_state
+            else:
+                return None
 
     def _parse_save_state_entry(self, dom):
@@ -1298,5 +1397,6 @@
                         data_info.notes.append(note_value)
             except Exception:
-                err_mess = "cansas_reader.read: error processing entry notes\n  %s" % sys.exc_value
+                err_mess = "cansas_reader.read: error processing entry notes\n"
+                err_mess += "  %s" % sys.exc_value
                 self.errors.append(err_mess)
                 logging.error(err_mess)
@@ -1308,9 +1408,13 @@
 
         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})
+        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:
@@ -1320,12 +1424,16 @@
                         data_info.sample.details.append(detail_value)
             except Exception:
-                err_mess = "cansas_reader.read: error processing sample details\n  %s" % sys.exc_value
+                err_mess = "cansas_reader.read: error processing entry notes\n"
+                err_mess += "  %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)
+        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)
@@ -1461,5 +1569,6 @@
                         process.term.append(term_attr)
                 except:
-                    err_mess = "cansas_reader.read: error processing process term\n  %s" % sys.exc_value
+                    err_mess = "cansas_reader.read: error processing "
+                    err_mess += "entry notes\n  %s" % sys.exc_value
                     self.errors.append(err_mess)
                     logging.error(err_mess)
@@ -1476,5 +1585,6 @@
         nodes = dom.xpath('ns:SASdata', namespaces={'ns': CANSAS_NS})
         if len(nodes) > 1:
-            raise RuntimeError, "CanSAS reader is not compatible with multiple SASdata entries"
+            raise RuntimeError, "CanSAS reader is not compatible with" + \
+                                " multiple SASdata entries"
 
         for entry in nodes:
@@ -1488,5 +1598,6 @@
                 value_list = []
                 if field is not None:
-                    value_list = [parse_entry_helper(node, item) for node in field]
+                    value_list = \
+                        [parse_entry_helper(node, item) for node in field]
                 if len(value_list) < 2:
                     setattr(data_info, item[0], None)
@@ -1498,17 +1609,15 @@
     def _read_cansas(self, path):
         """
-        Load data and P(r) information from a CanSAS XML file.
+        Load data and fitting 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 = []
+        simfitstate = None
         basename = os.path.basename(path)
         root, extension = os.path.splitext(basename)
@@ -1516,16 +1625,13 @@
         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':
-
+                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
+                    # Specifying the namespace will take care of the file
+                    # format version
                     root = tree.getroot()
                     entry_list = root.xpath('ns:SASentry',
                                             namespaces={'ns': CANSAS_NS})
+                    name_map = {}
                     for entry in entry_list:
                         try:
@@ -1535,10 +1641,11 @@
                         fitstate = self._parse_state(entry)
 
-                        #state could be None when .svs file is loaded
-                        #in this case, skip appending to output
-                        if fitstate != None:
+                        # state could be None when .svs file is loaded
+                        # in this case, skip appending to output
+                        if fitstate is not None:
                             sas_entry.meta_data['fitstate'] = fitstate
                             sas_entry.filename = fitstate.file
                             output.append(sas_entry)
+
             else:
                 self.call_back(format=ext)
@@ -1580,8 +1687,12 @@
                         name = original_fname
                     state.data.group_id = name
-                    #store state in fitting
+                    # store state in fitting
                     self.call_back(state=state,
                                    datainfo=output[ind], format=ext)
                     self.state = state
+                simfitstate = self._parse_simfit_state(entry)
+                if simfitstate is not None:
+                    self.call_back(state=simfitstate)
+
                 return output
         except:
@@ -1599,5 +1710,5 @@
         """
         # Sanity check
-        if self.cansas == True:
+        if self.cansas:
             # Add fitting information to the XML document
             doc = self.write_toXML(datainfo, fitstate)
@@ -1611,5 +1722,5 @@
         fd.close()
 
-    def write_toXML(self, datainfo=None, state=None):
+    def write_toXML(self, datainfo=None, state=None, batchfit=None):
         """
         Write toXML, a helper for write(),
@@ -1619,108 +1730,25 @@
         """
 
-        if state.data is None:
-            data = sas.sascalc.dataloader.data_info.Data1D(x=[], y=[])
+        self.batchfit_params = batchfit
+        if state.data is None or not state.data.is_data:
             return None
-        elif not state.data.is_data:
-            return None
+        # make sure title and data run are filled.
+        if state.data.title is None or state.data.title == '':
+            state.data.title = state.data.name
+        if state.data.run_name is None or state.data.run_name == {}:
+            state.data.run = [str(state.data.name)]
+            state.data.run_name[0] = state.data.name
+
+        if issubclass(state.data.__class__,
+                      sas.sascalc.dataloader.data_info.Data1D):
+            data = state.data
+            doc, sasentry = self._to_xml_doc(data)
         else:
-            #make sure title and data run is filled up.
-            if state.data.title == None or state.data.title == '':
-                state.data.title = state.data.name
-            if state.data.run_name == None or state.data.run_name == {}:
-                state.data.run = [str(state.data.name)]
-                state.data.run_name[0] = state.data.name
-
-            if issubclass(state.data.__class__,
-                          sas.sascalc.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)
+            data = state.data
+            doc, sasentry = self._data2d_to_xml_doc(data)
 
         if state is not None:
-            doc = state.toXML(doc=doc, file=data.filename, entry_node=sasentry)
+            doc = state.toXML(doc=doc, file=data.filename, entry_node=sasentry,
+                              batch_fit_state=self.batchfit_params)
 
         return doc
-
-# Simple html report templet
-HEADER = "<html>\n"
-HEADER += "<head>\n"
-HEADER += "<meta http-equiv=Content-Type content='text/html; "
-HEADER += "charset=windows-1252'> \n"
-HEADER += "<meta name=Generator >\n"
-HEADER += "</head>\n"
-HEADER += "<body lang=EN-US>\n"
-HEADER += "<div class=WordSection1>\n"
-HEADER += "<p class=MsoNormal><b><span ><center><font size='4' >"
-HEADER += "%s</font></center></span></center></b></p>"
-HEADER += "<p class=MsoNormal>&nbsp;</p>"
-PARA = "<p class=MsoNormal><font size='4' > %s \n"
-PARA += "</font></p>"
-CENTRE = "<p class=MsoNormal><center><font size='4' > %s \n"
-CENTRE += "</font></center></p>"
-FEET_1 = \
-"""
-<p class=MsoNormal>&nbsp;</p>
-<br>
-<p class=MsoNormal><b><span ><center> <font size='4' > Graph
-</font></span></center></b></p>
-<p class=MsoNormal>&nbsp;</p>
-<center>
-<br><font size='4' >Model Computation</font>
-<br><font size='4' >Data: "%s"</font><br>
-"""
-FEET_2 = \
-"""
-<img src="%s" >
-</img>
-"""
-FEET_3 = \
-"""
-</center>
-</div>
-</body>
-</html>
-"""
-ELINE = "<p class=MsoNormal>&nbsp;</p>"
-
-if __name__ == "__main__":
-    state = PageState(parent=None)
-    #state.toXML()
-    """
-
-    file = open("test_state", "w")
-    pickle.dump(state, file)
-    print pickle.dumps(state)
-    state.data_name = "hello---->"
-    pickle.dump(state, file)
-    file = open("test_state", "r")
-    new_state= pickle.load(file)
-    print "new state", new_state
-    new_state= pickle.load(file)
-    print "new state", new_state
-    #print "state", state
-    """
-    import bsddb
-    import pickle
-    db = bsddb.btopen('file_state.db', 'c')
-    val = (pickle.dumps(state), "hello", "hi")
-    db['state1'] = pickle.dumps(val)
-    print pickle.loads(db['state1'])
-    state.data_name = "hello---->22"
-    db['state2'] = pickle.dumps(state)
-    state.data_name = "hello---->2"
-    db['state3'] = pickle.dumps(state)
-    del db['state3']
-    state.data_name = "hello---->3"
-    db['state4'] = pickle.dumps(state)
-    new_state = pickle.loads(db['state1'])
-    #print db.last()
-    db.set_location('state2')
-    state.data_name = "hello---->5"
-    db['aastate5'] = pickle.dumps(state)
-    db.keys().sort()
-    print pickle.loads(db['state2'])
-
-    db.close()
Index: src/sas/sasgui/perspectives/fitting/simfitpage.py
===================================================================
--- src/sas/sasgui/perspectives/fitting/simfitpage.py	(revision 4cafdffe5bed3072f069cb0774c035a691b11443)
+++ src/sas/sasgui/perspectives/fitting/simfitpage.py	(revision 998ca90ad87500d7d5fb3f4eb13105752a2f2952)
@@ -9,11 +9,10 @@
 from wx.lib.scrolledpanel import ScrolledPanel
 
-from sas.sasgui.guiframe.events import StatusEvent
+from sas.sasgui.guiframe.events import StatusEvent, PanelOnFocusEvent
 from sas.sasgui.guiframe.panel_base import PanelBase
-from sas.sasgui.guiframe.events import PanelOnFocusEvent
 from sas.sasgui.guiframe.utils import IdList
 from sas.sasgui.guiframe.documentation_window import DocumentationWindow
 
-#Control panel width 
+# Control panel width
 if sys.platform.count("darwin") == 0:
     PANEL_WID = 420
@@ -29,4 +28,5 @@
         'model_cbox param_cbox egal_txt constraint btRemove sizer')
 
+
 def get_fittableParam(model):
     """
@@ -47,4 +47,5 @@
     return fittable_param
 
+
 class SimultaneousFitPage(ScrolledPanel, PanelBase):
     """
@@ -53,7 +54,7 @@
     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
+    # Internal name for the AUI manager
+    window_name = "Simultaneous Fit Page"
+    # Title to appear on top of the window
     window_caption = "Simultaneous Fit Page"
     ID_DOC = wx.NewId()
@@ -74,28 +75,29 @@
         self._ids = iter(self._id_pool)
         self.SetupScrolling()
-        ##Font size
+        # Font size
         self.SetWindowVariant(variant=FONT_VARIANT)
         self.uid = wx.NewId()
         self.parent = parent
         self.batch_on = batch_on
-        ## store page_finder
+        # store page_finder
         self.page_finder = page_finder
-        ## list containing info to set constraint
-        ## look like self.constraint_dict[page_id]= page
+        # list containing info to set constraint
+        # look like self.constraint_dict[page_id]= page
         self.constraint_dict = {}
-        ## item list
-        ## self.constraints_list=[combobox1, combobox2,=,textcrtl, button ]
+        # item list
+        # self.constraints_list=[combobox1, combobox2,=,textcrtl, button ]
         self.constraints_list = []
-        ## list of current model
+        # list of current model
         self.model_list = []
-        ## selected mdoel to fit
-        self.model_toFit = []
-        ## Control the fit state
+        # selected model to fit
+        self.model_to_fit = []
+        # Control the fit state
         self.fit_started = False
-        ## number of constraint
+        # number of constraint
         self.nb_constraint = 0
+        self.state = SimFitPageState()
         self.model_cbox_left = None
         self.model_cbox_right = None
-        ## draw page
+        # draw page
         self.define_page_structure()
         self.draw_page()
@@ -107,16 +109,37 @@
         """
         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)
+        self.data_selection_sizer = wx.BoxSizer(wx.VERTICAL)
+        self.constraints_sizer = wx.BoxSizer(wx.VERTICAL)
+        self.run_fit_sizer = wx.BoxSizer(wx.VERTICAL)
+
+        self.data_selection_sizer.SetMinSize((PANEL_WID, -1))
+        self.constraints_sizer.SetMinSize((PANEL_WID, -1))
+        self.run_fit_sizer.SetMinSize((PANEL_WID, -1))
+        self.vbox.Add(self.data_selection_sizer)
+        self.vbox.Add(self.constraints_sizer)
+        self.vbox.Add(self.run_fit_sizer)
         self.SetSizer(self.vbox)
         self.Centre()
+
+    def set_state(self):
+        """
+        Define a set of state parameters for saving simultaneous fits.
+        """
+        self._set_constraint()
+        self.state.fit_page_no = self.uid
+        self.state.select_all = self.cb1.GetValue()
+        self.state.model_list = self.model_list
+        self.state.model_to_fit = self.model_to_fit
+        self.state.no_constraint = self.nb_constraint
+        self.state.constraint_dict = self.constraint_dict
+        self.state.constraints_list = self.constraints_list
+        return self.get_state()
+
+    def get_state(self):
+        """
+        Return the state of the current page
+        :return: self.state
+        """
+        return self.state
 
     def draw_page(self):
@@ -131,5 +154,5 @@
         # create blank list of constraints
         self.model_list = []
-        self.model_toFit = []
+        self.model_to_fit = []
         self.constraints_list = []
         self.constraint_dict = {}
@@ -144,6 +167,6 @@
 
         #-------------------------------------------------------
-        ## setup sizer1 (which fitpages to include)
-        self.sizer1.Clear(True)
+        # setup sizer1 (which fitpages to include)
+        self.data_selection_sizer.Clear(True)
         box_description = wx.StaticBox(self, wx.ID_ANY, "Fit Combinations")
         boxsizer1 = wx.StaticBoxSizer(box_description, wx.VERTICAL)
@@ -151,8 +174,8 @@
         sizer_couples = wx.GridBagSizer(5, 5)
 
-        #This if statement should be obsolete and can be removed in version 4
-        #Leave it here for now as no time to thoroughly test.  However if no
-        #fit page is found the menu item that calls this page is inactive 
-        # Nov. 22 2015  --PDB
+        # The wx GUI has a flag to enable a menu item, but can still be
+        # reached via scripting. There is no guearantee future GUI
+        # implementations force this check, either.
+        # IMHO, this if statement should stay -- JRK 2016-OCT-05
         if len(self.page_finder) == 0:
             msg = " No fit combinations are found! \n\n"
@@ -161,5 +184,5 @@
             sizer_title.Add(wx.StaticText(self, wx.ID_ANY, msg))
         else:
-            ## store model
+            # store model
             self._store_model()
 
@@ -171,27 +194,30 @@
                 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 names
+                wx.TOP | wx.BOTTOM | wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE,
+                border=5)
+
+            # draw list of model and data names
             self._fill_sizer_model_list(sizer_couples)
 
         boxsizer1.Add(sizer_title, flag=wx.TOP | wx.BOTTOM, border=5)
         boxsizer1.Add(sizer_couples, 1, flag=wx.TOP | wx.BOTTOM, border=5)
-        self.sizer1.Add(boxsizer1, 1, wx.EXPAND | wx.ALL, 10)
-#        self.sizer1.Layout()
+        self.data_selection_sizer.Add(boxsizer1, 1, wx.EXPAND | wx.ALL, 10)
+        # self.sizer1.Layout()
 
         #--------------------------------------------------------
-        ## set up the other 2 sizers: the constraints list and the
-        ## buttons (fit, help etc) sizer at the bottom of the page.
-        ## Note: the if statement should be removed along with the above
-        ## if statement as soon as it can be properly tested.
-        ## Nov. 22 2015  --PDB
+        # set up the other 2 sizers: the constraints list and the
+        # buttons (fit, help etc) sizer at the bottom of the page.
+        # Note: the if statement should be removed along with the above
+        # if statement as soon as it can be properly tested.
+        # Nov. 22 2015  --PDB
+        # As above, this page can be accessed through other means than the
+        # base SasView GUI.
+        # Oct. 5, 2016 --JRK
         if len(self.page_finder) > 0:
-            ## draw the sizer containing constraint info
+            # draw the sizer containing constraint info
             if not self.batch_on:
                 self._fill_sizer_constraint()
-            ## draw fit button sizer
+            # draw fit button sizer
             self._fill_sizer_fit()
-
 
     def _fill_sizer_model_list(self, sizer):
@@ -201,5 +227,4 @@
         ix = 0
         iy = 0
-        list = []
         sizer.Clear(True)
 
@@ -209,5 +234,5 @@
         new_name.SetForegroundColour(wx.WHITE)
         sizer.Add(new_name, (iy, ix), (1, 1),
-                            wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
+                  wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
         ix += 2
         model_type = wx.StaticText(self, wx.ID_ANY, '  Model ')
@@ -215,5 +240,5 @@
         model_type.SetForegroundColour(wx.WHITE)
         sizer.Add(model_type, (iy, ix), (1, 1),
-                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+                  wx.EXPAND | wx.ADJUST_MINSIZE, 0)
         ix += 1
         data_used = wx.StaticText(self, wx.ID_ANY, '  Data ')
@@ -221,5 +246,5 @@
         data_used.SetForegroundColour(wx.WHITE)
         sizer.Add(data_used, (iy, ix), (1, 1),
-                            wx.EXPAND | wx.ADJUST_MINSIZE, 0)
+                  wx.EXPAND | wx.ADJUST_MINSIZE, 0)
         ix += 1
         tab_used = wx.StaticText(self, wx.ID_ANY, '  FitPage ')
@@ -302,11 +327,11 @@
         wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
 
-        self.sizer2.Clear(True)
+        self.constraints_sizer.Clear(True)
         if self.batch_on:
-            if self.sizer2.IsShown():
-                self.sizer2.Show(False)
+            if self.constraints_sizer.IsShown():
+                self.constraints_sizer.Show(False)
             return
         box_description = wx.StaticBox(self, wx.ID_ANY, "Fit Constraints")
-        boxsizer1 = wx.StaticBoxSizer(box_description, wx.VERTICAL)
+        box_sizer1 = wx.StaticBoxSizer(box_description, wx.VERTICAL)
         sizer_title = wx.BoxSizer(wx.HORIZONTAL)
         self.sizer_all_constraints = wx.BoxSizer(wx.HORIZONTAL)
@@ -338,5 +363,5 @@
 
         self.btAdd = wx.Button(self, self.ID_ADD, 'Add')
-        self.btAdd.Bind(wx.EVT_BUTTON, self._onAdd_constraint,
+        self.btAdd.Bind(wx.EVT_BUTTON, self._on_add_constraint,
                         id=self.btAdd.GetId())
         self.btAdd.SetToolTipString("Add another constraint?")
@@ -344,5 +369,5 @@
 
         text_hint = wx.StaticText(self, wx.ID_ANY,
-                                  "Example: [M0][paramter] = M1.parameter")
+                                  "Example: [M0][parameter] = M1.parameter")
         sizer_button.Add(text_hint, 0,
                          wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 10)
@@ -350,13 +375,12 @@
                          wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 10)
 
-        boxsizer1.Add(sizer_title, flag=wx.TOP | wx.BOTTOM, border=10)
-        boxsizer1.Add(self.sizer_all_constraints, flag=wx.TOP | wx.BOTTOM,
-                      border=10)
-        boxsizer1.Add(self.sizer_constraints, flag=wx.TOP | wx.BOTTOM,
-                      border=10)
-        boxsizer1.Add(sizer_button, flag=wx.TOP | wx.BOTTOM, border=10)
-
-        self.sizer2.Add(boxsizer1, 0, wx.EXPAND | wx.ALL, 10)
-
+        box_sizer1.Add(sizer_title, flag=wx.TOP | wx.BOTTOM, border=10)
+        box_sizer1.Add(self.sizer_all_constraints, flag=wx.TOP | wx.BOTTOM,
+                       border=10)
+        box_sizer1.Add(self.sizer_constraints, flag=wx.TOP | wx.BOTTOM,
+                       border=10)
+        box_sizer1.Add(sizer_button, flag=wx.TOP | wx.BOTTOM, border=10)
+
+        self.constraints_sizer.Add(box_sizer1, 0, wx.EXPAND | wx.ALL, 10)
 
     def _fill_sizer_fit(self):
@@ -364,20 +388,20 @@
         Draw fit button
         """
-        self.sizer3.Clear(True)
+        self.run_fit_sizer.Clear(True)
         box_description = wx.StaticBox(self, wx.ID_ANY, "Fit ")
         boxsizer1 = wx.StaticBoxSizer(box_description, wx.VERTICAL)
         sizer_button = wx.BoxSizer(wx.HORIZONTAL)
 
-        #Fit button
+        # Fit button
         self.btFit = wx.Button(self, self.ID_FIT, 'Fit', size=wx.DefaultSize)
-        self.btFit.Bind(wx.EVT_BUTTON, self.onFit, id=self.btFit.GetId())
+        self.btFit.Bind(wx.EVT_BUTTON, self.on_fit, id=self.btFit.GetId())
         self.btFit.SetToolTipString("Perform fit.")
 
-        #General Help button
+        # General Help button
         self.btHelp = wx.Button(self, wx.ID_HELP, 'HELP')
         self.btHelp.SetToolTipString("Simultaneous/Constrained Fitting help.")
-        self.btHelp.Bind(wx.EVT_BUTTON, self._onHelp)
-
-        #hint text on button line
+        self.btHelp.Bind(wx.EVT_BUTTON, self._on_help)
+
+        # hint text on button line
         if self.batch_on:
             text = " Fit in Parallel all Data sets\n"
@@ -393,7 +417,7 @@
 
         boxsizer1.Add(sizer_button, flag=wx.TOP | wx.BOTTOM, border=10)
-        self.sizer3.Add(boxsizer1, 0, wx.EXPAND | wx.ALL, 10)
-
-    def onRemove(self, event):
+        self.run_fit_sizer.Add(boxsizer1, 0, wx.EXPAND | wx.ALL, 10)
+
+    def on_remove(self, event):
         """
         Remove constraint fields
@@ -406,5 +430,5 @@
             return
         wx.CallAfter(self._remove_after, event.GetId())
-        #self._onAdd_constraint(None)
+        # self._onAdd_constraint(None)
 
     def _remove_after(self, id):
@@ -416,9 +440,9 @@
                 self.constraints_list.remove(item)
                 self.nb_constraint -= 1
-                self.sizer2.Layout()
+                self.constraints_sizer.Layout()
                 self.FitInside()
                 break
 
-    def onFit(self, event):
+    def on_fit(self, event):
         """
         signal for fitting
@@ -435,10 +459,10 @@
             flag = (self._manager.sim_page.uid == self.uid)
 
-        ## making sure all parameters content a constraint
+        # making sure all parameters content a constraint
         if not self.batch_on and self.show_constraint.GetValue():
             if not self._set_constraint():
                 return
-        ## model was actually selected from this page to be fit
-        if len(self.model_toFit) >= 1:
+        # model was actually selected from this page to be fit
+        if len(self.model_to_fit) >= 1:
             self.manager._reset_schedule_problem(value=0)
             for item in self.model_list:
@@ -456,4 +480,5 @@
             msg = "Select at least one model check box to fit "
             wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
+        self.set_state()
 
     def _on_fit_complete(self):
@@ -467,6 +492,8 @@
         """
         Attempt to stop the fitting thread
-        """
-        if event != None:
+
+        :param event: Event handler when stop fit is clicked
+        """
+        if event is not None:
             event.Skip()
         self.manager.stop_fit(self.uid)
@@ -485,5 +512,5 @@
         self.btFit.Enable(True)
 
-    def _onHelp(self, event):
+    def _on_help(self, event):
         """
         Bring up the simultaneous Fitting Documentation whenever the HELP
@@ -494,8 +521,8 @@
         versions of Wx (before 2.9) and thus not the release version of
         installers, the help comes up at the top level of the file as
-        webbrowser does not pass anything past the # to the browser when it is
+        web browser does not pass anything past the # to the browser when it is
         running "file:///...."
 
-    :param evt: Triggers on clicking the help button
+    :param event: Triggers on clicking the help button
     """
         _TreeLocation = "user/sasgui/perspectives/fitting/fitting_help.html"
@@ -510,5 +537,4 @@
 
         :param manager: instance of plugin fitting
-
         """
         self.manager = manager
@@ -518,15 +544,15 @@
         check all models names
         """
-        self.model_toFit = []
-        if self.cb1.GetValue() == True:
+        self.model_to_fit = []
+        if self.cb1.GetValue():
             for item in self.model_list:
                 if item[0].IsEnabled():
                     item[0].SetValue(True)
-                    self.model_toFit.append(item)
-
-            ## constraint info
+                    self.model_to_fit.append(item)
+
+            # constraint info
             self._store_model()
             if not self.batch_on:
-                ## display constraint fields
+                # display constraint fields
                 if (self.show_constraint.GetValue() and
                                  len(self.constraints_list) == 0):
@@ -538,5 +564,5 @@
 
             if not self.batch_on:
-                ##constraint info
+                # constraint info
                 self._hide_constraint()
 
@@ -544,21 +570,19 @@
         self.FitInside()
 
-
     def check_model_name(self, event):
         """
         Save information related to checkbox and their states
         """
-        self.model_toFit = []
-        cbox = event.GetEventObject()
+        self.model_to_fit = []
         for item in self.model_list:
-            if item[0].GetValue() == True:
-                self.model_toFit.append(item)
+            if item[0].GetValue():
+                self.model_to_fit.append(item)
             else:
-                if item in self.model_toFit:
-                    self.model_toFit.remove(item)
+                if item in self.model_to_fit:
+                    self.model_to_fit.remove(item)
                     self.cb1.SetValue(False)
 
-        ## display constraint fields
-        if len(self.model_toFit) >= 1:
+        # display constraint fields
+        if len(self.model_to_fit) >= 1:
             self._store_model()
             if not self.batch_on and self.show_constraint.GetValue() and\
@@ -567,11 +591,11 @@
                 self._show_constraint()
 
-        elif len(self.model_toFit) < 1:
-            ##constraint info
+        elif len(self.model_to_fit) < 1:
+            # constraint info
             self._hide_constraint()
 
         self._update_easy_setup_cb()
-        ## set the value of the main check button
-        if len(self.model_list) == len(self.model_toFit):
+        # set the value of the main check button
+        if len(self.model_list) == len(self.model_to_fit):
             self.cb1.SetValue(True)
             self.FitInside()
@@ -585,8 +609,8 @@
         Update easy setup combobox on selecting a model
         """
-        if self.model_cbox_left == None or self.model_cbox_right == None:
-            return
-
-        models = [(item[3].name, item[3]) for item in self.model_toFit]
+        if self.model_cbox_left is None or self.model_cbox_right is None:
+            return
+
+        models = [(item[3].name, item[3]) for item in self.model_to_fit]
         setComboBoxItems(self.model_cbox_left, models)
         setComboBoxItems(self.model_cbox_right, models)
@@ -595,5 +619,5 @@
         if self.model_cbox_left.GetSelection() == wx.NOT_FOUND:
             self.model_cbox_left.SetSelection(0)
-        self.sizer2.Layout()
+        self.constraints_sizer.Layout()
 
     def _store_model(self):
@@ -601,7 +625,7 @@
          Store selected model
         """
-        if len(self.model_toFit) < 1:
-            return
-        for item in self.model_toFit:
+        if len(self.model_to_fit) < 1:
+            return
+        for item in self.model_to_fit:
             model = item[3]
             page_id = item[2]
@@ -612,8 +636,8 @@
         Show fields to add constraint
         """
-        if len(self.model_toFit) < 1:
+        if len(self.model_to_fit) < 1:
             msg = "Select at least 1 model to add constraint "
             wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
-            ## hide button
+            # hide button
             self._hide_constraint()
             return
@@ -632,5 +656,5 @@
         """
         box_description = wx.StaticBox(self, wx.ID_ANY, "Easy Setup ")
-        boxsizer = wx.StaticBoxSizer(box_description, wx.HORIZONTAL)
+        box_sizer = wx.StaticBoxSizer(box_description, wx.HORIZONTAL)
         sizer_constraint = wx.BoxSizer(wx.HORIZONTAL)
         self.model_cbox_left = wx.ComboBox(self, wx.ID_ANY, style=wx.CB_READONLY)
@@ -650,24 +674,24 @@
 
         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
+            # check if all parameters have been selected for constraint
+            # then do not allow add constraint on parameters
             self.model_cbox_left.Append(str(model.name), model)
         self.model_cbox_left.Select(0)
         for id, model in self.constraint_dict.iteritems():
-            ## check if all parameters have been selected for constraint
-            ## then do not allow add constraint on parameters
+            # check if all parameters have been selected for constraint
+            # then do not allow add constraint on parameters
             self.model_cbox_right.Append(str(model.name), model)
-        boxsizer.Add(self.model_cbox_left,
+        box_sizer.Add(self.model_cbox_left,
                              flag=wx.RIGHT | wx.EXPAND, border=10)
-        #boxsizer.Add(wx.StaticText(self, wx.ID_ANY, ".parameters"),
+        # box_sizer.Add(wx.StaticText(self, wx.ID_ANY, ".parameters"),
         #                     flag=wx.RIGHT | wx.EXPAND, border=5)
-        boxsizer.Add(egal_txt, flag=wx.RIGHT | wx.EXPAND, border=5)
-        boxsizer.Add(self.model_cbox_right,
+        box_sizer.Add(egal_txt, flag=wx.RIGHT | wx.EXPAND, border=5)
+        box_sizer.Add(self.model_cbox_right,
                              flag=wx.RIGHT | wx.EXPAND, border=10)
-        #boxsizer.Add(wx.StaticText(self, wx.ID_ANY, ".parameters"),
+        # box_sizer.Add(wx.StaticText(self, wx.ID_ANY, ".parameters"),
         #                     flag=wx.RIGHT | wx.EXPAND, border=5)
-        boxsizer.Add((20, -1))
-        boxsizer.Add(self.set_button, flag=wx.RIGHT | wx.EXPAND, border=5)
-        sizer_constraint.Add(boxsizer, flag=wx.RIGHT | wx.EXPAND, border=5)
+        box_sizer.Add((20, -1))
+        box_sizer.Add(self.set_button, flag=wx.RIGHT | wx.EXPAND, border=5)
+        sizer_constraint.Add(box_sizer, flag=wx.RIGHT | wx.EXPAND, border=5)
         self.sizer_all_constraints.Insert(before=0,
                              item=sizer_constraint,
@@ -699,17 +723,17 @@
             return
         param_list = []
-        param_listB = []
+        param_list_b = []
         selection = self.model_cbox_left.GetCurrentSelection()
         model_left = self.model_cbox_left.GetValue()
         model = self.model_cbox_left.GetClientData(selection)
-        selectionB = self.model_cbox_right.GetCurrentSelection()
+        selection_b = self.model_cbox_right.GetCurrentSelection()
         model_right = self.model_cbox_right.GetValue()
-        modelB = self.model_cbox_right.GetClientData(selectionB)
+        model_b = self.model_cbox_right.GetClientData(selection_b)
         for id, dic_model in self.constraint_dict.iteritems():
             if model == dic_model:
                 param_list = self.page_finder[id].get_param2fit()
-            if modelB == dic_model:
-                param_listB = self.page_finder[id].get_param2fit()
-            if len(param_list) > 0 and len(param_listB) > 0:
+            if model_b == dic_model:
+                param_list_b = self.page_finder[id].get_param2fit()
+            if len(param_list) > 0 and len(param_list_b) > 0:
                 break
         num_cbox = 0
@@ -717,5 +741,5 @@
         for param in param_list:
             num_cbox += 1
-            if param in param_listB:
+            if param in param_list_b:
                 item = self.constraints_list[-1]
                 item.model_cbox.SetStringSelection(model_left)
@@ -744,4 +768,5 @@
         """
         Show constraint fields
+        :param dict: dictionary mapping constraint values
         """
         self.btAdd.Show(True)
@@ -750,5 +775,5 @@
             for id, model in self.constraint_dict.iteritems():
                 nb_fit_param += len(self.page_finder[id].get_param2fit())
-            ##Don't add anymore
+            # Don't add anymore
             if len(self.constraints_list) == nb_fit_param:
                 msg = "Cannot add another constraint. Maximum of number "
@@ -756,11 +781,11 @@
                 wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
                 self.sizer_constraints.Layout()
-                self.sizer2.Layout()
+                self.constraints_sizer.Layout()
                 return
-        if len(self.model_toFit) < 1:
+        if len(self.model_to_fit) < 1:
             msg = "Select at least 1 model to add constraint "
             wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
             self.sizer_constraints.Layout()
-            self.sizer2.Layout()
+            self.constraints_sizer.Layout()
             return
 
@@ -771,6 +796,6 @@
         model_cbox.Clear()
         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
+            # check if all parameters have been selected for constraint
+            # then do not allow add constraint on parameters
             model_cbox.Append(str(model.name), model)
         wx.EVT_COMBOBOX(model_cbox, wx.ID_ANY, self._on_select_model)
@@ -789,9 +814,9 @@
         # Remove button
         #btRemove = wx.Button(self, self.ID_REMOVE, 'Remove')
-        btRemove = wx.Button(self, self._ids.next(), 'Remove')
-        btRemove.Bind(wx.EVT_BUTTON, self.onRemove,
-                      id=btRemove.GetId())
-        btRemove.SetToolTipString("Remove constraint.")
-        btRemove.Hide()
+        bt_remove = wx.Button(self, self._ids.next(), 'Remove')
+        bt_remove.Bind(wx.EVT_BUTTON, self.on_remove,
+                      id=bt_remove.GetId())
+        bt_remove.SetToolTipString("Remove constraint.")
+        bt_remove.Hide()
 
         # Hid the add button, if it exists
@@ -804,5 +829,5 @@
         sizer_constraint.Add(egal_txt, flag=wx.RIGHT | wx.EXPAND, border=5)
         sizer_constraint.Add(constraint, flag=wx.RIGHT | wx.EXPAND, border=10)
-        sizer_constraint.Add(btRemove, flag=wx.RIGHT | wx.EXPAND, border=10)
+        sizer_constraint.Add(bt_remove, flag=wx.RIGHT | wx.EXPAND, border=10)
 
         self.sizer_constraints.Insert(before=self.nb_constraint,
@@ -810,11 +835,11 @@
                 border=5)
         c = ConstraintLine(model_cbox, param_cbox, egal_txt,
-                           constraint, btRemove, sizer_constraint)
+                           constraint, bt_remove, sizer_constraint)
         self.constraints_list.append(c)
 
         self.nb_constraint += 1
         self.sizer_constraints.Layout()
-        self.sizer2.Layout()
-        self.Layout
+        self.constraints_sizer.Layout()
+        self.Layout()
 
     def _hide_constraint(self):
@@ -841,16 +866,16 @@
         self.sizer_constraints.Clear(True)
         self.sizer_constraints.Layout()
-        self.sizer2.Layout()
-        self.Layout
+        self.constraints_sizer.Layout()
+        self.Layout()
         self.FitInside()
 
     def _on_select_model(self, event):
         """
-        fill combox box with list of parameters
+        fill combo box with list of parameters
         """
         if not self.constraints_list:
             return
 
-        ##This way PC/MAC both work, instead of using event.GetClientData().
+        # This way PC/MAC both work, instead of using event.GetClientData().
         model_cbox = self.constraints_list[-1].model_cbox
         n = model_cbox.GetCurrentSelection()
@@ -867,11 +892,11 @@
         param_cbox = self.constraints_list[-1].param_cbox
         param_cbox.Clear()
-        ## insert only fittable paramaters
+        # insert only fittable paramaters
         for param in param_list:
             param_cbox.Append(str(param), model)
         param_cbox.Show(True)
 
-        btRemove = self.constraints_list[-1].btRemove
-        btRemove.Show(True)
+        bt_remove = self.constraints_list[-1].btRemove
+        bt_remove.Show(True)
         self.btAdd.Show(True)
 #        self.Layout()
@@ -882,8 +907,8 @@
         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()
+        # 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()
 
         if self.constraints_list:
@@ -891,5 +916,5 @@
             self.constraints_list[-1].constraint.Show(True)
 
-    def _onAdd_constraint(self, event):
+    def _on_add_constraint(self, event):
         """
         Add another line for constraint
@@ -899,5 +924,5 @@
             wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
             return
-        ## check that a constraint is added
+        # check that a constraint is added
         # before allow to add another constraint
         for item in self.constraints_list:
@@ -913,5 +938,5 @@
                 model = item.param_cbox.GetClientData(
                                         item.param_cbox.GetCurrentSelection())
-                if model != None:
+                if model is not None:
                     msg = " Enter a constraint for %s.%s! " % (model.name,
                                         item.param_cbox.GetString(0))
@@ -920,5 +945,5 @@
                 wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
                 return
-        ## some model or parameters can be constrained
+        # some model or parameters can be constrained
         self._show_constraint()
         self.FitInside()
@@ -980,5 +1005,5 @@
     def on_set_focus(self, event=None):
         """
-        The  derivative class is on focus if implemented
+        The derivative class is on focus if implemented
         """
         if self.parent is not None:
@@ -995,2 +1020,81 @@
         cbox.Append(name, value)
     cbox.SetStringSelection(selected)
+
+
+class SimFitPageState:
+    """
+    State of the simultaneous fit page for saving purposes
+    """
+
+    def __init__(self):
+        # Sim Fit Page Number
+        self.fit_page_no = None
+        # Select all data
+        self.select_all = False
+        # Data sets sent to fit page
+        self.model_list = []
+        # Data sets to be fit
+        self.model_to_fit = []
+        # Number of constraints
+        self.no_constraint = 0
+        # Dictionary of constraints
+        self.constraint_dict = {}
+        # List of constraints
+        self.constraints_list = []
+
+    def load_from_save_state(self, fit):
+        """
+        Load in a simultaneous/constrained fit from a save state
+        :param fit: Fitpanel object
+        :return: None
+        """
+
+        model_map = {}
+        if fit.fit_panel.sim_page is None:
+            fit.fit_panel.add_sim_page()
+        sim_page = fit.fit_panel.sim_page
+
+        # Process each model and associate old M# with new M#
+        i = 0
+        for model in sim_page.model_list:
+            model_id = self._format_id(model[1].keys()[0])
+            for saved_model in self.model_list:
+                save_id = saved_model.pop('name')
+                saved_model['name'] = save_id
+                save_id = self._format_id(save_id)
+                if save_id == model_id:
+                    model_map[saved_model.pop('fit_page_source')] = \
+                        model[3].name
+                    check = bool(saved_model.pop('checked'))
+                    sim_page.model_list[i][0].SetValue(check)
+                    break
+            i += 1
+        sim_page.check_model_name(None)
+
+        if len(self.constraints_list) > 0:
+            sim_page.hide_constraint.SetValue(False)
+            sim_page.show_constraint.SetValue(True)
+            sim_page._display_constraint(None)
+
+        for index, item in enumerate(self.constraints_list):
+            model_cbox = item.pop('model_cbox')
+            if model_cbox != "":
+                constraint_value = item.pop('constraint')
+                param = item.pop('param_cbox')
+                equality = item.pop('egal_txt')
+                for key, value in model_map.iteritems():
+                    model_cbox.replace(key, value)
+                    constraint_value.replace(key, value)
+
+                sim_page.constraints_list[index][0].SetValue(model_cbox)
+                sim_page._on_select_model(None)
+                sim_page.constraints_list[index][1].SetValue(param)
+                sim_page.constraints_list[index][2].SetLabel(equality)
+                sim_page.constraints_list[index][3].SetValue(constraint_value)
+                sim_page._on_add_constraint(None)
+
+    def _format_id(self, original_id):
+        original_id = original_id.rstrip('1234567890.')
+        new_id_list = original_id.split()
+        new_id = ' '.join(new_id_list)
+        return new_id
Index: src/sas/sasgui/perspectives/invariant/invariant_panel.py
===================================================================
--- src/sas/sasgui/perspectives/invariant/invariant_panel.py	(revision 654e8e066dda6b98c221cad28e05fa6996e7fc11)
+++ src/sas/sasgui/perspectives/invariant/invariant_panel.py	(revision 18b7ecb9592132eb5e291874eb5b6c2046d54a45)
@@ -95,4 +95,5 @@
         self.is_state_data = False
         self.is_power_out = False
+        self._set_analysis(False)
 
         #container of invariant value
@@ -170,4 +171,5 @@
             self._reset_state_list()
             self._set_bookmark_flag(True)
+            self._set_analysis(True)
         return True
 
@@ -281,4 +283,5 @@
             self.new_state = False
             self.is_state_data = False
+            self._set_analysis(True)
 
     def clear_panel(self):
@@ -911,4 +914,5 @@
         self.button_calculate.SetFocus()
         #self.SetupScrolling()
+        self._set_analysis(False)
 
     def _set_state(self, event):
Index: src/sas/sasgui/perspectives/pr/inversion_panel.py
===================================================================
--- src/sas/sasgui/perspectives/pr/inversion_panel.py	(revision 5a58656233d026326525899991332b8052b6536b)
+++ src/sas/sasgui/perspectives/pr/inversion_panel.py	(revision 18b7ecb9592132eb5e291874eb5b6c2046d54a45)
@@ -43,4 +43,5 @@
         #Set window's font size
         self.SetWindowVariant(variant=FONT_VARIANT)
+        self._set_analysis(False)
 
         self.plots = plots
@@ -396,4 +397,7 @@
             self.bck = state.bck
 
+        # We have the data available for serialization
+        self._set_analysis(True)
+
         # Perform inversion
         self._on_invert(None)
@@ -401,4 +405,6 @@
     def set_manager(self, manager):
         self._manager = manager
+        if manager is not None:
+            self._set_analysis(False)
 
     def _do_layout(self):
@@ -752,4 +758,6 @@
         self.nterms_estimate_ctl.Enable(False)
         self.nterms_estimate_ctl.SetLabel("")
+        self._set_analysis(False)
+
         self._on_pars_changed()
 
@@ -936,4 +944,5 @@
                 self._on_pars_changed(None)
                 self._on_invert(None)
+                self._set_analysis(True)
             except:
                 msg = "InversionControl._change_file: %s" % sys.exc_value
