Index: src/sas/sascalc/fit/models.py
===================================================================
--- src/sas/sascalc/fit/models.py	(revision 65f39301a9c81af2f10a086dec2a40fcaf25a0bc)
+++ src/sas/sascalc/fit/models.py	(revision 277257f6caa3c7a18ad588f88501a89a34aa36e4)
@@ -16,4 +16,5 @@
 
 from sasmodels.sasview_model import load_custom_model, load_standard_models
+from sasmodels.sasview_model import MultiplicationModel
 
 # Explicitly import from the pluginmodel module so that py2exe
@@ -21,6 +22,4 @@
 # as the base class of plug-in models.
 from .pluginmodel import Model1DPlugin
-
-from sas.sasgui.guiframe.CategoryInstaller import CategoryInstaller
 
 logger = logging.getLogger(__name__)
@@ -92,47 +91,45 @@
     The plugin directory is located in the user's home directory.
     """
-    dir = os.path.join(os.path.expanduser("~"), '.sasview', PLUGIN_DIR)
-
+    path = os.path.join(os.path.expanduser("~"), '.sasview', PLUGIN_DIR)
+
+    # TODO: initializing ~/.sasview/plugin_models doesn't belong in sascalc
     # If the plugin directory doesn't exist, create it
-    if not os.path.isdir(dir):
-        os.makedirs(dir)
-
-    # Find paths needed
-    # TODO: remove unneeded try/except block
-    try:
-        # For source
-        if os.path.isdir(os.path.dirname(__file__)):
-            p_dir = os.path.join(os.path.dirname(__file__), PLUGIN_DIR)
-        else:
-            raise
-    except Exception:
-        # Check for data path next to exe/zip file.
-        #Look for maximum n_dir up of the current dir to find plugins dir
-        n_dir = 12
-        p_dir = None
-        f_dir = os.path.join(os.path.dirname(__file__))
-        for i in range(n_dir):
-            if i > 1:
-                f_dir, _ = os.path.split(f_dir)
-            plugin_path = os.path.join(f_dir, PLUGIN_DIR)
-            if os.path.isdir(plugin_path):
-                p_dir = plugin_path
-                break
-        if not p_dir:
-            raise
-    # Place example user models as needed
-    if os.path.isdir(p_dir):
-        for file in os.listdir(p_dir):
-            file_path = os.path.join(p_dir, file)
-            if os.path.isfile(file_path):
-                if file.split(".")[-1] == 'py' and\
-                    file.split(".")[0] != '__init__':
-                    if not os.path.isfile(os.path.join(dir, file)):
-                        shutil.copy(file_path, dir)
-
-    return dir
-
-
-class ReportProblem:
+    if not os.path.isdir(path):
+        os.makedirs(path)
+    # TODO: should we be checking for new default models every time?
+    initialize_plugins_dir(path)
+    return path
+
+
+def initialize_plugins_dir(path):
+    # TODO: There are no default plugins
+    # TODO: Move default plugins beside sample data files
+    # TODO: Should not look for defaults above the root of the sasview install
+
+    # Walk up the tree looking for default plugin_models directory
+    base = os.path.abspath(os.path.dirname(__file__))
+    for _ in range(12):
+        default_plugins_path = os.path.join(base, PLUGIN_DIR)
+        if os.path.isdir(default_plugins_path):
+            break
+        base, _ = os.path.split(base)
+    else:
+        logger.error("default plugins directory not found")
+        return
+
+    # Copy files from default plugins to the .sasview directory
+    # This may include c files, depending on the example.
+    # Note: files are never replaced, even if the default plugins are updated
+    for filename in os.listdir(default_plugins_path):
+        # skip __init__.py and all pyc files
+        if filename == "__init__.py" or filename.endswith('.pyc'):
+            continue
+        source = os.path.join(default_plugins_path, filename)
+        target = os.path.join(path, filename)
+        if os.path.isfile(source) and not os.path.isfile(target):
+            shutil.copy(source, target)
+
+
+class ReportProblem(object):
     """
     Class to check for problems with specific values
@@ -161,28 +158,30 @@
 
 
-def _find_models():
+def find_plugin_models():
     """
     Find custom models
     """
     # List of plugin objects
-    directory = find_plugins_dir()
+    plugins_dir = find_plugins_dir()
     # Go through files in plug-in directory
-    if not os.path.isdir(directory):
-        msg = "SasView couldn't locate Model plugin folder %r." % directory
+    if not os.path.isdir(plugins_dir):
+        msg = "SasView couldn't locate Model plugin folder %r." % plugins_dir
         logger.warning(msg)
         return {}
 
-    plugin_log("looking for models in: %s" % str(directory))
-    # compile_file(directory)  #always recompile the folder plugin
-    logger.info("plugin model dir: %s", str(directory))
+    plugin_log("looking for models in: %s" % plugins_dir)
+    # compile_file(plugins_dir)  #always recompile the folder plugin
+    logger.info("plugin model dir: %s", plugins_dir)
 
     plugins = {}
-    for filename in os.listdir(directory):
+    for filename in os.listdir(plugins_dir):
         name, ext = os.path.splitext(filename)
         if ext == '.py' and not name == '__init__':
-            path = os.path.abspath(os.path.join(directory, filename))
+            path = os.path.abspath(os.path.join(plugins_dir, filename))
             try:
                 model = load_custom_model(path)
-                model.name = PLUGIN_NAME_BASE + model.name
+                # TODO: add [plug-in] tag to model name in sasview_model
+                if not model.name.startswith(PLUGIN_NAME_BASE):
+                    model.name = PLUGIN_NAME_BASE + model.name
                 plugins[model.name] = model
             except Exception:
@@ -196,109 +195,31 @@
 
 
-class ModelList(object):
-    """
-    Contains dictionary of model and their type
-    """
+class ModelManagerBase(object):
+    """
+    Base class for the model manager
+    """
+    #: mutable dictionary of models, continually updated to reflect the
+    #: current set of plugins
+    model_dictionary = None  # type: Dict[str, Model]
+    #: constant list of standard models
+    standard_models = None  # type: Dict[str, Model]
+    #: list of plugin models reset each time the plugin directory is queried
+    plugin_models = None  # type: Dict[str, Model]
+    #: timestamp on the plugin directory at the last plugin update
+    last_time_dir_modified = 0  # type: int
+
     def __init__(self):
