source: sasview/prview/perspectives/pr/inversion_panel.py @ 8a7d922

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 8a7d922 was dd03efb, checked in by Gervaise Alina <gervyh@…>, 14 years ago

make sure pr is compute on set state

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