Index: src/sas/sasgui/perspectives/fitting/basepage.py
===================================================================
--- src/sas/sasgui/perspectives/fitting/basepage.py	(revision 33dc18f1612814b89c60662f4f734c0d8831ac72)
+++ src/sas/sasgui/perspectives/fitting/basepage.py	(revision 33dc18f1612814b89c60662f4f734c0d8831ac72)
@@ -6,6 +6,4 @@
 import sys
 import os
-import wx
-import numpy as np
 import time
 import copy
@@ -14,9 +12,19 @@
 import logging
 import traceback
-
 from collections import defaultdict
+
+import numpy as np
+
+import wx
 from wx.lib.scrolledpanel import ScrolledPanel
 
+from sasmodels.sasview_model import MultiplicationModel
 from sasmodels.weights import MODELS as POLYDISPERSITY_MODELS
+from sasmodels.weights import GaussianDispersion
+
+from sas.sascalc.dataloader.data_info import Detector
+from sas.sascalc.dataloader.data_info import Source
+from sas.sascalc.fit.pagestate import PageState
+from sas.sascalc.fit.models import PLUGIN_NAME_BASE
 
 from sas.sasgui.guiframe.panel_base import PanelBase
@@ -30,9 +38,9 @@
 from sas.sasgui.guiframe.dataFitting import check_data_validity
 from sas.sasgui.guiframe.gui_style import GUIFRAME_ID
-from sas.sascalc.dataloader.data_info import Detector
-from sas.sascalc.dataloader.data_info import Source
-from sas.sasgui.perspectives.fitting.pagestate import PageState
 from sas.sasgui.guiframe.CategoryInstaller import CategoryInstaller
 from sas.sasgui.guiframe.documentation_window import DocumentationWindow
+
+from .report_dialog import ReportDialog
+from .utils import get_weight
 
 logger = logging.getLogger(__name__)
@@ -153,5 +161,5 @@
         self.disp_cb_dict = {}
 
-        # self.state = PageState(parent=parent)
+        # self.state = PageState()
         # dictionary containing list of models
         self.model_list_box = {}
@@ -200,6 +208,5 @@
         self.fitrange = True
         # Create memento to save the current state
-        self.state = PageState(parent=self.parent,
-                               model=self.model, data=self.data)
+        self.state = PageState(model=self.model, data=self.data)
         # flag to determine if state has change
         self.state_change = False