-        """
-        """
-        self.mydict = {}
-
-    def set_list(self, name, mylist):
-        """
-        :param name: the type of the list
-        :param mylist: the list to add
-
-        """
-        if name not in self.mydict.keys():
-            self.reset_list(name, mylist)
-
-    def reset_list(self, name, mylist):
-        """
-        :param name: the type of the list
-        :param mylist: the list to add
-        """
-        self.mydict[name] = mylist
-
-    def get_list(self):
-        """
-        return all the list stored in a dictionary object
-        """
-        return self.mydict
-
-
-class ModelManagerBase(object):
-    """
-    Base class for the model manager
-    """
-    ## external dict for models
-    model_combobox = ModelList()
-    ## Dictionary of form factor models
-    form_factor_dict = {}
-    ## dictionary of structure factor models
-    struct_factor_dict = {}
-    ##list of structure factors
-    struct_list = []
-    ##list of model allowing multiplication by a structure factor
-    multiplication_factor = []
-    ##list of multifunctional shapes (i.e. that have user defined number of levels
-    multi_func_list = []
-    ## list of added models -- currently python models found in the plugin dir.
-    plugins = []
-    ## Event owner (guiframe)
-    event_owner = None
-    last_time_dir_modified = 0
-
-    def __init__(self):
+        # the model dictionary is allocated at the start and updated to
+        # reflect the current list of models.  Be sure to clear it rather
+        # than reassign to it.
         self.model_dictionary = {}
-        self.stored_plugins = {}
-        self._getModelList()
-
-    def findModels(self):
-        """
-        find  plugin model in directory of plugin .recompile all file
-        in the directory if file were modified
-        """
-        temp = {}
-        if self.is_changed():
-            return  _find_models()
-        logger.info("plugin model : %s", str(temp))
-        return temp
-
-    def _getModelList(self):
-        """
-        List of models we want to make available by default
-        for this application
-
-        :return: the next free event ID following the new menu events
-
-        """
-
-        # regular model names only
-        self.model_name_list = []
 
         #Build list automagically from sasmodels package
-        for model in load_standard_models():
-            self.model_dictionary[model.name] = model
-            if model.is_structure_factor:
-                self.struct_list.append(model)
-            if model.is_form_factor:
-                self.multiplication_factor.append(model)
-            if model.is_multiplicity_model:
-                self.multi_func_list.append(model)
-            else:
-                self.model_name_list.append(model.name)
-
-        #Looking for plugins
-        self.stored_plugins = self.findModels()
-        self.plugins = self.stored_plugins.values()
-        for name, plug in self.stored_plugins.iteritems():
-            self.model_dictionary[name] = plug
-
-        self._get_multifunc_models()
-
-        return 0
-
-    def is_changed(self):
+        self.standard_models = {model.name: model
+                                for model in load_standard_models()}
+        # Look for plugins
+        self.plugins_reset()
+
+    def _is_plugin_dir_changed(self):
         """
         check the last time the plugin dir has changed and return true
@@ -308,27 +229,30 @@
         plugin_dir = find_plugins_dir()
         if os.path.isdir(plugin_dir):
-            temp = os.path.getmtime(plugin_dir)
-            if  self.last_time_dir_modified != temp:
+            mod_time = os.path.getmtime(plugin_dir)
+            if  self.last_time_dir_modified != mod_time:
                 is_modified = True
-                self.last_time_dir_modified = temp
+                self.last_time_dir_modified = mod_time
 
         return is_modified
 
-    def update(self):
+    def composable_models(self):
+        """
+        return list of standard models that can be used in sum/multiply
+        """
+        # TODO: should scan plugin models in addition to standard models
+        # and update model_editor so that it doesn't add plugins to the list
+        return [model.name for model in self.standard_models.values()
+                if not model.is_multiplicity_model]
+
+    def plugins_update(self):
         """
         return a dictionary of model if
         new models were added else return empty dictionary
         """
-        new_plugins = self.findModels()
-        if len(new_plugins) > 0:
-            for name, plug in  new_plugins.iteritems():
-                if name not in self.stored_plugins.keys():
-                    self.stored_plugins[name] = plug
-                    self.plugins.append(plug)
-                    self.model_dictionary[name] = plug
-            self.model_combobox.set_list("Plugin Models", self.plugins)
-            return self.model_combobox.get_list()
-        else:
-            return {}
+        return self.plugins_reset()
+        #if self._is_plugin_dir_changed():
+        #    return self.plugins_reset()
+        #else:
+        #    return {}
 
     def plugins_reset(self):
@@ -336,46 +260,20 @@
         return a dictionary of model
         """
-        self.plugins = []
-        new_plugins = _find_models()
-        for name, plug in  new_plugins.iteritems():
-            for stored_name, stored_plug in self.stored_plugins.iteritems():
-                if name == stored_name:
-                    del self.stored_plugins[name]
-                    del self.model_dictionary[name]
-                    break
-            self.stored_plugins[name] = plug
-            self.plugins.append(plug)
-            self.model_dictionary[name] = plug
-
-        self.model_combobox.reset_list("Plugin Models", self.plugins)
-        return self.model_combobox.get_list()
-
-    def _on_model(self, evt):
-        """
-        React to a model menu event
-
-        :param event: wx menu event
-
-        """
-        if int(evt.GetId()) in self.form_factor_dict.keys():
-            from sasmodels.sasview_model import MultiplicationModel
-            self.model_dictionary[MultiplicationModel.__name__] = MultiplicationModel
-            model1, model2 = self.form_factor_dict[int(evt.GetId())]
-            model = MultiplicationModel(model1, model2)
-        else:
-            model = self.struct_factor_dict[str(evt.GetId())]()
-
-
-    def _get_multifunc_models(self):
-        """
-        Get the multifunctional models
-        """
-        items = [item for item in self.plugins if item.is_multiplicity_model]
-        self.multi_func_list = items
+        self.plugin_models = find_plugin_models()
+        self.model_dictionary.clear()
+        self.model_dictionary.update(self.standard_models)
+        self.model_dictionary.update(self.plugin_models)
+        return self.get_model_list()
 
     def get_model_list(self):
         """
-        return dictionary of models for fitpanel use
-
+        return dictionary of classified models
+
+        *Structure Factors* are the structure factor models
+        *Multi-Functions* are the multiplicity models
+        *Plugin Models* are the plugin models
+
+        Note that a model can be both a plugin and a structure factor or
+        multiplicity model.
         """
         ## Model_list now only contains attribute lists not category list.
@@ -387,31 +285,26 @@
         ## -PDB   April 26, 2014
 
