source: sasview/src/sas/sasgui/perspectives/calculator/resolution_calculator_panel.py @ 46f798f

Last change on this file since 46f798f was a0c1e1d, checked in by smk78, 8 years ago

Changed tool name "SAS Resolution Estimator" to "Q Resolution Estimator"
to better match purpose and documentation.

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