@@ -282,5 +289,5 @@
         """
         x = np.linspace(start=self.qmin_x, stop=self.qmax_x,
-                           num=self.npts_x, endpoint=True)
+                        num=self.npts_x, endpoint=True)
         self.data = Data1D(x=x)
         self.data.xaxis('\\rm{Q}', "A^{-1}")
@@ -308,5 +315,5 @@
 
         x = np.logspace(start=qmin, stop=qmax,
-                           num=self.npts_x, endpoint=True, base=10.0)
+                        num=self.npts_x, endpoint=True, base=10.0)
         self.data = Data1D(x=x)
         self.data.xaxis('\\rm{Q}', "A^{-1}")
@@ -497,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):
@@ -520,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"])
@@ -619,6 +624,50 @@
         # Get plot image from plotpanel
         images, canvases = self.get_images()
-        # get the report dialog
-        self.state.report(images, canvases)
+        imgRAM, images, refs = self._build_plots_for_report(images, canvases)
+
+        # get the strings for report
+        report_str, text_str = self.state.report(fig_urls=refs)
+
+        # Show the dialog
+        report_list = [report_str, text_str, images]
+        dialog = ReportDialog(report_list, None, wx.ID_ANY, "")
+        dialog.Show()
+
+    def _build_plots_for_report(self, figs, canvases):
+        """
+        Build image state that wx.html understand
+        by plotting, putting it into wx.FileSystem image object
+        """
+        images = []
+        refs = []
+
+        # For no figures in the list, prepare empty plot
+        if figs is None or len(figs) == 0:
+            figs = [None]
+
+        # Loop over the list of figures
+        # use wx.MemoryFSHandler
+        imgRAM = wx.MemoryFSHandler()
+        for fig in figs:
+            if fig is not None:
+                ind = figs.index(fig)
+                canvas = canvases[ind]
+
+            # store the image in wx.FileSystem Object
+            wx.FileSystem.AddHandler(wx.MemoryFSHandler())
+
+            # index of the fig
+            ind = figs.index(fig)
+
+            # AddFile, image can be retrieved with 'memory:filename'
+            name = 'img_fit%s.png' % ind
+            refs.append('memory:' + name)
+            imgRAM.AddFile(name, canvas.bitmap, wx.BITMAP_TYPE_PNG)
+
+            # append figs
+            images.append(fig)
+
+        return imgRAM, images, refs
+
 
     def on_save(self, event):
@@ -842,5 +891,4 @@
             self.state.disable_disp = self.disable_disp.GetValue()
 
-        self.state.smearer = copy.deepcopy(self.current_smearer)
         if hasattr(self, "enable_smearer"):
             self.state.enable_smearer = \
@@ -858,5 +906,5 @@
         if len(self._disp_obj_dict) > 0:
             for k, v in self._disp_obj_dict.iteritems():
-                self.state._disp_obj_dict[k] = v.type
+                self.state.disp_obj_dict[k] = v.type
 
             self.state.values = copy.deepcopy(self.values)
@@ -876,5 +924,5 @@
                                     self.state.str_parameters)
         self._copy_parameters_state(self.orientation_params,
-                                     self.state.orientation_params)
+                                    self.state.orientation_params)
         self._copy_parameters_state(self.orientation_params_disp,
                                     self.state.orientation_params_disp)
@@ -907,5 +955,4 @@
             self.state.disable_disp = self.disable_disp.GetValue()
 
-        self.state.smearer = copy.deepcopy(self.current_smearer)
         if hasattr(self, "enable_smearer"):
             self.state.enable_smearer = \
@@ -931,9 +978,9 @@
                         try:
                             self.state.disp_cb_dict[k] = v.GetValue()
-                        except:
+                        except Exception:
                             self.state.disp_cb_dict[k] = None
             if len(self._disp_obj_dict) > 0:
                 for k, v in self._disp_obj_dict.iteritems():
-                    self.state._disp_obj_dict[k] = v.type
+                    self.state.disp_obj_dict[k] = v.type
 
             self.state.values = copy.deepcopy(self.values)
@@ -977,5 +1024,5 @@
             # to support older version
             category_pos = int(state.categorycombobox)
-        except:
+        except Exception:
             category_pos = 0
             for ind_cat in range(self.categorybox.GetCount()):
@@ -989,5 +1036,5 @@
             # to support older version
             formfactor_pos = int(state.formfactorcombobox)
-        except:
+        except Exception:
             formfactor_pos = 0
             for ind_form in range(self.formfactorbox.GetCount()):
@@ -1002,5 +1049,5 @@
             # to support older version
             structfactor_pos = int(state.structurecombobox)
-        except:
+        except Exception:
             structfactor_pos = 0
             for ind_struct in range(self.structurebox.GetCount()):
@@ -1169,13 +1216,12 @@
         self.categorybox.Select(category_pos)
         self._show_combox(None)
-        from models import PLUGIN_NAME_BASE
-        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
@@ -1187,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
@@ -1239,5 +1285,5 @@
             self.dI_sqrdata.SetValue(state.dI_sqrdata)
             self.dI_idata.SetValue(state.dI_idata)
-        except:
+        except Exception:
             # to support older state file formats
             self.dI_noweight.SetValue(False)
@@ -1295,5 +1341,5 @@
         self.weights = copy.deepcopy(state.weights)
 
-        for key, disp_type in state._disp_obj_dict.iteritems():
+        for key, disp_type in state.disp_obj_dict.iteritems():
             # disp_model = disp
             disp_model = POLYDISPERSITY_MODELS[disp_type]()
@@ -1529,5 +1575,5 @@
                         try:
                             self.npts_x = float(self.Npts_total.GetValue())
-                        except:
+                        except Exception:
                             flag = False
                             return flag
@@ -1570,8 +1616,7 @@
             return
 
-        for j in range(len(listtorestore)):
+        for item_page in listtorestore:
             for param in statelist:
-                if param[1] == listtorestore[j][1]:
-                    item_page = listtorestore[j]
+                if param[1] == item_page[1]:
                     item_page_info = param
                     if (item_page_info[1] == "theta" or item_page_info[1] ==
@@ -1619,9 +1664,6 @@
         listtorestore = copy.deepcopy(statelist)
 
-        for j in range(len(listtorestore)):
-            item_page = listtorestore[j]
-            item_page_info = statelist[j]
+        for item_page, item_page_info in zip(listtorestore, statelist):
             # change the state of the check box for simple parameters
-
             if item_page[0] is not None:
                 item_page[0].SetValue(format_number(item_page_info[0], True))
@@ -1711,5 +1753,4 @@
                     temp_smear = self.current_smearer
             # compute weight for the current data
-            from sas.sasgui.perspectives.fitting.utils import get_weight
             flag = self.get_weight_flag()
             weight = get_weight(data=self.data, is2d=self._is_2D(), flag=flag)
@@ -1737,5 +1778,4 @@
 
         from sas.sasgui.plottools import Data1D as pf_data1d
-        # from sas.sasgui.perspectives.theory.profile_dialog import SLDPanel
         from sas.sasgui.guiframe.local_perspectives.plotting.profile_dialog \
             import SLDPanel
@@ -1798,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()
@@ -1875,5 +1915,5 @@
                     wx.PostEvent(self.parent, StatusEvent(status=msg))
                     return
-            except:
+            except Exception:
                 tcrtl.SetBackgroundColour("pink")
                 msg = "Model Error: wrong value entered: %s" % sys.exc_info()[1]
@@ -1932,5 +1972,5 @@
                     wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
                     return
-            except:
+            except Exception:
                 tcrtl.SetBackgroundColour("pink")
                 msg = "Model Error: wrong value entered: %s" % sys.exc_info()[1]
@@ -2032,5 +2072,4 @@
 
         if struct_factor is not None:
-            from sasmodels.sasview_model import MultiplicationModel
             self.model = MultiplicationModel(form_factor(self.multi_factor),
                                              struct_factor())
@@ -2110,5 +2149,5 @@
             # q value from qx and qy
             radius = np.sqrt(data.qx_data * data.qx_data +
-                                data.qy_data * data.qy_data)
+                             data.qy_data * data.qy_data)
             # get unmasked index
             index_data = (float(self.qmin.GetValue()) <= radius) & \
@@ -2339,5 +2378,5 @@
         put gaussian dispersity into current model
         """