-#        self.model_combobox.set_list("Shapes", self.shape_list)
-#        self.model_combobox.set_list("Shape-Independent",
-#                                     self.shape_indep_list)
-        self.model_combobox.set_list("Structure Factors", self.struct_list)
-        self.model_combobox.set_list("Plugin Models", self.plugins)
-        self.model_combobox.set_list("P(Q)*S(Q)", self.multiplication_factor)
-        self.model_combobox.set_list("multiplication",
-                                     self.multiplication_factor)
-        self.model_combobox.set_list("Multi-Functions", self.multi_func_list)
-        return self.model_combobox.get_list()
-
-    def get_model_name_list(self):
-        """
-        return regular model name list
-        """
-        return self.model_name_list
-
-    def get_model_dictionary(self):
-        """
-        return dictionary linking model names to objects
-        """
-        return self.model_dictionary
+
+        # Classify models
+        structure_factors = []
+        multiplicity_models = []
+        for model in self.model_dictionary.values():
+            # Old style models don't have is_structure_factor attribute
+            if getattr(model, 'is_structure_factor', False):
+                structure_factors.append(model)
+            if model.is_multiplicity_model:
+                multiplicity_models.append(model)
+        plugin_models = list(self.plugin_models.values())
+
+        return {
+            "Structure Factors": structure_factors,
+            "Plugin Models": plugin_models,
+            "Multi-Functions": multiplicity_models,
+        }
 
 
 class ModelManager(object):
     """
-    implement model
+    manage the list of available models
     """
     base = None  # type: ModelManagerBase()
@@ -419,42 +312,21 @@
     def __init__(self):
         if ModelManager.base is None:
-            self.base = ModelManagerBase()
+            ModelManager.base = ModelManagerBase()
 
     def cat_model_list(self):
-        models = self.base.model_dictionary
-        retval = [model for model_name, model in models.items()
-                  if model_name not in self.base.stored_plugins]
-        return retval
-
-    def findModels(self):
-        return self.base.findModels()
-
-    def _getModelList(self):
-        return self.base._getModelList()
-
-    def is_changed(self):
-        return self.base.is_changed()
+        return list(self.base.standard_models.values())
 
     def update(self):
-        return self.base.update()
+        return self.base.plugins_update()
 
     def plugins_reset(self):
         return self.base.plugins_reset()
 
-    #def populate_menu(self, modelmenu, event_owner):
-    #    return self.base.populate_menu(modelmenu, event_owner)
-
-    def _on_model(self, evt):
-        return self.base._on_model(evt)
-
-    def _get_multifunc_models(self):
-        return self.base._get_multifunc_models()
-
     def get_model_list(self):
         return self.base.get_model_list()
 
-    def get_model_name_list(self):
-        return self.base.get_model_name_list()
+    def composable_models(self):
+        return self.base.composable_models()
 
     def get_model_dictionary(self):
-        return self.base.get_model_dictionary()
+        return self.base.model_dictionary
Index: src/sas/sascalc/fit/pagestate.py
===================================================================
--- src/sas/sascalc/fit/pagestate.py	(revision ba8d326f0f4ceb55c77296a9b5af8bd013c7dd92)
+++ src/sas/sascalc/fit/pagestate.py	(revision 277257f6caa3c7a18ad588f88501a89a34aa36e4)
@@ -225,6 +225,4 @@
         # contains link between a model and selected parameters to fit
         self.param_toFit = []
-        # dictionary of model type and model class
-        self.model_list_box = None
         # save the state of the context menu
         self.saved_states = {}
@@ -288,5 +286,4 @@
         obj.data_name = self.data_name
         obj.is_data = self.is_data
-        obj.model_list_box = copy.deepcopy(self.model_list_box)
 
         obj.categorycombobox = self.categorycombobox
@@ -967,6 +964,8 @@
             # Get file name
             entry = get_content('ns:filename', node)
-            if entry is not None:
+            if entry is not None and entry.text:
                 self.file = entry.text.strip()
+            else:
+                self.file = ''
 
             # Get time stamp
Index: src/sas/sasgui/perspectives/calculator/model_editor.py
===================================================================
--- src/sas/sasgui/perspectives/calculator/model_editor.py	(revision 65f39301a9c81af2f10a086dec2a40fcaf25a0bc)
+++ src/sas/sasgui/perspectives/calculator/model_editor.py	(revision 277257f6caa3c7a18ad588f88501a89a34aa36e4)
@@ -31,10 +31,13 @@
 import re
 import logging
+import datetime
+
 from wx.py.editwindow import EditWindow
+
 from sas.sasgui.guiframe.documentation_window import DocumentationWindow
+
 from .pyconsole import show_model_output, check_model
 
 logger = logging.getLogger(__name__)
-
 
 if sys.platform.count("win32") > 0:
@@ -78,5 +81,5 @@
     a Modal Dialog.
 
-    :TODO the build in compiler currently balks at when it tries to import
+    :TODO the built in compiler currently balks at when it tries to import
     a model whose name contains spaces or symbols (such as + ... underscore
     should be fine).  Have fixed so the editor cannot save such a file name
@@ -336,5 +339,5 @@
             list_fnames = os.listdir(self.plugin_dir)
             # fake existing regular model name list
-            m_list = [model + ".py" for model in self.model_list]
+            m_list = [model.name + ".py" for model in self.model_list]
             list_fnames.append(m_list)
             if t_fname in list_fnames and title != mname:
@@ -855,5 +858,5 @@
                     exec "float(math.%s)" % item
                     self.math_combo.Append(str(item))
-                except:
+                except Exception:
                     self.math_combo.Append(str(item) + "()")
         self.math_combo.Bind(wx.EVT_COMBOBOX, self._on_math_select)
@@ -980,14 +983,7 @@
             msg = "Name exists already."
 
-        # Prepare the messagebox
+        #
         if self.base is not None and not msg:
             self.base.update_custom_combo()
-            # Passed exception in import test as it will fail for sasmodels.sasview_model class
-            # Should add similar test for new style?
-            Model = None
-            try:
-                exec "from %s import Model" % name
-            except:
-                logger.error(sys.exc_value)
 
         # Prepare the messagebox
