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

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 ec392464 was 79492222, checked in by krzywon, 9 years ago

Changed the file and folder names to remove all SANS references.

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