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

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

This should fix the suggested value buttons.

  • 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.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            # Check that we have a number
699            float(alpha)
700            self.alpha_ctl.SetValue(alpha)
701        except ValueError:
702            logging.error("InversionControl._on_accept_alpha got a value that was not a number: %s" % alpha )
703        except:
704            # No estimate or bad estimate, either do nothing
705            logging.error("InversionControl._on_accept_alpha: %s" % sys.exc_value)
706
707    def _on_accept_nterms(self, evt):
708        """
709        User has accepted the estimated number of terms,
710        set it as part of the input parameters
711        """
712        try:
713            nterms = self.nterms_estimate_ctl.GetLabel()
714            # Check that we have a number
715            float(nterms)
716            self.nfunc_ctl.SetValue(nterms)
717        except ValueError:
718            logging.error("InversionControl._on_accept_nterms got a value that was not a number: %s" % nterms )
719        except:
720            # No estimate or bad estimate, either do nothing
721            logging.error("InversionControl._on_accept_nterms: %s" % sys.exc_value)
722
723    def clear_panel(self):
724        """
725        """
726        self.plot_data.SetValue("")
727        self.on_reset(event=None)
728
729    def on_reset(self, event=None):
730        """
731        Resets inversion parameters
732        """
733        self.nfunc = self._manager.DEFAULT_NFUNC
734        self.d_max = self._manager.DEFAULT_DMAX
735        self.alpha = self._manager.DEFAULT_ALPHA
736        self.qmin_ctl.SetValue("")
737        self.qmax_ctl.SetValue("")
738        self.time_ctl.SetValue("")
739        self.rg_ctl.SetValue("")
740        self.iq0_ctl.SetValue("")
741        self.bck_ctl.SetValue("")
742        self.chi2_ctl.SetValue("")
743        self.osc_ctl.SetValue("")
744        self.pos_ctl.SetValue("")
745        self.pos_err_ctl.SetValue("")
746        self.alpha_estimate_ctl.Enable(False)
747        self.alpha_estimate_ctl.SetLabel("")
748        self.nterms_estimate_ctl.Enable(False)
749        self.nterms_estimate_ctl.SetLabel("")
750        self._on_pars_changed()
751
752    def _on_pars_changed(self, evt=None):
753        """
754        Called when an input parameter has changed
755        We will estimate the alpha parameter behind the
756        scenes.
757        """
758        flag, alpha, dmax, nfunc, qmin, qmax, height, width = self._read_pars()
759        has_bck = self.bck_chk.IsChecked()
760
761        # If the pars are valid, estimate alpha
762        if flag:
763            self.nterms_estimate_ctl.Enable(False)
764            self.alpha_estimate_ctl.Enable(False)
765
766            dataset = self.plot_data.GetValue()
767            if dataset is not None and dataset.strip() != "":
768                self._manager.estimate_plot_inversion(alpha=alpha, nfunc=nfunc,
769                                                      d_max=dmax,
770                                                      q_min=qmin, q_max=qmax,
771                                                      bck=has_bck,
772                                                      height=height,
773                                                      width=width)
774
775    def _read_pars(self, evt=None):
776        """
777        """
778        alpha = 0
779        nfunc = 5
780        dmax = 120
781        qmin = 0
782        qmax = 0
783        height = 0
784        width = 0
785        flag = True
786        # Read slit height
787        try:
788            height_str = self.sheight_ctl.GetValue()
789            if len(height_str.lstrip().rstrip()) == 0:
790                height = 0
791            else:
792                height = float(height_str)
793                self.sheight_ctl.SetBackgroundColour(wx.WHITE)
794                self.sheight_ctl.Refresh()
795        except:
796            flag = False
797            self.sheight_ctl.SetBackgroundColour("pink")
798            self.sheight_ctl.Refresh()
799
800        # Read slit width
801        try:
802            width_str = self.swidth_ctl.GetValue()
803            if len(width_str.lstrip().rstrip()) == 0:
804                width = 0
805            else:
806                width = float(width_str)
807                self.swidth_ctl.SetBackgroundColour(wx.WHITE)
808                self.swidth_ctl.Refresh()
809        except:
810            flag = False
811            self.swidth_ctl.SetBackgroundColour("pink")
812            self.swidth_ctl.Refresh()
813
814        # Read alpha
815        try:
816            alpha = float(self.alpha_ctl.GetValue())
817            self.alpha_ctl.SetBackgroundColour(wx.WHITE)
818            self.alpha_ctl.Refresh()
819        except:
820            flag = False
821            self.alpha_ctl.SetBackgroundColour("pink")
822            self.alpha_ctl.Refresh()
823
824        # Read d_max
825        try:
826            dmax = float(self.dmax_ctl.GetValue())
827            self.dmax_ctl.SetBackgroundColour(wx.WHITE)
828            self.dmax_ctl.Refresh()
829        except:
830            flag = False
831            self.dmax_ctl.SetBackgroundColour("pink")
832            self.dmax_ctl.Refresh()
833
834        # Read nfunc
835        try:
836            nfunc = int(self.nfunc_ctl.GetValue())
837            npts = self._manager.get_npts()
838            if npts > 0 and nfunc > npts:
839                message = "Number of function terms should be smaller "
840                message += "than the number of points"
841                wx.PostEvent(self._manager.parent, StatusEvent(status=message))
842                raise ValueError, message
843            self.nfunc_ctl.SetBackgroundColour(wx.WHITE)
844            self.nfunc_ctl.Refresh()
845        except:
846            flag = False
847            self.nfunc_ctl.SetBackgroundColour("pink")
848            self.nfunc_ctl.Refresh()
849
850        # Read qmin
851        try:
852            qmin_str = self.qmin_ctl.GetValue()
853            if len(qmin_str.lstrip().rstrip()) == 0:
854                qmin = None
855            else:
856                qmin = float(qmin_str)
857                self.qmin_ctl.SetBackgroundColour(wx.WHITE)
858                self.qmin_ctl.Refresh()
859        except:
860            flag = False
861            self.qmin_ctl.SetBackgroundColour("pink")
862            self.qmin_ctl.Refresh()
863
864        # Read qmax
865        try:
866            qmax_str = self.qmax_ctl.GetValue()
867            if len(qmax_str.lstrip().rstrip()) == 0:
868                qmax = None
869            else:
870                qmax = float(qmax_str)
871                self.qmax_ctl.SetBackgroundColour(wx.WHITE)
872                self.qmax_ctl.Refresh()
873        except:
874            flag = False
875            self.qmax_ctl.SetBackgroundColour("pink")
876            self.qmax_ctl.Refresh()
877
878        return flag, alpha, dmax, nfunc, qmin, qmax, height, width
879
880    def _on_explore(self, evt):
881        """
882        Invoke the d_max exploration dialog
883        """
884        from explore_dialog import ExploreDialog
885        if self._manager._last_pr is not None:
886            pr = self._manager._create_plot_pr()
887            dialog = ExploreDialog(pr, 10, None, -1, "")
888            dialog.ShowModal()
889        else:
890            message = "No data to analyze. Please load a data set to proceed."
891            wx.PostEvent(self._manager.parent, StatusEvent(status=message))
892
893    def _on_invert(self, evt):
894        """
895        Perform inversion
896
897        :param silent: when True, there will be no output for the user
898
899        """
900        # Get the data from the form
901        # Push it to the manager
902
903        flag, alpha, dmax, nfunc, qmin, qmax, height, width = self._read_pars()
904        has_bck = self.bck_chk.IsChecked()
905
906        if flag:
907            dataset = self.plot_data.GetValue()
908            if dataset == None or len(dataset.strip()) == 0:
909                message = "No data to invert. Select a data set before"
910                message += " proceeding with P(r) inversion."
911                wx.PostEvent(self._manager.parent, StatusEvent(status=message))
912            else:
913                self._manager.setup_plot_inversion(alpha=alpha, nfunc=nfunc,
914                                                   d_max=dmax,
915                                                   q_min=qmin, q_max=qmax,
916                                                   bck=has_bck,
917                                                   height=height,
918                                                   width=width)
919        else:
920            message = "The P(r) form contains invalid values: "
921            message += "please submit it again."
922            wx.PostEvent(self.parent, StatusEvent(status=message))
923
924    def _change_file(self, evt=None, filepath=None, data=None):
925        """
926        Choose a new input file for I(q)
927        """
928        if not self._manager is None:
929            self.plot_data.SetValue(str(data.name))
930            try:
931                self._manager.show_data(data=data, reset=True)
932                self._on_pars_changed(None)
933                self._on_invert(None)
934            except:
935                msg = "InversionControl._change_file: %s" % sys.exc_value
936                logging.error(msg)
937
938class HelpDialog(wx.Dialog):
939    """
940    """
941    def __init__(self, parent, id):
942        """
943        """
944        from sas.pr.invertor import help
945        wx.Dialog.__init__(self, parent, id, size=(400, 450))
946        self.SetTitle("P(r) help")
947        self.SetWindowVariant(variant=FONT_VARIANT)
948
949        vbox = wx.BoxSizer(wx.VERTICAL)
950
951        explanation = help()
952
953        label_explain = wx.StaticText(self, -1, explanation, size=(360, 350))
954
955        vbox.Add(label_explain, 0, wx.ALL | wx.EXPAND, 15)
956
957
958        static_line = wx.StaticLine(self, -1)
959        vbox.Add(static_line, 0, wx.EXPAND, 0)
960
961        button_ok = wx.Button(self, wx.ID_OK, "OK")
962
963        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
964        sizer_button.Add((20, 20), 1, wx.EXPAND | wx.ADJUST_MINSIZE, 0)
965        sizer_button.Add(button_ok, 0, wx.LEFT | wx.RIGHT | wx.ADJUST_MINSIZE, 10)
966
967        vbox.Add(sizer_button, 0, wx.EXPAND | wx.BOTTOM | wx.TOP, 10)
968
969        self.SetSizer(vbox)
970        self.SetAutoLayout(True)
971
972        self.Layout()
973        self.Centre()
974
975class PrDistDialog(wx.Dialog):
976    """
977    Property dialog to let the user change the number
978    of points on the P(r) plot.
979    """
980    def __init__(self, parent, id):
981        from sas.pr.invertor import help
982        wx.Dialog.__init__(self, parent, id, size=(250, 120))
983        self.SetTitle("P(r) distribution")
984
985
986        vbox = wx.BoxSizer(wx.VERTICAL)
987
988        label_npts = wx.StaticText(self, -1, "Number of points")
989        self.npts_ctl = PrTextCtrl(self, -1, size=(100, 20))
990
991        pars_sizer = wx.GridBagSizer(5, 5)
992        iy = 0
993        pars_sizer.Add(label_npts, (iy, 0), (1, 1), wx.LEFT, 15)
994        pars_sizer.Add(self.npts_ctl, (iy, 1), (1, 1), wx.RIGHT, 0)
995
996        vbox.Add(pars_sizer, 0, wx.ALL | wx.EXPAND, 15)
997
998        static_line = wx.StaticLine(self, -1)
999        vbox.Add(static_line, 0, wx.EXPAND, 0)
1000
1001        button_ok = wx.Button(self, wx.ID_OK, "OK")
1002        self.Bind(wx.EVT_BUTTON, self._checkValues, button_ok)
1003        button_cancel = wx.Button(self, wx.ID_CANCEL, "Cancel")
1004
1005        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
1006        sizer_button.Add((20, 20), 1, wx.EXPAND | wx.ADJUST_MINSIZE, 0)
1007        sizer_button.Add(button_ok, 0, wx.LEFT | wx.RIGHT | wx.ADJUST_MINSIZE, 10)
1008        sizer_button.Add(button_cancel, 0,
1009                         wx.LEFT | wx.RIGHT | wx.ADJUST_MINSIZE, 10)
1010        vbox.Add(sizer_button, 0, wx.EXPAND | wx.BOTTOM | wx.TOP, 10)
1011
1012        self.SetSizer(vbox)
1013        self.SetAutoLayout(True)
1014
1015        self.Layout()
1016        self.Centre()
1017
1018    def _checkValues(self, event):
1019        """
1020        Check the dialog content.
1021        """
1022        flag = True
1023        try:
1024            int(self.npts_ctl.GetValue())
1025            self.npts_ctl.SetBackgroundColour(wx.WHITE)
1026            self.npts_ctl.Refresh()
1027        except:
1028            flag = False
1029            self.npts_ctl.SetBackgroundColour("pink")
1030            self.npts_ctl.Refresh()
1031        if flag:
1032            event.Skip(True)
1033
1034    def get_content(self):
1035        """
1036        Return the content of the dialog.
1037        At this point the values have already been
1038        checked.
1039        """
1040        value = int(self.npts_ctl.GetValue())
1041        return value
1042
1043    def set_content(self, npts):
1044        """
1045        Initialize the content of the dialog.
1046        """
1047        self.npts_ctl.SetValue("%i" % npts)
Note: See TracBrowser for help on using the repository browser.