@@ -1020,105 +1016,74 @@
         :param func_str: content of func; Strings
         """
-        try:
-            out_f = open(fname, 'w')
-        except:
-            raise
-        # Prepare the content of the function
-        lines = CUSTOM_TEMPLATE.split('\n')
-
-        has_scipy = func_str.count("scipy.")
-        if has_scipy:
-            lines.insert(0, 'import scipy')
-
-        # Think about 2D later
-        #self.is_2d = func_str.count("#self.ndim = 2")
-        #line_2d = ''
-        #if self.is_2d:
-        #    line_2d = CUSTOM_2D_TEMP.split('\n')
-
-        # Also think about test later
-        #line_test = TEST_TEMPLATE.split('\n')
-        #local_params = ''
-        #spaces = '        '#8spaces
-        spaces4  = ' '*4
-        spaces13 = ' '*13
-        spaces16 = ' '*16
+        out_f = open(fname, 'w')
+
+        out_f.write(CUSTOM_TEMPLATE % {
+            'name': name,
+            'title': 'User model for ' + name,
+            'description': desc_str,
+            'date': datetime.datetime.now().strftime('%YYYY-%mm-%dd'),
+        })
+
+        # Write out parameters
         param_names = []    # to store parameter names
-        has_scipy = func_str.count("scipy.")
-        if has_scipy:
-            lines.insert(0, 'import scipy')
-
-        # write function here
-        for line in lines:
-            # The location where to put the strings is
-            # hard-coded in the template as shown below.
-            out_f.write(line + '\n')
-            if line.count('#name'):
-                out_f.write('name = "%s" \n' % name)
-            elif line.count('#title'):
-                out_f.write('title = "User model for %s"\n' % name)
-            elif line.count('#description'):
-                out_f.write('description = "%s"\n' % desc_str)
-            elif line.count('#parameters'):
-                out_f.write('parameters = [ \n')
-                for param_line in param_str.split('\n'):
-                    p_line = param_line.lstrip().rstrip()
-                    if p_line:
-                        pname, pvalue = self.get_param_helper(p_line)
-                        param_names.append(pname)
-                        out_f.write("%s['%s', '', %s, [-numpy.inf, numpy.inf], '', ''],\n" % (spaces16, pname, pvalue))
-                for param_line in pd_param_str.split('\n'):
-                    p_line = param_line.lstrip().rstrip()
-                    if p_line:
-                        pname, pvalue = self.get_param_helper(p_line)
-                        param_names.append(pname)
-                        out_f.write("%s['%s', '', %s, [-numpy.inf, numpy.inf], 'volume', ''],\n" % (spaces16, pname, pvalue))
-                out_f.write('%s]\n' % spaces13)
-
-        # No form_volume or ER available in simple model editor
-        out_f.write('def form_volume(*arg): \n')
-        out_f.write('    return 1.0 \n')
-        out_f.write('\n')
-        out_f.write('def ER(*arg): \n')
-        out_f.write('    return 1.0 \n')
-
-        # function to compute
-        out_f.write('\n')
-        out_f.write('def Iq(x ')
-        for name in param_names:
-            out_f.write(', %s' % name)
-        out_f.write('):\n')
+        pd_params = []
+        out_f.write('parameters = [ \n')
+        out_f.write('#   ["name", "units", default, [lower, upper], "type", "description"],\n')
+        for pname, pvalue in self.get_param_helper(param_str):
+            param_names.append(pname)
+            out_f.write("    ['%s', '', %s, [-inf, inf], '', ''],\n"
+                        % (pname, pvalue))
+        for pname, pvalue in self.get_param_helper(pd_param_str):
+            param_names.append(pname)
+            pd_params.append(pname)
+            out_f.write("    ['%s', '', %s, [-inf, inf], 'volume', ''],\n"
+                        % (pname, pvalue))
+        out_f.write('    ]\n')
+
+        # Write out function definition
+        out_f.write('def Iq(%s):\n' % ', '.join(['x'] + param_names))
+        out_f.write('    """Absolute scattering"""\n')
+        if "scipy." in func_str:
+            out_f.write('    import scipy')
+        if "numpy." in func_str:
+            out_f.write('    import numpy')
+        if "np." in func_str:
+            out_f.write('    import numpy as np')
         for func_line in func_str.split('\n'):
             out_f.write('%s%s\n' % (spaces4, func_line))
-
-        Iqxy_string = 'return Iq(numpy.sqrt(x**2+y**2) '
-
+        out_f.write('## uncomment the following if Iq works for vector x\n')
+        out_f.write('#Iq.vectorized = True\n')
+
+        # If polydisperse, create place holders for form_volume, ER and VR
+        if pd_params:
+            out_f.write('\n')
+            out_f.write(CUSTOM_TEMPLATE_PD % {'args': ', '.join(pd_params)})
+
+        # Create place holder for Iqxy
         out_f.write('\n')
-        out_f.write('def Iqxy(x, y ')
-        for name in param_names:
-            out_f.write(', %s' % name)
-            Iqxy_string += ', ' + name
-        out_f.write('):\n')
-        Iqxy_string += ')'
-        out_f.write('%s%s\n' % (spaces4, Iqxy_string))
+        out_f.write('#def Iqxy(%s):\n' % ', '.join(["x", "y"] + param_names))
+        out_f.write('#    """Absolute scattering of oriented particles."""\n')
+        out_f.write('#    ...\n')
+        out_f.write('#    return oriented_form(x, y, args)\n')
+        out_f.write('## uncomment the following if Iqxy works for vector x, y\n')
+        out_f.write('#Iqxy.vectorized = True\n')
 
         out_f.close()
 
-    def get_param_helper(self, line):
-        """
-        Get string in line to define the params dictionary
-
-        :param line: one line of string got from the param_str
-        """
-        items = line.split(";")
-        for item in items:
-            name = item.split("=")[0].lstrip().rstrip()
-            try:
-                value = item.split("=")[1].lstrip().rstrip()
-                float(value)
-            except:
-                value = 1.0 # default
-
-        return name, value
+    def get_param_helper(self, param_str):
+        """
+        yield a sequence of name, value pairs for the parameters in param_str
+
+        Parameters can be defined by one per line by name=value, or multiple
+        on the same line by separating the pairs by semicolon or comma.  The
+        value is optional and defaults to "1.0".
+        """
+        for line in param_str.replace(';', ',').split('\n'):
+            for item in line.split(','):
+                parts = item.plit('=')
+                name = parts[0].strip()
+                value = parts[1] if len(parts) > 0 else '1.0'
+                if name:
+                    yield name, value
 
     def set_function_helper(self, line):
@@ -1154,6 +1119,6 @@
         running "file:///...."
 
-    :param evt: Triggers on clicking the help button
-    """
+        :param evt: Triggers on clicking the help button
+        """
 
         _TreeLocation = "user/sasgui/perspectives/fitting/fitting_help.html"
@@ -1198,74 +1163,58 @@
 ## Templates for plugin models
 
-CUSTOM_TEMPLATE = """
+CUSTOM_TEMPLATE = '''\
+r"""
+Definition
+----------
+
+Calculates %(name)s.
+
+%(description)s
+
+References
+----------
+
+Authorship and Verification
+---------------------------
+
+* **Author:** --- **Date:** %(date)s
+* **Last Modified by:** --- **Date:** %(date)s
+* **Last Reviewed by:** --- **Date:** %(date)s
+"""
+
 from math import *
-import os
-import sys
-import numpy
-
-#name
-
-#title
-
-#description
-
-#parameters
-
-"""
-
-CUSTOM_2D_TEMP = """
-    def run(self, x=0.0, y=0.0):
-        if x.__class__.__name__ == 'list':
-            x_val = x[0]
-            y_val = y[0]*0.0
-            return self.function(x_val, y_val)
-        elif x.__class__.__name__ == 'tuple':
-            msg = "Tuples are not allowed as input to BaseComponent models"
-            raise ValueError, msg
-        else:
-            return self.function(x, 0.0)
-    def runXY(self, x=0.0, y=0.0):
-        if x.__class__.__name__ == 'list':
-            return self.function(x, y)
-        elif x.__class__.__name__ == 'tuple':
-            msg = "Tuples are not allowed as input to BaseComponent models"
-            raise ValueError, msg
-        else:
-            return self.function(x, y)
-    def evalDistribution(self, qdist):
-        if qdist.__class__.__name__ == 'list':
-            msg = "evalDistribution expects a list of 2 ndarrays"
-            if len(qdist)!=2:
-                raise RuntimeError, msg
-            if qdist[0].__class__.__name__ != 'ndarray':
-                raise RuntimeError, msg
-            if qdist[1].__class__.__name__ != 'ndarray':
-                raise RuntimeError, msg
-            v_model = numpy.vectorize(self.runXY, otypes=[float])
-            iq_array = v_model(qdist[0], qdist[1])
-            return iq_array
-        elif qdist.__class__.__name__ == 'ndarray':
-            v_model = numpy.vectorize(self.runXY, otypes=[float])
-            iq_array = v_model(qdist)
-            return iq_array
-"""
-TEST_TEMPLATE = """
-######################################################################
-## THIS IS FOR TEST. DO NOT MODIFY THE FOLLOWING LINES!!!!!!!!!!!!!!!!
-if __name__ == "__main__":
-    m= Model()
-    out1 = m.runXY(0.0)
-    out2 = m.runXY(0.01)
-    isfine1 = numpy.isfinite(out1)
-    isfine2 = numpy.isfinite(out2)
-    print "Testing the value at Q = 0.0:"
-    print out1, " : finite? ", isfine1
-    print "Testing the value at Q = 0.01:"
-    print out2, " : finite? ", isfine2
-    if isfine1 and isfine2:
-        print "===> Simple Test: Passed!"
-    else:
-        print "===> Simple Test: Failed!"
-"""
+from numpy import inf
+
+name = "%(name)s"
+title = "%(title)s"
+description = """%(description)s"""
+
+'''
+
+CUSTOM_TEMPLATE_PD = '''\
+def form_volume(%(args)s):
+    """
+    Volume of the particles used to compute absolute scattering intensity
+    and to weight polydisperse parameter contributions.
+    """
+    return 0.0
+
+def ER(%(args)s):
+    """
+    Effective radius of particles to be used when computing structure factors.
+
+    Input parameters are vectors ranging over the mesh of polydispersity values.
+    """
+    return 0.0
+
+def VR(%(args)s):
+    """
+    Volume ratio of particles to be used when computing structure factors.
+
+    Input parameters are vectors ranging over the mesh of polydispersity values.
+    """
+    return 1.0
+'''
+
 SUM_TEMPLATE = """
 # A sample of an experimental model function for Sum/Multiply(Pmodel1,Pmodel2)
@@ -1592,5 +1541,5 @@
     main_app = wx.App()
     main_frame = TextDialog(id=1, model_list=["SphereModel", "CylinderModel"],
-                       plugin_dir='../fitting/plugin_models')
+                            plugin_dir='../fitting/plugin_models')
     main_frame.ShowModal()
     main_app.MainLoop()
Index: src/sas/sasgui/perspectives/fitting/basepage.py
===================================================================
--- src/sas/sasgui/perspectives/fitting/basepage.py	(revision 65f39301a9c81af2f10a086dec2a40fcaf25a0bc)
+++ src/sas/sasgui/perspectives/fitting/basepage.py	(revision 277257f6caa3c7a18ad588f88501a89a34aa36e4)
@@ -504,22 +504,20 @@
         self.state.manager = manager
 
-    def populate_box(self, model_dict):
+    def populate_box(self, model_list_box):
         """
         Store list of model
 
