source: sasview/prview/perspectives/pr/inversion_panel.py @ 9fb814a

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 9fb814a was 55f5a77, checked in by Gervaise Alina <gervyh@…>, 14 years ago

working on guiframe load data

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