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

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 cacbd7d was 75df58b, checked in by Gervaise Alina <gervyh@…>, 14 years ago

working on loading for prview

  • Property mode set to 100644
File size: 39.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.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)
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 _save_state(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_state(self):
262        """
263        Get the current state
264       
265        : return: state object
266        """
267        # Construct the state object   
268        state = InversionState()
269       
270        # Read the panel's parameters
271        flag, alpha, dmax, nfunc, qmin, \
272        qmax, height, width = self._read_pars()
273       
274        state.nfunc = nfunc
275        state.d_max = dmax
276        state.alpha = alpha
277        state.qmin  = qmin
278        state.qmax  = qmax
279        state.width = width
280        state.height = height
281       
282        # Data file
283        state.file = self.plot_data.GetValue()
284       
285        # Background evaluation checkbox
286        state.estimate_bck = self.bck_chk.IsChecked()
287       
288        # Estimates
289        state.nterms_estimate = self.nterms_estimate
290        state.alpha_estimate = self.alpha_estimate
291       
292        # Read the output values
293        state.chi2    = self.chi2
294        state.elapsed = self.elapsed
295        state.osc     = self.oscillation
296        state.pos     = self.pos
297        state.pos_err = self.pos_err
298        state.rg      = self.rg
299        state.iq0     = self.iq0
300        state.bck     = self.bck
301       
302        return state
303   
304    def set_state(self, state):
305        """
306        Set the state of the panel and inversion problem to
307        the state passed as a parameter.
308        Execute the inversion immediately after filling the
309        controls.
310       
311        :param state: InversionState object
312        """
313        if state.nfunc is not None:
314            self.nfunc = state.nfunc
315        if state.d_max is not None:
316            self.d_max = state.d_max
317        if state.alpha is not None:
318            self.alpha = state.alpha
319        if state.qmin is not None:
320            self.q_min  = state.qmin
321        if state.qmax is not None:
322            self.q_max  = state.qmax
323        if state.width is not None:
324            self.slit_width = state.width
325        if state.height is not None:
326            self.slit_height = state.height
327       
328        # Data file
329        self.plot_data.SetValue(str(state.file))
330   
331        # Background evaluation checkbox
332        self.bck_chk.SetValue(state.estimate_bck)
333       
334        # Estimates
335        if state.nterms_estimate is not None:
336            self.nterms_estimate = state.nterms_estimate
337        if state.alpha_estimate is not None: 
338            self.alpha_estimate = state.alpha_estimate
339   
340       
341        # Read the output values
342        if state.chi2 is not None:
343            self.chi2    = state.chi2
344        if state.elapsed is not None:
345            self.elapsed = state.elapsed
346        if state.osc is not None:
347            self.oscillation = state.osc
348        if state.pos is not None:
349            self.positive = state.pos
350        if state.pos_err is not None:
351            self.pos_err = state.pos_err
352        if state.rg is not None:
353            self.rg      = state.rg
354        if state.iq0 is not None:
355            self.iq0     = state.iq0
356        if state.bck is not None:
357            self.bck     = state.bck
358
359        # Perform inversion
360        if self.standalone == False:
361            self._on_invert(None)   
362       
363    def set_manager(self, manager):
364        self.manager = manager
365       
366    def _do_layout(self):
367        vbox = wx.GridBagSizer(0,0)
368        iy_vb = 0
369
370        # ----- I(q) data -----
371        databox = wx.StaticBox(self, -1, "I(q) data source")
372       
373        boxsizer1 = wx.StaticBoxSizer(databox, wx.VERTICAL)
374        boxsizer1.SetMinSize((self._default_width, 50))
375        pars_sizer = wx.GridBagSizer(5, 5)
376
377        iy = 0
378        self.file_radio = wx.StaticText(self, -1, "Data:")
379        pars_sizer.Add(self.file_radio, (iy, 0), (1, 1),
380                       wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
381       
382        self.plot_data = DataFileTextCtrl(self, -1, size=(260,20))
383       
384        pars_sizer.Add(self.plot_data, (iy, 1), (1, 1),
385                       wx.EXPAND|wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 15)
386       
387        self.bck_chk = wx.CheckBox(self, -1, "Estimate background level")
388        hint_msg = "Check box to let the fit estimate "
389        hint_msg += "the constant background level."
390        self.bck_chk.SetToolTipString(hint_msg)
391        self.bck_chk.Bind(wx.EVT_CHECKBOX, self._on_pars_changed)
392        iy += 1
393        pars_sizer.Add(self.bck_chk, (iy, 0), (1, 2),
394                       wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
395        boxsizer1.Add(pars_sizer, 0, wx.EXPAND) 
396        vbox.Add(boxsizer1, (iy_vb, 0), (1, 1),
397                 wx.LEFT|wx.RIGHT|wx.EXPAND|wx.ADJUST_MINSIZE|wx.TOP, 5)
398       
399        # ----- Add slit parameters -----
400        if True:
401            sbox = wx.StaticBox(self, -1, "Slit parameters")
402            sboxsizer = wx.StaticBoxSizer(sbox, wx.VERTICAL)
403            sboxsizer.SetMinSize((self._default_width, 20))
404           
405            sizer_slit = wx.GridBagSizer(5, 5)
406   
407            label_sheight = wx.StaticText(self, -1, "Height", size=(40, 20))
408            label_swidth = wx.StaticText(self, -1, "Width", size=(40, 20))
409            #label_sunits1 = wx.StaticText(self, -1, "[A^(-1)]")
410            label_sunits2 = wx.StaticText(self, -1, "[A^(-1)]", size=(55,20))
411            self.sheight_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER,
412                                          size=(60,20))
413            self.swidth_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER,
414                                         size=(60,20))
415            hint_msg = "Enter slit height in units of Q or leave blank."
416            self.sheight_ctl.SetToolTipString(hint_msg)
417            hint_msg = "Enter slit width in units of Q or leave blank."
418            self.swidth_ctl.SetToolTipString(hint_msg)
419            #self.sheight_ctl.Bind(wx.EVT_TEXT, self._on_pars_changed)
420            #self.swidth_ctl.Bind(wx.EVT_TEXT,  self._on_pars_changed)
421           
422            iy = 0
423            sizer_slit.Add(label_sheight,    (iy, 0), (1, 1),
424                           wx.LEFT|wx.EXPAND, 5)
425            sizer_slit.Add(self.sheight_ctl, (iy, 1), (1, 1),
426                           wx.LEFT|wx.EXPAND, 5)
427   
428            sizer_slit.Add(label_swidth,     (iy, 2), (1, 1),
429                           wx.LEFT|wx.EXPAND, 5)
430            sizer_slit.Add(self.swidth_ctl,  (iy, 3), (1, 1),
431                           wx.LEFT|wx.EXPAND, 5)
432            sizer_slit.Add(label_sunits2,    (iy, 4), (1, 1),
433                           wx.LEFT|wx.EXPAND, 5)
434           
435            sboxsizer.Add(sizer_slit, wx.TOP, 15)
436            iy_vb += 1
437            vbox.Add(sboxsizer, (iy_vb, 0), (1, 1),
438                     wx.LEFT|wx.RIGHT|wx.EXPAND|wx.ADJUST_MINSIZE, 5)
439       
440       
441        # ----- Q range -----
442        qbox = wx.StaticBox(self, -1, "Q range")
443        qboxsizer = wx.StaticBoxSizer(qbox, wx.VERTICAL)
444        qboxsizer.SetMinSize((self._default_width, 20))
445       
446        sizer_q = wx.GridBagSizer(5, 5)
447
448        label_qmin = wx.StaticText(self, -1, "Q min", size=(40, 20))
449        label_qmax = wx.StaticText(self, -1, "Q max", size=(40, 20))
450        #label_qunits1 = wx.StaticText(self, -1, "[A^(-1)]")
451        label_qunits2 = wx.StaticText(self, -1, "[A^(-1)]", size=(55, 20))
452        self.qmin_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER,
453                                   size=(60,20))
454        self.qmax_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER,
455                                   size=(60,20))
456        hint_msg = "Select a lower bound for Q or leave blank."
457        self.qmin_ctl.SetToolTipString(hint_msg)
458        hint_msg = "Select an upper bound for Q or leave blank."
459        self.qmax_ctl.SetToolTipString(hint_msg)
460        self.qmin_ctl.Bind(wx.EVT_TEXT, self._on_pars_changed)
461        self.qmax_ctl.Bind(wx.EVT_TEXT, self._on_pars_changed)
462       
463        iy = 0
464        sizer_q.Add(label_qmin,    (iy, 0), (1, 1), wx.LEFT|wx.EXPAND, 5)
465        sizer_q.Add(self.qmin_ctl, (iy, 1), (1, 1), wx.LEFT|wx.EXPAND, 5)
466        #sizer_q.Add(label_qunits1, (iy,2), (1,1), wx.LEFT|wx.EXPAND, 15)
467        sizer_q.Add(label_qmax,    (iy, 2), (1, 1), wx.LEFT|wx.EXPAND, 5)
468        sizer_q.Add(self.qmax_ctl, (iy, 3), (1, 1), wx.LEFT|wx.EXPAND, 5)
469        sizer_q.Add(label_qunits2, (iy, 4), (1, 1), wx.LEFT|wx.EXPAND, 5)
470        qboxsizer.Add(sizer_q, wx.TOP, 15)
471
472        iy_vb += 1
473        vbox.Add(qboxsizer, (iy_vb,0), (1,1),
474                 wx.LEFT|wx.RIGHT|wx.EXPAND|wx.ADJUST_MINSIZE, 5)
475   
476        # ----- Parameters -----
477        parsbox = wx.StaticBox(self, -1, "Parameters")
478        boxsizer2 = wx.StaticBoxSizer(parsbox, wx.VERTICAL)
479        boxsizer2.SetMinSize((self._default_width,50))
480       
481        explanation  = "P(r) is found by fitting a set of base functions"
482        explanation += " to I(Q). The minimization involves"
483        explanation += " a regularization term to ensure a smooth P(r)."
484        explanation += " The regularization constant gives the size of that " 
485        explanation += "term. The suggested value is the value above which the"
486        explanation += " output P(r) will have only one peak."
487        label_explain = wx.StaticText(self, -1, explanation, size=(280,120))
488        boxsizer2.Add(label_explain,  wx.LEFT|wx.BOTTOM, 5)
489       
490       
491       
492        label_nfunc = wx.StaticText(self, -1, "Number of terms")
493        label_nfunc.SetMinSize((120, 20))
494        label_alpha = wx.StaticText(self, -1, "Regularization constant")
495        label_dmax  = wx.StaticText(self, -1, "Max distance [A]")
496        self.label_sugg  = wx.StaticText(self, -1, "Suggested value")
497        #self.label_sugg.Hide()
498       
499        self.nfunc_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER,
500                                    size=(60,20))
501        self.nfunc_ctl.SetToolTipString("Number of terms in the expansion.")
502        self.alpha_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER,
503                                    size=(60,20))
504        hint_msg = "Control parameter for the size of the regularization term."
505        self.alpha_ctl.SetToolTipString(hint_msg)
506        self.dmax_ctl  = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER,
507                                    size=(60,20))
508        hint_msg = "Maximum distance between any two points in the system."
509        self.dmax_ctl.SetToolTipString(hint_msg)
510        id = wx.NewId()
511        self.alpha_estimate_ctl  = wx.Button(self, id, "")
512        #self.alpha_estimate_ctl.Hide()
513        self.Bind(wx.EVT_BUTTON, self._on_accept_alpha, id = id)   
514        self.alpha_estimate_ctl.Enable(False)
515        #self.alpha_estimate_ctl.SetBackgroundColour('#ffdf85')
516        #self.alpha_estimate_ctl.SetBackgroundColour(self.GetBackgroundColour())
517        self.alpha_estimate_ctl.SetToolTipString("Waiting for estimate...")
518       
519        id = wx.NewId()
520        self.nterms_estimate_ctl  = wx.Button(self, id, "")
521        #self.nterms_estimate_ctl.Hide()
522        self.Bind(wx.EVT_BUTTON, self._on_accept_nterms, id = id)   
523        self.nterms_estimate_ctl.Enable(False)
524     
525        self.nterms_estimate_ctl.SetToolTipString("Waiting for estimate...")
526       
527        self.nfunc_ctl.Bind(wx.EVT_TEXT, self._read_pars)
528        self.alpha_ctl.Bind(wx.EVT_TEXT, self._read_pars)
529        self.dmax_ctl.Bind(wx.EVT_TEXT, self._on_pars_changed)
530       
531        # Distance explorator
532        id = wx.NewId()
533        self.distance_explorator_ctl = wx.Button(self, id, "Explore")
534        self.Bind(wx.EVT_BUTTON, self._on_explore, id = id)           
535       
536       
537        sizer_params = wx.GridBagSizer(5,5)
538
539        iy = 0
540        sizer_params.Add(self.label_sugg, (iy, 2), (1, 1), wx.LEFT, 15)
541        iy += 1
542        sizer_params.Add(label_nfunc,      (iy, 0), (1, 1), wx.LEFT, 15)
543        sizer_params.Add(self.nfunc_ctl,   (iy, 1), (1, 1), wx.RIGHT, 0)
544        sizer_params.Add(self.nterms_estimate_ctl, (iy, 2), (1, 1), wx.LEFT, 15)
545        iy += 1
546        sizer_params.Add(label_alpha,      (iy, 0), (1, 1), wx.LEFT, 15)
547        sizer_params.Add(self.alpha_ctl,   (iy, 1), (1, 1), wx.RIGHT, 0)
548        sizer_params.Add(self.alpha_estimate_ctl, (iy, 2), (1, 1), wx.LEFT, 15)
549        iy += 1
550        sizer_params.Add(label_dmax, (iy, 0), (1, 1), wx.LEFT, 15)
551        sizer_params.Add(self.dmax_ctl,   (iy, 1), (1, 1), wx.RIGHT, 0)
552        sizer_params.Add(self.distance_explorator_ctl, (iy, 2),
553                         (1, 1), wx.LEFT, 15)
554
555        boxsizer2.Add(sizer_params, 0)
556       
557        iy_vb += 1
558        vbox.Add(boxsizer2, (iy_vb, 0), (1, 1),
559                 wx.LEFT|wx.RIGHT|wx.EXPAND|wx.ADJUST_MINSIZE, 5)
560
561
562        # ----- Results -----
563        resbox = wx.StaticBox(self, -1, "Outputs")
564        ressizer = wx.StaticBoxSizer(resbox, wx.VERTICAL)
565        ressizer.SetMinSize((self._default_width, 50))
566       
567        label_rg       = wx.StaticText(self, -1, "Rg")
568        label_rg_unit  = wx.StaticText(self, -1, "[A]")
569        label_iq0      = wx.StaticText(self, -1, "I(Q=0)")
570        label_iq0_unit = wx.StaticText(self, -1, "[A^(-1)]")
571        label_bck      = wx.StaticText(self, -1, "Background")
572        label_bck_unit = wx.StaticText(self, -1, "[A^(-1)]")
573        self.rg_ctl    = OutputTextCtrl(self, -1, size=(60,20))
574        hint_msg = "Radius of gyration for the computed P(r)."
575        self.rg_ctl.SetToolTipString(hint_msg)
576        self.iq0_ctl   = OutputTextCtrl(self, -1, size=(60, 20))
577        hint_msg = "Scattering intensity at Q=0 for the computed P(r)."
578        self.iq0_ctl.SetToolTipString(hint_msg)
579        self.bck_ctl   = OutputTextCtrl(self, -1, size=(60, 20))
580        self.bck_ctl.SetToolTipString("Value of estimated constant background.")
581       
582        label_time = wx.StaticText(self, -1, "Computation time")
583        label_time_unit = wx.StaticText(self, -1, "secs")
584        label_time.SetMinSize((120,20))
585        label_chi2 = wx.StaticText(self, -1, "Chi2/dof")
586        label_osc = wx.StaticText(self, -1, "Oscillations")
587        label_pos = wx.StaticText(self, -1, "Positive fraction")
588        label_pos_err = wx.StaticText(self, -1, "1-sigma positive fraction")
589       
590        self.time_ctl = OutputTextCtrl(self, -1, size=(60, 20))
591        hint_msg = "Computation time for the last inversion, in seconds."
592        self.time_ctl.SetToolTipString(hint_msg)
593       
594        self.chi2_ctl = OutputTextCtrl(self, -1, size=(60, 20))
595        self.chi2_ctl.SetToolTipString("Chi^2 over degrees of freedom.")
596       
597        # Oscillation parameter
598        self.osc_ctl = OutputTextCtrl(self, -1, size=(60, 20))
599        hint_msg = "Oscillation parameter. P(r) for a sphere has an "
600        hint_msg += " oscillation parameter of 1.1."
601        self.osc_ctl.SetToolTipString(hint_msg)
602       
603        # Positive fraction figure of merit
604        self.pos_ctl = OutputTextCtrl(self, -1, size=(60, 20))
605        hint_msg = "Fraction of P(r) that is positive. "
606        hint_msg += "Theoretically, P(r) is defined positive."
607        self.pos_ctl.SetToolTipString(hint_msg)
608       
609        # 1-simga positive fraction figure of merit
610        self.pos_err_ctl = OutputTextCtrl(self, -1, size=(60, 20))
611        message  = "Fraction of P(r) that is at least 1 standard deviation"
612        message += " greater than zero.\n"
613        message += "This figure of merit tells you about the size of the "
614        message += "P(r) errors.\n"
615        message += "If it is close to 1 and the other figures of merit are bad,"
616        message += " consider changing the maximum distance."
617        self.pos_err_ctl.SetToolTipString(message)
618       
619        sizer_res = wx.GridBagSizer(5, 5)
620
621        iy = 0
622        sizer_res.Add(label_rg, (iy, 0), (1, 1), wx.LEFT|wx.EXPAND, 15)
623        sizer_res.Add(self.rg_ctl,   (iy, 1), (1, 1), wx.RIGHT|wx.EXPAND, 15)
624        sizer_res.Add(label_rg_unit,   (iy, 2), (1, 1), wx.RIGHT|wx.EXPAND, 15)
625        iy += 1
626        sizer_res.Add(label_iq0, (iy, 0), (1, 1), wx.LEFT|wx.EXPAND, 15)
627        sizer_res.Add(self.iq0_ctl,   (iy, 1), (1, 1), wx.RIGHT|wx.EXPAND, 15)
628        sizer_res.Add(label_iq0_unit, (iy, 2), (1, 1), wx.RIGHT|wx.EXPAND, 15)
629        iy += 1
630        sizer_res.Add(label_bck, (iy, 0), (1, 1), wx.LEFT|wx.EXPAND, 15)
631        sizer_res.Add(self.bck_ctl, (iy, 1), (1, 1), wx.RIGHT|wx.EXPAND, 15)
632        sizer_res.Add(label_bck_unit, (iy, 2), (1, 1), wx.RIGHT|wx.EXPAND, 15)
633        iy += 1
634        sizer_res.Add(label_time, (iy, 0), (1, 1), wx.LEFT|wx.EXPAND, 15)
635        sizer_res.Add(self.time_ctl,   (iy, 1), (1, 1), wx.RIGHT|wx.EXPAND, 15)
636        sizer_res.Add(label_time_unit, (iy, 2), (1, 1), wx.RIGHT|wx.EXPAND, 15)
637        iy += 1
638        sizer_res.Add(label_chi2, (iy, 0), (1, 1), wx.LEFT|wx.EXPAND, 15)
639        sizer_res.Add(self.chi2_ctl,   (iy, 1), (1, 1), wx.RIGHT|wx.EXPAND, 15)
640        iy += 1
641        sizer_res.Add(label_osc, (iy, 0), (1, 1), wx.LEFT|wx.EXPAND, 15)
642        sizer_res.Add(self.osc_ctl,   (iy, 1), (1, 1), wx.RIGHT|wx.EXPAND, 15)
643
644        iy += 1
645        sizer_res.Add(label_pos, (iy, 0), (1, 1), wx.LEFT|wx.EXPAND, 15)
646        sizer_res.Add(self.pos_ctl, (iy, 1), (1, 1), wx.RIGHT|wx.EXPAND, 15)
647
648        iy += 1
649        sizer_res.Add(label_pos_err, (iy, 0), (1, 1), wx.LEFT|wx.EXPAND, 15)
650        sizer_res.Add(self.pos_err_ctl,  (iy,1), (1, 1), wx.RIGHT|wx.EXPAND, 15)
651
652        ressizer.Add(sizer_res, 0)
653        iy_vb += 1
654        vbox.Add(ressizer, (iy_vb, 0), (1, 1),
655                 wx.LEFT|wx.RIGHT|wx.EXPAND|wx.ADJUST_MINSIZE, 5)
656
657        # ----- Buttons -----
658        id = wx.NewId()
659        button_OK = wx.Button(self, id, "Compute")
660        button_OK.SetToolTipString("Perform P(r) inversion.")
661        self.Bind(wx.EVT_BUTTON, self._on_invert, id = id)   
662       
663        id = wx.NewId()
664        button_Reset = wx.Button(self, id, "Reset")
665        button_Reset.SetToolTipString("Reset inversion parameters to default.")
666        self.Bind(wx.EVT_BUTTON, self._on_reset, id = id)   
667        #button_Cancel = wx.Button(self, wx.ID_CANCEL, "Cancel")
668       
669        id = wx.NewId()
670        button_Save = wx.Button(self, id, "Save")
671        button_Save.SetToolTipString("Save the current P(r) work to file.")
672        self.Bind(wx.EVT_BUTTON, self._save_state, id=id)   
673       
674        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
675        sizer_button.Add((20, 20), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
676        sizer_button.Add(button_Save, 0, wx.LEFT|wx.ADJUST_MINSIZE, 10)
677        sizer_button.Add(button_Reset, 0, wx.LEFT|wx.ADJUST_MINSIZE, 10)
678        sizer_button.Add(button_OK, 0, wx.LEFT|wx.ADJUST_MINSIZE, 10)
679       
680        iy_vb += 1
681        vbox.Add(sizer_button, (iy_vb, 0), (1, 1),
682                 wx.EXPAND|wx.BOTTOM|wx.TOP|wx.RIGHT, 10)
683
684        self.Bind(wx.EVT_TEXT_ENTER, self._on_invert)
685
686        self.SetSizer(vbox)
687       
688    def _on_accept_alpha(self, evt):
689        """
690        User has accepted the estimated alpha,
691        set it as part of the input parameters
692        """
693        try:
694            alpha = self.alpha_estimate_ctl.GetLabel()
695            tmp = float(alpha)
696            self.alpha_ctl.SetValue(alpha)
697        except:
698            # No estimate or bad estimate, either do nothing
699            #import sys
700            print "InversionControl._on_accept_alpha: %s" % sys.exc_value
701            pass
702   
703    def _on_accept_nterms(self, evt):
704        """
705        User has accepted the estimated number of terms,
706        set it as part of the input parameters
707        """
708        try:
709            nterms = self.nterms_estimate_ctl.GetLabel()
710            tmp = float(nterms)
711            self.nfunc_ctl.SetValue(nterms)
712        except:
713            # No estimate or bad estimate, either do nothing
714            import sys
715            print "InversionControl._on_accept_nterms: %s" % sys.exc_value
716            pass
717       
718    def _on_reset(self, evt):
719        """
720        Resets inversion parameters
721        """
722        self.nfunc = self.manager.DEFAULT_NFUNC
723        self.d_max = self.manager.DEFAULT_DMAX
724        self.alpha = self.manager.DEFAULT_ALPHA
725        self.qmin_ctl.SetValue("")
726        self.qmax_ctl.SetValue("")
727        self.time_ctl.SetValue("")
728        self.rg_ctl.SetValue("")
729        self.iq0_ctl.SetValue("")
730        self.bck_ctl.SetValue("")
731        self.chi2_ctl.SetValue("")
732        self.osc_ctl.SetValue("")
733        self.pos_ctl.SetValue("")
734        self.pos_err_ctl.SetValue("")
735        self.alpha_estimate_ctl.Enable(False)
736        self.alpha_estimate_ctl.SetLabel("")
737        self.nterms_estimate_ctl.Enable(False)
738        self.nterms_estimate_ctl.SetLabel("")
739        self._on_pars_changed()
740       
741    def _on_pars_changed(self, evt=None):
742        """
743        Called when an input parameter has changed
744        We will estimate the alpha parameter behind the
745        scenes.
746        """
747        flag, alpha, dmax, nfunc, qmin, qmax, height, width = self._read_pars()
748        has_bck = self.bck_chk.IsChecked()
749       
750        # If the pars are valid, estimate alpha
751        if flag:
752            self.nterms_estimate_ctl.Enable(False)
753            self.alpha_estimate_ctl.Enable(False)
754           
755            dataset = self.plot_data.GetValue()
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.