-        if len(self.param_toFit) > 0:
+        if self.param_toFit:
             for item in self.fittable_param:
                 if item in self.param_toFit:
@@ -2354,12 +2393,7 @@
         self.weights = {}
 
-        # from sas.models.dispersion_models import GaussianDispersion
-        from sasmodels.weights import GaussianDispersion
-        if len(self.disp_cb_dict) == 0:
-            self.save_current_state()
+        if not self.disp_cb_dict:
             self.sizer4_4.Clear(True)
-            self.Layout()
-            return
-        if (len(self.disp_cb_dict) > 0):
+        else:
             for p in self.disp_cb_dict:
                 # The parameter was un-selected.
@@ -2434,5 +2468,5 @@
                 self._disp_obj_dict[name1] = disp_model
                 self.model.set_dispersion(param_name, disp_model)
-                self.state._disp_obj_dict[name1] = disp_model.type
+                self.state.disp_obj_dict[name1] = disp_model.type
 
                 value1 = str(format_number(self.model.getParam(name1), True))
@@ -2549,5 +2583,5 @@
         self._disp_obj_dict[name] = disp
         self.model.set_dispersion(name.split('.')[0], disp)
-        self.state._disp_obj_dict[name] = disp.type
+        self.state.disp_obj_dict[name] = disp.type
         self.values[name] = values
         self.weights[name] = weights
