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

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 0391dae was 5a58656, checked in by butler, 8 years ago

linked new help to pr panel

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