source: sasview/prview/perspectives/pr/inversion_panel.py @ 91d910d

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 91d910d was 91d910d, checked in by Gervaise Alina <gervyh@…>, 13 years ago

working on delete plot

  • Property mode set to 100644
File size: 39.5 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 clear_panel(self):
712        """
713        """
714        self.plot_data.SetValue("")
715        self.on_reset(event=None)
716       
717    def on_reset(self, event=None):
718        """
719        Resets inversion parameters
720        """
721        self.nfunc = self._manager.DEFAULT_NFUNC
722        self.d_max = self._manager.DEFAULT_DMAX
723        self.alpha = self._manager.DEFAULT_ALPHA
724        self.qmin_ctl.SetValue("")
725        self.qmax_ctl.SetValue("")
726        self.time_ctl.SetValue("")
727        self.rg_ctl.SetValue("")
728        self.iq0_ctl.SetValue("")
729        self.bck_ctl.SetValue("")
730        self.chi2_ctl.SetValue("")
731        self.osc_ctl.SetValue("")
732        self.pos_ctl.SetValue("")
733        self.pos_err_ctl.SetValue("")
734        self.alpha_estimate_ctl.Enable(False)
735        self.alpha_estimate_ctl.SetLabel("")
736        self.nterms_estimate_ctl.Enable(False)
737        self.nterms_estimate_ctl.SetLabel("")
738        self._on_pars_changed()
739       
740    def _on_pars_changed(self, evt=None):
741        """
742        Called when an input parameter has changed
743        We will estimate the alpha parameter behind the
744        scenes.
745        """
746        flag, alpha, dmax, nfunc, qmin, qmax, height, width = self._read_pars()
747        has_bck = self.bck_chk.IsChecked()
748       
749        # If the pars are valid, estimate alpha
750        if flag:
751            self.nterms_estimate_ctl.Enable(False)
752            self.alpha_estimate_ctl.Enable(False)
753           
754            dataset = self.plot_data.GetValue()
755            if dataset.strip() != "":
756                self._manager.estimate_plot_inversion(alpha=alpha, nfunc=nfunc, 
757                                                 d_max=dmax,
758                                                 q_min=qmin, q_max=qmax,
759                                                 bck=has_bck, 
760                                                 height=height,
761                                                 width=width)
762       
763    def _read_pars(self, evt=None):
764        """
765        """   
766        alpha = 0
767        nfunc = 5
768        dmax  = 120
769        qmin  = 0
770        qmax  = 0
771        height = 0
772        width  = 0
773        flag = True
774        # Read slit height
775        try:
776            height_str = self.sheight_ctl.GetValue()
777            if len(height_str.lstrip().rstrip()) == 0:
778                height = 0
779            else:
780                height = float(height_str)
781                self.sheight_ctl.SetBackgroundColour(wx.WHITE)
782                self.sheight_ctl.Refresh()
783        except:
784            flag = False
785            self.sheight_ctl.SetBackgroundColour("pink")
786            self.sheight_ctl.Refresh()
787           
788        # Read slit width
789        try:
790            width_str = self.swidth_ctl.GetValue()
791            if len(width_str.lstrip().rstrip())==0:
792                width = 0
793            else:
794                width = float(width_str)
795                self.swidth_ctl.SetBackgroundColour(wx.WHITE)
796                self.swidth_ctl.Refresh()
797        except:
798            flag = False
799            self.swidth_ctl.SetBackgroundColour("pink")
800            self.swidth_ctl.Refresh()
801       
802        # Read alpha
803        try:
804            alpha = float(self.alpha_ctl.GetValue())
805            self.alpha_ctl.SetBackgroundColour(wx.WHITE)
806            self.alpha_ctl.Refresh()
807        except:
808            flag = False
809            self.alpha_ctl.SetBackgroundColour("pink")
810            self.alpha_ctl.Refresh()
811       
812        # Read d_max   
813        try:
814            dmax = float(self.dmax_ctl.GetValue())
815            self.dmax_ctl.SetBackgroundColour(wx.WHITE)
816            self.dmax_ctl.Refresh()
817        except:
818            flag = False
819            self.dmax_ctl.SetBackgroundColour("pink")
820            self.dmax_ctl.Refresh()
821           
822        # Read nfunc
823        try:
824            nfunc = int(self.nfunc_ctl.GetValue())
825            npts = self._manager.get_npts()
826            if npts > 0 and nfunc > npts:
827                message = "Number of function terms should be smaller "
828                message += "than the number of points"
829                wx.PostEvent(self._manager.parent, StatusEvent(status=message))
830                raise ValueError, message
831            self.nfunc_ctl.SetBackgroundColour(wx.WHITE)
832            self.nfunc_ctl.Refresh()
833        except:
834            flag = False
835            self.nfunc_ctl.SetBackgroundColour("pink")
836            self.nfunc_ctl.Refresh()
837       
838        # Read qmin
839        try:
840            qmin_str = self.qmin_ctl.GetValue()
841            if len(qmin_str.lstrip().rstrip()) == 0:
842                qmin = None
843            else:
844                qmin = float(qmin_str)
845                self.qmin_ctl.SetBackgroundColour(wx.WHITE)
846                self.qmin_ctl.Refresh()
847        except:
848            flag = False
849            self.qmin_ctl.SetBackgroundColour("pink")
850            self.qmin_ctl.Refresh()
851       
852        # Read qmax
853        try:
854            qmax_str = self.qmax_ctl.GetValue()
855            if len(qmax_str.lstrip().rstrip()) == 0:
856                qmax = None
857            else:
858                qmax = float(qmax_str)
859                self.qmax_ctl.SetBackgroundColour(wx.WHITE)
860                self.qmax_ctl.Refresh()
861        except:
862            flag = False
863            self.qmax_ctl.SetBackgroundColour("pink")
864            self.qmax_ctl.Refresh()
865       
866        return flag, alpha, dmax, nfunc, qmin, qmax, height, width
867   
868    def _on_explore(self, evt):
869        """
870        Invoke the d_max exploration dialog
871        """
872        from explore_dialog import ExploreDialog
873        if self._manager._last_pr is not None:
874            pr = self._manager._create_plot_pr()
875            dialog = ExploreDialog(pr, 10, None, -1, "")
876            dialog.ShowModal()
877        else:
878            message = "No data to analyze. Please load a data set to proceed."
879            wx.PostEvent(self._manager.parent, StatusEvent(status=message))
880           
881    def _on_invert(self, evt):
882        """
883        Perform inversion
884       
885        :param silent: when True, there will be no output for the user
886       
887        """
888        # Get the data from the form
889        # Push it to the manager
890       
891        flag, alpha, dmax, nfunc, qmin, qmax, height, width = self._read_pars()
892        has_bck = self.bck_chk.IsChecked()
893       
894        if flag:
895            dataset = self.plot_data.GetValue()
896            if dataset==None or len(dataset.strip())==0:
897                message = "No data to invert. Select a data set before"
898                message += " proceeding with P(r) inversion."
899                wx.PostEvent(self._manager.parent, StatusEvent(status=message))
900            else:
901                self._manager.setup_plot_inversion(alpha=alpha, nfunc=nfunc, 
902                                                  d_max=dmax,
903                                                  q_min=qmin, q_max=qmax,
904                                                  bck=has_bck,
905                                                  height=height,
906                                                  width=width)
907        else:
908            message = "The P(r) form contains invalid values: "
909            message += "please submit it again."
910            wx.PostEvent(self.parent, StatusEvent(status=message))
911       
912    def _change_file(self, evt=None, filepath=None, data=None):
913        """
914        Choose a new input file for I(q)
915        """
916        if not self._manager is None:
917            self.plot_data.SetValue(str(data.name))
918            try:
919                self._manager.show_data(data=data, reset=True)
920                self._on_pars_changed(None)
921                self._on_invert(None)
922            except:
923                msg = "InversionControl._change_file: %s" % sys.exc_value
924                logging.error(msg)                   
925
926class HelpDialog(wx.Dialog):
927    """
928    """
929    def __init__(self, parent, id):
930        """
931        """
932        from sans.pr.invertor import help
933        wx.Dialog.__init__(self, parent, id, size=(400, 420))
934        self.SetTitle("P(r) help") 
935       
936
937        vbox = wx.BoxSizer(wx.VERTICAL)
938
939        explanation = help()
940           
941        label_explain = wx.StaticText(self, -1, explanation, size=(350, 320))
942           
943        vbox.Add(label_explain, 0, wx.ALL|wx.EXPAND, 15)
944
945
946        static_line = wx.StaticLine(self, -1)
947        vbox.Add(static_line, 0, wx.EXPAND, 0)
948       
949        button_OK = wx.Button(self, wx.ID_OK, "OK")
950        #button_Cancel = wx.Button(self, wx.ID_CANCEL, "Cancel")
951       
952        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
953        sizer_button.Add((20, 20), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
954        sizer_button.Add(button_OK, 0, wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 10)
955       
956        vbox.Add(sizer_button, 0, wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
957
958        self.SetSizer(vbox)
959        self.SetAutoLayout(True)
960       
961        self.Layout()
962        self.Centre()
963
964class PrDistDialog(wx.Dialog):
965    """
966    Property dialog to let the user change the number
967    of points on the P(r) plot.
968    """
969    def __init__(self, parent, id):
970        from sans.pr.invertor import help
971        wx.Dialog.__init__(self, parent, id, size=(250, 120))
972        self.SetTitle("P(r) distribution") 
973       
974
975        vbox = wx.BoxSizer(wx.VERTICAL)
976       
977        label_npts = wx.StaticText(self, -1, "Number of points")
978        self.npts_ctl = PrTextCtrl(self, -1, size=(100, 20))
979                 
980        pars_sizer = wx.GridBagSizer(5, 5)
981        iy = 0
982        pars_sizer.Add(label_npts, (iy, 0), (1, 1), wx.LEFT, 15)
983        pars_sizer.Add(self.npts_ctl, (iy, 1), (1, 1), wx.RIGHT, 0)
984       
985        vbox.Add(pars_sizer, 0, wx.ALL|wx.EXPAND, 15)
986
987        static_line = wx.StaticLine(self, -1)
988        vbox.Add(static_line, 0, wx.EXPAND, 0)
989       
990        button_OK = wx.Button(self, wx.ID_OK, "OK")
991        self.Bind(wx.EVT_BUTTON, self._checkValues, button_OK)
992        button_Cancel = wx.Button(self, wx.ID_CANCEL, "Cancel")
993       
994        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
995        sizer_button.Add((20, 20), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
996        sizer_button.Add(button_OK, 0, wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 10)
997        sizer_button.Add(button_Cancel, 0,
998                         wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 10)       
999        vbox.Add(sizer_button, 0, wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1000
1001        self.SetSizer(vbox)
1002        self.SetAutoLayout(True)
1003       
1004        self.Layout()
1005        self.Centre()
1006
1007    def _checkValues(self, event):
1008        """
1009        Check the dialog content.
1010        """
1011        flag = True
1012        try:
1013            int(self.npts_ctl.GetValue())
1014            self.npts_ctl.SetBackgroundColour(wx.WHITE)
1015            self.npts_ctl.Refresh()
1016        except:
1017            flag = False
1018            self.npts_ctl.SetBackgroundColour("pink")
1019            self.npts_ctl.Refresh()
1020        if flag:
1021            event.Skip(True)
1022
1023    def get_content(self):
1024        """
1025        Return the content of the dialog.
1026        At this point the values have already been
1027        checked.
1028        """
1029        value = int(self.npts_ctl.GetValue())
1030        return value
1031   
1032    def set_content(self, npts):
1033        """
1034        Initialize the content of the dialog.
1035        """
1036        self.npts_ctl.SetValue("%i" % npts)
1037
1038##### testing code ############################################################
1039"""
1040Example: ::
1041
1042class TestPlot:
1043    def __init__(self, text):
1044        self.name = text
1045   
1046class MyApp(wx.App):
1047    def OnInit(self):
1048        wx.InitAllImageHandlers()
1049        dialog = PrDistDialog(None, -1)
1050        if dialog.ShowModal() == wx.ID_OK:
1051            pass
1052        dialog.Destroy()
1053       
1054        return 1
1055
1056# end of class MyApp
1057
1058if __name__ == "__main__":
1059    app = MyApp(0)
1060    app.MainLoop()
1061   
1062"""
1063##### end of testing code #####################################################   
Note: See TracBrowser for help on using the repository browser.