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

Last change on this file since b9d74f3 was b9d74f3, checked in by andyfaff, 8 years ago

MAINT: use raise Exception() not raise Exception

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