Index: guiframe/data_manager.py
===================================================================
--- guiframe/data_manager.py	(revision 03482452ef9a332c6837e62eff2ce71d9d608371)
+++ guiframe/data_manager.py	(revision a07e72f8fb8281178797bf691b36e56274d8580b)
@@ -23,4 +23,6 @@
 from sans.guiframe.dataFitting import Data2D
   
+import wx
+
 class DataManager(object):
     """
@@ -77,7 +79,7 @@
         new_plot.interactive = True
         ## when 2 data have the same id override the 1 st plotted
-        new_plot.id = name
+        new_plot.id = wx.NewId()
         ##group_id specify on which panel to plot this data
-        new_plot.group_id = name
+        new_plot.group_id = [wx.NewId()]
         new_plot.is_data = True
         new_plot.path = path
@@ -151,5 +153,4 @@
         """
         """
-        print "append theory", self.stored_data, data_id
         if data_id in self.stored_data:
             data_state = self.stored_data[data_id]
Index: guiframe/events.py
===================================================================
--- guiframe/events.py	(revision 4e4d3bb9dfac5ed6bc357291ff34909ee62258d6)
+++ guiframe/events.py	(revision a07e72f8fb8281178797bf691b36e56274d8580b)
@@ -16,6 +16,4 @@
 #slicer event
 (SlicerEvent, EVT_SLICER)   = wx.lib.newevent.NewEvent()
-# event containinG A DICTIONARY OF NAME and errors of selected data
-(ErrorDataEvent, ERR_DATA) = wx.lib.newevent.NewEvent()
 ## event that that destroy a page associate with Data1D removed from the graph
 (RemoveDataEvent, EVT_REMOVE_DATA)   = wx.lib.newevent.NewEvent()
Index: guiframe/gui_manager.py
===================================================================
--- guiframe/gui_manager.py	(revision 4c0572f4279d3b0619eca4d40af9f5266816f5be)
+++ guiframe/gui_manager.py	(revision a07e72f8fb8281178797bf691b36e56274d8580b)
@@ -431,5 +431,5 @@
                                   Hide())        
       
-    def get_context_menu(self, graph=None):
+    def get_context_menu(self, plotpanel=None):
         """
         Get the context menu items made available 
@@ -437,8 +437,9 @@
         This function is used by the plotting module
         """
+        if plotpanel is None:
+            return
         menu_list = []
         for item in self.plugins:
-            if hasattr(item, "get_context_menu"):
-                menu_list.extend(item.get_context_menu(graph))
+            menu_list.extend(item.get_context_menu(plotpanel=plotpanel))
         return menu_list
         
Index: guiframe/gui_style.py
===================================================================
--- guiframe/gui_style.py	(revision 59a4a750b2260e7dcd390d2d458e9caf2db169a6)
+++ guiframe/gui_style.py	(revision a07e72f8fb8281178797bf691b36e56274d8580b)
@@ -33,4 +33,5 @@
     PRINT_ID = wx.NewId()
     CURRENT_APPLICATION = wx.NewId()
+    CURVE_SYMBOL_NUM = 13
     
 class GUIFRAME_ICON:
Index: guiframe/local_perspectives/plotting/Plotter1D.py
===================================================================
--- guiframe/local_perspectives/plotting/Plotter1D.py	(revision a45037aaea7a183fd246884e3c1340d9e6d7fb7a)
+++ guiframe/local_perspectives/plotting/Plotter1D.py	(revision a07e72f8fb8281178797bf691b36e56274d8580b)
@@ -18,5 +18,5 @@
 import numpy
 import time
-import danse.common.plottools
+
 from danse.common.plottools.PlotPanel import PlotPanel
 from danse.common.plottools.plottables import Graph
@@ -26,5 +26,4 @@
 from sans.guiframe.events import NewPlotEvent
 from sans.guiframe.events import SlicerEvent
-from sans.guiframe.events import ErrorDataEvent
 from sans.guiframe.events import RemoveDataEvent
 from sans.guiframe.events import PanelOnFocusEvent
@@ -32,5 +31,4 @@
 from sans.guiframe.utils import PanelMenu
 from sans.guiframe.dataFitting import Data1D
-from sans.guiframe.dataFitting import Theory1D
 from sans.guiframe.panel_base import PanelBase
 from binder import BindArtist
@@ -67,16 +65,13 @@
         #context menu
         self._slicerpop = None
-        self._menu_add_plot = None
+        
         self._available_data = []
         self._menu_add_ids = []
-        ## save errors dy  for each data plotted
-        self.err_dy = {}
-        ## flag to determine if the hide or show context menu item should
-        ## be displayed
-        self.errors_hide = False
+        self._symbol_labels = self.get_symbol_label()
+      
+        self.hide_menu = None
         ## Unique ID (from gui_manager)
         self.uid = None
-        ## Action IDs for internal call-backs
-        self.action_ids = {}
+       
         ## Default locations
         self._default_save_location = os.getcwd()        
@@ -86,34 +81,41 @@
         self.graph.yaxis("\\rm{Intensity} ", "cm^{-1}")
         self.graph.render(self)
-        #bind with event to contrst context menu
-        #self.parent.Bind(EVT_NEW_LOADED_DATA, self._add_new_plot)
-        
-    def on_add_new_plot(self, event):
-        """
-        """
-    def _add_new_plot(self, event):
-        """
-        Construct the context menu given available data
-        """
-        self._available_data = event.data_to_add
+       
+    def get_symbol_label(self):
+        """
+        Associates label to symbol
+        """
+        _labels = {}
+        i = 0
+        _labels['Points'] = i
+        i += 1
+        _labels['X '] = i
+        i += 1
+        _labels['Triangle Down'] = i
+        i += 1
+        _labels['Triangle Up'] = i
+        i += 1
+        _labels['Triangle Left'] = i
+        i += 1
+        _labels['Triangle Right'] = i
+        i += 1
+        _labels['Plus'] = i
+        i += 1
+        _labels['Square'] = i
+        i += 1
+        _labels['Thin Diamond'] = i
+        i += 1
+        _labels['Diamond'] = i
+        i += 1
+        _labels['Hexagon1'] = i
+        i += 1
+        _labels['Hexagon2'] = i
+        i += 1
+        _labels['Pentagon'] = i
+        i += 1
+        _labels['Curve'] = i
+        return _labels
+
     
-    def _fill_menu_add_plot(self):
-        """
-        """
-        if self._menu_add_plot is not None:
-            if self._available_data:
-                for id in self._menu_add_ids:
-                    item = self._menu_add_plot.FindItemById(id)
-                    if item is None:
-                        for data in self._available_data:
-                            new_id = wx.NewId()
-                            hint = 'Add %s to this panel' % str(data.name)
-                            self._menu_add_plot.Append(new_id, str(data.name), hint)
-                            wx.EVT_MENU(self, id, self.on_add_new_plot)
-                            self._menu_add_ids.append(new_id)
-                    else:
-                        if item.GetLabel() == 'No Loaded Data':
-                            self._menu_add_plot.RemoveItem(item)
-                        
     def set_data(self, list=None):
         """
@@ -127,7 +129,28 @@
         self.graph.reset()
         self.plots      = {}
-        self.action_ids = {}
     
-    def _onEVT_1DREPLOT(self, event):
+    def remove_data_by_id(self, id):
+        """'
+        remove data from plot
+        """
+        if id in self.plots.keys():
+            data =  self.plots[id]
+            self.graph.delete(data)
+            data_manager = self._manager.parent.get_data_manager()
+            data_list = data_manager.get_by_id(id_list=[id])
+            data = data_list.values()[0].data
+            data.group_id.remove(self.group_id)
+            del self.plots[id]
+                 
+            self.graph.render(self)
+            self.subplot.figure.canvas.draw_idle()    
+            if len(self.graph.plottables) == 0:
+                print "_onRemove: graph is empty must be destroyed"
+           
+        else:
+            msg = "Attempt to remove an unexisting plot with ID %s " % str(id)
+            raise ValueError, msg
+        
+    def plot_data(self, data):
         """
         Data is ready to be displayed
@@ -135,57 +158,16 @@
         :param event: data event
         """
-        #TODO: Check for existence of plot attribute
-        # Check whether this is a replot. If we ask for a replot
-        # and the plottable no longer exists, ignore the event.
-        if hasattr(event, "update") and event.update == True \
-            and event.plot.name not in self.plots.keys():
-            return
-        if hasattr(event, "reset"):
-            self._reset()
-        # Check whether the plottable is empty
-        is_empty = len(event.plot.x) == 0
-                
-        is_new = True
-        if event.plot.name in self.plots.keys():
-            # If the plottable is empty, just remove the plottable from the graph
-            if is_empty:
-                self.graph.delete(self.plots[event.plot.name])
-                del self.plots[event.plot.name]
-            else:  
-                # Check whether the class of plottable changed
-                if not event.plot.__class__ == self.plots[event.plot.name].__class__:
-                    #overwrite a plottable using the same name
-                    self.graph.delete(self.plots[event.plot.name])              
-                else:
-                    # plottable is already draw on the panel
-                    is_new = False
-        
-        if not is_empty:
-            if is_new:
-                # a new plottable overwrites a plotted one  using the same id
-                for plottable in self.plots.itervalues():
-                    if hasattr(event.plot, "id") and hasattr(plottable, "id"):
-                        #remove the existing plot and same id and same name
-                        if event.plot.id == plottable.id and \
-                            event.plot.name == plottable.name:
-                            self.graph.delete(plottable)
-                
-                self.plots[event.plot.name] = event.plot
-                self.graph.add(self.plots[event.plot.name])
-            else:
-                #replot the graph
-                self.plots[event.plot.name].x = event.plot.x    
-                self.plots[event.plot.name].y = event.plot.y    
-                self.plots[event.plot.name].dy = event.plot.dy  
-                if hasattr(event.plot, 'dx') and \
-                    hasattr(self.plots[event.plot.name], 'dx'):
-                    self.plots[event.plot.name].dx = event.plot.dx    
-          
-        #TODO: Should re-factor this
-        ## for all added plot the option to hide error show be displayed first
-        #self.errors_hide = 0
-        ## Set axis labels
-        self.graph.xaxis(event.plot._xaxis, event.plot._xunit)
-        self.graph.yaxis(event.plot._yaxis, event.plot._yunit)
+        if data.id in self.plots.keys():
+            #replace
+            self.graph.replace(data)
+            self.plots[data.id] = data
+        else:
+            self.plots[data.id] = data
+            self.graph.add(self.plots[data.id]) 
+        
+        x_label, x_unit = data.get_xaxis()
+        y_label, y_unit = data.get_yaxis()
+        self.graph.xaxis(x_unit, x_label)
+        self.graph.yaxis(y_unit, y_label)
         ## Set the view scale for all plots
         self._onEVT_FUNC_PROPERTY()
@@ -193,9 +175,6 @@
         self.graph.render(self)
         self.subplot.figure.canvas.draw_idle()
-        #if self.errors_hide:
-        #    self._on_remove_errors(evt=None)
-        #else:
-        #    self._on_add_errors( evt=None)
-    
+
+       
     def onLeftDown(self,event): 
         """ 
@@ -211,5 +190,20 @@
         #post nd event to notify guiframe that this panel is on focus
         wx.PostEvent(self.parent, PanelOnFocusEvent(panel=self))
-        
+        self._manager.set_panel_on_focus(self)
+        
+    def _ontoggle_hide_error(self, event):
+        """
+        Toggle error display to hide or show
+        """
+        
+        selected_plot = self.plots[self.graph.selected_plottable]
+        if self.hide_menu.GetText() == "Hide Error":
+            selected_plot.hide_error = True
+        else:
+            selected_plot.hide_error = False
+        ## increment graph color
+        self.graph.render(self)
+        self.subplot.figure.canvas.draw_idle()  
+          
     def _onRemove(self, event):
         """
@@ -219,18 +213,14 @@
         
         """
-        selected_plot = self.plots[self.graph.selected_plottable]
         ## Check if there is a selected graph to remove
-        if not self.graph.selected_plottable == None and\
-            self.graph.selected_plottable in self.plots.keys():
-            color = self.graph.plottables[selected_plot]
+        if self.graph.selected_plottable in self.plots.keys():
+            selected_plot = self.plots[self.graph.selected_plottable]
+            id = self.graph.selected_plottable
+            self.remove_data_by_id( id)
             event = RemoveDataEvent(data=selected_plot)
             wx.PostEvent(self.parent, event)
             self.graph.delete(selected_plot)
             del selected_plot
-            ## increment graph color
-            self.graph.color += color
-            self.graph.render(self)
-            self.subplot.figure.canvas.draw_idle()    
-           
+       
     def onContextMenu(self, event):
         """
@@ -253,18 +243,16 @@
         self._slicerpop.Append(id, '&Print Preview', 'image preview for print')
         wx.EVT_MENU(self, id, self.onPrinterPreview)
-        """
-        #add plot
-        self._menu_add_plot = wx.Menu()
-        id = wx.NewId()
-        self._menu_add_plot.Append(id, '&No Loaded Data', 'Add new plot')
-        self._menu_add_plot.FindItemByPosition(0).Enable(False)
-        self._menu_add_ids.append(id)
-        
-        self._slicerpop.AppendSubMenu(self._menu_add_plot, '&Add New Plot')
-        self._fill_menu_add_plot()
-        """
+        
+        symbol_menu = wx.Menu()
+        for label in self._symbol_labels:
+            id = wx.NewId()
+            symbol_menu.Append(id, str(label), str(label))
+            wx.EVT_MENU(self, id, self.onChangeSymbol)
+        id = wx.NewId()
+        self._slicerpop.AppendMenu(id,'&Modify Symbol',  symbol_menu)
         self._slicerpop.AppendSeparator()
+        
         #add menu of other plugins
-        item_list = self.parent.get_context_menu(self.graph)
+        item_list = self.parent.get_context_menu(self)
         if (not item_list == None) and (not len(item_list) == 0):
             for item in item_list:
@@ -275,10 +263,10 @@
                 except:
                     msg = "ModelPanel1D.onContextMenu: "
-                    msg += "bad menu item  %s"%sys.exc_value
+                    msg += "bad menu item  %s" % sys.exc_value
                     wx.PostEvent(self.parent, StatusEvent(status=msg))
                     pass
             self._slicerpop.AppendSeparator()
         id = wx.NewId()
-        self._menu_add_plot = wx.Menu()
+        
         self._slicerpop.Append(id, '&Print image', 'Print image')
         if self.graph.selected_plottable in self.plots:
@@ -287,19 +275,26 @@
             name = plot.name
             self._slicerpop.Append(id, "&Save points")
-            self.action_ids[str(id)] = plot
             wx.EVT_MENU(self, id, self._onSave)
             id = wx.NewId()
             self._slicerpop.Append(id, "Remove %s curve" % name)
-            self.action_ids[str(id)] = plot
             wx.EVT_MENU(self, id, self._onRemove)
+            id = wx.NewId()
+            self.hide_menu = self._slicerpop.Append(id, "Hide Error")
+            if plot.dy is not None or plot.dy != []:
+                if plot.hide_error :
+                    self.hide_menu.SetText('Show Error')
+                else:
+                    self.hide_menu.SetText('Hide Error')
+            else:
+                self.hide_menu.Disable()
+            wx.EVT_MENU(self, id, self._ontoggle_hide_error)
+            
             self._slicerpop.AppendSeparator()
             # Option to hide
             #TODO: implement functionality to hide a plottable (legend click)
-        if self.graph.selected_plottable in self.plots:
-            selected_plot = self.plots[self.graph.selected_plottable]
-            id = wx.NewId()
-            self._slicerpop.Append(id, '&Linear fit')
-            wx.EVT_MENU(self, id, self.onFitting)
-            self._slicerpop.AppendSeparator()
+        id = wx.NewId()
+        self._slicerpop.Append(id, '&Linear Fit')
+        wx.EVT_MENU(self, id, self.onFitting)
+        
         id = wx.NewId()
         self._slicerpop.Append(id, '&Change scale')
@@ -311,149 +306,19 @@
         pos = self.ScreenToClient(pos)
         self.PopupMenu(self._slicerpop, pos)
-        
-    def _on_remove_errors(self, evt):
-        """
-        Save name and dy of data in dictionary self.err_dy
-        Create a new data1D with the same x, y
-        vector and dy with zeros.
-        post self.err_dy as event (ErrorDataEvent) for any object
-        which wants to reconstruct the initial data.
-        
-        :param evt: Menu event
-        
-        """
-        if not self.graph.selected_plottable == None:
-            ## store existing dy
-            name =self.plots[self.graph.selected_plottable].name
-            dy = self.plots[self.graph.selected_plottable].dy
-            self.err_dy[name] = dy
-            ## Create a new dy for a new plottable
-            #import numpy
-            dy = numpy.zeros(len(self.plots[self.graph.selected_plottable].y))
-            selected_plot = self.plots[self.graph.selected_plottable]
-            
-            if selected_plot.__class__.__name__ == "Data1D":
-                # Make sure that we can pass a basic Data1D
-                dxl = None
-                dxw = None
-                if hasattr(selected_plot, "dxl"):
-                    dxl = selected_plot.dxl
-                if hasattr(selected_plot, "dxw"):
-                    dxw = selected_plot.dxw
-                new_plot = Data1D(x=selected_plot.x, y=selected_plot.y,
-                               dx=selected_plot.dx, dy=dy)
-                new_plot.dxl = dxl
-                new_plot.dxw = dxw            
-            else:
-                 new_plot = Theory1D(x=selected_plot.x, 
-                                     y=selected_plot.y, dy=dy)
-            new_plot.interactive = True
-            self.errors_hide = True
-            new_plot.name = self.plots[self.graph.selected_plottable].name 
-            if hasattr(self.plots[self.graph.selected_plottable], "group_id"):
-                new_plot.group_id = self.plots[self.graph.selected_plottable].group_id
-                if hasattr(self.plots[self.graph.selected_plottable], "id"):
-                    new_plot.id = self.plots[self.graph.selected_plottable].id
-                else:
-                    new_plot.id = str(time.time())
-            else:
-                new_plot.group_id = str(time.time())
-                new_plot.id = str(time.time())
-            label, unit = self.plots[self.graph.selected_plottable].get_xaxis()
-            new_plot.xaxis(label, unit)
-            label, unit = self.plots[self.graph.selected_plottable].get_yaxis()
-            new_plot.yaxis(label, unit)
-            ## save the color of the selected plottable before it is deleted
-            color = self.graph.plottables[self.plots[self.graph.selected_plottable]]
-            self.graph.delete(self.plots[self.graph.selected_plottable])
-            ## add newly created plottable to the graph with the save color
-            self.graph.color += color
-            self.graph.add(new_plot, color)
-            ## transforming the view of the new data into the same of 
-            #the previous data
-            self._onEVT_FUNC_PROPERTY()
-            ## save the plot 
-            self.plots[self.graph.selected_plottable] = new_plot
-            ## Render the graph
-            self.graph.render(self)
-            self.subplot.figure.canvas.draw_idle() 
-            event = ErrorDataEvent(err_dy=self.err_dy)
-            wx.PostEvent(self.parent, event)
-
-    def _on_add_errors(self, evt):
-        """
-        create a new data1D witht the errors saved in self.err_dy
-        to show errors of the plot.
-        Compute reasonable errors for a data set without 
-        errors and transorm the plottable to a Data1D
-        
-        :param evt: Menu event
-        
-        """
-        if not self.graph.selected_plottable == None \
-            and self.graph.selected_plottable in self.plots.keys():
-            ##Reset the flag to display the hide option on the context menu
-            self.errors_hide = False
-            ## restore dy 
-            length = len(self.plots[self.graph.selected_plottable].x)
-            dy = numpy.zeros(length)
-            selected_plot = self.plots[self.graph.selected_plottable]
-            try:
-                dy = self.err_dy[selected_plot.name]
-            except:
-                if hasattr(selected_plot, "dy"):
-                    dy = selected_plot.dy
-                else:
-                    dy = numpy.zeros(selected_plot.dy)
-            ## Create a new plottable data1D
-            if selected_plot.__class__.__name__ == "Data1D":
-                # Make sure that we can pass a basic Data1D
-                dxl = None
-                dxw = None
-                if hasattr(selected_plot, "dxl"):
-                    dxl = selected_plot.dxl
-                if hasattr(selected_plot, "dxw"):
-                    dxw = selected_plot.dxw
-                new_plot = Data1D(x=selected_plot.x,
-                                               y=selected_plot.y,
-                                               dx=selected_plot.dx,
-                                               dy=dy)
-                new_plot.dxl = dxl
-                new_plot.dxw = dxw                       
-            else:
-                ## Create a new plottable Theory1D
-                new_plot = Theory1D(x=selected_plot.x, y=selected_plot.y, dy=dy)
-            new_plot.interactive = True
-            new_plot.name = self.plots[self.graph.selected_plottable].name 
-            if hasattr(self.plots[self.graph.selected_plottable], "group_id"):
-                new_plot.group_id = self.plots[self.graph.selected_plottable].group_id
-                if hasattr(self.plots[self.graph.selected_plottable],"id"):
-                    new_plot.id = self.plots[self.graph.selected_plottable].id
-                else:
-                    new_plot.id = str(time.time())
-            else:
-                new_plot.group_id = str(time.time())
-                new_plot.id = str(time.time())
-            
-            label, unit = self.plots[self.graph.selected_plottable].get_xaxis()
-            new_plot.xaxis(label, unit)
-            label, unit = self.plots[self.graph.selected_plottable].get_yaxis()
-            new_plot.yaxis(label, unit)
-            ## save the color of the selected plottable before it is deleted
-            color=self.graph.plottables[self.plots[self.graph.selected_plottable]]
-            self.graph.delete(self.plots[self.graph.selected_plottable])
-            self.graph.color += color
-            ## add newly created plottable to the graph with the save color
-            self.graph.add(new_plot, color)
-            ## transforming the view of the new data into the same of
-            # the previous data
-            self._onEVT_FUNC_PROPERTY()
-            ## save the plot 
-            self.plots[self.graph.selected_plottable]=new_plot
-            ## render the graph with its new content
-            self.graph.render(self)
-            self.subplot.figure.canvas.draw_idle() 
-               
-               
+     
+    def onChangeSymbol(self, event):
+        """
+        """
+        menu = event.GetEventObject()
+        id = event.GetId()
+        label =  menu.GetLabel(id)
+        selected_plot = self.plots[self.graph.selected_plottable]
+        selected_plot.symbol = self._symbol_labels[label]
+        ## Set the view scale for all plots
+        self._onEVT_FUNC_PROPERTY()
+        ## render the graph
+        self.graph.render(self)
+        self.subplot.figure.canvas.draw_idle()
+        
     def _onsaveTXT(self, path):
         """
@@ -503,36 +368,34 @@
         
         """
-        id = str(evt.GetId())
-        if id in self.action_ids:         
+       
+        path = None
+        wildcard = "Text files (*.txt)|*.txt|"\
+        "CanSAS 1D files(*.xml)|*.xml" 
+        dlg = wx.FileDialog(self, "Choose a file",
+                            self._default_save_location,
+                             "", wildcard , wx.SAVE)
+       
+        if dlg.ShowModal() == wx.ID_OK:
+            path = dlg.GetPath()
+            mypath = os.path.basename(path)
             
-            path = None
-            wildcard = "Text files (*.txt)|*.txt|"\
-            "CanSAS 1D files(*.xml)|*.xml" 
-            dlg = wx.FileDialog(self, "Choose a file",
-                                self._default_save_location,
-                                 "", wildcard , wx.SAVE)
-           
-            if dlg.ShowModal() == wx.ID_OK:
-                path = dlg.GetPath()
-                mypath = os.path.basename(path)
-                
-                #TODO: This is bad design. The DataLoader is designed 
-                #to recognize extensions.
-                # It should be a simple matter of calling the .
-                #save(file, data, '.xml') method
-                # of the DataLoader.loader.Loader class.
-                from DataLoader.loader import  Loader
-                #Instantiate a loader 
-                loader = Loader() 
-                data = self.plots[self.graph.selected_plottable]
-                format = ".txt"
-                if os.path.splitext(mypath)[1].lower() == format:
-                     self._onsaveTXT( path)
-                format = ".xml"
-                if os.path.splitext(mypath)[1].lower() == format:
-                    loader.save(path, data, format)
-                try:
-                    self._default_save_location = os.path.dirname(path)
-                except:
-                    pass    
-            dlg.Destroy()
+            #TODO: This is bad design. The DataLoader is designed 
+            #to recognize extensions.
+            # It should be a simple matter of calling the .
+            #save(file, data, '.xml') method
+            # of the DataLoader.loader.Loader class.
+            from DataLoader.loader import  Loader
+            #Instantiate a loader 
+            loader = Loader() 
+            data = self.plots[self.graph.selected_plottable]
+            format = ".txt"
+            if os.path.splitext(mypath)[1].lower() == format:
+                 self._onsaveTXT( path)
+            format = ".xml"
+            if os.path.splitext(mypath)[1].lower() == format:
+                loader.save(path, data, format)
+            try:
+                self._default_save_location = os.path.dirname(path)
+            except:
+                pass    
+        dlg.Destroy()
Index: guiframe/local_perspectives/plotting/Plotter2D.py
===================================================================
--- guiframe/local_perspectives/plotting/Plotter2D.py	(revision ed8ad21a74d5f0d78aa329ebf8db2fd13f46d510)
+++ guiframe/local_perspectives/plotting/Plotter2D.py	(revision a07e72f8fb8281178797bf691b36e56274d8580b)
@@ -23,4 +23,5 @@
 from sans.guiframe.events import StatusEvent 
 from sans.guiframe.events import NewPlotEvent
+from sans.guiframe.events import PanelOnFocusEvent
 from sans.guiframe.events import SlicerEvent
 from sans.guiframe.utils import PanelMenu
@@ -122,4 +123,20 @@
         self.default_zmax_ctl = self.zmax_2D
        
+    def onLeftDown(self, event): 
+        """
+        left button down and ready to drag
+        
+        """
+        # Check that the LEFT button was pressed
+        if event.button == 1:
+            self.leftdown = True
+            ax = event.inaxes
+            if ax != None:
+                self.xInit, self.yInit = event.xdata, event.ydata
+        self.plottable_selected(self.data2D.id)
+       
+        self._manager.set_panel_on_focus(self)
+        wx.PostEvent(self.parent, PanelOnFocusEvent(panel=self))
+        
     def add_toolbar(self):
         """
@@ -138,5 +155,5 @@
         self.toolbar.update()
          
-    def _onEVT_1DREPLOT(self, event):
+    def plot_data(self, data):
         """
         Data is ready to be displayed
@@ -149,51 +166,14 @@
         """
         ## Update self.data2d with the current plot
-        self.data2D = event.plot
-        
-        #TODO: Check for existence of plot attribute
-        
-        # Check whether this is a replot. If we ask for a replot
-        # and the plottable no longer exists, ignore the event.
-        if hasattr(event, "update") and event.update == True \
-            and event.plot.name not in self.plots.keys():
-            return
-        if hasattr(event, "reset"):
-            self._reset()
-        is_new = True
-        if event.plot.name in self.plots.keys():
-            # Check whether the class of plottable changed
-            if not event.plot.__class__ == self.plots[event.plot.name].__class__:
-                #overwrite a plottable using the same name
-                self.graph.delete(self.plots[event.plot.name])
-            else:
-                # plottable is already draw on the panel
-                is_new = False
-           
-        if is_new:
-            # a new plottable overwrites a plotted one  using the same id
-            for plottable in self.plots.itervalues():
-                if hasattr(event.plot,"id"):
-                    if event.plot.id == plottable.id:
-                        self.graph.delete(plottable)
-            
-            self.plots[event.plot.name] = event.plot
-            self.graph.add(self.plots[event.plot.name])
+        self.data2D = data
+        if data.id in self.plots.keys():
+            #replace
+            self.graph.replace(data)
+            self.plots[data.id] = data
         else:
-            # Update the plottable with the new data
-            
-            #TODO: we should have a method to do this, 
-            #      something along the lines of:
-            #      plottable1.update_data_from_plottable(plottable2)
-            
-            self.plots[event.plot.name].xmin = event.plot.xmin
-            self.plots[event.plot.name].xmax = event.plot.xmax
-            self.plots[event.plot.name].ymin = event.plot.ymin
-            self.plots[event.plot.name].ymax = event.plot.ymax
-            self.plots[event.plot.name].data = event.plot.data
-            self.plots[event.plot.name].qx_data = event.plot.qx_data
-            self.plots[event.plot.name].qy_data = event.plot.qy_data
-            self.plots[event.plot.name].err_data = event.plot.err_data
+            self.plots[data.id] = data
+            self.graph.add(self.plots[data.id]) 
             # update qmax with the new xmax of data plotted
-            self.qmax = event.plot.xmax
+            self.qmax = data.xmax
             
         self.slicer = None
@@ -204,11 +184,11 @@
         #data2D: put 'Pixel (Number)' for axis title and unit in case of having no detector info and none in _units
         if len(self.data2D.detector) < 1: 
-            if len(event.plot._xunit) < 1 and len(event.plot._yunit) < 1:
-                event.plot._xaxis = '\\rm{x}'
-                event.plot._yaxis = '\\rm{y}'
-                event.plot._xunit = 'pixel'
-                event.plot._yunit = 'pixel'
-        self.graph.xaxis(event.plot._xaxis, event.plot._xunit)
-        self.graph.yaxis(event.plot._yaxis, event.plot._yunit)
+            if len(data._xunit) < 1 and len(data._yunit) < 1:
+                data._xaxis = '\\rm{x}'
+                data._yaxis = '\\rm{y}'
+                data._xunit = 'pixel'
+                data._yunit = 'pixel'
+        self.graph.xaxis(data._xaxis, data._xunit)
+        self.graph.yaxis(data._yaxis, data._yunit)
         self.graph.title(self.data2D.name)
         self.graph.render(self)
@@ -252,5 +232,5 @@
         if len(self.data2D.detector) == 1:        
             
-            item_list = self.parent.get_context_menu(self.graph)
+            item_list = self.parent.get_context_menu(self)
             if (not item_list == None) and (not len(item_list) == 0):
                 for item in item_list:
Index: guiframe/local_perspectives/plotting/plotting.py
===================================================================
--- guiframe/local_perspectives/plotting/plotting.py	(revision bf4402c376f8ead3899ab313483ea9aad7c66042)
+++ guiframe/local_perspectives/plotting/plotting.py	(revision a07e72f8fb8281178797bf691b36e56274d8580b)
@@ -17,4 +17,6 @@
 from sans.guiframe.events import StatusEvent 
 from sans.guiframe.plugin_base import PluginBase
+from sans.guiframe.dataFitting import Data1D
+from sans.guiframe.dataFitting import Data2D
 
 class Plugin(PluginBase):
@@ -28,5 +30,12 @@
         ## Plot panels
         self.plot_panels = []
-       
+        self.new_plot_panels = {}
+        self._panel_on_focus = None
+     
+    def set_panel_on_focus(self, panel):
+        """
+        """
+        self._panel_on_focus = panel
+        
     def is_always_active(self):
         """
@@ -76,66 +85,70 @@
         
         """
+        if hasattr(event, 'remove'):
+            group_id = event.group_id
+            id = event.id
+            if group_id in self.new_plot_panels.keys():
+                panel = self.new_plot_panels[group_id]
+                panel.remove_data_by_id(id=id)
+            else:
+                msg = "Panel with GROUP_ID: %s canot be located" % str(group_id)
+                raise ValueError, msg
+            return
         # Check whether we already have a graph with the same units
         # as the plottable we just received. 
-        is_available = False
-        for panel in self.plot_panels:
-            if event.plot._xunit == panel.graph.prop["xunit_base"] \
-            and event.plot._yunit == panel.graph.prop["yunit_base"]:
-                if hasattr(event.plot, "group_id"):
-                    ## if same group_id used the same panel to plot
-                    if not event.plot.group_id == None \
-                        and event.plot.group_id == panel.group_id:
-                        is_available = True
-                        panel._onEVT_1DREPLOT(event)
-                        self.parent.show_panel(panel.uid)   
-                else:
-                    # Check that the plot panel has no group ID
-                    ## Use a panel with group_id ==None to plot
-                    if panel.group_id == None:
-                        is_available = True
-                        panel._onEVT_1DREPLOT(event)
-                        self.parent.show_panel(panel.uid)
-        # Create a new plot panel if none was available        
-        if not is_available:
-            #print"event.plot",hasattr(event.plot,'data')
-            if not hasattr(event.plot, 'data'):
+        data = event.plot
+        
+        group_id_list = data.group_id
+        group_id = None
+        if group_id_list:
+            group_id = group_id_list[len(group_id_list)-1]
+        if group_id in self.new_plot_panels.keys():
+            panel = self.new_plot_panels[group_id]
+            _, x_unit =  data.get_xaxis()
+            _, y_unit =  data.get_yaxis()
+           
+            if x_unit != panel.graph.prop["xunit"] \
+                or  y_unit != panel.graph.prop["yunit"]:
+                msg = "Cannot add %s" % str(data.name)
+                msg += " to panel %s\n" % str(panel.window_caption)
+                msg += "Please edit %s's units, labels" % str(data.name)
+                raise ValueError, msg
+            else:
+                panel.plot_data( data)
+                self.parent.show_panel(panel.uid)   
+        else:
+            # Create a new plot panel if none was available        
+            if issubclass(data.__class__, Data1D):
                 from Plotter1D import ModelPanel1D
                 ## get the data representation label of the data to plot
                 ## when even the user select "change scale"
-                if hasattr(event.plot, "xtransform"):
-                    xtransform = event.plot.xtransform
-                else:
-                    xtransform = None
-                    
-                if hasattr(event.plot, "ytransform"):
-                    ytransform = event.plot.ytransform
-                else:
-                    ytransform = None
+                xtransform = data.xtransform
+                ytransform = data.ytransform
                 ## create a plotpanel for 1D Data
                 new_panel = ModelPanel1D(self.parent, -1, xtransform=xtransform,
                          ytransform=ytransform, style=wx.RAISED_BORDER)
-            else:
+                new_panel.set_manager(self)
+                new_panel.group_id = group_id
+            elif issubclass(data.__class__, Data2D):
                 ##Create a new plotpanel for 2D data
                 from Plotter2D import ModelPanel2D
-                if hasattr(event.plot, "scale"):
-                    scale = event.plot.scale
-                else:
-                    scale = 'log'
+                scale = data.scale
                 new_panel = ModelPanel2D(self.parent, id = -1,
                                     data2d=event.plot, scale = scale, 
                                     style=wx.RAISED_BORDER)
+                new_panel.set_manager(self)
+                new_panel.group_id = group_id
+            else:
+                msg = "Plotting received unexpected"
+                msg += " data type : %s" % str(data.__class__)
+                raise ValueError, msg
             ## Set group ID if available
             ## Assign data properties to the new create panel
-            group_id_str = ''
-            if hasattr(event.plot, "group_id"):
-                if not event.plot.group_id == None:
-                    new_panel.group_id = event.plot.group_id
-                    group_id_str = ' [%s]' % event.plot.group_id
-            if hasattr(event, "title"):
-                new_panel.window_caption = event.title
-                new_panel.window_name = event.title
+            title = data.title
+            new_panel.window_caption = title
+            new_panel.window_name = title
             event_id = self.parent.popup_panel(new_panel)
             #remove the default item in the menu
-            if len(self.plot_panels) == 0:
+            if len(self.new_plot_panels) == 0:
                 self.menu.RemoveItem(self.menu.FindItemByPosition(0))
             self.menu.Append(event_id, new_panel.window_caption, 
@@ -144,7 +157,8 @@
             new_panel.uid = event_id
             # Ship the plottable to its panel
-            new_panel._onEVT_1DREPLOT(event)
-            self.plot_panels.append(new_panel)        
+            new_panel.plot_data(data)
+            self.plot_panels.append(new_panel)       
+            self.new_plot_panels[new_panel.group_id] = new_panel
             
         return
-        
+   
Index: guiframe/plugin_base.py
===================================================================
--- guiframe/plugin_base.py	(revision 03314e7471e80689c0f7229fc1a95f0856155e0d)
+++ guiframe/plugin_base.py	(revision a07e72f8fb8281178797bf691b36e56274d8580b)
@@ -143,5 +143,5 @@
         
     
-    def get_context_menu(self, graph=None):
+    def get_context_menu(self, plotpanel=None):
         """
         This method is optional.
@@ -151,5 +151,5 @@
         chance to add a menu item to the context menu.
         
-        A ref to a Graph object is passed so that you can
+        A ref to a plotpanel object is passed so that you can
         investigate the plot content and decide whether you
         need to add items to the context menu.  
Index: guiframe/state_reader.py
===================================================================
--- guiframe/state_reader.py	(revision a07e72f8fb8281178797bf691b36e56274d8580b)
+++ guiframe/state_reader.py	(revision a07e72f8fb8281178797bf691b36e56274d8580b)
@@ -0,0 +1,1596 @@
+
+
+############################################################################
+#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. 
+#If you use DANSE applications to do scientific research that leads to 
+#publication, we ask that you acknowledge the use of the software with the 
+#following sentence:
+#This work benefited from DANSE software developed under NSF award DMR-0520547. 
+#copyright 2008,2009 University of Tennessee
+#############################################################################
+
+# Known issue: reader not compatible with multiple SASdata entries
+# within a single SASentry. Will raise a runtime error.
+
+#TODO: check that all vectors are written only if they have at 
+#    least one non-empty value
+#TODO: Writing only allows one SASentry per file.
+#     Would be best to allow multiple entries.
+#TODO: Store error list
+#TODO: Allow for additional meta data for each section
+#TODO: Notes need to be implemented. They can be any XML 
+#    structure in version 1.0
+#      Process notes have the same problem.
+#TODO: Unit conversion is not complete (temperature units are missing)
+
+
+import logging
+import numpy
+import os
+import sys
+from sans.guiframe.dataFitting import Data1D
+from sans.guiframe.dataFitting import Data2D
+from DataLoader.data_info import Collimation
+from DataLoader.data_info import Detector
+from DataLoader.data_info import Process
+from DataLoader.data_info import Aperture
+from lxml import etree
+import xml.dom.minidom
+from DataLoader.readers.cansas_reader import Reader as CansasReader
+has_converter = True
+try:
+    from data_util.nxsunit import Converter
+except:
+    has_converter = False
+
+CANSAS_NS = "cansas1d/1.0"
+
+def write_node(doc, parent, name, value, attr={}):
+    """
+    :param doc: document DOM
+    :param parent: parent node
+    :param name: tag of the element
+    :param value: value of the child text node
+    :param attr: attribute dictionary
+    
+    :return: True if something was appended, otherwise False
+    """
+    if value is not None:
+        node = doc.createElement(name)
+        node.appendChild(doc.createTextNode(str(value)))
+        for item in attr:
+            node.setAttribute(item, attr[item])
+        parent.appendChild(node)
+        return True
+    return False
+
+def get_content(location, node):
+    """
+    Get the first instance of the content of a xpath location.
+    
+    :param location: xpath location
+    :param node: node to start at
+    
+    :return: Element, or None
+    """
+    nodes = node.xpath(location, namespaces={'ns': CANSAS_NS})
+    
+    if len(nodes)>0:
+        return nodes[0]
+    else:
+        return None
+
+def get_float(location, node):
+    """
+    Get the content of a node as a float 
+    
+    :param location: xpath location
+    :param node: node to start at
+    """
+    nodes = node.xpath(location, namespaces={'ns': CANSAS_NS})
+    
+    value = None
+    attr = {}
+    if len(nodes) > 0:
+        try:
+            value = float(nodes[0].text)   
+        except:
+            # Could not pass, skip and return None
+            msg = "cansas_reader.get_float: could not "
+            msg += " convert '%s' to float" % nodes[0].text
+            logging.error(msg)
+        if nodes[0].get('unit') is not None:
+            attr['unit'] = nodes[0].get('unit')
+    return value, attr
+
+            
+class Reader1D(CansasReader):
+    """
+    Class to load cansas 1D XML files
+    
+    :Dependencies:
+        The CanSas reader requires PyXML 0.8.4 or later.
+    """
+    ## CanSAS version
+    version = '1.0'
+    ## File type
+    type_name = "CanSAS 1D"
+    ## Wildcards
+    type = []
+
+    ## List of allowed extensions
+    ext = []  
+    
+    def __init__(self):
+        CansasReader.__init__(self)
+        ## List of errors
+        self.errors = []
+    
+    def read(self, path):
+        """ 
+        Load data 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 = []
+        if os.path.isfile(path):
+            basename  = os.path.basename(path)
+            root, extension = os.path.splitext(basename)
+            if extension.lower() in self.ext:
+                
+                tree = etree.parse(path, parser=etree.ETCompatXMLParser())
+                # Check the format version number
+                # Specifying the namespace will take care of the file
+                # format version 
+                root = tree.getroot()
+                
+                entry_list = root.xpath('/ns:SASroot/ns:SASentry',
+                                         namespaces={'ns': CANSAS_NS})
+                
+                for entry in entry_list:
+                    self.errors = []
+                    sas_entry = self._parse_entry(entry)
+                    sas_entry.filename = basename
+                    
+                    # Store loading process information
+                    sas_entry.errors = self.errors
+                    sas_entry.meta_data['loader'] = self.type_name
+                    output.append(sas_entry)
+                
+        else:
+            raise RuntimeError, "%s is not a file" % path
+        # Return output consistent with the loader's api
+        if len(output) == 0:
+            #cannot return none when it cannot read 
+            #return None
+            raise RuntimeError, "%s cannot be read \n" % path
+        elif len(output) == 1:
+            return output[0]
+        else:
+            return output                
+                
+    def _parse_entry(self, dom):
+        """
+        Parse a SASentry
+        
+        :param node: SASentry node
+        
+        :return: Data1D object
+        """
+        x = numpy.zeros(0)
+        y = numpy.zeros(0)
+        
+        data_info = Data1D(x, y)
+        
+        # Look up title      
+        self._store_content('ns:Title', dom, 'title', data_info)
+        
+        # Look up run number   
+        nodes = dom.xpath('ns:Run', namespaces={'ns': CANSAS_NS})
+        for item in nodes:    
+            if item.text is not None:
+                value = item.text.strip()
+                if len(value) > 0:
+                    data_info.run.append(value)
+                    if item.get('name') is not None:
+                        data_info.run_name[value] = item.get('name')
+                           
+        # Look up instrument name              
+        self._store_content('ns:SASinstrument/ns:name', dom, 'instrument',
+                             data_info)
+
+        # Notes
+        note_list = dom.xpath('ns:SASnote', namespaces={'ns': CANSAS_NS})
+        for note in note_list:
+            try:
+                if note.text is not None:
+                    note_value = note.text.strip()
+                    if len(note_value) > 0:
+                        data_info.notes.append(note_value)
+            except:
+                err_mess = "cansas_reader.read: error processing"
+                err_mess += " entry notes\n  %s" % sys.exc_value
+                self.errors.append(err_mess)
+                logging.error(err_mess)
+        
+        # Sample info ###################
+        entry = get_content('ns:SASsample', dom)
+        if entry is not None:
+            data_info.sample.name = entry.get('name')
+            
+        self._store_content('ns:SASsample/ns:ID', 
+                     dom, 'ID', data_info.sample)                    
+        self._store_float('ns:SASsample/ns:thickness', 
+                     dom, 'thickness', data_info.sample)
+        self._store_float('ns:SASsample/ns:transmission', 
+                     dom, 'transmission', data_info.sample)
+        self._store_float('ns:SASsample/ns:temperature', 
+                     dom, 'temperature', data_info.sample)
+        
+        nodes = dom.xpath('ns:SASsample/ns:details', 
+                          namespaces={'ns': CANSAS_NS})
+        for item in nodes:
+            try:
+                if item.text is not None:
+                    detail_value = item.text.strip()
+                    if len(detail_value) > 0:
+                        data_info.sample.details.append(detail_value)
+            except:
+                err_mess = "cansas_reader.read: error processing "
+                err_mess += " sample details\n  %s" % sys.exc_value
+                self.errors.append(err_mess)
+                logging.error(err_mess)
+        
+        # Position (as a vector)
+        self._store_float('ns:SASsample/ns:position/ns:x', 
+                     dom, 'position.x', data_info.sample)          
+        self._store_float('ns:SASsample/ns:position/ns:y', 
+                     dom, 'position.y', data_info.sample)          
+        self._store_float('ns:SASsample/ns:position/ns:z', 
+                     dom, 'position.z', data_info.sample)          
+        
+        # Orientation (as a vector)
+        self._store_float('ns:SASsample/ns:orientation/ns:roll', 
+                     dom, 'orientation.x', data_info.sample)          
+        self._store_float('ns:SASsample/ns:orientation/ns:pitch', 
+                     dom, 'orientation.y', data_info.sample)          
+        self._store_float('ns:SASsample/ns:orientation/ns:yaw', 
+                     dom, 'orientation.z', data_info.sample)          
+       
+        # Source info ###################
+        entry = get_content('ns:SASinstrument/ns:SASsource', dom)
+        if entry is not None:
+            data_info.source.name = entry.get('name')
+        
+        self._store_content('ns:SASinstrument/ns:SASsource/ns:radiation', 
+                     dom, 'radiation', data_info.source)                    
+        self._store_content('ns:SASinstrument/ns:SASsource/ns:beam_shape', 
+                     dom, 'beam_shape', data_info.source)                    
+        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength', 
+                     dom, 'wavelength', data_info.source)          
+        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_min', 
+                     dom, 'wavelength_min', data_info.source)          
+        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_max', 
+                     dom, 'wavelength_max', data_info.source)          
+        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_spread', 
+                     dom, 'wavelength_spread', data_info.source)    
+        
+        # Beam size (as a vector)   
+        entry = get_content('ns:SASinstrument/ns:SASsource/ns:beam_size', dom)
+        if entry is not None:
+            data_info.source.beam_size_name = entry.get('name')
+            
+        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:x', 
+                     dom, 'beam_size.x', data_info.source)    
+        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:y', 
+                     dom, 'beam_size.y', data_info.source)    
+        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:z', 
+                     dom, 'beam_size.z', data_info.source)    
+        
+        # Collimation info ###################
+        nodes = dom.xpath('ns:SASinstrument/ns:SAScollimation', 
+                          namespaces={'ns': CANSAS_NS})
+        for item in nodes:
+            collim = Collimation()
+            if item.get('name') is not None:
+                collim.name = item.get('name')
+            self._store_float('ns:length', item, 'length', collim)  
+            
+            # Look for apertures
+            apert_list = item.xpath('ns:aperture', namespaces={'ns': CANSAS_NS})
+            for apert in apert_list:
+                aperture =  Aperture()
+                
+                # Get the name and type of the aperture
+                aperture.name = apert.get('name')
+                aperture.type = apert.get('type')
+                    
+                self._store_float('ns:distance', apert, 'distance', aperture)    
+                
+                entry = get_content('ns:size', apert)
+                if entry is not None:
+                    aperture.size_name = entry.get('name')
+                
+                self._store_float('ns:size/ns:x', apert, 'size.x', aperture)    
+                self._store_float('ns:size/ns:y', apert, 'size.y', aperture)    
+                self._store_float('ns:size/ns:z', apert, 'size.z', aperture)
+                
+                collim.aperture.append(aperture)
+                
+            data_info.collimation.append(collim)
+        
+        # Detector info ######################
+        nodes = dom.xpath('ns:SASinstrument/ns:SASdetector',
+                           namespaces={'ns': CANSAS_NS})
+        for item in nodes:
+            
+            detector = Detector()
+            
+            self._store_content('ns:name', item, 'name', detector)
+            self._store_float('ns:SDD', item, 'distance', detector)    
+            
+            # Detector offset (as a vector)
+            self._store_float('ns:offset/ns:x', item, 'offset.x', detector)    
+            self._store_float('ns:offset/ns:y', item, 'offset.y', detector)    
+            self._store_float('ns:offset/ns:z', item, 'offset.z', detector)    
+            
+            # Detector orientation (as a vector)
+            self._store_float('ns:orientation/ns:roll',  item, 'orientation.x',
+                               detector)    
+            self._store_float('ns:orientation/ns:pitch', item, 'orientation.y',
+                               detector)    
+            self._store_float('ns:orientation/ns:yaw',   item, 'orientation.z',
+                               detector)    
+            
+            # Beam center (as a vector)
+            self._store_float('ns:beam_center/ns:x', item, 'beam_center.x',
+                               detector)    
+            self._store_float('ns:beam_center/ns:y', item, 'beam_center.y', 
+                              detector)    
+            self._store_float('ns:beam_center/ns:z', item, 'beam_center.z',
+                               detector)    
+            
+            # Pixel size (as a vector)
+            self._store_float('ns:pixel_size/ns:x', item, 'pixel_size.x',
+                               detector)    
+            self._store_float('ns:pixel_size/ns:y', item, 'pixel_size.y',
+                               detector)    
+            self._store_float('ns:pixel_size/ns:z', item, 'pixel_size.z',
+                               detector)    
+            
+            self._store_float('ns:slit_length', item, 'slit_length', detector)
+            
+            data_info.detector.append(detector)    
+
+        # Processes info ######################
+        nodes = dom.xpath('ns:SASprocess', namespaces={'ns': CANSAS_NS})
+        for item in nodes:
+            process = Process()
+            self._store_content('ns:name', item, 'name', process)
+            self._store_content('ns:date', item, 'date', process)
+            self._store_content('ns:description', item, 'description', process)
+            
+            term_list = item.xpath('ns:term', namespaces={'ns': CANSAS_NS})
+            for term in term_list:
+                try:
+                    term_attr = {}
+                    for attr in term.keys():
+                        term_attr[attr] = term.get(attr).strip()
+                    if term.text is not None:
+                        term_attr['value'] = term.text.strip()
+                        process.term.append(term_attr)
+                except:
+                    err_mess = "cansas_reader.read: error processing "
+                    err_mess += " process term\n  %s" % sys.exc_value
+                    self.errors.append(err_mess)
+                    logging.error(err_mess)
+            
+            note_list = item.xpath('ns:SASprocessnote', 
+                                   namespaces={'ns': CANSAS_NS})
+            for note in note_list:
+                if note.text is not None:
+                    process.notes.append(note.text.strip())
+            
+            data_info.process.append(process)
+            
+            
+        # Data info ######################
+        nodes = dom.xpath('ns:SASdata', namespaces={'ns': CANSAS_NS})
+        if len(nodes) > 1:
+            msg = "CanSAS reader is not compatible with multiple"
+            msg += " SASdata entries"
+            raise RuntimeError, msg
+        
+        nodes = dom.xpath('ns:SASdata/ns:Idata', namespaces={'ns': CANSAS_NS})
+
+        x  = numpy.zeros(0)
+        y  = numpy.zeros(0)
+        dx = numpy.zeros(0)
+        dy = numpy.zeros(0)
+        dxw = numpy.zeros(0)
+        dxl = numpy.zeros(0)
+        
+        for item in nodes:
+            _x, attr = get_float('ns:Q', item)
+            _dx, attr_d = get_float('ns:Qdev', item)
+            _dxl, attr_l = get_float('ns:dQl', item)
+            _dxw, attr_w = get_float('ns:dQw', item)
+            if _dx == None:
+                _dx = 0.0
+            if _dxl == None:
+                _dxl = 0.0
+            if _dxw == None:
+                _dxw = 0.0
+                
+            if attr.has_key('unit') and \
+                attr['unit'].lower() != data_info.x_unit.lower():
+                if has_converter==True:
+                    try:
+                        data_conv_q = Converter(attr['unit'])
+                        _x = data_conv_q(_x, units=data_info.x_unit)
+                    except:
+                        msg =  "CanSAS reader: could not convert "
+                        msg += "Q unit [%s]; " 
+                        msg += "expecting [%s]\n  %s" % (attr['unit'], 
+                                  data_info.x_unit, sys.exc_value)
+                        raise ValueError, msg
+                        
+                else:
+                    msg = "CanSAS reader: unrecognized Q unit [%s]; "
+                    msg += "expecting [%s]" % (attr['unit'], data_info.x_unit)
+                    raise ValueError, msg
+                        
+            # Error in Q
+            if attr_d.has_key('unit') and \
+                attr_d['unit'].lower() != data_info.x_unit.lower():
+                if has_converter==True:
+                    try:
+                        data_conv_q = Converter(attr_d['unit'])
+                        _dx = data_conv_q(_dx, units=data_info.x_unit)
+                    except:
+                        msg = "CanSAS reader: could not convert dQ unit [%s];"
+                        msg += " expecting " 
+                        msg += "[%s]\n  %s" % (attr['unit'],
+                                                data_info.x_unit, sys.exc_value)
+                        raise ValueError, msg
+                        
+                else:
+                    msg = "CanSAS reader: unrecognized dQ unit [%s]; "
+                    msg += "expecting [%s]" % (attr['unit'], data_info.x_unit)
+                    raise ValueError,  msg
+                        
+            # Slit length
+            if attr_l.has_key('unit') and \
+                attr_l['unit'].lower() != data_info.x_unit.lower():
+                if has_converter == True:
+                    try:
+                        data_conv_q = Converter(attr_l['unit'])
+                        _dxl = data_conv_q(_dxl, units=data_info.x_unit)
+                    except:
+                        msg = "CanSAS reader: could not convert dQl unit [%s];"
+                        msg += " expecting [%s]\n  %s" % (attr['unit'],
+                                             data_info.x_unit, sys.exc_value)
+                        raise ValueError, msg
+                        
+                else:
+                    msg = "CanSAS reader: unrecognized dQl unit [%s];"
+                    msg += " expecting [%s]" % (attr['unit'], data_info.x_unit)
+                    raise ValueError, msg
+                        
+            # Slit width
+            if attr_w.has_key('unit') and \
+            attr_w['unit'].lower() != data_info.x_unit.lower():
+                if has_converter == True:
+                    try:
+                        data_conv_q = Converter(attr_w['unit'])
+                        _dxw = data_conv_q(_dxw, units=data_info.x_unit)
+                    except:
+                        msg = "CanSAS reader: could not convert dQw unit [%s];"
+                        msg += " expecting [%s]\n  %s" % (attr['unit'], 
+                                                data_info.x_unit, sys.exc_value)
+                        raise ValueError, msg
+                        
+                else:
+                    msg = "CanSAS reader: unrecognized dQw unit [%s];"
+                    msg += " expecting [%s]" % (attr['unit'], data_info.x_unit)
+                    raise ValueError, msg   
+            _y, attr = get_float('ns:I', item)
+            _dy, attr_d = get_float('ns:Idev', item)
+            if _dy == None:
+                _dy = 0.0
+            if attr.has_key('unit') and \
+            attr['unit'].lower() != data_info.y_unit.lower():
+                if has_converter==True:
+                    try:
+                        data_conv_i = Converter(attr['unit'])
+                        _y = data_conv_i(_y, units=data_info.y_unit)
+                    except:
+                        msg = "CanSAS reader: could not convert I(q) unit [%s];"
+                        msg += " expecting [%s]\n  %s" % (attr['unit'], 
+                                            data_info.y_unit, sys.exc_value)
+                        raise ValueError, msg
+                else:
+                    msg = "CanSAS reader: unrecognized I(q) unit [%s];"
+                    msg += " expecting [%s]" % (attr['unit'], data_info.y_unit)
+                    raise ValueError, msg 
+                        
+            if attr_d.has_key('unit') and \
+            attr_d['unit'].lower() != data_info.y_unit.lower():
+                if has_converter==True:
+                    try:
+                        data_conv_i = Converter(attr_d['unit'])
+                        _dy = data_conv_i(_dy, units=data_info.y_unit)
+                    except:
+                        msg = "CanSAS reader: could not convert dI(q) unit "
+                        msg += "[%s]; expecting [%s]\n  %s"  % (attr_d['unit'],
+                                             data_info.y_unit, sys.exc_value)
+                        raise ValueError, msg
+                else:
+                    msg = "CanSAS reader: unrecognized dI(q) unit [%s]; "
+                    msg += "expecting [%s]" % (attr_d['unit'], data_info.y_unit)
+                    raise ValueError, msg
+                
+            if _x is not None and _y is not None:
+                x  = numpy.append(x, _x)
+                y  = numpy.append(y, _y)
+                dx = numpy.append(dx, _dx)
+                dy = numpy.append(dy, _dy)
+                dxl = numpy.append(dxl, _dxl)
+                dxw = numpy.append(dxw, _dxw)
+                
+        data_info.x = x
+        data_info.y = y
+        data_info.dx = dx
+        data_info.dy = dy
+        data_info.dxl = dxl
+        data_info.dxw = dxw
+        
+        data_conv_q = None
+        data_conv_i = None
+        
+        if has_converter == True and data_info.x_unit != '1/A':
+            data_conv_q = Converter('1/A')
+            # Test it
+            data_conv_q(1.0, output.Q_unit)
+            
+        if has_converter == True and data_info.y_unit != '1/cm':
+            data_conv_i = Converter('1/cm')
+            # Test it
+            data_conv_i(1.0, output.I_unit)                    
+                
+        if data_conv_q is not None:
+            data_info.xaxis("\\rm{Q}", data_info.x_unit)
+        else:
+            data_info.xaxis("\\rm{Q}", 'A^{-1}')
+        if data_conv_i is not None:
+            data_info.yaxis("\\rm{Intensity}", data_info.y_unit)
+        else:
+            data_info.yaxis("\\rm{Intensity}","cm^{-1}")
+        
+        return data_info
+
+    def _to_xml_doc(self, datainfo):
+        """
+        Create an XML document to contain the content of a Data1D
+        
+        :param datainfo: Data1D object
+        """
+        
+        if not issubclass(datainfo.__class__, Data1D):
+            raise RuntimeError, "The cansas writer expects a Data1D instance"
+        
+        doc = xml.dom.minidom.Document()
+        main_node = doc.createElement("SASroot")
+        main_node.setAttribute("version", self.version)
+        main_node.setAttribute("xmlns", "cansas1d/%s" % self.version)
+        main_node.setAttribute("xmlns:xsi",
+                               "http://www.w3.org/2001/XMLSchema-instance")
+        main_node.setAttribute("xsi:schemaLocation",
+                               "cansas1d/%s http://svn.smallangles.net/svn/canSAS/1dwg/trunk/cansas1d.xsd" % self.version)
+        
+        doc.appendChild(main_node)
+        
+        entry_node = doc.createElement("SASentry")
+        main_node.appendChild(entry_node)
+        
+        write_node(doc, entry_node, "Title", datainfo.title)
+        for item in datainfo.run:
+            runname = {}
+            if datainfo.run_name.has_key(item) and \
+            len(str(datainfo.run_name[item]))>1:
+                runname = {'name': datainfo.run_name[item] }
+            write_node(doc, entry_node, "Run", item, runname)
+        
+        # Data info
+        node = doc.createElement("SASdata")
+        entry_node.appendChild(node)
+        
+        for i in range(len(datainfo.x)):
+            pt = doc.createElement("Idata")
+            node.appendChild(pt)
+            write_node(doc, pt, "Q", datainfo.x[i], {'unit':datainfo.x_unit})
+            if len(datainfo.y)>=i:
+                write_node(doc, pt, "I", datainfo.y[i],
+                            {'unit':datainfo.y_unit})
+            if datainfo.dx != None and len(datainfo.dx) >= i:
+                write_node(doc, pt, "Qdev", datainfo.dx[i],
+                            {'unit':datainfo.x_unit})
+            if datainfo.dxl != None and len(datainfo.dxl) >= i:
+                write_node(doc, pt, "dQl", datainfo.dxl[i],
+                            {'unit':datainfo.x_unit})    
+            if datainfo.dxw != None and len(datainfo.dxw) >= i:
+                write_node(doc, pt, "dQw", datainfo.dxw[i],
+                            {'unit':datainfo.x_unit})              
+            if datainfo.dy != None and len(datainfo.dy) >= i:
+                write_node(doc, pt, "Idev", datainfo.dy[i],
+                            {'unit':datainfo.y_unit})
+
+        
+        # Sample info
+        sample = doc.createElement("SASsample")
+        if datainfo.sample.name is not None:
+            sample.setAttribute("name", str(datainfo.sample.name))
+        entry_node.appendChild(sample)
+        write_node(doc, sample, "ID", str(datainfo.sample.ID))
+        write_node(doc, sample, "thickness", datainfo.sample.thickness,
+                   {"unit":datainfo.sample.thickness_unit})
+        write_node(doc, sample, "transmission", datainfo.sample.transmission)
+        write_node(doc, sample, "temperature", datainfo.sample.temperature,
+                   {"unit":datainfo.sample.temperature_unit})
+        
+        for item in datainfo.sample.details:
+            write_node(doc, sample, "details", item)
+        
+        pos = doc.createElement("position")
+        written = write_node(doc, pos, "x", datainfo.sample.position.x,
+                             {"unit":datainfo.sample.position_unit})
+        written = written | write_node(doc, pos, "y",
+                                       datainfo.sample.position.y,
+                                       {"unit":datainfo.sample.position_unit})
+        written = written | write_node(doc, pos, "z",
+                                       datainfo.sample.position.z,
+                                       {"unit":datainfo.sample.position_unit})
+        if written == True:
+            sample.appendChild(pos)
+        
+        ori = doc.createElement("orientation")
+        written = write_node(doc, ori, "roll",
+                             datainfo.sample.orientation.x,
+                             {"unit":datainfo.sample.orientation_unit})
+        written = written | write_node(doc, ori, "pitch",
+                                       datainfo.sample.orientation.y,
+                                    {"unit":datainfo.sample.orientation_unit})
+        written = written | write_node(doc, ori, "yaw",
+                                       datainfo.sample.orientation.z,
+                                    {"unit":datainfo.sample.orientation_unit})
+        if written == True:
+            sample.appendChild(ori)
+        
+        # Instrument info
+        instr = doc.createElement("SASinstrument")
+        entry_node.appendChild(instr)
+        
+        write_node(doc, instr, "name", datainfo.instrument)
+        
+        #   Source
+        source = doc.createElement("SASsource")
+        if datainfo.source.name is not None:
+            source.setAttribute("name", str(datainfo.source.name))
+        instr.appendChild(source)
+        
+        write_node(doc, source, "radiation", datainfo.source.radiation)
+        write_node(doc, source, "beam_shape", datainfo.source.beam_shape)
+        size = doc.createElement("beam_size")
+        if datainfo.source.beam_size_name is not None:
+            size.setAttribute("name", str(datainfo.source.beam_size_name))
+        written = write_node(doc, size, "x", datainfo.source.beam_size.x,
+                             {"unit":datainfo.source.beam_size_unit})
+        written = written | write_node(doc, size, "y",
+                                       datainfo.source.beam_size.y,
+                                       {"unit":datainfo.source.beam_size_unit})
+        written = written | write_node(doc, size, "z",
+                                       datainfo.source.beam_size.z,
+                                       {"unit":datainfo.source.beam_size_unit})
+        if written == True:
+            source.appendChild(size)
+            
+        write_node(doc, source, "wavelength",
+                   datainfo.source.wavelength,
+                   {"unit":datainfo.source.wavelength_unit})
+        write_node(doc, source, "wavelength_min",
+                   datainfo.source.wavelength_min,
+                   {"unit":datainfo.source.wavelength_min_unit})
+        write_node(doc, source, "wavelength_max",
+                   datainfo.source.wavelength_max,
+                   {"unit":datainfo.source.wavelength_max_unit})
+        write_node(doc, source, "wavelength_spread",
+                   datainfo.source.wavelength_spread,
+                   {"unit":datainfo.source.wavelength_spread_unit})
+        
+        #   Collimation
+        for item in datainfo.collimation:
+            coll = doc.createElement("SAScollimation")
+            if item.name is not None:
+                coll.setAttribute("name", str(item.name))
+            instr.appendChild(coll)
+            
+            write_node(doc, coll, "length", item.length,
+                       {"unit":item.length_unit})
+            
+            for apert in item.aperture:
+                ap = doc.createElement("aperture")
+                if apert.name is not None:
+                    ap.setAttribute("name", str(apert.name))
+                if apert.type is not None:
+                    ap.setAttribute("type", str(apert.type))
+                coll.appendChild(ap)
+                
+                write_node(doc, ap, "distance", apert.distance,
+                           {"unit":apert.distance_unit})
+                
+                size = doc.createElement("size")
+                if apert.size_name is not None:
+                    size.setAttribute("name", str(apert.size_name))
+                written = write_node(doc, size, "x", apert.size.x,
+                                     {"unit":apert.size_unit})
+                written = written | write_node(doc, size, "y", apert.size.y,
+                                               {"unit":apert.size_unit})
+                written = written | write_node(doc, size, "z", apert.size.z,
+                                               {"unit":apert.size_unit})
+                if written == True:
+                    ap.appendChild(size)
+
+        #   Detectors
+        for item in datainfo.detector:
+            det = doc.createElement("SASdetector")
+            written = write_node(doc, det, "name", item.name)
+            written = written | write_node(doc, det, "SDD", item.distance,
+                                           {"unit":item.distance_unit})
+            written = written | write_node(doc, det, "slit_length",
+                                           item.slit_length,
+                                           {"unit":item.slit_length_unit})
+            if written == True:
+                instr.appendChild(det)
+            
+            off = doc.createElement("offset")
+            written = write_node(doc, off, "x", item.offset.x,
+                                 {"unit":item.offset_unit})
+            written = written | write_node(doc, off, "y", item.offset.y,
+                                           {"unit":item.offset_unit})
+            written = written | write_node(doc, off, "z", item.offset.z,
+                                           {"unit":item.offset_unit})
+            if written == True:
+                det.appendChild(off)
+            
+            center = doc.createElement("beam_center")
+            written = write_node(doc, center, "x", item.beam_center.x,
+                                 {"unit":item.beam_center_unit})
+            written = written | write_node(doc, center, "y",
+                                           item.beam_center.y,
+                                           {"unit":item.beam_center_unit})
+            written = written | write_node(doc, center, "z",
+                                           item.beam_center.z,
+                                           {"unit":item.beam_center_unit})
+            if written == True:
+                det.appendChild(center)
+                
+            pix = doc.createElement("pixel_size")
+            written = write_node(doc, pix, "x", item.pixel_size.x,
+                                 {"unit":item.pixel_size_unit})
+            written = written | write_node(doc, pix, "y", item.pixel_size.y,
+                                           {"unit":item.pixel_size_unit})
+            written = written | write_node(doc, pix, "z", item.pixel_size.z,
+                                           {"unit":item.pixel_size_unit})
+            if written == True:
+                det.appendChild(pix)
+                
+            ori = doc.createElement("orientation")
+            written = write_node(doc, ori, "roll",  item.orientation.x,
+                                 {"unit":item.orientation_unit})
+            written = written | write_node(doc, ori, "pitch",
+                                           item.orientation.y,
+                                           {"unit":item.orientation_unit})
+            written = written | write_node(doc, ori, "yaw",
+                                           item.orientation.z,
+                                           {"unit":item.orientation_unit})
+            if written == True:
+                det.appendChild(ori)
+                
+        
+        # Processes info
+        for item in datainfo.process:
+            node = doc.createElement("SASprocess")
+            entry_node.appendChild(node)
+
+            write_node(doc, node, "name", item.name)
+            write_node(doc, node, "date", item.date)
+            write_node(doc, node, "description", item.description)
+            for term in item.term:
+                value = term['value']
+                del term['value']
+                write_node(doc, node, "term", value, term)
+            for note in item.notes:
+                write_node(doc, node, "SASprocessnote", note)
+        
+        # Return the document, and the SASentry node associated with
+        # the data we just wrote
+        return doc, entry_node
+            
+    def write(self, filename, datainfo):
+        """
+        Write the content of a Data1D as a CanSAS XML file
+        
+        :param filename: name of the file to write
+        :param datainfo: Data1D object
+        """
+        # Create XML document
+        doc, sasentry = self._to_xml_doc(datainfo)
+        # Write the file
+        fd = open(filename, 'w')
+        fd.write(doc.toprettyxml())
+        fd.close()
+        
+    def _store_float(self, location, node, variable, storage, optional=True):
+        """
+        Get the content of a xpath location and store
+        the result. Check that the units are compatible
+        with the destination. The value is expected to
+        be a float.
+        
+        The xpath location might or might not exist.
+        If it does not exist, nothing is done
+        
+        :param location: xpath location to fetch
+        :param node: node to read the data from
+        :param variable: name of the data member to store it in [string]
+        :param storage: data object that has the 'variable' data member
+        :param optional: if True, no exception will be raised 
+            if unit conversion can't be done
+
+        :raise ValueError: raised when the units are not recognized
+        """
+        entry = get_content(location, node)
+        try:
+            value = float(entry.text)
+        except:
+            value = None
+            
+        if value is not None:
+            # If the entry has units, check to see that they are
+            # compatible with what we currently have in the data object
+            units = entry.get('unit')
+            if units is not None:
+                toks = variable.split('.')
+                exec "local_unit = storage.%s_unit" % toks[0]
+                if units.lower()!=local_unit.lower():
+                    if has_converter==True:
+                        try:
+                            conv = Converter(units)
+                            exec "storage.%s = %g" % (variable,
+                                            conv(value, units=local_unit))
+                        except:
+                            err_mess = "CanSAS reader: could not convert"
+                            err_mess += " %s unit [%s]; expecting [%s]\n  %s" \
+                                % (variable, units, local_unit, sys.exc_value)
+                            self.errors.append(err_mess)
+                            if optional:
+                                logging.info(err_mess)
+                            else:
+                                raise ValueError, err_mess 
+                    else:
+                        err_mess = "CanSAS reader: unrecognized %s unit [%s];"
+                        err_mess += " expecting [%s]" % (variable, 
+                                                         units, local_unit)
+                        self.errors.append(err_mess)
+                        if optional:
+                            logging.info(err_mess)
+                        else:
+                            raise ValueError, err_mess
+                else:
+                    exec "storage.%s = value" % variable
+            else:
+                exec "storage.%s = value" % variable
+                
+    def _store_content(self, location, node, variable, storage):
+        """
+        Get the content of a xpath location and store
+        the result. The value is treated as a string.
+        
+        The xpath location might or might not exist.
+        If it does not exist, nothing is done
+        
+        :param location: xpath location to fetch
+        :param node: node to read the data from
+        :param variable: name of the data member to store it in [string]
+        :param storage: data object that has the 'variable' data member
+        
+        :return: return a list of errors
+        """
+        entry = get_content(location, node)
+        if entry is not None and entry.text is not None:
+            exec "storage.%s = entry.text.strip()" % variable
+
+           
+
+class Reader2D(CansasReader):
+    """
+    Class to load a .fitv fitting file
+    """
+    ## File type
+    type_name = "Fitting"
+    
+    ## Wildcards
+    type = ["Fitting files (*.fitv)|*.fitv"
+            "SANSView file (*.svs)|*.svs"]
+    ## List of allowed extensions
+    ext=['.fitv', '.FITV', '.svs', 'SVS']   
+    
+    def __init__(self):
+        CansasReader.__init__(self)
+        """
+        Initialize the call-back method to be called
+        after we load a file
+        
+        :param call_back: call-back method
+        :param cansas:  True = files will be written/read in CanSAS format
+                        False = write CanSAS format
+            
+        """
+        ## Call back method to be executed after a file is read
+        #self.call_back = call_back
+        ## CanSAS format flag
+        self.cansas = cansas
+        self.state = None
+        
+    def read(self, path):
+        """ 
+        Load a new P(r) inversion state from file
+        
+        :param path: file path
+        
+        """
+        if self.cansas == True:
+            return self._read_cansas(path)
+     
+    def _to_xml_doc(self, datainfo):
+        """
+        Create an XML document to contain the content of a Data2D
+        
+        :param datainfo: Data2D object
+        
+        """
+        if not issubclass(datainfo.__class__, Data2D):
+            raise RuntimeError, "The cansas writer expects a Data2D instance"
+        
+        doc = xml.dom.minidom.Document()
+        main_node = doc.createElement("SASroot")
+        main_node.setAttribute("version", self.version)
+        main_node.setAttribute("xmlns", "cansas1d/%s" % self.version)
+        main_node.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance")
+        main_node.setAttribute("xsi:schemaLocation", "cansas1d/%s http://svn.smallangles.net/svn/canSAS/1dwg/trunk/cansas1d.xsd" % self.version)
+        
+        doc.appendChild(main_node)
+        
+        entry_node = doc.createElement("SASentry")
+        main_node.appendChild(entry_node)
+       
+        write_node(doc, entry_node, "Title", datainfo.title)
+        if datainfo is not None:
+            write_node(doc, entry_node, "data_class", datainfo.__class__.__name__)
+        for item in datainfo.run:
+            runname = {}
+            if datainfo.run_name.has_key(item) and len(str(datainfo.run_name[item]))>1:
+                runname = {'name': datainfo.run_name[item] }
+            write_node(doc, entry_node, "Run", item, runname)
+        # Data info
+        new_node = doc.createElement("SASdata")
+        entry_node.appendChild(new_node)
+        for item in list_of_data_2d_attr:
+            element = doc.createElement(item[0])
+            exec "element.setAttribute(item[0], str(datainfo.%s))"%(item[1])
+            new_node.appendChild(element)
+            
+        for item in list_of_data2d_values:
+            root_node = doc.createElement(item[0])
+            new_node.appendChild(root_node)
+            
+            exec "temp_list = datainfo.%s"%item[1]
+
+            if temp_list is None or len(temp_list)== 0:
+                element = doc.createElement(item[0])
+                exec "element.appendChild(doc.createTextNode(str(%s)))"%temp_list
+                root_node.appendChild(element)
+            else:
+                for value in temp_list:
+                    element = doc.createElement(item[0])
+                    exec "element.setAttribute(item[0], str(%s))"%value
+                    root_node.appendChild(element)
+       
+        # Sample info
+        sample = doc.createElement("SASsample")
+        if datainfo.sample.name is not None:
+            sample.setAttribute("name", str(datainfo.sample.name))
+        entry_node.appendChild(sample)
+        write_node(doc, sample, "ID", str(datainfo.sample.ID))
+        write_node(doc, sample, "thickness", datainfo.sample.thickness, {"unit":datainfo.sample.thickness_unit})
+        write_node(doc, sample, "transmission", datainfo.sample.transmission)
+        write_node(doc, sample, "temperature", datainfo.sample.temperature, {"unit":datainfo.sample.temperature_unit})
+        
+        for item in datainfo.sample.details:
+            write_node(doc, sample, "details", item)
+        
+        pos = doc.createElement("position")
+        written = write_node(doc, pos, "x", datainfo.sample.position.x, {"unit":datainfo.sample.position_unit})
+        written = written | write_node(doc, pos, "y", datainfo.sample.position.y, {"unit":datainfo.sample.position_unit})
+        written = written | write_node(doc, pos, "z", datainfo.sample.position.z, {"unit":datainfo.sample.position_unit})
+        if written == True:
+            sample.appendChild(pos)
+        
+        ori = doc.createElement("orientation")
+        written = write_node(doc, ori, "roll",  datainfo.sample.orientation.x, {"unit":datainfo.sample.orientation_unit})
+        written = written | write_node(doc, ori, "pitch", datainfo.sample.orientation.y, {"unit":datainfo.sample.orientation_unit})
+        written = written | write_node(doc, ori, "yaw",   datainfo.sample.orientation.z, {"unit":datainfo.sample.orientation_unit})
+        if written == True:
+            sample.appendChild(ori)
+        
+        # Instrument info
+        instr = doc.createElement("SASinstrument")
+        entry_node.appendChild(instr)
+        
+        write_node(doc, instr, "name", datainfo.instrument)
+        
+        #   Source
+        source = doc.createElement("SASsource")
+        if datainfo.source.name is not None:
+            source.setAttribute("name", str(datainfo.source.name))
+        instr.appendChild(source)
+        
+        write_node(doc, source, "radiation", datainfo.source.radiation)
+        write_node(doc, source, "beam_shape", datainfo.source.beam_shape)
+        size = doc.createElement("beam_size")
+        if datainfo.source.beam_size_name is not None:
+            size.setAttribute("name", str(datainfo.source.beam_size_name))
+        written = write_node(doc, size, "x", datainfo.source.beam_size.x, {"unit":datainfo.source.beam_size_unit})
+        written = written | write_node(doc, size, "y", datainfo.source.beam_size.y, {"unit":datainfo.source.beam_size_unit})
+        written = written | write_node(doc, size, "z", datainfo.source.beam_size.z, {"unit":datainfo.source.beam_size_unit})
+        if written == True:
+            source.appendChild(size)
+            
+        write_node(doc, source, "wavelength", datainfo.source.wavelength, {"unit":datainfo.source.wavelength_unit})
+        write_node(doc, source, "wavelength_min", datainfo.source.wavelength_min, {"unit":datainfo.source.wavelength_min_unit})
+        write_node(doc, source, "wavelength_max", datainfo.source.wavelength_max, {"unit":datainfo.source.wavelength_max_unit})
+        write_node(doc, source, "wavelength_spread", datainfo.source.wavelength_spread, {"unit":datainfo.source.wavelength_spread_unit})
+        
+        #   Collimation
+        for item in datainfo.collimation:
+            coll = doc.createElement("SAScollimation")
+            if item.name is not None:
+                coll.setAttribute("name", str(item.name))
+            instr.appendChild(coll)
+            
+            write_node(doc, coll, "length", item.length, {"unit":item.length_unit})
+            
+            for apert in item.aperture:
+                ap = doc.createElement("aperture")
+                if apert.name is not None:
+                    ap.setAttribute("name", str(apert.name))
+                if apert.type is not None:
+                    ap.setAttribute("type", str(apert.type))
+                coll.appendChild(ap)
+                
+                write_node(doc, ap, "distance", apert.distance, {"unit":apert.distance_unit})
+                
+                size = doc.createElement("size")
+                if apert.size_name is not None:
+                    size.setAttribute("name", str(apert.size_name))
+                written = write_node(doc, size, "x", apert.size.x, {"unit":apert.size_unit})
+                written = written | write_node(doc, size, "y", apert.size.y, {"unit":apert.size_unit})
+                written = written | write_node(doc, size, "z", apert.size.z, {"unit":apert.size_unit})
+                if written == True:
+                    ap.appendChild(size)
+
+        #   Detectors
+        for item in datainfo.detector:
+            det = doc.createElement("SASdetector")
+            written = write_node(doc, det, "name", item.name)
+            written = written | write_node(doc, det, "SDD", item.distance, {"unit":item.distance_unit})
+            written = written | write_node(doc, det, "slit_length", item.slit_length, {"unit":item.slit_length_unit})
+            if written == True:
+                instr.appendChild(det)
+            
+            off = doc.createElement("offset")
+            written = write_node(doc, off, "x", item.offset.x, {"unit":item.offset_unit})
+            written = written | write_node(doc, off, "y", item.offset.y, {"unit":item.offset_unit})
+            written = written | write_node(doc, off, "z", item.offset.z, {"unit":item.offset_unit})
+            if written == True:
+                det.appendChild(off)
+            
+            center = doc.createElement("beam_center")
+            written = write_node(doc, center, "x", item.beam_center.x, {"unit":item.beam_center_unit})
+            written = written | write_node(doc, center, "y", item.beam_center.y, {"unit":item.beam_center_unit})
+            written = written | write_node(doc, center, "z", item.beam_center.z, {"unit":item.beam_center_unit})
+            if written == True:
+                det.appendChild(center)
+                
+            pix = doc.createElement("pixel_size")
+            written = write_node(doc, pix, "x", item.pixel_size.x, {"unit":item.pixel_size_unit})
+            written = written | write_node(doc, pix, "y", item.pixel_size.y, {"unit":item.pixel_size_unit})
+            written = written | write_node(doc, pix, "z", item.pixel_size.z, {"unit":item.pixel_size_unit})
+            if written == True:
+                det.appendChild(pix)
+                
+            ori = doc.createElement("orientation")
+            written = write_node(doc, ori, "roll",  item.orientation.x, {"unit":item.orientation_unit})
+            written = written | write_node(doc, ori, "pitch", item.orientation.y, {"unit":item.orientation_unit})
+            written = written | write_node(doc, ori, "yaw",   item.orientation.z, {"unit":item.orientation_unit})
+            if written == True:
+                det.appendChild(ori)
+                
+        # Processes info
+        for item in datainfo.process:
+            node = doc.createElement("SASprocess")
+            entry_node.appendChild(node)
+
+            write_node(doc, node, "name", item.name)
+            write_node(doc, node, "date", item.date)
+            write_node(doc, node, "description", item.description)
+            for term in item.term:
+                value = term['value']
+                del term['value']
+                write_node(doc, node, "term", value, term)
+            for note in item.notes:
+                write_node(doc, node, "SASprocessnote", note)
+        # Return the document, and the SASentry node associated with
+        # the data we just wrote
+        return doc, entry_node
+   
+    def _parse_state(self, entry):
+        """
+        Read a fit result from an XML node
+        
+        :param entry: XML node to read from 
+        
+        :return: PageState object
+        """
+        # Create an empty state
+        state = None   
+        # Locate the P(r) node
+        try:
+            nodes = entry.xpath('ns:%s' % FITTING_NODE_NAME, namespaces={'ns': CANSAS_NS})
+            if nodes !=[]:
+                # Create an empty state
+                state =  PageState()
+                state.fromXML(node=nodes[0])
+        except:
+            logging.info("XML document does not contain fitting information.\n %s" % sys.exc_value)
+            
+        return state
+    
+   
+                    
+    def _parse_entry(self, dom):
+        """
+        Parse a SASentry
+        
+        :param node: SASentry node
+        
+        :return: Data1D/Data2D object
+        
+        """
+        node = dom.xpath('ns:data_class', namespaces={'ns': CANSAS_NS})
+        if not node or node[0].text.lstrip().rstrip() != "Data2D":
+            return CansasReader._parse_entry(self, dom)
+        
+        #Parse 2D
+        data_info = Data2D()
+        
+        # Look up title      
+        self._store_content('ns:Title', dom, 'title', data_info)
+        
+        # Look up run number   
+        nodes = dom.xpath('ns:Run', namespaces={'ns': CANSAS_NS})
+        for item in nodes:    
+            if item.text is not None:
+                value = item.text.strip()
+                if len(value) > 0:
+                    data_info.run.append(value)
+                    if item.get('name') is not None:
+                        data_info.run_name[value] = item.get('name')
+                           
+        # Look up instrument name              
+        self._store_content('ns:SASinstrument/ns:name', dom, 'instrument', data_info)
+
+        # Notes
+        note_list = dom.xpath('ns:SASnote', namespaces={'ns': CANSAS_NS})
+        for note in note_list:
+            try:
+                if note.text is not None:
+                    note_value = note.text.strip()
+                    if len(note_value) > 0:
+                        data_info.notes.append(note_value)
+            except:
+                err_mess = "cansas_reader.read: error processing entry notes\n  %s" % sys.exc_value
+                self.errors.append(err_mess)
+                logging.error(err_mess)
+        
+        # Sample info ###################
+        entry = get_content('ns:SASsample', dom)
+        if entry is not None:
+            data_info.sample.name = entry.get('name')
+            
+        self._store_content('ns:SASsample/ns:ID', 
+                     dom, 'ID', data_info.sample)                    
+        self._store_float('ns:SASsample/ns:thickness', 
+                     dom, 'thickness', data_info.sample)
+        self._store_float('ns:SASsample/ns:transmission', 
+                     dom, 'transmission', data_info.sample)
+        self._store_float('ns:SASsample/ns:temperature', 
+                     dom, 'temperature', data_info.sample)
+        
+        nodes = dom.xpath('ns:SASsample/ns:details', namespaces={'ns': CANSAS_NS})
+        for item in nodes:
+            try:
+                if item.text is not None:
+                    detail_value = item.text.strip()
+                    if len(detail_value) > 0:
+                        data_info.sample.details.append(detail_value)
+            except:
+                err_mess = "cansas_reader.read: error processing sample details\n  %s" % sys.exc_value
+                self.errors.append(err_mess)
+                logging.error(err_mess)
+        
+        # Position (as a vector)
+        self._store_float('ns:SASsample/ns:position/ns:x', 
+                     dom, 'position.x', data_info.sample)          
+        self._store_float('ns:SASsample/ns:position/ns:y', 
+                     dom, 'position.y', data_info.sample)          
+        self._store_float('ns:SASsample/ns:position/ns:z', 
+                     dom, 'position.z', data_info.sample)          
+        
+        # Orientation (as a vector)
+        self._store_float('ns:SASsample/ns:orientation/ns:roll', 
+                     dom, 'orientation.x', data_info.sample)          
+        self._store_float('ns:SASsample/ns:orientation/ns:pitch', 
+                     dom, 'orientation.y', data_info.sample)          
+        self._store_float('ns:SASsample/ns:orientation/ns:yaw', 
+                     dom, 'orientation.z', data_info.sample)          
+       
+        # Source info ###################
+        entry = get_content('ns:SASinstrument/ns:SASsource', dom)
+        if entry is not None:
+            data_info.source.name = entry.get('name')
+        
+        self._store_content('ns:SASinstrument/ns:SASsource/ns:radiation', 
+                     dom, 'radiation', data_info.source)                    
+        self._store_content('ns:SASinstrument/ns:SASsource/ns:beam_shape', 
+                     dom, 'beam_shape', data_info.source)                    
+        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength', 
+                     dom, 'wavelength', data_info.source)          
+        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_min', 
+                     dom, 'wavelength_min', data_info.source)          
+        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_max', 
+                     dom, 'wavelength_max', data_info.source)          
+        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_spread', 
+                     dom, 'wavelength_spread', data_info.source)    
+        
+        # Beam size (as a vector)   
+        entry = get_content('ns:SASinstrument/ns:SASsource/ns:beam_size', dom)
+        if entry is not None:
+            data_info.source.beam_size_name = entry.get('name')
+            
+        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:x', 
+                     dom, 'beam_size.x', data_info.source)    
+        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:y', 
+                     dom, 'beam_size.y', data_info.source)    
+        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:z', 
+                     dom, 'beam_size.z', data_info.source)    
+        
+        # Collimation info ###################
+        nodes = dom.xpath('ns:SASinstrument/ns:SAScollimation', namespaces={'ns': CANSAS_NS})
+        for item in nodes:
+            collim = Collimation()
+            if item.get('name') is not None:
+                collim.name = item.get('name')
+            self._store_float('ns:length', item, 'length', collim)  
+            
+            # Look for apertures
+            apert_list = item.xpath('ns:aperture', namespaces={'ns': CANSAS_NS})
+            for apert in apert_list:
+                aperture =  Aperture()
+                
+                # Get the name and type of the aperture
+                aperture.name = apert.get('name')
+                aperture.type = apert.get('type')
+                    
+                self._store_float('ns:distance', apert, 'distance', aperture)    
+                
+                entry = get_content('ns:size', apert)
+                if entry is not None:
+                    aperture.size_name = entry.get('name')
+                
+                self._store_float('ns:size/ns:x', apert, 'size.x', aperture)    
+                self._store_float('ns:size/ns:y', apert, 'size.y', aperture)    
+                self._store_float('ns:size/ns:z', apert, 'size.z', aperture)
+                
+                collim.aperture.append(aperture)
+                
+            data_info.collimation.append(collim)
+        
+        # Detector info ######################
+        nodes = dom.xpath('ns:SASinstrument/ns:SASdetector', namespaces={'ns': CANSAS_NS})
+        for item in nodes:
+            
+            detector = Detector()
+            
+            self._store_content('ns:name', item, 'name', detector)
+            self._store_float('ns:SDD', item, 'distance', detector)    
+            
+            # Detector offset (as a vector)
+            self._store_float('ns:offset/ns:x', item, 'offset.x', detector)    
+            self._store_float('ns:offset/ns:y', item, 'offset.y', detector)    
+            self._store_float('ns:offset/ns:z', item, 'offset.z', detector)    
+            
+            # Detector orientation (as a vector)
+            self._store_float('ns:orientation/ns:roll',  item, 'orientation.x', detector)    
+            self._store_float('ns:orientation/ns:pitch', item, 'orientation.y', detector)    
+            self._store_float('ns:orientation/ns:yaw',   item, 'orientation.z', detector)    
+            
+            # Beam center (as a vector)
+            self._store_float('ns:beam_center/ns:x', item, 'beam_center.x', detector)    
+            self._store_float('ns:beam_center/ns:y', item, 'beam_center.y', detector)    
+            self._store_float('ns:beam_center/ns:z', item, 'beam_center.z', detector)    
+            
+            # Pixel size (as a vector)
+            self._store_float('ns:pixel_size/ns:x', item, 'pixel_size.x', detector)    
+            self._store_float('ns:pixel_size/ns:y', item, 'pixel_size.y', detector)    
+            self._store_float('ns:pixel_size/ns:z', item, 'pixel_size.z', detector)    
+            
+            self._store_float('ns:slit_length', item, 'slit_length', detector)
+            
+            data_info.detector.append(detector)    
+
+        # Processes info ######################
+        nodes = dom.xpath('ns:SASprocess', namespaces={'ns': CANSAS_NS})
+        for item in nodes:
+            process = Process()
+            self._store_content('ns:name', item, 'name', process)
+            self._store_content('ns:date', item, 'date', process)
+            self._store_content('ns:description', item, 'description', process)
+            
+            term_list = item.xpath('ns:term', namespaces={'ns': CANSAS_NS})
+            for term in term_list:
+                try:
+                    term_attr = {}
+                    for attr in term.keys():
+                        term_attr[attr] = term.get(attr).strip()
+                    if term.text is not None:
+                        term_attr['value'] = term.text.strip()
+                        process.term.append(term_attr)
+                except:
+                    err_mess = "cansas_reader.read: error processing process term\n  %s" % sys.exc_value
+                    self.errors.append(err_mess)
+                    logging.error(err_mess)
+            
+            note_list = item.xpath('ns:SASprocessnote', namespaces={'ns': CANSAS_NS})
+            for note in note_list:
+                if note.text is not None:
+                    process.notes.append(note.text.strip())
+            
+            data_info.process.append(process)
+            
+            
+        # Data info ######################
+        nodes = dom.xpath('ns:SASdata', namespaces={'ns': CANSAS_NS})
+        if len(nodes)>1:
+            raise RuntimeError, "CanSAS reader is not compatible with multiple SASdata entries"
+       
+        for entry in nodes:
+            for item in list_of_data_2d_attr:
+                #get node
+                node = get_content('ns:%s'%item[0], entry)
+                exec "data_info.%s = parse_entry_helper(node, item)"%(item[1])
+                    
+            for item in list_of_data2d_values:
+                field = get_content('ns:%s'%item[0], entry)
+                list = []
+                if field is not None:
+                    list = [parse_entry_helper(node, item) for node in field]
+                exec "data_info.%s = numpy.array(list)"%item[0]
+        
+        return data_info
+
+    def _read_cansas(self, path):
+        """ 
+        Load data and P(r) information from a CanSAS XML file.
+        
+        :param path: file path
+        
+        :return: Data1D object if a single SASentry was found, 
+                    or a list of Data1D objects if multiple entries were found,
+                    or None of nothing was found
+                    
+        :raise RuntimeError: when the file can't be opened
+        :raise ValueError: when the length of the data vectors are inconsistent
+        
+        """
+        output = []
+        basename  = os.path.basename(path)
+        root, extension = os.path.splitext(basename)
+        ext = extension.lower()
+        try:
+            if os.path.isfile(path):
+                
+                #TODO: eventually remove the check for .xml once
+                # the P(r) writer/reader is truly complete.
+                if  ext in self.ext or \
+                    ext == '.xml':
+                    
+                    tree = etree.parse(path, parser=etree.ETCompatXMLParser())
+                    # Check the format version number
+                    # Specifying the namespace will take care of the file format version 
+                    root = tree.getroot()
+                    entry_list = root.xpath('ns:SASentry', namespaces={'ns': CANSAS_NS})
+                    for entry in entry_list:   
+                        try:
+                            sas_entry = self._parse_entry(entry)
+                        except:
+                            raise
+                        fitstate = self._parse_state(entry)
+                        
+                        #state could be None when .svs file is loaded
+                        #in this case, skip appending to output
+                        if fitstate != None:
+                            sas_entry.meta_data['fitstate'] = fitstate
+                            sas_entry.filename = fitstate.file
+                            output.append(sas_entry)
+            else:
+                self.call_back(format=ext)
+                raise RuntimeError, "%s is not a file" % path
+
+            # Return output consistent with the loader's api
+            if len(output)==0:
+                self.call_back(state=None, datainfo=None,format=ext)
+                return None
+            else:
+                for ind in range(len(output)):
+                    # Call back to post the new state
+                    state = output[ind].meta_data['fitstate']
+                    t = time.localtime(state.timestamp)
+                    time_str = time.strftime("%b %d %H:%M", t)
+                    # Check that no time stamp is already appended
+                    max_char = state.file.find("[")
+                    if max_char < 0:
+                        max_char = len(state.file)
+                    original_fname = state.file[0:max_char]
+                    state.file = original_fname +' [' + time_str + ']'
+                   
+                        
+                    if state is not None and state.is_data is not None:
+                        exec 'output[%d].is_data = state.is_data'% ind 
+                     
+                    output[ind].filename = state.file
+                    state.data = output[ind]
+                    state.data.name = output[ind].filename #state.data_name
+                    state.data.id = state.data_id
+                    if state.is_data is not None:
+                        state.data.is_data = state.is_data
+                    if output[ind].run_name is not None and\
+                        len(output[ind].run_name) != 0 :
+                        name = output[ind].run_name
+                    else: 
+                        name=original_fname
+                    state.data.group_id = name
+                    #store state in fitting
+                    self.call_back(state=state, datainfo=output[ind],format=ext)
+                    self.state= state
+                return output
+              
+        except:
+            #self.call_back(format=ext)
+            self.state= state
+            raise
+           
+    def write(self, filename, datainfo=None, fitstate=None):
+        """
+        Write the content of a Data1D as a CanSAS XML file only for standalone
+        
+        :param filename: name of the file to write
+        :param datainfo: Data1D object
+        :param fitstate: PageState object
+        
+        """
+        # Sanity check
+        if self.cansas == True:
+            
+            # Add fitting information to the XML document
+            doc = self.write_toXML(datainfo, fitstate)
+            # Write the XML document
+            fd = open(filename, 'w')
+            fd.write(doc.toprettyxml())
+            fd.close()
+        else:
+            fitstate.toXML(file=filename)
+        
+    def write_toXML(self, datainfo=None, state=None):
+        """
+        Write toXML, a helper for write() , could be used by guimanager._on_save()
+        
+        : return: xml doc
+        """
+
+        if state.data is None:
+            data = Data2D()  
+        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
+            doc, sasentry = self._to_xml_doc(data)
+           
+
+        if state is not None:
+            state.toXML(doc=doc, file=data.name, entry_node=sasentry)
+            
+        return doc 
+  
+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() 
+
+    logging.basicConfig(level=logging.ERROR,
+                        format='%(asctime)s %(levelname)s %(message)s',
+                        filename='cansas_reader.log',
+                        filemode='w')
+    reader = Reader()
+    print reader.read("../test/cansas1d.xml")
+    #print reader.read("../test/latex_smeared.xml")
+    
+    
+                        
Index: invariantview/perspectives/invariant/invariant.py
===================================================================
--- invariantview/perspectives/invariant/invariant.py	(revision cb69775e79a5b088ed99e66e533531920663e659)
+++ invariantview/perspectives/invariant/invariant.py	(revision a07e72f8fb8281178797bf691b36e56274d8580b)
@@ -18,15 +18,11 @@
 import logging
 
-from DataLoader.data_info import Data1D as LoaderData1D
-from sans.guiframe.dataFitting import Theory1D
+
 from sans.guiframe.dataFitting import Data1D
-
 from sans.guiframe.events import NewPlotEvent
-from sans.guiframe.events import ERR_DATA
+from sans.guiframe.gui_style import GUIFRAME_ID
 from .invariant_state import Reader as reader
 from DataLoader.loader import Loader
 from .invariant_panel import InvariantPanel
-#from sans.guiframe.events import EVT_INVSTATE_UPDATE
-
 from sans.guiframe.plugin_base import PluginBase
 
@@ -80,8 +76,4 @@
         ## Save a reference to the parent
         self.parent = parent
-        #add error back to the data
-        self.parent.Bind(ERR_DATA, self._on_data_error)
-        #self.parent.Bind(EVT_INVSTATE_UPDATE, self.on_set_state_helper)
-        
         self.invariant_panel = InvariantPanel(parent=self.parent)
         self.invariant_panel.set_manager(manager=self)
@@ -96,5 +88,5 @@
         return [self.invariant_panel]
   
-    def get_context_menu(self, graph=None):
+    def get_context_menu(self, plotpanel=None):
         """
         This method is optional.
@@ -117,51 +109,23 @@
         :return: a list of menu items with call-back function
         """
-        self.graph = graph
+        graph = plotpanel.graph
         invariant_option = "Compute invariant"
         invariant_hint = "Will displays the invariant panel for"
         invariant_hint += " futher computation"
-       
-        for item in self.graph.plottables:
-            if item.name == graph.selected_plottable :
-                if issubclass(item.__class__, LoaderData1D):
-           
-                    if item.name != "$I_{obs}(q)$" and \
-                        item.name != " $P_{fit}(r)$":
-                        if hasattr(item, "group_id"):
-                            return [[invariant_option, 
-                                        invariant_hint, 
+        
+        if graph.selected_plottable not in plotpanel.plots:
+            return []
+        data = plotpanel.plots[graph.selected_plottable]
+        
+        if not issubclass(data.__class__, Data1D):
+            name = data.__class__.__name__
+            msg = "Invariant use only Data1D got: [%s] " % str(name)
+            raise ValueError, msg 
+        
+        if data.name != "$I_{obs}(q)$" and  data.name != " $P_{fit}(r)$":
+           return [[invariant_option, invariant_hint, 
                                         self._compute_invariant]]
-        return []   
-
-    def copy_data(self, item, dy=None):
-        """
-        receive a data 1D and the list of errors on dy
-        and create a new data1D data
-        """
-        id = None
-        if hasattr(item,"id"):
-            id = item.id
-
-        data = Data1D(x=item.x, y=item.y, dx=None, dy=None)
-        data.copy_from_datainfo(item)
-        item.clone_without_data(clone=data)    
-        data.dy = dy
-        data.name = item.name
-        data.title = item.title
-        
-        ## allow to highlight data when plotted
-        data.interactive = item.interactive
-        ## when 2 data have the same id override the 1 st plotted
-        data.id = id
-        data.group_id = item.group_id
-        return data
-    
-    def _on_data_error(self, event):
-        """
-        receives and event from plotting plu-gins to store the data name and 
-        their errors of y coordinates for 1Data hide and show error
-        """
-        self.err_dy = event.err_dy
-        
+        return []
+
     def _compute_invariant(self, event):    
         """
@@ -170,16 +134,13 @@
         self.panel = event.GetEventObject()
         Plugin.on_perspective(self, event=event)
-        for plottable in self.panel.graph.plottables:
-            if plottable.name == self.panel.graph.selected_plottable:
-                ## put the errors values back to the model if the errors 
-                ## were hiden before sending them to the fit engine
-                if len(self.err_dy) > 0:
-                    dy = plottable.dy
-                    if plottable.name in  self.err_dy.iterkeys():
-                        dy = self.err_dy[plottable.name]
-                    data = self.copy_data(plottable, dy)
-                else:
-                    data = plottable
-                self.compute_helper(data=data)
+        id = self.panel.graph.selected_plottable
+        data = self.panel.plots[self.panel.graph.selected_plottable]
+        if data is None:
+            return
+        if not issubclass(data.__class__, Data1D):
+            name = data.__class__.__name__
+            msg = "Invariant use only Data1D got: [%s] " % str(name)
+            raise ValueError, msg 
+        self.compute_helper(data=data)
                 
     def set_data(self, data_list):
@@ -187,4 +148,5 @@
         receive a list of data and compute invariant
         """
+        data = None
         if len(data_list) > 1:
             msg = "invariant panel does not allow multiple data!\n"
@@ -194,26 +156,21 @@
             if dlg.ShowModal() == wx.ID_OK:
                 data = dlg.get_data()
-                if issubclass(data.__class__, LoaderData1D):
-                    wx.PostEvent(self.parent, NewPlotEvent(plot=data_list[0],
-                                               title=data_list[0].title))
-                    self.compute_helper(data_list[0])
-                else:    
-                    msg = "invariant cannot be computed for data of "
-                    msg += "type %s" % (data_list[0].__class__.__name__)
-                    wx.PostEvent(self.parent, 
-                             StatusEvent(status=msg, info='error'))
         elif len(data_list) == 1:
-            if issubclass(data_list[0].__class__, LoaderData1D):
-                wx.PostEvent(self.parent, NewPlotEvent(plot=data_list[0],
-                                               title=data_list[0].title))
-                self.compute_helper(data_list[0])
-            else:
-                msg = "invariant cannot be computed for"
-                msg += " data of type %s" % (data_list[0].__class__.__name__)
-                wx.PostEvent(self.parent, 
-                             StatusEvent(status=msg, info='error'))
-            
-            
+            data = data_list[0]
+        if data is None:
+            return
+        if issubclass(data.__class__, Data1D):
+            wx.PostEvent(self.parent, NewPlotEvent(plot=data,
+                                       title=data.title))
+            self.compute_helper(data)
+        else:    
+            msg = "invariant cannot be computed for data of "
+            msg += "type %s" % (data.__class__.__name__)
+            wx.PostEvent(self.parent, 
+                     StatusEvent(status=msg, info='error'))
+    
     def clear_panel(self):
+        """
+        """
         self.invariant_panel.clear_panel()
         
@@ -311,22 +268,32 @@
         #import copy
         if data is None:
-            new_plot = Theory1D(x=[], y=[], dy=None)
+            id = str(self.__data.id) + name
+            self.__data.group_id
+            wx.PostEvent(self.parent, NewPlotEvent(id=id,
+                                               group_id=group_id,
+                                               remove=true))
+            return
+    
+        new_plot = Data1D(x=[], y=[], dy=None)
+        new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
+        scale = self.invariant_panel.get_scale()
+        background = self.invariant_panel.get_background()
+        
+        if scale != 0:
+            # Put back the sacle and bkg for plotting
+            data.y = (data.y + background)/scale
+            new_plot = Data1D(x=data.x, y=data.y, dy=None)
+            new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
         else:
-            scale = self.invariant_panel.get_scale()
-            background = self.invariant_panel.get_background()
-            
-            if scale != 0:
-                # Put back the sacle and bkg for plotting
-                data.y = (data.y + background)/scale
-                new_plot = Theory1D(x=data.x, y=data.y, dy=None)
-            else:
-                msg = "Scale can not be zero."
-                raise ValueError, msg
-
+            msg = "Scale can not be zero."
+            raise ValueError, msg
+        if len(new_plot.x)== 0 :
+            return
+        
         new_plot.name = name
         new_plot.xaxis(self.__data._xaxis, self.__data._xunit)
         new_plot.yaxis(self.__data._yaxis, self.__data._yunit)
         new_plot.group_id = self.__data.group_id
-        new_plot.id = self.__data.id + name
+        new_plot.id = str(self.__data.id) + name
         new_plot.title = self.__data.title
         # Save theory_data in a state
Index: invariantview/perspectives/invariant/invariant_panel.py
===================================================================
--- invariantview/perspectives/invariant/invariant_panel.py	(revision 97ec26b2f31eb0fdfc69293585cfbc2a28adabbb)
+++ invariantview/perspectives/invariant/invariant_panel.py	(revision a07e72f8fb8281178797bf691b36e56274d8580b)
@@ -364,4 +364,5 @@
                                            name="Low-Q extrapolation")
             except:
+                raise
                 self.inv_container.qstar_low = "ERROR"
                 self.inv_container.qstar_low_err = "ERROR"
@@ -393,4 +394,5 @@
                                            name="High-Q extrapolation")
             except:
+                raisee
                 self.inv_container.qstar_high = "ERROR"
                 self.inv_container.qstar_high_err = "ERROR"
Index: prview/perspectives/pr/inversion_state.py
===================================================================
--- prview/perspectives/pr/inversion_state.py	(revision 229da98c3eef5aedd1335551f0f9046c902df65f)
+++ prview/perspectives/pr/inversion_state.py	(revision a07e72f8fb8281178797bf691b36e56274d8580b)
@@ -17,5 +17,5 @@
 from xml.dom.minidom import parse
 from lxml import etree
-import DataLoader
+from sans.guiframe.dataFitting import Data1D
 from DataLoader.readers.cansas_reader import Reader as CansasReader
 from DataLoader.readers.cansas_reader import get_content
@@ -519,6 +519,6 @@
         """
         if datainfo is None:
-            datainfo = DataLoader.data_info.Data1D(x=[], y=[])    
-        elif not issubclass(datainfo.__class__, DataLoader.data_info.Data1D):
+            datainfo = Data1D(x=[], y=[])    
+        elif not issubclass(datainfo.__class__, Data1D):
             msg = "The cansas writer expects a Data1D "
             msg += "instance: %s" % str(datainfo.__class__.__name__)
Index: prview/perspectives/pr/pr.py
===================================================================
--- prview/perspectives/pr/pr.py	(revision 229da98c3eef5aedd1335551f0f9046c902df65f)
+++ prview/perspectives/pr/pr.py	(revision a07e72f8fb8281178797bf691b36e56274d8580b)
@@ -24,12 +24,10 @@
 import pylab
 from sans.guiframe.dataFitting import Data1D
-from sans.guiframe.dataFitting import Theory1D
 from sans.guiframe.events import NewPlotEvent
-from sans.guiframe.events import StatusEvent    
-
+from sans.guiframe.events import StatusEvent 
+from sans.guiframe.gui_style import GUIFRAME_ID   
 from sans.pr.invertor import Invertor
 from DataLoader.loader import Loader
-from DataLoader.data_info import Data1D as LoaderData1D
-import DataLoader
+
 from pr_widgets import load_error 
 from sans.guiframe.plugin_base import PluginBase
@@ -41,8 +39,4 @@
 IQ_FIT_LABEL       = r"$I_{fit}(q)$"
 IQ_SMEARED_LABEL   = r"$I_{smeared}(q)$"
-
-#import wx.lib
-#(NewPrFileEvent, EVT_PR_FILE) = wx.lib.newevent.NewEvent()
-
 
 
@@ -103,5 +97,4 @@
         
         # Associate the inversion state reader with .prv files
-        from DataLoader.loader import Loader
         from inversion_state import Reader
          
@@ -166,6 +159,5 @@
             self.control_panel.set_state(state)
         except:
-            raise
-            #logging.error("prview.set_state: %s" % sys.exc_value)
+            logging.error("prview.set_state: %s" % sys.exc_value)
 
   
@@ -188,8 +180,4 @@
         """
         from sans.pr.invertor import Invertor
-            
-        from danse.common.plottools import Data1D as PlotData1D
-        from danse.common.plottools import Theory1D as Theory1D
-        
         # Generate P(r) for sphere
         radius = 60.0
@@ -220,12 +208,17 @@
         for i in range(len(out)):
             print "%g +- %g" % (out[i], math.sqrt(cov[i][i]))
-
+        
         # Show input P(r)
-        new_plot = PlotData1D(pr.x, pr.y, dy=pr.err)
+        title = "Pr"
+        new_plot = Data1D(pr.x, pr.y, dy=pr.err)
         new_plot.name = "P_{obs}(r)"
         new_plot.xaxis("\\rm{r}", 'A')
         new_plot.yaxis("\\rm{P(r)} ","cm^{-3}")
-        print "_fit_pr"
-        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title="Pr"))
+        group_id = "P_{obs}(r)"
+        if group_id not in new_plot.group_id:
+            new_plot.group_id.append(group_id)
+        new_plot.id = "P_{obs}(r)"
+        new_plot.title = title
+        wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title=title))
 
         # Show P(r) fit
@@ -239,5 +232,4 @@
         """
         """
-        from danse.common.plottools import Theory1D as PlotTheory1D
         # Show P(r)
         y_true = numpy.zeros(len(x))
@@ -251,5 +243,6 @@
         
         # Show the theory P(r)
-        new_plot = PlotTheory1D(x, y_true)
+        new_plot = Data1D(x, y_true)
+        new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
         new_plot.name = "P_{true}(r)"
         new_plot.xaxis("\\rm{r}", 'A')
@@ -272,6 +265,4 @@
         """
         """  
-        from danse.common.plottools import Theory1D as PlotTheory1D
-
         qtemp = pr.x
         if not q==None:
@@ -304,15 +295,19 @@
                 print "Error getting error", value, x[i]
                 
-        new_plot = PlotTheory1D(x, y)
+        new_plot = Data1D(x, y)
+        new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
         new_plot.name = IQ_FIT_LABEL
         new_plot.xaxis("\\rm{Q}", 'A^{-1}')
         new_plot.yaxis("\\rm{Intensity} ","cm^{-1}")
-        
         title = "I(q)"
+        new_plot.title = title
+        
         # If we have a group ID, use it
         if pr.info.has_key("plot_group_id"):
-            new_plot.group_id = pr.info["plot_group_id"]
-            title = pr.info["plot_group_id"]
-            
+            if len( pr.info["plot_group_id"]) > 0:
+                index =  len( pr.info["plot_group_id"]) - 1
+                new_plot.group_id.append( pr.info["plot_group_id"][index])
+        new_plot.id = IQ_FIT_LABEL
+        #new_plot.group_id.append(2)
         wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title=title))
         
@@ -331,5 +326,6 @@
                     print "Error getting error", value, x[i]
                     
-            new_plot = Theory1D(x, y)
+            new_plot = Data1D(x, y)
+            new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
             new_plot.name = IQ_SMEARED_LABEL
             new_plot.xaxis("\\rm{Q}", 'A^{-1}')
@@ -337,5 +333,11 @@
             # If we have a group ID, use it
             if pr.info.has_key("plot_group_id"):
-                new_plot.group_id = pr.info["plot_group_id"]
+                if len( pr.info["plot_group_id"]) > 0:
+                    index =  len( pr.info["plot_group_id"]) - 1
+                    new_plot.group_id.append( pr.info["plot_group_id"][index])
+           
+            new_plot.id = IQ_SMEARED_LABEL
+            new_plot.title = title
+            #new_plot.group_id.append(2)
             wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title=title))
         
@@ -359,7 +361,4 @@
         """
         """     
-        from danse.common.plottools import Data1D as PlotData1D 
-        from danse.common.plottools import Theory1D as PlotTheory1D
-        
         # Show P(r)
         x = pylab.arange(0.0, pr.d_max, pr.d_max/self._pr_npts)
@@ -394,11 +393,13 @@
         
         if cov2==None:
-            new_plot = PlotTheory1D(x, y)
+            new_plot = Data1D(x, y)
+            new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
         else:
-            new_plot = PlotData1D(x, y, dy=dy)
+            new_plot = Data1D(x, y, dy=dy)
         new_plot.name = PR_FIT_LABEL
         new_plot.xaxis("\\rm{r}", 'A')
         new_plot.yaxis("\\rm{P(r)} ","cm^{-3}")
         new_plot.title = "P(r) fit"
+        new_plot.id = PR_FIT_LABEL
         # Make sure that the plot is linear
         new_plot.xtransform = "x"
@@ -563,5 +564,5 @@
             return 0.0
 
-    def get_context_menu(self, graph=None):
+    def get_context_menu(self, plotpanel=None):
         """
         Get the context menu items available for P(r)
@@ -572,42 +573,38 @@
         
         """
+        graph = plotpanel.graph
         # Look whether this Graph contains P(r) data
-        #if graph.selected_plottable==IQ_DATA_LABEL:
-        for item in graph.plottables:
-            if item.name == PR_FIT_LABEL:
-                #add_data_hint = "Load a data file and display it on this plot"
-                #["Add P(r) data",add_data_hint , self._on_add_data],
-                change_n_hint = "Change the number of"
-                change_n_hint += " points on the P(r) output"
-                change_n_label = "Change number of P(r) points"
-                m_list = [[change_n_label, change_n_hint , self._on_pr_npts]]
-
-                if self._scale_output_unity or self._normalize_output:
-                    hint = "Let the output P(r) keep the scale of the data"
-                    m_list.append(["Disable P(r) scaling", hint, 
-                                   self._on_disable_scaling])
-                if not self._scale_output_unity:
-                    m_list.append(["Scale P_max(r) to unity", 
-                                   "Scale P(r) so that its maximum is 1", 
-                                   self._on_scale_unity])
-                if not self._normalize_output:
-                    m_list.append(["Normalize P(r) to unity", 
-                                   "Normalize the integral of P(r) to 1", 
-                                   self._on_normalize])
-                    
-                return m_list
-                #return [["Add P(r) data", 
-                #"Load a data file and display it on this plot",
-                # self._on_add_data],
-                #       ["Change number of P(r) points",
-                # "Change the number of points on the P(r) output",
-                # self._on_pr_npts]]
-
-            elif item.name == graph.selected_plottable:
-	            #TODO: we might want to check that the units are 
-	            #     consistent with I(q) before allowing this menu item
-                if not self.standalone and \
-                issubclass(item.__class__, DataLoader.data_info.Data1D):
-                    return [["Compute P(r)", 
+        if graph.selected_plottable not in plotpanel.plots:
+            return []
+        item = plotpanel.plots[graph.selected_plottable]
+        if item.id == PR_FIT_LABEL:
+            #add_data_hint = "Load a data file and display it on this plot"
+            #["Add P(r) data",add_data_hint , self._on_add_data],
+            change_n_hint = "Change the number of"
+            change_n_hint += " points on the P(r) output"
+            change_n_label = "Change number of P(r) points"
+            m_list = [[change_n_label, change_n_hint , self._on_pr_npts]]
+
+            if self._scale_output_unity or self._normalize_output:
+                hint = "Let the output P(r) keep the scale of the data"
+                m_list.append(["Disable P(r) scaling", hint, 
+                               self._on_disable_scaling])
+            if not self._scale_output_unity:
+                m_list.append(["Scale P_max(r) to unity", 
+                               "Scale P(r) so that its maximum is 1", 
+                               self._on_scale_unity])
+            if not self._normalize_output:
+                m_list.append(["Normalize P(r) to unity", 
+                               "Normalize the integral of P(r) to 1", 
+                               self._on_normalize])
+                
+            return m_list
+             
+        elif item.id in [PR_LOADED_LABEL, IQ_DATA_LABEL, IQ_FIT_LABEL,
+                          IQ_SMEARED_LABEL]:
+            return []
+        elif item.id == graph.selected_plottable:
+	       if not self.standalone and issubclass(item.__class__, Data1D):
+                return [["Compute P(r)", 
                              "Compute P(r) from distribution", 
                              self._on_context_inversion]]      
@@ -657,5 +654,11 @@
             y = self._added_plots[plot].y/sum
             
-            new_plot = Theory1D(self._added_plots[plot].x, y)
+            new_plot = Data1D(self._added_plots[plot].x, y)
+            new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
+            index  = len(self._added_plots[plot].group_id) - 1
+            if group_id not in new_plot.group_id:
+                new_plot.group_id.append(group_id)
+            new_plot.id = self._added_plots[plot].id
+            new_plot.title = self._added_plots[plot].title
             new_plot.name = self._added_plots[plot].name
             new_plot.xaxis("\\rm{r}", 'A')
@@ -686,5 +689,6 @@
             y = self._added_plots[plot].y/_max
             
-            new_plot = Theory1D(self._added_plots[plot].x, y)
+            new_plot = Data1D(self._added_plots[plot].x, y)
+            new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
             new_plot.name = self._added_plots[plot].name
             new_plot.xaxis("\\rm{r}", 'A')
@@ -736,7 +740,7 @@
         
         filename = os.path.basename(path)
-        
-        #new_plot = Data1D(x, y, dy=err)
-        new_plot = Theory1D(x, y)
+
+        new_plot = Data1D(x, y)
+        new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
         new_plot.name = filename
         new_plot.xaxis("\\rm{r}", 'A')
@@ -863,5 +867,7 @@
             new_plot.xaxis("\\rm{Q}", 'A^{-1}')
             new_plot.yaxis("\\rm{Intensity} ","cm^{-1}")
-            #new_plot.group_id = "test group"
+            if pr.info.has_key("plot_group_id"):
+                new_plot.group_id.append(pr.info["plot_group_id"])
+            new_plot.id = IQ_DATA_LABEL
             wx.PostEvent(self.parent, NewPlotEvent(plot=new_plot, title="Iq"))
                 
@@ -884,9 +890,7 @@
         
         """
-        print "show_data", data.name
         #if path is not None:
         if data is not None:
             try:
-                #pr = self._create_file_pr(path)
                 pr = self._create_file_pr(data)
             except:
@@ -902,6 +906,7 @@
        
         # Make a plot of I(q) data
-        if self.pr.err==None:
-            new_plot = Theory1D(self.pr.x, self.pr.y)
+        if self.pr.err == None:
+            new_plot = Data1D(self.pr.x, self.pr.y)
+            new_plot.symbol = GUIFRAME_ID.CURVE_SYMBOL_NUM
         else:
             new_plot = Data1D(self.pr.x, self.pr.y, dy=self.pr.err)
@@ -910,12 +915,11 @@
         new_plot.yaxis("\\rm{Intensity} ","cm^{-1}")
         new_plot.interactive = True
-        #new_plot.group_id = "test group"
+        new_plot.group_id.append(IQ_DATA_LABEL)
+        new_plot.id = IQ_DATA_LABEL
+        new_plot.title = "I(q)"
         wx.PostEvent(self.parent, 
                      NewPlotEvent(plot=new_plot, title="I(q)", reset=reset))
         
         self.current_plottable = new_plot
-        self.current_plottable.group_id = IQ_DATA_LABEL
-        
-        
         # Get Q range
         self.control_panel.q_min = self.pr.x.min()
@@ -1021,5 +1025,7 @@
         # Keep track of the plot window title to ensure that
         # we can overlay the plots
-        if hasattr(self.current_plottable, "group_id"):
+        if self.current_plottable.group_id:
+            index = len(self.current_plottable.group_id) - 1
+            group_id = self.current_plottable.group_id[index]
             pr.info["plot_group_id"] = self.current_plottable.group_id
         
@@ -1329,5 +1335,5 @@
             if dlg.ShowModal() == wx.ID_OK:
                 data = dlg.get_data()
-                if issubclass(data.__class__, LoaderData1D):
+                if issubclass(data.__class__, Data1D):
                     self.control_panel._change_file(evt=None, data=data)
                 else:    
@@ -1337,5 +1343,5 @@
                              StatusEvent(status=msg, info='error'))
         elif len(data_list) == 1:
-            if issubclass(data_list[0].__class__, LoaderData1D):
+            if issubclass(data_list[0].__class__, Data1D):
                 self.control_panel._change_file(evt=None, data=data_list[0])
             else:
Index: prview/perspectives/pr/pr_widgets.py
===================================================================
--- prview/perspectives/pr/pr_widgets.py	(revision af91b687903ba4681033e2e0d336e931f2aa6835)
+++ prview/perspectives/pr/pr_widgets.py	(revision a07e72f8fb8281178797bf691b36e56274d8580b)
@@ -27,5 +27,5 @@
         
         ## Set to True when the mouse is clicked while the whole string is selected
-        full_selection = False
+        self.full_selection = False
         ## Call back for EVT_SET_FOCUS events
         _on_set_focus_callback = None
Index: sansview/perspectives/fitting/Copy of basepage.py
===================================================================
--- sansview/perspectives/fitting/Copy of basepage.py	(revision a07e72f8fb8281178797bf691b36e56274d8580b)
+++ sansview/perspectives/fitting/Copy of basepage.py	(revision a07e72f8fb8281178797bf691b36e56274d8580b)
@@ -0,0 +1,2589 @@
+
+import sys
+import os
+import wx
+import numpy
+import time
+import copy 
+import math
+import string
+from sans.guiframe.panel_base import PanelBase
+from wx.lib.scrolledpanel import ScrolledPanel
+from sans.guiframe.utils import format_number,check_float
+from sans.guiframe.events import PanelOnFocusEvent
+from sans.guiframe.events import StatusEvent
+from sans.guiframe.events import AppendBookmarkEvent
+import pagestate
+from pagestate import PageState
+(PageInfoEvent, EVT_PAGE_INFO)   = wx.lib.newevent.NewEvent()
+(PreviousStateEvent, EVT_PREVIOUS_STATE)   = wx.lib.newevent.NewEvent()
+(NextStateEvent, EVT_NEXT_STATE)   = wx.lib.newevent.NewEvent()
+
+_BOX_WIDTH = 76
+_QMIN_DEFAULT = 0.001
+_QMAX_DEFAULT = 0.13
+_NPTS_DEFAULT = 50
+#Control panel width 
+if sys.platform.count("darwin")==0:
+    PANEL_WIDTH = 450
+    FONT_VARIANT = 0
+    ON_MAC = False
+else:
+    PANEL_WIDTH = 500
+    FONT_VARIANT = 1
+    ON_MAC = True
+    
+class BasicPage(ScrolledPanel, PanelBase):
+    """
+    This class provide general structure of  fitpanel page
+    """
+     ## Internal name for the AUI manager
+    window_name = "Basic Page"
+    ## Title to appear on top of the window
+    window_caption = "Basic page "
+    
+    def __init__(self, parent, page_info, **kwargs):
+        """
+        """
+        ScrolledPanel.__init__(self, parent, **kwargs)
+        PanelBase.__init__(self)
+        self.SetupScrolling()
+        #Set window's font size 
+        self.SetWindowVariant(variant=FONT_VARIANT)
+       
+        ## parent of the page
+        self.parent = parent
+        ## manager is the fitting plugin
+        ## owner of the page (fitting plugin)
+        self.event_owner = None
+         ## current model
+        self.model = None
+        ## data
+        self.data = None
+        self.mask = None
+        ## Q range
+        self.qmax_x = None
+        self.qmin_x = None
+        self.num_points= _NPTS_DEFAULT
+        ## total number of point: float
+        self.npts = None
+        ## default fitengine type
+        self.engine_type = 'scipy'
+        ## smear default
+        self.smearer = None
+        self.current_smearer = None
+        ## 2D smear accuracy default
+        self.smear2d_accuracy = 'Low'
+        ## slit smear: 
+        self.dxl = None
+        self.dxw = None
+        ## pinhole smear 
+        self.dx_min = None
+        self.dx_max = None
+       
+        self.disp_cb_dict = {}
+   
+        self.state = PageState(parent=parent)
+        ## dictionary containing list of models
+        self.model_list_box = None
+        self.set_page_info(page_info=page_info)
+        ## Data member to store the dispersion object created
+        self._disp_obj_dict = {}
+        ## selected parameters to apply dispersion
+        self.disp_cb_dict ={}
+
+        ## smearer object
+        self.smearer = None
+        
+        ##list of model parameters. each item must have same length
+        ## each item related to a given parameters
+        ##[cb state, name, value, "+/-", error of fit, min, max , units]
+        self.parameters = []
+        # non-fittable parameter whose value is astring
+        self.str_parameters = []
+        ## list of parameters to fit , must be like self.parameters
+        self.param_toFit = []
+        ## list of looking like parameters but with non fittable parameters info
+        self.fixed_param = []
+        ## list of looking like parameters but with  fittable parameters info
+        self.fittable_param = []
+        ##list of dispersion parameters
+        self.disp_list = []
+        self.disp_name = ""
+        
+        ## list of orientation parameters
+        self.orientation_params = []
+        self.orientation_params_disp = []
+        if self.model != None:
+            self.disp_list = self.model.getDispParamList()
+        
+        ##enable model 2D draw
+        self.enable2D = False
+        ## check that the fit range is correct to plot the model again
+        self.fitrange = True
+        ## Create memento to save the current state
+        self.state = PageState(parent=self.parent,
+                               model=self.model, data=self.data)
+        ## flag to determine if state has change
+        self.state_change = False
+        ## save customized array
+        self.values = []
+        self.weights = []
+        ## retrieve saved state
+        self.number_saved_state = 0
+        ## dictionary of saved state
+        self.saved_states = {} 
+        ## Create context menu for page
+        self.popUpMenu = wx.Menu()
+        #id = wx.NewId()
+        #self._undo = wx.MenuItem(self.popUpMenu,id, "Undo",
+        #"cancel the previous action")
+        #self.popUpMenu.AppendItem(self._undo)
+        #self._undo.Enable(False)
+        #wx.EVT_MENU(self, id, self.onUndo)
+        
+        #id = wx.NewId()
+        #self._redo = wx.MenuItem(self.popUpMenu,id,"Redo",
+        #" Restore the previous action")
+        #self.popUpMenu.AppendItem(self._redo)
+        #self._redo.Enable(False)
+        #wx.EVT_MENU(self, id, self.onRedo)       
+        #self.popUpMenu.AppendSeparator()
+        #if sys.platform.count("win32")>0:        
+        id = wx.NewId()
+        self._keep = wx.MenuItem(self.popUpMenu,id,"BookMark",
+                                 " Keep the panel status to recall it later")
+        self.popUpMenu.AppendItem(self._keep)
+        self._keep.Enable(True)
+        self._set_bookmark_flag(False)
+        self._set_save_flag(False)
+        wx.EVT_MENU(self, id, self.on_bookmark)
+        self.popUpMenu.AppendSeparator()
+    
+        ## Default locations
+        self._default_save_location = os.getcwd()     
+        ## save initial state on context menu
+        #self.onSave(event=None)
+        self.Bind(wx.EVT_CONTEXT_MENU, self.onContextMenu)
+
+        ## create the basic structure of the panel with empty sizer
+        self.define_page_structure()
+        ## drawing Initial dispersion parameters sizer 
+        self.set_dispers_sizer()
+        
+        ## layout
+        self.set_layout()
+        
+    def on_set_focus(self, event):
+        """
+        """
+        if self._manager is not None:
+            wx.PostEvent(self._manager.parent, PanelOnFocusEvent(panel=self))
+        
+    class ModelTextCtrl(wx.TextCtrl):
+        """
+        Text control for model and fit parameters.
+        Binds the appropriate events for user interactions.
+        Default callback methods can be overwritten on initialization
+        
+        :param kill_focus_callback: callback method for EVT_KILL_FOCUS event
+        :param set_focus_callback:  callback method for EVT_SET_FOCUS event
+        :param mouse_up_callback:   callback method for EVT_LEFT_UP event
+        :param text_enter_callback: callback method for EVT_TEXT_ENTER event
+        
+        """
+        ## Set to True when the mouse is clicked while the whole string is selected
+        full_selection = False
+        ## Call back for EVT_SET_FOCUS events
+        _on_set_focus_callback = None
+        
+        def __init__(self, parent, id=-1, 
+                     value=wx.EmptyString, 
+                     pos=wx.DefaultPosition, 
+                     size=wx.DefaultSize,
+                     style=0, 
+                     validator=wx.DefaultValidator,
+                     name=wx.TextCtrlNameStr,
+                     kill_focus_callback=None,
+                     set_focus_callback=None,
+                     mouse_up_callback=None,
+                     text_enter_callback = None):
+             
+            wx.TextCtrl.__init__(self, parent, id, value, pos,
+                                  size, style, validator, name)
+            
+            # Bind appropriate events
+            self._on_set_focus_callback = parent.onSetFocus \
+                      if set_focus_callback is None else set_focus_callback
+            self.Bind(wx.EVT_SET_FOCUS, self._on_set_focus)
+            self.Bind(wx.EVT_KILL_FOCUS, self._silent_kill_focus \
+                      if kill_focus_callback is None else kill_focus_callback)                
+            self.Bind(wx.EVT_TEXT_ENTER, parent._onparamEnter \
+                      if text_enter_callback is None else text_enter_callback)
+            if not ON_MAC :
+                self.Bind(wx.EVT_LEFT_UP,    self._highlight_text \
+                          if mouse_up_callback is None else mouse_up_callback)
+            
+        def _on_set_focus(self, event):
+            """
+            Catch when the text control is set in focus to highlight the whole
+            text if necessary
+            
+            :param event: mouse event
+            
+            """
+            event.Skip()
+            self.full_selection = True
+            return self._on_set_focus_callback(event)
+        
+ 
+            
+        def _highlight_text(self, event):
+            """
+            Highlight text of a TextCtrl only of no text has be selected
+            
+            :param event: mouse event
+            
+            """
+            # Make sure the mouse event is available to other listeners
+            event.Skip()
+            control  = event.GetEventObject()
+            if self.full_selection:
+                self.full_selection = False
+                # Check that we have a TextCtrl
+                if issubclass(control.__class__, wx.TextCtrl):
+                    # Check whether text has been selected, 
+                    # if not, select the whole string
+                    (start, end) = control.GetSelection()
+                    if start==end:
+                        control.SetSelection(-1,-1)
+                        
+        def _silent_kill_focus(self,event):
+            """
+            Save the state of the page
+            """
+            
+            event.Skip()
+            pass
+    
+    def set_page_info(self, page_info):
+        """
+        set some page important information at once
+        """
+       ##window_name
+        self.window_name = page_info.window_name
+        ##window_caption
+        self.window_caption = page_info.window_caption
+        ## manager is the fitting plugin
+        self._manager= page_info.manager
+        ## owner of the page (fitting plugin)
+        self.event_owner= page_info.event_owner
+         ## current model
+        self.model = page_info.model
+        ## data
+        self.data = page_info.data
+        ## dictionary containing list of models
+        self.model_list_box = page_info.model_list_box
+        ## Data member to store the dispersion object created
+        self.populate_box(dict=self.model_list_box)
+        
+    def onContextMenu(self, event): 
+        """
+        Retrieve the state selected state
+        """
+        # Skipping the save state functionality for release 0.9.0
+        #return
+    
+        pos = event.GetPosition()
+        pos = self.ScreenToClient(pos)
+       
+        self.PopupMenu(self.popUpMenu, pos) 
+      
+        
+    def onUndo(self, event):
+        """
+        Cancel the previous action
+        """
+        #print "enable undo"
+        event = PreviousStateEvent(page = self)
+        wx.PostEvent(self.parent, event)
+        
+    def onRedo(self, event):
+        """
+        Restore the previous action cancelled 
+        """
+        #print "enable redo"
+        event = NextStateEvent(page= self)
+        wx.PostEvent(self.parent, event)
+    
+    def define_page_structure(self):
+        """
+        Create empty sizer for a panel
+        """
+        self.vbox  = wx.BoxSizer(wx.VERTICAL)
+        self.sizer0 = wx.BoxSizer(wx.VERTICAL)
+        self.sizer1 = wx.BoxSizer(wx.VERTICAL)
+        self.sizer2 = wx.BoxSizer(wx.VERTICAL)
+        self.sizer3 = wx.BoxSizer(wx.VERTICAL)
+        self.sizer4 = wx.BoxSizer(wx.VERTICAL)
+        self.sizer5 = wx.BoxSizer(wx.VERTICAL)
+        self.sizer6 = wx.BoxSizer(wx.VERTICAL)
+        
+        self.sizer0.SetMinSize((PANEL_WIDTH,-1))
+        self.sizer1.SetMinSize((PANEL_WIDTH,-1))
+        self.sizer2.SetMinSize((PANEL_WIDTH,-1))
+        self.sizer3.SetMinSize((PANEL_WIDTH,-1))
+        self.sizer4.SetMinSize((PANEL_WIDTH,-1))
+        self.sizer5.SetMinSize((PANEL_WIDTH,-1))
+        self.sizer6.SetMinSize((PANEL_WIDTH,-1))
+        
+        self.vbox.Add(self.sizer0)
+        self.vbox.Add(self.sizer1)
+        self.vbox.Add(self.sizer2)
+        self.vbox.Add(self.sizer3)
+        self.vbox.Add(self.sizer4)
+        self.vbox.Add(self.sizer5)
+        self.vbox.Add(self.sizer6)
+        
+    def set_layout(self):
+        """
+        layout
+        """
+        self.vbox.Layout()
+        self.vbox.Fit(self) 
+        self.SetSizer(self.vbox)
+        self.Centre()
+ 
+    def set_owner(self,owner):
+        """ 
+        set owner of fitpage
+        
+        :param owner: the class responsible of plotting
+        
+        """
+        self.event_owner = owner    
+        self.state.event_owner = owner
+        
+    def get_state(self):
+        """
+        """
+        return self.state
+    def get_data(self):
+        """
+        return the current data 
+        """
+        return self.data  
+    
+    def set_manager(self, manager):
+        """
+        set panel manager
+        
+        :param manager: instance of plugin fitting
+        
+        """
+        self._manager = manager  
+        self.state.manager = manager
+        
+    def populate_box(self, dict):
+        """
+        Store list of model
+        
+        :param dict: dictionary containing list of models
+        
+        """
+        self.model_list_box = dict
+        self.state.model_list_box = self.model_list_box
+        
+    def initialize_combox(self): 
+        """
+        put default value in the combobox 
+        """  
+        ## fill combox box
+        if self.model_list_box is None:
+            return
+        if len(self.model_list_box) > 0:
+            self._populate_box(self.formfactorbox,
+                               self.model_list_box["Shapes"])
+       
+        if len(self.model_list_box) > 0:
+            self._populate_box(self.structurebox,
+                                self.model_list_box["Structure Factors"])
+            self.structurebox.Insert("None", 0, None)
+            self.structurebox.SetSelection(0)
+            self.structurebox.Hide()
+            self.text2.Hide()
+            self.structurebox.Disable()
+            self.text2.Disable()
+             
+            if self.model.__class__ in self.model_list_box["P(Q)*S(Q)"]:
+                self.structurebox.Show()
+                self.text2.Show()
+                self.structurebox.Enable()
+                self.text2.Enable()            
+                
+    def set_dispers_sizer(self):
+        """
+        fill sizer containing dispersity info
+        """
+        self.sizer4.Clear(True)
+        name="Polydispersity and Orientational Distribution"
+        box_description= wx.StaticBox(self, -1,name)
+        boxsizer1 = wx.StaticBoxSizer(box_description, wx.VERTICAL)
+        #----------------------------------------------------
+        self.disable_disp = wx.RadioButton(self, -1, 'Off', (10, 10),
+                                            style=wx.RB_GROUP)
+        self.enable_disp = wx.RadioButton(self, -1, 'On', (10, 30))
+       
+        
+        self.Bind(wx.EVT_RADIOBUTTON, self._set_dipers_Param,
+                     id=self.disable_disp.GetId())
+        self.Bind(wx.EVT_RADIOBUTTON, self._set_dipers_Param,
+                   id=self.enable_disp.GetId())
+        #MAC needs SetValue
+        self.disable_disp.SetValue(True)
+        sizer_dispersion = wx.BoxSizer(wx.HORIZONTAL)
+        sizer_dispersion.Add((20,20))
+        name=""#Polydispersity and \nOrientational Distribution "
+        sizer_dispersion.Add(wx.StaticText(self,-1,name))
+        sizer_dispersion.Add(self.enable_disp )
+        sizer_dispersion.Add((20,20))
+        sizer_dispersion.Add(self.disable_disp )
+        sizer_dispersion.Add((10,10))
+        
+        ## fill a sizer with the combobox to select dispersion type
+        sizer_select_dispers = wx.BoxSizer(wx.HORIZONTAL)  
+        self.model_disp = wx.StaticText(self, -1, 'Distribution Function ')
+            
+        import sans.models.dispersion_models 
+        self.polydisp= sans.models.dispersion_models.models
+        self.disp_box = wx.ComboBox(self, -1)
+
+        for key, value in self.polydisp.iteritems():
+            name = str(key)
+            self.disp_box.Append(name,value)
+        self.disp_box.SetStringSelection("gaussian") 
+        wx.EVT_COMBOBOX(self.disp_box,-1, self._on_select_Disp) 
+             
+        sizer_select_dispers.Add((10,10)) 
+        sizer_select_dispers.Add(self.model_disp) 
+        sizer_select_dispers.Add(self.disp_box,0,
+                wx.TOP|wx.BOTTOM|wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE,border=5)
+      
+        self.model_disp.Hide()
+        self.disp_box.Hide()
+        
+        boxsizer1.Add( sizer_dispersion,0,
+                wx.TOP|wx.BOTTOM|wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE,border=5)
+        #boxsizer1.Add( (10,10) )
+        boxsizer1.Add( sizer_select_dispers )
+        self.sizer4_4 = wx.GridBagSizer(5,5)
+        boxsizer1.Add( self.sizer4_4  )
+        #-----------------------------------------------------
+        self.sizer4.Add(boxsizer1,0, wx.EXPAND | wx.ALL, 10)
+        self.sizer4_4.Layout()
+        self.sizer4.Layout()
+        self.Layout()
+     
+        self.Refresh()
+        ## saving the state of enable dispersity button
+        self.state.enable_disp= self.enable_disp.GetValue()
+        self.state.disable_disp= self.disable_disp.GetValue()
+
+    def select_disp_angle(self, event): 
+        """
+        Event for when a user select a parameter to average over.
+        
+        :param event: radiobutton event
+        
+        """
+        self.values=[]
+        self.weights=[]
+        if event.GetEventObject()==self.noDisper_rbox:
+            if self.noDisper_rbox.GetValue():
+                #No array dispersity apply yet
+                self._reset_dispersity()
+                ## Redraw the model ???
+                self._draw_model()
+        # Go through the list of dispersion check boxes to identify
+        # which one has changed 
+        for p in self.disp_cb_dict:
+            self.state.disp_cb_dict[p]=  self.disp_cb_dict[p].GetValue()
+            # Catch which one of the box was just checked or unchecked.
+            if event.GetEventObject() == self.disp_cb_dict[p]:              
+                if self.disp_cb_dict[p].GetValue() == True:
+                    
+                    ##Temp. FIX for V1.0 regarding changing checkbox 
+                    #to radiobutton.
+                    ##This (self._reset_dispersity) should be removed 
+                    #when the array dispersion is fixed.                
+                    self._reset_dispersity()
+
+                    # The user wants this parameter to be averaged. 
+                    # Pop up the file selection dialog.
+                    path = self._selectDlg()
+                    
+                    # If nothing was selected, just return
+                    if path is None:
+                        self.disp_cb_dict[p].SetValue(False)
+                        self.noDisper_rbox.SetValue(True)
+                        return
+                    try:
+                        self._default_save_location = os.path.dirname(path)
+                    except:
+                        pass 
+                    try:
+                        self.values,self.weights = self.read_file(path)
+                    except:
+                        msg="Could not read input file"
+                        wx.PostEvent(self.parent.parent, 
+                                     StatusEvent(status=msg))
+                        return
+                    
+                    # If any of the two arrays is empty, notify the user that we won't
+                    # proceed 
+                    if self.values is None or self.weights is None or \
+                         self.values ==[] or self.weights ==[]:
+                        msg = "The loaded %s distrubtion is"
+                        msg + " corrupted or empty" % p
+                        wx.PostEvent(self.parent.parent, 
+                                     StatusEvent(status=msg))
+                        return
+                        
+                    # Tell the user that we are about to apply the distribution
+                    msg = "Applying loaded %s distribution: %s" % (p, path)
+                    wx.PostEvent(self.parent.parent, StatusEvent(status=msg))  
+                    
+                    # Create the dispersion objects
+                    from sans.models.dispersion_models import ArrayDispersion
+                    disp_model = ArrayDispersion()
+                    disp_model.set_weights(self.values, self.weights)
+                   
+                    # Store the object to make it persist outside the
+                    # scope of this method
+                    #TODO: refactor model to clean this up?
+                    self._disp_obj_dict[p] = disp_model
+                    self.state._disp_obj_dict [p]= disp_model
+                    self.state.values = []
+                    self.state.weights = []
+                    self.state.values = copy.deepcopy(self.values)
+                    self.state.weights = copy.deepcopy(self.weights)
+                    # Set the new model as the dispersion object for the 
+                    #selected parameter
+                    self.model.set_dispersion(p, disp_model)
+                    # Store a reference to the weights in the model object 
+                    #so that
+                    # it's not lost when we use the model within another thread.
+                    #TODO: total hack - fix this
+                    self.state.model= self.model.clone()
+
+                    self.model._persistency_dict = {}
+                    self.model._persistency_dict[p] = \
+                                                    [self.values, self.weights]
+                    self.state.model._persistency_dict[p] = \
+                                                    [self.values,self.weights]
+                else:
+                    self._reset_dispersity()
+              
+                ## Redraw the model
+                self._draw_model()
+        
+        ## post state to fit panel
+        event = PageInfoEvent(page = self)
+        wx.PostEvent(self.parent, event)
+       
+    
+    def onResetModel(self, event):
+        """
+        Reset model state
+        """
+        menu = event.GetEventObject()
+        ## post help message for the selected model 
+        msg = menu.GetHelpString(event.GetId())
+        msg +=" reloaded"
+        wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
+        
+        name = menu.GetLabel(event.GetId())
+        self._on_select_model_helper()
+        
+        if name in self.saved_states.keys():
+            previous_state = self.saved_states[name]
+            ## reset state of checkbox,textcrtl  and  regular parameters value
+            self.reset_page(previous_state)      
+         
+    def on_save(self, event):   
+        """
+        Save the current state into file
+        """  
+        self.save_current_state()
+        new_state = self.state.clone()
+        # Ask the user the location of the file to write to.
+        path = None
+        dlg = wx.FileDialog(self, "Choose a file", self._default_save_location,
+                                                 "", "*.fitv", wx.SAVE)
+        if dlg.ShowModal() == wx.ID_OK:
+            path = dlg.GetPath()
+            self._default_save_location = os.path.dirname(path)
+        else:
+            return None
+        #the manager write the state into file
+        self._manager.save_fit_state(filepath=path, fitstate=new_state)
+        return new_state  
+    
+    def _get_time_stamp(self):
+        """
+        return time and date stings
+        """
+        # date and time 
+        year, month, day,hour,minute,second,tda,ty,tm_isdst= time.localtime()
+        current_time= str(hour)+":"+str(minute)+":"+str(second)
+        current_date= str( month)+"/"+str(day)+"/"+str(year)
+        return current_time, current_date
+      
+    def on_bookmark(self, event):
+        """
+        save history of the data and model
+        """
+        if self.model==None:
+            msg="Can not bookmark; Please select Data and Model first..."
+            wx.MessageBox(msg, 'Info')
+            return 
+        self.save_current_state()
+        new_state = self.state.clone()
+        ##Add model state on context menu
+        self.number_saved_state += 1
+        current_time, current_date = self._get_time_stamp()
+        #name= self.model.name+"[%g]"%self.number_saved_state 
+        name = "Fitting: %g]" % self.number_saved_state 
+        name += self.model.__class__.__name__ 
+        name += "bookmarked at %s on %s" % (current_time, current_date)
+        self.saved_states[name]= new_state
+        
+        ## Add item in the context menu
+        msg =  "Model saved at %s on %s"%(current_time, current_date)
+         ## post help message for the selected model 
+        msg +=" Saved! right click on this page to retrieve this model"
+        wx.PostEvent(self.parent.parent, StatusEvent(status = msg ))
+        
+        #id = wx.NewId()
+        #self.popUpMenu.Append(id,name,str(msg))
+        #wx.EVT_MENU(self, id, self.onResetModel)
+        wx.PostEvent(self.parent.parent, 
+                     AppendBookmarkEvent(title=name, 
+                                         hint=str(msg), handler=self.onResetModel))
+        
+    def old_on_bookmark(self, event):
+        """
+        save history of the data and model
+        """
+        if self.model==None:
+            msg="Can not bookmark; Please select Data and Model first..."
+            wx.MessageBox(msg, 'Info')
+            return 
+        if hasattr(self,"enable_disp"):
+            self.state.enable_disp = copy.deepcopy(self.enable_disp.GetValue())
+        if hasattr(self, "disp_box"):
+            self.state.disp_box = copy.deepcopy(self.disp_box.GetSelection())
+
+        self.state.model.name= self.model.name
+        
+        #Remember fit engine_type for fit panel
+        if self.engine_type == None: 
+            self.engine_type = "scipy"
+        if self._manager !=None:
+            self._manager._on_change_engine(engine=self.engine_type)
+        
+            self.state.engine_type = self.engine_type
+
+        new_state = self.state.clone()
+        new_state.model.name = self.state.model.name
+        
+        new_state.enable2D = copy.deepcopy(self.enable2D)
+        ##Add model state on context menu
+        self.number_saved_state += 1
+        #name= self.model.name+"[%g]"%self.number_saved_state 
+        name= self.model.__class__.__name__+"[%g]"%self.number_saved_state 
+        self.saved_states[name]= new_state
+        
+        ## Add item in the context menu
+        
+        year, month, day,hour,minute,second,tda,ty,tm_isdst= time.localtime()
+        my_time= str(hour)+" : "+str(minute)+" : "+str(second)+" "
+        date= str( month)+"|"+str(day)+"|"+str(year)
+        msg=  "Model saved at %s on %s"%(my_time, date)
+         ## post help message for the selected model 
+        msg +=" Saved! right click on this page to retrieve this model"
+        wx.PostEvent(self.parent.parent, StatusEvent(status = msg ))
+        
+        id = wx.NewId()
+        self.popUpMenu.Append(id,name,str(msg))
+        wx.EVT_MENU(self, id, self.onResetModel)
+        
+    def onSetFocus(self, evt):
+        """
+        highlight the current textcrtl and hide the error text control shown 
+        after fitting
+        """
+        return
+    
+    def read_file(self, path):
+        """
+        Read two columns file
+        
+        :param path: the path to the file to read
+        
+        """
+        try:
+            if path==None:
+                wx.PostEvent(self.parent.parent, StatusEvent(status=\
+                            " Selected Distribution was not loaded: %s"%path))
+                return None, None
+            input_f = open(path, 'r')
+            buff = input_f.read()
+            lines = buff.split('\n')
+            
+            angles = []
+            weights=[]
+            for line in lines:
+                toks = line.split()
+                try:
+                    angle = float(toks[0])
+                    weight = float(toks[1])
+                except:
+                    # Skip non-data lines
+                    pass
+                angles.append(angle)
+                weights.append(weight)
+            return numpy.array(angles), numpy.array(weights)
+        except:
+            raise 
+
+    def createMemento(self):
+        """
+        return the current state of the page
+        """
+        return self.state.clone()
+    
+    
+    def save_current_state(self):
+        """
+        Store current state
+        """
+        self.state.engine_type = copy.deepcopy(self.engine_type)
+        ## save model option
+        if self.model!= None:
+            self.disp_list= self.model.getDispParamList()
+            self.state.disp_list= copy.deepcopy(self.disp_list)
+            self.state.model = self.model.clone()
+        #save radiobutton state for model selection
+        self.state.shape_rbutton = self.shape_rbutton.GetValue()
+        self.state.shape_indep_rbutton = self.shape_indep_rbutton.GetValue()
+        self.state.struct_rbutton = self.struct_rbutton.GetValue()
+        self.state.plugin_rbutton = self.plugin_rbutton.GetValue()
+        #model combobox
+        self.state.structurebox = self.structurebox.GetSelection()
+        self.state.formfactorbox = self.formfactorbox.GetSelection()
+        
+        self.state.enable2D = copy.deepcopy(self.enable2D)
+        self.state.values= copy.deepcopy(self.values)
+        self.state.weights = copy.deepcopy( self.weights)
+        ## save data    
+        self.state.data= copy.deepcopy(self.data)
+        self.state.qmax_x = self.qmax_x
+        self.state.qmin_x = self.qmin_x
+        try:
+            n = self.disp_box.GetCurrentSelection()
+            dispersity= self.disp_box.GetClientData(n)
+            name= dispersity.__name__
+            self.disp_name = name
+            if name == "GaussianDispersion" :
+               if hasattr(self,"cb1"):
+                   self.state.cb1= self.cb1.GetValue()
+        except:
+            pass
+      
+        if hasattr(self,"enable_disp"):
+            self.state.enable_disp= self.enable_disp.GetValue()
+            self.state.disable_disp = self.disable_disp.GetValue()
+            
+        self.state.smearer = copy.deepcopy(self.smearer)
+        if hasattr(self,"enable_smearer"):
+            self.state.enable_smearer = \
+                                copy.deepcopy(self.enable_smearer.GetValue())
+            self.state.disable_smearer = \
+                                copy.deepcopy(self.disable_smearer.GetValue())
+
+        self.state.pinhole_smearer = \
+                                copy.deepcopy(self.pinhole_smearer.GetValue())
+        self.state.slit_smearer = copy.deepcopy(self.slit_smearer.GetValue())  
+                  
+        if hasattr(self,"disp_box"):
+            self.state.disp_box = self.disp_box.GetSelection()
+
+            if len(self.disp_cb_dict)>0:
+                for k , v in self.disp_cb_dict.iteritems():
+         
+                    if v ==None :
+                        self.state.disp_cb_dict[k]= v
+                    else:
+                        try:
+                            self.state.disp_cb_dict[k]=v.GetValue()
+                        except:
+                            self.state.disp_cb_dict[k]= None
+           
+            if len(self._disp_obj_dict)>0:
+                for k , v in self._disp_obj_dict.iteritems():
+      
+                    self.state._disp_obj_dict[k]= v
+                        
+            
+            self.state.values = copy.deepcopy(self.values)
+            self.state.weights = copy.deepcopy(self.weights)
+        ## save plotting range
+        self._save_plotting_range()
+        
+        self.state.orientation_params =[]
+        self.state.orientation_params_disp =[]
+        self.state.parameters =[]
+        self.state.fittable_param =[]
+        self.state.fixed_param =[]
+
+        
+        ## save checkbutton state and txtcrtl values
+        self._copy_parameters_state(self.orientation_params,
+                                     self.state.orientation_params)
+        self._copy_parameters_state(self.orientation_params_disp,
+                                     self.state.orientation_params_disp)
+        
+        self._copy_parameters_state(self.parameters, self.state.parameters)
+        self._copy_parameters_state(self.fittable_param,
+                                     self.state.fittable_param)
+        self._copy_parameters_state(self.fixed_param, self.state.fixed_param)
+        #save chisqr
+        self.state.tcChi = self.tcChi.GetValue()
+        
+    def save_current_state_fit(self):
+        """
+        Store current state for fit_page
+        """
+        ## save model option
+        if self.model!= None:
+            self.disp_list= self.model.getDispParamList()
+            self.state.disp_list= copy.deepcopy(self.disp_list)
+            self.state.model = self.model.clone()
+        if hasattr(self, "engine_type"):
+            self.state.engine_type = copy.deepcopy(self.engine_type)
+            
+        self.state.enable2D = copy.deepcopy(self.enable2D)
+        self.state.values= copy.deepcopy(self.values)
+        self.state.weights = copy.deepcopy( self.weights)
+        ## save data    
+        self.state.data= copy.deepcopy(self.data)
+        try:
+            n = self.disp_box.GetCurrentSelection()
+            dispersity= self.disp_box.GetClientData(n)
+            name= dispersity.__name__
+            self.disp_name = name
+            if name == "GaussianDispersion" :
+               if hasattr(self,"cb1"):
+                   self.state.cb1= self.cb1.GetValue()
+
+        except:
+            pass
+        
+        if hasattr(self,"enable_disp"):
+            self.state.enable_disp= self.enable_disp.GetValue()
+            self.state.disable_disp = self.disable_disp.GetValue()
+            
+        self.state.smearer = copy.deepcopy(self.smearer)
+        if hasattr(self,"enable_smearer"):
+            self.state.enable_smearer = \
+                                copy.deepcopy(self.enable_smearer.GetValue())
+            self.state.disable_smearer = \
+                                copy.deepcopy(self.disable_smearer.GetValue())
+            
+        self.state.pinhole_smearer = \
+                                copy.deepcopy(self.pinhole_smearer.GetValue())
+        self.state.slit_smearer = copy.deepcopy(self.slit_smearer.GetValue())  
+            
+        if hasattr(self,"disp_box"):
+            self.state.disp_box = self.disp_box.GetCurrentSelection()
+
+            if len(self.disp_cb_dict) > 0:
+                for k, v in self.disp_cb_dict.iteritems():
+         
+                    if v == None :
+                        self.state.disp_cb_dict[k] = v
+                    else:
+                        try:
+                            self.state.disp_cb_dict[k] = v.GetValue()
+                        except:
+                            self.state.disp_cb_dict[k] = None
+           
+            if len(self._disp_obj_dict) > 0:
+                for k , v in self._disp_obj_dict.iteritems():
+      
+                    self.state._disp_obj_dict[k] = v
+                        
+            
+            self.state.values = copy.deepcopy(self.values)
+            self.state.weights = copy.deepcopy(self.weights)
+           
+        ## save plotting range
+        self._save_plotting_range()
+        
+        ## save checkbutton state and txtcrtl values
+        self._copy_parameters_state(self.orientation_params,
+                                     self.state.orientation_params)
+        self._copy_parameters_state(self.orientation_params_disp,
+                                     self.state.orientation_params_disp)
+        self._copy_parameters_state(self.parameters, self.state.parameters)
+        self._copy_parameters_state(self.fittable_param,
+                                             self.state.fittable_param)
+        self._copy_parameters_state(self.fixed_param, self.state.fixed_param)
+    
+          
+    def check_invalid_panel(self):  
+        """
+        check if the user can already perform some action with this panel
+        """ 
+        flag = False
+        if self.data is None:
+            self.disable_smearer.SetValue(True)
+            self.disable_disp.SetValue(True)
+            msg = "Please load Data and select Model to start..."
+            wx.MessageBox(msg, 'Info')
+            return  True
+        
+           
+    def reset_page_helper(self, state):
+        """
+        Use page_state and change the state of existing page
+        
+        :precondition: the page is already drawn or created
+        
+        :postcondition: the state of the underlying data change as well as the
+            state of the graphic interface
+        """
+        if state == None:
+            #self._undo.Enable(False)
+            return 
+    
+        self.set_data(state.data)
+        self.enable2D= state.enable2D
+        self.engine_type = state.engine_type
+
+        self.disp_cb_dict = state.disp_cb_dict
+        self.disp_list = state.disp_list
+      
+        ## set the state of the radio box
+        self.shape_rbutton.SetValue(state.shape_rbutton )
+        self.shape_indep_rbutton.SetValue(state.shape_indep_rbutton)
+        self.struct_rbutton.SetValue(state.struct_rbutton)
+        self.plugin_rbutton.SetValue(state.plugin_rbutton)
+        
+        ## fill model combobox
+        self._show_combox_helper()
+        #select the current model
+        self.formfactorbox.Select(int(state.formfactorcombobox))
+        self.structurebox.SetSelection(state.structurecombobox )
+        if state.multi_factor != None:
+            self.multifactorbox.SetSelection(state.multi_factor)
+
+        #reset the fitting engine type
+        self.engine_type = state.engine_type
+        #draw the pnael according to the new model parameter 
+        self._on_select_model(event=None)
+       
+        if self._manager !=None:
+            self._manager._on_change_engine(engine=self.engine_type)
+        ## set the select all check box to the a given state
+        self.cb1.SetValue(state.cb1)
+     
+        ## reset state of checkbox,textcrtl  and  regular parameters value
+        self._reset_parameters_state(self.orientation_params_disp,
+                                     state.orientation_params_disp)
+        self._reset_parameters_state(self.orientation_params,
+                                     state.orientation_params)
+        self._reset_parameters_state(self.str_parameters,
+                                     state.str_parameters)
+        self._reset_parameters_state(self.parameters,state.parameters)    
+         ## display dispersion info layer        
+        self.enable_disp.SetValue(state.enable_disp)
+        self.disable_disp.SetValue(state.disable_disp)
+        
+        if hasattr(self, "disp_box"):
+            
+            self.disp_box.SetSelection(state.disp_box) 
+            n= self.disp_box.GetCurrentSelection()
+            dispersity= self.disp_box.GetClientData(n)
+            name = dispersity.__name__     
+
+            self._set_dipers_Param(event=None)
+       
+            if name == "ArrayDispersion":
+                
+                for item in self.disp_cb_dict.keys():
+                    
+                    if hasattr(self.disp_cb_dict[item], "SetValue") :
+                        self.disp_cb_dict[item].SetValue(\
+                                                    state.disp_cb_dict[item])
+                        # Create the dispersion objects
+                        from sans.models.dispersion_models import ArrayDispersion
+                        disp_model = ArrayDispersion()
+                        if hasattr(state,"values")and\
+                                 self.disp_cb_dict[item].GetValue() == True:
+                            if len(state.values)>0:
+                                self.values=state.values
+                                self.weights=state.weights
+                                disp_model.set_weights(self.values,
+                                                        state.weights)
+                            else:
+                                self._reset_dispersity()
+                        
+                        self._disp_obj_dict[item] = disp_model
+                        # Set the new model as the dispersion object 
+                        #for the selected parameter
+                        self.model.set_dispersion(item, disp_model)
+                    
+                        self.model._persistency_dict[item] = \
+                                                [state.values, state.weights]
+                    
+            else:
+                keys = self.model.getParamList()
+                for item in keys:
+                    if item in self.disp_list and \
+                        not self.model.details.has_key(item):
+                        self.model.details[item] = ["", None, None]
+                for k,v in self.state.disp_cb_dict.iteritems():
+                    self.disp_cb_dict = copy.deepcopy(state.disp_cb_dict) 
+                    self.state.disp_cb_dict = copy.deepcopy(state.disp_cb_dict)
+
+        ##plotting range restore    
+        self._reset_plotting_range(state)
+        ## smearing info  restore
+        if hasattr(self, "enable_smearer"):
+            ## set smearing value whether or not the data 
+            #contain the smearing info
+            self.enable_smearer.SetValue(state.enable_smearer)
+            self.disable_smearer.SetValue(state.disable_smearer)
+            self.onSmear(event=None)           
+        self.pinhole_smearer.SetValue(state.pinhole_smearer)
+        self.slit_smearer.SetValue(state.slit_smearer)
+        ## we have two more options for smearing
+        if self.pinhole_smearer.GetValue(): self.onPinholeSmear(event=None)
+        elif self.slit_smearer.GetValue(): self.onSlitSmear(event=None)
+       
+        ## reset state of checkbox,textcrtl  and dispersity parameters value
+        self._reset_parameters_state(self.fittable_param,state.fittable_param)
+        self._reset_parameters_state(self.fixed_param,state.fixed_param)
+        
+        ## draw the model with previous parameters value
+        self._onparamEnter_helper()
+        #reset the value of chisqr when not consistent with the value computed
+        self.tcChi.SetValue(str(self.state.tcChi))
+        ## reset context menu items
+        self._reset_context_menu()
+        
+        ## set the value of the current state to the state given as parameter
+        self.state = state.clone() 
+    
+        
+    def old_reset_page_helper(self, state):
+        """
+        Use page_state and change the state of existing page
+        
+        :precondition: the page is already drawn or created
+        
+        :postcondition: the state of the underlying data change as well as the
+            state of the graphic interface
+        """
+        if state ==None:
+            #self._undo.Enable(False)
+            return 
+       
+        self.model= state.model
+        self.data = state.data
+        if self.data !=None:
+            from DataLoader.qsmearing import smear_selection
+            self.smearer= smear_selection(self.data, self.model)
+        self.enable2D= state.enable2D
+        self.engine_type = state.engine_type
+
+        self.disp_cb_dict = state.disp_cb_dict
+        self.disp_list = state.disp_list
+
+        ## set the state of the radio box
+        self.shape_rbutton.SetValue(state.shape_rbutton )
+        self.shape_indep_rbutton.SetValue(state.shape_indep_rbutton)
+        self.struct_rbutton.SetValue(state.struct_rbutton )
+        self.plugin_rbutton.SetValue(state.plugin_rbutton)
+        ##draw sizer containing model parameters value for the current model
+        self._set_model_sizer_selection( self.model )
+        self.set_model_param_sizer(self.model)
+
+        ## reset value of combox box
+        self.structurebox.SetSelection(state.structurecombobox )
+        self.formfactorbox.SetSelection(state.formfactorcombobox)
+        
+        
+        ## enable the view 2d button if this is a modelpage type
+        if hasattr(self,"model_view"):
+            if self.enable2D:
+                self.model_view.Disable()
+            else:
+                self.model_view.Enable()
+        ## set the select all check box to the a given state
+        if hasattr(self, "cb1"):   
+            self.cb1.SetValue(state.cb1)
+     
+        ## reset state of checkbox,textcrtl  and  regular parameters value
+            
+        self._reset_parameters_state(self.orientation_params_disp,
+                                     state.orientation_params_disp)
+        self._reset_parameters_state(self.orientation_params,
+                                     state.orientation_params)
+        self._reset_parameters_state(self.parameters,state.parameters)    
+         ## display dispersion info layer        
+        self.enable_disp.SetValue(state.enable_disp)
+        self.disable_disp.SetValue(state.disable_disp)
+        
+        if hasattr(self, "disp_box"):
+            
+            self.disp_box.SetSelection(state.disp_box) 
+            n= self.disp_box.GetCurrentSelection()
+            dispersity= self.disp_box.GetClientData(n)
+            name= dispersity.__name__     
+
+            self._set_dipers_Param(event=None)
+       
+            if name=="ArrayDispersion":
+                
+                for item in self.disp_cb_dict.keys():
+                    
+                    if hasattr(self.disp_cb_dict[item],"SetValue") :
+                        self.disp_cb_dict[item].SetValue(state.disp_cb_dict[item])
+                        # Create the dispersion objects
+                        from sans.models.dispersion_models import ArrayDispersion
+                        disp_model = ArrayDispersion()
+                        if hasattr(state,"values") and\
+                             self.disp_cb_dict[item].GetValue()==True:
+                            if len(state.values) > 0:
+                                self.values = state.values
+                                self.weights = state.weights
+                                disp_model.set_weights(self.values,
+                                                        state.weights)
+                            else:
+                                self._reset_dispersity()
+                        
+                        self._disp_obj_dict[item] = disp_model
+                        # Set the new model as the dispersion
+                        #object for the selected parameter
+                        self.model.set_dispersion(item, disp_model)
+                    
+                        self.model._persistency_dict[item] = [state.values,
+                                                               state.weights]
+                    
+            else:
+                keys = self.model.getParamList()
+                for item in keys:
+                    if item in self.disp_list and \
+                            not self.model.details.has_key(item):
+                        self.model.details[item]=["",None,None]
+                for k,v in self.state.disp_cb_dict.iteritems():
+                    self.disp_cb_dict = copy.deepcopy(state.disp_cb_dict) 
+                    self.state.disp_cb_dict = copy.deepcopy(state.disp_cb_dict)
+
+        ##plotting range restore    
+        self._reset_plotting_range(state)
+
+        ## smearing info  restore
+        if hasattr(self,"enable_smearer"):
+            ## set smearing value whether or not the data 
+            #contain the smearing info
+            self.enable_smearer.SetValue(state.enable_smearer)
+            self.disable_smearer.SetValue(state.disable_smearer)
+            self.onSmear(event=None)           
+        self.pinhole_smearer.SetValue(state.pinhole_smearer)
+        self.slit_smearer.SetValue(state.slit_smearer)
+        ## we have two more options for smearing
+        if self.pinhole_smearer.GetValue(): self.onPinholeSmear(event=None)
+        elif self.slit_smearer.GetValue(): self.onSlitSmear(event=None)
+       
+        ## reset state of checkbox,textcrtl  and dispersity parameters value
+        self._reset_parameters_state(self.fittable_param,state.fittable_param)
+        self._reset_parameters_state(self.fixed_param,state.fixed_param)
+        
+        ## draw the model with previous parameters value
+        self._onparamEnter_helper()
+        
+        ## reset context menu items
+        self._reset_context_menu()
+    
+        ## set the value of the current state to the state given as parameter
+        self.state = state.clone() 
+        self._draw_model()
+
+    def _selectDlg(self):
+        """
+        open a dialog file to selected the customized dispersity 
+        """
+        import os
+        dlg = wx.FileDialog(self, "Choose a weight file",
+                                self._default_save_location , "", 
+                                "*.*", wx.OPEN)
+        path = None
+        if dlg.ShowModal() == wx.ID_OK:
+            path = dlg.GetPath()
+        dlg.Destroy()
+        return path
+
+    def _reset_context_menu(self):
+        """
+        reset the context menu
+        """
+        for name, state in self.state.saved_states.iteritems():
+            self.number_saved_state += 1
+            ## Add item in the context menu
+            id = wx.NewId()
+            msg = 'Save model and state %g' % self.number_saved_state
+            self.popUpMenu.Append(id, name, msg)
+            wx.EVT_MENU(self, id, self.onResetModel)
+    
+    def _reset_plotting_range(self, state):
+        """
+        Reset the plotting range to a given state
+        """
+        if self.check_invalid_panel():
+            return
+        self.qmin.SetValue(str(state.qmin))
+        self.qmax.SetValue(str(state.qmax)) 
+
+    def _save_typeOfmodel(self):
+        """
+        save radiobutton containing the type model that can be selected
+        """
+        self.state.shape_rbutton = self.shape_rbutton.GetValue()
+        self.state.shape_indep_rbutton = self.shape_indep_rbutton.GetValue()
+        self.state.struct_rbutton = self.struct_rbutton.GetValue()
+        self.state.plugin_rbutton = self.plugin_rbutton.GetValue()
+        self.state.structurebox= self.structurebox.GetCurrentSelection()
+        self.state.formfactorbox = self.formfactorbox.GetCurrentSelection()
+       
+        #self._undo.Enable(True)
+        ## post state to fit panel
+        event = PageInfoEvent(page = self)
+        wx.PostEvent(self.parent, event)
+        
+    def _save_plotting_range(self ):
+        """
+        save the state of plotting range 
+        """
+        self.state.qmin = self.qmin_x
+        self.state.qmax = self.qmax_x 
+        if self.npts != None:
+            self.state.npts = self.num_points
+            
+    def _onparamEnter_helper(self):
+        """
+        check if values entered by the user are changed and valid to replot 
+        model
+        """
+        # Flag to register when a parameter has changed.   
+        is_modified = False
+        self.fitrange = True
+        is_2Ddata = False
+        #self._undo.Enable(True)
+        # check if 2d data
+        if self.data.__class__.__name__ == "Data2D":
+            is_2Ddata = True
+        if self.model !=None:
+            try:
+                is_modified = self._check_value_enter(self.fittable_param,
+                                                     is_modified)
+                is_modified = self._check_value_enter(self.fixed_param,
+                                                      is_modified)
+                is_modified = self._check_value_enter(self.parameters,
+                                                      is_modified) 
+            except:
+                pass
+            #if is_modified:
+
+            # Here we should check whether the boundaries have been modified.
+            # If qmin and qmax have been modified, update qmin and qmax and 
+            # set the is_modified flag to True
+            if self._validate_qrange(self.qmin, self.qmax):
+                tempmin = float(self.qmin.GetValue())
+                if tempmin != self.qmin_x:
+                    self.qmin_x = tempmin
+                    is_modified = True
+                tempmax = float(self.qmax.GetValue())
+                if tempmax != self.qmax_x:
+                    self.qmax_x = tempmax
+                    is_modified = True
+            
+                if is_2Ddata:
+                    # set mask   
+                    is_modified = self._validate_Npts()
+                    
+            else:
+                self.fitrange = False    
+
+            ## if any value is modify draw model with new value
+            if not self.fitrange:
+                #self.btFit.Disable()
+                if is_2Ddata: self.btEditMask.Disable()
+            else:
+                #self.btFit.Enable(True)
+                if is_2Ddata: self.btEditMask.Enable(True)
+
+            if is_modified and self.fitrange:
+                self.state_change= True
+                self._draw_model() 
+                self.Refresh()
+        return is_modified
+    
+    def _update_paramv_on_fit(self):
+        """
+        make sure that update param values just before the fitting
+        """
+        #flag for qmin qmax check values
+        flag = True
+        self.fitrange = True
+        is_modified = False
+
+        wx.PostEvent(self._manager.parent, StatusEvent(status=" \
+        updating ... ",type="update"))
+
+        ##So make sure that update param values on_Fit.
+        #self._undo.Enable(True)
+        if self.model !=None:           
+            ##Check the values
+            self._check_value_enter( self.fittable_param ,is_modified)
+            self._check_value_enter( self.fixed_param ,is_modified)
+            self._check_value_enter( self.parameters ,is_modified)
+
+            # If qmin and qmax have been modified, update qmin and qmax and 
+             # Here we should check whether the boundaries have been modified.
+            # If qmin and qmax have been modified, update qmin and qmax and 
+            # set the is_modified flag to True
+            self.fitrange = self._validate_qrange(self.qmin, self.qmax)
+            if self.fitrange:
+                tempmin = float(self.qmin.GetValue())
+                if tempmin != self.qmin_x:
+                    self.qmin_x = tempmin
+                tempmax = float(self.qmax.GetValue())
+                if tempmax != self.qmax_x:
+                    self.qmax_x = tempmax
+                if tempmax == tempmin:
+                    flag = False    
+                temp_smearer = None
+                if not self.disable_smearer.GetValue():
+                    temp_smearer= self.current_smearer
+                    if self.slit_smearer.GetValue():
+                        flag = self.update_slit_smear()
+                    elif self.pinhole_smearer.GetValue():
+                        flag = self.update_pinhole_smear()
+                    else:
+                        self._manager.set_smearer_nodraw(smearer=temp_smearer,
+                                                     qmin=float(self.qmin_x),
+                                                      qmax=float(self.qmax_x))
+                elif not self._is_2D():
+                    self._manager.set_smearer(smearer=temp_smearer,
+                                              qmin=float(self.qmin_x),
+                                                 qmax= float(self.qmax_x))
+                    index_data = ((self.qmin_x <= self.data.x)&\
+                                  (self.data.x <= self.qmax_x))
+                    val = str(len(self.data.x[index_data==True]))
+                    self.Npts_fit.SetValue(val)
+                    flag = True
+                if self._is_2D():
+                    # only 2D case set mask   
+                    flag = self._validate_Npts()
+                    if not flag:
+                        return flag
+            else: flag = False
+        else: 
+            flag = False
+
+        #For invalid q range, disable the mask editor and fit button, vs.    
+        if not self.fitrange:
+            #self.btFit.Disable()
+            if self._is_2D():self.btEditMask.Disable()
+        else:
+            #self.btFit.Enable(True)
+            if self._is_2D():self.btEditMask.Enable(True)
+
+        if not flag:
+            msg = "Cannot Plot or Fit :Must select a "
+            msg += " model or Fitting range is not valid!!!  "
+            wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
+        
+        self.save_current_state()
+   
+        return flag                           
+               
+    def _is_modified(self, is_modified):
+        """
+        return to self._is_modified
+        """
+        return is_modified
+                       
+    def _reset_parameters_state(self, listtorestore, statelist):
+        """
+        Reset the parameters at the given state
+        """
+        if len(statelist) == 0 or len(listtorestore) == 0:
+            return
+        if len(statelist) !=  len(listtorestore):
+            return
+
+        for j in range(len(listtorestore)):
+            item_page = listtorestore[j]
+            item_page_info = statelist[j]
+            ##change the state of the check box for simple parameters
+            if item_page[0]!=None:   
+                item_page[0].SetValue(item_page_info[0])
+            if item_page[2]!=None:
+                item_page[2].SetValue(item_page_info[2])
+            if item_page[3]!=None:
+                ## show or hide text +/-
+                if item_page_info[2]:
+                    item_page[3].Show(True)
+                else:
+                    item_page[3].Hide()
+            if item_page[4]!=None:
+                ## show of hide the text crtl for fitting error
+                if item_page_info[4][0]:
+                    item_page[4].Show(True)
+                    item_page[4].SetValue(item_page_info[4][1])
+                else:
+                    item_page[3].Hide()
+            if item_page[5]!=None:
+                ## show of hide the text crtl for fitting error
+                if item_page_info[5][0]:
+                    item_page[5].Show(True)
+                    item_page[5].SetValue(item_page_info[5][1])
+                else:
+                    item_page[5].Hide()
+                    
+            if item_page[6]!=None:
+                ## show of hide the text crtl for fitting error
+                if item_page_info[6][0]:
+                    item_page[6].Show(True)
+                    item_page[6].SetValue(item_page_info[6][1])
+                else:
+                    item_page[6].Hide()
+                                      
+    def _copy_parameters_state(self, listtocopy, statelist):
+        """
+        copy the state of button 
+        
+        :param listtocopy: the list of check button to copy
+        :param statelist: list of state object to store the current state
+        
+        """
+        if len(listtocopy)==0:
+            return
+       
+        for item in listtocopy:
+  
+            checkbox_state = None
+            if item[0]!= None:
+                checkbox_state= item[0].GetValue()
+            parameter_name = item[1]
+            parameter_value = None
+            if item[2]!=None:
+                parameter_value = item[2].GetValue()
+            static_text = None
+            if item[3]!=None:
+                static_text = item[3].IsShown()
+            error_value = None
+            error_state = None
+            if item[4]!= None:
+                error_value = item[4].GetValue()
+                error_state = item[4].IsShown()
+                
+            min_value = None
+            min_state = None
+            if item[5]!= None:
+                min_value = item[5].GetValue()
+                min_state = item[5].IsShown()
+                
+            max_value = None
+            max_state = None
+            if item[6]!= None:
+                max_value = item[6].GetValue()
+                max_state = item[6].IsShown()
+            unit=None
+            if item[7]!=None:
+                unit = item[7].GetLabel()
+               
+            statelist.append([checkbox_state, parameter_name, parameter_value,
+                              static_text ,[error_state, error_value],
+                                [min_state, min_value],
+                                [max_state, max_value], unit])
+           
+    def _set_model_sizer_selection(self, model):
+        """
+        Display the sizer according to the type of the current model
+        """
+        if model == None:
+            return
+        if hasattr(model ,"s_model"):
+            
+            class_name = model.s_model.__class__
+            name = model.s_model.name
+            flag = (name != "NoStructure")
+            if flag and \
+                (class_name in self.model_list_box["Structure Factors"]):
+                self.structurebox.Show()
+                self.text2.Show()                
+                self.structurebox.Enable()
+                self.text2.Enable()
+                items = self.structurebox.GetItems()
+                self.sizer1.Layout()
+                
+                for i in range(len(items)):
+                    if items[i]== str(name):
+                        self.structurebox.SetSelection(i)
+                        break
+                    
+        if hasattr(model ,"p_model"):
+            class_name = model.p_model.__class__
+            name = model.p_model.name
+            self.formfactorbox.Clear()
+            
+            for k, list in self.model_list_box.iteritems():
+                if k in["P(Q)*S(Q)","Shapes" ] and class_name in self.model_list_box["Shapes"]:
+                    self.shape_rbutton.SetValue(True)
+                    ## fill the form factor list with new model
+                    self._populate_box(self.formfactorbox,self.model_list_box["Shapes"])
+                    items = self.formfactorbox.GetItems()
+                    ## set comboxbox to the selected item
+                    for i in range(len(items)):
+                        if items[i]== str(name):
+                            self.formfactorbox.SetSelection(i)
+                            break
+                    return
+                elif k == "Shape-Independent":
+                    self.shape_indep_rbutton.SetValue(True)
+                elif k == "Structure Factors":
+                     self.struct_rbutton.SetValue(True)
+                elif  k == "Multi-Functions":
+                    continue
+                else:
+                    self.plugin_rbutton.SetValue(True)
+               
+                if class_name in list:
+                    ## fill the form factor list with new model
+                    self._populate_box(self.formfactorbox, list)
+                    items = self.formfactorbox.GetItems()
+                    ## set comboxbox to the selected item
+                    for i in range(len(items)):
+                        if items[i]== str(name):
+                            self.formfactorbox.SetSelection(i)
+                            break
+                    break
+        else:
+
+            ## Select the model from the menu
+            class_name = model.__class__
+            name = model.name
+            self.formfactorbox.Clear()
+            items = self.formfactorbox.GetItems()
+    
+            for k, list in self.model_list_box.iteritems():          
+                if k in["P(Q)*S(Q)","Shapes" ] and class_name in self.model_list_box["Shapes"]:
+                    if class_name in self.model_list_box["P(Q)*S(Q)"]:
+                        self.structurebox.Show()
+                        self.text2.Show()
+                        self.structurebox.Enable()
+                        self.structurebox.SetSelection(0)
+                        self.text2.Enable()
+                    else:
+                        self.structurebox.Hide()
+                        self.text2.Hide()
+                        self.structurebox.Disable()
+                        self.structurebox.SetSelection(0)
+                        self.text2.Disable()
+                        
+                    self.shape_rbutton.SetValue(True)
+                    ## fill the form factor list with new model
+                    self._populate_box(self.formfactorbox,self.model_list_box["Shapes"])
+                    items = self.formfactorbox.GetItems()
+                    ## set comboxbox to the selected item
+                    for i in range(len(items)):
+                        if items[i]== str(name):
+                            self.formfactorbox.SetSelection(i)
+                            break
+                    return
+                elif k == "Shape-Independent":
+                    self.shape_indep_rbutton.SetValue(True)
+                elif k == "Structure Factors":
+                    self.struct_rbutton.SetValue(True)
+                elif  k == "Multi-Functions":
+                    continue
+                else:
+                    self.plugin_rbutton.SetValue(True)
+                if class_name in list:
+                    self.structurebox.SetSelection(0)
+                    self.structurebox.Disable()
+                    self.text2.Disable()                    
+                    ## fill the form factor list with new model
+                    self._populate_box(self.formfactorbox, list)
+                    items = self.formfactorbox.GetItems()
+                    ## set comboxbox to the selected item
+                    for i in range(len(items)):
+                        if items[i]== str(name):
+                            self.formfactorbox.SetSelection(i)
+                            break
+                    break
+    
+    def _draw_model(self):
+        """
+        Method to draw or refresh a plotted model.
+        The method will use the data member from the model page
+        to build a call to the fitting perspective manager.
+        """
+        if self.check_invalid_panel():
+            return
+        if self.model !=None:
+            temp_smear=None
+            if hasattr(self, "enable_smearer"):
+                if not self.disable_smearer.GetValue():
+                    temp_smear= self.current_smearer
+            
+            self._manager.draw_model(self.model, 
+                                    data=self.data,
+                                    smearer= temp_smear,
+                                    qmin=float(self.qmin_x), 
+                                    qmax=float(self.qmax_x),
+                                    qstep= float(self.num_points),
+                                    enable2D=self.enable2D)
+        
+    def _set_model_sizer(self,sizer, box_sizer, title="", object=None):
+        """
+        Use lists to fill a sizer for model info
+        """
+       
+        sizer.Clear(True)
+        ##For MAC, this should defined here.
+        if box_sizer == None:
+            box_description= wx.StaticBox(self, -1,str(title))
+            boxsizer1 = wx.StaticBoxSizer(box_description, wx.VERTICAL)
+        else:
+            boxsizer1 = box_sizer
+        sizer_buttons = wx.BoxSizer(wx.HORIZONTAL)    
+        #--------------------------------------------------------
+        self.shape_rbutton = wx.RadioButton(self, -1, 'Shapes', style=wx.RB_GROUP)
+        self.shape_indep_rbutton = wx.RadioButton(self, -1, "Shape-Independent")
+        self.struct_rbutton = wx.RadioButton(self, -1, "Structure Factor ")
+        self.plugin_rbutton = wx.RadioButton(self, -1, "Customized Models")
+                
+        self.Bind( wx.EVT_RADIOBUTTON, self._show_combox,
+                            id= self.shape_rbutton.GetId() ) 
+        self.Bind( wx.EVT_RADIOBUTTON, self._show_combox,
+                            id= self.shape_indep_rbutton.GetId() ) 
+        self.Bind( wx.EVT_RADIOBUTTON, self._show_combox,
+                            id= self.struct_rbutton.GetId() ) 
+        self.Bind( wx.EVT_RADIOBUTTON, self._show_combox,
+                            id= self.plugin_rbutton.GetId() )  
+        #MAC needs SetValue
+        self.shape_rbutton.SetValue(True)
+      
+        sizer_radiobutton = wx.GridSizer(2, 2,5, 5)
+        sizer_radiobutton.Add(self.shape_rbutton)
+        sizer_radiobutton.Add(self.shape_indep_rbutton)
+
+        sizer_radiobutton.Add(self.plugin_rbutton)
+        sizer_radiobutton.Add(self.struct_rbutton)
+        sizer_buttons.Add(sizer_radiobutton)
+        # detail button
+        if object !=None:
+            sizer_buttons.Add((50,0))
+            sizer_buttons.Add(object)
+        
+        sizer_selection = wx.BoxSizer(wx.HORIZONTAL)
+        mutifactor_selection = wx.BoxSizer(wx.HORIZONTAL)
+        
+        self.text1 = wx.StaticText( self,-1,"" )
+        self.text2 = wx.StaticText( self,-1,"P(Q)*S(Q)" )
+        self.mutifactor_text = wx.StaticText( self,-1,"No. of Shells: " )
+        self.mutifactor_text1 = wx.StaticText( self,-1,"" )
+        self.show_sld_button = wx.Button( self,-1,"Show SLD Profile" )
+        self.show_sld_button.Bind(wx.EVT_BUTTON,self._on_show_sld)
+
+        self.formfactorbox = wx.ComboBox(self, -1,style=wx.CB_READONLY)
+        if self.model!= None:
+            self.formfactorbox.SetValue(self.model.name)
+           
+        self.structurebox = wx.ComboBox(self, -1,style=wx.CB_READONLY)
+        self.multifactorbox = wx.ComboBox(self, -1,style=wx.CB_READONLY)
+        self.initialize_combox()
+             
+        wx.EVT_COMBOBOX(self.formfactorbox,-1, self._on_select_model)
+        wx.EVT_COMBOBOX(self.structurebox,-1, self._on_select_model)
+        wx.EVT_COMBOBOX(self.multifactorbox,-1, self._on_select_model)
+        
+        ## check model type to show sizer
+        if self.model !=None:
+            self._set_model_sizer_selection( self.model )
+        
+        sizer_selection.Add(self.text1)
+        sizer_selection.Add((5,5))
+        sizer_selection.Add(self.formfactorbox)
+        sizer_selection.Add((5,5))
+        sizer_selection.Add(self.text2)
+        sizer_selection.Add((5,5))
+        sizer_selection.Add(self.structurebox)
+        #sizer_selection.Add((5,5))
+        mutifactor_selection.Add((10,5))
+        mutifactor_selection.Add(self.mutifactor_text)
+        mutifactor_selection.Add(self.multifactorbox)
+        mutifactor_selection.Add((5,5))
+        mutifactor_selection.Add(self.mutifactor_text1)
+        mutifactor_selection.Add((10,5))
+        mutifactor_selection.Add(self.show_sld_button)
+
+        boxsizer1.Add( sizer_buttons )
+        boxsizer1.Add( (15,15))
+        boxsizer1.Add( sizer_selection )
+        boxsizer1.Add( (10,10))
+        boxsizer1.Add(mutifactor_selection)
+        
+        self._set_multfactor_combobox()
+        self.multifactorbox.SetSelection(1)
+        self.show_sld_button.Hide()
+        #--------------------------------------------------------
+        sizer.Add(boxsizer1,0, wx.EXPAND | wx.ALL, 10)
+        sizer.Layout()
+       
+    def _on_show_sld(self, event=None):
+        """
+        Plot SLD profile
+        """
+        # get profile data
+        x,y=self.model.getProfile()
+
+        from danse.common.plottools import Data1D
+        #from sans.perspectives.theory.profile_dialog import SLDPanel
+        from sans.guiframe.local_perspectives.plotting.profile_dialog \
+        import SLDPanel
+        sld_data = Data1D(x,y)
+        sld_data.name = 'SLD'
+        sld_data.axes = self.sld_axes
+        self.panel = SLDPanel(self, data=sld_data,axes =self.sld_axes,id =-1 )
+        self.panel.ShowModal()    
+        
+    def _set_multfactor_combobox(self, multiplicity=10):   
+        """
+        Set comboBox for muitfactor of CoreMultiShellModel
+        :param multiplicit: no. of multi-functionality
+        """
+        # build content of the combobox
+        for idx in range(0,multiplicity):
+            self.multifactorbox.Append(str(idx),int(idx))
+            #self.multifactorbox.SetSelection(1) 
+        self._hide_multfactor_combobox()
+        
+    def _show_multfactor_combobox(self):   
+        """
+        Show the comboBox of muitfactor of CoreMultiShellModel
+        """ 
+        if not self.mutifactor_text.IsShown():
+            self.mutifactor_text.Show(True)
+            self.mutifactor_text1.Show(True)
+        if not self.multifactorbox.IsShown():
+            self.multifactorbox.Show(True)  
+             
+    def _hide_multfactor_combobox(self):   
+        """
+        Hide the comboBox of muitfactor of CoreMultiShellModel
+        """ 
+        if self.mutifactor_text.IsShown():
+            self.mutifactor_text.Hide()
+            self.mutifactor_text1.Hide()
+        if self.multifactorbox.IsShown():
+            self.multifactorbox.Hide()   
+
+        
+    def _show_combox_helper(self):
+        """
+        Fill panel's combo box according to the type of model selected
+        """
+        if self.shape_rbutton.GetValue():
+            ##fill the combobox with form factor list
+            self.structurebox.SetSelection(0)
+            self.structurebox.Disable()
+            self.formfactorbox.Clear()
+            self._populate_box( self.formfactorbox,self.model_list_box["Shapes"])
+        if self.shape_indep_rbutton.GetValue():
+            ##fill the combobox with shape independent  factor list
+            self.structurebox.SetSelection(0)
+            self.structurebox.Disable()
+            self.formfactorbox.Clear()
+            self._populate_box( self.formfactorbox,
+                                self.model_list_box["Shape-Independent"])
+        if self.struct_rbutton.GetValue():
+            ##fill the combobox with structure factor list
+            self.structurebox.SetSelection(0)
+            self.structurebox.Disable()
+            self.formfactorbox.Clear()
+            self._populate_box( self.formfactorbox,
+                                self.model_list_box["Structure Factors"])
+        if self.plugin_rbutton.GetValue():
+            ##fill the combobox with form factor list
+            self.structurebox.Disable()
+            self.formfactorbox.Clear()
+            self._populate_box( self.formfactorbox,
+                                self.model_list_box["Customized Models"])
+        
+    def _show_combox(self, event=None):
+        """
+        Show combox box associate with type of model selected
+        """
+        if self.check_invalid_panel():
+            self.shape_rbutton.SetValue(True)
+            return
+
+        self._show_combox_helper()
+        self._on_select_model(event=None)
+        self._save_typeOfmodel()
+        self.sizer4_4.Layout()
+        self.sizer4.Layout()
+        self.Layout()
+        self.Refresh()
+  
+    def _populate_box(self, combobox, list):
+        """
+        fill combox box with dict item
+        
+        :param list: contains item to fill the combox
+            item must model class
+        """
+        st = time.time()
+        for models in list:
+            model= models()
+            name = model.__class__.__name__
+            if models.__name__!="NoStructure":
+                if hasattr(model, "name"):
+                    name = model.name
+                combobox.Append(name,models)
+        return 0
+
+    def _onQrangeEnter(self, event):
+        """
+        Check validity of value enter in the Q range field
+        """
+        
+        tcrtl= event.GetEventObject()
+        #Clear msg if previously shown.
+        msg= ""
+        wx.PostEvent(self.parent.parent, StatusEvent(status = msg ))
+        # Flag to register when a parameter has changed.
+        is_modified = False
+        if tcrtl.GetValue().lstrip().rstrip()!="":
+            try:
+                value = float(tcrtl.GetValue())
+                tcrtl.SetBackgroundColour(wx.WHITE)
+
+                # If qmin and qmax have been modified, update qmin and qmax
+                if self._validate_qrange( self.qmin, self.qmax):
+                    tempmin = float(self.qmin.GetValue())
+                    if tempmin != self.qmin_x:
+                        self.qmin_x = tempmin
+                    tempmax = float(self.qmax.GetValue())
+                    if tempmax != self.qmax_x:
+                        self.qmax_x = tempmax
+                else:
+                    tcrtl.SetBackgroundColour("pink")
+                    msg= "Model Error:wrong value entered : %s"% sys.exc_value
+                    wx.PostEvent(self.parent.parent, StatusEvent(status = msg ))
+                    return  
+            except:
+                tcrtl.SetBackgroundColour("pink")
+                msg= "Model Error:wrong value entered : %s"% sys.exc_value
+                wx.PostEvent(self.parent.parent, StatusEvent(status = msg ))
+                return 
+            #Check if # of points for theory model are valid(>0).
+            if self.npts != None:
+                if check_float(self.npts):
+                    temp_npts = float(self.npts.GetValue())
+                    if temp_npts !=  self.num_points:
+                        self.num_points = temp_npts
+                        is_modified = True
+                else:
+                    msg= "Cannot Plot :No npts in that Qrange!!!  "
+                    wx.PostEvent(self.parent.parent, StatusEvent(status = msg ))
+        else:
+           tcrtl.SetBackgroundColour("pink")
+           msg= "Model Error:wrong value entered!!!"
+           wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
+        #self._undo.Enable(True)
+        self.save_current_state()
+        event = PageInfoEvent(page = self)
+        wx.PostEvent(self.parent, event)
+        self.state_change= False
+        #Draw the model for a different range
+        self._draw_model()
+                   
+    def _on_select_model_helper(self): 
+        """
+        call back for model selection
+        """
+        ## reset dictionary containing reference to dispersion
+        self._disp_obj_dict = {}
+        self.disp_cb_dict ={}
+        
+        f_id = self.formfactorbox.GetCurrentSelection()
+        #For MAC
+        form_factor = None
+        if f_id >= 0:
+            form_factor = self.formfactorbox.GetClientData(f_id)
+
+        if not form_factor in  self.model_list_box["multiplication"]:
+            self.structurebox.Hide()
+            self.text2.Hide()           
+            self.structurebox.Disable()
+            self.structurebox.SetSelection(0)
+            self.text2.Disable()
+        else:
+            self.structurebox.Show()
+            self.text2.Show()
+            self.structurebox.Enable()
+            self.text2.Enable()
+            
+        if form_factor != None:    
+            # set multifactor for Mutifunctional models    
+            if form_factor().__class__ in self.model_list_box["Multi-Functions"]:
+                m_id = self.multifactorbox.GetCurrentSelection()
+                multiplicity = form_factor().multiplicity_info[0]
+                self.multifactorbox.Clear()
+                #self.mutifactor_text.SetLabel(form_factor().details[])
+                self._set_multfactor_combobox(multiplicity)
+                self._show_multfactor_combobox()
+                #ToDo:  this info should be called directly from the model 
+                text = form_factor().multiplicity_info[1]#'No. of Shells: '
+
+                #self.mutifactor_text.Clear()
+                self.mutifactor_text.SetLabel(text)
+                if m_id > multiplicity -1:
+                    # default value
+                    m_id = 1
+                    
+                self.multi_factor = self.multifactorbox.GetClientData(m_id)
+                if self.multi_factor == None: self.multi_factor =0
+                form_factor = form_factor(int(self.multi_factor))
+                self.multifactorbox.SetSelection(m_id)
+                # Check len of the text1 and max_multiplicity
+                text = ''
+                if form_factor.multiplicity_info[0] == len(form_factor.multiplicity_info[2]):
+                    text = form_factor.multiplicity_info[2][self.multi_factor]
+                self.mutifactor_text1.SetLabel(text)
+                # Check if model has  get sld profile.
+                if len(form_factor.multiplicity_info[3]) > 0:
+                    self.sld_axes = form_factor.multiplicity_info[3]
+                    self.show_sld_button.Show(True)
+                else:
+                    self.sld_axes = ""
+
+            else:
+                self._hide_multfactor_combobox()
+                self.show_sld_button.Hide()
+                form_factor = form_factor()
+                self.multi_factor = None
+        else:
+            self._hide_multfactor_combobox()
+            self.show_sld_button.Hide()
+            self.multi_factor = None  
+              
+        s_id = self.structurebox.GetCurrentSelection()
+        struct_factor = self.structurebox.GetClientData( s_id )
+        
+        if  struct_factor !=None:
+            from sans.models.MultiplicationModel import MultiplicationModel
+            self.model= MultiplicationModel(form_factor,struct_factor())
+            
+        else:
+            if form_factor != None:
+                self.model= form_factor
+            else:
+                self.model = None
+                return self.model
+            
+
+        ## post state to fit panel
+        self.state.parameters =[]
+        self.state.model =self.model
+        self.state.qmin = self.qmin_x
+        self.state.multi_factor = self.multi_factor
+        self.disp_list =self.model.getDispParamList()
+        self.state.disp_list = self.disp_list
+        self.Layout()     
+        
+    def _validate_qrange(self, qmin_ctrl, qmax_ctrl):
+        """
+        Verify that the Q range controls have valid values
+        and that Qmin < Qmax.
+        
+        :param qmin_ctrl: text control for Qmin
+        :param qmax_ctrl: text control for Qmax
+        
+        :return: True is the Q range is value, False otherwise 
+        
+        """
+        qmin_validity = check_float(qmin_ctrl)
+        qmax_validity = check_float(qmax_ctrl)
+        if not (qmin_validity and qmax_validity):
+            return False
+        else:
+            qmin = float(qmin_ctrl.GetValue())
+            qmax = float(qmax_ctrl.GetValue())
+            if qmin <= qmax:
+                #Make sure to set both colours white.  
+                qmin_ctrl.SetBackgroundColour(wx.WHITE)
+                qmin_ctrl.Refresh()
+                qmax_ctrl.SetBackgroundColour(wx.WHITE)
+                qmax_ctrl.Refresh()
+            else:
+                qmin_ctrl.SetBackgroundColour("pink")
+                qmin_ctrl.Refresh()
+                qmax_ctrl.SetBackgroundColour("pink")
+                qmax_ctrl.Refresh()
+                msg= "Invalid Q range: Q min must be smaller than Q max"
+                wx.PostEvent(self.parent.parent, StatusEvent(status = msg))
+                return False
+        return True
+    
+    def _validate_Npts(self):  
+        """
+        Validate the number of points for fitting is more than 10 points.
+        If valid, setvalues Npts_fit otherwise post msg.
+        """
+        #default flag
+        flag = True
+
+        # q value from qx and qy
+        radius= numpy.sqrt( self.data.qx_data * self.data.qx_data + 
+                            self.data.qy_data * self.data.qy_data )
+        #get unmasked index
+        index_data = (float(self.qmin.GetValue()) <= radius) & \
+                        (radius <= float(self.qmax.GetValue()))
+        index_data = (index_data) & (self.data.mask) 
+        index_data = (index_data) & (numpy.isfinite(self.data.data))
+
+        if len(index_data[index_data]) < 10:
+            # change the color pink.
+            self.qmin.SetBackgroundColour("pink")
+            self.qmin.Refresh()
+            self.qmax.SetBackgroundColour("pink")
+            self.qmax.Refresh()
+            msg= "Cannot Plot :No or too little npts in that data range!!!  "
+            wx.PostEvent(self.parent.parent, StatusEvent(status = msg ))
+            self.fitrange = False
+            flag = False
+        else:
+            self.Npts_fit.SetValue(str(len(self.data.mask[index_data==True])))
+            self.fitrange = True
+            
+        return flag
+    
+    def _check_value_enter(self, list, modified):
+        """
+        :param list: model parameter and panel info
+        :Note: each item of the list should be as follow:
+            item=[check button state, parameter's name,
+                paramater's value, string="+/-", 
+                parameter's error of fit, 
+                parameter's minimum value, 
+                parrameter's maximum value , 
+                parameter's units]
+        """  
+        is_modified =  modified
+        if len(list)==0:
+            return is_modified
+        for item in list:
+            #skip angle parameters for 1D
+            if self.data.__class__.__name__ !="Data2D":
+                if item in self.orientation_params:
+                    continue
+            #try:
+            name = str(item[1])
+            
+            if string.find(name,".npts") ==-1 and string.find(name,".nsigmas")==-1:      
+                ## check model parameters range             
+                param_min= None
+                param_max= None
+               
+                ## check minimun value
+                if item[5]!= None and item[5]!= "":
+                    if item[5].GetValue().lstrip().rstrip()!="":
+                        try:
+                            
+                            param_min = float(item[5].GetValue())
+                            if not self._validate_qrange(item[5],item[2]):
+                                if numpy.isfinite(param_min):
+                                    item[2].SetValue(format_number(param_min))
+                            
+                            item[5].SetBackgroundColour(wx.WHITE)
+                            item[2].SetBackgroundColour(wx.WHITE)
+                                           
+                        except:
+                            msg = "Wrong Fit parameter range entered "
+                            wx.PostEvent(self.parent.parent, StatusEvent(status = msg))
+                            raise ValueError, msg
+                        is_modified = True
+                ## check maximum value
+                if item[6]!= None and item[6]!= "":
+                    if item[6].GetValue().lstrip().rstrip()!="":
+                        try:                          
+                            param_max = float(item[6].GetValue())
+                            if not self._validate_qrange(item[2],item[6]):
+                                if numpy.isfinite(param_max):
+                                    item[2].SetValue(format_number(param_max))  
+                            
+                            item[6].SetBackgroundColour(wx.WHITE)
+                            item[2].SetBackgroundColour(wx.WHITE)
+                        except:
+                            msg = "Wrong Fit parameter range entered "
+                            wx.PostEvent(self.parent.parent, StatusEvent(status = msg))
+                            raise ValueError, msg
+                        is_modified = True
+                
+
+                if param_min != None and param_max !=None:
+                    if not self._validate_qrange(item[5], item[6]):
+                        msg= "Wrong Fit range entered for parameter "
+                        msg+= "name %s of model %s "%(name, self.model.name)
+                        wx.PostEvent(self.parent.parent, StatusEvent(status = msg))
+                
+                if name in self.model.details.keys():   
+                        self.model.details[name][1:3]= param_min,param_max
+                        is_modified = True
+              
+                else:
+                        self.model.details [name] = ["",param_min,param_max] 
+                        is_modified = True
+            try:     
+                value= float(item[2].GetValue())
+      
+                # If the value of the parameter has changed,
+                # +update the model and set the is_modified flag
+                if value != self.model.getParam(name) and numpy.isfinite(value):
+                    self.model.setParam(name,value)
+                    is_modified = True   
+            except:
+                msg = "Wrong Fit parameter value entered "
+                wx.PostEvent(self.parent.parent, StatusEvent(status = msg))
+                
+        return is_modified 
+        
+ 
+    def _set_dipers_Param(self, event):
+        """
+        respond to self.enable_disp and self.disable_disp radio box.
+        The dispersity object is reset inside the model into Gaussian. 
+        When the user select yes , this method display a combo box for more selection
+        when the user selects No,the combo box disappears.
+        Redraw the model with the default dispersity (Gaussian)
+        """
+        if self.check_invalid_panel():
+            return 
+        ## On selction if no model exists.
+        if self.model ==None:
+            self.disable_disp.SetValue(True)
+            msg="Please select a Model first..."
+            wx.MessageBox(msg, 'Info')
+            wx.PostEvent(self._manager.parent, StatusEvent(status=\
+                            "Polydispersion: %s"%msg))
+            return
+
+        self._reset_dispersity()
+    
+        if self.model ==None:
+            self.model_disp.Hide()
+            self.disp_box.Hide()
+            self.sizer4_4.Clear(True)
+            return
+
+        if self.enable_disp.GetValue():
+            self.model_disp.Show(True)
+            self.disp_box.Show(True)
+            ## layout for model containing no dispersity parameters
+            
+            self.disp_list= self.model.getDispParamList()
+             
+            if len(self.disp_list)==0 and len(self.disp_cb_dict)==0:
+                self._layout_sizer_noDipers()  
+            else:
+                ## set gaussian sizer 
+                self._on_select_Disp(event=None)
+        else:
+            self.model_disp.Hide()
+            self.disp_box.Hide()
+            self.disp_box.SetSelection(0) 
+            self.sizer4_4.Clear(True)
+            
+        ## post state to fit panel 
+        self.save_current_state()
+        if event !=None:
+            #self._undo.Enable(True)
+            event = PageInfoEvent(page = self)
+            wx.PostEvent(self.parent, event)
+        #draw the model with the current dispersity
+        self._draw_model()
+        self.sizer4_4.Layout()
+        self.sizer5.Layout()
+        self.Layout()
+        self.Refresh()      
+          
+        
+    def _layout_sizer_noDipers(self):
+        """
+        Draw a sizer with no dispersity info
+        """
+        ix=0
+        iy=1
+        self.fittable_param=[]
+        self.fixed_param=[]
+        self.orientation_params_disp=[]
+        
+        self.model_disp.Hide()
+        self.disp_box.Hide()
+        self.sizer4_4.Clear(True)
+        model_disp = wx.StaticText(self, -1, 'No PolyDispersity for this model')
+        self.sizer4_4.Add(model_disp,( iy, ix),(1,1),  wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
+        self.sizer4_4.Layout()
+        self.sizer4.Layout()
+    
+    def _reset_dispersity(self):
+        """
+        put gaussian dispersity into current model
+        """
+        if len(self.param_toFit)>0:
+            for item in self.fittable_param:
+                if item in self.param_toFit:
+                    self.param_toFit.remove(item)
+
+            for item in self.orientation_params_disp:
+                if item in self.param_toFit:
+                    self.param_toFit.remove(item)
+         
+        self.fittable_param=[]
+        self.fixed_param=[]
+        self.orientation_params_disp=[]
+        self.values=[]
+        self.weights=[]
+      
+        from sans.models.dispersion_models import GaussianDispersion, ArrayDispersion
+        if len(self.disp_cb_dict)==0:
+            self.save_current_state()
+            self.sizer4_4.Clear(True)
+            self.Layout()
+ 
+            return 
+        if (len(self.disp_cb_dict)>0) :
+            for p in self.disp_cb_dict:
+                # The parameter was un-selected. Go back to Gaussian model (with 0 pts)                    
+                disp_model = GaussianDispersion()
+               
+                self._disp_obj_dict[p] = disp_model
+                # Set the new model as the dispersion object for the selected parameter
+                try:
+                   self.model.set_dispersion(p, disp_model)
+                except:
+
+                    pass
+
+        ## save state into
+        self.save_current_state()
+        self.Layout()  
+        self.Refresh()
+                  
+    def _on_select_Disp(self,event):
+        """
+        allow selecting different dispersion
+        self.disp_list should change type later .now only gaussian
+        """
+        n = self.disp_box.GetCurrentSelection()
+        name = self.disp_box.GetValue()
+        dispersity= self.disp_box.GetClientData(n)
+        self.disp_name = name
+        
+        if name.lower() == "array":
+            self._set_sizer_arraydispersion()
+        else:
+            self._set_sizer_dispersion(dispersity= dispersity)
+            
+        self.state.disp_box= n
+        ## Redraw the model
+        self._draw_model() 
+        #self._undo.Enable(True)
+        event = PageInfoEvent(page = self)
+        wx.PostEvent(self.parent, event)
+        
+        self.sizer4_4.Layout()
+        self.sizer4.Layout()
+    
+    def _set_sizer_arraydispersion(self):
+        """
+        draw sizer with array dispersity  parameters
+        """
+        
+        if len(self.param_toFit)>0:
+            for item in self.fittable_param:
+                if item in self.param_toFit:
+                    self.param_toFit.remove(item)
+            for item in self.orientation_params_disp:
+                if item in self.param_toFit:
+                    self.param_toFit.remove(item)
+        for item in self.model.details.keys():
+            if item in self.model.fixed:
+                del self.model.details [item]                           
+
+        self.fittable_param=[]
+        self.fixed_param=[]
+        self.orientation_params_disp=[]
+        self.sizer4_4.Clear(True) 
+        self._reset_dispersity()
+        ix=0
+        iy=1     
+        disp1 = wx.StaticText(self, -1, 'Array Dispersion')
+        self.sizer4_4.Add(disp1,( iy, ix),(1,1),  wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
+        
+        # Look for model parameters to which we can apply an ArrayDispersion model
+        # Add a check box for each parameter.
+        self.disp_cb_dict = {}
+        ix+=1 
+        self.noDisper_rbox = wx.RadioButton(self, -1,"None", (10, 10),style= wx.RB_GROUP)
+        self.Bind(wx.EVT_RADIOBUTTON,self.select_disp_angle , id=self.noDisper_rbox.GetId())
+        #MAC needs SetValue
+        self.noDisper_rbox.SetValue(True)
+        self.sizer4_4.Add(self.noDisper_rbox, (iy, ix),
+                           (1,1), wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+        
+        for p in self.model.dispersion.keys():
+            if not p in self.model.orientation_params:
+                ix+=1 
+                self.disp_cb_dict[p] = wx.RadioButton(self, -1, p, (10, 10))
+                self.state.disp_cb_dict[p]=  self.disp_cb_dict[p].GetValue()
+                self.Bind(wx.EVT_RADIOBUTTON, self.select_disp_angle, id=self.disp_cb_dict[p].GetId())
+                self.sizer4_4.Add(self.disp_cb_dict[p], (iy, ix), (1,1), wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+        
+        for p in self.model.dispersion.keys():
+            if p in self.model.orientation_params:
+                ix+=1 
+                self.disp_cb_dict[p] = wx.RadioButton(self, -1, p, (10, 10))
+                self.state.disp_cb_dict[p]=  self.disp_cb_dict[p].GetValue()
+                if not (self.enable2D or self.data.__class__.__name__ =="Data2D"):
+                    self.disp_cb_dict[p].Hide()
+                else:
+                    self.disp_cb_dict[p].Show(True)
+                self.Bind(wx.EVT_RADIOBUTTON, self.select_disp_angle, id=self.disp_cb_dict[p].GetId())
+                self.sizer4_4.Add(self.disp_cb_dict[p], (iy, ix), (1,1), wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+
+
+        ix =0
+        iy +=1 
+        self.sizer4_4.Add((20,20),(iy,ix),(1,1), wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)        
+        self.Layout()
+
+        self.state.orientation_params =[]
+        self.state.orientation_params_disp =[]
+        self.state.parameters =[]
+        self.state.fittable_param =[]
+        self.state.fixed_param =[]
+        
+        ## save checkbutton state and txtcrtl values
+        
+        self._copy_parameters_state(self.orientation_params,
+                                     self.state.orientation_params)
+
+        self._copy_parameters_state(self.orientation_params_disp,
+                                     self.state.orientation_params_disp)
+        
+        self._copy_parameters_state(self.parameters, self.state.parameters)
+        self._copy_parameters_state(self.fittable_param, self.state.fittable_param)
+        self._copy_parameters_state(self.fixed_param, self.state.fixed_param)
+        
+        
+        ## post state to fit panel
+        event = PageInfoEvent(page = self)
+        wx.PostEvent(self.parent, event)
+        
+    def _set_range_sizer(self, title, box_sizer=None, object1=None,object=None):
+        """
+        Fill the Q range sizer
+        """
+        #2D data? default
+        is_2Ddata = False
+        
+        #check if it is 2D data
+        if self.data.__class__.__name__ == 'Data2D':
+            is_2Ddata = True
+            
+        self.sizer5.Clear(True)
+        #--------------------------------------------------------------
+        if box_sizer == None:
+            box_description= wx.StaticBox(self, -1,str(title))
+            boxsizer1 = wx.StaticBoxSizer(box_description, wx.VERTICAL)
+        else:
+            #for MAC
+            boxsizer1 = box_sizer
+
+        self.qmin    = self.ModelTextCtrl(self, -1,size=(_BOX_WIDTH,20),style=wx.TE_PROCESS_ENTER,
+                                            text_enter_callback = self._onQrangeEnter)
+        self.qmin.SetValue(str(self.qmin_x))
+        self.qmin.SetToolTipString("Minimun value of Q in linear scale.")
+     
+        self.qmax    = self.ModelTextCtrl(self, -1,size=(_BOX_WIDTH,20),style=wx.TE_PROCESS_ENTER,
+                                            text_enter_callback = self._onQrangeEnter)
+        self.qmax.SetValue(str(self.qmax_x))
+        self.qmax.SetToolTipString("Maximum value of Q in linear scale.")
+        
+        id = wx.NewId()
+        self.reset_qrange =wx.Button(self,id,'Reset',size=(77,20))
+      
+        self.reset_qrange.Bind(wx.EVT_BUTTON, self.on_reset_clicked,id=id)
+        self.reset_qrange.SetToolTipString("Reset Q range to the default values")
+     
+        sizer_horizontal=wx.BoxSizer(wx.HORIZONTAL)
+        sizer= wx.GridSizer(2, 4,2, 6)
+
+        self.btEditMask = wx.Button(self,wx.NewId(),'Editor', size=(88,23))
+        self.btEditMask.Bind(wx.EVT_BUTTON, self._onMask,id= self.btEditMask.GetId())
+        self.btEditMask.SetToolTipString("Edit Mask.")
+        self.EditMask_title = wx.StaticText(self, -1, ' Masking(2D)')
+
+        sizer.Add(wx.StaticText(self, -1, '    Q range'))     
+        sizer.Add(wx.StaticText(self, -1, ' Min[1/A]'))
+        sizer.Add(wx.StaticText(self, -1, ' Max[1/A]'))
+        sizer.Add(self.EditMask_title)
+        sizer.Add(self.reset_qrange)   
+        
+        sizer.Add(self.qmin)
+        sizer.Add(self.qmax)
+        sizer.Add(self.btEditMask)
+       
+        if object1!=None:
+            
+            boxsizer1.Add(object1) 
+            boxsizer1.Add((10,10))
+        boxsizer1.Add(sizer)
+        if object!=None:
+            boxsizer1.Add((10,15))
+            boxsizer1.Add(object)
+        if is_2Ddata:
+            self.btEditMask.Enable()  
+            self.EditMask_title.Enable() 
+        else:
+            self.btEditMask.Disable()  
+            self.EditMask_title.Disable()
+        ## save state
+        self.save_current_state()
+        #----------------------------------------------------------------
+        self.sizer5.Add(boxsizer1,0, wx.EXPAND | wx.ALL, 10)
+        self.sizer5.Layout()
+
+    def _lay_out(self):
+        """
+        returns self.Layout
+        
+        :Note: Mac seems to like this better when self.
+            Layout is called after fitting.
+        """
+        self._sleep4sec()
+        self.Layout()
+        return 
+    
+    def _sleep4sec(self):
+        """
+            sleep for 1 sec only applied on Mac
+            Note: This 1sec helps for Mac not to crash on self.:ayout after self._draw_model
+        """
+        if ON_MAC == True:
+            time.sleep(1)
+            
+    def on_reset_clicked(self,event):
+        """
+        On 'Reset' button  for Q range clicked
+        """
+        flag = True
+        if self.check_invalid_panel():
+            return
+        ##For 3 different cases: Data2D, Data1D, and theory
+        if self.data.__class__.__name__ == "Data2D":
+            data_min= 0
+            x= max(math.fabs(self.data.xmin), math.fabs(self.data.xmax)) 
+            y= max(math.fabs(self.data.ymin), math.fabs(self.data.ymax))
+            self.qmin_x = data_min
+            self.qmax_x = math.sqrt(x*x + y*y)
+            # check smearing
+            if not self.disable_smearer.GetValue():
+                temp_smearer= self.current_smearer
+                ## set smearing value whether or not the data contain the smearing info
+                if self.pinhole_smearer.GetValue():
+                    flag = self.update_pinhole_smear()
+                else:
+                    flag = True
+        elif self.data.__class__.__name__ != "Data2D":
+            self.qmin_x = min(self.data.x)
+            self.qmax_x = max(self.data.x)
+            # check smearing
+            if not self.disable_smearer.GetValue():
+                temp_smearer= self.current_smearer
+                ## set smearing value whether or not the data contain the smearing info
+                if self.slit_smearer.GetValue():
+                    flag = self.update_slit_smear()
+                elif self.pinhole_smearer.GetValue():
+                    flag = self.update_pinhole_smear()
+                else:
+                    flag = True
+        else:
+            self.qmin_x = _QMIN_DEFAULT
+            self.qmax_x = _QMAX_DEFAULT
+            self.num_points = _NPTS_DEFAULT            
+            self.state.npts = self.num_points
+            
+        if flag == False:
+            msg= "Cannot Plot :Must enter a number!!!  "
+            wx.PostEvent(self.parent.parent, StatusEvent(status = msg ))
+        else:
+            # set relative text ctrs.
+            self.qmin.SetValue(str(self.qmin_x))
+            self.qmax.SetValue(str(self.qmax_x))
+            self.set_npts2fit()
+            # At this point, some button and variables satatus (disabled?) should be checked 
+            # such as color that should be reset to white in case that it was pink.
+            self._onparamEnter_helper()
+
+        self.save_current_state()
+        self.state.qmin = self.qmin_x
+        self.state.qmax = self.qmax_x
+        
+        #reset the q range values
+        self._reset_plotting_range(self.state)
+        #self.compute_chisqr(smearer=self.current_smearer)
+        #Re draw plot
+        self._draw_model()
+
+    def on_model_help_clicked(self,event):
+        """
+        on 'More details' button
+        """
+        from help_panel import  HelpWindow
+        
+        if self.model == None:
+            name = 'FuncHelp'
+        else:
+            name = self.model.origin_name
+
+        frame = HelpWindow(None, -1,  pageToOpen="media/model_functions.html")    
+        frame.Show(True)
+        if frame.rhelp.HasAnchor(name):
+            frame.rhelp.ScrollToAnchor(name)
+        else:
+           msg= "Model does not contains an available description "
+           msg +="Please try searching in the Help window"
+           wx.PostEvent(self.parent.parent, StatusEvent(status = msg ))                    
+                
Index: sansview/perspectives/fitting/Copy of fitpage.py
===================================================================
--- sansview/perspectives/fitting/Copy of fitpage.py	(revision a07e72f8fb8281178797bf691b36e56274d8580b)
+++ sansview/perspectives/fitting/Copy of fitpage.py	(revision a07e72f8fb8281178797bf691b36e56274d8580b)
@@ -0,0 +1,2611 @@
+
+
+import sys
+import wx
+import wx.lib.newevent
+import numpy
+import copy
+import math
+import time
+from sans.models.dispersion_models import ArrayDispersion, GaussianDispersion
+from DataLoader.data_info import Data1D
+from sans.guiframe.events import StatusEvent   
+from sans.guiframe.utils import format_number,check_float
+
+
+
+## event to know the selected fit engine
+(FitterTypeEvent, EVT_FITTER_TYPE)   = wx.lib.newevent.NewEvent()
+(FitStopEvent, EVT_FIT_STOP)   = wx.lib.newevent.NewEvent()
+(Chi2UpdateEvent, EVT_CHI2_UPDATE)   = wx.lib.newevent.NewEvent()
+_BOX_WIDTH = 76
+_DATA_BOX_WIDTH = 300
+SMEAR_SIZE_L = 0.005
+SMEAR_SIZE_H = 0.006
+
+import basepage
+from basepage import BasicPage
+from basepage import PageInfoEvent
+from DataLoader.qsmearing import smear_selection
+
+class FitPage(BasicPage):
+    """
+    FitPanel class contains fields allowing to display results when
+    fitting  a model and one data
+    
+    :note: For Fit to be performed the user should check at least one parameter
+        on fit Panel window.
+    """
+    
+    def __init__(self,parent, page_info):
+        """ 
+        Initialization of the Panel
+        """
+        BasicPage.__init__(self, parent, page_info)
+ 
+        ## draw sizer
+        self._fill_datainfo_sizer()
+        # get smear info from data
+        self._get_smear_info()
+        self._fill_model_sizer( self.sizer1)
+        self._get_defult_custom_smear()
+        self._fill_range_sizer() 
+        
+        if self.data is None:
+            self.formfactorbox.Disable()
+            self.structurebox.Disable()
+        else:
+            self.smearer = smear_selection(self.data, self.model)
+            if self.smearer ==None:
+                self.enable_smearer.Disable()
+        ## to update the panel according to the fit engine type selected
+        self.Bind(EVT_FITTER_TYPE,self._on_engine_change)
+        self.Bind(EVT_FIT_STOP,self._on_fit_complete)
+        self.Bind(EVT_CHI2_UPDATE, self.on_complete_chisqr)
+        self._set_bookmark_flag(False)
+        self._set_save_flag(False)
+
+    def _on_fit_complete(self, event):
+        """
+        When fit is complete ,reset the fit button label.
+        """
+        #self.btFit.SetLabel("Fit")
+        #self.btFit.Unbind(event=wx.EVT_BUTTON, id=self.btFit.GetId())
+        #self.btFit.Bind(event=wx.EVT_BUTTON, handler=self._onFit,id=self.btFit.GetId())
+        pass
+        
+    def _is_2D(self):
+        """
+        Check if data_name is Data2D
+        
+        :return: True or False
+        
+        """
+        
+        if self.data.__class__.__name__ =="Data2D":
+            return True
+        return False
+            
+    def _on_engine_change(self, event):
+        """
+        get an event containing the current name of the fit engine type
+        
+        :param event: FitterTypeEvent containing  the name of the current engine
+        
+        """
+        self.engine_type = event.type
+        self.state.engine_type = self.engine_type
+        if len(self.parameters)==0:
+            self.Layout()
+            return
+        if event.type =="park":
+            self.btFit.SetLabel("Fit")
+
+        for item in self.parameters:
+            if event.type =="scipy" :
+                item[5].SetValue("")
+                item[5].Hide()
+                item[6].SetValue("")
+                item[6].Hide()
+                self.text2_min.Hide()
+                self.text2_max.Hide()
+
+            else:
+                item[5].Show(True)
+                item[6].Show(True)
+                self.text2_min.Show(True)
+                self.text2_max.Show(True)
+        for item in self.fittable_param:
+            if item[5]!=None and item[6]!=None and not item in self.orientation_params_disp:
+                if event.type =="scipy" and not item in self.orientation_params:
+                    item[5].SetValue("")
+                    item[5].Hide()
+                    item[6].SetValue("")
+                    item[6].Hide()
+                    self.text2_min.Hide()
+                    self.text2_max.Hide()
+                    self.text_disp_min.Hide()
+                    self.text_disp_max.Hide()
+                else:
+                    item[5].Show(True)
+                    item[6].Show(True)
+                    self.text2_min.Show(True)
+                    self.text2_max.Show(True)
+                    self.text_disp_min.Show(True)
+                    self.text_disp_max.Show(True)
+            
+        for item in self.orientation_params:
+            if item[5]!=None and item[6]!=None:
+                if event.type =="scipy" or self.data.__class__.__name__ !="Data2D":
+                    item[5].SetValue("")
+                    item[5].Hide()
+                    item[6].SetValue("")
+                    item[6].Hide()
+                else:
+                    item[5].Show(True)
+                    item[6].Show(True)
+                    
+        for item in self.orientation_params_disp:         
+            if item[5]!=None and item[6]!=None:
+                if event.type =="scipy" or self.data.__class__.__name__ !="Data2D":
+                    item[5].SetValue("")
+                    item[5].Hide()
+                    item[6].SetValue("")
+                    item[6].Hide()
+                else:
+                    item[5].Show(True)
+                    item[6].Show(True)
+        self.Layout()
+        self.Refresh()
+
+    def _fill_range_sizer(self):
+        """
+        Fill the sizer containing the plotting range
+        add  access to npts
+        """
+        is_2Ddata = False
+        
+        # Check if data is 2D
+        if self.data.__class__.__name__ == 'Data2D':
+            is_2Ddata = True
+            
+        title = "Fitting"
+        #smear messages & titles
+        smear_message_none  =  "No smearing is selected..."
+        smear_message_dqdata  =  "The dQ data is being used for smearing..."
+        smear_message_2d  =  "Higher accuracy is very time-expensive. Use it with care..."
+        smear_message_new_ssmear  =  "Please enter only the value of interest to customize smearing..."
+        smear_message_new_psmear  =  "Please enter both; the dQ will be generated by interpolation..."
+        smear_message_2d_x_title = "<dQx>[1/A]:"
+        smear_message_2d_y_title = "<dQy>[1/A]:"
+        smear_message_pinhole_min_title = "dQ_low[1/A]:"
+        smear_message_pinhole_max_title = "dQ_high[1/A]:"
+        smear_message_slit_height_title = "Slit height[1/A]:"
+        smear_message_slit_width_title = "Slit width[1/A]:"
+        
+        self._get_smear_info()
+        
+        #Sizers
+        box_description_range = wx.StaticBox(self, -1,str(title))
+        boxsizer_range = wx.StaticBoxSizer(box_description_range, wx.VERTICAL)      
+        self.sizer_set_smearer = wx.BoxSizer(wx.VERTICAL)
+        sizer_smearer = wx.BoxSizer(wx.HORIZONTAL)
+        self.sizer_new_smear= wx.BoxSizer(wx.HORIZONTAL)
+        self.sizer_set_masking = wx.BoxSizer(wx.HORIZONTAL)
+        sizer_chi2 = wx.BoxSizer(wx.VERTICAL)
+        smear_set_box= wx.StaticBox(self, -1,'Set Instrumental Smearing')
+        sizer_smearer_box = wx.StaticBoxSizer(smear_set_box, wx.HORIZONTAL)
+        sizer_smearer_box.SetMinSize((_DATA_BOX_WIDTH,85))
+        sizer_fit = wx.GridSizer(2, 4,2,6)
+        
+        # combobox for smear2d accuracy selection
+        self.smear_accuracy = wx.ComboBox(self, -1,size=(50,-1),style=wx.CB_READONLY)
+        self._set_accuracy_list()
+        self.smear_accuracy.SetValue(self.smear2d_accuracy)
+        self.smear_accuracy.SetSelection(0)
+        self.smear_accuracy.SetToolTipString("'Higher' uses more Gaussian points for smearing computation.")
+                   
+        wx.EVT_COMBOBOX(self.smear_accuracy,-1, self._on_select_accuracy)
+
+        #Fit button
+        self.btFit = wx.Button(self,wx.NewId(),'Fit', size=(88,25))
+        self.btFit.Bind(wx.EVT_BUTTON, self._onFit,id= self.btFit.GetId())
+        self.btFit.SetToolTipString("Start fitting.")
+        
+        #textcntrl for custom resolution
+        self.smear_pinhole_max = self.ModelTextCtrl(self, -1,size=(_BOX_WIDTH-25,20),style=wx.TE_PROCESS_ENTER,
+                                            text_enter_callback = self.onPinholeSmear)
+        self.smear_pinhole_min = self.ModelTextCtrl(self, -1,size=(_BOX_WIDTH-25,20),style=wx.TE_PROCESS_ENTER,
+                                            text_enter_callback = self.onPinholeSmear)
+        self.smear_slit_height= self.ModelTextCtrl(self, -1,size=(_BOX_WIDTH-25,20),style=wx.TE_PROCESS_ENTER,
+                                            text_enter_callback = self.onSlitSmear)
+        self.smear_slit_width = self.ModelTextCtrl(self, -1,size=(_BOX_WIDTH-25,20),style=wx.TE_PROCESS_ENTER,
+                                            text_enter_callback = self.onSlitSmear)
+
+        ## smear
+        self.smear_data_left= BGTextCtrl(self, -1, size=(_BOX_WIDTH-25,20), style=0)
+        self.smear_data_left.SetValue(str(self.dq_l))
+        self.smear_data_right = BGTextCtrl(self, -1, size=(_BOX_WIDTH-25,20), style=0)
+        self.smear_data_right.SetValue(str(self.dq_r))
+
+        #set default values for smear
+        self.smear_pinhole_max.SetValue(str(self.dx_max))
+        self.smear_pinhole_min.SetValue(str(self.dx_min))
+        self.smear_slit_height.SetValue(str(self.dxl))
+        self.smear_slit_width.SetValue(str(self.dxw))
+
+        #Filling the sizer containing instruments smearing info.
+        self.disable_smearer = wx.RadioButton(self, -1, 'None', style=wx.RB_GROUP)
+        self.enable_smearer = wx.RadioButton(self, -1, 'Use dQ Data')
+        #self.enable_smearer.SetToolTipString("Click to use the loaded dQ data for smearing.")
+        self.pinhole_smearer = wx.RadioButton(self, -1, 'Custom Pinhole Smear')
+        #self.pinhole_smearer.SetToolTipString("Click to input custom resolution for pinhole smearing.")
+        self.slit_smearer = wx.RadioButton(self, -1, 'Custom Slit Smear')
+        #self.slit_smearer.SetToolTipString("Click to input custom resolution for slit smearing.")
+        self.Bind(wx.EVT_RADIOBUTTON, self.onSmear, id=self.disable_smearer.GetId())
+        self.Bind(wx.EVT_RADIOBUTTON, self.onSmear, id=self.enable_smearer.GetId())
+        self.Bind(wx.EVT_RADIOBUTTON, self.onPinholeSmear, id=self.pinhole_smearer.GetId())
+        self.Bind(wx.EVT_RADIOBUTTON, self.onSlitSmear, id=self.slit_smearer.GetId())
+        self.disable_smearer.SetValue(True)
+        
+        # add 4 types of smearing to the sizer
+        sizer_smearer.Add( self.disable_smearer,0, wx.LEFT, 10)
+        sizer_smearer.Add((10,10))
+        sizer_smearer.Add( self.enable_smearer)
+        sizer_smearer.Add((10,10))
+        sizer_smearer.Add( self.pinhole_smearer ) 
+        sizer_smearer.Add((10,10))
+        sizer_smearer.Add( self.slit_smearer ) 
+        sizer_smearer.Add((10,10))       
+        
+        # StaticText for chi2, N(for fitting), Npts
+        self.tcChi    =  BGTextCtrl(self, -1, "-", size=(75,20), style=0)
+        self.tcChi.SetToolTipString("Chi2/Npts")
+        self.Npts_fit    =  BGTextCtrl(self, -1, "-", size=(75,20), style=0)
+        self.Npts_fit.SetToolTipString(" Npts : number of points selected for fitting")
+        self.Npts_total  =  BGTextCtrl(self, -1, "-", size=(75,20), style=0)
+        self.Npts_total.SetToolTipString(" Total Npts : total number of data points")
+        box_description_1= wx.StaticText(self, -1,'    Chi2/Npts')
+        box_description_2= wx.StaticText(self, -1,'  Npts')
+        box_description_3= wx.StaticText(self, -1,'  Total Npts')
+        box_description_4= wx.StaticText(self, -1,' ')
+        
+        
+        sizer_fit.Add(box_description_1,0,0)
+        sizer_fit.Add(box_description_2,0,0)
+        sizer_fit.Add(box_description_3,0,0)       
+        sizer_fit.Add(box_description_4,0,0)
+        sizer_fit.Add(self.tcChi,0,0)
+        sizer_fit.Add(self.Npts_fit ,0,0)
+        sizer_fit.Add(self.Npts_total,0,0)
+        sizer_fit.Add(self.btFit,0,0) 
+
+        # StaticText for smear
+        #self.tcChi    =  wx.StaticText(self, -1, "-", style=wx.ALIGN_LEFT)
+        self.smear_description_none    =  wx.StaticText(self, -1, smear_message_none , style=wx.ALIGN_LEFT)
+        self.smear_description_dqdata    =  wx.StaticText(self, -1, smear_message_dqdata , style=wx.ALIGN_LEFT)
+        self.smear_description_type    =  wx.StaticText(self, -1, "Type:" , style=wx.ALIGN_LEFT)
+        self.smear_description_accuracy_type    =  wx.StaticText(self, -1, "Accuracy:" , style=wx.ALIGN_LEFT)
+        self.smear_description_smear_type    =  BGTextCtrl(self, -1, size=(57,20), style=0)
+        self.smear_description_smear_type.SetValue(str(self.dq_l))
+        self.SetBackgroundColour(self.GetParent().GetBackgroundColour())
+        self.smear_description_2d     =  wx.StaticText(self, -1, smear_message_2d  , style=wx.ALIGN_LEFT)
+        self.smear_message_new_s = wx.StaticText(self, -1, smear_message_new_ssmear  , style=wx.ALIGN_LEFT)
+        self.smear_message_new_p = wx.StaticText(self, -1, smear_message_new_psmear  , style=wx.ALIGN_LEFT)
+        self.smear_description_2d_x     =  wx.StaticText(self, -1, smear_message_2d_x_title  , style=wx.ALIGN_LEFT)
+        self.smear_description_2d_y     =  wx.StaticText(self, -1, smear_message_2d_y_title  , style=wx.ALIGN_LEFT)
+        self.smear_description_pin_min     =  wx.StaticText(self, -1, smear_message_pinhole_min_title  , style=wx.ALIGN_LEFT)
+        self.smear_description_pin_max     =  wx.StaticText(self, -1, smear_message_pinhole_max_title  , style=wx.ALIGN_LEFT)
+        self.smear_description_slit_height    =  wx.StaticText(self, -1, smear_message_slit_height_title   , style=wx.ALIGN_LEFT)
+        self.smear_description_slit_width    =  wx.StaticText(self, -1, smear_message_slit_width_title   , style=wx.ALIGN_LEFT)
+        
+        #arrange sizers 
+        #boxsizer1.Add( self.tcChi )  
+        self.sizer_set_smearer.Add(sizer_smearer )
+        self.sizer_set_smearer.Add((10,10))
+        self.sizer_set_smearer.Add( self.smear_description_none,0, wx.CENTER, 10 ) 
+        self.sizer_set_smearer.Add( self.smear_description_dqdata,0, wx.CENTER, 10 )
+        self.sizer_set_smearer.Add( self.smear_description_2d,0, wx.CENTER, 10 )
+        self.sizer_new_smear.Add( self.smear_description_type,0, wx.CENTER, 10 )
+        self.sizer_new_smear.Add( self.smear_description_accuracy_type,0, wx.CENTER, 10 )
+        self.sizer_new_smear.Add( self.smear_accuracy )
+        self.sizer_new_smear.Add( self.smear_description_smear_type,0, wx.CENTER, 10 )
+        self.sizer_new_smear.Add((15,-1))
+        self.sizer_new_smear.Add( self.smear_description_2d_x,0, wx.CENTER, 10 )
+        self.sizer_new_smear.Add( self.smear_description_pin_min,0, wx.CENTER, 10 )
+        self.sizer_new_smear.Add( self.smear_description_slit_height,0, wx.CENTER, 10 )
+
+        self.sizer_new_smear.Add( self.smear_pinhole_min,0, wx.CENTER, 10 )
+        self.sizer_new_smear.Add( self.smear_slit_height,0, wx.CENTER, 10 )
+        self.sizer_new_smear.Add( self.smear_data_left,0, wx.CENTER, 10 )
+        self.sizer_new_smear.Add((20,-1))
+        self.sizer_new_smear.Add( self.smear_description_2d_y,0, wx.CENTER, 10 )
+        self.sizer_new_smear.Add( self.smear_description_pin_max,0, wx.CENTER, 10 )
+        self.sizer_new_smear.Add( self.smear_description_slit_width,0, wx.CENTER, 10 )
+
+        self.sizer_new_smear.Add( self.smear_pinhole_max,0, wx.CENTER, 10 )
+        self.sizer_new_smear.Add( self.smear_slit_width,0, wx.CENTER, 10 )
+        self.sizer_new_smear.Add( self.smear_data_right,0, wx.CENTER, 10 )
+           
+        self.sizer_set_smearer.Add( self.smear_message_new_s,0, wx.CENTER, 10)
+        self.sizer_set_smearer.Add( self.smear_message_new_p,0, wx.CENTER, 10)
+        self.sizer_set_smearer.Add((5,2))
+        self.sizer_set_smearer.Add( self.sizer_new_smear,0, wx.CENTER, 10 )
+        
+        # add all to chi2 sizer 
+        sizer_smearer_box.Add(self.sizer_set_smearer)       
+        sizer_chi2.Add(sizer_smearer_box)
+        sizer_chi2.Add((-1,5))
+
+        # hide all smear messages and textctrl
+        self._hide_all_smear_info()
+        
+        # get smear_selection
+        self.current_smearer= smear_selection( self.data, self.model )
+
+        # Show only the relevant smear messages, etc
+        if self.current_smearer == None:
+            if not is_2Ddata:
+                self.smear_description_none.Show(True)
+                self.enable_smearer.Disable()  
+            else:
+                self.smear_description_none.Show(True)
+                #self.smear_description_2d.Show(True) 
+                #self.pinhole_smearer.Disable() 
+                self.slit_smearer.Disable()   
+                #self.enable_smearer.Disable() 
+        else: self._show_smear_sizer()
+        boxsizer_range.Add(self.sizer_set_masking)
+            
+        #Set sizer for Fitting section
+        self._set_range_sizer( title=title,box_sizer=boxsizer_range, object1=sizer_chi2, object= sizer_fit)
+        
+    def _fill_datainfo_sizer(self):
+        """
+        fill sizer 0 with data info
+        """
+        ## no loaded data , don't fill the sizer
+        if self.data is None:
+            data_min = ""
+            data_max = ""
+            data_name = ""
+        else:
+            data_name = self.data.name
+            #set maximum range for x in linear scale
+            if not hasattr(self.data,"data"): #Display only for 1D data fit
+                # Minimum value of data   
+                data_min = min(self.data.x)
+                # Maximum value of data  
+                data_max = max(self.data.x)
+            else:
+                ## Minimum value of data 
+                data_min = 0
+                x = max(math.fabs(self.data.xmin), math.fabs(self.data.xmax)) 
+                y = max(math.fabs(self.data.ymin), math.fabs(self.data.ymax))
+                ## Maximum value of data  
+                data_max = math.sqrt(x*x + y*y)
+        
+        box_description= wx.StaticBox(self, -1, 'Data')
+        boxsizer1 = wx.StaticBoxSizer(box_description, wx.VERTICAL)
+        #----------------------------------------------------------
+        sizer_data = wx.BoxSizer(wx.HORIZONTAL)
+        #Filling the sizer containing data related fields
+        #self.dataSource = wx.StaticText(self, -1,str(self.data.name))
+        self.dataSource = BGTextCtrl(self, -1)
+        self.dataSource.SetValue(str(data_name))
+        #self.dataSource.SetEditable(False)
+        self.dataSource.SetMinSize((_DATA_BOX_WIDTH, -1))
+        sizer_data.Add(wx.StaticText(self, -1, 'Source Name : '))
+        sizer_data.Add(self.dataSource )
+        sizer_data.Add( (0,5) )
+        
+        #---------sizer 2 draw--------------------------------
+        text4_3 = wx.StaticText(self, -1, 'Total Q Range (1/A)',
+                                 style=wx.ALIGN_LEFT)
+        sizer_range = wx.BoxSizer(wx.HORIZONTAL)
+        sizer_range.Add( text4_3 ,0, wx.RIGHT, 10)
+        self.minimum_q = BGTextCtrl(self, -1, size=(_BOX_WIDTH,20))
+        self.minimum_q.SetValue(str(data_min))
+        #self.minimum_q.SetEditable(False)
+        self.maximum_q = BGTextCtrl(self, -1,size=(_BOX_WIDTH,20))
+        self.maximum_q.SetValue(str(data_max))
+        #self.maximum_q.SetEditable(False)
+        sizer_range.Add(wx.StaticText(self, -1, "Min: "),0, wx.LEFT, 10)
+        sizer_range.Add(self.minimum_q,0, wx.LEFT, 10)
+        sizer_range.Add(wx.StaticText(self, -1, "Max: "),0, wx.LEFT, 10)
+        sizer_range.Add(self.maximum_q,0, wx.LEFT, 10)
+
+        ## set q range to plot
+        self.qmin_x = data_min
+        self.qmax_x = data_max
+
+        boxsizer1.Add(sizer_data,0, wx.ALL, 10)
+        boxsizer1.Add(sizer_range, 0 , wx.LEFT, 10)
+        #------------------------------------------------------------
+        self.sizer0.Add(boxsizer1,0, wx.EXPAND | wx.ALL, 10)
+        self.sizer0.Layout()
+       
+    def _fill_model_sizer(self, sizer):
+        """
+        fill sizer containing model info
+        """
+        ##Add model function Details button in fitpanel.
+        ##The following 3 lines are for Mac. Let JHC know before modifying... 
+        title = "Model"
+        box_description= wx.StaticBox(self, -1,str(title))
+        boxsizer1 = wx.StaticBoxSizer(box_description, wx.VERTICAL)
+         
+        id = wx.NewId()
+        self.model_help =wx.Button(self,id,'Details', size=(80,23))
+        self.model_help.Bind(wx.EVT_BUTTON, self.on_model_help_clicked,id=id)
+        self.model_help.SetToolTipString("Model Function Help")
+        
+        ## class base method  to add view 2d button    
+        self._set_model_sizer(sizer=sizer, box_sizer=boxsizer1, 
+                              title="Model",object=self.model_help )   
+        
+    def _set_sizer_dispersion(self, dispersity):
+        """
+        draw sizer with gaussian dispersity parameters
+        """
+        self.fittable_param=[]
+        self.fixed_param=[]
+        self.orientation_params_disp=[]
+
+        self.sizer4_4.Clear(True)
+        if self.model==None:
+            ##no model is selected
+            return
+        if not self.enable_disp.GetValue():
+            ## the user didn't select dispersity display
+            return 
+            
+        self._reset_dispersity()
+        
+        # Create the dispersion objects
+        for item in self.model.dispersion.keys():
+            #disp_model =  GaussianDispersion()
+            disp_model = dispersity()
+            self._disp_obj_dict[item] = disp_model
+            self.model.set_dispersion(item, disp_model)
+            self.state._disp_obj_dict[item]= disp_model
+
+
+        ix=0
+        iy=1
+        disp = wx.StaticText(self, -1, ' ')
+        self.sizer4_4.Add(disp,( iy, ix),(1,1), 
+                           wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
+        ix += 1 
+        values = wx.StaticText(self, -1, 'Sigma [A]')
+        values.SetToolTipString(\
+        "Sigma(STD) in the A unit; the standard deviation from the mean value.")
+
+        self.sizer4_4.Add(values,( iy, ix),(1,1), wx.EXPAND|wx.ADJUST_MINSIZE, 
+                          0)
+        ix +=2 
+        self.text_disp_1 = wx.StaticText(self, -1, '')
+        self.sizer4_4.Add( self.text_disp_1,(iy, ix),(1,1),\
+                            wx.EXPAND|wx.ADJUST_MINSIZE, 0) 
+        self.text_disp_1.Hide()
+        
+        
+        ix +=1 
+        self.text_disp_min = wx.StaticText(self, -1, 'Min')
+        self.sizer4_4.Add(self.text_disp_min,(iy, ix),(1,1),\
+                            wx.EXPAND|wx.ADJUST_MINSIZE, 0) 
+        self.text_disp_min.Hide()
+        ix +=1 
+        self.text_disp_max = wx.StaticText(self, -1, 'Max')
+        self.sizer4_4.Add(self.text_disp_max,(iy, ix),(1,1),\
+                            wx.EXPAND|wx.ADJUST_MINSIZE, 0) 
+        self.text_disp_max.Hide()
+                        
+        
+        ix += 1 
+        npts = wx.StaticText(self, -1, 'Npts')
+        npts.SetToolTipString("Number of sampling points for the numerical\n\
+        integration over the distribution function.")
+        self.sizer4_4.Add(npts,( iy, ix),(1,1), wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+        ix += 1 
+        nsigmas = wx.StaticText(self, -1, 'Nsigmas')
+        nsigmas.SetToolTipString("   Number of sigmas between which the range\n\
+         of the distribution function will be used for weighting. \n\
+        The value '3' covers 99.5% for Gaussian distribution \n\
+        function.")
+        self.sizer4_4.Add(nsigmas,( iy, ix),(1,1), wx.EXPAND|wx.ADJUST_MINSIZE,
+                           0)
+        
+        if self.engine_type=="park":
+            self.text_disp_max.Show(True)
+            self.text_disp_min.Show(True)
+
+        for item in self.model.dispersion.keys():
+            if not item in self.model.orientation_params:
+                if not self.disp_cb_dict.has_key(item):
+                    self.disp_cb_dict[item]= None
+                name0="Distribution of " + item
+                name1=item+".width"
+                name2=item+".npts"
+                name3=item+".nsigmas"
+                if not self.model.details.has_key(name1):
+                    self.model.details [name1] = ["",None,None] 
+
+                iy += 1
+                for p in self.model.dispersion[item].keys(): 
+        
+                    if p=="width":
+                        ix = 0
+                        cb = wx.CheckBox(self, -1, name0, (10, 10))
+                        cb.SetToolTipString("Check for fitting")
+                        wx.EVT_CHECKBOX(self, cb.GetId(), self.select_param)
+                        self.sizer4_4.Add( cb,( iy, ix),(1,1),  
+                                           wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 
+                                           15)
+                        ix = 1
+                        value= self.model.getParam(name1)
+                        ctl1 = self.ModelTextCtrl(self, -1, size=(_BOX_WIDTH,20)
+                                                  ,style=wx.TE_PROCESS_ENTER)
+                        ctl1.SetToolTipString("Absolute Sigma: \n\
+                        1) It is the STD (ratio*mean) of '%s' distribution.\n \
+                        2) It should not exceed Mean/(2*Nsigmas)." %item)
+                        ctl1.SetValue(str (format_number(value)))
+                        self.sizer4_4.Add(ctl1, (iy,ix),(1,1),wx.EXPAND)
+                        ## text to show error sign
+                        ix = 2
+                        text2=wx.StaticText(self, -1, '+/-')
+                        self.sizer4_4.Add(text2,(iy, ix),(1,1),
+                                          wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+                        text2.Hide() 
+
+                        ix = 3
+                        ctl2 = wx.TextCtrl(self, -1, size=(_BOX_WIDTH,20), 
+                                           style=0)
+                  
+                        self.sizer4_4.Add(ctl2, (iy,ix),(1,1), 
+                                          wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+                     
+                        ctl2.Hide()
+
+                        ix = 4
+                        ctl3 = self.ModelTextCtrl(self, -1, size=(_BOX_WIDTH/2,
+                                            20), style=wx.TE_PROCESS_ENTER,
+                                text_enter_callback = self._onparamRangeEnter)
+                        
+                        self.sizer4_4.Add(ctl3, (iy,ix),(1,1), 
+                                          wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+                        ctl3.Hide()
+                
+                        ix = 5
+                        ctl4 = self.ModelTextCtrl(self, -1, size=(_BOX_WIDTH/2,
+                                            20), style=wx.TE_PROCESS_ENTER,
+                            text_enter_callback = self._onparamRangeEnter)
+                        
+                        self.sizer4_4.Add(ctl4, (iy,ix),(1,1), 
+                                          wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+
+                        ctl4.Hide()
+                        
+                        if self.engine_type=="park":
+                            ctl3.Show(True)
+                            ctl4.Show(True)
+                                          
+                        self.fittable_param.append([cb,name1,ctl1,text2,
+                                                    ctl2, ctl3, ctl4,None])                    
+                    
+                    elif p=="npts":
+                            ix = 6
+                            value= self.model.getParam(name2)
+                            Tctl = self.ModelTextCtrl(self, -1, 
+                                                      size=(_BOX_WIDTH/2,20),
+                                                style=wx.TE_PROCESS_ENTER)
+                            
+                            Tctl.SetValue(str (format_number(value)))
+                            self.sizer4_4.Add(Tctl, (iy,ix),(1,1),
+                                               wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+                            self.fixed_param.append([None,name2, Tctl,None,None,
+                                                      None, None,None])
+                    elif p=="nsigmas":
+                            ix = 7
+                            value= self.model.getParam(name3)
+                            Tct2 = self.ModelTextCtrl(self, -1, 
+                                                      size=(_BOX_WIDTH/2,20),
+                                                style=wx.TE_PROCESS_ENTER)
+                            
+                            Tct2.SetValue(str (format_number(value)))
+                            self.sizer4_4.Add(Tct2, (iy,ix),(1,1),
+                                               wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+                            ix +=1
+                            self.sizer4_4.Add((20,20), (iy,ix),(1,1),
+                                               wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+                            
+                            self.fixed_param.append([None,name3, Tct2
+                                                     ,None,None,None, 
+                                                     None,None])
+                            
+        ix =0
+        iy +=1 
+        self.sizer4_4.Add((20,20),(iy,ix),(1,1), 
+                          wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)  
+        first_orient  = True
+        for item in self.model.dispersion.keys():
+            if  item in self.model.orientation_params:
+                if not self.disp_cb_dict.has_key(item):
+                    self.disp_cb_dict[item]= None
+                name0="Distribution of " + item
+                name1=item+".width"
+                name2=item+".npts"
+                name3=item+".nsigmas"
+                
+                if not self.model.details.has_key(name1):
+                    self.model.details [name1] = ["",None,None]                  
+ 
+
+                iy += 1
+                for p in self.model.dispersion[item].keys(): 
+        
+                    if p=="width":
+                        ix = 0
+                        cb = wx.CheckBox(self, -1, name0, (10, 10))
+                        cb.SetToolTipString("Check for fitting")
+                        wx.EVT_CHECKBOX(self, cb.GetId(), self.select_param)
+                        self.sizer4_4.Add( cb,( iy, ix),(1,1),  
+                                           wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 
+                                           15)
+                        if self.data.__class__.__name__ =="Data2D":
+                            cb.Show(True)
+                        elif cb.IsShown():
+                            cb.Hide()
+                        ix = 1
+                        value= self.model.getParam(name1)
+                        ctl1 = self.ModelTextCtrl(self, -1, size=(_BOX_WIDTH,
+                                        20),style=wx.TE_PROCESS_ENTER)
+                        ctl1.SetToolTipString("Absolute Sigma: \n\
+                        1) It is the STD (ratio*mean) of '%s' distribution."% \
+                        item)
+                        ctl1.SetValue(str (format_number(value)))
+                        if self.data.__class__.__name__ =="Data2D":
+                            if first_orient:
+                                values.SetLabel('Sigma [A (or deg)]')
+                                values.SetToolTipString(\
+                                "Sigma(STD) in the A or deg(for angles) unit;\n\
+                                the standard deviation from the mean value.")
+                                first_orient = False
+                            ctl1.Show(True)
+                        elif ctl1.IsShown():
+                            ctl1.Hide()
+                        
+                        self.sizer4_4.Add(ctl1, (iy,ix),(1,1),wx.EXPAND)
+                        ## text to show error sign
+                        ix = 2
+                        text2=wx.StaticText(self, -1, '+/-')
+                        self.sizer4_4.Add(text2,(iy, ix),(1,1),
+                                          wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+                        text2.Hide() 
+
+                        ix = 3
+                        ctl2 = wx.TextCtrl(self, -1, size=(_BOX_WIDTH,20), 
+                                           style=0)
+                    
+                        self.sizer4_4.Add(ctl2, (iy,ix),(1,1), 
+                                          wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+                        ctl2.Hide()
+                            
+                        ix = 4
+                        ctl3 = self.ModelTextCtrl(self, -1, 
+                                                  size=(_BOX_WIDTH/2,20), 
+                                                  style=wx.TE_PROCESS_ENTER,
+                                text_enter_callback = self._onparamRangeEnter)
+
+                        self.sizer4_4.Add(ctl3, (iy,ix),(1,1), 
+                                          wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+
+                        ctl3.Hide()
+                
+                        ix = 5
+                        ctl4 = self.ModelTextCtrl(self, -1, 
+                            size=(_BOX_WIDTH/2,20), style=wx.TE_PROCESS_ENTER,
+                                text_enter_callback = self._onparamRangeEnter)
+                        self.sizer4_4.Add(ctl4, (iy,ix),(1,1), 
+                                          wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+                        ctl4.Hide()
+                        #if self.data.__class__.__name__ =="Data2D":
+                            #ctl4.Enable(True)
+                        #elif ctl4.Shown():
+                            #ctl4.Hide()
+                        
+                        if self.engine_type=="park" and \
+                                    self.data.__class__.__name__ =="Data2D":
+                            ctl3.Show(True)
+                            ctl4.Show(True) 
+                            
+                            
+                            
+                            
+                        self.fittable_param.append([cb,name1,ctl1,text2,
+                                                    ctl2, ctl3, ctl4,None])
+                        self.orientation_params_disp.append([cb,name1,ctl1,
+                                                  text2, ctl2, ctl3, ctl4,None])
+                    elif p=="npts":
+                            ix = 6
+                            value= self.model.getParam(name2)
+                            Tctl = self.ModelTextCtrl(self, -1, 
+                                                      size=(_BOX_WIDTH/2,20),
+                                                style=wx.TE_PROCESS_ENTER)
+                            
+                            Tctl.SetValue(str (format_number(value)))
+                            if self.data.__class__.__name__ =="Data2D":
+                                Tctl.Show(True)
+                            else:
+                                Tctl.Hide()
+                            self.sizer4_4.Add(Tctl, (iy,ix),(1,1),
+                                               wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+                            self.fixed_param.append([None,name2, Tctl,None,None,
+                                                      None, None,None])
+                            self.orientation_params_disp.append([None,name2, 
+                                                    Tctl,None,None,
+                                                      None, None,None])
+                    elif p=="nsigmas":
+                            ix = 7
+                            value= self.model.getParam(name3)
+                            Tct2 = self.ModelTextCtrl(self, -1, 
+                                                      size=(_BOX_WIDTH/2,20),
+                                                style=wx.TE_PROCESS_ENTER)
+                            
+                            Tct2.SetValue(str (format_number(value)))
+                            if self.data.__class__.__name__ =="Data2D":
+                                Tct2.Show(True)
+                            else:
+                                Tct2.Hide()
+                            self.sizer4_4.Add(Tct2, (iy,ix),(1,1),
+                                               wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+                            ix +=1
+
+                            self.fixed_param.append([None,name3, Tct2
+                                                 ,None,None, None, None,None])    
+                                                       
+                            self.orientation_params_disp.append([None,name3, 
+                                            Tct2 ,None,None, None, None,None])
+        
+        self.state.disp_cb_dict = copy.deepcopy(self.disp_cb_dict)  
+          
+        self.state.model = self.model.clone()  
+         ## save state into
+        self.state.cb1 = self.cb1.GetValue()
+        self._copy_parameters_state(self.parameters, self.state.parameters)
+        self._copy_parameters_state(self.orientation_params_disp,
+                                     self.state.orientation_params_disp)
+        self._copy_parameters_state(self.fittable_param, 
+                                    self.state.fittable_param)
+        self._copy_parameters_state(self.fixed_param, self.state.fixed_param)
+
+
+        wx.PostEvent(self.parent, StatusEvent(status=\
+                        " Selected Distribution: Gaussian"))   
+        #Fill the list of fittable parameters
+        self.select_all_param(event=None)
+        
+        self.Layout()
+
+    def _onFit(self, event):     
+        """
+        Allow to fit
+        """
+        #make sure all parameter values are updated.
+        if self.check_invalid_panel():
+            return
+        if self.model ==None:
+            msg="Please select a Model first..."
+            wx.MessageBox(msg, 'Info')
+            wx.PostEvent(self._manager.parent, StatusEvent(status=\
+                            "Fit: %s"%msg))
+            return
+
+        flag = self._update_paramv_on_fit() 
+                
+        if not flag:
+            msg= "Fitting range or parameters are invalid"
+            wx.PostEvent(self.parent.parent, StatusEvent(status= msg ))
+            return 
+        
+        if len(self.param_toFit) <= 0:
+            msg= "Select at least one parameter to fit"
+            wx.PostEvent(self.parent.parent, StatusEvent(status= msg ))
+            return 
+      
+        self.select_param(event =None)
+        
+        #Clear errors if exist from previous fitting
+        #self._clear_Err_on_Fit() 
+
+        # Remove or do not allow fitting on the Q=0 point, especially 
+        # when y(q=0)=None at x[0].         
+        self.qmin_x = float(self.qmin.GetValue())
+        self.qmax_x = float( self.qmax.GetValue())
+        self._manager._reset_schedule_problem( value=0)
+        self._manager.schedule_for_fit( value=1,page=self,fitproblem =None) 
+        self._manager.set_fit_range(page= self,qmin= self.qmin_x, 
+                                   qmax= self.qmax_x)
+        
+        #single fit 
+        self._manager.onFit()
+        ## allow stopping the fit 
+        #if self.engine_type=="scipy":
+        #    self.btFit.SetLabel("Stop")
+        #    self.btFit.Unbind(event=wx.EVT_BUTTON, id= self.btFit.GetId())
+        #    self.btFit.Bind(event= wx.EVT_BUTTON, 
+        #            handler=self._StopFit, id=self.btFit.GetId())
+        #else:
+        #    self.btFit.SetLabel("Fit")
+        #    self.btFit.Bind(event= wx.EVT_BUTTON, 
+        #                handler=self._onFit, id=self.btFit.GetId())
+           
+    def _StopFit(self, event):
+        """
+        Stop fit 
+        """
+        self.btFit.SetLabel("Fit")
+        if self.engine_type=="scipy":
+            self._manager.stop_fit()
+        self.btFit.Unbind(event=wx.EVT_BUTTON, id=self.btFit.GetId())
+        self.btFit.Bind(event=wx.EVT_BUTTON, handler=self._onFit,
+                        id=self.btFit.GetId())
+        
+            
+    def _on_select_model(self, event=None): 
+        """
+        call back for model selection
+        """  
+        self._on_select_model_helper() 
+        self.set_model_param_sizer(self.model)                   
+        
+        if self.model is None:
+            self._set_bookmark_flag(False)
+            self._set_save_flag(False)
+        self.enable_disp.SetValue(False)
+        self.disable_disp.SetValue(True)
+        try:
+            self.set_dispers_sizer()
+        except:
+            pass
+        self.btFit.SetFocus() 
+        self.state.enable_disp = self.enable_disp.GetValue()
+        self.state.disable_disp = self.disable_disp.GetValue()
+        self.state.pinhole_smearer = self.pinhole_smearer.GetValue()
+        self.state.slit_smearer = self.slit_smearer.GetValue()
+    
+        self.state.structurecombobox = self.structurebox.GetCurrentSelection()
+        self.state.formfactorcombobox = self.formfactorbox.GetCurrentSelection()
+      
+        if self.model != None:
+            self._set_bookmark_flag(True)
+            self._set_save_flag(True)
+            # Reset smearer, model and data
+            self.set_data(self.data)
+            # update smearer sizer
+            self.onSmear(None)
+            try:
+                temp_smear = None
+                if self.enable_smearer.GetValue():
+                    # Set the smearer environments
+                    temp_smear= self.smearer
+                #self.compute_chisqr(temp_smear)
+            except:
+                ## error occured on chisqr computation
+                pass
+            ## event to post model to fit to fitting plugins
+            (ModelEventbox, EVT_MODEL_BOX) = wx.lib.newevent.NewEvent()
+            if self.data is not None and \
+                    self.data.__class__.__name__ !="Data2D":
+                ## set smearing value whether or not 
+                #    the data contain the smearing info
+                evt = ModelEventbox(model = self.model, 
+                                        smearer = temp_smear, 
+                                        qmin = float(self.qmin_x),
+                                     qmax = float(self.qmax_x)) 
+            else:   
+                 evt = ModelEventbox(model = self.model)
+          
+            self._manager._on_model_panel(evt = evt)
+            self.state.model = self.model.clone()
+            self.state.model.name = self.model.name
+            if event is not None:
+                self._draw_model()
+        if event !=None:
+            #self._undo.Enable(True)
+            ## post state to fit panel
+            event = PageInfoEvent(page = self)
+            wx.PostEvent(self.parent, event) 
+      
+      
+    def _onparamEnter(self,event):
+        """ 
+        when enter value on panel redraw model according to changed
+        """
+        if self.model ==None:
+            msg="Please select a Model first..."
+            wx.MessageBox(msg, 'Info')
+            #wx.PostEvent(self._manager.parent, StatusEvent(status=\
+            #                "Parameters: %s"%msg))
+            return
+
+        #default flag
+        flag = False
+        self.fitrange = True
+        #get event object
+        tcrtl= event.GetEventObject()
+        wx.PostEvent(self._manager.parent, StatusEvent(status=" \
+                                updating ... ",type="update"))
+        #Clear msg if previously shown.
+        msg= ""
+        wx.PostEvent(self.parent.parent, StatusEvent(status = msg ))
+
+        if check_float(tcrtl):
+            flag = self._onparamEnter_helper() 
+            self.set_npts2fit() 
+            if self.fitrange:             
+                temp_smearer = None
+                if not self.disable_smearer.GetValue():
+                    temp_smearer= self.current_smearer
+                    ## set smearing value whether or not 
+                    #        the data contain the smearing info
+                    if self.slit_smearer.GetValue():
+                        flag1 = self.update_slit_smear()
+                        flag = flag or flag1
+                    elif self.pinhole_smearer.GetValue():
+                        flag1 = self.update_pinhole_smear()
+                        flag = flag or flag1
+                elif self.data.__class__.__name__ !="Data2D":
+                    self._manager.set_smearer(smearer=temp_smearer, 
+                                             qmin= float(self.qmin_x),
+                                            qmax= float(self.qmax_x)) 
+                if flag:   
+                    #self.compute_chisqr(smearer= temp_smearer)
+        
+                    ## new state posted
+                    if self.state_change:
+                        #self._undo.Enable(True)
+                        event = PageInfoEvent(page = self)
+                        wx.PostEvent(self.parent, event)
+                    self.state_change= False 
+            else: 
+                # invalid fit range: do nothing here: 
+                # msg already displayed in validate 
+                return      
+        else:
+            self.save_current_state()
+            msg= "Cannot Plot :Must enter a number!!!  "
+            wx.PostEvent(self.parent.parent, StatusEvent(status = msg ))
+             
+        self.save_current_state() 
+        return 
+   
+    def _onparamRangeEnter(self, event):
+        """
+        Check validity of value enter in the parameters range field
+        """
+        if self.check_invalid_panel():
+            return
+        tcrtl= event.GetEventObject()
+        #Clear msg if previously shown.
+        msg= ""
+        wx.PostEvent(self.parent.parent, StatusEvent(status = msg ))
+        # Flag to register when a parameter has changed.
+        is_modified = False
+        if tcrtl.GetValue().lstrip().rstrip()!="":
+            try:
+                value = float(tcrtl.GetValue())
+                tcrtl.SetBackgroundColour(wx.WHITE)
+                self._check_value_enter(self.fittable_param ,is_modified)
+                self._check_value_enter(self.parameters ,is_modified) 
+            except:
+                tcrtl.SetBackgroundColour("pink")
+                msg= "Model Error:wrong value entered : %s"% sys.exc_value
+                wx.PostEvent(self.parent.parent, StatusEvent(status = msg ))
+                return 
+        else:
+           tcrtl.SetBackgroundColour(wx.WHITE)
+           
+        #self._undo.Enable(True)
+        self.save_current_state()
+        event = PageInfoEvent(page = self)
+        wx.PostEvent(self.parent, event)
+        self.state_change= False
+
+                    
+    def _onQrangeEnter(self, event):
+        """
+        Check validity of value enter in the Q range field
+        """
+        if self.check_invalid_panel():
+            return
+        tcrtl= event.GetEventObject()
+        #Clear msg if previously shown.
+        msg= ""
+        wx.PostEvent(self.parent.parent, StatusEvent(status = msg ))
+        # Flag to register when a parameter has changed.
+        is_modified = False
+        if tcrtl.GetValue().lstrip().rstrip()!="":
+            try:
+                value = float(tcrtl.GetValue())
+                tcrtl.SetBackgroundColour(wx.WHITE)
+
+                # If qmin and qmax have been modified, update qmin and qmax
+                if self._validate_qrange( self.qmin, self.qmax):
+                    tempmin = float(self.qmin.GetValue())
+                    if tempmin != self.qmin_x:
+                        self.qmin_x = tempmin
+                    tempmax = float(self.qmax.GetValue())
+                    if tempmax != self.qmax_x:
+                        self.qmax_x = tempmax
+                else:
+                    tcrtl.SetBackgroundColour("pink")
+                    msg= "Model Error:wrong value entered : %s"% sys.exc_value
+                    wx.PostEvent(self.parent.parent, StatusEvent(status = msg ))
+                    return 
+                
+            except:
+                tcrtl.SetBackgroundColour("pink")
+                msg= "Model Error:wrong value entered : %s"% sys.exc_value
+                wx.PostEvent(self.parent.parent, StatusEvent(status = msg ))
+                return 
+            #Check if # of points for theory model are valid(>0).
+            # check for 2d
+            if self.data.__class__.__name__ =="Data2D":
+                # set mask   
+                radius= numpy.sqrt( self.data.qx_data*self.data.qx_data + 
+                                    self.data.qy_data*self.data.qy_data )
+                index_data = ((self.qmin_x <= radius)& \
+                                (radius<= self.qmax_x))
+                index_data = (index_data)&(self.data.mask)
+                index_data = (index_data)&(numpy.isfinite(self.data.data))
+                if len(index_data[index_data]) < 10:
+                    msg = "Cannot Plot :No or too little npts in"
+                    msg += " that data range!!!  "
+                    wx.PostEvent(self.parent.parent, StatusEvent(status = msg ))
+                    return
+                else:
+                    self.data.mask = index_data
+                    #self.Npts_fit.SetValue(str(len(self.data.mask)))
+                    self.set_npts2fit() 
+            else:
+                index_data = ((self.qmin_x <= self.data.x)& \
+                              (self.data.x <= self.qmax_x))
+                self.Npts_fit.SetValue(str(len(self.data.x[index_data])))
+
+           
+        else:
+           tcrtl.SetBackgroundColour("pink")
+           msg= "Model Error:wrong value entered!!!"
+           wx.PostEvent(self.parent.parent, StatusEvent(status = msg ))
+        
+        self._draw_model()
+        ##update chi2
+        #self.compute_chisqr(smearer= temp_smearer)
+        #self._undo.Enable(True)
+        self.save_current_state()
+        event = PageInfoEvent(page = self)
+        wx.PostEvent(self.parent, event)
+        self.state_change= False
+        return
+    
+    def _clear_Err_on_Fit(self):
+        """
+        hide the error text control shown 
+        after fitting
+        """
+        
+        if hasattr(self,"text2_3"):
+            self.text2_3.Hide()
+
+        if len(self.parameters)>0:
+            for item in self.parameters:
+                #Skip t ifhe angle parameters if 1D data
+                if self.data.__class__.__name__ !="Data2D":
+                    if item in self.orientation_params:
+                        continue
+                if item in self.param_toFit:
+                    continue
+                ## hide statictext +/-    
+                if item[3]!=None and item[3].IsShown():
+                    item[3].Hide()
+                ## hide textcrtl  for error after fit
+                if item[4]!=None and item[4].IsShown():                   
+                    item[4].Hide()
+  
+        if len(self.fittable_param)>0:
+            for item in self.fittable_param:
+                #Skip t ifhe angle parameters if 1D data
+                if self.data.__class__.__name__ !="Data2D":
+                    if item in self.orientation_params:
+                        continue
+                if item in self.param_toFit:
+                    continue
+                ## hide statictext +/-    
+                if item[3]!=None and item[3].IsShown():
+                    item[3].Hide()
+                ## hide textcrtl  for error after fit
+                if item[4]!=None and item[4].IsShown():
+                    item[4].Hide()
+        return        
+                
+    def _get_defult_custom_smear(self):
+        """
+        Get the defult values for custum smearing.
+        """
+        # get the default values
+        if self.dxl == None: self.dxl = 0.0
+        if self.dxw == None: self.dxw = ""
+        if self.dx_min == None: self.dx_min = SMEAR_SIZE_L
+        if self.dx_max == None: self.dx_max = SMEAR_SIZE_H
+        
+    def _get_smear_info(self):
+        """
+        Get the smear info from data.
+       
+        :return: self.smear_type, self.dq_l and self.dq_r, 
+            respectively the type of the smear, dq_min and 
+            dq_max for pinhole smear data 
+            while dxl and dxw for slit smear
+            
+        """
+
+        # default
+        self.smear_type = None
+        self.dq_l = None
+        self.dq_r = None
+        data = self.data
+        if self.data is None:
+            return
+        elif self.data.__class__.__name__ == 'Data2D':  
+            if data.dqx_data == None or  data.dqy_data ==None: 
+                return
+            elif self.smearer != None and (data.dqx_data.any()!=0) and \
+                            (data.dqx_data.any()!=0): 
+                self.smear_type = "Pinhole2d"
+                self.dq_l = format_number(numpy.average(data.dqx_data)) 
+                self.dq_r = format_number(numpy.average(data.dqy_data))  
+                return 
+            else: 
+                return
+        # check if it is pinhole smear and get min max if it is.
+        if data.dx != None and all(data.dx !=0): 
+            self.smear_type = "Pinhole" 
+            self.dq_l = data.dx[0]
+            self.dq_r = data.dx[-1]
+            
+        # check if it is slit smear and get min max if it is.
+        elif data.dxl != None or data.dxw != None: 
+            self.smear_type = "Slit" 
+            if data.dxl != None and all(data.dxl !=0):
+                self.dq_l = data.dxl[0]
+            if data.dxw != None and all(data.dxw !=0): 
+                self.dq_r = data.dxw[0]    
+        #return self.smear_type,self.dq_l,self.dq_r       
+    
+    def _show_smear_sizer(self):    
+        """
+        Show only the sizers depending on smear selection
+        """
+        # smear disabled
+        if self.disable_smearer.GetValue():
+            self.smear_description_none.Show(True)
+        # 2Dsmear
+        elif self._is_2D():
+            self.smear_description_accuracy_type.Show(True)
+            self.smear_accuracy.Show(True)
+            self.smear_description_accuracy_type.Show(True)
+            self.smear_description_2d.Show(True)
+            self.smear_description_2d_x.Show(True)
+            self.smear_description_2d_y.Show(True)
+            if self.pinhole_smearer.GetValue():
+                self.smear_pinhole_min.Show(True)
+                self.smear_pinhole_max.Show(True)
+        # smear from data
+        elif self.enable_smearer.GetValue():
+
+            self.smear_description_dqdata.Show(True)
+            if self.smear_type != None:
+                self.smear_description_smear_type.Show(True)
+                if self.smear_type == 'Slit':
+                    self.smear_description_slit_height.Show(True)
+                    self.smear_description_slit_width.Show(True)                
+                elif self.smear_type == 'Pinhole':
+                    self.smear_description_pin_min.Show(True)
+                    self.smear_description_pin_max.Show(True)
+                self.smear_description_smear_type.Show(True)
+                self.smear_description_type.Show(True)
+                self.smear_data_left.Show(True)
+                self.smear_data_right.Show(True)
+        # custom pinhole smear
+        elif self.pinhole_smearer.GetValue():
+            if self.smear_type == 'Pinhole':
+                self.smear_message_new_p.Show(True)
+                self.smear_description_pin_min.Show(True)
+                self.smear_description_pin_max.Show(True)
+
+            self.smear_pinhole_min.Show(True)
+            self.smear_pinhole_max.Show(True)
+        # custom slit smear
+        elif self.slit_smearer.GetValue():
+            self.smear_message_new_s.Show(True)
+            self.smear_description_slit_height.Show(True)
+            self.smear_slit_height.Show(True)
+            self.smear_description_slit_width.Show(True)
+            self.smear_slit_width.Show(True)
+
+    def _hide_all_smear_info(self):
+        """
+        Hide all smearing messages in the set_smearer sizer
+        """
+        self.smear_description_none.Hide()
+        self.smear_description_dqdata.Hide()
+        self.smear_description_type.Hide()
+        self.smear_description_smear_type.Hide()
+        self.smear_description_accuracy_type.Hide()
+        self.smear_description_2d_x.Hide()
+        self.smear_description_2d_y.Hide()
+        self.smear_description_2d.Hide()
+        
+        self.smear_accuracy.Hide()
+        self.smear_data_left.Hide()
+        self.smear_data_right.Hide()
+        self.smear_description_pin_min.Hide()
+        self.smear_pinhole_min.Hide()
+        self.smear_description_pin_max.Hide()
+        self.smear_pinhole_max.Hide()
+        self.smear_description_slit_height.Hide()
+        self.smear_slit_height.Hide()
+        self.smear_description_slit_width.Hide()
+        self.smear_slit_width.Hide()
+        self.smear_message_new_p.Hide()
+        self.smear_message_new_s.Hide()
+    
+    def _set_accuracy_list(self):
+        """
+        Set the list of an accuracy in 2D custum smear: Xhigh, High, Med, or Low
+        """
+        # list of accuracy choices
+        list = ['Low','Med','High','Xhigh']
+        for idx in range(len(list)):
+            self.smear_accuracy.Append(list[idx],idx)
+            
+    def _set_fun_box_list(self,fun_box):
+        """
+        Set the list of func for multifunctional models
+        """
+        # Check if it is multi_functional model
+        if self.model.__class__ not in self.model_list_box["Multi-Functions"]:
+            return None
+        # Get the func name list
+        list = self.model.fun_list
+        if len(list) == 0:
+            return None
+        # build function (combo)box
+        ind = 0
+        while(ind < len(list)):
+            for key, val in list.iteritems():
+                if (val == ind):
+                    fun_box.Append(key,val)
+                    break
+            ind += 1
+        
+    def _on_select_accuracy(self,event):
+        """
+        Select an accuracy in 2D custom smear: Xhigh, High, Med, or Low
+        """
+        #event.Skip()
+        # Check if the accuracy is same as before
+        #self.smear2d_accuracy = event.GetEventObject().GetValue()
+        self.smear2d_accuracy = self.smear_accuracy.GetValue()
+        if self.pinhole_smearer.GetValue():
+            self.onPinholeSmear(event=None)
+        else:    
+            self.onSmear(event=None)
+            if self.current_smearer != None:
+                self.current_smearer.set_accuracy(accuracy = 
+                                                  self.smear2d_accuracy) 
+        event.Skip()
+
+    def _on_fun_box(self,event):
+        """
+        Select an func: Erf,Rparabola,LParabola
+        """
+        fun_val = None
+        fun_box = event.GetEventObject()
+        name = fun_box.Name
+        value = fun_box.GetValue()
+        if self.model.fun_list.has_key(value):
+            fun_val = self.model.fun_list[value]
+
+        self.model.setParam(name,fun_val)
+        # save state
+        self._copy_parameters_state(self.str_parameters, 
+                                    self.state.str_parameters)
+        # update params
+        self._update_paramv_on_fit() 
+        # draw
+        self._draw_model()
+        self.Refresh()
+        # get ready for new event
+        event.Skip()
+        
+    def _onMask(self, event):     
+        """
+        Build a panel to allow to edit Mask
+        """
+        
+        from sans.guiframe.local_perspectives.plotting.masking \
+        import MaskPanel as MaskDialog
+        
+        self.panel = MaskDialog(self, data=self.data,id =-1 )
+        self.panel.Bind(wx.EVT_CLOSE, self._draw_masked_model)
+        self.panel.ShowModal()
+        #wx.PostEvent(self.parent, event)
+        
+    def _draw_masked_model(self,event):
+        """
+        Draw model image w/mask
+        """
+        event.Skip()
+
+        is_valid_qrange = self._update_paramv_on_fit()
+
+        if is_valid_qrange:
+            # try re draw the model plot if it exists
+            self._draw_model()
+            self.panel.Destroy() # frame
+            self.set_npts2fit()
+        elif self.model == None:
+            self.panel.Destroy()
+            self.set_npts2fit()
+            msg= "No model is found on updating MASK in the model plot... "
+            wx.PostEvent(self.parent.parent, StatusEvent(status = msg ))
+        else:
+            msg = ' Please consider your Q range, too.'
+            self.panel.ShowMessage(msg)
+        
+    def set_data(self, data):
+        """
+        reset the current data 
+        """
+        self.data = data
+        if self.data is None:
+            data_min = ""
+            data_max = ""
+            data_name = ""
+            self.formfactorbox.Disable()
+            self.structurebox.Disable()
+            self._set_bookmark_flag(False)
+            self._set_save_flag(False)
+        else:
+            self._set_bookmark_flag(True)
+            self._set_save_flag(True)
+            self.smearer = smear_selection(self.data, self.model)
+            self.disable_smearer.SetValue(True)
+            if self.smearer == None:
+                self.enable_smearer.Disable()
+            else:
+                self.enable_smearer.Enable()
+            
+            # more disables for 2D
+            if self.data.__class__.__name__ =="Data2D":
+                self.slit_smearer.Disable()
+                self.default_mask = copy.deepcopy(self.data.mask)
+                
+            self.formfactorbox.Enable()
+            self.structurebox.Enable()
+            data_name = self.data.name
+            #set maximum range for x in linear scale
+            if not hasattr(self.data,"data"): #Display only for 1D data fit
+                # Minimum value of data   
+                data_min = min(self.data.x)
+                # Maximum value of data  
+                data_max = max(self.data.x)
+                #number of total data points
+                self.Npts_total.SetValue(str(len(self.data.x)))
+                #default:number of data points selected to fit
+                self.Npts_fit.SetValue(str(len(self.data.x)))
+                self.btEditMask.Disable()  
+                self.EditMask_title.Disable()
+            else:
+                ## Minimum value of data 
+                data_min = 0
+                x = max(math.fabs(self.data.xmin), math.fabs(self.data.xmax)) 
+                y = max(math.fabs(self.data.ymin), math.fabs(self.data.ymax))
+                ## Maximum value of data  
+                data_max = math.sqrt(x*x + y*y)
+                #number of total data points
+                self.Npts_total.SetValue(str(len(self.data.data)))
+                #default:number of data points selected to fit
+                self.Npts_fit.SetValue(str(len(self.data.data)))
+                self.btEditMask.Enable()  
+                self.EditMask_title.Enable() 
+        self.dataSource.SetValue(data_name)
+        self.qmin_x = data_min
+        self.qmax_x = data_max
+        self.minimum_q.SetValue(str(data_min))
+        self.maximum_q.SetValue(str(data_max))
+        self.qmin.SetValue(str(data_min))
+        self.qmax.SetValue(str(data_max))
+        self.qmin.SetBackgroundColour("white")
+        self.qmax.SetBackgroundColour("white")
+        self.state.data = data
+        self.state.qmin = self.qmin_x
+        self.state.qmax = self.qmax_x
+        
+    def reset_page(self, state,first=False):
+        """
+        reset the state
+        """
+        self.reset_page_helper(state)
+        #import sans.guiframe.gui_manager
+        #evt = ModelEventbox(model=state.model)
+        #wx.PostEvent(self.event_owner, evt)  
+   
+        if self.engine_type != None:
+            self._manager._on_change_engine(engine=self.engine_type)
+
+        self.select_param(event = None) 
+        #Save state_fit
+        self.save_current_state_fit()
+        self._lay_out()
+        self.Refresh()
+        
+    def get_range(self):
+        """
+        return the fitting range
+        """
+        return float(self.qmin_x) , float(self.qmax_x)
+    
+    def get_npts2fit(self):
+        """
+        return numbers of data points within qrange
+        
+        :Note: This is for Park where chi2 is not normalized by Npts of fit
+        
+        """
+        npts2fit = 0
+        qmin,qmax = self.get_range()
+        if self.data.__class__.__name__ =="Data2D": 
+            radius= numpy.sqrt( self.data.qx_data*self.data.qx_data + 
+                                self.data.qy_data*self.data.qy_data )
+            index_data = (self.qmin_x <= radius)&(radius<= self.qmax_x)
+            index_data= (index_data)&(self.data.mask)
+            index_data = (index_data)&(numpy.isfinite(self.data.data))
+            npts2fit = len(self.data.data[index_data])
+        else:
+            for qx in self.data.x:
+                   if qx >= qmin and qx <= qmax:
+                       npts2fit += 1
+        return npts2fit
+
+    def set_npts2fit(self):
+        """
+        setValue Npts for fitting 
+        """
+        self.Npts_fit.SetValue(str(self.get_npts2fit()))
+        
+    def get_chi2(self):
+        """
+        return the current chi2
+        """
+        return self.tcChi.GetValue()
+        
+    def get_param_list(self):
+        """
+        :return self.param_toFit: list containing  references to TextCtrl
+            checked.Theses TextCtrl will allow reference to parameters to fit.
+        
+        :raise: if return an empty list of parameter fit will nnote work 
+            properly so raise ValueError,"missing parameter to fit"
+        """
+        if self.param_toFit !=[]:
+            return self.param_toFit
+        else:
+            raise ValueError,"missing parameter to fit"    
+      
+    def onsetValues(self,chisqr,p_name, out,cov):
+        """
+        Build the panel from the fit result
+        
+        :param chisqr: Value of the goodness of fit metric
+        :param p_name: the name of parameters
+        :param out: list of parameter with the best value found during fitting
+        :param cov: Covariance matrix
+   
+        """
+        if out == None or not numpy.isfinite(chisqr):
+            raise ValueError,"Fit error occured..." 
+        
+        is_modified = False
+        has_error = False    
+        
+        #Hide textctrl boxes of errors.
+        self._clear_Err_on_Fit()    
+        
+        #Check if chi2 is finite
+        if chisqr != None or numpy.isfinite(chisqr):
+        #format chi2  
+            if self.engine_type == "park":  
+                npt_fit = float(self.get_npts2fit()) 
+                if npt_fit > 0:
+                    chisqr =chisqr/npt_fit    
+            chi2 = format_number(chisqr)    
+            self.tcChi.SetValue(chi2)    
+            self.tcChi.Refresh()    
+        else:
+            self.tcChi.SetValue("-")
+        
+        #Hide error title
+        if self.text2_3.IsShown():
+            self.text2_3.Hide()
+      
+        try:
+            n = self.disp_box.GetCurrentSelection()
+            dispersity= self.disp_box.GetClientData(n)
+            if dispersity !=None and self.enable_disp.GetValue():
+                name= dispersity.__name__
+                if name == "GaussianDispersion":
+                    if hasattr(self,"text_disp_1" ):
+                        if self.text_disp_1 !=None:
+                            self.text_disp_1.Hide()
+        except:
+            dispersty = None
+            pass
+        #set the panel when fit result are float not list
+        if out.__class__== numpy.float64:
+            self.param_toFit[0][2].SetValue(format_number(out))
+            
+            if self.param_toFit[0][4].IsShown:
+                self.param_toFit[0][4].Hide()
+            if cov !=None :
+                self.text2_3.Show(True)
+                try:
+                    if dispersity !=None:
+                        name= dispersity.__name__
+                        if name == "GaussianDispersion" and \
+                        self.enable_disp.GetValue():
+                            if hasattr(self,"text_disp_1" ):
+                                if self.text_disp_1 !=None:
+                                    self.text_disp_1.Show(True)
+                except:
+                    pass
+
+                if cov[0]==None or  not numpy.isfinite(cov[0]): 
+                    if self.param_toFit[0][3].IsShown:
+                        self.param_toFit[0][3].Hide()
+                else:                    
+                    self.param_toFit[0][3].Show(True)               
+                    self.param_toFit[0][4].Show(True)
+                    self.param_toFit[0][4].SetValue(format_number(cov[0]))
+                    has_error = True
+        else:
+
+            i = 0
+            #Set the panel when fit result are list
+            for item in self.param_toFit:     
+                if len(item)>5 and item != None:     
+                    ## reset error value to initial state
+                    item[3].Hide()
+                    item[4].Hide()
+                    
+                    for ind in range(len(out)):
+                        
+                        if item[1] == p_name[ind]:
+                            break        
+                    if len(out)<=len(self.param_toFit) and out[ind] !=None:   
+                        val_out = format_number(out[ind])                  
+                        item[2].SetValue(val_out)
+
+                    if(cov !=None):
+                        
+                        try:
+                            if dispersity !=None:
+                                name= dispersity.__name__
+                                if name == "GaussianDispersion" and \
+                                                self.enable_disp.GetValue():
+                                    if hasattr(self,"text_disp_1" ):
+                                        if self.text_disp_1!=None:
+                                            if not self.text_disp_1.IsShown():
+                                                self.text_disp_1.Show(True)
+                        except:
+                            pass    
+                   
+                        if cov[ind]!=None :
+                            if numpy.isfinite(float(cov[ind])):
+                                val_err = format_number(cov[ind])
+                                item[3].Show(True)
+                                item[4].Show(True)
+                                item[4].SetValue(val_err)
+
+                                has_error = True
+                    i += 1         
+        #Show error title when any errors displayed
+        if has_error: 
+            if not self.text2_3.IsShown():
+                self.text2_3.Show(True) 
+                
+        ## save current state  
+        self.save_current_state()          
+        #plot model
+        self._draw_model()   
+        self._lay_out()      
+        #PostStatusEvent     
+        msg = "Fit completed! "
+        wx.PostEvent(self._manager.parent, StatusEvent(status=msg))
+
+    def onPinholeSmear(self, event):
+        """
+        Create a custom pinhole smear object that will change the way residuals
+        are compute when fitting
+        
+        :Note: accuracy is given by strings'High','Med', 'Low' FOR 2d,
+                     None for 1D
+                     
+        """
+
+        if self.check_invalid_panel():
+            return
+        if self.model ==None:
+            self.disable_smearer.SetValue(True)
+            msg="Please select a Model first..."
+            wx.MessageBox(msg, 'Info')
+            wx.PostEvent(self._manager.parent, StatusEvent(status=\
+                            "Smear: %s"%msg))
+            return
+
+        # Need update param values
+        self._update_paramv_on_fit()     
+
+        # msg default
+        msg = None
+        if event != None:
+            tcrtl= event.GetEventObject()
+            # event case of radio button
+            if tcrtl.GetValue()== True:
+                self.dx_min = 0.0
+                self.dx_max = 0.0
+                is_new_pinhole = True
+            else:
+                is_new_pinhole = self._is_changed_pinhole()
+        else:
+            is_new_pinhole = True 
+        # if any value is changed
+        if is_new_pinhole:
+            msg = self._set_pinhole_smear()
+        # hide all silt sizer    
+        self._hide_all_smear_info()
+        
+        ##Calculate chi2
+        temp_smearer = self.current_smearer
+        #self.compute_chisqr(smearer= temp_smearer)
+            
+        # show relevant slit sizers 
+        self._show_smear_sizer()
+
+        self.sizer_set_smearer.Layout()
+        self.Layout()   
+        
+        if event != None: 
+            event.Skip()                  
+        #self._undo.Enable(True)
+        self.save_current_state()
+        event = PageInfoEvent(page = self)
+        wx.PostEvent(self.parent, event)
+        
+    def _is_changed_pinhole(self):  
+        """
+        check if any of pinhole smear is changed
+        
+        :return: True or False
+        
+        """
+        # get the values
+        pin_min = self.smear_pinhole_min.GetValue()
+        pin_max = self.smear_pinhole_max.GetValue()
+                    
+        # Check changes in slit width    
+        try:
+            dx_min = float(pin_min)
+        except:
+            return True
+        if self.dx_min != dx_min:
+            return True
+        
+        # Check changes in slit heigth
+        try:
+            dx_max = float(pin_max)
+        except:
+            return True
+        if self.dx_max != dx_max:
+            return True
+        return False
+    
+    def _set_pinhole_smear(self):
+        """
+        Set custom pinhole smear
+        
+        :return: msg
+        
+        """
+        # copy data
+        data = copy.deepcopy(self.data)
+        if self._is_2D():
+            self.smear_type = 'Pinhole2d'
+            len_data = len(data.data)
+            data.dqx_data = numpy.zeros(len_data)
+            data.dqy_data = numpy.zeros(len_data)
+        else:
+            self.smear_type = 'Pinhole'
+            len_data = len(data.x)
+            data.dx = numpy.zeros(len_data)
+            data.dxl = None
+            data.dxw = None
+        msg = None
+   
+        get_pin_min = self.smear_pinhole_min
+        get_pin_max = self.smear_pinhole_max        
+
+        if not check_float(get_pin_min):
+            get_pin_min.SetBackgroundColour("pink")
+            msg= "Model Error:wrong value entered!!!"
+        elif not check_float(get_pin_max ):
+            get_pin_max.SetBackgroundColour("pink")
+            msg= "Model Error:wrong value entered!!!"
+        else:
+            if len_data < 2: len_data = 2     
+            self.dx_min = float(get_pin_min.GetValue())
+            self.dx_max = float(get_pin_max.GetValue())
+            if self.dx_min < 0:
+                get_pin_min.SetBackgroundColour("pink")
+                msg= "Model Error:This value can not be negative!!!"
+            elif self.dx_max <0:
+                get_pin_max.SetBackgroundColour("pink")
+                msg= "Model Error:This value can not be negative!!!"
+            elif self.dx_min != None and self.dx_max != None:   
+                if self._is_2D():
+                    data.dqx_data[data.dqx_data==0] = self.dx_min
+                    data.dqy_data[data.dqy_data==0] = self.dx_max          
+                elif self.dx_min == self.dx_max:
+                    data.dx[data.dx==0] = self.dx_min
+                else:
+                    step = (self.dx_max - self.dx_min)/(len_data-1)    
+                    data.dx = numpy.arange(self.dx_min,self.dx_max+step/1.1,
+                                           step)            
+            elif self.dx_min != None: 
+                if self._is_2D(): data.dqx_data[data.dqx_data==0] = self.dx_min
+                else: data.dx[data.dx==0] = self.dx_min 
+            elif self.dx_max != None:
+                if self._is_2D(): data.dqy_data[data.dqy_data==0] = self.dx_max
+                else: data.dx[data.dx==0] = self.dx_max          
+            self.current_smearer = smear_selection(data, self.model)
+            # 2D need to set accuracy
+            if self._is_2D(): 
+                self.current_smearer.set_accuracy(accuracy = \
+                                                  self.smear2d_accuracy)
+
+        if msg != None:
+            wx.PostEvent(self._manager.parent, StatusEvent(status = msg ))
+        else:
+            get_pin_min.SetBackgroundColour("white")
+            get_pin_max.SetBackgroundColour("white")
+        ## set smearing value whether or not the data contain the smearing info
+        self._manager.set_smearer(smearer=self.current_smearer, qmin= \
+                                 float(self.qmin_x),qmax= float(self.qmax_x))
+        return msg
+        
+    def update_pinhole_smear(self):
+        """
+        called by kill_focus on pinhole TextCntrl
+        to update the changes 
+        
+        :return: False when wrong value was entered
+        
+        """
+        # msg default
+        msg = None
+        # check if any value is changed
+        if self._is_changed_pinhole():
+            msg = self._set_pinhole_smear()           
+        #self._undo.Enable(True)
+        self.save_current_state()
+
+        if msg != None:
+            return False   
+        else:
+            return True
+                     
+    def onSlitSmear(self, event):
+        """
+        Create a custom slit smear object that will change the way residuals
+        are compute when fitting
+        """
+ 
+        if self.check_invalid_panel():
+            return
+
+        if self.model ==None:
+            self.disable_smearer.SetValue(True)
+            
+            msg="Please select a Model first..."
+            wx.MessageBox(msg, 'Info')
+            wx.PostEvent(self._manager.parent, StatusEvent(status=\
+                            "Smear: %s"%msg))
+            return
+
+        # Need update param values
+        self._update_paramv_on_fit()
+
+        # msg default
+        msg = None
+        # for event given
+        if event != None:
+            tcrtl= event.GetEventObject()
+            # event case of radio button
+            if tcrtl.GetValue():
+                self.dxl = 0.0
+                self.dxw = 0.0
+                is_new_slit = True
+            else:
+                is_new_slit = self._is_changed_slit()
+        else:
+            is_new_slit = True 
+        
+        # if any value is changed
+        if is_new_slit:
+            msg = self._set_slit_smear()
+            
+        # hide all silt sizer
+        self._hide_all_smear_info()        
+        ##Calculate chi2
+        #self.compute_chisqr(smearer= self.current_smearer)  
+        # show relevant slit sizers       
+        self._show_smear_sizer()
+        self.sizer_set_smearer.Layout()
+        self.Layout()
+
+        if event != None:
+            event.Skip()     
+        #self._undo.Enable(True)
+        self.save_current_state()
+        event = PageInfoEvent(page = self)
+        wx.PostEvent(self.parent, event)
+        if msg != None:
+            wx.PostEvent(self._manager.parent, StatusEvent(status = msg))
+
+    def _is_changed_slit(self):  
+        """
+        check if any of slit lengths is changed
+        
+        :return: True or False
+        
+        """
+        # get the values
+        width = self.smear_slit_width.GetValue()
+        height = self.smear_slit_height.GetValue()
+        
+        # check and change the box bg color if it was pink 
+        #    but it should be white now
+        # because this is the case that _set_slit_smear() will not handle
+        if height.lstrip().rstrip()=="":
+            self.smear_slit_height.SetBackgroundColour(wx.WHITE)
+        if width.lstrip().rstrip()=="":
+            self.smear_slit_width.SetBackgroundColour(wx.WHITE)
+            
+        # Check changes in slit width    
+        if width == "": 
+            dxw = 0.0
+        else:
+            try:
+                dxw = float(width)
+            except:
+                return True
+        if self.dxw != dxw:
+            return True
+        
+        # Check changes in slit heigth
+        if height == "": 
+            dxl = 0.0
+        else:
+            try:
+                dxl = float(height)
+            except:
+                return True
+        if self.dxl != dxl:
+            return True
+
+        return False
+    
+    def _set_slit_smear(self):
+        """
+        Set custom slit smear
+        
+        :return: message to inform the user about the validity
+            of the values entered for slit smear
+        """
+        if self.data.__class__.__name__ == "Data2D":
+            return
+        temp_smearer = None
+        # make sure once more if it is smearer
+        data = copy.deepcopy(self.data)
+        data_len = len(data.x)
+        data.dx = None
+        data.dxl = None
+        data.dxw = None
+        msg = None
+   
+        try:
+            self.dxl = float(self.smear_slit_height.GetValue())
+            data.dxl = self.dxl* numpy.ones(data_len)
+            self.smear_slit_height.SetBackgroundColour(wx.WHITE)
+        except: 
+            data.dxl = numpy.zeros(data_len)
+            if self.smear_slit_height.GetValue().lstrip().rstrip()!="":
+                self.smear_slit_height.SetBackgroundColour("pink")
+                msg = "Wrong value entered... "  
+            else:
+                self.smear_slit_height.SetBackgroundColour(wx.WHITE)
+        try:
+            self.dxw = float(self.smear_slit_width.GetValue())
+            self.smear_slit_width.SetBackgroundColour(wx.WHITE)
+            data.dxw = self.dxw* numpy.ones(data_len)
+        except: 
+            data.dxw = numpy.zeros(data_len)
+            if self.smear_slit_width.GetValue().lstrip().rstrip()!="":
+                self.smear_slit_width.SetBackgroundColour("pink")
+                msg = "Wrong Fit value entered... "
+            else:
+                self.smear_slit_width.SetBackgroundColour(wx.WHITE)
+              
+        self.current_smearer = smear_selection(data, self.model)
+        #temp_smearer = self.current_smearer
+        ## set smearing value whether or not the data contain the smearing info
+        self._manager.set_smearer(smearer=self.current_smearer, qmin= \
+                                 float(self.qmin_x), qmax= float(self.qmax_x)) 
+        return msg
+    
+    def update_slit_smear(self):
+        """
+        called by kill_focus on pinhole TextCntrl
+        to update the changes 
+        
+        :return: False when wrong value was entered
+        
+        """             
+        # msg default
+        msg = None
+        # check if any value is changed
+        if self._is_changed_slit():
+            msg = self._set_slit_smear()           
+        #self._undo.Enable(True)
+        self.save_current_state()
+
+        if msg != None:
+            return False   
+        else:
+            return True
+                            
+    def onSmear(self, event):
+        """
+        Create a smear object that will change the way residuals
+        are compute when fitting
+        """
+        if event != None: 
+            event.Skip()    
+        if self.check_invalid_panel():
+            return
+        if self.model == None:
+            self.disable_smearer.SetValue(True)
+            msg="Please select a Model first..."
+            wx.MessageBox(msg, 'Info')
+            wx.PostEvent(self._manager.parent, StatusEvent(status=\
+                            "Smear: %s"%msg))
+            return
+        
+        # Need update param values
+        self._update_paramv_on_fit()
+              
+        temp_smearer = None
+        self._get_smear_info()
+        
+        #renew smear sizer
+        if self.smear_type != None:
+            self.smear_description_smear_type.SetValue(str(self.smear_type))
+            self.smear_data_left.SetValue(str(self.dq_l))   
+            self.smear_data_right.SetValue(str(self.dq_r))       
+
+        self._hide_all_smear_info()
+        
+        data = copy.deepcopy(self.data)
+        # make sure once more if it is smearer
+        self.current_smearer = smear_selection(data, self.model)
+        
+        if self.enable_smearer.GetValue():
+            if hasattr(self.data,"dxl"):
+                
+                msg= ": Resolution smearing parameters"
+            if hasattr(self.data,"dxw"):
+                msg= ": Slit smearing parameters"
+            if self.smearer ==None:
+                wx.PostEvent(self._manager.parent, StatusEvent(status=\
+                            "Data contains no smearing information"))
+            else:
+                wx.PostEvent(self._manager.parent, StatusEvent(status=\
+                            "Data contains smearing information"))
+
+            #self.smear_description_dqdata.Show(True)
+            self.smear_data_left.Show(True)
+            self.smear_data_right.Show(True)
+            temp_smearer= self.current_smearer
+        elif self.disable_smearer.GetValue():
+            self.smear_description_none.Show(True)
+            
+        self._show_smear_sizer()
+        
+        self.sizer_set_smearer.Layout()
+        self.Layout()
+        ## set smearing value whether or not the data contain the smearing info
+        self._manager.set_smearer(smearer=temp_smearer, qmin= float(self.qmin_x),
+                                     qmax= float(self.qmax_x)) 
+
+        ##Calculate chi2
+        #self.compute_chisqr(smearer= temp_smearer)
+        
+        self.state.enable_smearer=  self.enable_smearer.GetValue()
+        self.state.disable_smearer=self.disable_smearer.GetValue()
+        self.state.pinhole_smearer = self.pinhole_smearer.GetValue()
+        self.state.slit_smearer = self.slit_smearer.GetValue()
+      
+    def on_complete_chisqr(self, event):  
+        """
+        print result chisqr 
+        
+        :event: activated by fitting/ complete after draw
+        
+        """
+        try:
+            if event ==None:
+                output= "-"
+            else:
+                output = event.output
+            self.tcChi.SetValue(str(format_number(output)))
+
+            self.state.tcChi = self.tcChi.GetValue()
+        except:
+            pass  
+            
+
+    def select_all_param(self,event): 
+        """
+        set to true or false all checkBox given the main checkbox value cb1
+        """            
+        self.param_toFit=[]
+        if  self.parameters !=[]:
+            if  self.cb1.GetValue():
+                for item in self.parameters:
+                    ## for data2D select all to fit
+                    if self.data.__class__.__name__=="Data2D":
+                        item[0].SetValue(True)
+                        self.param_toFit.append(item )
+                    else:
+                        ## for 1D all parameters except orientation
+                        if not item in self.orientation_params:
+                            item[0].SetValue(True)
+                            self.param_toFit.append(item )
+                #if len(self.fittable_param)>0:
+                for item in self.fittable_param:
+                    if self.data.__class__.__name__=="Data2D":
+                        item[0].SetValue(True)
+                        self.param_toFit.append(item )
+                    else:
+                        ## for 1D all parameters except orientation
+                        if not item in self.orientation_params_disp:
+                            item[0].SetValue(True)
+                            self.param_toFit.append(item )
+            else:
+                for item in self.parameters:
+                    item[0].SetValue(False)
+                for item in self.fittable_param:
+                    item[0].SetValue(False)
+                self.param_toFit=[]
+           
+        self.save_current_state_fit()  
+       
+        if event !=None:
+            #self._undo.Enable(True)
+            ## post state to fit panel
+            event = PageInfoEvent(page = self)
+            wx.PostEvent(self.parent, event) 
+     
+    def select_param(self,event):
+        """ 
+        Select TextCtrl  checked for fitting purpose and stores them
+        in  self.param_toFit=[] list
+        """
+        self.param_toFit=[]
+        for item in self.parameters:
+            #Skip t ifhe angle parameters if 1D data
+            if self.data.__class__.__name__ !="Data2D":
+                if item in self.orientation_params:
+                    continue
+            #Select parameters to fit for list of primary parameters
+            if item[0].GetValue():
+                if not (item in self.param_toFit):
+                    self.param_toFit.append(item )  
+            else:
+                #remove parameters from the fitting list
+                if item in self.param_toFit:
+                    self.param_toFit.remove(item)
+
+        #Select parameters to fit for list of fittable parameters 
+        #        with dispersion          
+        for item in self.fittable_param:
+            #Skip t ifhe angle parameters if 1D data
+            if self.data.__class__.__name__ !="Data2D":
+                if item in self.orientation_params:
+                    continue
+            if item[0].GetValue():
+                if not (item in self.param_toFit):
+                    self.param_toFit.append(item)  
+            else:
+                #remove parameters from the fitting list
+                if item in self.param_toFit:
+                    self.param_toFit.remove(item)
+
+        #Calculate num. of angle parameters 
+        if self.data.__class__.__name__ =="Data2D":  
+            len_orient_para = 0
+        else:
+            len_orient_para = len(self.orientation_params)  #assume even len 
+        #Total num. of angle parameters
+        if len(self.fittable_param) > 0:
+            len_orient_para *= 2
+        #Set the value of checkbox that selected every checkbox or not            
+        if len(self.parameters)+len(self.fittable_param)-len_orient_para ==\
+                len(self.param_toFit):
+            self.cb1.SetValue(True)
+        else:
+            self.cb1.SetValue(False)
+        self.save_current_state_fit()
+        if event !=None:
+            #self._undo.Enable(True)
+            ## post state to fit panel
+            event = PageInfoEvent(page = self)
+            wx.PostEvent(self.parent, event) 
+    
+    def set_model_param_sizer(self, model):
+        """
+        Build the panel from the model content
+        
+        :param model: the model selected in combo box for fitting purpose
+        
+        """
+        self.sizer3.Clear(True)
+        self.parameters = []
+        self.str_parameters = []
+        self.param_toFit=[]
+        self.fittable_param=[]
+        self.fixed_param=[]
+        self.orientation_params=[]
+        self.orientation_params_disp=[]
+        
+        if model ==None:
+            self.sizer3.Layout()
+            self.SetupScrolling()
+            return
+        ## the panel is drawn using the current value of the fit engine
+        if self.engine_type==None and self._manager !=None:
+            self.engine_type= self._manager._return_engine_type()
+
+        box_description= wx.StaticBox(self, -1,str("Model Parameters"))
+        boxsizer1 = wx.StaticBoxSizer(box_description, wx.VERTICAL)
+        sizer = wx.GridBagSizer(5,5)
+        ## save the current model
+        self.model = model
+           
+        keys = self.model.getParamList()
+        #list of dispersion paramaters
+        self.disp_list=self.model.getDispParamList()
+
+        def custom_compare(a,b):
+            """
+            Custom compare to order, first by alphabets then second by number.
+            """ 
+            # number at the last digit
+            a_last = a[len(a)-1]
+            b_last = b[len(b)-1]
+            # default
+            num_a = None
+            num_b = None
+            # split the names
+            a2 = a.lower().split('_')
+            b2 = b.lower().split('_')
+            # check length of a2, b2
+            len_a2 = len(a2)
+            len_b2 = len(b2)
+            # check if it contains a int number(<10)
+            try: 
+                num_a = int(a_last)
+            except: pass
+            try:
+                num_b = int(b_last)
+            except: pass
+            # Put 'scale' near the top; happens 
+            # when numbered param name exists
+            if a == 'scale':
+                return -1
+            # both have a number    
+            if num_a != None and num_b != None:
+                if num_a > num_b: return -1
+                # same number
+                elif num_a == num_b: 
+                    # different last names
+                    if a2[len_a2-1] != b2[len_b2-1] and num_a != 0:
+                        return -cmp(a2[len_a2-1], b2[len_b2-1])
+                    else: 
+                        return cmp(a, b) 
+                else: return 1
+            # one of them has a number
+            elif num_a != None: return 1
+            elif num_b != None: return -1
+            # no numbers
+            else: return cmp(a.lower(), b.lower())
+
+                        
+        keys.sort(custom_compare)
+    
+        iy = 0
+        ix = 0
+        select_text = "Select All"
+        self.cb1 = wx.CheckBox(self, -1,str(select_text), (10, 10))
+        wx.EVT_CHECKBOX(self, self.cb1.GetId(), self.select_all_param)
+        self.cb1.SetToolTipString("To check/uncheck all the boxes below.")
+        #self.cb1.SetValue(True)
+        
+        sizer.Add(self.cb1,(iy, ix),(1,1),\
+                             wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 5)
+        ix +=1
+        self.text2_2 = wx.StaticText(self, -1, 'Values')
+        sizer.Add(self.text2_2,(iy, ix),(1,1),\
+                            wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+        ix +=2 
+        self.text2_3 = wx.StaticText(self, -1, 'Errors')
+        sizer.Add(self.text2_3,(iy, ix),(1,1),\
+                            wx.EXPAND|wx.ADJUST_MINSIZE, 0) 
+        self.text2_3.Hide()
+        ix +=1 
+        self.text2_min = wx.StaticText(self, -1, 'Min')
+        sizer.Add(self.text2_min,(iy, ix),(1,1),\
+                            wx.EXPAND|wx.ADJUST_MINSIZE, 0) 
+        self.text2_min.Hide()
+        ix +=1 
+        self.text2_max = wx.StaticText(self, -1, 'Max')
+        sizer.Add(self.text2_max,(iy, ix),(1,1),\
+                            wx.EXPAND|wx.ADJUST_MINSIZE, 0) 
+        self.text2_max.Hide()
+        ix += 1
+        self.text2_4 = wx.StaticText(self, -1, '[Units]')
+        sizer.Add(self.text2_4,(iy, ix),(1,1),\
+                            wx.EXPAND|wx.ADJUST_MINSIZE, 0) 
+        self.text2_4.Hide()
+        if self.engine_type=="park":
+            self.text2_max.Show(True)
+            self.text2_min.Show(True)
+        
+        for item in keys:
+            if not item in self.disp_list and not item in \
+                    self.model.orientation_params:
+                
+                ##prepare a spot to store errors
+                if not self.model.details.has_key(item):
+                    self.model.details [item] = ["",None,None] 
+         
+                iy += 1
+                ix = 0
+                if self.model.__class__ in \
+                    self.model_list_box["Multi-Functions"] \
+                    and item in self.model.non_fittable:
+                    non_fittable_name = wx.StaticText(self, -1, item )
+                    sizer.Add(non_fittable_name,(iy, ix),(1,1),\
+                            wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 21)
+                    ## add parameter value
+                    ix += 1
+                    value= self.model.getParam(item)
+                    if len(self.model.fun_list) > 0:
+                        num = item.split('_')[1][5:7]
+                        fun_box = wx.ComboBox(self, -1,size=(100,-1),
+                                    style=wx.CB_READONLY, name = '%s'% item)
+                        self._set_fun_box_list(fun_box)
+                        fun_box.SetSelection(0)
+                        #self.fun_box.SetToolTipString("A function 
+                        #    describing the interface")
+                        wx.EVT_COMBOBOX(fun_box,-1, self._on_fun_box)
+                    else:
+                        fun_box = self.ModelTextCtrl(self, -1, 
+                                                     size=(_BOX_WIDTH,20),
+                                style=wx.TE_PROCESS_ENTER, name ='%s'% item)
+                        fun_box.SetToolTipString("Hit 'Enter' after typing.")
+                        fun_box.SetValue(format_number(value))
+                    sizer.Add(fun_box, (iy,ix),(1,1), wx.EXPAND)
+                    self.str_parameters.append([None,item, fun_box,
+                                                None,None,None,None,None])
+
+                    
+                else:
+                    ## add parameters name with checkbox for selecting to fit
+                    cb = wx.CheckBox(self, -1, item )              
+                    cb.SetToolTipString(" Check for fitting.")
+                    #cb.SetValue(True)
+                    wx.EVT_CHECKBOX(self, cb.GetId(), self.select_param)
+                    
+                    sizer.Add( cb,( iy, ix),(1,1),
+                                 wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 5)
+
+                    ## add parameter value
+                    ix += 1
+                    value= self.model.getParam(item)
+                    ctl1 = self.ModelTextCtrl(self, -1, size=(_BOX_WIDTH,20),
+                                        style=wx.TE_PROCESS_ENTER)
+                    ctl1.SetToolTipString("Hit 'Enter' after typing.")
+                    ctl1.SetValue(format_number(value))
+                    sizer.Add(ctl1, (iy,ix),(1,1), wx.EXPAND)
+                    ## text to show error sign
+                    ix += 1
+                    text2=wx.StaticText(self, -1, '+/-')
+                    sizer.Add(text2,(iy, ix),(1,1),\
+                                    wx.EXPAND|wx.ADJUST_MINSIZE, 0) 
+                    text2.Hide() 
+                    ix += 1
+                    ctl2 = wx.TextCtrl(self, -1, size=(_BOX_WIDTH,20), style=0)
+                    sizer.Add(ctl2, (iy,ix),(1,1), 
+                              wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+                    ctl2.Hide()
+                    
+                    ix += 1
+                    ctl3 = self.ModelTextCtrl(self, -1, size=(_BOX_WIDTH/2,20),
+                                               style=wx.TE_PROCESS_ENTER,
+                                text_enter_callback = self._onparamRangeEnter)
+         
+                    sizer.Add(ctl3, (iy,ix),(1,1), 
+                              wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+                    ctl3.Hide()
+            
+                    ix += 1
+                    ctl4 = self.ModelTextCtrl(self, -1, size=(_BOX_WIDTH/2,20),
+                                               style=wx.TE_PROCESS_ENTER,
+                                text_enter_callback = self._onparamRangeEnter)
+                    sizer.Add(ctl4, (iy,ix),(1,1), 
+                              wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+          
+                    ctl4.Hide()
+    
+                    if self.engine_type=="park":
+                        ctl3.Show(True)
+                        ctl4.Show(True)
+                    ix +=1
+                    # Units
+                    if self.model.details.has_key(item):
+                        units = wx.StaticText(self, -1, 
+                            self.model.details[item][0], style=wx.ALIGN_LEFT)
+                    else:
+                        units = wx.StaticText(self, -1, "", style=wx.ALIGN_LEFT)
+                    sizer.Add(units, (iy,ix),(1,1), 
+                               wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+                        
+                    ##[cb state, name, value, "+/-", error of fit, min, max , units]
+                    self.parameters.append([cb,item, ctl1,
+                                            text2,ctl2, ctl3, ctl4,units])
+                                  
+        iy+=1
+        sizer.Add((10,10),(iy,ix),(1,1), 
+                  wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
+        
+        # type can be either Guassian or Array
+        if len(self.model.dispersion.values())>0:
+            type= self.model.dispersion.values()[0]["type"]
+        else:
+            type = "Gaussian"
+            
+        iy += 1
+        ix = 0
+        #Add tile for orientational angle
+        for item in keys:
+            if item in self.model.orientation_params:       
+                orient_angle = wx.StaticText(self, -1, '[For 2D only]:')
+                sizer.Add(orient_angle,(iy, ix),(1,1), 
+                          wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15) 
+                if not self.data.__class__.__name__ =="Data2D":
+                    orient_angle.Hide()
+                else:
+                    orient_angle.Show(True)
+                break
+      
+        #For Gaussian only
+        if type.lower() != "array":
+            for item in self.model.orientation_params:
+                if not item in self.disp_list:
+                    ##prepare a spot to store min max
+                    if not self.model.details.has_key(item):
+                        self.model.details [item] = ["",None,None] 
+                          
+                    iy += 1
+                    ix = 0
+                    ## add parameters name with checkbox for selecting to fit
+                    cb = wx.CheckBox(self, -1, item )
+                    cb.SetValue(False)
+                    cb.SetToolTipString("Check for fitting")
+                    wx.EVT_CHECKBOX(self, cb.GetId(), self.select_param)
+                    if self.data.__class__.__name__ =="Data2D":
+                        cb.Show(True)
+                    else:
+                        cb.Hide()
+                    sizer.Add( cb,( iy, ix),(1,1),
+                                 wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 5)
+    
+                    ## add parameter value
+                    ix += 1
+                    value= self.model.getParam(item)
+                    ctl1 = self.ModelTextCtrl(self, -1, size=(_BOX_WIDTH,20),
+                                        style=wx.TE_PROCESS_ENTER)
+                    ctl1.SetToolTipString("Hit 'Enter' after typing.")
+                    ctl1.SetValue(format_number(value))
+                    if self.data.__class__.__name__ =="Data2D":
+                        ctl1.Show(True)
+                    else:
+                        ctl1.Hide()
+                    sizer.Add(ctl1, (iy,ix),(1,1), wx.EXPAND)
+                    ## text to show error sign
+                    ix += 1
+                    text2=wx.StaticText(self, -1, '+/-')
+                    sizer.Add(text2,(iy, ix),(1,1),\
+                                    wx.EXPAND|wx.ADJUST_MINSIZE, 0) 
+                    text2.Hide() 
+                    ix += 1
+                    ctl2 = wx.TextCtrl(self, -1, size=(_BOX_WIDTH,20), style=0)
+                    sizer.Add(ctl2, (iy,ix),(1,1), 
+                              wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+                    ctl2.Hide()
+                    
+                    
+                    ix += 1
+                    ctl3 = self.ModelTextCtrl(self, -1, size=(_BOX_WIDTH/2,20), 
+                                              style=wx.TE_PROCESS_ENTER,
+                                text_enter_callback = self._onparamRangeEnter)
+                    
+                    sizer.Add(ctl3, (iy,ix),(1,1), 
+                              wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+                    ctl3.Hide()
+                 
+                    ix += 1
+                    ctl4 = self.ModelTextCtrl(self, -1, size=(_BOX_WIDTH/2,20),
+                                               style=wx.TE_PROCESS_ENTER,
+                            text_enter_callback = self._onparamRangeEnter)
+                    sizer.Add(ctl4, (iy,ix),(1,1), 
+                              wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+                   
+                    ctl4.Hide()
+                    
+                    if self.engine_type =="park" and \
+                            self.data.__class__.__name__ =="Data2D":                      
+                        ctl3.Show(True)
+                        ctl4.Show(True)
+                    
+                    ix +=1
+                    # Units
+                    if self.model.details.has_key(item):
+                        units = wx.StaticText(self, -1, 
+                                              self.model.details[item][0], 
+                                              style=wx.ALIGN_LEFT)
+                    else:
+                        units = wx.StaticText(self, -1, "", style=wx.ALIGN_LEFT)
+                    if self.data.__class__.__name__ =="Data2D":
+                        units.Show(True)
+                    
+                    else:
+                        units.Hide()
+                    
+                    sizer.Add(units, (iy,ix),(1,1),  
+                              wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+                                          
+                    ##[cb state, name, value, "+/-", error of fit,min,max,units]
+                    self.parameters.append([cb,item, ctl1,
+                                            text2,ctl2, ctl3, ctl4,units])
+                    self.orientation_params.append([cb,item, ctl1,
+                                            text2,ctl2, ctl3, ctl4,units])
+              
+        iy+=1
+        
+        #Display units text on panel
+        for item in keys:   
+            if self.model.details.has_key(item):
+                self.text2_4.Show()
+        #Fill the list of fittable parameters
+        self.select_all_param(event=None)
+
+        self.save_current_state_fit()
+        boxsizer1.Add(sizer)
+        self.sizer3.Add(boxsizer1,0, wx.EXPAND | wx.ALL, 10)
+        self.sizer3.Layout()
+        self.Layout()
+        self.Refresh()
+        self.SetupScrolling()
+        
+
+class BGTextCtrl(wx.TextCtrl):
+    """
+    Text control used to display outputs.
+    No editing allowed. The background is 
+    grayed out. User can't select text.
+    """
+    def __init__(self, *args, **kwds):
+        wx.TextCtrl.__init__(self, *args, **kwds)
+        self.SetEditable(False)
+        self.SetBackgroundColour(self.GetParent().GetBackgroundColour())
+        
+        # Bind to mouse event to avoid text highlighting
+        # The event will be skipped once the call-back
+        # is called.
+        self.Bind(wx.EVT_MOUSE_EVENTS, self._click)
+        
+    def _click(self, event):
+        """
+        Prevent further handling of the mouse event
+        by not calling Skip().
+        """ 
+        pass
+ 
+"""
+Example: ::   
+    
+    class HelpWindow(wx.Frame):
+        def __init__(self, parent, id, title):
+            wx.Frame.__init__(self, parent, id, title, size=(570, 400))
+           
+            from sans.models.CylinderModel import CylinderModel
+            model = CylinderModel()
+           
+            from danse.common.plottools.plottables import Data1D
+            data= Data1D(x=[1,2], y=[3,4], dy=[0.1, 0,1])
+        
+            from fitpanel import PageInfo
+            myinfo = PageInfo(self,  model, data=data )
+            
+            ## add data
+            
+            from models import ModelList
+            mylist= ModelList()
+    
+            from sans.models.SphereModel import SphereModel
+            from sans.models.SquareWellStructure import SquareWellStructure
+            from sans.models.DebyeModel import DebyeModel
+            from sans.models.LineModel import LineModel
+            name= "shapes"
+            list1= [SphereModel]
+            mylist.set_list( name, list1)
+            
+            name= "Shape-independent"
+            list1= [DebyeModel]
+            mylist.set_list( name, list1)
+            
+            name= "Structure Factors"
+            list1= [SquareWellStructure]
+            mylist.set_list( name, list1)
+            
+            name= "Added models"
+            list1= [LineModel]
+            mylist.set_list( name, list1)
+            
+            myinfo.model_list_box = mylist.get_list()
+            
+            self.page = FitPage(self, myinfo) 
+            
+            self.Centre()
+            self.Show(True)
+     
+    if __name__=="__main__":
+        app = wx.App()
+        HelpWindow(None, -1, 'HelpWindow')
+        app.MainLoop()
+"""
+            
Index: sansview/perspectives/fitting/Copy of models.py
===================================================================
--- sansview/perspectives/fitting/Copy of models.py	(revision a07e72f8fb8281178797bf691b36e56274d8580b)
+++ sansview/perspectives/fitting/Copy of models.py	(revision a07e72f8fb8281178797bf691b36e56274d8580b)
@@ -0,0 +1,562 @@
+
+import wx
+import wx.lib.newevent
+import imp
+import os,sys,math
+import os.path
+
+(ModelEvent, EVT_MODEL) = wx.lib.newevent.NewEvent()
+from sans.guiframe.events import StatusEvent  
+# Time is needed by the log method
+import time
+
+# Explicitly import from the pluginmodel module so that py2exe
+# places it in the distribution. The Model1DPlugin class is used
+# as the base class of plug-in models.
+from sans.models.pluginmodel import Model1DPlugin
+    
+def log(message):
+    """
+    """
+    out = open("plugins.log", 'a')
+    out.write("%10g:  %s\n" % (time.clock(), message))
+    out.close()
+
+def findModels():
+    """
+    """
+    log("looking for models in: %s/plugins" % os.getcwd())
+    if os.path.isdir('plugins'):
+        return _findModels('plugins')
+    return []
+    
+def _check_plugin(model, name):
+    """
+    Do some checking before model adding plugins in the list
+    
+    :param model: class model to add into the plugin list
+    :param name:name of the module plugin
+    
+    :return model: model if valid model or None if not valid
+    
+    """
+    #Check is the plugin is of type Model1DPlugin
+    if not issubclass(model, Model1DPlugin):
+        msg= "Plugin %s must be of type Model1DPlugin \n"%str(name)
+        log(msg)
+        return None
+    if model.__name__!="Model":
+        msg= "Plugin %s class name must be Model \n"%str(name)
+        log(msg)
+        return None
+    try:
+        new_instance= model()
+    except:
+        msg="Plugin %s error in __init__ \n\t: %s %s\n"%(str(name),
+                                    str(sys.exc_type),sys.exc_value)
+        log(msg)
+        return None
+   
+    new_instance= model() 
+    if hasattr(new_instance,"function"):
+        try:
+           value=new_instance.function()
+        except:
+           msg="Plugin %s: error writing function \n\t :%s %s\n "%(str(name),
+                                    str(sys.exc_type),sys.exc_value)
+           log(msg)
+           return None
+    else:
+       msg="Plugin  %s needs a method called function \n"%str(name)
+       log(msg)
+       return None
+    return model
+  
+  
+def _findModels(dir):
+    """
+    """
+    # List of plugin objects
+    plugins = []
+    # Go through files in plug-in directory
+    try:
+        list = os.listdir(dir)
+        for item in list:
+            toks = os.path.splitext(os.path.basename(item))
+            if toks[1]=='.py' and not toks[0]=='__init__':
+                name = toks[0]
+            
+                path = [os.path.abspath(dir)]
+                file = None
+                try:
+                    (file, path, info) = imp.find_module(name, path)
+                    module = imp.load_module( name, file, item, info )
+                    if hasattr(module, "Model"):
+                        try:
+                            if _check_plugin(module.Model, name)!=None:
+                                plugins.append(module.Model)
+                        except:
+                            msg="Error accessing Model"
+                            msg+="in %s\n  %s %s\n" % (name,
+                                    str(sys.exc_type), sys.exc_value)
+                            log(msg)
+                except:
+                    msg="Error accessing Model"
+                    msg +=" in %s\n  %s %s \n" %(name,
+                                    str(sys.exc_type), sys.exc_value)
+                    log(msg)
+                finally:
+              
+                    if not file==None:
+                        file.close()
+    except:
+        # Don't deal with bad plug-in imports. Just skip.
+        pass
+    return plugins
+
+class ModelList(object):
+    """
+    Contains dictionary of model and their type
+    """
+    def __init__(self):
+        """
+        """
+        self.mydict = {}
+        
+    def set_list(self, name, mylist):
+        """
+        :param name: the type of the list
+        :param mylist: the list to add
+        
+        """
+        if name not in self.mydict.keys():
+            self.mydict[name] = mylist
+            
+            
+    def get_list(self):
+        """
+        return all the list stored in a dictionary object
+        """
+        return self.mydict
+        
+class ModelManager:
+    """
+    """
+    ## external dict for models
+    model_combobox = ModelList()
+    ## Dictionary of form models
+    form_factor_dict = {}
+    ## dictionary of other
+    struct_factor_dict = {}
+    ##list of form factors
+    shape_list =[]
+    ## independent shape model list
+    shape_indep_list = []
+    ##list of structure factors 
+    struct_list= []
+    ##list of model allowing multiplication
+    multiplication_factor=[]
+    ##list of multifunctional shapes
+    multi_func_list =[]
+    ## list of added models
+    plugins=[]
+    ## Event owner (guiframe)
+    event_owner = None
+    
+    def _getModelList(self):
+        """
+        List of models we want to make available by default
+        for this application
+    
+        :return: the next free event ID following the new menu events
+        
+        """
+        from sans.models.SphereModel import SphereModel
+        self.shape_list.append(SphereModel)
+        self.multiplication_factor.append(SphereModel)
+        
+        from sans.models.BinaryHSModel import BinaryHSModel
+        self.shape_list.append(BinaryHSModel)
+                        
+        from sans.models.FuzzySphereModel import FuzzySphereModel
+        self.shape_list.append(FuzzySphereModel)
+        self.multiplication_factor.append(FuzzySphereModel)
+           
+        from sans.models.CoreShellModel import CoreShellModel
+        self.shape_list.append(CoreShellModel)
+        self.multiplication_factor.append(CoreShellModel)
+        
+        from sans.models.CoreMultiShellModel import CoreMultiShellModel
+        self.shape_list.append(CoreMultiShellModel)
+        self.multiplication_factor.append(CoreMultiShellModel)
+        self.multi_func_list.append(CoreMultiShellModel)
+
+        from sans.models.VesicleModel import VesicleModel
+        self.shape_list.append(VesicleModel)
+        self.multiplication_factor.append(VesicleModel)
+        
+        from sans.models.MultiShellModel import MultiShellModel
+        self.shape_list.append(MultiShellModel)
+        self.multiplication_factor.append(MultiShellModel)
+        
+        from sans.models.OnionExpShellModel import OnionExpShellModel
+        self.shape_list.append(OnionExpShellModel)
+        self.multiplication_factor.append(OnionExpShellModel)
+        self.multi_func_list.append(OnionExpShellModel)
+                        
+        from sans.models.SphericalSLDModel import SphericalSLDModel
+        self.shape_list.append(SphericalSLDModel)
+        self.multiplication_factor.append(SphericalSLDModel)
+        self.multi_func_list.append(SphericalSLDModel)
+        
+        from sans.models.CylinderModel import CylinderModel
+        self.shape_list.append(CylinderModel)
+        self.multiplication_factor.append(CylinderModel)
+        
+        from sans.models.CoreShellCylinderModel import CoreShellCylinderModel
+        self.shape_list.append(CoreShellCylinderModel)
+        self.multiplication_factor.append(CoreShellCylinderModel)
+        
+        from sans.models.HollowCylinderModel import HollowCylinderModel
+        self.shape_list.append(HollowCylinderModel)
+        self.multiplication_factor.append(HollowCylinderModel)
+              
+        from sans.models.FlexibleCylinderModel import FlexibleCylinderModel
+        self.shape_list.append(FlexibleCylinderModel)
+
+        from sans.models.FlexCylEllipXModel import FlexCylEllipXModel
+        self.shape_list.append(FlexCylEllipXModel)
+        
+        from sans.models.StackedDisksModel import StackedDisksModel
+        self.shape_list.append(StackedDisksModel)
+        self.multiplication_factor.append(StackedDisksModel)
+        
+        from sans.models.ParallelepipedModel import ParallelepipedModel
+        self.shape_list.append(ParallelepipedModel)
+        self.multiplication_factor.append(ParallelepipedModel)
+        
+        from sans.models.CSParallelepipedModel import CSParallelepipedModel
+        self.shape_list.append(CSParallelepipedModel)
+        self.multiplication_factor.append(CSParallelepipedModel)
+        
+        from sans.models.EllipticalCylinderModel import EllipticalCylinderModel
+        self.shape_list.append(EllipticalCylinderModel)
+        self.multiplication_factor.append(EllipticalCylinderModel)
+        
+        from sans.models.BarBellModel import BarBellModel
+        self.shape_list.append(BarBellModel)
+        # not implemeted yet!
+        #self.multiplication_factor.append(BarBellModel)
+        
+        from sans.models.CappedCylinderModel import CappedCylinderModel
+        self.shape_list.append(CappedCylinderModel)
+        # not implemeted yet!
+        #self.multiplication_factor.append(CappedCylinderModel)
+        
+        from sans.models.EllipsoidModel import EllipsoidModel
+        self.shape_list.append(EllipsoidModel)
+        self.multiplication_factor.append(EllipsoidModel)
+      
+        from sans.models.CoreShellEllipsoidModel import CoreShellEllipsoidModel
+        self.shape_list.append(CoreShellEllipsoidModel)
+        self.multiplication_factor.append(CoreShellEllipsoidModel)
+         
+        from sans.models.TriaxialEllipsoidModel import TriaxialEllipsoidModel
+        self.shape_list.append(TriaxialEllipsoidModel)
+        self.multiplication_factor.append(TriaxialEllipsoidModel)
+        
+        from sans.models.LamellarModel import LamellarModel
+        self.shape_list.append(LamellarModel)
+        
+        from sans.models.LamellarFFHGModel import LamellarFFHGModel
+        self.shape_list.append(LamellarFFHGModel)
+        
+        from sans.models.LamellarPSModel import LamellarPSModel
+        self.shape_list.append(LamellarPSModel)
+     
+        from sans.models.LamellarPSHGModel import LamellarPSHGModel
+        self.shape_list.append(LamellarPSHGModel)
+        
+        from sans.models.LamellarPCrystalModel import LamellarPCrystalModel
+        self.shape_list.append(LamellarPCrystalModel)
+        
+        from sans.models.SCCrystalModel import SCCrystalModel
+        self.shape_list.append(SCCrystalModel)
+        
+        from sans.models.FCCrystalModel import FCCrystalModel
+        self.shape_list.append(FCCrystalModel)
+        
+        from sans.models.BCCrystalModel import BCCrystalModel
+        self.shape_list.append(BCCrystalModel)
+      
+        ## Structure factor 
+        from sans.models.SquareWellStructure import SquareWellStructure
+        self.struct_list.append(SquareWellStructure)
+        
+        from sans.models.HardsphereStructure import HardsphereStructure
+        self.struct_list.append(HardsphereStructure)
+         
+        from sans.models.StickyHSStructure import StickyHSStructure
+        self.struct_list.append(StickyHSStructure)
+        
+        from sans.models.HayterMSAStructure import HayterMSAStructure
+        self.struct_list.append(HayterMSAStructure)
+        
+        ##shape-independent models
+        from sans.models.PowerLawAbsModel import PowerLawAbsModel
+        self.shape_indep_list.append( PowerLawAbsModel )
+        
+        from sans.models.BEPolyelectrolyte import BEPolyelectrolyte
+        self.shape_indep_list.append(BEPolyelectrolyte )
+        self.form_factor_dict[str(wx.NewId())] =  [SphereModel]
+        
+        from sans.models.BroadPeakModel import BroadPeakModel
+        self.shape_indep_list.append(BroadPeakModel)
+        
+        from sans.models.CorrLengthModel import CorrLengthModel
+        self.shape_indep_list.append(CorrLengthModel)
+        
+        from sans.models.DABModel import DABModel
+        self.shape_indep_list.append(DABModel )
+        
+        from sans.models.DebyeModel import DebyeModel
+        self.shape_indep_list.append(DebyeModel )
+        
+        #FractalModel (a c-model)is now being used instead of FractalAbsModel.
+        from sans.models.FractalModel import FractalModel
+        self.shape_indep_list.append(FractalModel )
+        
+        from sans.models.FractalCoreShellModel import FractalCoreShellModel
+        self.shape_indep_list.append(FractalCoreShellModel )
+        
+        from sans.models.GaussLorentzGelModel import GaussLorentzGelModel
+        self.shape_indep_list.append(GaussLorentzGelModel) 
+                
+        from sans.models.GuinierModel import GuinierModel
+        self.shape_indep_list.append(GuinierModel )
+        
+        from sans.models.GuinierPorodModel import GuinierPorodModel
+        self.shape_indep_list.append(GuinierPorodModel )
+
+        from sans.models.LorentzModel import LorentzModel
+        self.shape_indep_list.append( LorentzModel) 
+        
+        from sans.models.PeakGaussModel import PeakGaussModel
+        self.shape_indep_list.append(PeakGaussModel)
+        
+        from sans.models.PeakLorentzModel import PeakLorentzModel
+        self.shape_indep_list.append(PeakLorentzModel)
+        
+        from sans.models.Poly_GaussCoil import Poly_GaussCoil
+        self.shape_indep_list.append(Poly_GaussCoil)
+        
+        from sans.models.PolymerExclVolume import PolymerExclVolume
+        self.shape_indep_list.append(PolymerExclVolume)
+        
+        from sans.models.PorodModel import PorodModel
+        self.shape_indep_list.append(PorodModel )      
+        
+        from sans.models.RPA10Model import RPA10Model
+        self.shape_indep_list.append(RPA10Model)
+        self.multi_func_list.append(RPA10Model)
+        
+        from sans.models.TeubnerStreyModel import TeubnerStreyModel
+        self.shape_indep_list.append(TeubnerStreyModel )
+        
+        from sans.models.TwoLorentzianModel import TwoLorentzianModel
+        self.shape_indep_list.append(TwoLorentzianModel )
+        
+        from sans.models.TwoPowerLawModel import TwoPowerLawModel
+        self.shape_indep_list.append(TwoPowerLawModel )
+        
+        from sans.models.UnifiedPowerRgModel import UnifiedPowerRgModel
+        self.shape_indep_list.append(UnifiedPowerRgModel )
+        self.multi_func_list.append(UnifiedPowerRgModel)
+        
+        from sans.models.LineModel import LineModel
+        self.shape_indep_list.append(LineModel)
+        
+        from sans.models.ReflectivityModel import ReflectivityModel
+        self.multi_func_list.append(ReflectivityModel)
+        
+        from sans.models.ReflectivityIIModel import ReflectivityIIModel
+        self.multi_func_list.append(ReflectivityIIModel)
+    
+        #Looking for plugins
+        self.plugins = findModels()
+        self._get_multifunc_models()
+        self.plugins.append(ReflectivityModel)
+        self.plugins.append(ReflectivityIIModel)
+        return 0
+
+    
+    def populate_menu(self, modelmenu, event_owner):
+        """
+        Populate a menu with our models
+        
+        :param id: first menu event ID to use when binding the menu events
+        :param modelmenu: wx.Menu object to populate
+        :param event_owner: wx object to bind the menu events to
+        
+        :return: the next free event ID following the new menu events
+        
+        """
+        ## Fill model lists
+        self._getModelList()
+        ## store reference to model menu of guiframe
+        self.modelmenu = modelmenu
+        ## guiframe reference
+        self.event_owner = event_owner
+        
+        shape_submenu = wx.Menu()
+        shape_indep_submenu = wx.Menu()
+        structure_factor = wx.Menu()
+        added_models = wx.Menu()
+        multip_models = wx.Menu()
+        ## create menu with shape
+        self._fill_simple_menu(menuinfo=["Shapes",shape_submenu," simple shape"],
+                         list1=self.shape_list)
+        
+        self._fill_simple_menu(menuinfo=["Shape-Independent",shape_indep_submenu,
+                                    "List of shape-independent models"],
+                         list1=self.shape_indep_list )
+        
+        self._fill_simple_menu(menuinfo=["Structure Factors",structure_factor,
+                                          "List of Structure factors models" ],
+                                list1=self.struct_list)
+        
+        self._fill_plugin_menu(menuinfo=["Customized Models", added_models,
+                                            "List of additional models"],
+                                 list1=self.plugins)
+        
+        self._fill_menu(menuinfo=["P(Q)*S(Q)",multip_models,
+                                  "mulplication of 2 models"],
+                                   list1=self.multiplication_factor ,
+                                   list2= self.struct_list)
+        return 0
+    
+    def _fill_plugin_menu(self, menuinfo, list1):
+        """
+        fill the plugin menu with costumized models
+        """
+        if len(list1)==0:
+            id = wx.NewId() 
+            msg= "No model available check plugins.log for errors to fix problem"
+            menuinfo[1].Append(int(id),"Empty",msg)
+        self._fill_simple_menu( menuinfo,list1)
+        
+    def _fill_simple_menu(self, menuinfo, list1):
+        """
+        Fill the menu with list item
+        
+        :param modelmenu: the menu to fill
+        :param menuinfo: submenu item for the first column of this modelmenu
+                         with info.Should be a list :
+                         [name(string) , menu(wx.menu), help(string)]
+        :param list1: contains item (form factor )to fill modelmenu second column
+        
+        """
+        if len(list1)>0:
+            self.model_combobox.set_list(menuinfo[0],list1)
+            
+            for item in list1:
+                try:
+                    id = wx.NewId() 
+                    struct_factor=item()
+                    struct_name = struct_factor.__class__.__name__
+                    if hasattr(struct_factor, "name"):
+                        struct_name = struct_factor.name
+                        
+                    menuinfo[1].Append(int(id),struct_name,struct_name)
+                    if not  item in self.struct_factor_dict.itervalues():
+                        self.struct_factor_dict[str(id)]= item
+                    wx.EVT_MENU(self.event_owner, int(id), self._on_model)
+                except:
+                    msg= "Error Occured: %s"%sys.exc_value
+                    wx.PostEvent(self.event_owner, StatusEvent(status=msg))
+                
+        id = wx.NewId()         
+        self.modelmenu.AppendMenu(id, menuinfo[0],menuinfo[1],menuinfo[2])
+        
+    def _fill_menu(self, menuinfo, list1, list2):
+        """
+        Fill the menu with list item
+        
+        :param menuinfo: submenu item for the first column of this modelmenu
+                         with info.Should be a list :
+                         [name(string) , menu(wx.menu), help(string)]
+        :param list1: contains item (form factor )to fill modelmenu second column
+        :param list2: contains item (Structure factor )to fill modelmenu 
+                third column
+                
+        """
+        if len(list1)>0:
+            self.model_combobox.set_list(menuinfo[0],list1)
+            
+            for item in list1:   
+                form_factor= item()
+                form_name = form_factor.__class__.__name__
+                if hasattr(form_factor, "name"):
+                    form_name = form_factor.name
+                ### store form factor to return to other users   
+                newmenu= wx.Menu()
+                if len(list2)>0:
+                    for model  in list2:
+                        id = wx.NewId()
+                        struct_factor = model()
+                        name = struct_factor.__class__.__name__
+                        if hasattr(struct_factor, "name"):
+                            name = struct_factor.name
+                        newmenu.Append(id,name, name)
+                        wx.EVT_MENU(self.event_owner, int(id), self._on_model)
+                        ## save form_fact and struct_fact
+                        self.form_factor_dict[int(id)] = [form_factor,struct_factor]
+                        
+                form_id= wx.NewId()    
+                menuinfo[1].AppendMenu(int(form_id), form_name,newmenu,menuinfo[2])
+        id=wx.NewId()
+        self.modelmenu.AppendMenu(id,menuinfo[0],menuinfo[1], menuinfo[2])
+        
+    def _on_model(self, evt):
+        """
+        React to a model menu event
+        
+        :param event: wx menu event
+        
+        """
+        if int(evt.GetId()) in self.form_factor_dict.keys():
+            from sans.models.MultiplicationModel import MultiplicationModel
+            model1, model2 = self.form_factor_dict[int(evt.GetId())]
+            model = MultiplicationModel(model1, model2)    
+        else:
+            model= self.struct_factor_dict[str(evt.GetId())]()
+        evt = ModelEvent(model=model)
+        wx.PostEvent(self.event_owner, evt)
+        
+    def _get_multifunc_models(self):
+        """
+        Get the multifunctional models
+        """
+        for item in self.plugins:
+            try:
+                # check the multiplicity if any
+                if item.multiplicity_info[0] > 1:
+                    self.multi_func_list.append(item)
+            except:
+                # pass to other items
+                pass
+                    
+    def get_model_list(self):    
+        """
+        return dictionary of models for fitpanel use 
+        
+        """
+        self.model_combobox.set_list("multiplication", self.multiplication_factor)
+        self.model_combobox.set_list("Multi-Functions", self.multi_func_list)
+        return self.model_combobox
+    
+  
+        
+    
+    
+  
Index: sansview/sansview.py
===================================================================
--- sansview/sansview.py	(revision 75fbd1798f95ec58502a817f7435b1dfae03b69f)
+++ sansview/sansview.py	(revision a07e72f8fb8281178797bf691b36e56274d8580b)
@@ -74,6 +74,7 @@
             self.gui.add_perspective(invariant_plug)
         except:
-            logging.error("SansView: could not find Invariant plug-in module") 
-            logging.error(sys.exc_value)  
+            raise
+            #logging.error("SansView: could not find Invariant plug-in module") 
+            #logging.error(sys.exc_value)  
         
         #Calculator perspective   
@@ -85,5 +86,5 @@
             logging.error("SansView: could not find Calculator plug-in module")
             logging.error(sys.exc_value)  
-            
+        """    
         # theory perspective
         try:
@@ -93,5 +94,6 @@
         except:
             logging.error("SansView: could not find theory plug-in module")
-            logging.error(sys.exc_value)  
+            logging.error(sys.exc_value) 
+        """ 
             
         # Fitting perspective
