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

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 f8b79d6 was 9ff861b, checked in by Mathieu Doucet <doucetm@…>, 15 years ago

prview: improved text control look&feel, added scrolled panel, got rid of old code.

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