source: sasview/prview/perspectives/pr/inversion_panel.py @ 357b79b

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

Minor improvements

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