-        :param model_dict: dictionary containing list of models
-
-        """
-        self.model_list_box = model_dict
-        self.state.model_list_box = self.model_list_box
+        :param model_list_box: dictionary containing categorized models
+        """
+        self.model_list_box = model_list_box
         self.initialize_combox()
 
-    def set_model_dictionary(self, model_dict):
+    def set_model_dictionary(self, model_dictionary):
         """
         Store a dictionary linking model name -> model object
 
-        :param model_dict: dictionary containing list of models
-        """
-        self.model_dict = model_dict
+        :param model_dictionary: dictionary containing all models
+        """
+        self.model_dictionary = model_dictionary
 
     def initialize_combox(self):
@@ -527,5 +525,5 @@
         put default value in the combo box
         """
-        if self.model_list_box is not None and len(self.model_list_box) > 0:
+        if self.model_list_box:
             self._populate_box(self.structurebox,
                                self.model_list_box["Structure Factors"])
@@ -1218,12 +1216,12 @@
         self.categorybox.Select(category_pos)
         self._show_combox(None)
-        if self.categorybox.GetValue() == CUSTOM_MODEL \
-                and PLUGIN_NAME_BASE not in state.formfactorcombobox:
+        if (self.categorybox.GetValue() == CUSTOM_MODEL
+                and PLUGIN_NAME_BASE not in state.formfactorcombobox):
             state.formfactorcombobox = \
                 PLUGIN_NAME_BASE + state.formfactorcombobox
         formfactor_pos = 0
         for ind_form in range(self.formfactorbox.GetCount()):
-            if self.formfactorbox.GetString(ind_form) == \
-                                                (state.formfactorcombobox):
+            if (self.formfactorbox.GetString(ind_form)
+                    == state.formfactorcombobox):
                 formfactor_pos = int(ind_form)
                 break
@@ -1235,6 +1233,6 @@
             state.structurecombobox = unicode(state.structurecombobox)
             for ind_struct in range(self.structurebox.GetCount()):
-                if self.structurebox.GetString(ind_struct) == \
-                                                (state.structurecombobox):
+                if (self.structurebox.GetString(ind_struct)
+                        == state.structurecombobox):
                     structfactor_pos = int(ind_struct)
                     break
@@ -1840,10 +1838,10 @@
             if mod_cat == CUSTOM_MODEL:
                 for model in self.model_list_box[mod_cat]:
-                    m_list.append(self.model_dict[model.name])
+                    m_list.append(self.model_dictionary[model.name])
             else:
                 cat_dic = self.master_category_dict[mod_cat]
-                for (model, enabled) in cat_dic:
+                for model, enabled in cat_dic:
                     if enabled:
-                        m_list.append(self.model_dict[model])
+                        m_list.append(self.model_dictionary[model])
         except Exception:
             msg = traceback.format_exc()
@@ -3497,5 +3495,5 @@
         self.model_box.Clear()
 
-        if category == 'Plugin Models':
+        if category == CUSTOM_MODEL:
             for model in self.model_list_box[category]:
                 str_m = str(model).split(".")[0]
