source: sasview/src/sas/sasgui/perspectives/pr/inversion_panel.py @ b9d74f3

Last change on this file since b9d74f3 was b9d74f3, checked in by andyfaff, 8 years ago

MAINT: use raise Exception() not raise Exception

  • Property mode set to 100644
File size: 38.1 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 sas.sasgui.guiframe.events import StatusEvent
13from sas.sasgui.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
18from sas.sasgui.guiframe.documentation_window import DocumentationWindow
19
20logger = logging.getLogger(__name__)
21
22if sys.platform.count("win32") > 0:
23    FONT_VARIANT = 0
24else:
25    FONT_VARIANT = 1
26
27class InversionControl(ScrolledPanel, PanelBase):
28    """
29    """
30    window_name = 'pr_control'
31    window_caption = "P(r) control panel"
32    CENTER_PANE = True
33
34    # Figure of merit parameters [default]
35
36    ## Oscillation parameters (sin function = 1.1)
37    oscillation_max = 1.5
38
39    def __init__(self, parent, id=-1, plots=None, **kwargs):
40        """
41        """
42        ScrolledPanel.__init__(self, parent, id=id, **kwargs)
43        PanelBase.__init__(self, parent)
44        self.SetupScrolling()
45        #Set window's font size
46        self.SetWindowVariant(variant=FONT_VARIANT)
47        self._set_analysis(False)
48
49        self.plots = plots
50        self.radio_buttons = {}
51        self.parent = parent.parent
52
53        ## Data file TextCtrl
54        self.data_file = None
55        self.plot_data = None
56        self.nfunc_ctl = None
57        self.alpha_ctl = None
58        self.dmax_ctl = None
59        self.time_ctl = None
60        self.chi2_ctl = None
61        self.osc_ctl = None
62        self.file_radio = None
63        self.plot_radio = None
64        self.label_sugg = None
65        self.qmin_ctl = None
66        self.qmax_ctl = None
67        self.swidth_ctl = None
68        self.sheight_ctl = None
69
70        self.rg_ctl = None
71        self.iq0_ctl = None
72        self.bck_chk = None
73        self.bck_ctl = None
74
75        # TextCtrl for fraction of positive P(r)
76        self.pos_ctl = None
77
78        # TextCtrl for fraction of 1 sigma positive P(r)
79        self.pos_err_ctl = None
80
81        ## Estimates
82        self.alpha_estimate_ctl = None
83        self.nterms_estimate_ctl = None
84        ## D_max distance explorator
85        self.distance_explorator_ctl = None
86        ## Data manager
87        self._manager = None
88        ## Default file location for save
89        self._default_save_location = os.getcwd()
90        if self.parent is not None:
91            self._default_save_location = \
92                        self.parent._default_save_location
93
94        # Default width
95        self._default_width = 350
96        self._do_layout()
97
98    def __setattr__(self, name, value):
99        """
100        Allow direct hooks to text boxes
101        """
102        if name == 'nfunc':
103            self.nfunc_ctl.SetValue(str(int(value)))
104        elif name == 'd_max':
105            self.dmax_ctl.SetValue(str(value))
106        elif name == 'alpha':
107            self.alpha_ctl.SetValue(str(value))
108        elif name == 'chi2':
109            self.chi2_ctl.SetValue("%-5.2g" % value)
110        elif name == 'bck':
111            self.bck_ctl.SetValue("%-5.2g" % value)
112        elif name == 'q_min':
113            self.qmin_ctl.SetValue("%-5.2g" % value)
114        elif name == 'q_max':
115            self.qmax_ctl.SetValue("%-5.2g" % value)
116        elif name == 'elapsed':
117            self.time_ctl.SetValue("%-5.2g" % value)
118        elif name == 'rg':
119            self.rg_ctl.SetValue("%-5.2g" % value)
120        elif name == 'iq0':
121            self.iq0_ctl.SetValue("%-5.2g" % value)
122        elif name == 'oscillation':
123            self.osc_ctl.SetValue("%-5.2g" % value)
124        elif name == 'slit_width':
125            self.swidth_ctl.SetValue("%-5.2g" % value)
126        elif name == 'slit_height':
127            self.sheight_ctl.SetValue("%-5.2g" % value)
128        elif name == 'positive':
129            self.pos_ctl.SetValue("%-5.2g" % value)
130        elif name == 'pos_err':
131            self.pos_err_ctl.SetValue("%-5.2g" % value)
132        elif name == 'alpha_estimate':
133            self.alpha_estimate_ctl.SetToolTipString("Click to accept value.")
134            self.alpha_estimate_ctl.Enable(True)
135            self.alpha_estimate_ctl.SetLabel("%-3.1g" % value)
136            #self.alpha_estimate_ctl.Show()
137            #self.label_sugg.Show()
138        elif name == 'nterms_estimate':
139            self.nterms_estimate_ctl.SetToolTipString("Click to accept value.")
140            self.nterms_estimate_ctl.Enable(True)
141            self.nterms_estimate_ctl.SetLabel("%-g" % value)
142        elif name == 'plotname':
143            self.plot_data.SetValue(str(value))
144            self._on_pars_changed(None)
145        elif name == 'datafile':
146            self.plot_data.SetValue(str(value))
147            self._on_pars_changed(None)
148        else:
149            wx.Panel.__setattr__(self, name, value)
150
151    def __getattr__(self, name):
152        """
153        Allow direct hooks to text boxes
154        """
155        if name == 'nfunc':
156            try:
157                return int(self.nfunc_ctl.GetValue())
158            except:
159                return -1
160        elif name == 'd_max':
161            try:
162                return self.dmax_ctl.GetValue()
163            except:
164                return -1.0
165        elif name == 'alpha':
166            try:
167                return self.alpha_ctl.GetValue()
168            except:
169                return -1.0
170        elif name == 'chi2':
171            try:
172                return float(self.chi2_ctl.GetValue())
173            except:
174                return None
175        elif name == 'bck':
176            try:
177                return float(self.bck_ctl.GetValue())
178            except:
179                return None
180        elif name == 'q_min':
181            try:
182                return float(self.qmin_ctl.GetValue())
183            except:
184                return 0.0
185        elif name == 'q_max':
186            try:
187                return float(self.qmax_ctl.GetValue())
188            except:
189                return 0.0
190        elif name == 'elapsed':
191            try:
192                return float(self.time_ctl.GetValue())
193            except:
194                return None
195        elif name == 'rg':
196            try:
197                return float(self.rg_ctl.GetValue())
198            except:
199                return None
200        elif name == 'iq0':
201            try:
202                return float(self.iq0_ctl.GetValue())
203            except:
204                return None
205        elif name == 'oscillation':
206            try:
207                return float(self.osc_ctl.GetValue())
208            except:
209                return None
210        elif name == 'slit_width':
211            try:
212                return float(self.swidth_ctl.GetValue())
213            except:
214                return None
215        elif name == 'slit_height':
216            try:
217                return float(self.sheight_ctl.GetValue())
218            except:
219                return None
220        elif name == 'pos':
221            try:
222                return float(self.pos_ctl.GetValue())
223            except:
224                return None
225        elif name == 'pos_err':
226            try:
227                return float(self.pos_err_ctl.GetValue())
228            except:
229                return None
230        elif name == 'alpha_estimate':
231            try:
232                return float(self.alpha_estimate_ctl.GetLabel())
233            except:
234                return None
235        elif name == 'nterms_estimate':
236            try:
237                return int(self.nterms_estimate_ctl.GetLabel())
238            except:
239                return None
240        elif name == 'plotname':
241            return self.plot_data.GetValue()
242        elif name == 'datafile':
243            return self.plot_data.GetValue()
244        else:
245            return wx.Panel.__getattribute__(self, name)
246
247    def save_project(self, doc=None):
248        """
249        return an xml node containing state of the panel
250         that guiframe can write to file
251        """
252        data = self.get_data()
253        state = self.get_state()
254        if data is not None:
255            new_doc = self._manager.state_reader.write_toXML(data, state)
256            if new_doc is not None:
257                if doc is not None and hasattr(doc, "firstChild"):
258                    child = new_doc.getElementsByTagName("SASentry")
259                    for item in child:
260                        doc.firstChild.appendChild(item)
261                else:
262                    doc = new_doc
263        return doc
264
265    def on_save(self, evt=None):
266        """
267        Method used to create a memento of the current state
268
269        :return: state object
270        """
271        # Ask the user the location of the file to write to.
272        path = None
273        if self.parent is not None:
274            self._default_save_location = self.parent._default_save_location
275        dlg = wx.FileDialog(self, "Choose a file",
276                            self._default_save_location,
277                            self.window_caption, "*.prv", wx.SAVE)
278        if dlg.ShowModal() == wx.ID_OK:
279            path = dlg.GetPath()
280            self._default_save_location = os.path.dirname(path)
281            if self.parent is not None:
282                self.parent._default_save_location = 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        # We have the data available for serialization
402        self._set_analysis(True)
403
404        # Perform inversion
405        self._on_invert(None)
406
407    def set_manager(self, manager):
408        self._manager = manager
409        if manager is not None:
410            self._set_analysis(False)
411
412    def _do_layout(self):
413        vbox = wx.GridBagSizer(0, 0)
414        iy_vb = 0
415
416        # ----- I(q) data -----
417        databox = wx.StaticBox(self, -1, "I(q) data source")
418
419        boxsizer1 = wx.StaticBoxSizer(databox, wx.VERTICAL)
420        boxsizer1.SetMinSize((self._default_width, 50))
421        pars_sizer = wx.GridBagSizer(5, 5)
422
423        iy = 0
424        self.file_radio = wx.StaticText(self, -1, "Name:")
425        pars_sizer.Add(self.file_radio, (iy, 0), (1, 1),
426                       wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
427
428        self.plot_data = DataFileTextCtrl(self, -1, size=(260, 20))
429
430        pars_sizer.Add(self.plot_data, (iy, 1), (1, 1),
431                       wx.EXPAND | wx.LEFT | wx.RIGHT | wx.ADJUST_MINSIZE, 15)
432
433        self.bck_chk = wx.CheckBox(self, -1, "Estimate background level")
434        hint_msg = "Check box to let the fit estimate "
435        hint_msg += "the constant background level."
436        self.bck_chk.SetToolTipString(hint_msg)
437        self.bck_chk.Bind(wx.EVT_CHECKBOX, self._on_pars_changed)
438        iy += 1
439        pars_sizer.Add(self.bck_chk, (iy, 0), (1, 2),
440                       wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
441        boxsizer1.Add(pars_sizer, 0, wx.EXPAND)
442        vbox.Add(boxsizer1, (iy_vb, 0), (1, 1),
443                 wx.LEFT | wx.RIGHT | wx.EXPAND | wx.ADJUST_MINSIZE | wx.TOP, 5)
444
445        # ----- Add slit parameters -----
446        if True:
447            sbox = wx.StaticBox(self, -1, "Slit parameters")
448            sboxsizer = wx.StaticBoxSizer(sbox, wx.VERTICAL)
449            sboxsizer.SetMinSize((self._default_width, 20))
450
451            sizer_slit = wx.GridBagSizer(5, 5)
452
453            label_sheight = wx.StaticText(self, -1, "Height", size=(40, 20))
454            label_swidth = wx.StaticText(self, -1, "Width", size=(40, 20))
455            label_sunits2 = wx.StaticText(self, -1, "[A^(-1)]", size=(55, 20))
456            self.sheight_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60, 20))
457            self.swidth_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60, 20))
458            hint_msg = "Enter slit height in units of Q or leave blank."
459            self.sheight_ctl.SetToolTipString(hint_msg)
460            hint_msg = "Enter slit width in units of Q or leave blank."
461            self.swidth_ctl.SetToolTipString(hint_msg)
462
463            iy = 0
464            sizer_slit.Add(label_sheight, (iy, 0), (1, 1), wx.LEFT | wx.EXPAND, 5)
465            sizer_slit.Add(self.sheight_ctl, (iy, 1), (1, 1), wx.LEFT | wx.EXPAND, 5)
466            sizer_slit.Add(label_swidth, (iy, 2), (1, 1), wx.LEFT | wx.EXPAND, 5)
467            sizer_slit.Add(self.swidth_ctl, (iy, 3), (1, 1), wx.LEFT | wx.EXPAND, 5)
468            sizer_slit.Add(label_sunits2, (iy, 4), (1, 1), wx.LEFT | wx.EXPAND, 5)
469
470            sboxsizer.Add(sizer_slit, wx.TOP, 15)
471            iy_vb += 1
472            vbox.Add(sboxsizer, (iy_vb, 0), (1, 1),
473                     wx.LEFT | wx.RIGHT | wx.EXPAND | wx.ADJUST_MINSIZE, 5)
474
475        # ----- Q range -----
476        qbox = wx.StaticBox(self, -1, "Q range")
477        qboxsizer = wx.StaticBoxSizer(qbox, wx.VERTICAL)
478        qboxsizer.SetMinSize((self._default_width, 20))
479
480        sizer_q = wx.GridBagSizer(5, 5)
481
482        label_qmin = wx.StaticText(self, -1, "Q min", size=(40, 20))
483        label_qmax = wx.StaticText(self, -1, "Q max", size=(40, 20))
484        label_qunits2 = wx.StaticText(self, -1, "[A^(-1)]", size=(55, 20))
485        self.qmin_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60, 20))
486        self.qmax_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60, 20))
487        hint_msg = "Select a lower bound for Q or leave blank."
488        self.qmin_ctl.SetToolTipString(hint_msg)
489        hint_msg = "Select an upper bound for Q or leave blank."
490        self.qmax_ctl.SetToolTipString(hint_msg)
491        self.qmin_ctl.Bind(wx.EVT_TEXT, self._on_pars_changed)
492        self.qmax_ctl.Bind(wx.EVT_TEXT, self._on_pars_changed)
493
494        iy = 0
495        sizer_q.Add(label_qmin, (iy, 0), (1, 1), wx.LEFT | wx.EXPAND, 5)
496        sizer_q.Add(self.qmin_ctl, (iy, 1), (1, 1), wx.LEFT | wx.EXPAND, 5)
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        label_nfunc = wx.StaticText(self, -1, "Number of terms")
521        label_nfunc.SetMinSize((120, 20))
522        label_alpha = wx.StaticText(self, -1, "Regularization constant")
523        label_dmax = wx.StaticText(self, -1, "Max distance [A]")
524        self.label_sugg = wx.StaticText(self, -1, "Suggested value")
525
526        self.nfunc_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60, 20))
527        self.nfunc_ctl.SetToolTipString("Number of terms in the expansion.")
528        self.alpha_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60, 20))
529        hint_msg = "Control parameter for the size of the regularization term."
530        self.alpha_ctl.SetToolTipString(hint_msg)
531        self.dmax_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER, size=(60, 20))
532        hint_msg = "Maximum distance between any two points in the system."
533        self.dmax_ctl.SetToolTipString(hint_msg)
534        wx_id = wx.NewId()
535        self.alpha_estimate_ctl = wx.Button(self, wx_id, "")
536        self.Bind(wx.EVT_BUTTON, self._on_accept_alpha, id=wx_id)
537        self.alpha_estimate_ctl.Enable(False)
538        self.alpha_estimate_ctl.SetToolTipString("Waiting for estimate...")
539
540        wx_id = wx.NewId()
541        self.nterms_estimate_ctl = wx.Button(self, wx_id, "")
542        #self.nterms_estimate_ctl.Hide()
543        self.Bind(wx.EVT_BUTTON, self._on_accept_nterms, id=wx_id)
544        self.nterms_estimate_ctl.Enable(False)
545
546        self.nterms_estimate_ctl.SetToolTipString("Waiting for estimate...")
547
548        self.nfunc_ctl.Bind(wx.EVT_TEXT, self._read_pars)
549        self.alpha_ctl.Bind(wx.EVT_TEXT, self._read_pars)
550        self.dmax_ctl.Bind(wx.EVT_TEXT, self._on_pars_changed)
551
552        # Distance explorator
553        wx_id = wx.NewId()
554        self.distance_explorator_ctl = wx.Button(self, wx_id, "Explore")
555        self.Bind(wx.EVT_BUTTON, self._on_explore, id=wx_id)
556
557
558        sizer_params = wx.GridBagSizer(5, 5)
559
560        iy = 0
561        sizer_params.Add(self.label_sugg, (iy, 2), (1, 1), wx.LEFT, 15)
562        iy += 1
563        sizer_params.Add(label_nfunc, (iy, 0), (1, 1), wx.LEFT, 15)
564        sizer_params.Add(self.nfunc_ctl, (iy, 1), (1, 1), wx.RIGHT, 0)
565        sizer_params.Add(self.nterms_estimate_ctl, (iy, 2), (1, 1), wx.LEFT, 15)
566        iy += 1
567        sizer_params.Add(label_alpha, (iy, 0), (1, 1), wx.LEFT, 15)
568        sizer_params.Add(self.alpha_ctl, (iy, 1), (1, 1), wx.RIGHT, 0)
569        sizer_params.Add(self.alpha_estimate_ctl, (iy, 2), (1, 1), wx.LEFT, 15)
570        iy += 1
571        sizer_params.Add(label_dmax, (iy, 0), (1, 1), wx.LEFT, 15)
572        sizer_params.Add(self.dmax_ctl, (iy, 1), (1, 1), wx.RIGHT, 0)
573        sizer_params.Add(self.distance_explorator_ctl, (iy, 2),
574                         (1, 1), wx.LEFT, 15)
575
576        boxsizer2.Add(sizer_params, 0)
577
578        iy_vb += 1
579        vbox.Add(boxsizer2, (iy_vb, 0), (1, 1),
580                 wx.LEFT | wx.RIGHT | wx.EXPAND | wx.ADJUST_MINSIZE, 5)
581
582
583        # ----- Results -----
584        resbox = wx.StaticBox(self, -1, "Outputs")
585        ressizer = wx.StaticBoxSizer(resbox, wx.VERTICAL)
586        ressizer.SetMinSize((self._default_width, 50))
587
588        label_rg = wx.StaticText(self, -1, "Rg")
589        label_rg_unit = wx.StaticText(self, -1, "[A]")
590        label_iq0 = wx.StaticText(self, -1, "I(Q=0)")
591        label_iq0_unit = wx.StaticText(self, -1, "[A^(-1)]")
592        label_bck = wx.StaticText(self, -1, "Background")
593        label_bck_unit = wx.StaticText(self, -1, "[A^(-1)]")
594        self.rg_ctl = OutputTextCtrl(self, -1, size=(60, 20))
595        hint_msg = "Radius of gyration for the computed P(r)."
596        self.rg_ctl.SetToolTipString(hint_msg)
597        self.iq0_ctl = OutputTextCtrl(self, -1, size=(60, 20))
598        hint_msg = "Scattering intensity at Q=0 for the computed P(r)."
599        self.iq0_ctl.SetToolTipString(hint_msg)
600        self.bck_ctl = OutputTextCtrl(self, -1, size=(60, 20))
601        self.bck_ctl.SetToolTipString("Value of estimated constant background.")
602
603        label_time = wx.StaticText(self, -1, "Computation time")
604        label_time_unit = wx.StaticText(self, -1, "secs")
605        label_time.SetMinSize((120, 20))
606        label_chi2 = wx.StaticText(self, -1, "Chi2/dof")
607        label_osc = wx.StaticText(self, -1, "Oscillations")
608        label_pos = wx.StaticText(self, -1, "Positive fraction")
609        label_pos_err = wx.StaticText(self, -1, "1-sigma positive fraction")
610
611        self.time_ctl = OutputTextCtrl(self, -1, size=(60, 20))
612        hint_msg = "Computation time for the last inversion, in seconds."
613        self.time_ctl.SetToolTipString(hint_msg)
614
615        self.chi2_ctl = OutputTextCtrl(self, -1, size=(60, 20))
616        self.chi2_ctl.SetToolTipString("Chi^2 over degrees of freedom.")
617
618        # Oscillation parameter
619        self.osc_ctl = OutputTextCtrl(self, -1, size=(60, 20))
620        hint_msg = "Oscillation parameter. P(r) for a sphere has an "
621        hint_msg += " oscillation parameter of 1.1."
622        self.osc_ctl.SetToolTipString(hint_msg)
623
624        # Positive fraction figure of merit
625        self.pos_ctl = OutputTextCtrl(self, -1, size=(60, 20))
626        hint_msg = "Fraction of P(r) that is positive. "
627        hint_msg += "Theoretically, P(r) is defined positive."
628        self.pos_ctl.SetToolTipString(hint_msg)
629
630        # 1-simga positive fraction figure of merit
631        self.pos_err_ctl = OutputTextCtrl(self, -1, size=(60, 20))
632        message = "Fraction of P(r) that is at least 1 standard deviation"
633        message += " greater than zero.\n"
634        message += "This figure of merit tells you about the size of the "
635        message += "P(r) errors.\n"
636        message += "If it is close to 1 and the other figures of merit are bad,"
637        message += " consider changing the maximum distance."
638        self.pos_err_ctl.SetToolTipString(message)
639
640        sizer_res = wx.GridBagSizer(5, 5)
641
642        iy = 0
643        sizer_res.Add(label_rg, (iy, 0), (1, 1), wx.LEFT | wx.EXPAND, 15)
644        sizer_res.Add(self.rg_ctl, (iy, 1), (1, 1), wx.RIGHT | wx.EXPAND, 15)
645        sizer_res.Add(label_rg_unit, (iy, 2), (1, 1), wx.RIGHT | wx.EXPAND, 15)
646        iy += 1
647        sizer_res.Add(label_iq0, (iy, 0), (1, 1), wx.LEFT | wx.EXPAND, 15)
648        sizer_res.Add(self.iq0_ctl, (iy, 1), (1, 1), wx.RIGHT | wx.EXPAND, 15)
649        sizer_res.Add(label_iq0_unit, (iy, 2), (1, 1), wx.RIGHT | wx.EXPAND, 15)
650        iy += 1
651        sizer_res.Add(label_bck, (iy, 0), (1, 1), wx.LEFT | wx.EXPAND, 15)
652        sizer_res.Add(self.bck_ctl, (iy, 1), (1, 1), wx.RIGHT | wx.EXPAND, 15)
653        sizer_res.Add(label_bck_unit, (iy, 2), (1, 1), wx.RIGHT | wx.EXPAND, 15)
654        iy += 1
655        sizer_res.Add(label_time, (iy, 0), (1, 1), wx.LEFT | wx.EXPAND, 15)
656        sizer_res.Add(self.time_ctl, (iy, 1), (1, 1), wx.RIGHT | wx.EXPAND, 15)
657        sizer_res.Add(label_time_unit, (iy, 2), (1, 1), wx.RIGHT | wx.EXPAND, 15)
658        iy += 1
659        sizer_res.Add(label_chi2, (iy, 0), (1, 1), wx.LEFT | wx.EXPAND, 15)
660        sizer_res.Add(self.chi2_ctl, (iy, 1), (1, 1), wx.RIGHT | wx.EXPAND, 15)
661        iy += 1
662        sizer_res.Add(label_osc, (iy, 0), (1, 1), wx.LEFT | wx.EXPAND, 15)
663        sizer_res.Add(self.osc_ctl, (iy, 1), (1, 1), wx.RIGHT | wx.EXPAND, 15)
664
665        iy += 1
666        sizer_res.Add(label_pos, (iy, 0), (1, 1), wx.LEFT | wx.EXPAND, 15)
667        sizer_res.Add(self.pos_ctl, (iy, 1), (1, 1), wx.RIGHT | wx.EXPAND, 15)
668
669        iy += 1
670        sizer_res.Add(label_pos_err, (iy, 0), (1, 1), wx.LEFT | wx.EXPAND, 15)
671        sizer_res.Add(self.pos_err_ctl, (iy, 1), (1, 1), wx.RIGHT | wx.EXPAND, 15)
672
673        ressizer.Add(sizer_res, 0)
674        iy_vb += 1
675        vbox.Add(ressizer, (iy_vb, 0), (1, 1),
676                 wx.LEFT | wx.RIGHT | wx.EXPAND | wx.ADJUST_MINSIZE, 5)
677
678        # ----- Buttons -----
679        wx_id = wx.NewId()
680        button_ok = wx.Button(self, wx_id, "Compute")
681        button_ok.SetToolTipString("Perform P(r) inversion.")
682        self.Bind(wx.EVT_BUTTON, self._on_invert, id=wx_id)
683
684        self.button_help = wx.Button(self, -1, "HELP")
685        self.button_help.SetToolTipString("Get help on P(r) inversion.")
686        self.button_help.Bind(wx.EVT_BUTTON, self.on_help)
687
688        self._set_reset_flag(True)
689        self._set_save_flag(True)
690        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
691        sizer_button.Add((20, 20), 1, wx.EXPAND | wx.ADJUST_MINSIZE, 0)
692        sizer_button.Add(button_ok, 0, wx.LEFT | wx.ADJUST_MINSIZE, 10)
693        sizer_button.Add(self.button_help, 0, wx.LEFT | wx.ADJUST_MINSIZE, 10)
694
695        iy_vb += 1
696        vbox.Add(sizer_button, (iy_vb, 0), (1, 1),
697                 wx.EXPAND | wx.BOTTOM | wx.TOP | wx.RIGHT, 10)
698
699        self.Bind(wx.EVT_TEXT_ENTER, self._on_invert)
700
701        self.SetSizer(vbox)
702
703    def _on_accept_alpha(self, evt):
704        """
705        User has accepted the estimated alpha,
706        set it as part of the input parameters
707        """
708        try:
709            alpha = self.alpha_estimate_ctl.GetLabel()
710            # Check that we have a number
711            float(alpha)
712            self.alpha_ctl.SetValue(alpha)
713        except ValueError:
714            logger.error("InversionControl._on_accept_alpha got a value that was not a number: %s" % alpha )
715        except:
716            # No estimate or bad estimate, either do nothing
717            logger.error("InversionControl._on_accept_alpha: %s" % sys.exc_value)
718
719    def _on_accept_nterms(self, evt):
720        """
721        User has accepted the estimated number of terms,
722        set it as part of the input parameters
723        """
724        try:
725            nterms = self.nterms_estimate_ctl.GetLabel()
726            # Check that we have a number
727            float(nterms)
728            self.nfunc_ctl.SetValue(nterms)
729        except ValueError:
730            logger.error("InversionControl._on_accept_nterms got a value that was not a number: %s" % nterms )
731        except:
732            # No estimate or bad estimate, either do nothing
733            logger.error("InversionControl._on_accept_nterms: %s" % sys.exc_value)
734
735    def clear_panel(self):
736        """
737        """
738        self.plot_data.SetValue("")
739        self.on_reset(event=None)
740
741    def on_reset(self, event=None):
742        """
743        Resets inversion parameters
744        """
745        self.nfunc = self._manager.DEFAULT_NFUNC
746        self.d_max = self._manager.DEFAULT_DMAX
747        self.alpha = self._manager.DEFAULT_ALPHA
748        self.qmin_ctl.SetValue("")
749        self.qmax_ctl.SetValue("")
750        self.time_ctl.SetValue("")
751        self.rg_ctl.SetValue("")
752        self.iq0_ctl.SetValue("")
753        self.bck_ctl.SetValue("")
754        self.chi2_ctl.SetValue("")
755        self.osc_ctl.SetValue("")
756        self.pos_ctl.SetValue("")
757        self.pos_err_ctl.SetValue("")
758        self.alpha_estimate_ctl.Enable(False)
759        self.alpha_estimate_ctl.SetLabel("")
760        self.nterms_estimate_ctl.Enable(False)
761        self.nterms_estimate_ctl.SetLabel("")
762        self._set_analysis(False)
763
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.Show()
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 is 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 self._manager is not 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                self._set_analysis(True)
949            except:
950                msg = "InversionControl._change_file: %s" % sys.exc_value
951                logger.error(msg)
952
953    def on_help(self, event):
954        """
955        Bring up the P(r) Documentation whenever
956        the HELP button is clicked.
957
958        Calls DocumentationWindow with the path of the location within the
959        documentation tree (after /doc/ ....".  Note that when using old
960        versions of Wx (before 2.9) and thus not the release version of
961        installers, the help comes up at the top level of the file as
962        webbrowser does not pass anything past the # to the browser when it is
963        running "file:///...."
964
965    :param evt: Triggers on clicking the help button
966    """
967
968        _TreeLocation = "user/sasgui/perspectives/pr/pr_help.html"
969        _doc_viewer = DocumentationWindow(self, -1, _TreeLocation, "",
970                                          "P(r) Help")
971
972
973class PrDistDialog(wx.Dialog):
974    """
975    Property dialog to let the user change the number
976    of points on the P(r) plot.
977    """
978    def __init__(self, parent, id):
979        from sas.sascalc.pr.invertor import help
980        wx.Dialog.__init__(self, parent, id, size=(250, 120))
981        self.SetTitle("P(r) distribution")
982
983
984        vbox = wx.BoxSizer(wx.VERTICAL)
985
986        label_npts = wx.StaticText(self, -1, "Number of points")
987        self.npts_ctl = PrTextCtrl(self, -1, size=(100, 20))
988
989        pars_sizer = wx.GridBagSizer(5, 5)
990        iy = 0
991        pars_sizer.Add(label_npts, (iy, 0), (1, 1), wx.LEFT, 15)
992        pars_sizer.Add(self.npts_ctl, (iy, 1), (1, 1), wx.RIGHT, 0)
993
994        vbox.Add(pars_sizer, 0, wx.ALL | wx.EXPAND, 15)
995
996        static_line = wx.StaticLine(self, -1)
997        vbox.Add(static_line, 0, wx.EXPAND, 0)
998
999        button_ok = wx.Button(self, wx.ID_OK, "OK")
1000        self.Bind(wx.EVT_BUTTON, self._checkValues, button_ok)
1001        button_cancel = wx.Button(self, wx.ID_CANCEL, "Cancel")
1002
1003        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
1004        sizer_button.Add((20, 20), 1, wx.EXPAND | wx.ADJUST_MINSIZE, 0)
1005        sizer_button.Add(button_ok, 0, wx.LEFT | wx.RIGHT | wx.ADJUST_MINSIZE, 10)
1006        sizer_button.Add(button_cancel, 0,
1007                         wx.LEFT | wx.RIGHT | wx.ADJUST_MINSIZE, 10)
1008        vbox.Add(sizer_button, 0, wx.EXPAND | wx.BOTTOM | wx.TOP, 10)
1009
1010        self.SetSizer(vbox)
1011        self.SetAutoLayout(True)
1012
1013        self.Layout()
1014        self.Centre()
1015
1016    def _checkValues(self, event):
1017        """
1018        Check the dialog content.
1019        """
1020        flag = True
1021        try:
1022            int(self.npts_ctl.GetValue())
1023            self.npts_ctl.SetBackgroundColour(wx.WHITE)
1024            self.npts_ctl.Refresh()
1025        except:
1026            flag = False
1027            self.npts_ctl.SetBackgroundColour("pink")
1028            self.npts_ctl.Refresh()
1029        if flag:
1030            event.Skip(True)
1031
1032    def get_content(self):
1033        """
1034        Return the content of the dialog.
1035        At this point the values have already been
1036        checked.
1037        """
1038        value = int(self.npts_ctl.GetValue())
1039        return value
1040
1041    def set_content(self, npts):
1042        """
1043        Initialize the content of the dialog.
1044        """
1045        self.npts_ctl.SetValue("%i" % npts)
Note: See TracBrowser for help on using the repository browser.