source: sasview/prview/perspectives/pr/inversion_panel.py @ d85ee8c

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 d85ee8c was d85ee8c, checked in by Gervaise Alina <gervyh@…>, 13 years ago

add save project function pr prview

  • Property mode set to 100644
File size: 40.2 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        self._manager.save_data(filepath=path, prstate=state)
275       
276        return state
277   
278    def get_data(self):
279        """
280        """
281        return self._manager.get_data()
282   
283    def get_state(self):
284        """
285        Get the current state
286       
287        : return: state object
288        """
289        # Construct the state object   
290        state = InversionState()
291       
292        # Read the panel's parameters
293        flag, alpha, dmax, nfunc, qmin, \
294        qmax, height, width = self._read_pars()
295       
296        state.nfunc = nfunc
297        state.d_max = dmax
298        state.alpha = alpha
299        state.qmin  = qmin
300        state.qmax  = qmax
301        state.width = width
302        state.height = height
303       
304        # Data file
305        state.file = self.plot_data.GetValue()
306       
307        # Background evaluation checkbox
308        state.estimate_bck = self.bck_chk.IsChecked()
309       
310        # Estimates
311        state.nterms_estimate = self.nterms_estimate
312        state.alpha_estimate = self.alpha_estimate
313       
314        # Read the output values
315        state.chi2    = self.chi2
316        state.elapsed = self.elapsed
317        state.osc     = self.oscillation
318        state.pos     = self.pos
319        state.pos_err = self.pos_err
320        state.rg      = self.rg
321        state.iq0     = self.iq0
322        state.bck     = self.bck
323       
324        return state
325   
326    def set_state(self, state):
327        """
328        Set the state of the panel and inversion problem to
329        the state passed as a parameter.
330        Execute the inversion immediately after filling the
331        controls.
332       
333        :param state: InversionState object
334        """
335        if state.nfunc is not None:
336            self.nfunc = state.nfunc
337        if state.d_max is not None:
338            self.d_max = state.d_max
339        if state.alpha is not None:
340            self.alpha = state.alpha
341        if state.qmin is not None:
342            self.q_min  = state.qmin
343        if state.qmax is not None:
344            self.q_max  = state.qmax
345        if state.width is not None:
346            self.slit_width = state.width
347        if state.height is not None:
348            self.slit_height = state.height
349       
350        # Data file
351        self.plot_data.SetValue(str(state.file))
352   
353        # Background evaluation checkbox
354        self.bck_chk.SetValue(state.estimate_bck)
355       
356        # Estimates
357        if state.nterms_estimate is not None:
358            self.nterms_estimate = state.nterms_estimate
359        if state.alpha_estimate is not None: 
360            self.alpha_estimate = state.alpha_estimate
361   
362       
363        # Read the output values
364        if state.chi2 is not None:
365            self.chi2    = state.chi2
366        if state.elapsed is not None:
367            self.elapsed = state.elapsed
368        if state.osc is not None:
369            self.oscillation = state.osc
370        if state.pos is not None:
371            self.positive = state.pos
372        if state.pos_err is not None:
373            self.pos_err = state.pos_err
374        if state.rg is not None:
375            self.rg      = state.rg
376        if state.iq0 is not None:
377            self.iq0     = state.iq0
378        if state.bck is not None:
379            self.bck     = state.bck
380
381        # Perform inversion
382        self._on_invert(None)   
383       
384    def set_manager(self, manager):
385        self._manager = manager
386       
387    def _do_layout(self):
388        vbox = wx.GridBagSizer(0,0)
389        iy_vb = 0
390
391        # ----- I(q) data -----
392        databox = wx.StaticBox(self, -1, "I(q) data source")
393       
394        boxsizer1 = wx.StaticBoxSizer(databox, wx.VERTICAL)
395        boxsizer1.SetMinSize((self._default_width, 50))
396        pars_sizer = wx.GridBagSizer(5, 5)
397
398        iy = 0
399        self.file_radio = wx.StaticText(self, -1, "Data:")
400        pars_sizer.Add(self.file_radio, (iy, 0), (1, 1),
401                       wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
402       
403        self.plot_data = DataFileTextCtrl(self, -1, size=(260,20))
404       
405        pars_sizer.Add(self.plot_data, (iy, 1), (1, 1),
406                       wx.EXPAND|wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 15)
407       
408        self.bck_chk = wx.CheckBox(self, -1, "Estimate background level")
409        hint_msg = "Check box to let the fit estimate "
410        hint_msg += "the constant background level."
411        self.bck_chk.SetToolTipString(hint_msg)
412        self.bck_chk.Bind(wx.EVT_CHECKBOX, self._on_pars_changed)
413        iy += 1
414        pars_sizer.Add(self.bck_chk, (iy, 0), (1, 2),
415                       wx.LEFT|wx.EXPAND|wx.ADJUST_MINSIZE, 15)
416        boxsizer1.Add(pars_sizer, 0, wx.EXPAND) 
417        vbox.Add(boxsizer1, (iy_vb, 0), (1, 1),
418                 wx.LEFT|wx.RIGHT|wx.EXPAND|wx.ADJUST_MINSIZE|wx.TOP, 5)
419       
420        # ----- Add slit parameters -----
421        if True:
422            sbox = wx.StaticBox(self, -1, "Slit parameters")
423            sboxsizer = wx.StaticBoxSizer(sbox, wx.VERTICAL)
424            sboxsizer.SetMinSize((self._default_width, 20))
425           
426            sizer_slit = wx.GridBagSizer(5, 5)
427   
428            label_sheight = wx.StaticText(self, -1, "Height", size=(40, 20))
429            label_swidth = wx.StaticText(self, -1, "Width", size=(40, 20))
430            #label_sunits1 = wx.StaticText(self, -1, "[A^(-1)]")
431            label_sunits2 = wx.StaticText(self, -1, "[A^(-1)]", size=(55,20))
432            self.sheight_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER,
433                                          size=(60,20))
434            self.swidth_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER,
435                                         size=(60,20))
436            hint_msg = "Enter slit height in units of Q or leave blank."
437            self.sheight_ctl.SetToolTipString(hint_msg)
438            hint_msg = "Enter slit width in units of Q or leave blank."
439            self.swidth_ctl.SetToolTipString(hint_msg)
440            #self.sheight_ctl.Bind(wx.EVT_TEXT, self._on_pars_changed)
441            #self.swidth_ctl.Bind(wx.EVT_TEXT,  self._on_pars_changed)
442           
443            iy = 0
444            sizer_slit.Add(label_sheight,    (iy, 0), (1, 1),
445                           wx.LEFT|wx.EXPAND, 5)
446            sizer_slit.Add(self.sheight_ctl, (iy, 1), (1, 1),
447                           wx.LEFT|wx.EXPAND, 5)
448   
449            sizer_slit.Add(label_swidth,     (iy, 2), (1, 1),
450                           wx.LEFT|wx.EXPAND, 5)
451            sizer_slit.Add(self.swidth_ctl,  (iy, 3), (1, 1),
452                           wx.LEFT|wx.EXPAND, 5)
453            sizer_slit.Add(label_sunits2,    (iy, 4), (1, 1),
454                           wx.LEFT|wx.EXPAND, 5)
455           
456            sboxsizer.Add(sizer_slit, wx.TOP, 15)
457            iy_vb += 1
458            vbox.Add(sboxsizer, (iy_vb, 0), (1, 1),
459                     wx.LEFT|wx.RIGHT|wx.EXPAND|wx.ADJUST_MINSIZE, 5)
460       
461       
462        # ----- Q range -----
463        qbox = wx.StaticBox(self, -1, "Q range")
464        qboxsizer = wx.StaticBoxSizer(qbox, wx.VERTICAL)
465        qboxsizer.SetMinSize((self._default_width, 20))
466       
467        sizer_q = wx.GridBagSizer(5, 5)
468
469        label_qmin = wx.StaticText(self, -1, "Q min", size=(40, 20))
470        label_qmax = wx.StaticText(self, -1, "Q max", size=(40, 20))
471        #label_qunits1 = wx.StaticText(self, -1, "[A^(-1)]")
472        label_qunits2 = wx.StaticText(self, -1, "[A^(-1)]", size=(55, 20))
473        self.qmin_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER,
474                                   size=(60,20))
475        self.qmax_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER,
476                                   size=(60,20))
477        hint_msg = "Select a lower bound for Q or leave blank."
478        self.qmin_ctl.SetToolTipString(hint_msg)
479        hint_msg = "Select an upper bound for Q or leave blank."
480        self.qmax_ctl.SetToolTipString(hint_msg)
481        self.qmin_ctl.Bind(wx.EVT_TEXT, self._on_pars_changed)
482        self.qmax_ctl.Bind(wx.EVT_TEXT, self._on_pars_changed)
483       
484        iy = 0
485        sizer_q.Add(label_qmin,    (iy, 0), (1, 1), wx.LEFT|wx.EXPAND, 5)
486        sizer_q.Add(self.qmin_ctl, (iy, 1), (1, 1), wx.LEFT|wx.EXPAND, 5)
487        #sizer_q.Add(label_qunits1, (iy,2), (1,1), wx.LEFT|wx.EXPAND, 15)
488        sizer_q.Add(label_qmax,    (iy, 2), (1, 1), wx.LEFT|wx.EXPAND, 5)
489        sizer_q.Add(self.qmax_ctl, (iy, 3), (1, 1), wx.LEFT|wx.EXPAND, 5)
490        sizer_q.Add(label_qunits2, (iy, 4), (1, 1), wx.LEFT|wx.EXPAND, 5)
491        qboxsizer.Add(sizer_q, wx.TOP, 15)
492
493        iy_vb += 1
494        vbox.Add(qboxsizer, (iy_vb,0), (1,1),
495                 wx.LEFT|wx.RIGHT|wx.EXPAND|wx.ADJUST_MINSIZE, 5)
496   
497        # ----- Parameters -----
498        parsbox = wx.StaticBox(self, -1, "Parameters")
499        boxsizer2 = wx.StaticBoxSizer(parsbox, wx.VERTICAL)
500        boxsizer2.SetMinSize((self._default_width,50))
501       
502        explanation  = "P(r) is found by fitting a set of base functions"
503        explanation += " to I(Q). The minimization involves"
504        explanation += " a regularization term to ensure a smooth P(r)."
505        explanation += " The regularization constant gives the size of that " 
506        explanation += "term. The suggested value is the value above which the"
507        explanation += " output P(r) will have only one peak."
508        label_explain = wx.StaticText(self, -1, explanation, size=(280,120))
509        boxsizer2.Add(label_explain,  wx.LEFT|wx.BOTTOM, 5)
510       
511       
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        #self.label_sugg.Hide()
519       
520        self.nfunc_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER,
521                                    size=(60,20))
522        self.nfunc_ctl.SetToolTipString("Number of terms in the expansion.")
523        self.alpha_ctl = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER,
524                                    size=(60,20))
525        hint_msg = "Control parameter for the size of the regularization term."
526        self.alpha_ctl.SetToolTipString(hint_msg)
527        self.dmax_ctl  = PrTextCtrl(self, -1, style=wx.TE_PROCESS_ENTER,
528                                    size=(60,20))
529        hint_msg = "Maximum distance between any two points in the system."
530        self.dmax_ctl.SetToolTipString(hint_msg)
531        id = wx.NewId()
532        self.alpha_estimate_ctl  = wx.Button(self, id, "")
533        #self.alpha_estimate_ctl.Hide()
534        self.Bind(wx.EVT_BUTTON, self._on_accept_alpha, id = id)   
535        self.alpha_estimate_ctl.Enable(False)
536        #self.alpha_estimate_ctl.SetBackgroundColour('#ffdf85')
537        #self.alpha_estimate_ctl.SetBackgroundColour(self.GetBackgroundColour())
538        self.alpha_estimate_ctl.SetToolTipString("Waiting for estimate...")
539       
540        id = wx.NewId()
541        self.nterms_estimate_ctl  = wx.Button(self, id, "")
542        #self.nterms_estimate_ctl.Hide()
543        self.Bind(wx.EVT_BUTTON, self._on_accept_nterms, id = id)   
544        self.nterms_estimate_ctl.Enable(False)
545     
546        self.nterms_estimate_ctl.SetToolTipString("Waiting for estimate...")
547       
548        self.nfunc_ctl.Bind(wx.EVT_TEXT, self._read_pars)
549        self.alpha_ctl.Bind(wx.EVT_TEXT, self._read_pars)
550        self.dmax_ctl.Bind(wx.EVT_TEXT, self._on_pars_changed)
551       
552        # Distance explorator
553        id = wx.NewId()
554        self.distance_explorator_ctl = wx.Button(self, id, "Explore")
555        self.Bind(wx.EVT_BUTTON, self._on_explore, id = id)           
556       
557       
558        sizer_params = wx.GridBagSizer(5,5)
559
560        iy = 0
561        sizer_params.Add(self.label_sugg, (iy, 2), (1, 1), wx.LEFT, 15)
562        iy += 1
563        sizer_params.Add(label_nfunc,      (iy, 0), (1, 1), wx.LEFT, 15)
564        sizer_params.Add(self.nfunc_ctl,   (iy, 1), (1, 1), wx.RIGHT, 0)
565        sizer_params.Add(self.nterms_estimate_ctl, (iy, 2), (1, 1), wx.LEFT, 15)
566        iy += 1
567        sizer_params.Add(label_alpha,      (iy, 0), (1, 1), wx.LEFT, 15)
568        sizer_params.Add(self.alpha_ctl,   (iy, 1), (1, 1), wx.RIGHT, 0)
569        sizer_params.Add(self.alpha_estimate_ctl, (iy, 2), (1, 1), wx.LEFT, 15)
570        iy += 1
571        sizer_params.Add(label_dmax, (iy, 0), (1, 1), wx.LEFT, 15)
572        sizer_params.Add(self.dmax_ctl,   (iy, 1), (1, 1), wx.RIGHT, 0)
573        sizer_params.Add(self.distance_explorator_ctl, (iy, 2),
574                         (1, 1), wx.LEFT, 15)
575
576        boxsizer2.Add(sizer_params, 0)
577       
578        iy_vb += 1
579        vbox.Add(boxsizer2, (iy_vb, 0), (1, 1),
580                 wx.LEFT|wx.RIGHT|wx.EXPAND|wx.ADJUST_MINSIZE, 5)
581
582
583        # ----- Results -----
584        resbox = wx.StaticBox(self, -1, "Outputs")
585        ressizer = wx.StaticBoxSizer(resbox, wx.VERTICAL)
586        ressizer.SetMinSize((self._default_width, 50))
587       
588        label_rg       = wx.StaticText(self, -1, "Rg")
589        label_rg_unit  = wx.StaticText(self, -1, "[A]")
590        label_iq0      = wx.StaticText(self, -1, "I(Q=0)")
591        label_iq0_unit = wx.StaticText(self, -1, "[A^(-1)]")
592        label_bck      = wx.StaticText(self, -1, "Background")
593        label_bck_unit = wx.StaticText(self, -1, "[A^(-1)]")
594        self.rg_ctl    = OutputTextCtrl(self, -1, size=(60,20))
595        hint_msg = "Radius of gyration for the computed P(r)."
596        self.rg_ctl.SetToolTipString(hint_msg)
597        self.iq0_ctl   = OutputTextCtrl(self, -1, size=(60, 20))
598        hint_msg = "Scattering intensity at Q=0 for the computed P(r)."
599        self.iq0_ctl.SetToolTipString(hint_msg)
600        self.bck_ctl   = OutputTextCtrl(self, -1, size=(60, 20))
601        self.bck_ctl.SetToolTipString("Value of estimated constant background.")
602       
603        label_time = wx.StaticText(self, -1, "Computation time")
604        label_time_unit = wx.StaticText(self, -1, "secs")
605        label_time.SetMinSize((120,20))
606        label_chi2 = wx.StaticText(self, -1, "Chi2/dof")
607        label_osc = wx.StaticText(self, -1, "Oscillations")
608        label_pos = wx.StaticText(self, -1, "Positive fraction")
609        label_pos_err = wx.StaticText(self, -1, "1-sigma positive fraction")
610       
611        self.time_ctl = OutputTextCtrl(self, -1, size=(60, 20))
612        hint_msg = "Computation time for the last inversion, in seconds."
613        self.time_ctl.SetToolTipString(hint_msg)
614       
615        self.chi2_ctl = OutputTextCtrl(self, -1, size=(60, 20))
616        self.chi2_ctl.SetToolTipString("Chi^2 over degrees of freedom.")
617       
618        # Oscillation parameter
619        self.osc_ctl = OutputTextCtrl(self, -1, size=(60, 20))
620        hint_msg = "Oscillation parameter. P(r) for a sphere has an "
621        hint_msg += " oscillation parameter of 1.1."
622        self.osc_ctl.SetToolTipString(hint_msg)
623       
624        # Positive fraction figure of merit
625        self.pos_ctl = OutputTextCtrl(self, -1, size=(60, 20))
626        hint_msg = "Fraction of P(r) that is positive. "
627        hint_msg += "Theoretically, P(r) is defined positive."
628        self.pos_ctl.SetToolTipString(hint_msg)
629       
630        # 1-simga positive fraction figure of merit
631        self.pos_err_ctl = OutputTextCtrl(self, -1, size=(60, 20))
632        message  = "Fraction of P(r) that is at least 1 standard deviation"
633        message += " greater than zero.\n"
634        message += "This figure of merit tells you about the size of the "
635        message += "P(r) errors.\n"
636        message += "If it is close to 1 and the other figures of merit are bad,"
637        message += " consider changing the maximum distance."
638        self.pos_err_ctl.SetToolTipString(message)
639       
640        sizer_res = wx.GridBagSizer(5, 5)
641
642        iy = 0
643        sizer_res.Add(label_rg, (iy, 0), (1, 1), wx.LEFT|wx.EXPAND, 15)
644        sizer_res.Add(self.rg_ctl,   (iy, 1), (1, 1), wx.RIGHT|wx.EXPAND, 15)
645        sizer_res.Add(label_rg_unit,   (iy, 2), (1, 1), wx.RIGHT|wx.EXPAND, 15)
646        iy += 1
647        sizer_res.Add(label_iq0, (iy, 0), (1, 1), wx.LEFT|wx.EXPAND, 15)
648        sizer_res.Add(self.iq0_ctl,   (iy, 1), (1, 1), wx.RIGHT|wx.EXPAND, 15)
649        sizer_res.Add(label_iq0_unit, (iy, 2), (1, 1), wx.RIGHT|wx.EXPAND, 15)
650        iy += 1
651        sizer_res.Add(label_bck, (iy, 0), (1, 1), wx.LEFT|wx.EXPAND, 15)
652        sizer_res.Add(self.bck_ctl, (iy, 1), (1, 1), wx.RIGHT|wx.EXPAND, 15)
653        sizer_res.Add(label_bck_unit, (iy, 2), (1, 1), wx.RIGHT|wx.EXPAND, 15)
654        iy += 1
655        sizer_res.Add(label_time, (iy, 0), (1, 1), wx.LEFT|wx.EXPAND, 15)
656        sizer_res.Add(self.time_ctl,   (iy, 1), (1, 1), wx.RIGHT|wx.EXPAND, 15)
657        sizer_res.Add(label_time_unit, (iy, 2), (1, 1), wx.RIGHT|wx.EXPAND, 15)
658        iy += 1
659        sizer_res.Add(label_chi2, (iy, 0), (1, 1), wx.LEFT|wx.EXPAND, 15)
660        sizer_res.Add(self.chi2_ctl,   (iy, 1), (1, 1), wx.RIGHT|wx.EXPAND, 15)
661        iy += 1
662        sizer_res.Add(label_osc, (iy, 0), (1, 1), wx.LEFT|wx.EXPAND, 15)
663        sizer_res.Add(self.osc_ctl,   (iy, 1), (1, 1), wx.RIGHT|wx.EXPAND, 15)
664
665        iy += 1
666        sizer_res.Add(label_pos, (iy, 0), (1, 1), wx.LEFT|wx.EXPAND, 15)
667        sizer_res.Add(self.pos_ctl, (iy, 1), (1, 1), wx.RIGHT|wx.EXPAND, 15)
668
669        iy += 1
670        sizer_res.Add(label_pos_err, (iy, 0), (1, 1), wx.LEFT|wx.EXPAND, 15)
671        sizer_res.Add(self.pos_err_ctl,  (iy,1), (1, 1), wx.RIGHT|wx.EXPAND, 15)
672
673        ressizer.Add(sizer_res, 0)
674        iy_vb += 1
675        vbox.Add(ressizer, (iy_vb, 0), (1, 1),
676                 wx.LEFT|wx.RIGHT|wx.EXPAND|wx.ADJUST_MINSIZE, 5)
677
678        # ----- Buttons -----
679        id = wx.NewId()
680        button_OK = wx.Button(self, id, "Compute")
681        button_OK.SetToolTipString("Perform P(r) inversion.")
682        self.Bind(wx.EVT_BUTTON, self._on_invert, id = id)   
683       
684        self._set_reset_flag(True)
685        self._set_save_flag(True)
686        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
687        sizer_button.Add((20, 20), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
688        sizer_button.Add(button_OK, 0, wx.LEFT|wx.ADJUST_MINSIZE, 10)
689       
690        iy_vb += 1
691        vbox.Add(sizer_button, (iy_vb, 0), (1, 1),
692                 wx.EXPAND|wx.BOTTOM|wx.TOP|wx.RIGHT, 10)
693
694        self.Bind(wx.EVT_TEXT_ENTER, self._on_invert)
695
696        self.SetSizer(vbox)
697       
698    def _on_accept_alpha(self, evt):
699        """
700        User has accepted the estimated alpha,
701        set it as part of the input parameters
702        """
703        try:
704            alpha = self.alpha_estimate_ctl.GetLabel()
705            tmp = float(alpha)
706            self.alpha_ctl.SetValue(alpha)
707        except:
708            # No estimate or bad estimate, either do nothing
709            #import sys
710            print "InversionControl._on_accept_alpha: %s" % sys.exc_value
711            pass
712   
713    def _on_accept_nterms(self, evt):
714        """
715        User has accepted the estimated number of terms,
716        set it as part of the input parameters
717        """
718        try:
719            nterms = self.nterms_estimate_ctl.GetLabel()
720            tmp = float(nterms)
721            self.nfunc_ctl.SetValue(nterms)
722        except:
723            # No estimate or bad estimate, either do nothing
724            import sys
725            print "InversionControl._on_accept_nterms: %s" % sys.exc_value
726            pass
727       
728    def clear_panel(self):
729        """
730        """
731        self.plot_data.SetValue("")
732        self.on_reset(event=None)
733       
734    def on_reset(self, event=None):
735        """
736        Resets inversion parameters
737        """
738        self.nfunc = self._manager.DEFAULT_NFUNC
739        self.d_max = self._manager.DEFAULT_DMAX
740        self.alpha = self._manager.DEFAULT_ALPHA
741        self.qmin_ctl.SetValue("")
742        self.qmax_ctl.SetValue("")
743        self.time_ctl.SetValue("")
744        self.rg_ctl.SetValue("")
745        self.iq0_ctl.SetValue("")
746        self.bck_ctl.SetValue("")
747        self.chi2_ctl.SetValue("")
748        self.osc_ctl.SetValue("")
749        self.pos_ctl.SetValue("")
750        self.pos_err_ctl.SetValue("")
751        self.alpha_estimate_ctl.Enable(False)
752        self.alpha_estimate_ctl.SetLabel("")
753        self.nterms_estimate_ctl.Enable(False)
754        self.nterms_estimate_ctl.SetLabel("")
755        self._on_pars_changed()
756       
757    def _on_pars_changed(self, evt=None):
758        """
759        Called when an input parameter has changed
760        We will estimate the alpha parameter behind the
761        scenes.
762        """
763        flag, alpha, dmax, nfunc, qmin, qmax, height, width = self._read_pars()
764        has_bck = self.bck_chk.IsChecked()
765       
766        # If the pars are valid, estimate alpha
767        if flag:
768            self.nterms_estimate_ctl.Enable(False)
769            self.alpha_estimate_ctl.Enable(False)
770           
771            dataset = self.plot_data.GetValue()
772            if dataset is not None and dataset.strip() != "":
773                self._manager.estimate_plot_inversion(alpha=alpha, nfunc=nfunc, 
774                                                 d_max=dmax,
775                                                 q_min=qmin, q_max=qmax,
776                                                 bck=has_bck, 
777                                                 height=height,
778                                                 width=width)
779       
780    def _read_pars(self, evt=None):
781        """
782        """   
783        alpha = 0
784        nfunc = 5
785        dmax  = 120
786        qmin  = 0
787        qmax  = 0
788        height = 0
789        width  = 0
790        flag = True
791        # Read slit height
792        try:
793            height_str = self.sheight_ctl.GetValue()
794            if len(height_str.lstrip().rstrip()) == 0:
795                height = 0
796            else:
797                height = float(height_str)
798                self.sheight_ctl.SetBackgroundColour(wx.WHITE)
799                self.sheight_ctl.Refresh()
800        except:
801            flag = False
802            self.sheight_ctl.SetBackgroundColour("pink")
803            self.sheight_ctl.Refresh()
804           
805        # Read slit width
806        try:
807            width_str = self.swidth_ctl.GetValue()
808            if len(width_str.lstrip().rstrip())==0:
809                width = 0
810            else:
811                width = float(width_str)
812                self.swidth_ctl.SetBackgroundColour(wx.WHITE)
813                self.swidth_ctl.Refresh()
814        except:
815            flag = False
816            self.swidth_ctl.SetBackgroundColour("pink")
817            self.swidth_ctl.Refresh()
818       
819        # Read alpha
820        try:
821            alpha = float(self.alpha_ctl.GetValue())
822            self.alpha_ctl.SetBackgroundColour(wx.WHITE)
823            self.alpha_ctl.Refresh()
824        except:
825            flag = False
826            self.alpha_ctl.SetBackgroundColour("pink")
827            self.alpha_ctl.Refresh()
828       
829        # Read d_max   
830        try:
831            dmax = float(self.dmax_ctl.GetValue())
832            self.dmax_ctl.SetBackgroundColour(wx.WHITE)
833            self.dmax_ctl.Refresh()
834        except:
835            flag = False
836            self.dmax_ctl.SetBackgroundColour("pink")
837            self.dmax_ctl.Refresh()
838           
839        # Read nfunc
840        try:
841            nfunc = int(self.nfunc_ctl.GetValue())
842            npts = self._manager.get_npts()
843            if npts > 0 and nfunc > npts:
844                message = "Number of function terms should be smaller "
845                message += "than the number of points"
846                wx.PostEvent(self._manager.parent, StatusEvent(status=message))
847                raise ValueError, message
848            self.nfunc_ctl.SetBackgroundColour(wx.WHITE)
849            self.nfunc_ctl.Refresh()
850        except:
851            flag = False
852            self.nfunc_ctl.SetBackgroundColour("pink")
853            self.nfunc_ctl.Refresh()
854       
855        # Read qmin
856        try:
857            qmin_str = self.qmin_ctl.GetValue()
858            if len(qmin_str.lstrip().rstrip()) == 0:
859                qmin = None
860            else:
861                qmin = float(qmin_str)
862                self.qmin_ctl.SetBackgroundColour(wx.WHITE)
863                self.qmin_ctl.Refresh()
864        except:
865            flag = False
866            self.qmin_ctl.SetBackgroundColour("pink")
867            self.qmin_ctl.Refresh()
868       
869        # Read qmax
870        try:
871            qmax_str = self.qmax_ctl.GetValue()
872            if len(qmax_str.lstrip().rstrip()) == 0:
873                qmax = None
874            else:
875                qmax = float(qmax_str)
876                self.qmax_ctl.SetBackgroundColour(wx.WHITE)
877                self.qmax_ctl.Refresh()
878        except:
879            flag = False
880            self.qmax_ctl.SetBackgroundColour("pink")
881            self.qmax_ctl.Refresh()
882       
883        return flag, alpha, dmax, nfunc, qmin, qmax, height, width
884   
885    def _on_explore(self, evt):
886        """
887        Invoke the d_max exploration dialog
888        """
889        from explore_dialog import ExploreDialog
890        if self._manager._last_pr is not None:
891            pr = self._manager._create_plot_pr()
892            dialog = ExploreDialog(pr, 10, None, -1, "")
893            dialog.ShowModal()
894        else:
895            message = "No data to analyze. Please load a data set to proceed."
896            wx.PostEvent(self._manager.parent, StatusEvent(status=message))
897           
898    def _on_invert(self, evt):
899        """
900        Perform inversion
901       
902        :param silent: when True, there will be no output for the user
903       
904        """
905        # Get the data from the form
906        # Push it to the manager
907       
908        flag, alpha, dmax, nfunc, qmin, qmax, height, width = self._read_pars()
909        has_bck = self.bck_chk.IsChecked()
910       
911        if flag:
912            dataset = self.plot_data.GetValue()
913            if dataset==None or len(dataset.strip())==0:
914                message = "No data to invert. Select a data set before"
915                message += " proceeding with P(r) inversion."
916                wx.PostEvent(self._manager.parent, StatusEvent(status=message))
917            else:
918                self._manager.setup_plot_inversion(alpha=alpha, nfunc=nfunc, 
919                                                  d_max=dmax,
920                                                  q_min=qmin, q_max=qmax,
921                                                  bck=has_bck,
922                                                  height=height,
923                                                  width=width)
924        else:
925            message = "The P(r) form contains invalid values: "
926            message += "please submit it again."
927            wx.PostEvent(self.parent, StatusEvent(status=message))
928       
929    def _change_file(self, evt=None, filepath=None, data=None):
930        """
931        Choose a new input file for I(q)
932        """
933        if not self._manager is None:
934            self.plot_data.SetValue(str(data.name))
935            try:
936                self._manager.show_data(data=data, reset=True)
937                self._on_pars_changed(None)
938                self._on_invert(None)
939            except:
940                msg = "InversionControl._change_file: %s" % sys.exc_value
941                logging.error(msg)                   
942
943class HelpDialog(wx.Dialog):
944    """
945    """
946    def __init__(self, parent, id):
947        """
948        """
949        from sans.pr.invertor import help
950        wx.Dialog.__init__(self, parent, id, size=(400, 420))
951        self.SetTitle("P(r) help") 
952       
953
954        vbox = wx.BoxSizer(wx.VERTICAL)
955
956        explanation = help()
957           
958        label_explain = wx.StaticText(self, -1, explanation, size=(350, 320))
959           
960        vbox.Add(label_explain, 0, wx.ALL|wx.EXPAND, 15)
961
962
963        static_line = wx.StaticLine(self, -1)
964        vbox.Add(static_line, 0, wx.EXPAND, 0)
965       
966        button_OK = wx.Button(self, wx.ID_OK, "OK")
967        #button_Cancel = wx.Button(self, wx.ID_CANCEL, "Cancel")
968       
969        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
970        sizer_button.Add((20, 20), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
971        sizer_button.Add(button_OK, 0, wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 10)
972       
973        vbox.Add(sizer_button, 0, wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
974
975        self.SetSizer(vbox)
976        self.SetAutoLayout(True)
977       
978        self.Layout()
979        self.Centre()
980
981class PrDistDialog(wx.Dialog):
982    """
983    Property dialog to let the user change the number
984    of points on the P(r) plot.
985    """
986    def __init__(self, parent, id):
987        from sans.pr.invertor import help
988        wx.Dialog.__init__(self, parent, id, size=(250, 120))
989        self.SetTitle("P(r) distribution") 
990       
991
992        vbox = wx.BoxSizer(wx.VERTICAL)
993       
994        label_npts = wx.StaticText(self, -1, "Number of points")
995        self.npts_ctl = PrTextCtrl(self, -1, size=(100, 20))
996                 
997        pars_sizer = wx.GridBagSizer(5, 5)
998        iy = 0
999        pars_sizer.Add(label_npts, (iy, 0), (1, 1), wx.LEFT, 15)
1000        pars_sizer.Add(self.npts_ctl, (iy, 1), (1, 1), wx.RIGHT, 0)
1001       
1002        vbox.Add(pars_sizer, 0, wx.ALL|wx.EXPAND, 15)
1003
1004        static_line = wx.StaticLine(self, -1)
1005        vbox.Add(static_line, 0, wx.EXPAND, 0)
1006       
1007        button_OK = wx.Button(self, wx.ID_OK, "OK")
1008        self.Bind(wx.EVT_BUTTON, self._checkValues, button_OK)
1009        button_Cancel = wx.Button(self, wx.ID_CANCEL, "Cancel")
1010       
1011        sizer_button = wx.BoxSizer(wx.HORIZONTAL)
1012        sizer_button.Add((20, 20), 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
1013        sizer_button.Add(button_OK, 0, wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 10)
1014        sizer_button.Add(button_Cancel, 0,
1015                         wx.LEFT|wx.RIGHT|wx.ADJUST_MINSIZE, 10)       
1016        vbox.Add(sizer_button, 0, wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1017
1018        self.SetSizer(vbox)
1019        self.SetAutoLayout(True)
1020       
1021        self.Layout()
1022        self.Centre()
1023
1024    def _checkValues(self, event):
1025        """
1026        Check the dialog content.
1027        """
1028        flag = True
1029        try:
1030            int(self.npts_ctl.GetValue())
1031            self.npts_ctl.SetBackgroundColour(wx.WHITE)
1032            self.npts_ctl.Refresh()
1033        except:
1034            flag = False
1035            self.npts_ctl.SetBackgroundColour("pink")
1036            self.npts_ctl.Refresh()
1037        if flag:
1038            event.Skip(True)
1039
1040    def get_content(self):
1041        """
1042        Return the content of the dialog.
1043        At this point the values have already been
1044        checked.
1045        """
1046        value = int(self.npts_ctl.GetValue())
1047        return value
1048   
1049    def set_content(self, npts):
1050        """
1051        Initialize the content of the dialog.
1052        """
1053        self.npts_ctl.SetValue("%i" % npts)
1054
1055##### testing code ############################################################
1056"""
1057Example: ::
1058
1059class TestPlot:
1060    def __init__(self, text):
1061        self.name = text
1062   
1063class MyApp(wx.App):
1064    def OnInit(self):
1065        wx.InitAllImageHandlers()
1066        dialog = PrDistDialog(None, -1)
1067        if dialog.ShowModal() == wx.ID_OK:
1068            pass
1069        dialog.Destroy()
1070       
1071        return 1
1072
1073# end of class MyApp
1074
1075if __name__ == "__main__":
1076    app = MyApp(0)
1077    app.MainLoop()
1078   
1079"""
1080##### end of testing code #####################################################   
Note: See TracBrowser for help on using the repository browser.