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

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

working on guiframe

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