Index: src/sas/sasgui/perspectives/fitting/fitpage.py
===================================================================
--- src/sas/sasgui/perspectives/fitting/fitpage.py	(revision 50fcb09b3ee47cf23c26b90d9c291f41415c0aa5)
+++ src/sas/sasgui/perspectives/fitting/fitpage.py	(revision 277257f6caa3c7a18ad588f88501a89a34aa36e4)
@@ -1143,18 +1143,23 @@
             self.model.name = "M" + str(self.index_model)
 
-    def _on_select_model(self, event=None):
+    def _on_select_model(self, event=None, keep_pars=False):
         """
         call back for model selection
         """
         self.Show(False)
-        copy_flag = False
-        is_poly_enabled = None
         if event is not None:
-            if (event.GetEventObject() == self.formfactorbox
-                    and self.structurebox.GetLabel() != 'None')\
-                    or event.GetEventObject() == self.structurebox\
-                    or event.GetEventObject() == self.multifactorbox:
-                copy_flag = self.get_copy_params()
-                is_poly_enabled = self.enable_disp.GetValue()
+            control = event.GetEventObject()
+            if ((control == self.formfactorbox
+                 and self.structurebox.GetLabel() != 'None')
+                    or control == self.structurebox
+                    or control == self.multifactorbox):
+                keep_pars = True
+
+        if keep_pars:
+            saved_pars = self.get_copy_params()
+            is_poly_enabled = self.enable_disp.GetValue()
+        else:
+            saved_pars = None
+            is_poly_enabled = None
 
         self._on_select_model_helper()
@@ -1171,5 +1176,5 @@
         try:
             self.set_dispers_sizer()
-        except:
+        except Exception:
             pass
         self.state.enable_disp = self.enable_disp.GetValue()
@@ -1232,31 +1237,31 @@
             self.state.model.name = self.model.name
 
+        # when select a model only from guictr/button
+        if is_poly_enabled is not None:
+            self.enable_disp.SetValue(is_poly_enabled)
+            self.disable_disp.SetValue(not is_poly_enabled)
+            self._set_dipers_Param(event=None)
+            self.state.enable_disp = self.enable_disp.GetValue()
+            self.state.disable_disp = self.disable_disp.GetValue()
+
+        # Keep the previous param values
+        if saved_pars:
+            self.get_paste_params(saved_pars)
+
         if event is not None:
+            # update list of plugins if new plugin is available
+            # mod_cat = self.categorybox.GetStringSelection()
+            # if mod_cat == CUSTOM_MODEL:
+            #     temp = self.parent.update_model_list()
+            #     if temp:
+            #         self.model_list_box = temp
+            #         current_val = self.formfactorbox.GetLabel()
+            #         pos = self.formfactorbox.GetSelection()
+            #         self._show_combox_helper()
+            #         self.formfactorbox.SetStringSelection(current_val)
+            #         self.formfactorbox.SetValue(current_val)
             # post state to fit panel
             new_event = PageInfoEvent(page=self)
             wx.PostEvent(self.parent, new_event)
-            # update list of plugins if new plugin is available
-            custom_model = CUSTOM_MODEL
-            mod_cat = self.categorybox.GetStringSelection()
-            if mod_cat == custom_model:
-                temp = self.parent.update_model_list()
-                if temp:
-                    self.model_list_box = temp
-                    current_val = self.formfactorbox.GetLabel()
-                    pos = self.formfactorbox.GetSelection()
-                    self._show_combox_helper()
-                    self.formfactorbox.SetSelection(pos)
-                    self.formfactorbox.SetValue(current_val)
-            # when select a model only from guictr/button
-            if is_poly_enabled is not None:
-                self.enable_disp.SetValue(is_poly_enabled)
-                self.disable_disp.SetValue(not is_poly_enabled)
-                self._set_dipers_Param(event=None)
-                self.state.enable_disp = self.enable_disp.GetValue()
-                self.state.disable_disp = self.disable_disp.GetValue()
-
-            # Keep the previous param values
-            if copy_flag:
-                self.get_paste_params(copy_flag)
             wx.CallAfter(self._onDraw, None)
 
@@ -1709,4 +1714,5 @@
                 and not self.temp_multi_functional:
             return None
+        print("_set_fun_box_list", self.model.name)
         # Get the func name list
         list = self.model.fun_list
@@ -1716,6 +1722,6 @@
         ind = 0
         while(ind < len(list)):
-            for key, val in list.iteritems():
-                if (val == ind):
+            for key, val in list.items():
+                if val == ind:
                     fun_box.Append(key, val)
                     break
Index: src/sas/sasgui/perspectives/fitting/fitpanel.py
===================================================================
--- src/sas/sasgui/perspectives/fitting/fitpanel.py	(revision 65f39301a9c81af2f10a086dec2a40fcaf25a0bc)
+++ src/sas/sasgui/perspectives/fitting/fitpanel.py	(revision 277257f6caa3c7a18ad588f88501a89a34aa36e4)
@@ -114,5 +114,5 @@
         """
         temp = self.menu_mng.update()
-        if len(temp):
+        if temp:
             self.model_list_box = temp
         return temp
@@ -121,8 +121,6 @@
         """
         """
-        temp = self.menu_mng.plugins_reset()
-        if len(temp):
-            self.model_list_box = temp
-        return temp
+        self.model_list_box = self.menu_mng.plugins_reset()
+        return self.model_list_box
 
     def get_page_by_id(self, uid):
@@ -298,9 +296,9 @@
         self.model_list_box = dict
 
-    def set_model_dict(self, m_dict):
+    def set_model_dictionary(self, model_dictionary):
         """
         copy a dictionary of model name -> model object
 
