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

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

make pr plugin inheriting from pluginbase in guiframe

  • Property mode set to 100644
File size: 40.1 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
15from pr_widgets import DataFileTextCtrl
16from pr_widgets import OutputTextCtrl
17
18
19
20class InversionControl(ScrolledPanel):
21    """
22    """
23    window_name = 'pr_control'
24    window_caption = "P(r) control panel"
25    CENTER_PANE = True
26   
27    # Figure of merit parameters [default]
28   
29    ## Oscillation parameters (sin function = 1.1)
30    oscillation_max = 1.5
31   
32    def __init__(self, parent, id=-1, plots=None, 
33                 standalone=False, **kwargs):
34        """
35        """
36        ScrolledPanel.__init__(self, parent, id=id, **kwargs)
37        self.SetupScrolling()
38       
39        self.plots = plots
40        self.radio_buttons = {}
41        self.parent = parent
42       
43        ## Data file TextCtrl
44        self.data_file  = None
45        self.plot_data  = None
46        self.nfunc_ctl  = None
47        self.alpha_ctl  = None
48        self.dmax_ctl   = None
49        self.time_ctl   = None
50        self.chi2_ctl   = None
51        self.osc_ctl    = None
52        self.file_radio = None
53        self.plot_radio = None
54        self.label_sugg = None
55        self.qmin_ctl   = None
56        self.qmax_ctl   = None
57        self.swidth_ctl = None
58        self.sheight_ctl = None
59       
60        self.rg_ctl     = None
61        self.iq0_ctl    = None
62        self.bck_chk    = None
63        self.bck_ctl    = None
64       
65        # TextCtrl for fraction of positive P(r)
66        self.pos_ctl = None
67       
68        # TextCtrl for fraction of 1 sigma positive P(r)
69        self.pos_err_ctl = None 
70       
71        ## Estimates
72        self.alpha_estimate_ctl = None
73        self.nterms_estimate_ctl = None
74       
75        ## D_max distance explorator
76        self.distance_explorator_ctl = None
77       
78        ## Data manager
79        self.manager   = None
80       
81        ## Standalone flage
82        self.standalone = standalone
83       
84        ## Default file location for save
85        self._default_save_location = os.getcwd()
86       
87       
88        # Default width
89        self._default_width = 350
90        self._do_layout()
91       
92    def __setattr__(self, name, value):
93        """
94        Allow direct hooks to text boxes
95        """
96        if name == 'nfunc':
97            self.nfunc_ctl.SetValue(str(int(value)))
98        elif name == 'd_max':
99            self.dmax_ctl.SetValue(str(value))
100        elif name == 'alpha':
101            self.alpha_ctl.SetValue(str(value))
102        elif name == 'chi2':
103            self.chi2_ctl.SetValue("%-5.2g" % value)
104        elif name == 'bck':
105            self.bck_ctl.SetValue("%-5.2g" % value)
106        elif name == 'q_min':
107            self.qmin_ctl.SetValue("%-5.2g" % value)
108        elif name == 'q_max':
109            self.qmax_ctl.SetValue("%-5.2g" % value)
110        elif name == 'elapsed':
111            self.time_ctl.SetValue("%-5.2g" % value)
112        elif name ==' rg':
113            self.rg_ctl.SetValue("%-5.2g" % value)
114        elif name == 'iq0':
115            self.iq0_ctl.SetValue("%-5.2g" % value)
116        elif name == 'oscillation':
117            self.osc_ctl.SetValue("%-5.2g" % value)
118        elif name == 'slit_width':
119            self.swidth_ctl.SetValue("%-5.2g" % value)
120        elif name == 'slit_height':
121            self.sheight_ctl.SetValue("%-5.2g" % value)
122        elif name == 'positive':
123            self.pos_ctl.SetValue("%-5.2g" % value)
124        elif name == 'pos_err':
125            self.pos_err_ctl.SetValue("%-5.2g" % value)
126        elif name == 'alpha_estimate':
127            self.alpha_estimate_ctl.SetToolTipString("Click to accept value.")
128            self.alpha_estimate_ctl.Enable(True)
129            self.alpha_estimate_ctl.SetLabel("%-3.1g" % value)
130            #self.alpha_estimate_ctl.Show()
131            #self.label_sugg.Show()
132        elif name == 'nterms_estimate':
133            self.nterms_estimate_ctl.SetToolTipString("Click to accept value.")
134            self.nterms_estimate_ctl.Enable(True)
135            self.nterms_estimate_ctl.SetLabel("%-g" % value)
136        elif name == 'plotname':
137            self.plot_data.SetValue(str(value))
138            self._on_pars_changed(None)
139        elif name == 'datafile':
140            self.plot_data.SetValue(str(value))
141            self._on_pars_changed(None)
142        else:
143            wx.Panel.__setattr__(self, name, value)
144       
145    def __getattr__(self, name):
146        """
147        Allow direct hooks to text boxes
148        """
149        if name == 'nfunc':
150            try:
151                return int(self.nfunc_ctl.GetValue())
152            except:
153                return -1
154        elif name == 'd_max':
155            try:
156                return self.dmax_ctl.GetValue()
157            except:
158                return -1.0
159        elif name == 'alpha':
160            try:
161                return self.alpha_ctl.GetValue()
162            except:
163                return -1.0
164        elif name == 'chi2':
165            try:
166                return float(self.chi2_ctl.GetValue())
167            except:
168                return None
169        elif name == 'bck':
170            try:
171                return float(self.bck_ctl.GetValue())
172            except:
173                return None
174        elif name == 'q_min':
175            try:
176                return float(self.qmin_ctl.GetValue())
177            except:
178                return 0.0
179        elif name=='q_max':
180            try:
181                return float(self.qmax_ctl.GetValue())
182            except:
183                return 0.0
184        elif name == 'elapsed':
185            try:
186                return float(self.time_ctl.GetValue())
187            except:
188                return None
189        elif name == 'rg':
190            try:
191                return float(self.rg_ctl.GetValue())
192            except:
193                return None
194        elif name=='iq0':
195            try:
196                return float(self.iq0_ctl.GetValue())
197            except:
198                return None
199        elif name == 'oscillation':
200            try:
201                return float(self.osc_ctl.GetValue())
202            except:
203                return None
204        elif name == 'slit_width':
205            try:
206                return float(self.swidth_ctl.GetValue())
207            except:
208                return None
209        elif name=='slit_height':
210            try:
211                return float(self.sheight_ctl.GetValue())
212            except:
213                return None
214        elif name == 'pos':
215            try:
216                return float(self.pos_ctl.GetValue())
217            except:
218                return None
219        elif name == 'pos_err':
220            try:
221                return float(self.pos_err_ctl.GetValue())
222            except:
223                return None
224        elif name == 'alpha_estimate':
225            try:
226                return float(self.alpha_estimate_ctl.GetLabel())
227            except:
228                return None
229        elif name == 'nterms_estimate':
230            try:
231                return int(self.nterms_estimate_ctl.GetLabel())
232            except:
233                return None
234        elif name == 'plotname':
235            return self.plot_data.GetValue()
236        elif name == 'datafile':
237            return self.plot_data.GetValue()
238        else:
239            return wx.Panel.__getattribute__(self, name)
240       
241    def _save_state(self, evt=None):
242        """
243        Method used to create a memento of the current state
244           
245        :return: state object
246        """
247        # Ask the user the location of the file to write to.
248        path = None
249        dlg = wx.FileDialog(self, "Choose a file",
250                            self._default_save_location, "", "*.prv", wx.SAVE)
251        if dlg.ShowModal() == wx.ID_OK:
252            path = dlg.GetPath()
253            self._default_save_location = os.path.dirname(path)
254        else:
255            return None
256       
257        dlg.Destroy()
258       
259        state = self.get_state()
260           
261        self.manager.save_data(filepath=path, prstate=state)
262       
263        return state
264   
265    def get_state(self):
266        """
267        Get the current state
268       
269        : return: state object
270        """
271        # Construct the state object   
272        state = InversionState()
273       
274        # Read the panel's parameters
275        flag, alpha, dmax, nfunc, qmin, \
276        qmax, height, width = self._read_pars()
277       
278        state.nfunc = nfunc
279        state.d_max = dmax
280        state.alpha = alpha
281        state.qmin  = qmin
282        state.qmax  = qmax
283        state.width = width
284        state.height = height
285       
286        # Data file
287        state.file = self.plot_data.GetValue()
288       
289        # Background evaluation checkbox
290        state.estimate_bck = self.bck_chk.IsChecked()
291       
292        # Estimates
293        state.nterms_estimate = self.nterms_estimate
294        state.alpha_estimate = self.alpha_estimate
295       
296        # Read the output values
297        state.chi2    = self.chi2
298        state.elapsed = self.elapsed
299        state.osc     = self.oscillation
300        state.pos     = self.pos
301        state.pos_err = self.pos_err
302        state.rg      = self.rg
303        state.iq0     = self.iq0
304        state.bck     = self.bck
305       
306        return state
307   
308    def set_state(self, state):
309        """
310        Set the state of the panel and inversion problem to
311        the state passed as a parameter.
312        Execute the inversion immediately after filling the
313        controls.
314       
315        :param state: InversionState object
316        """
317        if state.nfunc is not None:
318            self.nfunc = state.nfunc
319        if state.d_max is not None:
320            self.d_max = state.d_max
321        if state.alpha is not None:
322            self.alpha = state.alpha
323        if state.qmin is not None:
324            self.q_min  = state.qmin
325        if state.qmax is not None:
326            self.q_max  = state.qmax
327        if state.width is not None:
328            self.slit_width = state.width
329        if state.height is not None:
330            self.slit_height = state.height
331       
332        # Data file
333        self.plot_data.SetValue(str(state.file))
334   
335        # Background evaluation checkbox
336        self.bck_chk.SetValue(state.estimate_bck)
337       
338        # Estimates
339        if state.nterms_estimate is not None:
340            self.nterms_estimate = state.nterms_estimate
341        if state.alpha_estimate is not None: 
342            self.alpha_estimate = state.alpha_estimate
343   
344       
345        # Read the output values
346        if state.chi2 is not None:
347            self.chi2    = state.chi2
348        if state.elapsed is not None:
349            self.elapsed = state.elapsed
350        if state.osc is not None:
351            self.oscillation = state.osc
352        if state.pos is not None:
353            self.positive = state.pos
354        if state.pos_err is not None:
355            self.pos_err = state.pos_err
356        if state.rg is not None:
357            self.rg      = state.rg
358        if state.iq0 is not None:
359            self.iq0     = state.iq0
360        if state.bck is not None:
361            self.bck     = state.bck
362
363        # Perform inversion
364        if self.standalone == False:
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        id = wx.NewId()
668        button_Reset = wx.Button(self, id, "Reset")
669        button_Reset.SetToolTipString("Reset inversion parameters to default.")
670        self.Bind(wx.EVT_BUTTON, self._on_reset, id = id)   
671        #button_Cancel = wx.Button(self, wx.ID_CANCEL, "Cancel")
672       
673        id = wx.NewId()
674        button_Save = wx.Button(self, id, "Save")
675        button_Save.SetToolTipString("Save the current P(r) work to file.")
676        self.Bind(wx.EVT_BUTTON, self._save_state, id=id)   
677       
678        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
679        sizer_button.Add((20, 20), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
680        sizer_button.Add(button_Save, 0, wx.LEFT|wx.ADJUST_MINSIZE, 10)
681        sizer_button.Add(button_Reset, 0, wx.LEFT|wx.ADJUST_MINSIZE, 10)
682        sizer_button.Add(button_OK, 0, wx.LEFT|wx.ADJUST_MINSIZE, 10)
683       
684        iy_vb += 1
685        vbox.Add(sizer_button, (iy_vb, 0), (1, 1),
686                 wx.EXPAND|wx.BOTTOM|wx.TOP|wx.RIGHT, 10)
687
688        self.Bind(wx.EVT_TEXT_ENTER, self._on_invert)
689
690        self.SetSizer(vbox)
691       
692    def _on_accept_alpha(self, evt):
693        """
694        User has accepted the estimated alpha,
695        set it as part of the input parameters
696        """
697        try:
698            alpha = self.alpha_estimate_ctl.GetLabel()
699            tmp = float(alpha)
700            self.alpha_ctl.SetValue(alpha)
701        except:
702            # No estimate or bad estimate, either do nothing
703            #import sys
704            print "InversionControl._on_accept_alpha: %s" % sys.exc_value
705            pass
706   
707    def _on_accept_nterms(self, evt):
708        """
709        User has accepted the estimated number of terms,
710        set it as part of the input parameters
711        """
712        try:
713            nterms = self.nterms_estimate_ctl.GetLabel()
714            tmp = float(nterms)
715            self.nfunc_ctl.SetValue(nterms)
716        except:
717            # No estimate or bad estimate, either do nothing
718            import sys
719            print "InversionControl._on_accept_nterms: %s" % sys.exc_value
720            pass
721       
722    def _on_reset(self, evt):
723        """
724        Resets inversion parameters
725        """
726        self.nfunc = self.manager.DEFAULT_NFUNC
727        self.d_max = self.manager.DEFAULT_DMAX
728        self.alpha = self.manager.DEFAULT_ALPHA
729        self.qmin_ctl.SetValue("")
730        self.qmax_ctl.SetValue("")
731        self.time_ctl.SetValue("")
732        self.rg_ctl.SetValue("")
733        self.iq0_ctl.SetValue("")
734        self.bck_ctl.SetValue("")
735        self.chi2_ctl.SetValue("")
736        self.osc_ctl.SetValue("")
737        self.pos_ctl.SetValue("")
738        self.pos_err_ctl.SetValue("")
739        self.alpha_estimate_ctl.Enable(False)
740        self.alpha_estimate_ctl.SetLabel("")
741        self.nterms_estimate_ctl.Enable(False)
742        self.nterms_estimate_ctl.SetLabel("")
743        self._on_pars_changed()
744       
745    def _on_pars_changed(self, evt=None):
746        """
747        Called when an input parameter has changed
748        We will estimate the alpha parameter behind the
749        scenes.
750        """
751        flag, alpha, dmax, nfunc, qmin, qmax, height, width = self._read_pars()
752        has_bck = self.bck_chk.IsChecked()
753       
754        # If the pars are valid, estimate alpha
755        if flag:
756            self.nterms_estimate_ctl.Enable(False)
757            self.alpha_estimate_ctl.Enable(False)
758           
759            dataset = self.plot_data.GetValue()
760            self.manager.estimate_plot_inversion(alpha=alpha, nfunc=nfunc, 
761                                                 d_max=dmax,
762                                                 q_min=qmin, q_max=qmax,
763                                                 bck=has_bck, 
764                                                 height=height,
765                                                 width=width)
766       
767    def _read_pars(self, evt=None):
768        """
769        """   
770        alpha = 0
771        nfunc = 5
772        dmax  = 120
773        qmin  = 0
774        qmax  = 0
775        height = 0
776        width  = 0
777       
778        flag = True
779       
780       
781        # Read slit height
782        try:
783            height_str = self.sheight_ctl.GetValue()
784            if len(height_str.lstrip().rstrip()) == 0:
785                height = 0
786            else:
787                height = float(height_str)
788                self.sheight_ctl.SetBackgroundColour(wx.WHITE)
789                self.sheight_ctl.Refresh()
790        except:
791            flag = False
792            self.sheight_ctl.SetBackgroundColour("pink")
793            self.sheight_ctl.Refresh()
794           
795        # Read slit width
796        try:
797            width_str = self.swidth_ctl.GetValue()
798            if len(width_str.lstrip().rstrip())==0:
799                width = 0
800            else:
801                width = float(width_str)
802                self.swidth_ctl.SetBackgroundColour(wx.WHITE)
803                self.swidth_ctl.Refresh()
804        except:
805            flag = False
806            self.swidth_ctl.SetBackgroundColour("pink")
807            self.swidth_ctl.Refresh()
808       
809        # Read alpha
810        try:
811            alpha = float(self.alpha_ctl.GetValue())
812            self.alpha_ctl.SetBackgroundColour(wx.WHITE)
813            self.alpha_ctl.Refresh()
814        except:
815            flag = False
816            self.alpha_ctl.SetBackgroundColour("pink")
817            self.alpha_ctl.Refresh()
818       
819        # Read d_max   
820        try:
821            dmax = float(self.dmax_ctl.GetValue())
822            self.dmax_ctl.SetBackgroundColour(wx.WHITE)
823            self.dmax_ctl.Refresh()
824        except:
825            flag = False
826            self.dmax_ctl.SetBackgroundColour("pink")
827            self.dmax_ctl.Refresh()
828           
829        # Read nfunc
830        try:
831            nfunc = int(self.nfunc_ctl.GetValue())
832            npts = self.manager.get_npts()
833            if npts > 0 and nfunc > npts:
834                message = "Number of function terms should be smaller "
835                message += "than the number of points"
836                wx.PostEvent(self.manager.parent, StatusEvent(status=message))
837                raise ValueError, message
838            self.nfunc_ctl.SetBackgroundColour(wx.WHITE)
839            self.nfunc_ctl.Refresh()
840        except:
841            flag = False
842            self.nfunc_ctl.SetBackgroundColour("pink")
843            self.nfunc_ctl.Refresh()
844       
845        # Read qmin
846        try:
847            qmin_str = self.qmin_ctl.GetValue()
848            if len(qmin_str.lstrip().rstrip()) == 0:
849                qmin = None
850            else:
851                qmin = float(qmin_str)
852                self.qmin_ctl.SetBackgroundColour(wx.WHITE)
853                self.qmin_ctl.Refresh()
854        except:
855            flag = False
856            self.qmin_ctl.SetBackgroundColour("pink")
857            self.qmin_ctl.Refresh()
858       
859        # Read qmax
860        try:
861            qmax_str = self.qmax_ctl.GetValue()
862            if len(qmax_str.lstrip().rstrip()) == 0:
863                qmax = None
864            else:
865                qmax = float(qmax_str)
866                self.qmax_ctl.SetBackgroundColour(wx.WHITE)
867                self.qmax_ctl.Refresh()
868        except:
869            flag = False
870            self.qmax_ctl.SetBackgroundColour("pink")
871            self.qmax_ctl.Refresh()
872       
873        return flag, alpha, dmax, nfunc, qmin, qmax, height, width
874   
875    def _on_explore(self, evt):
876        """
877        Invoke the d_max exploration dialog
878        """
879        from explore_dialog import ExploreDialog
880        if self.manager._last_pr is not None:
881            pr = self.manager._create_plot_pr()
882            dialog = ExploreDialog(pr, 10, None, -1, "")
883            dialog.ShowModal()
884        else:
885            message = "No data to analyze. Please load a data set to proceed."
886            wx.PostEvent(self.manager.parent, StatusEvent(status=message))
887           
888    def _on_invert(self, evt):
889        """
890        Perform inversion
891       
892        :param silent: when True, there will be no output for the user
893       
894        """
895        # Get the data from the form
896        # Push it to the manager
897       
898        flag, alpha, dmax, nfunc, qmin, qmax, height, width = self._read_pars()
899        has_bck = self.bck_chk.IsChecked()
900       
901        if flag:
902            dataset = self.plot_data.GetValue()
903            if dataset==None or len(dataset.strip())==0:
904                message = "No data to invert. Select a data set before"
905                message += " proceeding with P(r) inversion."
906                wx.PostEvent(self.manager.parent, StatusEvent(status=message))
907            else:
908                self.manager.setup_plot_inversion(alpha=alpha, nfunc=nfunc, 
909                                                  d_max=dmax,
910                                                  q_min=qmin, q_max=qmax,
911                                                  bck=has_bck,
912                                                  height=height,
913                                                  width=width)
914        else:
915            message = "The P(r) form contains invalid values: "
916            message += "please submit it again."
917            wx.PostEvent(self.parent, StatusEvent(status=message))
918       
919    def _change_file(self, evt=None, filepath=None):
920        """
921        Choose a new input file for I(q)
922        """
923        import os
924        if not self.manager==None:
925            path = self.manager.choose_file(path=filepath)
926           
927            if path and os.path.isfile(path):
928                self.plot_data.SetValue(str(path))
929                try:
930                    self.manager.show_data(path, reset=True)
931                    self._on_pars_changed(None)
932               
933                    # Perform inversion
934                    if self.standalone == True:
935                        self._on_invert(None)
936                except:
937                    # Invalid data
938                    msg = "InversionControl._change_file: %s" % sys.exc_value
939                    logging.error(msg)                   
940
941class HelpDialog(wx.Dialog):
942    """
943    """
944    def __init__(self, parent, id):
945        """
946        """
947        from sans.pr.invertor import help
948        wx.Dialog.__init__(self, parent, id, size=(400, 420))
949        self.SetTitle("P(r) help") 
950       
951
952        vbox = wx.BoxSizer(wx.VERTICAL)
953
954        explanation = help()
955           
956        label_explain = wx.StaticText(self, -1, explanation, size=(350,320))
957           
958        vbox.Add(label_explain, 0, wx.ALL|wx.EXPAND, 15)
959
960
961        static_line = wx.StaticLine(self, -1)
962        vbox.Add(static_line, 0, wx.EXPAND, 0)
963       
964        button_OK = wx.Button(self, wx.ID_OK, "OK")
965        #button_Cancel = wx.Button(self, wx.ID_CANCEL, "Cancel")
966       
967        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
968        sizer_button.Add((20, 20), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
969        sizer_button.Add(button_OK, 0, wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 10)
970       
971        vbox.Add(sizer_button, 0, wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
972
973        self.SetSizer(vbox)
974        self.SetAutoLayout(True)
975       
976        self.Layout()
977        self.Centre()
978
979class PrDistDialog(wx.Dialog):
980    """
981    Property dialog to let the user change the number
982    of points on the P(r) plot.
983    """
984    def __init__(self, parent, id):
985        from sans.pr.invertor import help
986        wx.Dialog.__init__(self, parent, id, size=(250, 120))
987        self.SetTitle("P(r) distribution") 
988       
989
990        vbox = wx.BoxSizer(wx.VERTICAL)
991       
992        label_npts = wx.StaticText(self, -1, "Number of points")
993        self.npts_ctl = PrTextCtrl(self, -1, size=(100,20))
994                 
995        pars_sizer = wx.GridBagSizer(5,5)
996        iy = 0
997        pars_sizer.Add(label_npts,      (iy,0), (1,1), wx.LEFT, 15)
998        pars_sizer.Add(self.npts_ctl,   (iy,1), (1,1), wx.RIGHT, 0)
999       
1000        vbox.Add(pars_sizer, 0, wx.ALL|wx.EXPAND, 15)
1001
1002
1003        static_line = wx.StaticLine(self, -1)
1004        vbox.Add(static_line, 0, wx.EXPAND, 0)
1005       
1006        button_OK = wx.Button(self, wx.ID_OK, "OK")
1007        self.Bind(wx.EVT_BUTTON, self._checkValues, button_OK)
1008        button_Cancel = wx.Button(self, wx.ID_CANCEL, "Cancel")
1009       
1010        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
1011        sizer_button.Add((20, 20), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
1012        sizer_button.Add(button_OK, 0, wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 10)
1013        sizer_button.Add(button_Cancel, 0,
1014                         wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 10)       
1015        vbox.Add(sizer_button, 0, wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1016
1017        self.SetSizer(vbox)
1018        self.SetAutoLayout(True)
1019       
1020        self.Layout()
1021        self.Centre()
1022
1023    def _checkValues(self, event):
1024        """
1025        Check the dialog content.
1026        """
1027        flag = True
1028        try:
1029            int(self.npts_ctl.GetValue())
1030            self.npts_ctl.SetBackgroundColour(wx.WHITE)
1031            self.npts_ctl.Refresh()
1032        except:
1033            flag = False
1034            self.npts_ctl.SetBackgroundColour("pink")
1035            self.npts_ctl.Refresh()
1036        if flag:
1037            event.Skip(True)
1038
1039    def get_content(self):
1040        """
1041        Return the content of the dialog.
1042        At this point the values have already been
1043        checked.
1044        """
1045        value = int(self.npts_ctl.GetValue())
1046        return value
1047   
1048    def set_content(self, npts):
1049        """
1050        Initialize the content of the dialog.
1051        """
1052        self.npts_ctl.SetValue("%i" % npts)
1053
1054##### testing code ############################################################
1055"""
1056Example: ::
1057
1058class TestPlot:
1059    def __init__(self, text):
1060        self.name = text
1061   
1062class MyApp(wx.App):
1063    def OnInit(self):
1064        wx.InitAllImageHandlers()
1065        dialog = PrDistDialog(None, -1)
1066        if dialog.ShowModal() == wx.ID_OK:
1067            pass
1068        dialog.Destroy()
1069       
1070        return 1
1071
1072# end of class MyApp
1073
1074if __name__ == "__main__":
1075    app = MyApp(0)
1076    app.MainLoop()
1077   
1078"""
1079##### end of testing code #####################################################   
Note: See TracBrowser for help on using the repository browser.