source: sasview/prview/perspectives/pr/inversion_panel.py @ 88f286d

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 88f286d was 57b551e, checked in by Gervaise Alina <gervyh@…>, 14 years ago

make sure start up config work for prview

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