source: sasview/prview/perspectives/pr/inversion_panel.py @ 3cd5806

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 3cd5806 was 3f0351c6, checked in by Gervaise Alina <gervyh@…>, 14 years ago

bind prview button with guiframe toolbar

  • Property mode set to 100644
File size: 40.0 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        if self.standalone == False:
366            self._on_invert(None)   
367       
368    def set_manager(self, manager):
369        self._manager = manager
370       
371    def _do_layout(self):
372        vbox = wx.GridBagSizer(0,0)
373        iy_vb = 0
374
375        # ----- I(q) data -----
376        databox = wx.StaticBox(self, -1, "I(q) data source")
377       
378        boxsizer1 = wx.StaticBoxSizer(databox, wx.VERTICAL)
379        boxsizer1.SetMinSize((self._default_width, 50))
380        pars_sizer = wx.GridBagSizer(5, 5)
381
382        iy = 0
383        self.file_radio = wx.StaticText(self, -1, "Data:")
384        pars_sizer.Add(self.file_radio, (iy, 0), (1, 1),
385                       wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
386       
387        self.plot_data = DataFileTextCtrl(self, -1, size=(260,20))
388       
389        pars_sizer.Add(self.plot_data, (iy, 1), (1, 1),
390                       wx.EXPAND|wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 15)
391       
392        self.bck_chk = wx.CheckBox(self, -1, "Estimate background level")
393        hint_msg = "Check box to let the fit estimate "
394        hint_msg += "the constant background level."
395        self.bck_chk.SetToolTipString(hint_msg)
396        self.bck_chk.Bind(wx.EVT_CHECKBOX, self._on_pars_changed)
397        iy += 1
398        pars_sizer.Add(self.bck_chk, (iy, 0), (1, 2),
399                       wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
400        boxsizer1.Add(pars_sizer, 0, wx.EXPAND) 
401        vbox.Add(boxsizer1, (iy_vb, 0), (1, 1),
402                 wx.LEFT|wx.RIGHT|wx.EXPAND|wx.ADJUST_MINSIZE|wx.TOP, 5)
403       
404        # ----- Add slit parameters -----
405        if True:
406            sbox = wx.StaticBox(self, -1, "Slit parameters")
407            sboxsizer = wx.StaticBoxSizer(sbox, wx.VERTICAL)
408            sboxsizer.SetMinSize((self._default_width, 20))
409           
410            sizer_slit = wx.GridBagSizer(5, 5)
411   
412            label_sheight = wx.StaticText(self, -1, "Height", size=(40, 20))
413            label_swidth = wx.StaticText(self, -1, "Width", size=(40, 20))
414            #label_sunits1 = wx.StaticText(self, -1, "[A^(-1)]")
415            label_sunits2 = wx.StaticText(self, -1, "[A^(-1)]", size=(55,20))
416            self.sheight_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER,
417                                          size=(60,20))
418            self.swidth_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER,
419                                         size=(60,20))
420            hint_msg = "Enter slit height in units of Q or leave blank."
421            self.sheight_ctl.SetToolTipString(hint_msg)
422            hint_msg = "Enter slit width in units of Q or leave blank."
423            self.swidth_ctl.SetToolTipString(hint_msg)
424            #self.sheight_ctl.Bind(wx.EVT_TEXT, self._on_pars_changed)
425            #self.swidth_ctl.Bind(wx.EVT_TEXT,  self._on_pars_changed)
426           
427            iy = 0
428            sizer_slit.Add(label_sheight,    (iy, 0), (1, 1),
429                           wx.LEFT|wx.EXPAND, 5)
430            sizer_slit.Add(self.sheight_ctl, (iy, 1), (1, 1),
431                           wx.LEFT|wx.EXPAND, 5)
432   
433            sizer_slit.Add(label_swidth,     (iy, 2), (1, 1),
434                           wx.LEFT|wx.EXPAND, 5)
435            sizer_slit.Add(self.swidth_ctl,  (iy, 3), (1, 1),
436                           wx.LEFT|wx.EXPAND, 5)
437            sizer_slit.Add(label_sunits2,    (iy, 4), (1, 1),
438                           wx.LEFT|wx.EXPAND, 5)
439           
440            sboxsizer.Add(sizer_slit, wx.TOP, 15)
441            iy_vb += 1
442            vbox.Add(sboxsizer, (iy_vb, 0), (1, 1),
443                     wx.LEFT|wx.RIGHT|wx.EXPAND|wx.ADJUST_MINSIZE, 5)
444       
445       
446        # ----- Q range -----
447        qbox = wx.StaticBox(self, -1, "Q range")
448        qboxsizer = wx.StaticBoxSizer(qbox, wx.VERTICAL)
449        qboxsizer.SetMinSize((self._default_width, 20))
450       
451        sizer_q = wx.GridBagSizer(5, 5)
452
453        label_qmin = wx.StaticText(self, -1, "Q min", size=(40, 20))
454        label_qmax = wx.StaticText(self, -1, "Q max", size=(40, 20))
455        #label_qunits1 = wx.StaticText(self, -1, "[A^(-1)]")
456        label_qunits2 = wx.StaticText(self, -1, "[A^(-1)]", size=(55, 20))
457        self.qmin_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER,
458                                   size=(60,20))
459        self.qmax_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER,
460                                   size=(60,20))
461        hint_msg = "Select a lower bound for Q or leave blank."
462        self.qmin_ctl.SetToolTipString(hint_msg)
463        hint_msg = "Select an upper bound for Q or leave blank."
464        self.qmax_ctl.SetToolTipString(hint_msg)
465        self.qmin_ctl.Bind(wx.EVT_TEXT, self._on_pars_changed)
466        self.qmax_ctl.Bind(wx.EVT_TEXT, self._on_pars_changed)
467       
468        iy = 0
469        sizer_q.Add(label_qmin,    (iy, 0), (1, 1), wx.LEFT|wx.EXPAND, 5)
470        sizer_q.Add(self.qmin_ctl, (iy, 1), (1, 1), wx.LEFT|wx.EXPAND, 5)
471        #sizer_q.Add(label_qunits1, (iy,2), (1,1), wx.LEFT|wx.EXPAND, 15)
472        sizer_q.Add(label_qmax,    (iy, 2), (1, 1), wx.LEFT|wx.EXPAND, 5)
473        sizer_q.Add(self.qmax_ctl, (iy, 3), (1, 1), wx.LEFT|wx.EXPAND, 5)
474        sizer_q.Add(label_qunits2, (iy, 4), (1, 1), wx.LEFT|wx.EXPAND, 5)
475        qboxsizer.Add(sizer_q, wx.TOP, 15)
476
477        iy_vb += 1
478        vbox.Add(qboxsizer, (iy_vb,0), (1,1),
479                 wx.LEFT|wx.RIGHT|wx.EXPAND|wx.ADJUST_MINSIZE, 5)
480   
481        # ----- Parameters -----
482        parsbox = wx.StaticBox(self, -1, "Parameters")
483        boxsizer2 = wx.StaticBoxSizer(parsbox, wx.VERTICAL)
484        boxsizer2.SetMinSize((self._default_width,50))
485       
486        explanation  = "P(r) is found by fitting a set of base functions"
487        explanation += " to I(Q). The minimization involves"
488        explanation += " a regularization term to ensure a smooth P(r)."
489        explanation += " The regularization constant gives the size of that " 
490        explanation += "term. The suggested value is the value above which the"
491        explanation += " output P(r) will have only one peak."
492        label_explain = wx.StaticText(self, -1, explanation, size=(280,120))
493        boxsizer2.Add(label_explain,  wx.LEFT|wx.BOTTOM, 5)
494       
495       
496       
497        label_nfunc = wx.StaticText(self, -1, "Number of terms")
498        label_nfunc.SetMinSize((120, 20))
499        label_alpha = wx.StaticText(self, -1, "Regularization constant")
500        label_dmax  = wx.StaticText(self, -1, "Max distance [A]")
501        self.label_sugg  = wx.StaticText(self, -1, "Suggested value")
502        #self.label_sugg.Hide()
503       
504        self.nfunc_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER,
505                                    size=(60,20))
506        self.nfunc_ctl.SetToolTipString("Number of terms in the expansion.")
507        self.alpha_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER,
508                                    size=(60,20))
509        hint_msg = "Control parameter for the size of the regularization term."
510        self.alpha_ctl.SetToolTipString(hint_msg)
511        self.dmax_ctl  = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER,
512                                    size=(60,20))
513        hint_msg = "Maximum distance between any two points in the system."
514        self.dmax_ctl.SetToolTipString(hint_msg)
515        id = wx.NewId()
516        self.alpha_estimate_ctl  = wx.Button(self, id, "")
517        #self.alpha_estimate_ctl.Hide()
518        self.Bind(wx.EVT_BUTTON, self._on_accept_alpha, id = id)   
519        self.alpha_estimate_ctl.Enable(False)
520        #self.alpha_estimate_ctl.SetBackgroundColour('#ffdf85')
521        #self.alpha_estimate_ctl.SetBackgroundColour(self.GetBackgroundColour())
522        self.alpha_estimate_ctl.SetToolTipString("Waiting for estimate...")
523       
524        id = wx.NewId()
525        self.nterms_estimate_ctl  = wx.Button(self, id, "")
526        #self.nterms_estimate_ctl.Hide()
527        self.Bind(wx.EVT_BUTTON, self._on_accept_nterms, id = id)   
528        self.nterms_estimate_ctl.Enable(False)
529     
530        self.nterms_estimate_ctl.SetToolTipString("Waiting for estimate...")
531       
532        self.nfunc_ctl.Bind(wx.EVT_TEXT, self._read_pars)
533        self.alpha_ctl.Bind(wx.EVT_TEXT, self._read_pars)
534        self.dmax_ctl.Bind(wx.EVT_TEXT, self._on_pars_changed)
535       
536        # Distance explorator
537        id = wx.NewId()
538        self.distance_explorator_ctl = wx.Button(self, id, "Explore")
539        self.Bind(wx.EVT_BUTTON, self._on_explore, id = id)           
540       
541       
542        sizer_params = wx.GridBagSizer(5,5)
543
544        iy = 0
545        sizer_params.Add(self.label_sugg, (iy, 2), (1, 1), wx.LEFT, 15)
546        iy += 1
547        sizer_params.Add(label_nfunc,      (iy, 0), (1, 1), wx.LEFT, 15)
548        sizer_params.Add(self.nfunc_ctl,   (iy, 1), (1, 1), wx.RIGHT, 0)
549        sizer_params.Add(self.nterms_estimate_ctl, (iy, 2), (1, 1), wx.LEFT, 15)
550        iy += 1
551        sizer_params.Add(label_alpha,      (iy, 0), (1, 1), wx.LEFT, 15)
552        sizer_params.Add(self.alpha_ctl,   (iy, 1), (1, 1), wx.RIGHT, 0)
553        sizer_params.Add(self.alpha_estimate_ctl, (iy, 2), (1, 1), wx.LEFT, 15)
554        iy += 1
555        sizer_params.Add(label_dmax, (iy, 0), (1, 1), wx.LEFT, 15)
556        sizer_params.Add(self.dmax_ctl,   (iy, 1), (1, 1), wx.RIGHT, 0)
557        sizer_params.Add(self.distance_explorator_ctl, (iy, 2),
558                         (1, 1), wx.LEFT, 15)
559
560        boxsizer2.Add(sizer_params, 0)
561       
562        iy_vb += 1
563        vbox.Add(boxsizer2, (iy_vb, 0), (1, 1),
564                 wx.LEFT|wx.RIGHT|wx.EXPAND|wx.ADJUST_MINSIZE, 5)
565
566
567        # ----- Results -----
568        resbox = wx.StaticBox(self, -1, "Outputs")
569        ressizer = wx.StaticBoxSizer(resbox, wx.VERTICAL)
570        ressizer.SetMinSize((self._default_width, 50))
571       
572        label_rg       = wx.StaticText(self, -1, "Rg")
573        label_rg_unit  = wx.StaticText(self, -1, "[A]")
574        label_iq0      = wx.StaticText(self, -1, "I(Q=0)")
575        label_iq0_unit = wx.StaticText(self, -1, "[A^(-1)]")
576        label_bck      = wx.StaticText(self, -1, "Background")
577        label_bck_unit = wx.StaticText(self, -1, "[A^(-1)]")
578        self.rg_ctl    = OutputTextCtrl(self, -1, size=(60,20))
579        hint_msg = "Radius of gyration for the computed P(r)."
580        self.rg_ctl.SetToolTipString(hint_msg)
581        self.iq0_ctl   = OutputTextCtrl(self, -1, size=(60, 20))
582        hint_msg = "Scattering intensity at Q=0 for the computed P(r)."
583        self.iq0_ctl.SetToolTipString(hint_msg)
584        self.bck_ctl   = OutputTextCtrl(self, -1, size=(60, 20))
585        self.bck_ctl.SetToolTipString("Value of estimated constant background.")
586       
587        label_time = wx.StaticText(self, -1, "Computation time")
588        label_time_unit = wx.StaticText(self, -1, "secs")
589        label_time.SetMinSize((120,20))
590        label_chi2 = wx.StaticText(self, -1, "Chi2/dof")
591        label_osc = wx.StaticText(self, -1, "Oscillations")
592        label_pos = wx.StaticText(self, -1, "Positive fraction")
593        label_pos_err = wx.StaticText(self, -1, "1-sigma positive fraction")
594       
595        self.time_ctl = OutputTextCtrl(self, -1, size=(60, 20))
596        hint_msg = "Computation time for the last inversion, in seconds."
597        self.time_ctl.SetToolTipString(hint_msg)
598       
599        self.chi2_ctl = OutputTextCtrl(self, -1, size=(60, 20))
600        self.chi2_ctl.SetToolTipString("Chi^2 over degrees of freedom.")
601       
602        # Oscillation parameter
603        self.osc_ctl = OutputTextCtrl(self, -1, size=(60, 20))
604        hint_msg = "Oscillation parameter. P(r) for a sphere has an "
605        hint_msg += " oscillation parameter of 1.1."
606        self.osc_ctl.SetToolTipString(hint_msg)
607       
608        # Positive fraction figure of merit
609        self.pos_ctl = OutputTextCtrl(self, -1, size=(60, 20))
610        hint_msg = "Fraction of P(r) that is positive. "
611        hint_msg += "Theoretically, P(r) is defined positive."
612        self.pos_ctl.SetToolTipString(hint_msg)
613       
614        # 1-simga positive fraction figure of merit
615        self.pos_err_ctl = OutputTextCtrl(self, -1, size=(60, 20))
616        message  = "Fraction of P(r) that is at least 1 standard deviation"
617        message += " greater than zero.\n"
618        message += "This figure of merit tells you about the size of the "
619        message += "P(r) errors.\n"
620        message += "If it is close to 1 and the other figures of merit are bad,"
621        message += " consider changing the maximum distance."
622        self.pos_err_ctl.SetToolTipString(message)
623       
624        sizer_res = wx.GridBagSizer(5, 5)
625
626        iy = 0
627        sizer_res.Add(label_rg, (iy, 0), (1, 1), wx.LEFT|wx.EXPAND, 15)
628        sizer_res.Add(self.rg_ctl,   (iy, 1), (1, 1), wx.RIGHT|wx.EXPAND, 15)
629        sizer_res.Add(label_rg_unit,   (iy, 2), (1, 1), wx.RIGHT|wx.EXPAND, 15)
630        iy += 1
631        sizer_res.Add(label_iq0, (iy, 0), (1, 1), wx.LEFT|wx.EXPAND, 15)
632        sizer_res.Add(self.iq0_ctl,   (iy, 1), (1, 1), wx.RIGHT|wx.EXPAND, 15)
633        sizer_res.Add(label_iq0_unit, (iy, 2), (1, 1), wx.RIGHT|wx.EXPAND, 15)
634        iy += 1
635        sizer_res.Add(label_bck, (iy, 0), (1, 1), wx.LEFT|wx.EXPAND, 15)
636        sizer_res.Add(self.bck_ctl, (iy, 1), (1, 1), wx.RIGHT|wx.EXPAND, 15)
637        sizer_res.Add(label_bck_unit, (iy, 2), (1, 1), wx.RIGHT|wx.EXPAND, 15)
638        iy += 1
639        sizer_res.Add(label_time, (iy, 0), (1, 1), wx.LEFT|wx.EXPAND, 15)
640        sizer_res.Add(self.time_ctl,   (iy, 1), (1, 1), wx.RIGHT|wx.EXPAND, 15)
641        sizer_res.Add(label_time_unit, (iy, 2), (1, 1), wx.RIGHT|wx.EXPAND, 15)
642        iy += 1
643        sizer_res.Add(label_chi2, (iy, 0), (1, 1), wx.LEFT|wx.EXPAND, 15)
644        sizer_res.Add(self.chi2_ctl,   (iy, 1), (1, 1), wx.RIGHT|wx.EXPAND, 15)
645        iy += 1
646        sizer_res.Add(label_osc, (iy, 0), (1, 1), wx.LEFT|wx.EXPAND, 15)
647        sizer_res.Add(self.osc_ctl,   (iy, 1), (1, 1), wx.RIGHT|wx.EXPAND, 15)
648
649        iy += 1
650        sizer_res.Add(label_pos, (iy, 0), (1, 1), wx.LEFT|wx.EXPAND, 15)
651        sizer_res.Add(self.pos_ctl, (iy, 1), (1, 1), wx.RIGHT|wx.EXPAND, 15)
652
653        iy += 1
654        sizer_res.Add(label_pos_err, (iy, 0), (1, 1), wx.LEFT|wx.EXPAND, 15)
655        sizer_res.Add(self.pos_err_ctl,  (iy,1), (1, 1), wx.RIGHT|wx.EXPAND, 15)
656
657        ressizer.Add(sizer_res, 0)
658        iy_vb += 1
659        vbox.Add(ressizer, (iy_vb, 0), (1, 1),
660                 wx.LEFT|wx.RIGHT|wx.EXPAND|wx.ADJUST_MINSIZE, 5)
661
662        # ----- Buttons -----
663        id = wx.NewId()
664        button_OK = wx.Button(self, id, "Compute")
665        button_OK.SetToolTipString("Perform P(r) inversion.")
666        self.Bind(wx.EVT_BUTTON, self._on_invert, id = id)   
667       
668        id = wx.NewId()
669        button_Reset = wx.Button(self, id, "Reset")
670        button_Reset.SetToolTipString("Reset inversion parameters to default.")
671        self.Bind(wx.EVT_BUTTON, self._on_reset, id = id)   
672        #button_Cancel = wx.Button(self, wx.ID_CANCEL, "Cancel")
673       
674        id = wx.NewId()
675        button_Save = wx.Button(self, id, "Save")
676        button_Save.SetToolTipString("Save the current P(r) work to file.")
677        self.Bind(wx.EVT_BUTTON, self.on_save, id=id)   
678        self._set_save_flag(True)
679        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
680        sizer_button.Add((20, 20), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
681        sizer_button.Add(button_Save, 0, wx.LEFT|wx.ADJUST_MINSIZE, 10)
682        sizer_button.Add(button_Reset, 0, wx.LEFT|wx.ADJUST_MINSIZE, 10)
683        sizer_button.Add(button_OK, 0, wx.LEFT|wx.ADJUST_MINSIZE, 10)
684       
685        iy_vb += 1
686        vbox.Add(sizer_button, (iy_vb, 0), (1, 1),
687                 wx.EXPAND|wx.BOTTOM|wx.TOP|wx.RIGHT, 10)
688
689        self.Bind(wx.EVT_TEXT_ENTER, self._on_invert)
690
691        self.SetSizer(vbox)
692       
693    def _on_accept_alpha(self, evt):
694        """
695        User has accepted the estimated alpha,
696        set it as part of the input parameters
697        """
698        try:
699            alpha = self.alpha_estimate_ctl.GetLabel()
700            tmp = float(alpha)
701            self.alpha_ctl.SetValue(alpha)
702        except:
703            # No estimate or bad estimate, either do nothing
704            #import sys
705            print "InversionControl._on_accept_alpha: %s" % sys.exc_value
706            pass
707   
708    def _on_accept_nterms(self, evt):
709        """
710        User has accepted the estimated number of terms,
711        set it as part of the input parameters
712        """
713        try:
714            nterms = self.nterms_estimate_ctl.GetLabel()
715            tmp = float(nterms)
716            self.nfunc_ctl.SetValue(nterms)
717        except:
718            # No estimate or bad estimate, either do nothing
719            import sys
720            print "InversionControl._on_accept_nterms: %s" % sys.exc_value
721            pass
722       
723    def _on_reset(self, evt):
724        """
725        Resets inversion parameters
726        """
727        self.nfunc = self._manager.DEFAULT_NFUNC
728        self.d_max = self._manager.DEFAULT_DMAX
729        self.alpha = self._manager.DEFAULT_ALPHA
730        self.qmin_ctl.SetValue("")
731        self.qmax_ctl.SetValue("")
732        self.time_ctl.SetValue("")
733        self.rg_ctl.SetValue("")
734        self.iq0_ctl.SetValue("")
735        self.bck_ctl.SetValue("")
736        self.chi2_ctl.SetValue("")
737        self.osc_ctl.SetValue("")
738        self.pos_ctl.SetValue("")
739        self.pos_err_ctl.SetValue("")
740        self.alpha_estimate_ctl.Enable(False)
741        self.alpha_estimate_ctl.SetLabel("")
742        self.nterms_estimate_ctl.Enable(False)
743        self.nterms_estimate_ctl.SetLabel("")
744        self._on_pars_changed()
745       
746    def _on_pars_changed(self, evt=None):
747        """
748        Called when an input parameter has changed
749        We will estimate the alpha parameter behind the
750        scenes.
751        """
752        flag, alpha, dmax, nfunc, qmin, qmax, height, width = self._read_pars()
753        has_bck = self.bck_chk.IsChecked()
754       
755        # If the pars are valid, estimate alpha
756        if flag:
757            self.nterms_estimate_ctl.Enable(False)
758            self.alpha_estimate_ctl.Enable(False)
759           
760            dataset = self.plot_data.GetValue()
761            self._manager.estimate_plot_inversion(alpha=alpha, nfunc=nfunc, 
762                                                 d_max=dmax,
763                                                 q_min=qmin, q_max=qmax,
764                                                 bck=has_bck, 
765                                                 height=height,
766                                                 width=width)
767       
768    def _read_pars(self, evt=None):
769        """
770        """   
771        alpha = 0
772        nfunc = 5
773        dmax  = 120
774        qmin  = 0
775        qmax  = 0
776        height = 0
777        width  = 0
778        flag = True
779        # Read slit height
780        try:
781            height_str = self.sheight_ctl.GetValue()
782            if len(height_str.lstrip().rstrip()) == 0:
783                height = 0
784            else:
785                height = float(height_str)
786                self.sheight_ctl.SetBackgroundColour(wx.WHITE)
787                self.sheight_ctl.Refresh()
788        except:
789            flag = False
790            self.sheight_ctl.SetBackgroundColour("pink")
791            self.sheight_ctl.Refresh()
792           
793        # Read slit width
794        try:
795            width_str = self.swidth_ctl.GetValue()
796            if len(width_str.lstrip().rstrip())==0:
797                width = 0
798            else:
799                width = float(width_str)
800                self.swidth_ctl.SetBackgroundColour(wx.WHITE)
801                self.swidth_ctl.Refresh()
802        except:
803            flag = False
804            self.swidth_ctl.SetBackgroundColour("pink")
805            self.swidth_ctl.Refresh()
806       
807        # Read alpha
808        try:
809            alpha = float(self.alpha_ctl.GetValue())
810            self.alpha_ctl.SetBackgroundColour(wx.WHITE)
811            self.alpha_ctl.Refresh()
812        except:
813            flag = False
814            self.alpha_ctl.SetBackgroundColour("pink")
815            self.alpha_ctl.Refresh()
816       
817        # Read d_max   
818        try:
819            dmax = float(self.dmax_ctl.GetValue())
820            self.dmax_ctl.SetBackgroundColour(wx.WHITE)
821            self.dmax_ctl.Refresh()
822        except:
823            flag = False
824            self.dmax_ctl.SetBackgroundColour("pink")
825            self.dmax_ctl.Refresh()
826           
827        # Read nfunc
828        try:
829            nfunc = int(self.nfunc_ctl.GetValue())
830            npts = self._manager.get_npts()
831            if npts > 0 and nfunc > npts:
832                message = "Number of function terms should be smaller "
833                message += "than the number of points"
834                wx.PostEvent(self._manager.parent, StatusEvent(status=message))
835                raise ValueError, message
836            self.nfunc_ctl.SetBackgroundColour(wx.WHITE)
837            self.nfunc_ctl.Refresh()
838        except:
839            flag = False
840            self.nfunc_ctl.SetBackgroundColour("pink")
841            self.nfunc_ctl.Refresh()
842       
843        # Read qmin
844        try:
845            qmin_str = self.qmin_ctl.GetValue()
846            if len(qmin_str.lstrip().rstrip()) == 0:
847                qmin = None
848            else:
849                qmin = float(qmin_str)
850                self.qmin_ctl.SetBackgroundColour(wx.WHITE)
851                self.qmin_ctl.Refresh()
852        except:
853            flag = False
854            self.qmin_ctl.SetBackgroundColour("pink")
855            self.qmin_ctl.Refresh()
856       
857        # Read qmax
858        try:
859            qmax_str = self.qmax_ctl.GetValue()
860            if len(qmax_str.lstrip().rstrip()) == 0:
861                qmax = None
862            else:
863                qmax = float(qmax_str)
864                self.qmax_ctl.SetBackgroundColour(wx.WHITE)
865                self.qmax_ctl.Refresh()
866        except:
867            flag = False
868            self.qmax_ctl.SetBackgroundColour("pink")
869            self.qmax_ctl.Refresh()
870       
871        return flag, alpha, dmax, nfunc, qmin, qmax, height, width
872   
873    def _on_explore(self, evt):
874        """
875        Invoke the d_max exploration dialog
876        """
877        from explore_dialog import ExploreDialog
878        if self._manager._last_pr is not None:
879            pr = self._manager._create_plot_pr()
880            dialog = ExploreDialog(pr, 10, None, -1, "")
881            dialog.ShowModal()
882        else:
883            message = "No data to analyze. Please load a data set to proceed."
884            wx.PostEvent(self._manager.parent, StatusEvent(status=message))
885           
886    def _on_invert(self, evt):
887        """
888        Perform inversion
889       
890        :param silent: when True, there will be no output for the user
891       
892        """
893        # Get the data from the form
894        # Push it to the manager
895       
896        flag, alpha, dmax, nfunc, qmin, qmax, height, width = self._read_pars()
897        has_bck = self.bck_chk.IsChecked()
898       
899        if flag:
900            dataset = self.plot_data.GetValue()
901            if dataset==None or len(dataset.strip())==0:
902                message = "No data to invert. Select a data set before"
903                message += " proceeding with P(r) inversion."
904                wx.PostEvent(self._manager.parent, StatusEvent(status=message))
905            else:
906                self._manager.setup_plot_inversion(alpha=alpha, nfunc=nfunc, 
907                                                  d_max=dmax,
908                                                  q_min=qmin, q_max=qmax,
909                                                  bck=has_bck,
910                                                  height=height,
911                                                  width=width)
912        else:
913            message = "The P(r) form contains invalid values: "
914            message += "please submit it again."
915            wx.PostEvent(self.parent, StatusEvent(status=message))
916       
917    def _change_file(self, evt=None, filepath=None, data=None):
918        """
919        Choose a new input file for I(q)
920        """
921        if not self._manager is None:
922            self.plot_data.SetValue(str(data.name))
923            try:
924                self._manager.show_data(data=data, reset=True)
925                self._on_pars_changed(None)
926                self._on_invert(None)
927            except:
928                msg = "InversionControl._change_file: %s" % sys.exc_value
929                logging.error(msg)                   
930
931class HelpDialog(wx.Dialog):
932    """
933    """
934    def __init__(self, parent, id):
935        """
936        """
937        from sans.pr.invertor import help
938        wx.Dialog.__init__(self, parent, id, size=(400, 420))
939        self.SetTitle("P(r) help") 
940       
941
942        vbox = wx.BoxSizer(wx.VERTICAL)
943
944        explanation = help()
945           
946        label_explain = wx.StaticText(self, -1, explanation, size=(350, 320))
947           
948        vbox.Add(label_explain, 0, wx.ALL|wx.EXPAND, 15)
949
950
951        static_line = wx.StaticLine(self, -1)
952        vbox.Add(static_line, 0, wx.EXPAND, 0)
953       
954        button_OK = wx.Button(self, wx.ID_OK, "OK")
955        #button_Cancel = wx.Button(self, wx.ID_CANCEL, "Cancel")
956       
957        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
958        sizer_button.Add((20, 20), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
959        sizer_button.Add(button_OK, 0, wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 10)
960       
961        vbox.Add(sizer_button, 0, wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
962
963        self.SetSizer(vbox)
964        self.SetAutoLayout(True)
965       
966        self.Layout()
967        self.Centre()
968
969class PrDistDialog(wx.Dialog):
970    """
971    Property dialog to let the user change the number
972    of points on the P(r) plot.
973    """
974    def __init__(self, parent, id):
975        from sans.pr.invertor import help
976        wx.Dialog.__init__(self, parent, id, size=(250, 120))
977        self.SetTitle("P(r) distribution") 
978       
979
980        vbox = wx.BoxSizer(wx.VERTICAL)
981       
982        label_npts = wx.StaticText(self, -1, "Number of points")
983        self.npts_ctl = PrTextCtrl(self, -1, size=(100, 20))
984                 
985        pars_sizer = wx.GridBagSizer(5, 5)
986        iy = 0
987        pars_sizer.Add(label_npts, (iy, 0), (1, 1), wx.LEFT, 15)
988        pars_sizer.Add(self.npts_ctl, (iy, 1), (1, 1), wx.RIGHT, 0)
989       
990        vbox.Add(pars_sizer, 0, wx.ALL|wx.EXPAND, 15)
991
992        static_line = wx.StaticLine(self, -1)
993        vbox.Add(static_line, 0, wx.EXPAND, 0)
994       
995        button_OK = wx.Button(self, wx.ID_OK, "OK")
996        self.Bind(wx.EVT_BUTTON, self._checkValues, button_OK)
997        button_Cancel = wx.Button(self, wx.ID_CANCEL, "Cancel")
998       
999        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
1000        sizer_button.Add((20, 20), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
1001        sizer_button.Add(button_OK, 0, wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 10)
1002        sizer_button.Add(button_Cancel, 0,
1003                         wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 10)       
1004        vbox.Add(sizer_button, 0, wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1005
1006        self.SetSizer(vbox)
1007        self.SetAutoLayout(True)
1008       
1009        self.Layout()
1010        self.Centre()
1011
1012    def _checkValues(self, event):
1013        """
1014        Check the dialog content.
1015        """
1016        flag = True
1017        try:
1018            int(self.npts_ctl.GetValue())
1019            self.npts_ctl.SetBackgroundColour(wx.WHITE)
1020            self.npts_ctl.Refresh()
1021        except:
1022            flag = False
1023            self.npts_ctl.SetBackgroundColour("pink")
1024            self.npts_ctl.Refresh()
1025        if flag:
1026            event.Skip(True)
1027
1028    def get_content(self):
1029        """
1030        Return the content of the dialog.
1031        At this point the values have already been
1032        checked.
1033        """
1034        value = int(self.npts_ctl.GetValue())
1035        return value
1036   
1037    def set_content(self, npts):
1038        """
1039        Initialize the content of the dialog.
1040        """
1041        self.npts_ctl.SetValue("%i" % npts)
1042
1043##### testing code ############################################################
1044"""
1045Example: ::
1046
1047class TestPlot:
1048    def __init__(self, text):
1049        self.name = text
1050   
1051class MyApp(wx.App):
1052    def OnInit(self):
1053        wx.InitAllImageHandlers()
1054        dialog = PrDistDialog(None, -1)
1055        if dialog.ShowModal() == wx.ID_OK:
1056            pass
1057        dialog.Destroy()
1058       
1059        return 1
1060
1061# end of class MyApp
1062
1063if __name__ == "__main__":
1064    app = MyApp(0)
1065    app.MainLoop()
1066   
1067"""
1068##### end of testing code #####################################################   
Note: See TracBrowser for help on using the repository browser.