source: sasview/prview/perspectives/pr/inversion_panel.py @ cb190a19

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since cb190a19 was 9bc4ed23, checked in by Gervaise Alina <gervyh@…>, 15 years ago

small bug fix

  • Property mode set to 100644
File size: 38.8 KB
RevLine 
[f3d51f6]1#!/usr/bin/env python
2
3# version
4__id__ = "$Id: aboutdialog.py 1193 2007-05-03 17:29:59Z dmitriy $"
5__revision__ = "$Revision: 1193 $"
6
7import wx
[5d98370]8import os
[ceaf16e]9import sys
10import logging
[9ff861b]11from wx.lib.scrolledpanel import ScrolledPanel
[f3d51f6]12from sans.guicomm.events import StatusEvent   
[5d98370]13from inversion_state import InversionState
[9ff861b]14from pr_widgets import PrTextCtrl, DataFileTextCtrl, OutputTextCtrl
[f3d51f6]15
16
17
[9ff861b]18class InversionControl(ScrolledPanel):
[f3d51f6]19    window_name = 'pr_control'
20    window_caption = "P(r) control panel"
21    CENTER_PANE = True
22   
23    # Figure of merit parameters [default]
[dfb58f8]24   
25    ## Oscillation parameters (sin function = 1.1)
[f3d51f6]26    oscillation_max = 1.5
27   
[aa4b8379]28    def __init__(self, parent, id = -1, plots = None, standalone=False, **kwargs):
[9ff861b]29        ScrolledPanel.__init__(self, parent, id = id, **kwargs)
30        self.SetupScrolling()
[f3d51f6]31       
32        self.plots = plots
33        self.radio_buttons = {}
[c405c69]34        self.parent = parent
[f3d51f6]35       
36        ## Data file TextCtrl
[a4bd2ac]37        self.data_file  = None
38        self.plot_data  = None
39        self.nfunc_ctl  = None
40        self.alpha_ctl  = None
41        self.dmax_ctl   = None
42        self.time_ctl   = None
43        self.chi2_ctl   = None
44        self.osc_ctl    = None
[f3d51f6]45        self.file_radio = None
46        self.plot_radio = None
[634f1cf]47        self.label_sugg = None
[a4bd2ac]48        self.qmin_ctl   = None
49        self.qmax_ctl   = None
[5d98370]50        self.swidth_ctl = None
51        self.sheight_ctl = None
[a4bd2ac]52       
53        self.rg_ctl     = None
54        self.iq0_ctl    = None
55        self.bck_chk    = None
56        self.bck_ctl    = None
[f3d51f6]57       
[dfb58f8]58        # TextCtrl for fraction of positive P(r)
59        self.pos_ctl = None
60       
61        # TextCtrl for fraction of 1 sigma positive P(r)
62        self.pos_err_ctl = None 
63       
[f3d51f6]64        ## Estimates
65        self.alpha_estimate_ctl = None
[35adaf6]66        self.nterms_estimate_ctl = None
[f3d51f6]67       
[a00ee4c]68        ## D_max distance explorator
69        self.distance_explorator_ctl = None
70       
[f3d51f6]71        ## Data manager
72        self.manager   = None
73       
[aa4b8379]74        ## Standalone flage
75        self.standalone = standalone
76       
[5d98370]77        ## Default file location for save
78        self._default_save_location = os.getcwd()
79       
[0ccd214]80       
81        # Default width
82        self._default_width = 350
[f3d51f6]83        self._do_layout()
84       
85    def __setattr__(self, name, value):
86        """
87            Allow direct hooks to text boxes
88        """
89        if name=='nfunc':
[5d98370]90            self.nfunc_ctl.SetValue(str(int(value)))
[f3d51f6]91        elif name=='d_max':
92            self.dmax_ctl.SetValue(str(value))
93        elif name=='alpha':
94            self.alpha_ctl.SetValue(str(value))
95        elif name=='chi2':
[a4bd2ac]96            self.chi2_ctl.SetValue("%-5.2g" % value)
97        elif name=='bck':
98            self.bck_ctl.SetValue("%-5.2g" % value)
[634f1cf]99        elif name=='q_min':
[a4bd2ac]100            self.qmin_ctl.SetValue("%-5.2g" % value)
[634f1cf]101        elif name=='q_max':
[a4bd2ac]102            self.qmax_ctl.SetValue("%-5.2g" % value)
[f3d51f6]103        elif name=='elapsed':
104            self.time_ctl.SetValue("%-5.2g" % value)
[a4bd2ac]105        elif name=='rg':
106            self.rg_ctl.SetValue("%-5.2g" % value)
107        elif name=='iq0':
108            self.iq0_ctl.SetValue("%-5.2g" % value)
[f3d51f6]109        elif name=='oscillation':
110            self.osc_ctl.SetValue("%-5.2g" % value)
[5d98370]111        elif name=='slit_width':
112            self.swidth_ctl.SetValue("%-5.2g" % value)
113        elif name=='slit_height':
114            self.sheight_ctl.SetValue("%-5.2g" % value)
[dfb58f8]115        elif name=='positive':
116            self.pos_ctl.SetValue("%-5.2g" % value)
117        elif name=='pos_err':
118            self.pos_err_ctl.SetValue("%-5.2g" % value)
[f3d51f6]119        elif name=='alpha_estimate':
[634f1cf]120            self.alpha_estimate_ctl.SetToolTipString("Click to accept value.")
121            self.alpha_estimate_ctl.Enable(True)
122            self.alpha_estimate_ctl.SetLabel("%-3.1g" % value)
123            #self.alpha_estimate_ctl.Show()
124            #self.label_sugg.Show()
[35adaf6]125        elif name=='nterms_estimate':
126            self.nterms_estimate_ctl.SetToolTipString("Click to accept value.")
127            self.nterms_estimate_ctl.Enable(True)
128            self.nterms_estimate_ctl.SetLabel("%-g" % value)
[f3d51f6]129        elif name=='plotname':
[0d88a09]130            self.plot_data.SetValue(str(value))
131            self._on_pars_changed(None)
[aa4b8379]132        elif name=='datafile':
[0d88a09]133            self.plot_data.SetValue(str(value))
134            self._on_pars_changed(None)
[f3d51f6]135        else:
136            wx.Panel.__setattr__(self, name, value)
137       
138    def __getattr__(self, name):
139        """
140            Allow direct hooks to text boxes
141        """
142        if name=='nfunc':
[3fd1ebc]143            try:
144                return int(self.nfunc_ctl.GetValue())
145            except:
146                return -1
[f3d51f6]147        elif name=='d_max':
[3fd1ebc]148            try:
149                return self.dmax_ctl.GetValue()
150            except:
151                return -1.0
[f3d51f6]152        elif name=='alpha':
[3fd1ebc]153            try:
154                return self.alpha_ctl.GetValue()
155            except:
156                return -1.0
[f3d51f6]157        elif name=='chi2':
[3fd1ebc]158            try:
159                return float(self.chi2_ctl.GetValue())
160            except:
[5d98370]161                return None
[a4bd2ac]162        elif name=='bck':
163            try:
164                return float(self.bck_ctl.GetValue())
165            except:
[5d98370]166                return None
[634f1cf]167        elif name=='q_min':
[3fd1ebc]168            try:
169                return float(self.qmin_ctl.GetValue())
170            except:
171                return 0.0
[634f1cf]172        elif name=='q_max':
[3fd1ebc]173            try:
174                return float(self.qmax_ctl.GetValue())
175            except:
176                return 0.0
[f3d51f6]177        elif name=='elapsed':
[3fd1ebc]178            try:
179                return float(self.time_ctl.GetValue())
180            except:
[5d98370]181                return None
[a4bd2ac]182        elif name=='rg':
183            try:
184                return float(self.rg_ctl.GetValue())
185            except:
[5d98370]186                return None
[a4bd2ac]187        elif name=='iq0':
188            try:
189                return float(self.iq0_ctl.GetValue())
190            except:
[5d98370]191                return None
[f3d51f6]192        elif name=='oscillation':
[3fd1ebc]193            try:
194                return float(self.osc_ctl.GetValue())
195            except:
[5d98370]196                return None
197        elif name=='slit_width':
198            try:
199                return float(self.swidth_ctl.GetValue())
200            except:
201                return None
202        elif name=='slit_height':
203            try:
204                return float(self.sheight_ctl.GetValue())
205            except:
206                return None
[dfb58f8]207        elif name=='pos':
[3fd1ebc]208            try:
209                return float(self.pos_ctl.GetValue())
210            except:
[5d98370]211                return None
[dfb58f8]212        elif name=='pos_err':
[5d98370]213            try:
214                return float(self.pos_err_ctl.GetValue())
215            except:
216                return None
[f3d51f6]217        elif name=='alpha_estimate':
[5d98370]218            try:
219                return float(self.alpha_estimate_ctl.GetLabel())
220            except:
221                return None
[35adaf6]222        elif name=='nterms_estimate':
[5d98370]223            try:
224                return int(self.nterms_estimate_ctl.GetLabel())
225            except:
226                return None
[f3d51f6]227        elif name=='plotname':
[0d88a09]228            return self.plot_data.GetValue()
[aa4b8379]229        elif name=='datafile':
[0d88a09]230            return self.plot_data.GetValue()
[f3d51f6]231        else:
[a00ee4c]232            return wx.Panel.__getattribute__(self, name)
[f3d51f6]233       
[5d98370]234    def _save_state(self, evt=None):
235        """
236            Method used to create a memento of the current state
237           
238            @return: state object
239        """
240        # Ask the user the location of the file to write to.
241        path = None
242        dlg = wx.FileDialog(self, "Choose a file", self._default_save_location, "", "*.prv", wx.SAVE)
243        if dlg.ShowModal() == wx.ID_OK:
244            path = dlg.GetPath()
245            self._default_save_location = os.path.dirname(path)
[0d88a09]246        else:
247            return None
248       
[5d98370]249        dlg.Destroy()
250               
251        # Construct the state object   
252        state = InversionState()
253       
254        # Read the panel's parameters
255        flag, alpha, dmax, nfunc, qmin, \
256        qmax, height, width = self._read_pars()
257       
258        state.nfunc = nfunc
259        state.d_max = dmax
260        state.alpha = alpha
261        state.qmin  = qmin
262        state.qmax  = qmax
263        state.width = width
264        state.height = height
265       
266        # Data file
[0d88a09]267        state.file = self.plot_data.GetValue()
[5d98370]268       
269        # Background evaluation checkbox
270        state.estimate_bck = self.bck_chk.IsChecked()
271       
272        # Estimates
273        state.nterms_estimate = self.nterms_estimate
274        state.alpha_estimate = self.alpha_estimate
275       
276        # Read the output values
277        state.chi2    = self.chi2
278        state.elapsed = self.elapsed
279        state.osc     = self.oscillation
280        state.pos     = self.pos
281        state.pos_err = self.pos_err
282        state.rg      = self.rg
283        state.iq0     = self.iq0
284        state.bck     = self.bck
285           
[0d88a09]286        self.manager.save_data(filepath=path, prstate=state)
[fde249c]287       
[5d98370]288        return state
289   
290    def set_state(self, state):
291        """
292            Set the state of the panel and inversion problem to
293            the state passed as a parameter.
294            Execute the inversion immediately after filling the
295            controls.
296           
297            @param state: InversionState object
298        """
[fde249c]299        if state.nfunc is not None:
300            self.nfunc = state.nfunc
301        if state.d_max is not None:
302            self.d_max = state.d_max
303        if state.alpha is not None:
304            self.alpha = state.alpha
305        if state.qmin is not None:
306            self.q_min  = state.qmin
307        if state.qmax is not None:
308            self.q_max  = state.qmax
309        if state.width is not None:
310            self.slit_width = state.width
311        if state.height is not None:
312            self.slit_height = state.height
[5d98370]313       
314        # Data file
[0d88a09]315        self.plot_data.SetValue(str(state.file))
[5d98370]316   
317        # Background evaluation checkbox
318        self.bck_chk.SetValue(state.estimate_bck)
319       
320        # Estimates
[fde249c]321        if state.nterms_estimate is not None:
322            self.nterms_estimate = state.nterms_estimate
323        if state.alpha_estimate is not None: 
324            self.alpha_estimate = state.alpha_estimate
[5d98370]325   
326       
327        # Read the output values
[fde249c]328        if state.chi2 is not None:
329            self.chi2    = state.chi2
330        if state.elapsed is not None:
331            self.elapsed = state.elapsed
332        if state.osc is not None:
333            self.oscillation = state.osc
334        if state.pos is not None:
335            self.positive = state.pos
336        if state.pos_err is not None:
337            self.pos_err = state.pos_err
338        if state.rg is not None:
339            self.rg      = state.rg
340        if state.iq0 is not None:
341            self.iq0     = state.iq0
342        if state.bck is not None:
343            self.bck     = state.bck
[5d98370]344
[0d88a09]345        # Perform inversion
346        if self.standalone == False:
[fde249c]347            self._on_invert(None)   
[5d98370]348       
[f3d51f6]349    def set_manager(self, manager):
350        self.manager = manager
351       
352    def _do_layout(self):
[4318af7f]353        vbox = wx.GridBagSizer(0,0)
354        iy_vb = 0
[f3d51f6]355
356        # ----- I(q) data -----
[aa4b8379]357        databox = wx.StaticBox(self, -1, "I(q) data source")
[f3d51f6]358       
359        boxsizer1 = wx.StaticBoxSizer(databox, wx.VERTICAL)
[0ccd214]360        boxsizer1.SetMinSize((self._default_width,50))
[f3d51f6]361        pars_sizer = wx.GridBagSizer(5,5)
362
363        iy = 0
[d2ee6f6]364        self.file_radio = wx.StaticText(self, -1, "Data:")
[f3d51f6]365        pars_sizer.Add(self.file_radio, (iy,0), (1,1), wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
366       
[9ff861b]367        self.plot_data = DataFileTextCtrl(self, -1, size=(260,20))
368       
369        pars_sizer.Add(self.plot_data, (iy,1), (1,1), wx.EXPAND|wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 15)
[f3d51f6]370       
[a4bd2ac]371        self.bck_chk = wx.CheckBox(self, -1, "Estimate background level")
372        self.bck_chk.SetToolTipString("Check box to let the fit estimate the constant background level.")
373        self.bck_chk.Bind(wx.EVT_CHECKBOX, self._on_pars_changed)
374        iy += 1
375        pars_sizer.Add(self.bck_chk, (iy,0), (1,2), wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
376        boxsizer1.Add(pars_sizer, 0, wx.EXPAND) 
[0ccd214]377        vbox.Add(boxsizer1, (iy_vb,0), (1,1), wx.LEFT|wx.RIGHT|wx.EXPAND|wx.ADJUST_MINSIZE|wx.TOP, 5)
[a4bd2ac]378       
379        # ----- Add slit parameters -----
[2a92852]380        if True:
[a4bd2ac]381            sbox = wx.StaticBox(self, -1, "Slit parameters")
382            sboxsizer = wx.StaticBoxSizer(sbox, wx.VERTICAL)
[0ccd214]383            sboxsizer.SetMinSize((self._default_width,20))
[a4bd2ac]384           
385            sizer_slit = wx.GridBagSizer(5,5)
386   
[4318af7f]387            label_sheight = wx.StaticText(self, -1, "Height", size=(40,20))
388            label_swidth = wx.StaticText(self, -1, "Width", size=(40,20))
[a4bd2ac]389            #label_sunits1 = wx.StaticText(self, -1, "[A^(-1)]")
[4318af7f]390            label_sunits2 = wx.StaticText(self, -1, "[A^(-1)]", size=(55,20))
[9ff861b]391            self.sheight_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60,20))
392            self.swidth_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60,20))
[a4bd2ac]393            self.sheight_ctl.SetToolTipString("Enter slit height in units of Q or leave blank.")
394            self.swidth_ctl.SetToolTipString("Enter slit width in units of Q or leave blank.")
[2a92852]395            #self.sheight_ctl.Bind(wx.EVT_TEXT, self._on_pars_changed)
396            #self.swidth_ctl.Bind(wx.EVT_TEXT,  self._on_pars_changed)
[a4bd2ac]397           
398            iy = 0
[357b79b]399            sizer_slit.Add(label_sheight,    (iy,0), (1,1), wx.LEFT|wx.EXPAND, 5)
400            sizer_slit.Add(self.sheight_ctl, (iy,1), (1,1), wx.LEFT|wx.EXPAND, 5)
[a4bd2ac]401            #sizer_slit.Add(label_sunits1,    (iy,2), (1,1), wx.LEFT|wx.EXPAND, 10)
[357b79b]402            sizer_slit.Add(label_swidth,     (iy,2), (1,1), wx.LEFT|wx.EXPAND, 5)
403            sizer_slit.Add(self.swidth_ctl,  (iy,3), (1,1), wx.LEFT|wx.EXPAND, 5)
404            sizer_slit.Add(label_sunits2,    (iy,4), (1,1), wx.LEFT|wx.EXPAND, 5)
[a4bd2ac]405           
406            sboxsizer.Add(sizer_slit, wx.TOP, 15)
[4318af7f]407            iy_vb += 1
[0ccd214]408            vbox.Add(sboxsizer, (iy_vb,0), (1,1), wx.LEFT|wx.RIGHT|wx.EXPAND|wx.ADJUST_MINSIZE, 5)
[a4bd2ac]409       
[357b79b]410       
411        # ----- Q range -----
412        qbox = wx.StaticBox(self, -1, "Q range")
413        qboxsizer = wx.StaticBoxSizer(qbox, wx.VERTICAL)
[0ccd214]414        qboxsizer.SetMinSize((self._default_width,20))
[357b79b]415       
416        sizer_q = wx.GridBagSizer(5,5)
417
[4318af7f]418        label_qmin = wx.StaticText(self, -1, "Q min", size=(40,20))
419        label_qmax = wx.StaticText(self, -1, "Q max", size=(40,20))
[357b79b]420        #label_qunits1 = wx.StaticText(self, -1, "[A^(-1)]")
[4318af7f]421        label_qunits2 = wx.StaticText(self, -1, "[A^(-1)]", size=(55,20))
[9ff861b]422        self.qmin_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60,20))
423        self.qmax_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60,20))
[357b79b]424        self.qmin_ctl.SetToolTipString("Select a lower bound for Q or leave blank.")
425        self.qmax_ctl.SetToolTipString("Select an upper bound for Q or leave blank.")
426        self.qmin_ctl.Bind(wx.EVT_TEXT, self._on_pars_changed)
427        self.qmax_ctl.Bind(wx.EVT_TEXT, self._on_pars_changed)
428       
429        iy = 0
430        sizer_q.Add(label_qmin,    (iy,0), (1,1), wx.LEFT|wx.EXPAND, 5)
431        sizer_q.Add(self.qmin_ctl, (iy,1), (1,1), wx.LEFT|wx.EXPAND, 5)
432        #sizer_q.Add(label_qunits1, (iy,2), (1,1), wx.LEFT|wx.EXPAND, 15)
433        sizer_q.Add(label_qmax,    (iy,2), (1,1), wx.LEFT|wx.EXPAND, 5)
434        sizer_q.Add(self.qmax_ctl, (iy,3), (1,1), wx.LEFT|wx.EXPAND, 5)
435        sizer_q.Add(label_qunits2, (iy,4), (1,1), wx.LEFT|wx.EXPAND, 5)
436        qboxsizer.Add(sizer_q, wx.TOP, 15)
[4318af7f]437
438        iy_vb += 1
[0ccd214]439        vbox.Add(qboxsizer, (iy_vb,0), (1,1), wx.LEFT|wx.RIGHT|wx.EXPAND|wx.ADJUST_MINSIZE, 5)
[357b79b]440       
441       
442       
[f3d51f6]443
444        # ----- Parameters -----
445        parsbox = wx.StaticBox(self, -1, "Parameters")
446        boxsizer2 = wx.StaticBoxSizer(parsbox, wx.VERTICAL)
[0ccd214]447        boxsizer2.SetMinSize((self._default_width,50))
[f3d51f6]448       
[32dffae4]449        explanation  = "P(r) is found by fitting a set of base functions to I(Q). "
450        explanation += "The minimization involves a regularization term to ensure "
[0bae207]451        explanation += "a smooth P(r). The regularization constant gives the size of that " 
[119a11d]452        explanation += "term. The suggested value is the value above which the "
[32dffae4]453        explanation += "output P(r) will have only one peak."
[0ccd214]454        label_explain = wx.StaticText(self, -1, explanation, size=(280,120))
[32dffae4]455        boxsizer2.Add(label_explain,  wx.LEFT|wx.BOTTOM, 5)
456       
457       
458       
[f3d51f6]459        label_nfunc = wx.StaticText(self, -1, "Number of terms")
460        label_nfunc.SetMinSize((120,20))
461        label_alpha = wx.StaticText(self, -1, "Regularization constant")
462        label_dmax  = wx.StaticText(self, -1, "Max distance [A]")
[634f1cf]463        self.label_sugg  = wx.StaticText(self, -1, "Suggested value")
464        #self.label_sugg.Hide()
[f3d51f6]465       
[9ff861b]466        self.nfunc_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60,20))
[32dffae4]467        self.nfunc_ctl.SetToolTipString("Number of terms in the expansion.")
[9ff861b]468        self.alpha_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60,20))
[32dffae4]469        self.alpha_ctl.SetToolTipString("Control parameter for the size of the regularization term.")
[9ff861b]470        self.dmax_ctl  = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60,20))
[32dffae4]471        self.dmax_ctl.SetToolTipString("Maximum distance between any two points in the system.")
[634f1cf]472        id = wx.NewId()
473        self.alpha_estimate_ctl  = wx.Button(self, id, "")
474        #self.alpha_estimate_ctl.Hide()
475        self.Bind(wx.EVT_BUTTON, self._on_accept_alpha, id = id)   
[f3d51f6]476        self.alpha_estimate_ctl.Enable(False)
[634f1cf]477        #self.alpha_estimate_ctl.SetBackgroundColour('#ffdf85')
478        #self.alpha_estimate_ctl.SetBackgroundColour(self.GetBackgroundColour())
479        self.alpha_estimate_ctl.SetToolTipString("Waiting for estimate...")
[f3d51f6]480       
[35adaf6]481        id = wx.NewId()
482        self.nterms_estimate_ctl  = wx.Button(self, id, "")
483        #self.nterms_estimate_ctl.Hide()
484        self.Bind(wx.EVT_BUTTON, self._on_accept_nterms, id = id)   
485        self.nterms_estimate_ctl.Enable(False)
486        #self.nterms_estimate_ctl.SetBackgroundColour('#ffdf85')
487        #self.nterms_estimate_ctl.SetBackgroundColour(self.GetBackgroundColour())
488        self.nterms_estimate_ctl.SetToolTipString("Waiting for estimate...")
489       
[d6113849]490        self.nfunc_ctl.Bind(wx.EVT_TEXT, self._read_pars)
[a4bd2ac]491        self.alpha_ctl.Bind(wx.EVT_TEXT, self._read_pars)
492        self.dmax_ctl.Bind(wx.EVT_TEXT, self._on_pars_changed)
493       
[a00ee4c]494        # Distance explorator
495        id = wx.NewId()
496        self.distance_explorator_ctl = wx.Button(self, id, "Explore")
497        self.Bind(wx.EVT_BUTTON, self._on_explore, id = id)           
[a4bd2ac]498       
499       
[f3d51f6]500        sizer_params = wx.GridBagSizer(5,5)
501
502        iy = 0
[634f1cf]503        sizer_params.Add(self.label_sugg,       (iy,2), (1,1), wx.LEFT, 15)
[f3d51f6]504        iy += 1
[32dffae4]505        sizer_params.Add(label_nfunc,      (iy,0), (1,1), wx.LEFT, 15)
506        sizer_params.Add(self.nfunc_ctl,   (iy,1), (1,1), wx.RIGHT, 0)
[35adaf6]507        sizer_params.Add(self.nterms_estimate_ctl, (iy,2), (1,1), wx.LEFT, 15)
[f3d51f6]508        iy += 1
[32dffae4]509        sizer_params.Add(label_alpha,      (iy,0), (1,1), wx.LEFT, 15)
510        sizer_params.Add(self.alpha_ctl,   (iy,1), (1,1), wx.RIGHT, 0)
511        sizer_params.Add(self.alpha_estimate_ctl, (iy,2), (1,1), wx.LEFT, 15)
[f3d51f6]512        iy += 1
[32dffae4]513        sizer_params.Add(label_dmax, (iy,0), (1,1), wx.LEFT, 15)
514        sizer_params.Add(self.dmax_ctl,   (iy,1), (1,1), wx.RIGHT, 0)
[a00ee4c]515        sizer_params.Add(self.distance_explorator_ctl,   (iy,2), (1,1), wx.LEFT, 15)
[f3d51f6]516
517        boxsizer2.Add(sizer_params, 0)
[634f1cf]518       
[4318af7f]519        iy_vb += 1
[0ccd214]520        vbox.Add(boxsizer2, (iy_vb,0), (1,1), wx.LEFT|wx.RIGHT|wx.EXPAND|wx.ADJUST_MINSIZE, 5)
[f3d51f6]521
[634f1cf]522
[f3d51f6]523        # ----- Results -----
524        resbox = wx.StaticBox(self, -1, "Outputs")
525        ressizer = wx.StaticBoxSizer(resbox, wx.VERTICAL)
[0ccd214]526        ressizer.SetMinSize((self._default_width,50))
[f3d51f6]527       
[a4bd2ac]528        label_rg       = wx.StaticText(self, -1, "Rg")
529        label_rg_unit  = wx.StaticText(self, -1, "[A]")
530        label_iq0      = wx.StaticText(self, -1, "I(Q=0)")
531        label_iq0_unit = wx.StaticText(self, -1, "[A^(-1)]")
532        label_bck      = wx.StaticText(self, -1, "Background")
533        label_bck_unit = wx.StaticText(self, -1, "[A^(-1)]")
[9ff861b]534        self.rg_ctl    = OutputTextCtrl(self, -1, size=(60,20))
[a4bd2ac]535        self.rg_ctl.SetToolTipString("Radius of gyration for the computed P(r).")
[9ff861b]536        self.iq0_ctl   = OutputTextCtrl(self, -1, size=(60,20))
[a4bd2ac]537        self.iq0_ctl.SetToolTipString("Scattering intensity at Q=0 for the computed P(r).")
[9ff861b]538        self.bck_ctl   = OutputTextCtrl(self, -1, size=(60,20))
[a4bd2ac]539        self.bck_ctl.SetToolTipString("Value of estimated constant background.")
540       
[f3d51f6]541        label_time = wx.StaticText(self, -1, "Computation time")
542        label_time_unit = wx.StaticText(self, -1, "secs")
543        label_time.SetMinSize((120,20))
544        label_chi2 = wx.StaticText(self, -1, "Chi2/dof")
545        label_osc = wx.StaticText(self, -1, "Oscillations")
[dfb58f8]546        label_pos = wx.StaticText(self, -1, "Positive fraction")
547        label_pos_err = wx.StaticText(self, -1, "1-sigma positive fraction")
[f3d51f6]548       
[9ff861b]549        self.time_ctl = OutputTextCtrl(self, -1, size=(60,20))
[32dffae4]550        self.time_ctl.SetToolTipString("Computation time for the last inversion, in seconds.")
551       
[9ff861b]552        self.chi2_ctl = OutputTextCtrl(self, -1, size=(60,20))
[32dffae4]553        self.chi2_ctl.SetToolTipString("Chi^2 over degrees of freedom.")
554       
[dfb58f8]555        # Oscillation parameter
[9ff861b]556        self.osc_ctl = OutputTextCtrl(self, -1, size=(60,20))
[32dffae4]557        self.osc_ctl.SetToolTipString("Oscillation parameter. P(r) for a sphere has an oscillation parameter of 1.1.")
[f3d51f6]558       
[dfb58f8]559        # Positive fraction figure of merit
[9ff861b]560        self.pos_ctl = OutputTextCtrl(self, -1, size=(60,20))
[dfb58f8]561        self.pos_ctl.SetToolTipString("Fraction of P(r) that is positive. Theoretically, P(r) is defined positive.")
562       
563        # 1-simga positive fraction figure of merit
[9ff861b]564        self.pos_err_ctl = OutputTextCtrl(self, -1, size=(60,20))
[dfb58f8]565        message  = "Fraction of P(r) that is at least 1 standard deviation greater than zero.\n"
566        message += "This figure of merit tells you about the size of the P(r) errors.\n"
567        message += "If it is close to 1 and the other figures of merit are bad, consider changing "
568        message += "the maximum distance."
569        self.pos_err_ctl.SetToolTipString(message)
570       
[f3d51f6]571        sizer_res = wx.GridBagSizer(5,5)
572
573        iy = 0
[a4bd2ac]574        sizer_res.Add(label_rg, (iy,0), (1,1), wx.LEFT|wx.EXPAND, 15)
575        sizer_res.Add(self.rg_ctl,   (iy,1), (1,1), wx.RIGHT|wx.EXPAND, 15)
576        sizer_res.Add(label_rg_unit,   (iy,2), (1,1), wx.RIGHT|wx.EXPAND, 15)
577        iy += 1
578        sizer_res.Add(label_iq0, (iy,0), (1,1), wx.LEFT|wx.EXPAND, 15)
579        sizer_res.Add(self.iq0_ctl,   (iy,1), (1,1), wx.RIGHT|wx.EXPAND, 15)
580        sizer_res.Add(label_iq0_unit,   (iy,2), (1,1), wx.RIGHT|wx.EXPAND, 15)
581        iy += 1
582        sizer_res.Add(label_bck, (iy,0), (1,1), wx.LEFT|wx.EXPAND, 15)
583        sizer_res.Add(self.bck_ctl,   (iy,1), (1,1), wx.RIGHT|wx.EXPAND, 15)
584        sizer_res.Add(label_bck_unit,   (iy,2), (1,1), wx.RIGHT|wx.EXPAND, 15)
585        iy += 1
[f3d51f6]586        sizer_res.Add(label_time, (iy,0), (1,1), wx.LEFT|wx.EXPAND, 15)
587        sizer_res.Add(self.time_ctl,   (iy,1), (1,1), wx.RIGHT|wx.EXPAND, 15)
588        sizer_res.Add(label_time_unit,   (iy,2), (1,1), wx.RIGHT|wx.EXPAND, 15)
589        iy += 1
590        sizer_res.Add(label_chi2, (iy,0), (1,1), wx.LEFT|wx.EXPAND, 15)
591        sizer_res.Add(self.chi2_ctl,   (iy,1), (1,1), wx.RIGHT|wx.EXPAND, 15)
592        iy += 1
593        sizer_res.Add(label_osc, (iy,0), (1,1), wx.LEFT|wx.EXPAND, 15)
594        sizer_res.Add(self.osc_ctl,   (iy,1), (1,1), wx.RIGHT|wx.EXPAND, 15)
595
[dfb58f8]596        iy += 1
597        sizer_res.Add(label_pos, (iy,0), (1,1), wx.LEFT|wx.EXPAND, 15)
598        sizer_res.Add(self.pos_ctl,   (iy,1), (1,1), wx.RIGHT|wx.EXPAND, 15)
599
600        iy += 1
601        sizer_res.Add(label_pos_err, (iy,0), (1,1), wx.LEFT|wx.EXPAND, 15)
602        sizer_res.Add(self.pos_err_ctl,   (iy,1), (1,1), wx.RIGHT|wx.EXPAND, 15)
603
[f3d51f6]604        ressizer.Add(sizer_res, 0)
[4318af7f]605        iy_vb += 1
[0ccd214]606        vbox.Add(ressizer, (iy_vb,0), (1,1), wx.LEFT|wx.RIGHT|wx.EXPAND|wx.ADJUST_MINSIZE, 5)
[f3d51f6]607
608        # ----- Buttons -----
609        id = wx.NewId()
610        button_OK = wx.Button(self, id, "Compute")
[32dffae4]611        button_OK.SetToolTipString("Perform P(r) inversion.")
[f3d51f6]612        self.Bind(wx.EVT_BUTTON, self._on_invert, id = id)   
[b659551]613       
614        id = wx.NewId()
615        button_Reset = wx.Button(self, id, "Reset")
616        button_Reset.SetToolTipString("Reset inversion parameters to default.")
617        self.Bind(wx.EVT_BUTTON, self._on_reset, id = id)   
[f3d51f6]618        #button_Cancel = wx.Button(self, wx.ID_CANCEL, "Cancel")
619       
[5d98370]620        id = wx.NewId()
621        button_Save = wx.Button(self, id, "Save")
622        button_Save.SetToolTipString("Save the current P(r) work to file.")
623        self.Bind(wx.EVT_BUTTON, self._save_state, id = id)   
624       
[f3d51f6]625        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
626        sizer_button.Add((20, 20), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
[5d98370]627        sizer_button.Add(button_Save, 0, wx.LEFT|wx.ADJUST_MINSIZE, 10)
[b659551]628        sizer_button.Add(button_Reset, 0, wx.LEFT|wx.ADJUST_MINSIZE, 10)
[f3d51f6]629        sizer_button.Add(button_OK, 0, wx.LEFT|wx.ADJUST_MINSIZE, 10)
630        #sizer_button.Add(button_Cancel, 0, wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 10)       
[4318af7f]631        iy_vb += 1
[0ccd214]632        vbox.Add(sizer_button, (iy_vb,0), (1,1), wx.EXPAND|wx.BOTTOM|wx.TOP|wx.RIGHT, 10)
[f3d51f6]633
[a00ee4c]634        self.Bind(wx.EVT_TEXT_ENTER, self._on_invert)
[f3d51f6]635
636        self.SetSizer(vbox)
637       
[634f1cf]638    def _on_accept_alpha(self, evt):
639        """
640            User has accepted the estimated alpha,
641            set it as part of the input parameters
642        """
643        try:
644            alpha = self.alpha_estimate_ctl.GetLabel()
645            tmp = float(alpha)
646            self.alpha_ctl.SetValue(alpha)
647        except:
648            # No estimate or bad estimate, either do nothing
649            import sys
650            print "InversionControl._on_accept_alpha: %s" % sys.exc_value
651            pass
[35adaf6]652   
653    def _on_accept_nterms(self, evt):
654        """
655            User has accepted the estimated number of terms,
656            set it as part of the input parameters
657        """
658        try:
659            nterms = self.nterms_estimate_ctl.GetLabel()
660            tmp = float(nterms)
661            self.nfunc_ctl.SetValue(nterms)
662        except:
663            # No estimate or bad estimate, either do nothing
664            import sys
665            print "InversionControl._on_accept_nterms: %s" % sys.exc_value
666            pass
[634f1cf]667       
[b659551]668    def _on_reset(self, evt):
669        """
670            Resets inversion parameters
671        """
672        self.nfunc = self.manager.DEFAULT_NFUNC
673        self.d_max = self.manager.DEFAULT_DMAX
674        self.alpha = self.manager.DEFAULT_ALPHA
675        self.qmin_ctl.SetValue("")
676        self.qmax_ctl.SetValue("")
677        self.time_ctl.SetValue("")
[a4bd2ac]678        self.rg_ctl.SetValue("")
679        self.iq0_ctl.SetValue("")
680        self.bck_ctl.SetValue("")
[b659551]681        self.chi2_ctl.SetValue("")
682        self.osc_ctl.SetValue("")
683        self.pos_ctl.SetValue("")
684        self.pos_err_ctl.SetValue("")
685        self.alpha_estimate_ctl.Enable(False)
686        self.alpha_estimate_ctl.SetLabel("")
[35adaf6]687        self.nterms_estimate_ctl.Enable(False)
688        self.nterms_estimate_ctl.SetLabel("")
[b659551]689        self._on_pars_changed()
[634f1cf]690       
[b659551]691    def _on_pars_changed(self, evt=None):
[f3d51f6]692        """
693            Called when an input parameter has changed
694            We will estimate the alpha parameter behind the
695            scenes.
696        """
[2a92852]697        flag, alpha, dmax, nfunc, qmin, qmax, height, width = self._read_pars()
[a4bd2ac]698        has_bck = self.bck_chk.IsChecked()
[f3d51f6]699       
700        # If the pars are valid, estimate alpha
701        if flag:
[2a92852]702            self.nterms_estimate_ctl.Enable(False)
703            self.alpha_estimate_ctl.Enable(False)
704           
[0d88a09]705            dataset = self.plot_data.GetValue()
706            self.manager.estimate_plot_inversion(alpha=alpha, nfunc=nfunc, 
707                                                 d_max=dmax,
708                                                 q_min=qmin, q_max=qmax,
709                                                 bck=has_bck, 
710                                                 height=height,
711                                                 width=width)
[f3d51f6]712       
[634f1cf]713    def _read_pars(self, evt=None):   
[f3d51f6]714        alpha = 0
715        nfunc = 5
716        dmax  = 120
[3fd1ebc]717        qmin  = 0
718        qmax  = 0
[2a92852]719        height = 0
720        width  = 0
[f3d51f6]721       
722        flag = True
723       
[2a92852]724       
725        # Read slit height
726        try:
727            height_str = self.sheight_ctl.GetValue()
728            if len(height_str.lstrip().rstrip())==0:
729                height = 0
730            else:
731                height = float(height_str)
732                self.sheight_ctl.SetBackgroundColour(wx.WHITE)
733                self.sheight_ctl.Refresh()
734        except:
735            flag = False
736            self.sheight_ctl.SetBackgroundColour("pink")
737            self.sheight_ctl.Refresh()
738           
739        # Read slit width
740        try:
741            width_str = self.swidth_ctl.GetValue()
742            if len(width_str.lstrip().rstrip())==0:
743                width = 0
744            else:
745                width = float(width_str)
746                self.swidth_ctl.SetBackgroundColour(wx.WHITE)
747                self.swidth_ctl.Refresh()
748        except:
749            flag = False
750            self.swidth_ctl.SetBackgroundColour("pink")
751            self.swidth_ctl.Refresh()
752       
[f3d51f6]753        # Read alpha
754        try:
755            alpha = float(self.alpha_ctl.GetValue())
756            self.alpha_ctl.SetBackgroundColour(wx.WHITE)
757            self.alpha_ctl.Refresh()
758        except:
759            flag = False
760            self.alpha_ctl.SetBackgroundColour("pink")
761            self.alpha_ctl.Refresh()
762       
763        # Read d_max   
764        try:
765            dmax = float(self.dmax_ctl.GetValue())
766            self.dmax_ctl.SetBackgroundColour(wx.WHITE)
767            self.dmax_ctl.Refresh()
768        except:
769            flag = False
770            self.dmax_ctl.SetBackgroundColour("pink")
771            self.dmax_ctl.Refresh()
772           
773        # Read nfunc
774        try:
775            nfunc = int(self.nfunc_ctl.GetValue())
[b659551]776            npts = self.manager.get_npts()
777            if npts>0 and nfunc>npts:
778                message = "Number of function terms should be smaller than the number of points"
779                wx.PostEvent(self.manager.parent, StatusEvent(status=message))
780                raise ValueError, message
[f3d51f6]781            self.nfunc_ctl.SetBackgroundColour(wx.WHITE)
782            self.nfunc_ctl.Refresh()
783        except:
784            flag = False
785            self.nfunc_ctl.SetBackgroundColour("pink")
786            self.nfunc_ctl.Refresh()
787       
[634f1cf]788        # Read qmin
789        try:
790            qmin_str = self.qmin_ctl.GetValue()
791            if len(qmin_str.lstrip().rstrip())==0:
792                qmin = None
793            else:
794                qmin = float(qmin_str)
795                self.qmin_ctl.SetBackgroundColour(wx.WHITE)
796                self.qmin_ctl.Refresh()
797        except:
798            flag = False
799            self.qmin_ctl.SetBackgroundColour("pink")
800            self.qmin_ctl.Refresh()
801       
802        # Read qmax
803        try:
804            qmax_str = self.qmax_ctl.GetValue()
805            if len(qmax_str.lstrip().rstrip())==0:
806                qmax = None
807            else:
808                qmax = float(qmax_str)
809                self.qmax_ctl.SetBackgroundColour(wx.WHITE)
810                self.qmax_ctl.Refresh()
811        except:
812            flag = False
813            self.qmax_ctl.SetBackgroundColour("pink")
814            self.qmax_ctl.Refresh()
815       
[2a92852]816        return flag, alpha, dmax, nfunc, qmin, qmax, height, width
[f3d51f6]817   
[a00ee4c]818    def _on_explore(self, evt):
819        """
820            Invoke the d_max exploration dialog
821        """
822        from explore_dialog import ExploreDialog
823        if self.manager._last_pr is not None:
824            pr = self.manager._create_plot_pr()
825            dialog = ExploreDialog(pr, 10, None, -1, "")
826            dialog.ShowModal()
827        else:
828            message = "No data to analyze. Please load a data set to proceed."
829            wx.PostEvent(self.manager.parent, StatusEvent(status=message))
830           
[f3d51f6]831    def _on_invert(self, evt):
832        """
833            Perform inversion
834            @param silent: when True, there will be no output for the user
835        """
836        # Get the data from the form
837        # Push it to the manager
838       
[2a92852]839        flag, alpha, dmax, nfunc, qmin, qmax, height, width = self._read_pars()
[a4bd2ac]840        has_bck = self.bck_chk.IsChecked()
[f3d51f6]841       
842        if flag:
[0d88a09]843            dataset = self.plot_data.GetValue()
[9bc4ed23]844            if dataset==None or len(dataset.strip())==0:
[0d88a09]845                message = "No data to invert. Select a data set before proceeding with P(r) inversion."
846                wx.PostEvent(self.manager.parent, StatusEvent(status=message))
[f3d51f6]847            else:
[0d88a09]848                self.manager.setup_plot_inversion(alpha=alpha, nfunc=nfunc, 
849                                                  d_max=dmax,
850                                                  q_min=qmin, q_max=qmax,
851                                                  bck=has_bck,
852                                                  height=height,
853                                                  width=width)
[f3d51f6]854        else:
855            message = "The P(r) form contains invalid values: please submit it again."
856            wx.PostEvent(self.parent, StatusEvent(status=message))
857       
[5d98370]858    def _change_file(self, evt=None, filepath=None):
[f3d51f6]859        """
860            Choose a new input file for I(q)
861        """
862        import os
863        if not self.manager==None:
[5d98370]864            path = self.manager.choose_file(path=filepath)
[f3d51f6]865           
866            if path and os.path.isfile(path):
[0d88a09]867                self.plot_data.SetValue(str(path))
[ceaf16e]868                try:
869                    self.manager.show_data(path, reset=True)
870                    self._on_pars_changed(None)
871               
872                    # Perform inversion
873                    if self.standalone == True:
874                        self._on_invert(None)
875                except:
876                    # Invalid data
877                    logging.error("InversionControl._change_file: %s" % sys.exc_value)                   
[119a11d]878
879class HelpDialog(wx.Dialog):
880    def __init__(self, parent, id):
881        from sans.pr.invertor import help
[fc4ab6e]882        wx.Dialog.__init__(self, parent, id, size=(400, 420))
[119a11d]883        self.SetTitle("P(r) help") 
884       
885
886        vbox = wx.BoxSizer(wx.VERTICAL)
887
888        explanation = help()
889           
[fc4ab6e]890        label_explain = wx.StaticText(self, -1, explanation, size=(350,320))
[119a11d]891           
892        vbox.Add(label_explain, 0, wx.ALL|wx.EXPAND, 15)
893
894
895        static_line = wx.StaticLine(self, -1)
896        vbox.Add(static_line, 0, wx.EXPAND, 0)
897       
898        button_OK = wx.Button(self, wx.ID_OK, "OK")
899        #button_Cancel = wx.Button(self, wx.ID_CANCEL, "Cancel")
900       
901        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
902        sizer_button.Add((20, 20), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
903        sizer_button.Add(button_OK, 0, wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 10)
904        #sizer_button.Add(button_Cancel, 0, wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 10)       
905        vbox.Add(sizer_button, 0, wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
906
907        self.SetSizer(vbox)
908        self.SetAutoLayout(True)
909       
910        self.Layout()
911        self.Centre()
912
[b659551]913class PrDistDialog(wx.Dialog):
914    """
915        Property dialog to let the user change the number
916        of points on the P(r) plot.
917    """
918    def __init__(self, parent, id):
919        from sans.pr.invertor import help
920        wx.Dialog.__init__(self, parent, id, size=(250, 120))
921        self.SetTitle("P(r) distribution") 
922       
923
924        vbox = wx.BoxSizer(wx.VERTICAL)
925       
926        label_npts = wx.StaticText(self, -1, "Number of points")
[9ff861b]927        self.npts_ctl = PrTextCtrl(self, -1, size=(100,20))
[b659551]928                 
929        pars_sizer = wx.GridBagSizer(5,5)
930        iy = 0
931        pars_sizer.Add(label_npts,      (iy,0), (1,1), wx.LEFT, 15)
932        pars_sizer.Add(self.npts_ctl,   (iy,1), (1,1), wx.RIGHT, 0)
933       
934        vbox.Add(pars_sizer, 0, wx.ALL|wx.EXPAND, 15)
935
936
937        static_line = wx.StaticLine(self, -1)
938        vbox.Add(static_line, 0, wx.EXPAND, 0)
939       
940        button_OK = wx.Button(self, wx.ID_OK, "OK")
941        self.Bind(wx.EVT_BUTTON, self._checkValues, button_OK)
942        button_Cancel = wx.Button(self, wx.ID_CANCEL, "Cancel")
943       
944        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
945        sizer_button.Add((20, 20), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
946        sizer_button.Add(button_OK, 0, wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 10)
947        sizer_button.Add(button_Cancel, 0, wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 10)       
948        vbox.Add(sizer_button, 0, wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
949
950        self.SetSizer(vbox)
951        self.SetAutoLayout(True)
952       
953        self.Layout()
954        self.Centre()
955
956    def _checkValues(self, event):
957        """
958            Check the dialog content.
959        """
960        flag = True
961        try:
962            int(self.npts_ctl.GetValue())
963            self.npts_ctl.SetBackgroundColour(wx.WHITE)
964            self.npts_ctl.Refresh()
965        except:
966            flag = False
967            self.npts_ctl.SetBackgroundColour("pink")
968            self.npts_ctl.Refresh()
969        if flag:
970            event.Skip(True)
971
972    def get_content(self):
973        """
974            Return the content of the dialog.
975            At this point the values have already been
976            checked.
977        """
978        value = int(self.npts_ctl.GetValue())
979        return value
980   
981    def set_content(self, npts):
982        """
983            Initialize the content of the dialog.
984        """
985        self.npts_ctl.SetValue("%i" % npts)
986
[f3d51f6]987##### testing code ############################################################
988class TestPlot:
989    def __init__(self, text):
990        self.name = text
991   
992class MyApp(wx.App):
993    def OnInit(self):
994        wx.InitAllImageHandlers()
[b659551]995        dialog = PrDistDialog(None, -1)
[f3d51f6]996        if dialog.ShowModal() == wx.ID_OK:
[119a11d]997            pass
[f3d51f6]998        dialog.Destroy()
999       
1000        return 1
1001
1002# end of class MyApp
1003
1004if __name__ == "__main__":
1005    app = MyApp(0)
1006    app.MainLoop()
1007   
1008##### end of testing code #####################################################   
Note: See TracBrowser for help on using the repository browser.