source: sasview/calculatorview/src/sans/perspectives/calculator/resolution_calculator_panel.py @ b025572

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 b025572 was b025572, checked in by Jae Cho <jhjcho@…>, 12 years ago

cleanup the _pylab_helpers.Gcf.figs for resolution panel on close: that caused C++ error on sansview exit

  • Property mode set to 100644
File size: 61.2 KB
Line 
1"""
2   This software was developed by the University of Tennessee as part of the
3Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
4project funded by the US National Science Foundation.
5
6See the license text in license.txt
7
8copyright 2008, 2009, 2010 University of Tennessee
9"""
10import wx
11import sys
12import os
13import matplotlib
14import math
15#Use the WxAgg back end. The Wx one takes too long to render
16matplotlib.use('WXAgg')
17from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
18from matplotlib.backends.backend_wxagg import NavigationToolbar2Wx as Toolbar
19from matplotlib.backend_bases import FigureManagerBase
20# Wx-Pylab magic for displaying plots within an application's window.
21from matplotlib import _pylab_helpers
22# The Figure object is used to create backend-independent plot representations.
23from matplotlib.figure import Figure
24
25#from sans.guicomm.events import StatusEvent 
26from sans.calculator.resolution_calculator import ResolutionCalculator
27from sans.guiframe.events import StatusEvent 
28from calculator_widgets import OutputTextCtrl
29from calculator_widgets import InputTextCtrl
30from wx.lib.scrolledpanel import ScrolledPanel
31from math import fabs
32_BOX_WIDTH = 100
33_Q_DEFAULT = 0.0
34#Slit length panel size
35if sys.platform.count("win32") > 0:
36    PANEL_WIDTH = 525
37    PANEL_HEIGHT = 653
38    FONT_VARIANT = 0
39    IS_WIN = True
40else:
41    PANEL_WIDTH = 540
42    PANEL_HEIGHT = 662
43    FONT_VARIANT = 1
44    IS_WIN = False
45
46_SOURCE_MASS = {'Alpha':6.64465620E-24,
47                'Deuteron':3.34358320E-24,
48                'Neutron':1.67492729E-24, 
49                'Photon': 0.0,
50                'Proton':1.67262137E-24,
51                'Triton':5.00826667E-24}
52
53class ResolutionCalculatorPanel(ScrolledPanel):
54    """
55    Provides the Resolution calculator GUI.
56    """
57    ## Internal nickname for the window, used by the AUI manager
58    window_name = "SANS Resolution Estimator"
59    ## Name to appear on the window title bar
60    window_caption = ""
61    ## Flag to tell the AUI manager to put this panel in the center pane
62    CENTER_PANE = True
63   
64    def __init__(self, parent,  *args, **kwds):
65        kwds["size"]= (PANEL_WIDTH * 2, PANEL_HEIGHT)
66        kwds["style"]= wx.FULL_REPAINT_ON_RESIZE
67        ScrolledPanel.__init__(self, parent, *args, **kwds)
68        self.SetupScrolling()
69        self.parent = parent
70       
71        # input defaults
72        self.qx = []
73        self.qy = []
74        # dQ defaults
75        self.sigma_r = None
76        self.sigma_phi = None
77        self.sigma_1d = None
78        # monchromatic or polychromatic
79        self.wave_color = 'mono'
80        self.num_wave = 10
81        self.spectrum_dic = {}
82        # dQ 2d image
83        self.image = None
84        # results of sigmas
85        self.sigma_strings =' '
86        #Font size
87        self.SetWindowVariant(variant=FONT_VARIANT)
88        # Object that receive status event
89        self.resolution = ResolutionCalculator()
90        # Source selection dic
91        self.source_mass = _SOURCE_MASS
92        #layout attribute
93        self.hint_sizer = None
94        # detector coordinate of estimation of sigmas
95        self.det_coordinate = 'cartesian'
96        self.source_cb = None
97        self._do_layout()
98
99    def _define_structure(self):
100        """
101        Define the main sizers building to build this application.
102        """
103        self.main_sizer = wx.BoxSizer(wx.HORIZONTAL)
104        self.vertical_l_sizer = wx.BoxSizer(wx.VERTICAL)
105        self.vertical_r_spacer = wx.BoxSizer(wx.VERTICAL)
106        self.vertical_r_frame = wx.StaticBox(self, -1, '')
107        self.vertical_r_sizer = wx.StaticBoxSizer(self.vertical_r_frame,
108                                                  wx.VERTICAL)
109        self.box_source = wx.StaticBox(self, -1,
110                                str(self.window_caption))
111        self.boxsizer_source = wx.StaticBoxSizer(self.box_source,
112                                                    wx.VERTICAL)
113        self.mass_sizer = wx.BoxSizer(wx.HORIZONTAL)
114        self.intensity_sizer = wx.BoxSizer(wx.HORIZONTAL)
115        self.wavelength_sizer = wx.BoxSizer(wx.HORIZONTAL)
116        self.wavelength_spread_sizer = wx.BoxSizer(wx.HORIZONTAL)
117        self.source_aperture_sizer = wx.BoxSizer(wx.HORIZONTAL)
118        self.sample_aperture_sizer = wx.BoxSizer(wx.HORIZONTAL)
119        self.source2sample_distance_sizer = wx.BoxSizer(wx.HORIZONTAL)
120        self.sample2sample_distance_sizer = wx.BoxSizer(wx.HORIZONTAL)
121        self.sample2detector_distance_sizer = wx.BoxSizer(wx.HORIZONTAL)
122        self.detector_size_sizer = wx.BoxSizer(wx.HORIZONTAL)
123        self.detector_pix_size_sizer = wx.BoxSizer(wx.HORIZONTAL)
124        #self.detector_offset_sizer = wx.BoxSizer(wx.HORIZONTAL)
125        self.input_sizer = wx.BoxSizer(wx.VERTICAL)
126        self.output_sizer = wx.BoxSizer(wx.VERTICAL)
127        self.hint_sizer = wx.BoxSizer(wx.HORIZONTAL)
128        self.button_sizer = wx.BoxSizer(wx.HORIZONTAL)
129       
130    def _layout_mass(self):
131        """
132        Fill the sizer containing mass
133        """
134        # get the mass
135        mass_value = str(self.resolution.mass)
136        self.mass_txt = wx.StaticText(self, -1, 
137                                'Source: ')
138        self.mass_hint = "Mass of Neutrons m = %s [g]"\
139                                 % str(self.resolution.mass)
140        self.source_cb = wx.ComboBox(self, -1,
141                                style=wx.CB_READONLY,
142                                name = '%s'%mass_value)
143        # Sort source name because wx2.9 on Mac does not support CB_SORT
144        # Custom sorting
145        source_list = []
146        for key, value in self.source_mass.iteritems():
147            name_source = str(key)
148            source_list.append(name_source)
149        source_list.sort()
150        for idx in range(len(source_list)):
151            self.source_cb.Append(source_list[idx],idx)
152        self.source_cb.SetStringSelection("Neutron") 
153        wx.EVT_COMBOBOX(self.source_cb,-1, self._on_source_selection) 
154       
155        # combo box for color
156        self.wave_color_cb =  wx.ComboBox(self, -1,
157                                style=wx.CB_READONLY,
158                                name = 'color')
159        # two choices
160        self.wave_color_cb.Append('Monochromatic')
161        self.wave_color_cb.Append('TOF')
162        self.wave_color_cb.SetStringSelection("Monochromatic") 
163        wx.EVT_COMBOBOX(self.wave_color_cb,-1, self._on_source_color) 
164       
165        source_hint = "Source Selection: Affect on"
166        source_hint += " the gravitational contribution.\n"
167        source_hint += "Mass of %s: m = %s [g]" % \
168                            ('Neutron', str(self.resolution.mass))
169        self.mass_txt.SetToolTipString(source_hint)
170        self.mass_sizer.AddMany([(self.mass_txt, 0, wx.LEFT, 15),
171                                    (self.source_cb, 0, wx.LEFT, 15),
172                                    (self.wave_color_cb, 0, wx.LEFT, 15)])   
173       
174    def _layout_intensity(self):
175        """
176        Fill the sizer containing intensity
177        """
178        # get the intensity
179        intensity_value = str(self.resolution.intensity)
180        intensity_unit_txt = wx.StaticText(self, -1, '[counts/s]')
181        intensity_txt = wx.StaticText(self, -1, 
182                                'Intensity: ')
183        self.intensity_tcl = InputTextCtrl(self, -1, 
184                                        size=(_BOX_WIDTH,-1))
185        intensity_hint = "Intensity of Neutrons"
186        self.intensity_tcl.SetValue(intensity_value)
187        self.intensity_tcl.SetToolTipString(intensity_hint)
188        self.intensity_sizer.AddMany([(intensity_txt, 0, wx.LEFT, 15),
189                                    (self.intensity_tcl, 0, wx.LEFT, 15),
190                                    (intensity_unit_txt,0, wx.LEFT, 10)])   
191
192       
193    def _layout_wavelength(self):
194        """
195        Fill the sizer containing wavelength
196        """
197        # get the wavelength
198        wavelength_value = str(self.resolution.get_wavelength())
199        wavelength_unit_txt = wx.StaticText(self, -1, '[A]')
200        wavelength_txt = wx.StaticText(self, -1, 
201                                'Wavelength: ')
202        self.wavelength_tcl = InputTextCtrl(self, -1, 
203                                         size=(_BOX_WIDTH,-1))
204        wavelength_hint = "Wavelength of Neutrons"
205        self.wavelength_tcl.SetValue(wavelength_value)
206        self.wavelength_tcl.SetToolTipString(wavelength_hint)
207
208        # get the spectrum
209        spectrum_value = self.resolution.get_default_spectrum()
210        self.spectrum_dic['Add new'] = ''
211        self.spectrum_dic['Flat'] = spectrum_value
212       
213        self.spectrum_txt = wx.StaticText(self, -1, 
214                                'Spectrum: ')
215        self.spectrum_cb = wx.ComboBox(self, -1,
216                                style=wx.CB_READONLY,
217                                size=(_BOX_WIDTH,-1),
218                                name = 'spectrum')
219        self.spectrum_cb.Append('Add new')
220        self.spectrum_cb.Append('Flat')
221        wx.EVT_COMBOBOX(self.spectrum_cb, -1, self._on_spectrum_cb) 
222        spectrum_hint = "Wavelength Spectrum: Intensity vs. wavelength"
223        #self.spectrum_cb.SetValue(spectrum_value)
224        self.spectrum_cb.SetStringSelection('Flat') 
225        self.spectrum_cb.SetToolTipString(spectrum_hint)
226        self.wavelength_sizer.AddMany([(wavelength_txt, 0, wx.LEFT, 15),
227                                    (self.wavelength_tcl, 0, wx.LEFT, 5),
228                                    (wavelength_unit_txt,0, wx.LEFT, 5),
229                                    (self.spectrum_txt, 0, wx.LEFT, 20),
230                                    (self.spectrum_cb, 0, wx.LEFT, 5)])   
231        self.spectrum_txt.Show(False)
232        self.spectrum_cb.Show(False)
233       
234    def _layout_wavelength_spread(self):
235        """
236        Fill the sizer containing wavelength
237        """
238        # get the wavelength
239        wavelength_spread_value = str(self.resolution.get_wavelength_spread())
240        wavelength_spread_unit_txt = wx.StaticText(self, -1, '')
241        wavelength_spread_txt = wx.StaticText(self, -1, 
242                                'Wavelength Spread: ')
243        self.wavelength_spread_tcl = InputTextCtrl(self, -1, 
244                                         size=(_BOX_WIDTH,-1))
245        wavelength_spread_hint = "Wavelength  Spread of Neutrons"
246        self.wavelength_spread_tcl.SetValue(wavelength_spread_value)
247        self.wavelength_spread_tcl.SetToolTipString(wavelength_spread_hint)
248        self.wavelength_spread_sizer.AddMany([(wavelength_spread_txt, 0, 
249                                               wx.LEFT, 15),
250                                (self.wavelength_spread_tcl, 0, wx.LEFT, 15),
251                                (wavelength_spread_unit_txt,0, wx.LEFT, 10)])     
252         
253       
254    def _layout_source_aperture(self):
255        """
256        Fill the sizer containing source aperture size
257        """
258        # get the wavelength
259        source_aperture_value = str(self.resolution.source_aperture_size[0])
260        if len(self.resolution.source_aperture_size)>1:
261            source_aperture_value += ", "
262            source_aperture_value += str(\
263                                    self.resolution.source_aperture_size[1])
264        source_aperture_unit_txt = wx.StaticText(self, -1, '[cm]')
265        source_aperture_txt = wx.StaticText(self, -1, 
266                                'Source Aperture Size: ')
267        self.source_aperture_tcl = InputTextCtrl(self, -1, 
268                                         size=(_BOX_WIDTH,-1))
269        source_aperture_hint = "Source Aperture Size"
270        self.source_aperture_tcl.SetValue(source_aperture_value)
271        self.source_aperture_tcl.SetToolTipString(source_aperture_hint)
272        self.source_aperture_sizer.AddMany([(source_aperture_txt, 0, 
273                                               wx.LEFT, 15),
274                                (self.source_aperture_tcl, 0, wx.LEFT, 15),
275                                    (source_aperture_unit_txt,0, wx.LEFT, 10)]) 
276
277       
278    def _layout_sample_aperture(self):
279        """
280        Fill the sizer containing sample aperture size
281        """
282        # get the wavelength
283        sample_aperture_value = str(self.resolution.sample_aperture_size[0])
284        if len(self.resolution.sample_aperture_size)>1:
285            sample_aperture_value += ", "
286            sample_aperture_value += str(\
287                                    self.resolution.sample_aperture_size[1])
288        sample_aperture_unit_txt = wx.StaticText(self, -1, '[cm]')
289        sample_aperture_txt = wx.StaticText(self, -1, 
290                                'Sample Aperture Size: ')
291        self.sample_aperture_tcl = InputTextCtrl(self, -1, 
292                                         size=(_BOX_WIDTH,-1))
293        sample_aperture_hint = "Sample Aperture Size"
294        self.sample_aperture_tcl.SetValue(sample_aperture_value)
295        self.sample_aperture_tcl.SetToolTipString(sample_aperture_hint)
296        self.sample_aperture_sizer.AddMany([(sample_aperture_txt, 0, 
297                                               wx.LEFT, 15),
298                                (self.sample_aperture_tcl, 0, wx.LEFT, 15),
299                                    (sample_aperture_unit_txt,0, wx.LEFT, 10)]) 
300
301
302    def _layout_source2sample_distance(self):
303        """
304        Fill the sizer containing souce2sample_distance
305        """
306        # get the wavelength
307        source2sample_distance_value = str(\
308                                    self.resolution.source2sample_distance[0])
309
310        source2sample_distance_unit_txt = wx.StaticText(self, -1, '[cm]')
311        source2sample_distance_txt = wx.StaticText(self, -1, 
312                                'Source to Sample Aperture Distance: ')
313        self.source2sample_distance_tcl = InputTextCtrl(self, -1, 
314                                         size=(_BOX_WIDTH,-1))
315        source2sample_distance_hint = "Source to Sample Aperture Distance"
316        self.source2sample_distance_tcl.SetValue(source2sample_distance_value)
317        self.source2sample_distance_tcl.SetToolTipString(\
318                                                source2sample_distance_hint)
319        self.source2sample_distance_sizer.AddMany([(source2sample_distance_txt, 
320                                               0, wx.LEFT, 15),
321                            (self.source2sample_distance_tcl, 0, wx.LEFT, 15),
322                            (source2sample_distance_unit_txt,0, wx.LEFT, 10)]) 
323
324    def _layout_sample2sample_distance(self):
325        """
326        Fill the sizer containing sampleslit2sample_distance
327        """
328        # get the distance
329        sample2sample_distance_value = str(\
330                                    self.resolution.sample2sample_distance[0])
331
332        sample2sample_distance_unit_txt = wx.StaticText(self, -1, '[cm]')
333        sample2sample_distance_txt = wx.StaticText(self, -1, 
334                                'Sample Offset: ')
335        self.sample2sample_distance_tcl = InputTextCtrl(self, -1, 
336                                         size=(_BOX_WIDTH,-1))
337        sample2sample_distance_hint = "Sample Aperture to Sample Distance"
338        self.sample2sample_distance_tcl.SetValue(sample2sample_distance_value)
339        self.sample2sample_distance_tcl.SetToolTipString(\
340                                                sample2sample_distance_hint)
341        self.sample2sample_distance_sizer.AddMany([(sample2sample_distance_txt, 
342                                               0, wx.LEFT, 15),
343                            (self.sample2sample_distance_tcl, 0, wx.LEFT, 15),
344                            (sample2sample_distance_unit_txt,0, wx.LEFT, 10)]) 
345
346
347
348    def _layout_sample2detector_distance(self):
349        """
350        Fill the sizer containing sample2detector_distance
351        """
352        # get the wavelength
353        sample2detector_distance_value = str(\
354                                    self.resolution.sample2detector_distance[0])
355
356        sample2detector_distance_unit_txt = wx.StaticText(self, -1, '[cm]')
357        sample2detector_distance_txt = wx.StaticText(self, -1, 
358                                'Sample Aperture to Detector Distance: ')
359        self.sample2detector_distance_tcl = InputTextCtrl(self, -1, 
360                                         size=(_BOX_WIDTH,-1))
361        sample2detector_distance_hint = \
362                                "Sample Aperture to Detector Distance"
363        self.sample2detector_distance_tcl.SetValue(\
364                                                sample2detector_distance_value)
365        self.sample2detector_distance_tcl.SetToolTipString(\
366                                                sample2detector_distance_hint)
367        self.sample2detector_distance_sizer.AddMany([\
368                            (sample2detector_distance_txt, 0, wx.LEFT, 15),       
369                            (self.sample2detector_distance_tcl, 0, wx.LEFT, 15),
370                            (sample2detector_distance_unit_txt,0, wx.LEFT, 10)]) 
371       
372    def _layout_detector_size(self):
373        """
374        Fill the sizer containing detector size
375        """
376        # get the wavelength
377        detector_size_value = str(self.resolution.detector_size[0])
378        if len(self.resolution.detector_size)>1:
379            detector_size_value += ", "
380            detector_size_value += str(self.resolution.detector_size[1])
381        detector_size_unit_txt = wx.StaticText(self, -1, '')
382        detector_size_txt = wx.StaticText(self, -1, 
383                                'Number of Pixels on Detector: ')
384        self.detector_size_tcl = InputTextCtrl(self, -1, 
385                                         size=(_BOX_WIDTH,-1))
386        detector_size_hint = "Number of Pixels on Detector"
387        self.detector_size_tcl.SetValue(detector_size_value)
388        self.detector_size_tcl.SetToolTipString(detector_size_hint)
389        self.detector_size_sizer.AddMany([(detector_size_txt, 0, 
390                                               wx.LEFT, 15),
391                                (self.detector_size_tcl, 0, wx.LEFT, 15),
392                                    (detector_size_unit_txt,0, wx.LEFT, 10)]) 
393
394       
395    def _layout_detector_pix_size(self):
396        """
397        Fill the sizer containing detector pixel size
398        """
399        # get the detector_pix_size
400        detector_pix_size_value = str(self.resolution.detector_pix_size[0])
401        if len(self.resolution.detector_pix_size)>1:
402            detector_pix_size_value += ", "
403            detector_pix_size_value += str(self.resolution.detector_pix_size[1])
404        detector_pix_size_unit_txt = wx.StaticText(self, -1, '[cm]')
405        detector_pix_size_txt = wx.StaticText(self, -1, 
406                                'Detector Pixel Size: ')
407        self.detector_pix_size_tcl = InputTextCtrl(self, -1, 
408                                         size=(_BOX_WIDTH,-1))
409        detector_pix_size_hint = "Detector Pixel Size"
410        self.detector_pix_size_tcl.SetValue(detector_pix_size_value)
411        self.detector_pix_size_tcl.SetToolTipString(detector_pix_size_hint)
412        self.detector_pix_size_sizer.AddMany([(detector_pix_size_txt, 0, 
413                                               wx.LEFT, 15),
414                                (self.detector_pix_size_tcl, 0, wx.LEFT, 15),
415                                (detector_pix_size_unit_txt,0, wx.LEFT, 10)])
416       
417    def _layout_input(self):
418        """
419        Fill the sizer containing inputs; qx, qy
420        """
421       
422        q_title = wx.StaticText(self, -1, 
423                            "[Q Location of the Estimation]:")
424        # sizers for inputs
425        inputQx_sizer = wx.BoxSizer(wx.HORIZONTAL)
426        inputQy_sizer = wx.BoxSizer(wx.HORIZONTAL)
427        # get the default dq
428        qx_value = str(_Q_DEFAULT)
429        qy_value = str(_Q_DEFAULT)
430        qx_unit_txt = wx.StaticText(self, -1, '[1/A]  ')
431        qy_unit_txt = wx.StaticText(self, -1, '[1/A]  ')
432        qx_name_txt = wx.StaticText(self, -1, 
433                                'Qx: ')
434        qy_name_txt = wx.StaticText(self, -1, 
435                                'Qy: ')
436        self.qx_tcl = InputTextCtrl(self, -1, 
437                                         size=(_BOX_WIDTH*3,-1))
438        self.qy_tcl = InputTextCtrl(self, -1, 
439                                         size=(_BOX_WIDTH*3,-1))
440        qx_hint = "Type the Qx value."
441        qy_hint = "Type the Qy value."
442        self.qx_tcl.SetValue(qx_value)
443        self.qy_tcl.SetValue(qy_value)
444        self.qx_tcl.SetToolTipString(qx_hint)
445        self.qy_tcl.SetToolTipString(qy_hint)
446        inputQx_sizer.AddMany([(qx_name_txt, 0, wx.LEFT, 15),
447                                    (self.qx_tcl, 0, wx.LEFT, 15),
448                                    (qx_unit_txt, 0, wx.LEFT, 15)])
449        inputQy_sizer.AddMany([(qy_name_txt, 0, wx.LEFT, 15),
450                                    (self.qy_tcl, 0, wx.LEFT, 15),
451                                    (qy_unit_txt, 0, wx.LEFT, 15)])
452        self.input_sizer.AddMany([(q_title, 0, wx.LEFT, 15), 
453                                    (inputQx_sizer, 0,
454                                     wx.EXPAND|wx.TOP|wx.BOTTOM, 5),
455                                    (inputQy_sizer, 0, 
456                                     wx.EXPAND|wx.TOP|wx.BOTTOM, 5)])
457                                #(self.compute_button, 0, wx.LEFT, 30)])
458       
459    def _layout_output(self):
460        """
461        Fill the sizer containing dQ|| and dQ+
462        """
463        sigma_title = wx.StaticText(self, -1, 
464                        "[Standard Deviation of the Resolution Distribution]:")
465         # sizers for inputs
466        outputQxy_sizer = wx.BoxSizer(wx.HORIZONTAL)
467        outputQ_sizer = wx.BoxSizer(wx.HORIZONTAL)
468        sigma_unit = '['+'1/A' +']'
469        self.sigma_r_txt = wx.StaticText(self, -1, 
470                                           'Sigma_x:   ')
471        self.sigma_r_tcl = OutputTextCtrl(self, -1, 
472                                                 size=(_BOX_WIDTH*0.8,-1))
473        self.sigma_phi_txt = wx.StaticText(self, -1, 
474                                           'Sigma_y:')
475        self.sigma_phi_tcl = OutputTextCtrl(self, -1, 
476                                                 size=(_BOX_WIDTH*0.8,-1))
477        self.sigma_lamd_txt = wx.StaticText(self, -1, 
478                                           'Sigma_lamd:')
479        self.sigma_lamd_tcl = OutputTextCtrl(self, -1, 
480                                                 size=(_BOX_WIDTH*0.7,-1))
481        sigma_1d_txt = wx.StaticText(self, -1, '( 1D:   Sigma:')
482        self.sigma_1d_tcl = OutputTextCtrl(self, -1, 
483                                                 size=(_BOX_WIDTH*0.7,-1))
484        sigmax_hint = " The x component of the geometric resolution,"
485        sigmax_hint +=  " excluding sigma_lamda."
486        sigmay_hint = " The y component of the geometric resolution,"
487        sigmay_hint +=  " excluding sigma_lamda."
488        sigma_hint_lamd = " The wavelength contribution in the radial direction"
489        sigma_hint_lamd += ".\n Note: The phi component is always zero."
490        sigma_hint_1d = " Resolution in 1-dimension (for 1D data)."
491        self.sigma_r_tcl.SetToolTipString(sigmax_hint)
492        self.sigma_phi_tcl.SetToolTipString(sigmay_hint)
493        self.sigma_lamd_tcl.SetToolTipString(sigma_hint_lamd)
494        self.sigma_1d_tcl.SetToolTipString(sigma_hint_1d)
495        sigma_r_unit_txt = wx.StaticText(self, -1, sigma_unit)
496        sigma_phi_unit_txt = wx.StaticText(self, -1, sigma_unit)
497        sigma_lamd_unit_txt = wx.StaticText(self, -1, sigma_unit)
498        sigma_1d_unit_txt = wx.StaticText(self, -1, sigma_unit+' )')
499        outputQxy_sizer.AddMany([(self.sigma_r_txt, 0, wx.LEFT, 15),
500                                    (self.sigma_r_tcl, 0, wx.LEFT, 15),
501                                    (sigma_r_unit_txt, 0, wx.LEFT, 15),
502                                    (self.sigma_phi_txt, 0, wx.LEFT, 15),
503                                    (self.sigma_phi_tcl, 0, wx.LEFT, 15),
504                                    (sigma_phi_unit_txt, 0, wx.LEFT, 15)])
505        outputQ_sizer.AddMany([(self.sigma_lamd_txt, 0, wx.LEFT, 15),
506                                    (self.sigma_lamd_tcl, 0, wx.LEFT, 15),
507                                    (sigma_lamd_unit_txt, 0, wx.LEFT, 15),
508                                    (sigma_1d_txt, 0, wx.LEFT, 15),
509                                    (self.sigma_1d_tcl, 0, wx.LEFT, 15),
510                                    (sigma_1d_unit_txt, 0, wx.LEFT, 15)])
511        self.output_sizer.AddMany([(sigma_title, 0, wx.LEFT, 15), 
512                                    (outputQxy_sizer, 0,
513                                     wx.EXPAND|wx.TOP|wx.BOTTOM, 5),
514                                    (outputQ_sizer, 0, 
515                                     wx.EXPAND|wx.TOP|wx.BOTTOM, 5)])
516       
517   
518    def _layout_hint(self):
519        """
520        Fill the sizer containing hint
521        """
522        hint_msg = ""
523        #hint_msg += "This tool is to approximately compute "
524        #hint_msg += "the instrumental resolution (dQ)."
525
526        self.hint_txt = wx.StaticText(self, -1, hint_msg)
527        self.hint_sizer.AddMany([(self.hint_txt, 0, wx.LEFT, 15)])
528   
529    def _layout_button(self): 
530        """
531        Do the layout for the button widgets
532        """ 
533        # coordinate selction removed
534        #outerbox_txt = wx.StaticText(self, -1, 'Outer Box')
535        #self.x_y_rb = wx.RadioButton(self, -1,"Cartesian")
536        #self.Bind(wx.EVT_RADIOBUTTON,
537        #          self._on_xy_coordinate, id=self.x_y_rb.GetId())
538        #self.r_phi_rb = wx.RadioButton(self, -1,"Polar")
539        #self.Bind(wx.EVT_RADIOBUTTON,
540        #          self._on_rp_coordinate, id=self.r_phi_rb.GetId())
541        #self.r_phi_rb.SetValue(True)
542        #reset button
543        id = wx.NewId()
544        self.reset_button = wx.Button(self, id, "Reset")
545        hint_on_reset = "..."
546        self.reset_button.SetToolTipString(hint_on_reset)
547        self.Bind(wx.EVT_BUTTON, self.on_reset, id=id)
548        #compute button
549        id = wx.NewId()
550        self.compute_button = wx.Button(self, id, "Compute")
551        hint_on_compute = "Compute... Please wait until finished."
552        self.compute_button.SetToolTipString(hint_on_compute)
553        self.Bind(wx.EVT_BUTTON, self.on_compute, id=id)
554        # close button
555        self.bt_close = wx.Button(self, wx.ID_CANCEL,'Close')
556        self.bt_close.Bind(wx.EVT_BUTTON, self.on_close)
557        self.bt_close.SetToolTipString("Close this window.")
558        """
559        self.button_sizer.AddMany([(self.r_phi_rb,  0, wx.LEFT, 15),
560                                   (self.x_y_rb,  0, wx.LEFT, 15),
561                                   (self.reset_button, 0, wx.LEFT, 50),
562                                   (self.compute_button, 0, wx.LEFT, 15),
563                                   (self.bt_close, 0, wx.LEFT, 15)])#370)])
564        """
565        self.button_sizer.Add((110, -1))
566        self.button_sizer.AddMany([(self.reset_button, 0, wx.LEFT, 50),
567                                   (self.compute_button, 0, wx.LEFT, 15),
568                                   (self.bt_close, 0, wx.LEFT, 15)])
569        self.compute_button.SetFocus()
570       
571    def _layout_image(self):
572        """
573        Layout for image plot
574        """
575        color = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)
576
577        # Contribution by James C.
578        # Instantiate a figure object that will contain our plots.
579        # Make the fig a little smaller than the default
580        self.figure = Figure(figsize=(6.5, 6), facecolor = 'white')
581       
582        # Initialize the figure canvas, mapping the figure object to the plot
583        # engine backend.
584        self.canvas = FigureCanvas(self, wx.ID_ANY, self.figure)
585
586        # Wx-Pylab magic ...
587        # Make our canvas the active figure manager for pylab so that when
588        # pylab plotting statements are executed they will operate on our
589        # canvas and not create a new frame and canvas for display purposes.
590        # This technique allows this application to execute code that uses
591        # pylab stataments to generate plots and embed these plots in our
592        # application window(s).
593        self.fm = FigureManagerBase(self.canvas, 1)
594        _pylab_helpers.Gcf.set_active(self.fm)
595       
596        # Instantiate the matplotlib navigation toolbar and explicitly show it.
597        mpl_toolbar = Toolbar(self.canvas)
598        # Diable pan
599        mpl_toolbar.DeleteToolByPos(3)
600
601        # Add a toolbar into the frame
602        mpl_toolbar.Realize()
603
604        # Compute before adding the canvas to the sizer
605        self.on_compute()
606       
607        # Fill up the sizer
608        if IS_WIN:
609            gap = 27
610        else:
611            gap = 13
612        self.vertical_r_sizer.Add(self.canvas, 0, 
613                                       wx.ALL|wx.EXPAND, 2) 
614        self.vertical_r_spacer.Add((0, gap)) 
615        self.vertical_r_spacer.Add(self.vertical_r_sizer, 0, 
616                                       wx.ALL|wx.EXPAND, 2)
617        self.vertical_r_spacer.Add((0, gap)) 
618        self.vertical_r_spacer.Add(wx.StaticLine(self), 0, 
619                                       wx.ALL|wx.EXPAND, 2)
620        self.vertical_r_spacer.Add(mpl_toolbar, 0,  wx.ALL|wx.EXPAND, 2)
621
622       
623    def _do_layout(self):
624        """
625            Draw window layout
626        """
627        #  Title of parameters
628        instrument_txt = wx.StaticText(self, -1, 
629                                '[Instrumental Parameters]:')
630        # Build individual layouts
631        self._define_structure()
632        self._layout_mass()
633        #self._layout_intensity()
634        self._layout_wavelength()
635        self._layout_wavelength_spread()
636        self._layout_source_aperture()
637        self._layout_sample_aperture()
638        self._layout_source2sample_distance()
639        self._layout_sample2detector_distance()
640        self._layout_sample2sample_distance()
641        self._layout_detector_size()
642        self._layout_detector_pix_size()
643        self._layout_input()
644        self._layout_output()
645        self._layout_hint()
646        self._layout_button()
647        # Fill the sizers
648        self.boxsizer_source.AddMany([(instrument_txt, 0,
649                                       wx.EXPAND|wx.LEFT, 15),
650                                      (self.mass_sizer, 0,
651                                      wx.EXPAND|wx.TOP|wx.BOTTOM, 5),
652                                      #(self.intensity_sizer, 0,
653                                      #wx.EXPAND|wx.TOP|wx.BOTTOM, 5),
654                                      (self.wavelength_sizer, 0,
655                                      wx.EXPAND|wx.TOP|wx.BOTTOM, 5),
656                                      (self.wavelength_spread_sizer, 0,
657                                      wx.EXPAND|wx.TOP|wx.BOTTOM, 5),
658                                      (self.source_aperture_sizer, 0,
659                                      wx.EXPAND|wx.TOP|wx.BOTTOM, 5),
660                                      (self.sample_aperture_sizer, 0,
661                                      wx.EXPAND|wx.TOP|wx.BOTTOM, 5),
662                                      (self.source2sample_distance_sizer, 0,
663                                      wx.EXPAND|wx.TOP|wx.BOTTOM, 5),
664                                      (self.sample2detector_distance_sizer, 0,
665                                      wx.EXPAND|wx.TOP|wx.BOTTOM, 5),
666                                      (self.sample2sample_distance_sizer, 0,
667                                      wx.EXPAND|wx.TOP|wx.BOTTOM, 5),
668                                      (self.detector_size_sizer, 0,
669                                      wx.EXPAND|wx.TOP|wx.BOTTOM, 5),
670                                      (self.detector_pix_size_sizer, 0,
671                                      wx.EXPAND|wx.TOP|wx.BOTTOM, 5),
672                                      (wx.StaticLine(self), 0, 
673                                       wx.ALL|wx.EXPAND, 5),
674                                      (self.input_sizer, 0,
675                                      wx.EXPAND|wx.TOP|wx.BOTTOM, 5),
676                                      (wx.StaticLine(self), 0, 
677                                       wx.ALL|wx.EXPAND, 5),
678                                      (self.output_sizer, 0,
679                                      wx.EXPAND|wx.TOP|wx.BOTTOM, 5)])
680        self.vertical_l_sizer.AddMany([(self.boxsizer_source, 0, wx.ALL, 10),
681                                       (wx.StaticLine(self), 0, 
682                                       wx.ALL|wx.EXPAND, 5),
683                                  (self.button_sizer, 0,
684                                    wx.EXPAND|wx.TOP|wx.BOTTOM, 5)])
685        self.main_sizer.Add(self.vertical_l_sizer, 0, wx.ALL, 10)
686       
687        # Build image plot layout                     
688        self._layout_image()
689        # Add a vertical static line
690        self.main_sizer.Add( wx.StaticLine(self, -1, (2,2), 
691                            (2,PANEL_HEIGHT * 0.94), style = wx.LI_VERTICAL))
692        # Add the plot to main sizer
693        self.main_sizer.Add(self.vertical_r_spacer, 0, wx.ALL, 10)
694        self.SetSizer(self.main_sizer)
695        self.SetAutoLayout(True)
696       
697
698    def on_close(self, event):
699        """
700        close the window containing this panel
701        """
702        # get ready for other events
703        if event is not None:
704            event.Skip()
705        # Clear the plot
706        if self.image != None:
707            self.image.clf()
708            # reset image
709            self.image = None
710        # Close panel
711        self.parent.OnClose(None)
712       
713   
714    def on_compute(self, event = None): 
715        """
716        Execute the computation of resolution
717        """
718        wx.CallAfter(self.on_compute_call, event)
719       
720    def on_compute_call(self, event = None):
721        """
722        Execute the computation of resolution
723        """
724        # Skip event for next event
725        if event != None:
726            event.Skip()
727            msg = "Please Check your input values "
728            msg += "before starting the computation..."
729
730        # message
731        status_type = 'progress' 
732        msg = 'Calculating...'
733        self._status_info(msg, status_type)
734
735        status_type = 'stop'           
736        # Q min max list default
737        qx_min = []   
738        qx_max = [] 
739        qy_min = [] 
740        qy_max = [] 
741        # possible max qrange
742        self.resolution.qxmin_limit = 0
743        self.resolution.qxmax_limit = 0
744        self.resolution.qymin_limit = 0
745        self.resolution.qymax_limit = 0
746        # q min and max of the detector
747        try:
748            # Get all the values at set to compute
749            # default num bin of wave list
750            self.num_wave = 10
751            wavelength = self._str2longlist(self.wavelength_tcl.GetValue())
752            source = self.source_cb.GetValue()
753            mass = self.source_mass[str(source)]
754            self.resolution.set_neutron_mass(float(mass))
755            wavelength_spread = self._str2longlist(\
756                        self.wavelength_spread_tcl.GetValue().split(';')[0])
757            # Validate the wave inputs
758            wave_input = self._validate_q_input(wavelength, wavelength_spread)
759            if wave_input != None:
760                wavelength, wavelength_spread = wave_input
761   
762            #self.resolution.set_wave(float(wavelength))
763            self.resolution.set_wave(wavelength)
764            #self.resolution.set_wave_spread(float(wavelength_spread))
765            self.resolution.set_wave_spread(wavelength_spread)
766            source_aperture_size = self.source_aperture_tcl.GetValue()
767            source_aperture_size = self._string2list(source_aperture_size)
768            self.resolution.set_source_aperture_size(source_aperture_size)
769            sample_aperture_size = self.sample_aperture_tcl.GetValue()
770            sample_aperture_size = self._string2list(sample_aperture_size)
771            self.resolution.set_sample_aperture_size(sample_aperture_size)
772            source2sample_distance = self.source2sample_distance_tcl.GetValue()
773            source2sample_distance = self._string2list(source2sample_distance)
774            self.resolution.set_source2sample_distance(source2sample_distance)
775            sample2sample_distance = self.sample2sample_distance_tcl.GetValue()
776            sample2sample_distance = self._string2list(sample2sample_distance)
777            self.resolution.set_sample2sample_distance(sample2sample_distance)
778            sample2detector_distance = self.sample2detector_distance_tcl.GetValue()
779            sample2detector_distance = self._string2list(sample2detector_distance)
780            self.resolution.set_sample2detector_distance(sample2detector_distance)
781            detector_size = self.detector_size_tcl.GetValue()
782            detector_size = self._string2list(detector_size)
783            self.resolution.set_detector_size(detector_size)
784            detector_pix_size = self.detector_pix_size_tcl.GetValue()
785            detector_pix_size = self._string2list(detector_pix_size)
786            self.resolution.set_detector_pix_size(detector_pix_size)
787            self.qx = self._string2inputlist(self.qx_tcl.GetValue())
788            self.qy = self._string2inputlist(self.qy_tcl.GetValue())
789           
790           
791            # Find min max of qs
792            xmin = min(self.qx)
793            xmax = max(self.qx)
794            ymin = min(self.qy)
795            ymax = max(self.qy)
796            if not self._validate_q_input(self.qx, self.qy):
797                raise
798        except:
799            msg = "An error occured during the resolution computation."
800            msg += "Please check your inputs..."
801            self._status_info(msg, status_type)
802            wx.MessageBox(msg, 'Warning')
803            return
804            #raise ValueError, "Invalid Q Input..."
805
806        # Validate the q inputs
807        q_input = self._validate_q_input(self.qx, self.qy)
808        if q_input != None:
809            self.qx, self.qy = q_input
810       
811        # Make list of q min max for mapping
812        for length in range(len(self.qx)):
813            qx_min.append(xmin)
814            qx_max.append(xmax)
815        for length in range(len(self.qy)):
816            qy_min.append(ymin)
817            qy_max.append(ymax)
818       
819        # Compute the resolution
820        if self.image != None:
821            #_pylab_helpers.Gcf.set_active(self.fm)
822            # Clear the image before redraw
823            self.image.clf()
824            # reset the image
825            self.resolution.reset_image()
826
827        # Compute and get the image plot
828        try:
829            from sans.perspectives.calculator.resolcal_thread import CalcRes as thread
830            self.sigma_strings = '\nResolution: Computation is finished. \n'
831            cal_res = thread(func = self._map_func,
832                         qx = self.qx,
833                         qy = self.qy,
834                         qx_min = qx_min,
835                         qx_max = qx_max,
836                         qy_min = qy_min,
837                         qy_max = qy_max,
838                         image = self.image,
839                         completefn = self.complete)
840            #self.image = map(self._map_func, self.qx, self.qy,
841            #                 qx_min, qx_max, qy_min, qy_max)[0]
842            cal_res.queue()
843            msg = "Computation is in progress..."
844            #msg = "Finished the resolution computation..."
845            status_type = 'progress'
846            self._status_info(msg, status_type)
847        except:
848            raise
849            msg = "An error occured during the resolution computation."
850            msg += "Please check your inputs..."
851            status_type = 'stop'
852            self._status_info(msg, status_type)
853            wx.MessageBox(msg, 'Warning')
854           
855    def complete(self, image, elapsed=None):
856        """
857        Callafter complete: wx call after needed for stable output
858        """
859        wx.CallAfter(self.complete_cal, image, elapsed)   
860       
861    def complete_cal(self, image, elapsed=None):
862        """
863        Complete computation
864        """
865        self.image = image
866        # Draw lines in image before drawing
867        wave_list, _ = self.resolution.get_wave_list()
868        if len(wave_list) > 1 and wave_list[-1] == max(wave_list):
869            # draw a green rectangle(limit for the longest wavelength
870            # to be involved) for tof inputs
871            self._draw_lines(self.image, color='g')
872        self._draw_lines(self.image, color='r')
873        # Draw image
874        self.image.draw()
875        #self.vertical_r_sizer.Layout()
876       
877        # Get and format the sigmas
878        sigma_r = self.format_number(self.resolution.sigma_1)
879        sigma_phi = self.format_number(self.resolution.sigma_2)
880        sigma_lamd = self.format_number(self.resolution.sigma_lamd)
881        sigma_1d =  self.format_number(self.resolution.sigma_1d)
882
883        # Set output values
884        self.sigma_r_tcl.SetValue(str(sigma_r))
885        self.sigma_phi_tcl.SetValue(str(sigma_phi))
886        self.sigma_lamd_tcl.SetValue(str(sigma_lamd))
887        self.sigma_1d_tcl.SetValue(str(sigma_1d))
888        msg = self.sigma_strings
889        msg += "\n"
890        status_type = 'stop'
891        self._status_info(msg, status_type)
892       
893    def _draw_lines(self, image=None, color='r'):
894        """
895        Draw lines in image if applicable
896        : Param image: pylab object
897        """
898        if image == None:
899            return
900        if color == 'g':
901            # Get the params from resolution
902            # ploting range for largest wavelength
903            qx_min = self.resolution.qx_min
904            qx_max = self.resolution.qx_max
905            qy_min = self.resolution.qy_min
906            qy_max = self.resolution.qy_max
907            # detector range
908            detector_qx_min = self.resolution.detector_qx_min
909            detector_qx_max = self.resolution.detector_qx_max
910            detector_qy_min = self.resolution.detector_qy_min
911            detector_qy_max = self.resolution.detector_qy_max
912        else:
913            qx_min, qx_max, qy_min, qy_max = self.resolution.get_detector_qrange()
914            # detector range
915            detector_qx_min = self.resolution.qxmin_limit
916            detector_qx_max = self.resolution.qxmax_limit
917            detector_qy_min = self.resolution.qymin_limit
918            detector_qy_max = self.resolution.qymax_limit
919
920        # Draw zero axis lines
921        if qy_min < 0 and qy_max >= 0:
922            image.axhline(linewidth = 1)
923        if qx_min < 0 and qx_max >= 0:
924            image.axvline(linewidth = 1)
925           
926        # Find x and y ratio values to draw the detector outline
927        x_min = fabs(detector_qx_min - qx_min) / (qx_max - qx_min)
928        x_max = fabs(detector_qx_max - qx_min) / (qx_max - qx_min)
929        y_min = fabs(detector_qy_min - qy_min) / (qy_max - qy_min)
930        y_max = fabs(detector_qy_max - qy_min) / (qy_max - qy_min)
931
932        # Draw Detector outline
933        if detector_qy_min >= qy_min:
934            image.axhline(y = detector_qy_min + 0.0002,
935                               xmin = x_min,
936                               xmax = x_max, 
937                               linewidth = 2, color=color)
938        if detector_qy_max <= qy_max:
939            image.axhline(y = detector_qy_max - 0.0002, 
940                               xmin = x_min, 
941                               xmax = x_max, 
942                               linewidth = 2, color=color)
943        if detector_qx_min >= qx_min:
944            image.axvline(x = detector_qx_min + 0.0002, 
945                               ymin = y_min, 
946                               ymax = y_max, 
947                               linewidth = 2, color=color)
948        if detector_qx_max <= qx_max:
949            image.axvline(x = detector_qx_max - 0.0002, 
950                               ymin = y_min, 
951                               ymax = y_max, 
952                               linewidth = 2, color=color)
953        xmin = min(self.qx)
954        xmax = max(self.qx)
955        ymin = min(self.qy)
956        ymax = max(self.qy)
957        if color != 'g':
958            if xmin < detector_qx_min or xmax > detector_qx_max or \
959                        ymin < detector_qy_min or ymax > detector_qy_max:
960                # message
961                status_type = 'stop' 
962                msg = 'At least one q value located out side of\n'
963                msg += " the detector range (%s < qx < %s, %s < qy < %s),\n" % \
964                        (self.format_number(detector_qx_min), 
965                         self.format_number(detector_qx_max),
966                         self.format_number(detector_qy_min), 
967                         self.format_number(detector_qy_max))
968                msg += " is ignored in computation.\n"
969               
970                self._status_info(msg, status_type)
971                wx.MessageBox(msg, 'Warning')
972       
973    def _map_func(self, qx, qy, qx_min, qx_max, qy_min, qy_max):   
974        """
975        Prepare the Mapping for the computation
976        : params qx, qy, qx_min, qx_max, qy_min, qy_max:
977       
978        : return: image (pylab)
979        """
980        try:
981            qx_value = float(qx)
982            qy_value = float(qy)
983        except:
984            raise
985        # calculate 2D resolution distribution image
986        image = self.resolution.compute_and_plot(qx_value, qy_value, 
987                                 qx_min, qx_max, qy_min, qy_max, 
988                                 self.det_coordinate)
989        # record sigmas
990        self.sigma_strings += " At Qx = %s, Qy = %s; \n"% (qx_value,qy_value)
991        self._sigma_strings()
992        return image
993    def _sigma_strings(self):
994        """
995        Recode sigmas as strins
996        """
997        sigma_r = self.format_number(self.resolution.sigma_1)
998        sigma_phi = self.format_number(self.resolution.sigma_2)
999        sigma_lamd = self.format_number(self.resolution.sigma_lamd)
1000        sigma_1d =  self.format_number(self.resolution.sigma_1d)
1001        # Set output values
1002        self.sigma_strings += "   sigma_x = %s\n"% sigma_r
1003        self.sigma_strings += "   sigma_y = %s\n"% sigma_phi
1004        self.sigma_strings += "   sigma_lamd = %s\n"% sigma_lamd
1005        self.sigma_strings += "   sigma_1D = %s\n"% sigma_1d
1006   
1007    def _validate_q_input(self, qx, qy):   
1008        """
1009        Check if q inputs are valid
1010        : params qx:  qx as a list
1011        : params qy:  qy as a list
1012       
1013        : return: True/False
1014        """
1015        # check qualifications
1016        if qx.__class__.__name__ != 'list':
1017            return None
1018        if qy.__class__.__name__ != 'list' :
1019            return None
1020        if len(qx) < 1:
1021            return None
1022        if len(qy) < 1:
1023            return None
1024        # allow one input
1025        if len(qx) == 1 and len(qy) > 1:
1026            qx = [qx[0] for ind in range(len(qy))]
1027            #self.qx = qx
1028        if len(qy) == 1 and len(qx) > 1:
1029            qy = [qy[0] for ind in range(len(qx))]
1030            #self.qy = qy
1031        # check length
1032        if len(qx) != len(qy):
1033            return None
1034        if qx == None or qy == None:
1035            return None
1036        return qx, qy
1037     
1038    def on_reset(self, event):
1039        """
1040        Execute the reset
1041        """
1042        # skip for another event
1043        if event != None:
1044            event.Skip() 
1045        # init resolution_calculator
1046        self.resolution = ResolutionCalculator()
1047        self.resolution.get_all_instrument_params()
1048        # reset all param values
1049        self.source_cb.SetValue('Neutron')
1050        self._on_source_selection(None)
1051        self.wave_color_cb.SetValue('Monochromatic')
1052        self._on_source_color(None)
1053        #self.intensity_tcl.SetValue(str(self.resolution.intensity))
1054        self.wavelength_tcl.SetValue(str(6.0))
1055        self.wavelength_spread_tcl.SetValue(str(0.125))
1056        self.resolution.set_spectrum(self.spectrum_dic['Flat'])
1057        self.spectrum_txt.Show(False)
1058        self.spectrum_cb.Show(False)
1059        source_aperture_value = str(self.resolution.source_aperture_size[0])
1060        if len(self.resolution.source_aperture_size)>1:
1061            source_aperture_value += ", "
1062            source_aperture_value += \
1063                str(self.resolution.source_aperture_size[1])
1064        self.source_aperture_tcl.SetValue(str(source_aperture_value))
1065        sample_aperture_value = str(self.resolution.sample_aperture_size[0])
1066        if len(self.resolution.sample_aperture_size)>1:
1067            sample_aperture_value += ", "
1068            sample_aperture_value += \
1069                str(self.resolution.sample_aperture_size[1])
1070        self.sample_aperture_tcl.SetValue(sample_aperture_value)
1071        source2sample_distance_value = \
1072            str(self.resolution.source2sample_distance[0])
1073        self.source2sample_distance_tcl.SetValue(source2sample_distance_value)
1074        sample2sample_distance_value = \
1075            str(self.resolution.sample2sample_distance[0])
1076        self.sample2sample_distance_tcl.SetValue(sample2sample_distance_value)
1077        sample2detector_distance_value = \
1078            str(self.resolution.sample2detector_distance[0])
1079        self.sample2detector_distance_tcl.SetValue(\
1080                                            sample2detector_distance_value)
1081        detector_size_value = str(self.resolution.detector_size[0])
1082        if len(self.resolution.detector_size)>1:
1083            detector_size_value += ", "
1084            detector_size_value += str(self.resolution.detector_size[1])
1085        self.detector_size_tcl.SetValue(detector_size_value)
1086        detector_pix_size_value = str(self.resolution.detector_pix_size[0])
1087        if len(self.resolution.detector_pix_size)>1:
1088            detector_pix_size_value += ", "
1089            detector_pix_size_value += str(self.resolution.detector_pix_size[1])
1090        self.detector_pix_size_tcl.SetValue(detector_pix_size_value)
1091        #layout attribute
1092        self.hint_sizer = None
1093        # reset q inputs
1094        self.qx_tcl.SetValue(str(_Q_DEFAULT))
1095        self.qy_tcl.SetValue(str(_Q_DEFAULT))
1096        # reset sigma outputs
1097        self.sigma_r_tcl.SetValue('')
1098        self.sigma_phi_tcl.SetValue('')
1099        self.sigma_1d_tcl.SetValue('')
1100        # reset radio button
1101        #self.r_phi_rb.SetValue(True)
1102        # Finally re-compute
1103        self.on_compute()
1104        # msg on info
1105        msg = " Finished the resetting..."
1106        self._status_info(msg, 'stop')
1107       
1108    def format_number(self, value=None):
1109        """
1110        Return a float in a standardized, human-readable formatted string
1111        """
1112        try: 
1113            value = float(value)
1114        except:
1115            output = None
1116            return output
1117
1118        output = "%-7.4g" % value
1119        return output.lstrip().rstrip() 
1120     
1121    def _string2list(self, string):
1122        """
1123        Change NNN, NNN to list,ie. [NNN, NNN] where NNN is a number
1124        """
1125        new_string = []
1126        # check the number of floats
1127        try:
1128            strg = float(string)
1129            new_string.append(strg)
1130            #new_string.append(0)
1131        except:
1132            string_split = string.split(',')
1133            if len(string_split) == 2:
1134                str_1 = string_split[0]
1135                str_2 = string_split[1]
1136                new_string.append(float(str_1))
1137                new_string.append(float(str_2))
1138            elif len(string_split) == 1:
1139                str_1 = string_split[0]
1140                new_string.append(float(str_1))
1141            else:
1142                msg = "The numbers must be one or two (separated by ',')..."
1143                self._status_info(msg, 'stop')
1144                raise RuntimeError, msg
1145
1146        return new_string
1147   
1148    def _string2inputlist(self, string):
1149        """
1150        Change NNN, NNN,... to list,ie. [NNN, NNN,...] where NNN is a number
1151       
1152        : return new_string: string like list
1153        """
1154        new_string = []
1155        string_split = string.split(',')
1156        length = len(string_split)
1157        for ind in range(length):
1158            try:
1159                value = float(string_split[ind])
1160                new_string.append(value)
1161            except:
1162                pass
1163       
1164        return new_string
1165   
1166    def _str2longlist(self, string):
1167        """
1168        Change NNN, NNN,... to list, NNN - NNN ; NNN to list, or float to list
1169       
1170        : return new_string: string like list
1171        """
1172        new_string = []
1173        msg = "Wrong format of intputs."
1174        try:
1175            # is float
1176            out = [float(string)]
1177            return out
1178        except:
1179            if self.wave_color.lower().count('mono') > 0:
1180                wx.MessageBox(msg, 'Warning')
1181            else:
1182                try:
1183                    # has a '-'
1184                    if string.count('-') > 0:
1185                        value = string.split('-')
1186                        if value[1].count(';') > 0:
1187                            # has a ';'
1188                            last_list = value[1].split(';')
1189                            num =  math.ceil(float(last_list[1]))
1190                            max = float(last_list[0])
1191                            self.num_wave = num
1192                        else:
1193                            # default num
1194                            num = self.num_wave
1195                            max = float(value[1])
1196                        min = float(value[0])
1197                        # make a list
1198                        bin_size = math.fabs(max - min) / (num - 1)
1199                        out = [min + bin_size * bnum for bnum in range(num)]
1200                        return out
1201                    if string.count(',') > 0:
1202                        out = self._string2inputlist(string)
1203                        return out
1204                except:
1205                    pass
1206                #    wx.MessageBox(msg, 'Warning')
1207
1208    def _on_xy_coordinate(self,event=None):
1209        """
1210        Set the detector coordinate for sigmas to x-y coordinate
1211        """
1212        if event != None:
1213            event.Skip()       
1214        # Set the coordinate in Cartesian       
1215        self.det_coordinate = 'cartesian'
1216        self.sigma_r_txt.SetLabel('Sigma_x:')
1217        self.sigma_phi_txt.SetLabel('Sigma_y:')
1218        self._onparamEnter()
1219       
1220    def _on_rp_coordinate(self,event=None):
1221        """
1222        Set the detector coordinate for sigmas to polar coordinate
1223        """
1224        if event != None:
1225            event.Skip()       
1226        # Set the coordinate in polar           
1227        self.det_coordinate = 'polar'
1228        self.sigma_r_txt.SetLabel('Sigma_r:   ')
1229        self.sigma_phi_txt.SetLabel('Sigma_phi:')
1230        self._onparamEnter()
1231       
1232    def _status_info(self, msg = '', type = "update"):
1233        """
1234        Status msg
1235        """
1236        if type == "stop":
1237            label = "Compute"
1238            able = True
1239        else:   
1240            label = "Wait..."
1241            able = False
1242        self.compute_button.Enable(able)
1243        self.compute_button.SetLabel(label)
1244        self.compute_button.SetToolTipString(label)
1245        if self.parent.parent != None:
1246                wx.PostEvent(self.parent.parent, 
1247                             StatusEvent(status = msg, type = type ))
1248
1249
1250    def _onparamEnter(self, event = None):
1251        """
1252        On Text_enter_callback, perform compute
1253        """
1254        self.on_compute()
1255       
1256    def _on_source_selection(self, event = None):
1257        """
1258        On source combobox selection
1259        """
1260        if event != None:
1261            combo = event.GetEventObject()
1262            event.Skip()
1263        else:
1264            combo = self.source_cb
1265        selection = combo.GetValue()
1266        mass = self.source_mass[selection]
1267        self.resolution.set_neutron_mass(mass)   
1268        source_hint = "Source Selection: Affect on"
1269        source_hint += " the gravitational contribution.\n"
1270        source_hint += "Mass of %s: m = %s [g]" % \
1271                            (selection, str(self.resolution.get_neutron_mass()))
1272        #source_tip.SetTip(source_hint)
1273        self.mass_txt.ToolTip.SetTip(source_hint)
1274       
1275    def _on_source_color(self, event = None):
1276        """
1277        On source color combobox selection
1278        """
1279        if event != None:
1280            #combo = event.GetEventObject()
1281            event.Skip()
1282        #else:
1283        combo = self.wave_color_cb
1284        selection = combo.GetValue()
1285        self.wave_color = selection
1286        if self.wave_color.lower() == 'tof':
1287            list = self.resolution.get_wave_list()
1288            minw = min(list[0])
1289            if len(list[0]) < 2:
1290                maxw = 2 * minw
1291            else:
1292                maxw = max(list[0])
1293            self.wavelength_tcl.SetValue('%s - %s' % (minw, maxw))
1294            minw = min(list[1])
1295            maxw = max(list[1])
1296            self.wavelength_spread_tcl.SetValue('%s - %s' % (minw, maxw))
1297            spectrum_val = self.spectrum_cb.GetValue()
1298            self.resolution.set_spectrum(self.spectrum_dic[spectrum_val])
1299            self.spectrum_txt.Show(True)
1300            self.spectrum_cb.Show(True)
1301         
1302        else:
1303            wavelength = self.resolution.get_wavelength()
1304            wavelength_spread = self.resolution.get_wavelength_spread()
1305            self.wavelength_tcl.SetValue(str(wavelength))
1306            self.wavelength_spread_tcl.SetValue(str(wavelength_spread))
1307            self.resolution.set_spectrum(self.spectrum_dic['Flat'])
1308            self.spectrum_txt.Show(False)
1309            self.spectrum_cb.Show(False)
1310        self.wavelength_sizer.Layout() 
1311        self.Layout()
1312       
1313    def _on_spectrum_cb(self, event=None):
1314        """
1315        On spectrum ComboBox event
1316        """
1317        if event != None:
1318            combo = event.GetEventObject()
1319            event.Skip()
1320        else:
1321            raise
1322        selection = self.spectrum_cb.GetValue()
1323        if selection == 'Add new':
1324            path = self._selectDlg()
1325            if path == None:
1326                self.spectrum_cb.SetValue('Flat')
1327                self.resolution.set_spectrum(self.spectrum_dic['Flat'])
1328                msg = "No file has been chosen."
1329                wx.MessageBox(msg, 'Info')
1330                return
1331            try:
1332                basename  = os.path.basename(path)
1333                if basename not in self.spectrum_dic.keys():
1334                    self.spectrum_cb.Append(basename)
1335                self.spectrum_dic[basename] = self._read_file(path)
1336                self.spectrum_cb.SetValue(basename)
1337                self.resolution.set_spectrum(self.spectrum_dic[basename])
1338                return
1339            except:
1340                raise
1341                msg = "Failed to load the spectrum data file."
1342                wx.MessageBox(msg, 'Warning')
1343                raise ValueError, "Invalid spectrum file..."
1344       
1345        self.resolution.set_spectrum(self.spectrum_dic[selection])
1346                             
1347    def _selectDlg(self):
1348        """
1349        open a dialog file to select a customized spectrum
1350        """
1351        import os
1352        dlg = wx.FileDialog(self, 
1353                "Choose a wavelength spectrum file: Intensity vs. wavelength",
1354                self.parent.parent._default_save_location , "", 
1355                "*.*", wx.OPEN)
1356        path = None
1357        if dlg.ShowModal() == wx.ID_OK:
1358            path = dlg.GetPath()
1359        dlg.Destroy()
1360        return path
1361       
1362    def _read_file(self, path):
1363        """
1364        Read two columns file as tuples of numpy array
1365       
1366        :param path: the path to the file to read
1367       
1368        """
1369        try:
1370            if path==None:
1371                wx.PostEvent(self.parent.parent, StatusEvent(status=\
1372                            " Selected Distribution was not loaded: %s"%path))
1373                return None, None
1374            input_f = open(path, 'r')
1375            buff = input_f.read()
1376            lines = buff.split('\n')
1377           
1378            wavelength = []
1379            intensity = []
1380            for line in lines:
1381                toks = line.split()
1382                try:
1383                    wave = float(toks[0])
1384                    intens = float(toks[1])
1385                    wavelength.append(wave)
1386                    intensity.append(intens)
1387                except:
1388                    # Skip non-data lines
1389                    pass
1390               
1391            return [wavelength, intensity]
1392        except:
1393            raise 
1394       
1395class ResolutionWindow(wx.Frame):
1396    def __init__(self, parent = None, title = "SANS Resolution Estimator",
1397                  size=(PANEL_WIDTH * 2, PANEL_HEIGHT), *args, **kwds):
1398        kwds['title'] = title
1399        kwds['size'] = size
1400        wx.Frame.__init__(self, parent=None, *args, **kwds)
1401        self.parent = parent
1402        self.panel = ResolutionCalculatorPanel(parent=self)
1403        self.Centre()
1404        self.Show(True)
1405        wx.EVT_CLOSE(self, self.OnClose)
1406   
1407    def OnClose(self, event): 
1408        """
1409        On close event
1410        """
1411        _pylab_helpers.Gcf.figs = {}
1412        self.Destroy() 
1413
1414       
1415if __name__ == "__main__": 
1416    app = wx.PySimpleApp()
1417    frame = ResolutionWindow()   
1418    frame.Show(True)
1419    app.MainLoop()     
Note: See TracBrowser for help on using the repository browser.