source: sasview/inversionview/src/sans/perspectives/pr/inversion_panel.py @ fa6db8b

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 fa6db8b was c4ae1c2, checked in by Jae Cho <jhjcho@…>, 13 years ago

correction: default folder for inv and pr

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