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

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 d65a00a was b35d3d1, checked in by Jae Cho <jhjcho@…>, 14 years ago

save state file: working, but more work to do

  • Property mode set to 100644
File size: 39.0 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        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), wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
380       
381        self.plot_data = DataFileTextCtrl(self, -1, size=(260,20))
382       
383        pars_sizer.Add(self.plot_data, (iy,1), (1,1), wx.EXPAND|wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 15)
384       
385        self.bck_chk = wx.CheckBox(self, -1, "Estimate background level")
386        self.bck_chk.SetToolTipString("Check box to let the fit estimate the constant background level.")
387        self.bck_chk.Bind(wx.EVT_CHECKBOX, self._on_pars_changed)
388        iy += 1
389        pars_sizer.Add(self.bck_chk, (iy,0), (1,2), wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
390        boxsizer1.Add(pars_sizer, 0, wx.EXPAND) 
391        vbox.Add(boxsizer1, (iy_vb,0), (1,1), wx.LEFT|wx.RIGHT|wx.EXPAND|wx.ADJUST_MINSIZE|wx.TOP, 5)
392       
393        # ----- Add slit parameters -----
394        if True:
395            sbox = wx.StaticBox(self, -1, "Slit parameters")
396            sboxsizer = wx.StaticBoxSizer(sbox, wx.VERTICAL)
397            sboxsizer.SetMinSize((self._default_width,20))
398           
399            sizer_slit = wx.GridBagSizer(5,5)
400   
401            label_sheight = wx.StaticText(self, -1, "Height", size=(40,20))
402            label_swidth = wx.StaticText(self, -1, "Width", size=(40,20))
403            #label_sunits1 = wx.StaticText(self, -1, "[A^(-1)]")
404            label_sunits2 = wx.StaticText(self, -1, "[A^(-1)]", size=(55,20))
405            self.sheight_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60,20))
406            self.swidth_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60,20))
407            self.sheight_ctl.SetToolTipString("Enter slit height in units of Q or leave blank.")
408            self.swidth_ctl.SetToolTipString("Enter slit width in units of Q or leave blank.")
409            #self.sheight_ctl.Bind(wx.EVT_TEXT, self._on_pars_changed)
410            #self.swidth_ctl.Bind(wx.EVT_TEXT,  self._on_pars_changed)
411           
412            iy = 0
413            sizer_slit.Add(label_sheight,    (iy,0), (1,1), wx.LEFT|wx.EXPAND, 5)
414            sizer_slit.Add(self.sheight_ctl, (iy,1), (1,1), wx.LEFT|wx.EXPAND, 5)
415            #sizer_slit.Add(label_sunits1,    (iy,2), (1,1), wx.LEFT|wx.EXPAND, 10)
416            sizer_slit.Add(label_swidth,     (iy,2), (1,1), wx.LEFT|wx.EXPAND, 5)
417            sizer_slit.Add(self.swidth_ctl,  (iy,3), (1,1), wx.LEFT|wx.EXPAND, 5)
418            sizer_slit.Add(label_sunits2,    (iy,4), (1,1), wx.LEFT|wx.EXPAND, 5)
419           
420            sboxsizer.Add(sizer_slit, wx.TOP, 15)
421            iy_vb += 1
422            vbox.Add(sboxsizer, (iy_vb,0), (1,1), wx.LEFT|wx.RIGHT|wx.EXPAND|wx.ADJUST_MINSIZE, 5)
423       
424       
425        # ----- Q range -----
426        qbox = wx.StaticBox(self, -1, "Q range")
427        qboxsizer = wx.StaticBoxSizer(qbox, wx.VERTICAL)
428        qboxsizer.SetMinSize((self._default_width,20))
429       
430        sizer_q = wx.GridBagSizer(5,5)
431
432        label_qmin = wx.StaticText(self, -1, "Q min", size=(40,20))
433        label_qmax = wx.StaticText(self, -1, "Q max", size=(40,20))
434        #label_qunits1 = wx.StaticText(self, -1, "[A^(-1)]")
435        label_qunits2 = wx.StaticText(self, -1, "[A^(-1)]", size=(55,20))
436        self.qmin_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60,20))
437        self.qmax_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60,20))
438        self.qmin_ctl.SetToolTipString("Select a lower bound for Q or leave blank.")
439        self.qmax_ctl.SetToolTipString("Select an upper bound for Q or leave blank.")
440        self.qmin_ctl.Bind(wx.EVT_TEXT, self._on_pars_changed)
441        self.qmax_ctl.Bind(wx.EVT_TEXT, self._on_pars_changed)
442       
443        iy = 0
444        sizer_q.Add(label_qmin,    (iy,0), (1,1), wx.LEFT|wx.EXPAND, 5)
445        sizer_q.Add(self.qmin_ctl, (iy,1), (1,1), wx.LEFT|wx.EXPAND, 5)
446        #sizer_q.Add(label_qunits1, (iy,2), (1,1), wx.LEFT|wx.EXPAND, 15)
447        sizer_q.Add(label_qmax,    (iy,2), (1,1), wx.LEFT|wx.EXPAND, 5)
448        sizer_q.Add(self.qmax_ctl, (iy,3), (1,1), wx.LEFT|wx.EXPAND, 5)
449        sizer_q.Add(label_qunits2, (iy,4), (1,1), wx.LEFT|wx.EXPAND, 5)
450        qboxsizer.Add(sizer_q, wx.TOP, 15)
451
452        iy_vb += 1
453        vbox.Add(qboxsizer, (iy_vb,0), (1,1), wx.LEFT|wx.RIGHT|wx.EXPAND|wx.ADJUST_MINSIZE, 5)
454       
455       
456       
457
458        # ----- Parameters -----
459        parsbox = wx.StaticBox(self, -1, "Parameters")
460        boxsizer2 = wx.StaticBoxSizer(parsbox, wx.VERTICAL)
461        boxsizer2.SetMinSize((self._default_width,50))
462       
463        explanation  = "P(r) is found by fitting a set of base functions to I(Q). "
464        explanation += "The minimization involves a regularization term to ensure "
465        explanation += "a smooth P(r). The regularization constant gives the size of that " 
466        explanation += "term. The suggested value is the value above which the "
467        explanation += "output P(r) will have only one peak."
468        label_explain = wx.StaticText(self, -1, explanation, size=(280,120))
469        boxsizer2.Add(label_explain,  wx.LEFT|wx.BOTTOM, 5)
470       
471       
472       
473        label_nfunc = wx.StaticText(self, -1, "Number of terms")
474        label_nfunc.SetMinSize((120,20))
475        label_alpha = wx.StaticText(self, -1, "Regularization constant")
476        label_dmax  = wx.StaticText(self, -1, "Max distance [A]")
477        self.label_sugg  = wx.StaticText(self, -1, "Suggested value")
478        #self.label_sugg.Hide()
479       
480        self.nfunc_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60,20))
481        self.nfunc_ctl.SetToolTipString("Number of terms in the expansion.")
482        self.alpha_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60,20))
483        self.alpha_ctl.SetToolTipString("Control parameter for the size of the regularization term.")
484        self.dmax_ctl  = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60,20))
485        self.dmax_ctl.SetToolTipString("Maximum distance between any two points in the system.")
486        id = wx.NewId()
487        self.alpha_estimate_ctl  = wx.Button(self, id, "")
488        #self.alpha_estimate_ctl.Hide()
489        self.Bind(wx.EVT_BUTTON, self._on_accept_alpha, id = id)   
490        self.alpha_estimate_ctl.Enable(False)
491        #self.alpha_estimate_ctl.SetBackgroundColour('#ffdf85')
492        #self.alpha_estimate_ctl.SetBackgroundColour(self.GetBackgroundColour())
493        self.alpha_estimate_ctl.SetToolTipString("Waiting for estimate...")
494       
495        id = wx.NewId()
496        self.nterms_estimate_ctl  = wx.Button(self, id, "")
497        #self.nterms_estimate_ctl.Hide()
498        self.Bind(wx.EVT_BUTTON, self._on_accept_nterms, id = id)   
499        self.nterms_estimate_ctl.Enable(False)
500        #self.nterms_estimate_ctl.SetBackgroundColour('#ffdf85')
501        #self.nterms_estimate_ctl.SetBackgroundColour(self.GetBackgroundColour())
502        self.nterms_estimate_ctl.SetToolTipString("Waiting for estimate...")
503       
504        self.nfunc_ctl.Bind(wx.EVT_TEXT, self._read_pars)
505        self.alpha_ctl.Bind(wx.EVT_TEXT, self._read_pars)
506        self.dmax_ctl.Bind(wx.EVT_TEXT, self._on_pars_changed)
507       
508        # Distance explorator
509        id = wx.NewId()
510        self.distance_explorator_ctl = wx.Button(self, id, "Explore")
511        self.Bind(wx.EVT_BUTTON, self._on_explore, id = id)           
512       
513       
514        sizer_params = wx.GridBagSizer(5,5)
515
516        iy = 0
517        sizer_params.Add(self.label_sugg,       (iy,2), (1,1), wx.LEFT, 15)
518        iy += 1
519        sizer_params.Add(label_nfunc,      (iy,0), (1,1), wx.LEFT, 15)
520        sizer_params.Add(self.nfunc_ctl,   (iy,1), (1,1), wx.RIGHT, 0)
521        sizer_params.Add(self.nterms_estimate_ctl, (iy,2), (1,1), wx.LEFT, 15)
522        iy += 1
523        sizer_params.Add(label_alpha,      (iy,0), (1,1), wx.LEFT, 15)
524        sizer_params.Add(self.alpha_ctl,   (iy,1), (1,1), wx.RIGHT, 0)
525        sizer_params.Add(self.alpha_estimate_ctl, (iy,2), (1,1), wx.LEFT, 15)
526        iy += 1
527        sizer_params.Add(label_dmax, (iy,0), (1,1), wx.LEFT, 15)
528        sizer_params.Add(self.dmax_ctl,   (iy,1), (1,1), wx.RIGHT, 0)
529        sizer_params.Add(self.distance_explorator_ctl,   (iy,2), (1,1), wx.LEFT, 15)
530
531        boxsizer2.Add(sizer_params, 0)
532       
533        iy_vb += 1
534        vbox.Add(boxsizer2, (iy_vb,0), (1,1), wx.LEFT|wx.RIGHT|wx.EXPAND|wx.ADJUST_MINSIZE, 5)
535
536
537        # ----- Results -----
538        resbox = wx.StaticBox(self, -1, "Outputs")
539        ressizer = wx.StaticBoxSizer(resbox, wx.VERTICAL)
540        ressizer.SetMinSize((self._default_width,50))
541       
542        label_rg       = wx.StaticText(self, -1, "Rg")
543        label_rg_unit  = wx.StaticText(self, -1, "[A]")
544        label_iq0      = wx.StaticText(self, -1, "I(Q=0)")
545        label_iq0_unit = wx.StaticText(self, -1, "[A^(-1)]")
546        label_bck      = wx.StaticText(self, -1, "Background")
547        label_bck_unit = wx.StaticText(self, -1, "[A^(-1)]")
548        self.rg_ctl    = OutputTextCtrl(self, -1, size=(60,20))
549        self.rg_ctl.SetToolTipString("Radius of gyration for the computed P(r).")
550        self.iq0_ctl   = OutputTextCtrl(self, -1, size=(60,20))
551        self.iq0_ctl.SetToolTipString("Scattering intensity at Q=0 for the computed P(r).")
552        self.bck_ctl   = OutputTextCtrl(self, -1, size=(60,20))
553        self.bck_ctl.SetToolTipString("Value of estimated constant background.")
554       
555        label_time = wx.StaticText(self, -1, "Computation time")
556        label_time_unit = wx.StaticText(self, -1, "secs")
557        label_time.SetMinSize((120,20))
558        label_chi2 = wx.StaticText(self, -1, "Chi2/dof")
559        label_osc = wx.StaticText(self, -1, "Oscillations")
560        label_pos = wx.StaticText(self, -1, "Positive fraction")
561        label_pos_err = wx.StaticText(self, -1, "1-sigma positive fraction")
562       
563        self.time_ctl = OutputTextCtrl(self, -1, size=(60,20))
564        self.time_ctl.SetToolTipString("Computation time for the last inversion, in seconds.")
565       
566        self.chi2_ctl = OutputTextCtrl(self, -1, size=(60,20))
567        self.chi2_ctl.SetToolTipString("Chi^2 over degrees of freedom.")
568       
569        # Oscillation parameter
570        self.osc_ctl = OutputTextCtrl(self, -1, size=(60,20))
571        self.osc_ctl.SetToolTipString("Oscillation parameter. P(r) for a sphere has an oscillation parameter of 1.1.")
572       
573        # Positive fraction figure of merit
574        self.pos_ctl = OutputTextCtrl(self, -1, size=(60,20))
575        self.pos_ctl.SetToolTipString("Fraction of P(r) that is positive. Theoretically, P(r) is defined positive.")
576       
577        # 1-simga positive fraction figure of merit
578        self.pos_err_ctl = OutputTextCtrl(self, -1, size=(60,20))
579        message  = "Fraction of P(r) that is at least 1 standard deviation greater than zero.\n"
580        message += "This figure of merit tells you about the size of the P(r) errors.\n"
581        message += "If it is close to 1 and the other figures of merit are bad, consider changing "
582        message += "the maximum distance."
583        self.pos_err_ctl.SetToolTipString(message)
584       
585        sizer_res = wx.GridBagSizer(5,5)
586
587        iy = 0
588        sizer_res.Add(label_rg, (iy,0), (1,1), wx.LEFT|wx.EXPAND, 15)
589        sizer_res.Add(self.rg_ctl,   (iy,1), (1,1), wx.RIGHT|wx.EXPAND, 15)
590        sizer_res.Add(label_rg_unit,   (iy,2), (1,1), wx.RIGHT|wx.EXPAND, 15)
591        iy += 1
592        sizer_res.Add(label_iq0, (iy,0), (1,1), wx.LEFT|wx.EXPAND, 15)
593        sizer_res.Add(self.iq0_ctl,   (iy,1), (1,1), wx.RIGHT|wx.EXPAND, 15)
594        sizer_res.Add(label_iq0_unit,   (iy,2), (1,1), wx.RIGHT|wx.EXPAND, 15)
595        iy += 1
596        sizer_res.Add(label_bck, (iy,0), (1,1), wx.LEFT|wx.EXPAND, 15)
597        sizer_res.Add(self.bck_ctl,   (iy,1), (1,1), wx.RIGHT|wx.EXPAND, 15)
598        sizer_res.Add(label_bck_unit,   (iy,2), (1,1), wx.RIGHT|wx.EXPAND, 15)
599        iy += 1
600        sizer_res.Add(label_time, (iy,0), (1,1), wx.LEFT|wx.EXPAND, 15)
601        sizer_res.Add(self.time_ctl,   (iy,1), (1,1), wx.RIGHT|wx.EXPAND, 15)
602        sizer_res.Add(label_time_unit,   (iy,2), (1,1), wx.RIGHT|wx.EXPAND, 15)
603        iy += 1
604        sizer_res.Add(label_chi2, (iy,0), (1,1), wx.LEFT|wx.EXPAND, 15)
605        sizer_res.Add(self.chi2_ctl,   (iy,1), (1,1), wx.RIGHT|wx.EXPAND, 15)
606        iy += 1
607        sizer_res.Add(label_osc, (iy,0), (1,1), wx.LEFT|wx.EXPAND, 15)
608        sizer_res.Add(self.osc_ctl,   (iy,1), (1,1), wx.RIGHT|wx.EXPAND, 15)
609
610        iy += 1
611        sizer_res.Add(label_pos, (iy,0), (1,1), wx.LEFT|wx.EXPAND, 15)
612        sizer_res.Add(self.pos_ctl,   (iy,1), (1,1), wx.RIGHT|wx.EXPAND, 15)
613
614        iy += 1
615        sizer_res.Add(label_pos_err, (iy,0), (1,1), wx.LEFT|wx.EXPAND, 15)
616        sizer_res.Add(self.pos_err_ctl,   (iy,1), (1,1), wx.RIGHT|wx.EXPAND, 15)
617
618        ressizer.Add(sizer_res, 0)
619        iy_vb += 1
620        vbox.Add(ressizer, (iy_vb,0), (1,1), wx.LEFT|wx.RIGHT|wx.EXPAND|wx.ADJUST_MINSIZE, 5)
621
622        # ----- Buttons -----
623        id = wx.NewId()
624        button_OK = wx.Button(self, id, "Compute")
625        button_OK.SetToolTipString("Perform P(r) inversion.")
626        self.Bind(wx.EVT_BUTTON, self._on_invert, id = id)   
627       
628        id = wx.NewId()
629        button_Reset = wx.Button(self, id, "Reset")
630        button_Reset.SetToolTipString("Reset inversion parameters to default.")
631        self.Bind(wx.EVT_BUTTON, self._on_reset, id = id)   
632        #button_Cancel = wx.Button(self, wx.ID_CANCEL, "Cancel")
633       
634        id = wx.NewId()
635        button_Save = wx.Button(self, id, "Save")
636        button_Save.SetToolTipString("Save the current P(r) work to file.")
637        self.Bind(wx.EVT_BUTTON, self._save_state, id = id)   
638       
639        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
640        sizer_button.Add((20, 20), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
641        sizer_button.Add(button_Save, 0, wx.LEFT|wx.ADJUST_MINSIZE, 10)
642        sizer_button.Add(button_Reset, 0, wx.LEFT|wx.ADJUST_MINSIZE, 10)
643        sizer_button.Add(button_OK, 0, wx.LEFT|wx.ADJUST_MINSIZE, 10)
644        #sizer_button.Add(button_Cancel, 0, wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 10)       
645        iy_vb += 1
646        vbox.Add(sizer_button, (iy_vb,0), (1,1), wx.EXPAND|wx.BOTTOM|wx.TOP|wx.RIGHT, 10)
647
648        self.Bind(wx.EVT_TEXT_ENTER, self._on_invert)
649
650        self.SetSizer(vbox)
651       
652    def _on_accept_alpha(self, evt):
653        """
654        User has accepted the estimated alpha,
655        set it as part of the input parameters
656        """
657        try:
658            alpha = self.alpha_estimate_ctl.GetLabel()
659            tmp = float(alpha)
660            self.alpha_ctl.SetValue(alpha)
661        except:
662            # No estimate or bad estimate, either do nothing
663            import sys
664            print "InversionControl._on_accept_alpha: %s" % sys.exc_value
665            pass
666   
667    def _on_accept_nterms(self, evt):
668        """
669        User has accepted the estimated number of terms,
670        set it as part of the input parameters
671        """
672        try:
673            nterms = self.nterms_estimate_ctl.GetLabel()
674            tmp = float(nterms)
675            self.nfunc_ctl.SetValue(nterms)
676        except:
677            # No estimate or bad estimate, either do nothing
678            import sys
679            print "InversionControl._on_accept_nterms: %s" % sys.exc_value
680            pass
681       
682    def _on_reset(self, evt):
683        """
684        Resets inversion parameters
685        """
686        self.nfunc = self.manager.DEFAULT_NFUNC
687        self.d_max = self.manager.DEFAULT_DMAX
688        self.alpha = self.manager.DEFAULT_ALPHA
689        self.qmin_ctl.SetValue("")
690        self.qmax_ctl.SetValue("")
691        self.time_ctl.SetValue("")
692        self.rg_ctl.SetValue("")
693        self.iq0_ctl.SetValue("")
694        self.bck_ctl.SetValue("")
695        self.chi2_ctl.SetValue("")
696        self.osc_ctl.SetValue("")
697        self.pos_ctl.SetValue("")
698        self.pos_err_ctl.SetValue("")
699        self.alpha_estimate_ctl.Enable(False)
700        self.alpha_estimate_ctl.SetLabel("")
701        self.nterms_estimate_ctl.Enable(False)
702        self.nterms_estimate_ctl.SetLabel("")
703        self._on_pars_changed()
704       
705    def _on_pars_changed(self, evt=None):
706        """
707        Called when an input parameter has changed
708        We will estimate the alpha parameter behind the
709        scenes.
710        """
711        flag, alpha, dmax, nfunc, qmin, qmax, height, width = self._read_pars()
712        has_bck = self.bck_chk.IsChecked()
713       
714        # If the pars are valid, estimate alpha
715        if flag:
716            self.nterms_estimate_ctl.Enable(False)
717            self.alpha_estimate_ctl.Enable(False)
718           
719            dataset = self.plot_data.GetValue()
720            self.manager.estimate_plot_inversion(alpha=alpha, nfunc=nfunc, 
721                                                 d_max=dmax,
722                                                 q_min=qmin, q_max=qmax,
723                                                 bck=has_bck, 
724                                                 height=height,
725                                                 width=width)
726       
727    def _read_pars(self, evt=None):
728        """
729        """   
730        alpha = 0
731        nfunc = 5
732        dmax  = 120
733        qmin  = 0
734        qmax  = 0
735        height = 0
736        width  = 0
737       
738        flag = True
739       
740       
741        # Read slit height
742        try:
743            height_str = self.sheight_ctl.GetValue()
744            if len(height_str.lstrip().rstrip())==0:
745                height = 0
746            else:
747                height = float(height_str)
748                self.sheight_ctl.SetBackgroundColour(wx.WHITE)
749                self.sheight_ctl.Refresh()
750        except:
751            flag = False
752            self.sheight_ctl.SetBackgroundColour("pink")
753            self.sheight_ctl.Refresh()
754           
755        # Read slit width
756        try:
757            width_str = self.swidth_ctl.GetValue()
758            if len(width_str.lstrip().rstrip())==0:
759                width = 0
760            else:
761                width = float(width_str)
762                self.swidth_ctl.SetBackgroundColour(wx.WHITE)
763                self.swidth_ctl.Refresh()
764        except:
765            flag = False
766            self.swidth_ctl.SetBackgroundColour("pink")
767            self.swidth_ctl.Refresh()
768       
769        # Read alpha
770        try:
771            alpha = float(self.alpha_ctl.GetValue())
772            self.alpha_ctl.SetBackgroundColour(wx.WHITE)
773            self.alpha_ctl.Refresh()
774        except:
775            flag = False
776            self.alpha_ctl.SetBackgroundColour("pink")
777            self.alpha_ctl.Refresh()
778       
779        # Read d_max   
780        try:
781            dmax = float(self.dmax_ctl.GetValue())
782            self.dmax_ctl.SetBackgroundColour(wx.WHITE)
783            self.dmax_ctl.Refresh()
784        except:
785            flag = False
786            self.dmax_ctl.SetBackgroundColour("pink")
787            self.dmax_ctl.Refresh()
788           
789        # Read nfunc
790        try:
791            nfunc = int(self.nfunc_ctl.GetValue())
792            npts = self.manager.get_npts()
793            if npts>0 and nfunc>npts:
794                message = "Number of function terms should be smaller than the number of points"
795                wx.PostEvent(self.manager.parent, StatusEvent(status=message))
796                raise ValueError, message
797            self.nfunc_ctl.SetBackgroundColour(wx.WHITE)
798            self.nfunc_ctl.Refresh()
799        except:
800            flag = False
801            self.nfunc_ctl.SetBackgroundColour("pink")
802            self.nfunc_ctl.Refresh()
803       
804        # Read qmin
805        try:
806            qmin_str = self.qmin_ctl.GetValue()
807            if len(qmin_str.lstrip().rstrip())==0:
808                qmin = None
809            else:
810                qmin = float(qmin_str)
811                self.qmin_ctl.SetBackgroundColour(wx.WHITE)
812                self.qmin_ctl.Refresh()
813        except:
814            flag = False
815            self.qmin_ctl.SetBackgroundColour("pink")
816            self.qmin_ctl.Refresh()
817       
818        # Read qmax
819        try:
820            qmax_str = self.qmax_ctl.GetValue()
821            if len(qmax_str.lstrip().rstrip())==0:
822                qmax = None
823            else:
824                qmax = float(qmax_str)
825                self.qmax_ctl.SetBackgroundColour(wx.WHITE)
826                self.qmax_ctl.Refresh()
827        except:
828            flag = False
829            self.qmax_ctl.SetBackgroundColour("pink")
830            self.qmax_ctl.Refresh()
831       
832        return flag, alpha, dmax, nfunc, qmin, qmax, height, width
833   
834    def _on_explore(self, evt):
835        """
836        Invoke the d_max exploration dialog
837        """
838        from explore_dialog import ExploreDialog
839        if self.manager._last_pr is not None:
840            pr = self.manager._create_plot_pr()
841            dialog = ExploreDialog(pr, 10, None, -1, "")
842            dialog.ShowModal()
843        else:
844            message = "No data to analyze. Please load a data set to proceed."
845            wx.PostEvent(self.manager.parent, StatusEvent(status=message))
846           
847    def _on_invert(self, evt):
848        """
849        Perform inversion
850       
851        :param silent: when True, there will be no output for the user
852       
853        """
854        # Get the data from the form
855        # Push it to the manager
856       
857        flag, alpha, dmax, nfunc, qmin, qmax, height, width = self._read_pars()
858        has_bck = self.bck_chk.IsChecked()
859       
860        if flag:
861            dataset = self.plot_data.GetValue()
862            if dataset==None or len(dataset.strip())==0:
863                message = "No data to invert. Select a data set before proceeding with P(r) inversion."
864                wx.PostEvent(self.manager.parent, StatusEvent(status=message))
865            else:
866                self.manager.setup_plot_inversion(alpha=alpha, nfunc=nfunc, 
867                                                  d_max=dmax,
868                                                  q_min=qmin, q_max=qmax,
869                                                  bck=has_bck,
870                                                  height=height,
871                                                  width=width)
872        else:
873            message = "The P(r) form contains invalid values: please submit it again."
874            wx.PostEvent(self.parent, StatusEvent(status=message))
875       
876    def _change_file(self, evt=None, filepath=None):
877        """
878        Choose a new input file for I(q)
879        """
880        import os
881        if not self.manager==None:
882            path = self.manager.choose_file(path=filepath)
883           
884            if path and os.path.isfile(path):
885                self.plot_data.SetValue(str(path))
886                try:
887                    self.manager.show_data(path, reset=True)
888                    self._on_pars_changed(None)
889               
890                    # Perform inversion
891                    if self.standalone == True:
892                        self._on_invert(None)
893                except:
894                    # Invalid data
895                    logging.error("InversionControl._change_file: %s" % sys.exc_value)                   
896
897class HelpDialog(wx.Dialog):
898    """
899    """
900    def __init__(self, parent, id):
901        """
902        """
903        from sans.pr.invertor import help
904        wx.Dialog.__init__(self, parent, id, size=(400, 420))
905        self.SetTitle("P(r) help") 
906       
907
908        vbox = wx.BoxSizer(wx.VERTICAL)
909
910        explanation = help()
911           
912        label_explain = wx.StaticText(self, -1, explanation, size=(350,320))
913           
914        vbox.Add(label_explain, 0, wx.ALL|wx.EXPAND, 15)
915
916
917        static_line = wx.StaticLine(self, -1)
918        vbox.Add(static_line, 0, wx.EXPAND, 0)
919       
920        button_OK = wx.Button(self, wx.ID_OK, "OK")
921        #button_Cancel = wx.Button(self, wx.ID_CANCEL, "Cancel")
922       
923        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
924        sizer_button.Add((20, 20), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
925        sizer_button.Add(button_OK, 0, wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 10)
926        #sizer_button.Add(button_Cancel, 0, wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 10)       
927        vbox.Add(sizer_button, 0, wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
928
929        self.SetSizer(vbox)
930        self.SetAutoLayout(True)
931       
932        self.Layout()
933        self.Centre()
934
935class PrDistDialog(wx.Dialog):
936    """
937    Property dialog to let the user change the number
938    of points on the P(r) plot.
939    """
940    def __init__(self, parent, id):
941        from sans.pr.invertor import help
942        wx.Dialog.__init__(self, parent, id, size=(250, 120))
943        self.SetTitle("P(r) distribution") 
944       
945
946        vbox = wx.BoxSizer(wx.VERTICAL)
947       
948        label_npts = wx.StaticText(self, -1, "Number of points")
949        self.npts_ctl = PrTextCtrl(self, -1, size=(100,20))
950                 
951        pars_sizer = wx.GridBagSizer(5,5)
952        iy = 0
953        pars_sizer.Add(label_npts,      (iy,0), (1,1), wx.LEFT, 15)
954        pars_sizer.Add(self.npts_ctl,   (iy,1), (1,1), wx.RIGHT, 0)
955       
956        vbox.Add(pars_sizer, 0, wx.ALL|wx.EXPAND, 15)
957
958
959        static_line = wx.StaticLine(self, -1)
960        vbox.Add(static_line, 0, wx.EXPAND, 0)
961       
962        button_OK = wx.Button(self, wx.ID_OK, "OK")
963        self.Bind(wx.EVT_BUTTON, self._checkValues, button_OK)
964        button_Cancel = wx.Button(self, wx.ID_CANCEL, "Cancel")
965       
966        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
967        sizer_button.Add((20, 20), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
968        sizer_button.Add(button_OK, 0, wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 10)
969        sizer_button.Add(button_Cancel, 0, wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 10)       
970        vbox.Add(sizer_button, 0, wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
971
972        self.SetSizer(vbox)
973        self.SetAutoLayout(True)
974       
975        self.Layout()
976        self.Centre()
977
978    def _checkValues(self, event):
979        """
980        Check the dialog content.
981        """
982        flag = True
983        try:
984            int(self.npts_ctl.GetValue())
985            self.npts_ctl.SetBackgroundColour(wx.WHITE)
986            self.npts_ctl.Refresh()
987        except:
988            flag = False
989            self.npts_ctl.SetBackgroundColour("pink")
990            self.npts_ctl.Refresh()
991        if flag:
992            event.Skip(True)
993
994    def get_content(self):
995        """
996        Return the content of the dialog.
997        At this point the values have already been
998        checked.
999        """
1000        value = int(self.npts_ctl.GetValue())
1001        return value
1002   
1003    def set_content(self, npts):
1004        """
1005        Initialize the content of the dialog.
1006        """
1007        self.npts_ctl.SetValue("%i" % npts)
1008
1009##### testing code ############################################################
1010"""
1011Example: ::
1012
1013class TestPlot:
1014    def __init__(self, text):
1015        self.name = text
1016   
1017class MyApp(wx.App):
1018    def OnInit(self):
1019        wx.InitAllImageHandlers()
1020        dialog = PrDistDialog(None, -1)
1021        if dialog.ShowModal() == wx.ID_OK:
1022            pass
1023        dialog.Destroy()
1024       
1025        return 1
1026
1027# end of class MyApp
1028
1029if __name__ == "__main__":
1030    app = MyApp(0)
1031    app.MainLoop()
1032   
1033"""
1034##### end of testing code #####################################################   
Note: See TracBrowser for help on using the repository browser.