-        :param m_dict: dictionary linking model name -> model object
+        :param model_dictionary: dictionary linking model name -> model object
         """
 
@@ -356,5 +354,5 @@
         panel._set_save_flag(not panel.batch_on)
         panel.set_model_dictionary(self.model_dictionary)
-        panel.populate_box(model_dict=self.model_list_box)
+        panel.populate_box(model_list_box=self.model_list_box)
         panel.formfactor_combo_init()
         panel.set_manager(self._manager)
Index: src/sas/sasgui/perspectives/fitting/fitting.py
===================================================================
--- src/sas/sasgui/perspectives/fitting/fitting.py	(revision 00f7ff108e83f1acedecaa5d4ab9f0032a2ca8d5)
+++ src/sas/sasgui/perspectives/fitting/fitting.py	(revision 277257f6caa3c7a18ad588f88501a89a34aa36e4)
@@ -34,4 +34,5 @@
 from sas.sascalc.fit.BumpsFitting import BumpsFit as Fit
 from sas.sascalc.fit.pagestate import Reader, PageState, SimFitPageState
+from sas.sascalc.fit import models
 
 from sas.sasgui.guiframe.dataFitting import Data2D
@@ -60,5 +61,4 @@
 from .resultpanel import ResultPanel, PlotResultEvent
 from .gpu_options import GpuOptions
-from . import models
 
 logger = logging.getLogger(__name__)
@@ -297,5 +297,5 @@
         event_id = event.GetId()
         model_manager = models.ModelManager()
-        model_list = model_manager.get_model_name_list()
+        model_list = model_manager.composable_models()
         plug_dir = models.find_plugins_dir()
         textdial = TextDialog(None, self, wx.ID_ANY, 'Easy Sum/Multi(p1, p2) Editor',
@@ -337,22 +337,28 @@
             self.set_edit_menu_helper(self.parent, self.edit_custom_model)
             self.set_edit_menu_helper(self.parent, self.delete_custom_model)
-            temp = self.fit_panel.reset_pmodel_list()
-            if temp:
-                # Set the new plugin model list for all fit pages
-                for uid, page in self.fit_panel.opened_pages.iteritems():
-                    if hasattr(page, "formfactorbox"):
-                        page.model_list_box = temp
-                        current_val = page.formfactorbox.GetLabel()
-                        #if page.plugin_rbutton.GetValue():
-                        mod_cat = page.categorybox.GetStringSelection()
-                        if mod_cat == custom_model:
-                            #pos = page.formfactorbox.GetSelection()
-                            page._show_combox_helper()
-                            new_val = page.formfactorbox.GetLabel()
-                            if current_val != new_val and new_val != '':
-                                page.formfactorbox.SetLabel(new_val)
-                            else:
-                                page.formfactorbox.SetLabel(current_val)
-        except:
+            new_pmodel_list = self.fit_panel.reset_pmodel_list()
+            if not new_pmodel_list:
+                return
+            # Set the new plugin model list for all fit pages
+            for uid, page in self.fit_panel.opened_pages.iteritems():
+                if hasattr(page, "formfactorbox"):
+                    page.model_list_box = new_pmodel_list
+                    mod_cat = page.categorybox.GetStringSelection()
+                    if mod_cat == custom_model:
+                        box = page.formfactorbox
+                        model_name = box.GetValue()
+                        model = (box.GetClientData(box.GetCurrentSelection())
+                                 if model_name else None)
+                        page._show_combox_helper()
+                        new_index = box.FindString(model_name)
+                        new_model = (box.GetClientData(new_index)
+                                     if new_index >= 0 else None)
+                        if new_index >= 0:
+                            box.SetStringSelection(model_name)
+                        else:
+                            box.SetStringSelection('')
+                        if model and new_model != model:
+                            page._on_select_model(keep_pars=True)
+        except Exception:
             logger.error("update_custom_combo: %s", sys.exc_value)
 
@@ -364,5 +370,5 @@
         #new_model_menu = wx.Menu()
         self.edit_model_menu.Append(wx_id, 'New Plugin Model',
-                                   'Add a new model function')
+                                    'Add a new model function')
         wx.EVT_MENU(owner, wx_id, self.make_new_model)
 
@@ -646,5 +652,5 @@
                 self.parent.add_data(data_list={data.id: data})
                 wx.PostEvent(self.parent, NewPlotEvent(plot=data,
-                                        title=data.title))
+                             title=data.title))
                 #need to be fix later make sure we are sendind guiframe.data
                 #to panel
@@ -657,9 +663,9 @@
                 self.parent.add_data(data_list={data.id: data})
                 wx.PostEvent(self.parent, NewPlotEvent(plot=data,
-                                        title=data.title))
+                             title=data.title))
                 page = self.add_fit_page([data])
                 caption = page.window_caption
                 self.store_data(uid=page.uid, data_list=page.get_data_list(),
-                        caption=caption)
+                                caption=caption)
                 self.mypanels.append(page)
 
@@ -778,5 +784,5 @@
         """
         if item.find(".") >= 0:
-            param_names = re.split("\.", item)
+            param_names = re.split(r"\.", item)
             model_name = param_names[0]
             ##Assume max len is 3; eg., M0.radius.width
@@ -881,6 +887,6 @@
 
             self.draw_model(model=model, data=data, page_id=uid, smearer=smear,
-                enable1D=enable1D, enable2D=enable2D,
-                qmin=qmin, qmax=qmax, weight=weight)
+                            enable1D=enable1D, enable2D=enable2D,
+                            qmin=qmin, qmax=qmax, weight=weight)
 
     def draw_model(self, model, page_id, data=None, smearer=None,
@@ -925,16 +931,16 @@
             ## draw model 2D with no initial data
             self._draw_model2D(model=model,
-                                page_id=page_id,
-                                data=data,
-                                enable2D=enable2D,
-                                smearer=smearer,
-                                qmin=qmin,
-                                qmax=qmax,
-                                fid=fid,
-                                weight=weight,
-                                state=state,
-                                toggle_mode_on=toggle_mode_on,
-                                update_chisqr=update_chisqr,
-                                source=source)
+                               page_id=page_id,
+                               data=data,
+                               enable2D=enable2D,
+                               smearer=smearer,
+                               qmin=qmin,
+                               qmax=qmax,
+                               fid=fid,
+                               weight=weight,
+                               state=state,
+                               toggle_mode_on=toggle_mode_on,
+                               update_chisqr=update_chisqr,
+                               source=source)
 
     def onFit(self, uid):
