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

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

removed setfocusignoringchild

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