Index: prview/src/sans/perspectives/pr/inversion_panel.py
===================================================================
--- prview/src/sans/perspectives/pr/inversion_panel.py	(revision 24e093a8dce51131b5845f3696386c1ddb08daf7)
+++ prview/src/sans/perspectives/pr/inversion_panel.py	(revision 24e093a8dce51131b5845f3696386c1ddb08daf7)
@@ -0,0 +1,1089 @@
+#!/usr/bin/env python
+
+# version
+__id__ = "$Id: aboutdialog.py 1193 2007-05-03 17:29:59Z dmitriy $"
+__revision__ = "$Revision: 1193 $"
+
+import wx
+import os
+import sys
+import logging
+from wx.lib.scrolledpanel import ScrolledPanel
+from sans.guiframe.events import StatusEvent   
+from sans.guiframe.panel_base import PanelBase 
+from inversion_state import InversionState
+from pr_widgets import PrTextCtrl
+from pr_widgets import DataFileTextCtrl
+from pr_widgets import OutputTextCtrl
+
+if sys.platform.count("darwin")==0:
+    FONT_VARIANT = 0
+else:
+    FONT_VARIANT = 1
+
+class InversionControl(ScrolledPanel, PanelBase):
+    """
+    """
+    window_name = 'pr_control'
+    window_caption = "P(r) control panel"
+    CENTER_PANE = True
+    
+    # Figure of merit parameters [default]
+    
+    ## Oscillation parameters (sin function = 1.1)
+    oscillation_max = 1.5
+    
+    def __init__(self, parent, id=-1, plots=None, 
+                 standalone=False, **kwargs):
+        """
+        """
+        ScrolledPanel.__init__(self, parent, id=id, **kwargs)
+        PanelBase.__init__(self, parent)
+        self.SetupScrolling()
+        #Set window's font size 
+        self.SetWindowVariant(variant=FONT_VARIANT)
+        
+        self.plots = plots
+        self.radio_buttons = {}
+        self.parent = parent
+        
+        ## Data file TextCtrl
+        self.data_file  = None
+        self.plot_data  = None
+        self.nfunc_ctl  = None
+        self.alpha_ctl  = None
+        self.dmax_ctl   = None
+        self.time_ctl   = None
+        self.chi2_ctl   = None
+        self.osc_ctl    = None
+        self.file_radio = None
+        self.plot_radio = None
+        self.label_sugg = None
+        self.qmin_ctl   = None
+        self.qmax_ctl   = None
+        self.swidth_ctl = None
+        self.sheight_ctl = None
+        
+        self.rg_ctl     = None
+        self.iq0_ctl    = None
+        self.bck_chk    = None
+        self.bck_ctl    = None
+        
+        # TextCtrl for fraction of positive P(r)
+        self.pos_ctl = None
+        
+        # TextCtrl for fraction of 1 sigma positive P(r)
+        self.pos_err_ctl = None 
+        
+        ## Estimates
+        self.alpha_estimate_ctl = None
+        self.nterms_estimate_ctl = None
+        ## D_max distance explorator
+        self.distance_explorator_ctl = None
+        ## Data manager
+        self._manager   = None
+        ## Standalone flage
+        self.standalone = standalone
+        ## Default file location for save
+        self._default_save_location = os.getcwd()
+        # Default width
+        self._default_width = 350
+        self._do_layout()
+        
+    def __setattr__(self, name, value):
+        """
+        Allow direct hooks to text boxes
+        """
+        if name == 'nfunc':
+            self.nfunc_ctl.SetValue(str(int(value)))
+        elif name == 'd_max':
+            self.dmax_ctl.SetValue(str(value))
+        elif name == 'alpha':
+            self.alpha_ctl.SetValue(str(value))
+        elif name == 'chi2':
+            self.chi2_ctl.SetValue("%-5.2g" % value)
+        elif name == 'bck':
+            self.bck_ctl.SetValue("%-5.2g" % value)
+        elif name == 'q_min':
+            self.qmin_ctl.SetValue("%-5.2g" % value)
+        elif name == 'q_max':
+            self.qmax_ctl.SetValue("%-5.2g" % value)
+        elif name == 'elapsed':
+            self.time_ctl.SetValue("%-5.2g" % value)
+        elif name =='rg':
+            self.rg_ctl.SetValue("%-5.2g" % value)
+        elif name == 'iq0':
+            self.iq0_ctl.SetValue("%-5.2g" % value)
+        elif name == 'oscillation':
+            self.osc_ctl.SetValue("%-5.2g" % value)
+        elif name == 'slit_width':
+            self.swidth_ctl.SetValue("%-5.2g" % value)
+        elif name == 'slit_height':
+            self.sheight_ctl.SetValue("%-5.2g" % value)
+        elif name == 'positive':
+            self.pos_ctl.SetValue("%-5.2g" % value)
+        elif name == 'pos_err':
+            self.pos_err_ctl.SetValue("%-5.2g" % value)
+        elif name == 'alpha_estimate':
+            self.alpha_estimate_ctl.SetToolTipString("Click to accept value.")
+            self.alpha_estimate_ctl.Enable(True)
+            self.alpha_estimate_ctl.SetLabel("%-3.1g" % value)
+            #self.alpha_estimate_ctl.Show()
+            #self.label_sugg.Show()
+        elif name == 'nterms_estimate':
+            self.nterms_estimate_ctl.SetToolTipString("Click to accept value.")
+            self.nterms_estimate_ctl.Enable(True)
+            self.nterms_estimate_ctl.SetLabel("%-g" % value)
+        elif name == 'plotname':
+            self.plot_data.SetValue(str(value))
+            self._on_pars_changed(None)
+        elif name == 'datafile':
+            self.plot_data.SetValue(str(value))
+            self._on_pars_changed(None)
+        else:
+            wx.Panel.__setattr__(self, name, value)
+        
+    def __getattr__(self, name):
+        """
+        Allow direct hooks to text boxes
+        """
+        if name == 'nfunc':
+            try:
+                return int(self.nfunc_ctl.GetValue())
+            except:
+                return -1
+        elif name == 'd_max':
+            try:
+                return self.dmax_ctl.GetValue()
+            except:
+                return -1.0
+        elif name == 'alpha':
+            try:
+                return self.alpha_ctl.GetValue()
+            except:
+                return -1.0
+        elif name == 'chi2':
+            try:
+                return float(self.chi2_ctl.GetValue())
+            except:
+                return None
+        elif name == 'bck':
+            try:
+                return float(self.bck_ctl.GetValue())
+            except:
+                return None
+        elif name == 'q_min':
+            try:
+                return float(self.qmin_ctl.GetValue())
+            except:
+                return 0.0
+        elif name=='q_max':
+            try:
+                return float(self.qmax_ctl.GetValue())
+            except:
+                return 0.0
+        elif name == 'elapsed':
+            try:
+                return float(self.time_ctl.GetValue())
+            except:
+                return None
+        elif name == 'rg':
+            try:
+                return float(self.rg_ctl.GetValue())
+            except:
+                return None
+        elif name=='iq0':
+            try:
+                return float(self.iq0_ctl.GetValue())
+            except:
+                return None
+        elif name == 'oscillation':
+            try:
+                return float(self.osc_ctl.GetValue())
+            except:
+                return None
+        elif name == 'slit_width':
+            try:
+                return float(self.swidth_ctl.GetValue())
+            except:
+                return None
+        elif name=='slit_height':
+            try:
+                return float(self.sheight_ctl.GetValue())
+            except:
+                return None
+        elif name == 'pos':
+            try:
+                return float(self.pos_ctl.GetValue())
+            except:
+                return None
+        elif name == 'pos_err':
+            try:
+                return float(self.pos_err_ctl.GetValue())
+            except:
+                return None
+        elif name == 'alpha_estimate':
+            try:
+                return float(self.alpha_estimate_ctl.GetLabel())
+            except:
+                return None
+        elif name == 'nterms_estimate':
+            try:
+                return int(self.nterms_estimate_ctl.GetLabel())
+            except:
+                return None
+        elif name == 'plotname':
+            return self.plot_data.GetValue()
+        elif name == 'datafile':
+            return self.plot_data.GetValue()
+        else:
+            return wx.Panel.__getattribute__(self, name)
+        
+    def save_project(self, doc=None):
+        """
+        return an xml node containing state of the panel
+         that guiframe can write to file
+        """
+        data = self.get_data()
+        state = self.get_state()
+        if data is not None:
+            new_doc = self._manager.state_reader.write_toXML(data, state)
+            if new_doc is not None:
+                if doc is not None and hasattr(doc, "firstChild"):
+                    child = new_doc.firstChild.firstChild
+                    doc.firstChild.appendChild(child)  
+                else:
+                    doc = new_doc
+        return doc   
+    
+    def on_save(self, evt=None):
+        """
+        Method used to create a memento of the current state
+            
+        :return: state object 
+        """
+        # Ask the user the location of the file to write to.
+        path = None
+        dlg = wx.FileDialog(self, "Choose a file",
+                            self._default_save_location, "", "*.prv", wx.SAVE)
+        if dlg.ShowModal() == wx.ID_OK:
+            path = dlg.GetPath()
+            self._default_save_location = os.path.dirname(path)
+        else:
+            return None
+        
+        dlg.Destroy()
+        
+        state = self.get_state()
+        
+        # MAC always needs the extension for saving
+        extens = ".prv"
+        # Make sure the ext included in the file name
+        fName = os.path.splitext(path)[0] + extens
+        self._manager.save_data(filepath=fName, prstate=state)
+        
+        return state
+    
+    def get_data(self):
+        """
+        """
+        return self._manager.get_data()
+    
+    def get_state(self):
+        """
+        Get the current state
+        
+        : return: state object
+        """
+        # Construct the state object    
+        state = InversionState()
+        
+        # Read the panel's parameters
+        flag, alpha, dmax, nfunc, qmin, \
+        qmax, height, width = self._read_pars()
+        
+        state.nfunc = nfunc
+        state.d_max = dmax
+        state.alpha = alpha
+        state.qmin  = qmin
+        state.qmax  = qmax
+        state.width = width
+        state.height = height
+        
+        # Data file
+        state.file = self.plot_data.GetValue()
+        
+        # Background evaluation checkbox
+        state.estimate_bck = self.bck_chk.IsChecked()
+        
+        # Estimates
+        state.nterms_estimate = self.nterms_estimate
+        state.alpha_estimate = self.alpha_estimate
+        
+        # Read the output values
+        state.chi2    = self.chi2
+        state.elapsed = self.elapsed
+        state.osc     = self.oscillation
+        state.pos     = self.pos
+        state.pos_err = self.pos_err
+        state.rg      = self.rg
+        state.iq0     = self.iq0
+        state.bck     = self.bck
+        
+        return state
+    
+    def set_state(self, state):
+        """
+        Set the state of the panel and inversion problem to
+        the state passed as a parameter.
+        Execute the inversion immediately after filling the 
+        controls.
+        
+        :param state: InversionState object
+        """
+        if state.nfunc is not None:
+            self.nfunc = state.nfunc
+        if state.d_max is not None:
+            self.d_max = state.d_max
+        if state.alpha is not None:
+            self.alpha = state.alpha
+        if state.qmin is not None:
+            self.q_min  = state.qmin
+        if state.qmax is not None:
+            self.q_max  = state.qmax
+        if state.width is not None:
+            self.slit_width = state.width
+        if state.height is not None:
+            self.slit_height = state.height
+        
+        # Data file
+        self.plot_data.SetValue(str(state.file))
+    
+        # Background evaluation checkbox
+        self.bck_chk.SetValue(state.estimate_bck)
+        
+        # Estimates
+        if state.nterms_estimate is not None:
+            self.nterms_estimate = state.nterms_estimate
+        if state.alpha_estimate is not None: 
+            self.alpha_estimate = state.alpha_estimate 
+    
+        
+        # Read the output values
+        if state.chi2 is not None:
+            self.chi2    = state.chi2
+        if state.elapsed is not None:
+            self.elapsed = state.elapsed
+        if state.osc is not None:
+            self.oscillation = state.osc
+        if state.pos is not None:
+            self.positive = state.pos
+        if state.pos_err is not None:
+            self.pos_err = state.pos_err
+        if state.rg is not None:
+            self.rg      = state.rg
+        if state.iq0 is not None:
+            self.iq0     = state.iq0
+        if state.bck is not None:
+            self.bck     = state.bck
+
+        # Perform inversion
+        self._on_invert(None)    
+        
+    def set_manager(self, manager):
+        self._manager = manager
+        
+    def _do_layout(self):
+        vbox = wx.GridBagSizer(0,0)
+        iy_vb = 0
+
+        # ----- I(q) data -----
+        databox = wx.StaticBox(self, -1, "I(q) data source")
+        
+        boxsizer1 = wx.StaticBoxSizer(databox, wx.VERTICAL)
+        boxsizer1.SetMinSize((self._default_width, 50))
+        pars_sizer = wx.GridBagSizer(5, 5)
+
+        iy = 0
+        self.file_radio = wx.StaticText(self, -1, "Name:")
+        pars_sizer.Add(self.file_radio, (iy, 0), (1, 1),
+                       wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
+        
+        self.plot_data = DataFileTextCtrl(self, -1, size=(260,20))
+        
+        pars_sizer.Add(self.plot_data, (iy, 1), (1, 1),
+                       wx.EXPAND|wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 15)
+        
+        self.bck_chk = wx.CheckBox(self, -1, "Estimate background level")
+        hint_msg = "Check box to let the fit estimate "
+        hint_msg += "the constant background level."
+        self.bck_chk.SetToolTipString(hint_msg)
+        self.bck_chk.Bind(wx.EVT_CHECKBOX, self._on_pars_changed)
+        iy += 1
+        pars_sizer.Add(self.bck_chk, (iy, 0), (1, 2),
+                       wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
+        boxsizer1.Add(pars_sizer, 0, wx.EXPAND)  
+        vbox.Add(boxsizer1, (iy_vb, 0), (1, 1),
+                 wx.LEFT|wx.RIGHT|wx.EXPAND|wx.ADJUST_MINSIZE|wx.TOP, 5)
+        
+        # ----- Add slit parameters -----
+        if True:
+            sbox = wx.StaticBox(self, -1, "Slit parameters")
+            sboxsizer = wx.StaticBoxSizer(sbox, wx.VERTICAL)
+            sboxsizer.SetMinSize((self._default_width, 20))
+            
+            sizer_slit = wx.GridBagSizer(5, 5)
+    
+            label_sheight = wx.StaticText(self, -1, "Height", size=(40, 20))
+            label_swidth = wx.StaticText(self, -1, "Width", size=(40, 20))
+            #label_sunits1 = wx.StaticText(self, -1, "[A^(-1)]")
+            label_sunits2 = wx.StaticText(self, -1, "[A^(-1)]", size=(55,20))
+            self.sheight_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER,
+                                          size=(60,20))
+            self.swidth_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER,
+                                         size=(60,20))
+            hint_msg = "Enter slit height in units of Q or leave blank."
+            self.sheight_ctl.SetToolTipString(hint_msg)
+            hint_msg = "Enter slit width in units of Q or leave blank."
+            self.swidth_ctl.SetToolTipString(hint_msg)
+            #self.sheight_ctl.Bind(wx.EVT_TEXT, self._on_pars_changed)
+            #self.swidth_ctl.Bind(wx.EVT_TEXT,  self._on_pars_changed)
+            
+            iy = 0
+            sizer_slit.Add(label_sheight,    (iy, 0), (1, 1),
+                           wx.LEFT|wx.EXPAND, 5)
+            sizer_slit.Add(self.sheight_ctl, (iy, 1), (1, 1),
+                           wx.LEFT|wx.EXPAND, 5)
+    
+            sizer_slit.Add(label_swidth,     (iy, 2), (1, 1),
+                           wx.LEFT|wx.EXPAND, 5)
+            sizer_slit.Add(self.swidth_ctl,  (iy, 3), (1, 1),
+                           wx.LEFT|wx.EXPAND, 5)
+            sizer_slit.Add(label_sunits2,    (iy, 4), (1, 1),
+                           wx.LEFT|wx.EXPAND, 5)
+            
+            sboxsizer.Add(sizer_slit, wx.TOP, 15)
+            iy_vb += 1
+            vbox.Add(sboxsizer, (iy_vb, 0), (1, 1),
+                     wx.LEFT|wx.RIGHT|wx.EXPAND|wx.ADJUST_MINSIZE, 5)
+        
+        
+        # ----- Q range -----
+        qbox = wx.StaticBox(self, -1, "Q range")
+        qboxsizer = wx.StaticBoxSizer(qbox, wx.VERTICAL)
+        qboxsizer.SetMinSize((self._default_width, 20))
+        
+        sizer_q = wx.GridBagSizer(5, 5)
+
+        label_qmin = wx.StaticText(self, -1, "Q min", size=(40, 20))
+        label_qmax = wx.StaticText(self, -1, "Q max", size=(40, 20))
+        #label_qunits1 = wx.StaticText(self, -1, "[A^(-1)]")
+        label_qunits2 = wx.StaticText(self, -1, "[A^(-1)]", size=(55, 20))
+        self.qmin_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER,
+                                   size=(60,20))
+        self.qmax_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER,
+                                   size=(60,20))
+        hint_msg = "Select a lower bound for Q or leave blank."
+        self.qmin_ctl.SetToolTipString(hint_msg)
+        hint_msg = "Select an upper bound for Q or leave blank."
+        self.qmax_ctl.SetToolTipString(hint_msg)
+        self.qmin_ctl.Bind(wx.EVT_TEXT, self._on_pars_changed)
+        self.qmax_ctl.Bind(wx.EVT_TEXT, self._on_pars_changed)
+        
+        iy = 0
+        sizer_q.Add(label_qmin,    (iy, 0), (1, 1), wx.LEFT|wx.EXPAND, 5)
+        sizer_q.Add(self.qmin_ctl, (iy, 1), (1, 1), wx.LEFT|wx.EXPAND, 5)
+        #sizer_q.Add(label_qunits1, (iy,2), (1,1), wx.LEFT|wx.EXPAND, 15)
+        sizer_q.Add(label_qmax,    (iy, 2), (1, 1), wx.LEFT|wx.EXPAND, 5)
+        sizer_q.Add(self.qmax_ctl, (iy, 3), (1, 1), wx.LEFT|wx.EXPAND, 5)
+        sizer_q.Add(label_qunits2, (iy, 4), (1, 1), wx.LEFT|wx.EXPAND, 5)
+        qboxsizer.Add(sizer_q, wx.TOP, 15)
+
+        iy_vb += 1
+        vbox.Add(qboxsizer, (iy_vb,0), (1,1),
+                 wx.LEFT|wx.RIGHT|wx.EXPAND|wx.ADJUST_MINSIZE, 5)
+    
+        # ----- Parameters -----
+        parsbox = wx.StaticBox(self, -1, "Parameters")
+        boxsizer2 = wx.StaticBoxSizer(parsbox, wx.VERTICAL)
+        boxsizer2.SetMinSize((self._default_width,50))
+        
+        explanation  = "P(r) is found by fitting a set of base functions"
+        explanation += " to I(Q). The minimization involves"
+        explanation += " a regularization term to ensure a smooth P(r)."
+        explanation += " The regularization constant gives the size of that "  
+        explanation += "term. The suggested value is the value above which the"
+        explanation += " output P(r) will have only one peak."
+        label_explain = wx.StaticText(self, -1, explanation, size=(280,90))
+        boxsizer2.Add(label_explain,  wx.LEFT|wx.BOTTOM, 5)
+        
+        
+        
+        label_nfunc = wx.StaticText(self, -1, "Number of terms")
+        label_nfunc.SetMinSize((120, 20))
+        label_alpha = wx.StaticText(self, -1, "Regularization constant")
+        label_dmax  = wx.StaticText(self, -1, "Max distance [A]")
+        self.label_sugg  = wx.StaticText(self, -1, "Suggested value")
+        #self.label_sugg.Hide()
+        
+        self.nfunc_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER,
+                                    size=(60,20))
+        self.nfunc_ctl.SetToolTipString("Number of terms in the expansion.")
+        self.alpha_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER,
+                                    size=(60,20))
+        hint_msg = "Control parameter for the size of the regularization term."
+        self.alpha_ctl.SetToolTipString(hint_msg)
+        self.dmax_ctl  = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER,
+                                    size=(60,20))
+        hint_msg = "Maximum distance between any two points in the system."
+        self.dmax_ctl.SetToolTipString(hint_msg)
+        id = wx.NewId()
+        self.alpha_estimate_ctl  = wx.Button(self, id, "")
+        #self.alpha_estimate_ctl.Hide()
+        self.Bind(wx.EVT_BUTTON, self._on_accept_alpha, id = id)   
+        self.alpha_estimate_ctl.Enable(False)
+        #self.alpha_estimate_ctl.SetBackgroundColour('#ffdf85')
+        #self.alpha_estimate_ctl.SetBackgroundColour(self.GetBackgroundColour())
+        self.alpha_estimate_ctl.SetToolTipString("Waiting for estimate...")
+        
+        id = wx.NewId()
+        self.nterms_estimate_ctl  = wx.Button(self, id, "")
+        #self.nterms_estimate_ctl.Hide()
+        self.Bind(wx.EVT_BUTTON, self._on_accept_nterms, id = id)   
+        self.nterms_estimate_ctl.Enable(False)
+      
+        self.nterms_estimate_ctl.SetToolTipString("Waiting for estimate...")
+        
+        self.nfunc_ctl.Bind(wx.EVT_TEXT, self._read_pars)
+        self.alpha_ctl.Bind(wx.EVT_TEXT, self._read_pars)
+        self.dmax_ctl.Bind(wx.EVT_TEXT, self._on_pars_changed)
+        
+        # Distance explorator
+        id = wx.NewId()
+        self.distance_explorator_ctl = wx.Button(self, id, "Explore")
+        self.Bind(wx.EVT_BUTTON, self._on_explore, id = id)           
+        
+        
+        sizer_params = wx.GridBagSizer(5,5)
+
+        iy = 0
+        sizer_params.Add(self.label_sugg, (iy, 2), (1, 1), wx.LEFT, 15)
+        iy += 1
+        sizer_params.Add(label_nfunc,      (iy, 0), (1, 1), wx.LEFT, 15)
+        sizer_params.Add(self.nfunc_ctl,   (iy, 1), (1, 1), wx.RIGHT, 0)
+        sizer_params.Add(self.nterms_estimate_ctl, (iy, 2), (1, 1), wx.LEFT, 15)
+        iy += 1
+        sizer_params.Add(label_alpha,      (iy, 0), (1, 1), wx.LEFT, 15)
+        sizer_params.Add(self.alpha_ctl,   (iy, 1), (1, 1), wx.RIGHT, 0)
+        sizer_params.Add(self.alpha_estimate_ctl, (iy, 2), (1, 1), wx.LEFT, 15)
+        iy += 1
+        sizer_params.Add(label_dmax, (iy, 0), (1, 1), wx.LEFT, 15)
+        sizer_params.Add(self.dmax_ctl,   (iy, 1), (1, 1), wx.RIGHT, 0)
+        sizer_params.Add(self.distance_explorator_ctl, (iy, 2),
+                         (1, 1), wx.LEFT, 15)
+
+        boxsizer2.Add(sizer_params, 0)
+        
+        iy_vb += 1
+        vbox.Add(boxsizer2, (iy_vb, 0), (1, 1),
+                 wx.LEFT|wx.RIGHT|wx.EXPAND|wx.ADJUST_MINSIZE, 5)
+
+
+        # ----- Results -----
+        resbox = wx.StaticBox(self, -1, "Outputs")
+        ressizer = wx.StaticBoxSizer(resbox, wx.VERTICAL)
+        ressizer.SetMinSize((self._default_width, 50))
+        
+        label_rg       = wx.StaticText(self, -1, "Rg")
+        label_rg_unit  = wx.StaticText(self, -1, "[A]")
+        label_iq0      = wx.StaticText(self, -1, "I(Q=0)")
+        label_iq0_unit = wx.StaticText(self, -1, "[A^(-1)]")
+        label_bck      = wx.StaticText(self, -1, "Background")
+        label_bck_unit = wx.StaticText(self, -1, "[A^(-1)]")
+        self.rg_ctl    = OutputTextCtrl(self, -1, size=(60,20))
+        hint_msg = "Radius of gyration for the computed P(r)."
+        self.rg_ctl.SetToolTipString(hint_msg)
+        self.iq0_ctl   = OutputTextCtrl(self, -1, size=(60, 20))
+        hint_msg = "Scattering intensity at Q=0 for the computed P(r)."
+        self.iq0_ctl.SetToolTipString(hint_msg)
+        self.bck_ctl   = OutputTextCtrl(self, -1, size=(60, 20))
+        self.bck_ctl.SetToolTipString("Value of estimated constant background.")
+        
+        label_time = wx.StaticText(self, -1, "Computation time")
+        label_time_unit = wx.StaticText(self, -1, "secs")
+        label_time.SetMinSize((120,20))
+        label_chi2 = wx.StaticText(self, -1, "Chi2/dof")
+        label_osc = wx.StaticText(self, -1, "Oscillations")
+        label_pos = wx.StaticText(self, -1, "Positive fraction")
+        label_pos_err = wx.StaticText(self, -1, "1-sigma positive fraction")
+        
+        self.time_ctl = OutputTextCtrl(self, -1, size=(60, 20))
+        hint_msg = "Computation time for the last inversion, in seconds."
+        self.time_ctl.SetToolTipString(hint_msg)
+        
+        self.chi2_ctl = OutputTextCtrl(self, -1, size=(60, 20))
+        self.chi2_ctl.SetToolTipString("Chi^2 over degrees of freedom.")
+        
+        # Oscillation parameter
+        self.osc_ctl = OutputTextCtrl(self, -1, size=(60, 20))
+        hint_msg = "Oscillation parameter. P(r) for a sphere has an "
+        hint_msg += " oscillation parameter of 1.1."
+        self.osc_ctl.SetToolTipString(hint_msg)
+        
+        # Positive fraction figure of merit
+        self.pos_ctl = OutputTextCtrl(self, -1, size=(60, 20))
+        hint_msg = "Fraction of P(r) that is positive. "
+        hint_msg += "Theoretically, P(r) is defined positive."
+        self.pos_ctl.SetToolTipString(hint_msg)
+        
+        # 1-simga positive fraction figure of merit
+        self.pos_err_ctl = OutputTextCtrl(self, -1, size=(60, 20))
+        message  = "Fraction of P(r) that is at least 1 standard deviation"
+        message += " greater than zero.\n"
+        message += "This figure of merit tells you about the size of the "
+        message += "P(r) errors.\n"
+        message += "If it is close to 1 and the other figures of merit are bad,"
+        message += " consider changing the maximum distance."
+        self.pos_err_ctl.SetToolTipString(message)
+        
+        sizer_res = wx.GridBagSizer(5, 5)
+
+        iy = 0
+        sizer_res.Add(label_rg, (iy, 0), (1, 1), wx.LEFT|wx.EXPAND, 15)
+        sizer_res.Add(self.rg_ctl,   (iy, 1), (1, 1), wx.RIGHT|wx.EXPAND, 15)
+        sizer_res.Add(label_rg_unit,   (iy, 2), (1, 1), wx.RIGHT|wx.EXPAND, 15)
+        iy += 1
+        sizer_res.Add(label_iq0, (iy, 0), (1, 1), wx.LEFT|wx.EXPAND, 15)
+        sizer_res.Add(self.iq0_ctl,   (iy, 1), (1, 1), wx.RIGHT|wx.EXPAND, 15)
+        sizer_res.Add(label_iq0_unit, (iy, 2), (1, 1), wx.RIGHT|wx.EXPAND, 15)
+        iy += 1
+        sizer_res.Add(label_bck, (iy, 0), (1, 1), wx.LEFT|wx.EXPAND, 15)
+        sizer_res.Add(self.bck_ctl, (iy, 1), (1, 1), wx.RIGHT|wx.EXPAND, 15)
+        sizer_res.Add(label_bck_unit, (iy, 2), (1, 1), wx.RIGHT|wx.EXPAND, 15)
+        iy += 1
+        sizer_res.Add(label_time, (iy, 0), (1, 1), wx.LEFT|wx.EXPAND, 15)
+        sizer_res.Add(self.time_ctl,   (iy, 1), (1, 1), wx.RIGHT|wx.EXPAND, 15)
+        sizer_res.Add(label_time_unit, (iy, 2), (1, 1), wx.RIGHT|wx.EXPAND, 15)
+        iy += 1
+        sizer_res.Add(label_chi2, (iy, 0), (1, 1), wx.LEFT|wx.EXPAND, 15)
+        sizer_res.Add(self.chi2_ctl,   (iy, 1), (1, 1), wx.RIGHT|wx.EXPAND, 15)
+        iy += 1
+        sizer_res.Add(label_osc, (iy, 0), (1, 1), wx.LEFT|wx.EXPAND, 15)
+        sizer_res.Add(self.osc_ctl,   (iy, 1), (1, 1), wx.RIGHT|wx.EXPAND, 15)
+
+        iy += 1
+        sizer_res.Add(label_pos, (iy, 0), (1, 1), wx.LEFT|wx.EXPAND, 15)
+        sizer_res.Add(self.pos_ctl, (iy, 1), (1, 1), wx.RIGHT|wx.EXPAND, 15)
+
+        iy += 1
+        sizer_res.Add(label_pos_err, (iy, 0), (1, 1), wx.LEFT|wx.EXPAND, 15)
+        sizer_res.Add(self.pos_err_ctl,  (iy,1), (1, 1), wx.RIGHT|wx.EXPAND, 15)
+
+        ressizer.Add(sizer_res, 0)
+        iy_vb += 1
+        vbox.Add(ressizer, (iy_vb, 0), (1, 1),
+                 wx.LEFT|wx.RIGHT|wx.EXPAND|wx.ADJUST_MINSIZE, 5)
+
+        # ----- Buttons -----
+        id = wx.NewId()
+        button_OK = wx.Button(self, id, "Compute")
+        button_OK.SetToolTipString("Perform P(r) inversion.")
+        self.Bind(wx.EVT_BUTTON, self._on_invert, id = id)   
+        
+        self._set_reset_flag(True)
+        self._set_save_flag(True)
+        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
+        sizer_button.Add((20, 20), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+        sizer_button.Add(button_OK, 0, wx.LEFT|wx.ADJUST_MINSIZE, 10)
+       
+        iy_vb += 1
+        vbox.Add(sizer_button, (iy_vb, 0), (1, 1),
+                 wx.EXPAND|wx.BOTTOM|wx.TOP|wx.RIGHT, 10)
+
+        self.Bind(wx.EVT_TEXT_ENTER, self._on_invert)
+
+        self.SetSizer(vbox)
+        
+    def _on_accept_alpha(self, evt):
+        """
+        User has accepted the estimated alpha, 
+        set it as part of the input parameters
+        """
+        try:
+            alpha = self.alpha_estimate_ctl.GetLabel()
+            tmp = float(alpha)
+            self.alpha_ctl.SetValue(alpha)
+        except:
+            # No estimate or bad estimate, either do nothing
+            #import sys
+            print "InversionControl._on_accept_alpha: %s" % sys.exc_value
+            pass
+    
+    def _on_accept_nterms(self, evt):
+        """
+        User has accepted the estimated number of terms, 
+        set it as part of the input parameters
+        """
+        try:
+            nterms = self.nterms_estimate_ctl.GetLabel()
+            tmp = float(nterms)
+            self.nfunc_ctl.SetValue(nterms)
+        except:
+            # No estimate or bad estimate, either do nothing
+            import sys
+            print "InversionControl._on_accept_nterms: %s" % sys.exc_value
+            pass
+        
+    def clear_panel(self):
+        """
+        """
+        self.plot_data.SetValue("")
+        self.on_reset(event=None)
+        
+    def on_reset(self, event=None):
+        """
+        Resets inversion parameters
+        """
+        self.nfunc = self._manager.DEFAULT_NFUNC
+        self.d_max = self._manager.DEFAULT_DMAX
+        self.alpha = self._manager.DEFAULT_ALPHA
+        self.qmin_ctl.SetValue("")
+        self.qmax_ctl.SetValue("")
+        self.time_ctl.SetValue("")
+        self.rg_ctl.SetValue("")
+        self.iq0_ctl.SetValue("")
+        self.bck_ctl.SetValue("")
+        self.chi2_ctl.SetValue("")
+        self.osc_ctl.SetValue("")
+        self.pos_ctl.SetValue("")
+        self.pos_err_ctl.SetValue("")
+        self.alpha_estimate_ctl.Enable(False)
+        self.alpha_estimate_ctl.SetLabel("")
+        self.nterms_estimate_ctl.Enable(False)
+        self.nterms_estimate_ctl.SetLabel("")
+        self._on_pars_changed()
+        
+    def _on_pars_changed(self, evt=None):
+        """
+        Called when an input parameter has changed
+        We will estimate the alpha parameter behind the
+        scenes. 
+        """
+        flag, alpha, dmax, nfunc, qmin, qmax, height, width = self._read_pars()
+        has_bck = self.bck_chk.IsChecked()
+        
+        # If the pars are valid, estimate alpha
+        if flag:
+            self.nterms_estimate_ctl.Enable(False)
+            self.alpha_estimate_ctl.Enable(False)
+            
+            dataset = self.plot_data.GetValue()
+            if dataset is not None and dataset.strip() != "":
+                self._manager.estimate_plot_inversion(alpha=alpha, nfunc=nfunc, 
+                                                 d_max=dmax,
+                                                 q_min=qmin, q_max=qmax,
+                                                 bck=has_bck, 
+                                                 height=height,
+                                                 width=width)
+        
+    def _read_pars(self, evt=None):
+        """
+        """    
+        alpha = 0
+        nfunc = 5
+        dmax  = 120
+        qmin  = 0
+        qmax  = 0
+        height = 0
+        width  = 0
+        flag = True
+        # Read slit height
+        try:
+            height_str = self.sheight_ctl.GetValue()
+            if len(height_str.lstrip().rstrip()) == 0:
+                height = 0
+            else:
+                height = float(height_str)
+                self.sheight_ctl.SetBackgroundColour(wx.WHITE)
+                self.sheight_ctl.Refresh()
+        except:
+            flag = False
+            self.sheight_ctl.SetBackgroundColour("pink")
+            self.sheight_ctl.Refresh()
+            
+        # Read slit width
+        try:
+            width_str = self.swidth_ctl.GetValue()
+            if len(width_str.lstrip().rstrip())==0:
+                width = 0
+            else:
+                width = float(width_str)
+                self.swidth_ctl.SetBackgroundColour(wx.WHITE)
+                self.swidth_ctl.Refresh()
+        except:
+            flag = False
+            self.swidth_ctl.SetBackgroundColour("pink")
+            self.swidth_ctl.Refresh()
+        
+        # Read alpha
+        try:
+            alpha = float(self.alpha_ctl.GetValue())
+            self.alpha_ctl.SetBackgroundColour(wx.WHITE)
+            self.alpha_ctl.Refresh()
+        except:
+            flag = False
+            self.alpha_ctl.SetBackgroundColour("pink")
+            self.alpha_ctl.Refresh()
+        
+        # Read d_max    
+        try:
+            dmax = float(self.dmax_ctl.GetValue())
+            self.dmax_ctl.SetBackgroundColour(wx.WHITE)
+            self.dmax_ctl.Refresh()
+        except:
+            flag = False
+            self.dmax_ctl.SetBackgroundColour("pink")
+            self.dmax_ctl.Refresh()
+            
+        # Read nfunc
+        try:
+            nfunc = int(self.nfunc_ctl.GetValue())
+            npts = self._manager.get_npts()
+            if npts > 0 and nfunc > npts:
+                message = "Number of function terms should be smaller "
+                message += "than the number of points"
+                wx.PostEvent(self._manager.parent, StatusEvent(status=message))
+                raise ValueError, message
+            self.nfunc_ctl.SetBackgroundColour(wx.WHITE)
+            self.nfunc_ctl.Refresh()
+        except:
+            flag = False
+            self.nfunc_ctl.SetBackgroundColour("pink")
+            self.nfunc_ctl.Refresh()
+        
+        # Read qmin
+        try:
+            qmin_str = self.qmin_ctl.GetValue()
+            if len(qmin_str.lstrip().rstrip()) == 0:
+                qmin = None
+            else:
+                qmin = float(qmin_str)
+                self.qmin_ctl.SetBackgroundColour(wx.WHITE)
+                self.qmin_ctl.Refresh()
+        except:
+            flag = False
+            self.qmin_ctl.SetBackgroundColour("pink")
+            self.qmin_ctl.Refresh()
+        
+        # Read qmax
+        try:
+            qmax_str = self.qmax_ctl.GetValue()
+            if len(qmax_str.lstrip().rstrip()) == 0:
+                qmax = None
+            else:
+                qmax = float(qmax_str)
+                self.qmax_ctl.SetBackgroundColour(wx.WHITE)
+                self.qmax_ctl.Refresh()
+        except:
+            flag = False
+            self.qmax_ctl.SetBackgroundColour("pink")
+            self.qmax_ctl.Refresh()
+        
+        return flag, alpha, dmax, nfunc, qmin, qmax, height, width
+    
+    def _on_explore(self, evt):
+        """
+        Invoke the d_max exploration dialog
+        """
+        from explore_dialog import ExploreDialog
+        if self._manager._last_pr is not None:
+            pr = self._manager._create_plot_pr()
+            dialog = ExploreDialog(pr, 10, None, -1, "")
+            dialog.ShowModal()
+        else:
+            message = "No data to analyze. Please load a data set to proceed."
+            wx.PostEvent(self._manager.parent, StatusEvent(status=message))
+            
+    def _on_invert(self, evt):
+        """
+        Perform inversion
+        
+        :param silent: when True, there will be no output for the user 
+        
+        """
+        # Get the data from the form
+        # Push it to the manager
+        
+        flag, alpha, dmax, nfunc, qmin, qmax, height, width = self._read_pars()
+        has_bck = self.bck_chk.IsChecked()
+        
+        if flag:
+            dataset = self.plot_data.GetValue()
+            if dataset==None or len(dataset.strip())==0:
+                message = "No data to invert. Select a data set before"
+                message += " proceeding with P(r) inversion."
+                wx.PostEvent(self._manager.parent, StatusEvent(status=message))
+            else:
+                self._manager.setup_plot_inversion(alpha=alpha, nfunc=nfunc, 
+                                                  d_max=dmax,
+                                                  q_min=qmin, q_max=qmax,
+                                                  bck=has_bck,
+                                                  height=height,
+                                                  width=width)
+        else:
+            message = "The P(r) form contains invalid values: "
+            message += "please submit it again."
+            wx.PostEvent(self.parent, StatusEvent(status=message))
+        
+    def _change_file(self, evt=None, filepath=None, data=None):
+        """
+        Choose a new input file for I(q)
+        """
+        if not self._manager is None:
+            self.plot_data.SetValue(str(data.name))
+            try:
+                self._manager.show_data(data=data, reset=True)
+                self._on_pars_changed(None)
+                self._on_invert(None)
+            except:
+                msg = "InversionControl._change_file: %s" % sys.exc_value
+                logging.error(msg)                    
+
+class HelpDialog(wx.Dialog):
+    """
+    """
+    def __init__(self, parent, id):
+        """
+        """
+        from sans.pr.invertor import help
+        wx.Dialog.__init__(self, parent, id, size=(400, 420))
+        self.SetTitle("P(r) help") 
+        
+
+        vbox = wx.BoxSizer(wx.VERTICAL)
+
+        explanation = help()
+           
+        label_explain = wx.StaticText(self, -1, explanation, size=(350, 320))
+            
+        vbox.Add(label_explain, 0, wx.ALL|wx.EXPAND, 15)
+
+
+        static_line = wx.StaticLine(self, -1)
+        vbox.Add(static_line, 0, wx.EXPAND, 0)
+        
+        button_OK = wx.Button(self, wx.ID_OK, "OK")
+        #button_Cancel = wx.Button(self, wx.ID_CANCEL, "Cancel")
+        
+        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
+        sizer_button.Add((20, 20), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+        sizer_button.Add(button_OK, 0, wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 10)
+        
+        vbox.Add(sizer_button, 0, wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
+
+        self.SetSizer(vbox)
+        self.SetAutoLayout(True)
+        
+        self.Layout()
+        self.Centre()
+
+class PrDistDialog(wx.Dialog):
+    """
+    Property dialog to let the user change the number
+    of points on the P(r) plot.
+    """
+    def __init__(self, parent, id):
+        from sans.pr.invertor import help
+        wx.Dialog.__init__(self, parent, id, size=(250, 120))
+        self.SetTitle("P(r) distribution") 
+        
+
+        vbox = wx.BoxSizer(wx.VERTICAL)
+        
+        label_npts = wx.StaticText(self, -1, "Number of points")
+        self.npts_ctl = PrTextCtrl(self, -1, size=(100, 20))
+                 
+        pars_sizer = wx.GridBagSizer(5, 5)
+        iy = 0
+        pars_sizer.Add(label_npts, (iy, 0), (1, 1), wx.LEFT, 15)
+        pars_sizer.Add(self.npts_ctl, (iy, 1), (1, 1), wx.RIGHT, 0)
+        
+        vbox.Add(pars_sizer, 0, wx.ALL|wx.EXPAND, 15)
+
+        static_line = wx.StaticLine(self, -1)
+        vbox.Add(static_line, 0, wx.EXPAND, 0)
+        
+        button_OK = wx.Button(self, wx.ID_OK, "OK")
+        self.Bind(wx.EVT_BUTTON, self._checkValues, button_OK)
+        button_Cancel = wx.Button(self, wx.ID_CANCEL, "Cancel")
+        
+        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
+        sizer_button.Add((20, 20), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
+        sizer_button.Add(button_OK, 0, wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 10)
+        sizer_button.Add(button_Cancel, 0,
+                         wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 10)        
+        vbox.Add(sizer_button, 0, wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
+
+        self.SetSizer(vbox)
+        self.SetAutoLayout(True)
+        
+        self.Layout()
+        self.Centre()
+
+    def _checkValues(self, event):
+        """
+        Check the dialog content.
+        """
+        flag = True
+        try:
+            int(self.npts_ctl.GetValue())
+            self.npts_ctl.SetBackgroundColour(wx.WHITE)
+            self.npts_ctl.Refresh()
+        except:
+            flag = False
+            self.npts_ctl.SetBackgroundColour("pink")
+            self.npts_ctl.Refresh()
+        if flag:
+            event.Skip(True)
+
+    def get_content(self):
+        """
+        Return the content of the dialog.
+        At this point the values have already been
+        checked.
+        """
+        value = int(self.npts_ctl.GetValue())
+        return value
+    
+    def set_content(self, npts):
+        """
+        Initialize the content of the dialog.
+        """
+        self.npts_ctl.SetValue("%i" % npts)
+
+##### testing code ############################################################
+"""
+Example: ::
+
+class TestPlot:
+    def __init__(self, text):
+        self.name = text
+    
+class MyApp(wx.App):
+    def OnInit(self):
+        wx.InitAllImageHandlers()
+        dialog = PrDistDialog(None, -1)
+        if dialog.ShowModal() == wx.ID_OK:
+            pass
+        dialog.Destroy()
+        
+        return 1
+
+# end of class MyApp
+
+if __name__ == "__main__":
+    app = MyApp(0)
+    app.MainLoop()
+    
+"""
+##### end of testing code #####################################################    
