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

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.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 5c3c310 was 5c3c310, checked in by lewis, 7 years ago

Add manual background input to Pr perspective

Manual background value isn't yet used in the calculation

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