source: sasview/src/sas/perspectives/pr/inversion_panel.py @ 8d302cd

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 8d302cd was 8b21fa7, checked in by Doucet, Mathieu <doucetm@…>, 10 years ago

pylint fixes

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