@@ -945,5 +951,6 @@
         :param uid: id related to the panel currently calling this fit function.
         """
-        if uid is None: raise RuntimeError("no page to fit") # Should never happen
+        if uid is None:
+            raise RuntimeError("no page to fit") # Should never happen
 
         sim_page_uid = getattr(self.sim_page, 'uid', None)
@@ -979,6 +986,6 @@
                     page = self.fit_panel.get_page_by_id(page_id)
                     self.set_fit_weight(uid=page.uid,
-                                     flag=page.get_weight_flag(),
-                                     is2d=page._is_2D())
+                                        flag=page.get_weight_flag(),
+                                        is2d=page._is_2D())
                     if not page.param_toFit:
                         msg = "No fitting parameters for %s" % page.window_caption
@@ -1004,7 +1011,7 @@
                             fitter = sim_fitter
                         self._add_problem_to_fit(fitproblem=fitproblem,
-                                             pars=pars,
-                                             fitter=fitter,
-                                             fit_id=fit_id)
+                                                 pars=pars,
+                                                 fitter=fitter,
+                                                 fit_id=fit_id)
                         fit_id += 1
                     list_page_id.append(page_id)
@@ -1053,10 +1060,10 @@
             ## Perform more than 1 fit at the time
             calc_fit = FitThread(handler=handler,
-                                    fn=fitter_list,
-                                    batch_inputs=batch_inputs,
-                                    batch_outputs=batch_outputs,
-                                    page_id=list_page_id,
-                                    updatefn=handler.update_fit,
-                                    completefn=self._fit_completed)
+                                 fn=fitter_list,
+                                 batch_inputs=batch_inputs,
+                                 batch_outputs=batch_outputs,
+                                 page_id=list_page_id,
+                                 updatefn=handler.update_fit,
+                                 completefn=self._fit_completed)
         #self.fit_thread_list[current_page_id] = calc_fit
         self.fit_thread_list[uid] = calc_fit
@@ -1119,5 +1126,5 @@
                 evt = StatusEvent(status=msg, info="warning")
                 wx.PostEvent(self.parent, evt)
-        except:
+        except Exception:
             msg = "Creating Fit page: %s" % sys.exc_value
             wx.PostEvent(self.parent, StatusEvent(status=msg, info="error"))
@@ -1143,8 +1150,8 @@
                     group_id = str(page.uid) + " Model1D"
                     wx.PostEvent(self.parent,
-                             NewPlotEvent(group_id=group_id,
-                                               action="delete"))
+                                 NewPlotEvent(group_id=group_id,
+                                              action="delete"))
                     self.parent.update_data(prev_data=theory_data,
-                                             new_data=data)
+                                            new_data=data)
             else:
                 if theory_data is not None:
@@ -1152,8 +1159,8 @@
                     data.group_id = theory_data.group_id
                     wx.PostEvent(self.parent,
-                             NewPlotEvent(group_id=group_id,
-                                               action="delete"))
+                                 NewPlotEvent(group_id=group_id,
+                                              action="delete"))
                     self.parent.update_data(prev_data=theory_data,
-                                             new_data=data)
+                                            new_data=data)
         self.store_data(uid=page.uid, data_list=page.get_data_list(),
                         caption=page.window_caption)
@@ -1557,11 +1564,11 @@
                     except KeyboardInterrupt:
                         fit_msg += "\nSingular point: Fitting stopped."
-                    except:
+                    except Exception:
                         fit_msg += "\nSingular point: Fitting error occurred."
                 if fit_msg:
-                   evt = StatusEvent(status=fit_msg, info="warning", type="stop")
-                   wx.PostEvent(self.parent, evt)
-
-        except:
+                    evt = StatusEvent(status=fit_msg, info="warning", type="stop")
+                    wx.PostEvent(self.parent, evt)
+
+        except Exception:
             msg = ("Fit completed but the following error occurred: %s"
                    % sys.exc_value)
@@ -1721,5 +1728,5 @@
                                                   fid=data.id)
         self.parent.update_theory(data_id=data.id, theory=new_plot,
-                                   state=state)
+                                  state=state)
         return new_plot
 
@@ -1775,18 +1782,17 @@
 
         self.page_finder[page_id].set_theory_data(data=new_plot,
-                                                      fid=data.id)
+                                                  fid=data.id)
         if toggle_mode_on:
             wx.PostEvent(self.parent,
                          NewPlotEvent(group_id=str(page_id) + " Model2D",
-                                          action="Hide"))
+                                      action="Hide"))
         else:
             if update_chisqr:
-                wx.PostEvent(current_pg,
-                             Chi2UpdateEvent(output=self._cal_chisqr(
-                                                                data=data,
-                                                                fid=fid,
-                                                                weight=weight,
-                                                                page_id=page_id,
-                                                                index=index)))
+                output = self._cal_chisqr(data=data,
+                                          fid=fid,
+                                          weight=weight,
+                                          page_id=page_id,
+                                          index=index)
+                wx.PostEvent(current_pg, Chi2UpdateEvent(output=output))
             else:
                 self._plot_residuals(page_id=page_id, data=data, fid=fid,
@@ -1796,5 +1802,5 @@
             logger.error("Using the present parameters the model does not return any finite value. ")
             msg = "Computing Error: Model did not return any finite value."
-            wx.PostEvent(self.parent, StatusEvent(status = msg, info="error"))
+            wx.PostEvent(self.parent, StatusEvent(status=msg, info="error"))
         else:
             msg = "Computation  completed!"
@@ -1821,6 +1827,6 @@
 
     def _complete2D(self, image, data, model, page_id, elapsed, index, qmin,
-                qmax, fid=None, weight=None, toggle_mode_on=False, state=None,
-                     update_chisqr=True, source='model', plot_result=True):
+                    qmax, fid=None, weight=None, toggle_mode_on=False, state=None,
+                    update_chisqr=True, source='model', plot_result=True):
         """
         Complete get the result of modelthread and create model 2D
@@ -1863,32 +1869,31 @@
                                                   fid=data.id)
         self.parent.update_theory(data_id=data.id,
-                                       theory=new_plot,
-                                       state=state)
+                                  theory=new_plot,
+                                  state=state)
         current_pg = self.fit_panel.get_page_by_id(page_id)
         title = new_plot.title
         if not source == 'fit' and plot_result:
-            wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot,
-                                               title=title))
+            wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title=title))
         if toggle_mode_on:
             wx.PostEvent(self.parent,
-                             NewPlotEvent(group_id=str(page_id) + " Model1D",
-                                               action="Hide"))
+                         NewPlotEvent(group_id=str(page_id) + " Model1D",
+                                      action="Hide"))
         else:
             # Chisqr in fitpage
             if update_chisqr:
-                wx.PostEvent(current_pg,
-                             Chi2UpdateEvent(output=self._cal_chisqr(data=data,
-                                                                    weight=weight,
-                                                                    fid=fid,
-                                                         page_id=page_id,
-                                                         index=index)))
+                output = self._cal_chisqr(data=data,
+                                          weight=weight,
+                                          fid=fid,
+                                          page_id=page_id,
+                                          index=index)
+                wx.PostEvent(current_pg, Chi2UpdateEvent(output=output))
             else:
                 self._plot_residuals(page_id=page_id, data=data, fid=fid,
-                                      index=index, weight=weight)
+                                     index=index, weight=weight)
 
         if not number_finite:
             logger.error("Using the present parameters the model does not return any finite value. ")
             msg = "Computing Error: Model did not return any finite value."
-            wx.PostEvent(self.parent, StatusEvent(status = msg, info="error"))
+            wx.PostEvent(self.parent, StatusEvent(status=msg, info="error"))
         else:
             msg = "Computation  completed!"
@@ -1906,5 +1911,5 @@
                       weight=None,
                       toggle_mode_on=False,
-                       update_chisqr=True, source='model'):
+                      update_chisqr=True, source='model'):
         """
         draw model in 2D
@@ -1952,9 +1957,7 @@
     def _draw_model1D(self, model, page_id, data,
                       qmin, qmax, smearer=None,
-                state=None,
-                weight=None,
-                fid=None,
-                toggle_mode_on=False, update_chisqr=True, source='model',
-                enable1D=True):
+                      state=None, weight=None, fid=None,
+                      toggle_mode_on=False, update_chisqr=True, source='model',
+                      enable1D=True):
         """
         Draw model 1D from loaded data1D
