source: sasview/prview/perspectives/pr/inversion_panel.py @ 4e9583c

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 4e9583c was f5038c06, checked in by Gervaise Alina <gervyh@…>, 14 years ago

reverse code

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