@@ -2907,20 +2941,20 @@
             # go through the parameters
             strings = self._get_copy_helper(self.parameters,
-                                           self.orientation_params)
+                                            self.orientation_params)
             content += strings
 
             # go through the fittables
             strings = self._get_copy_helper(self.fittable_param,
-                                           self.orientation_params_disp)
+                                            self.orientation_params_disp)
             content += strings
 
             # go through the fixed params
             strings = self._get_copy_helper(self.fixed_param,
-                                           self.orientation_params_disp)
+                                            self.orientation_params_disp)
             content += strings
 
             # go through the str params
             strings = self._get_copy_helper(self.str_parameters,
-                                           self.orientation_params)
+                                            self.orientation_params)
             content += strings
             return content
@@ -2935,5 +2969,5 @@
         the names of parameters that have been fitted
 
-        :returns: all_params - A list of all parameters, in the format of 
+        :returns: all_params - A list of all parameters, in the format of
         self.parameters
         :returns: fitted_par_names - A list of the names of parameters that have
@@ -3012,5 +3046,5 @@
                 # Only print errors for fitted parameters
                 content += param[4].GetValue()
-                content += tab 
+                content += tab
 
         return content
@@ -3032,7 +3066,7 @@
             # Do nothing if self.parameters doesn't exist
             return False
-        
-        content = '\\begin{table}'
-        content += '\\begin{tabular}[h]'
+
+        content = r'\begin{table}'
+        content += r'\begin{tabular}[h]'
 
         crlf = chr(13) + chr(10)
@@ -3044,5 +3078,5 @@
         for param in all_params:
             content += 'l|l|'
-        content += '}\hline'
+        content += r'}\hline'
         content += crlf
 
@@ -3050,13 +3084,13 @@
         for index, param in enumerate(all_params):
             name = param[1] # Parameter name
-            content += name.replace('_', '\_')  # Escape underscores
+            content += name.replace('_', r'\_')  # Escape underscores
             if name in fitted_param_names:
                 # Only print errors for fitted parameters
                 content += ' & '
-                content += name.replace('_', '\_') + "\_err"
+                content += name.replace('_', r'\_') + r"\_err"
             if index < len(all_params) - 1:
                 content += ' & '
 
-        content += '\\\\ \\hline'
+        content += r'\\ \hline'
         content += crlf
 
@@ -3077,9 +3111,9 @@
             if index < len(all_params) - 1:
                 content += ' & '
-        
-        content += '\\\\ \\hline'
+
+        content += r'\\ \hline'
         content += crlf
-        content += '\\end{tabular}'
-        content += '\\end{table}'
+        content += r'\end{tabular}'
+        content += r'\end{table}'
 
         return content
@@ -3338,5 +3372,5 @@
                                 if name.endswith('.npts'):
                                     pd = int(pd)
-                            except:
+                            except Exception:
                                 # continue
                                 if not pd and pd != '':
@@ -3424,5 +3458,5 @@
                 self._disp_obj_dict[name] = disp_model
                 self.model.set_dispersion(param_name, disp_model)
-                self.state._disp_obj_dict[name] = disp_model.type
+                self.state.disp_obj_dict[name] = disp_model.type
                 # TODO: It's not an array, why update values and weights?
                 self.model._persistency_dict[param_name] = \
@@ -3524,5 +3558,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]
@@ -3530,7 +3564,7 @@
 
         else:
-            for (model, enabled) in sorted(self.master_category_dict[category],
-                                           key=lambda name: name[0]):
-                if(enabled):
+            for model, enabled in sorted(self.master_category_dict[category],
+                                         key=lambda name: name[0]):
+                if enabled:
                     self.model_box.Append(model)
 
@@ -3773,10 +3807,10 @@
         self.Bind(wx.EVT_SET_FOCUS, self._on_set_focus)
         self.Bind(wx.EVT_KILL_FOCUS, self._silent_kill_focus
-        if kill_focus_callback is None else kill_focus_callback)
+                  if kill_focus_callback is None else kill_focus_callback)
         self.Bind(wx.EVT_TEXT_ENTER, parent._onparamEnter
-        if text_enter_callback is None else text_enter_callback)
+                  if text_enter_callback is None else text_enter_callback)
         if not ON_MAC:
             self.Bind(wx.EVT_LEFT_UP, self._highlight_text
-            if mouse_up_callback is None else mouse_up_callback)
+                      if mouse_up_callback is None else mouse_up_callback)
 
     def _on_set_focus(self, event):
