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

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since b699768 was b699768, checked in by Piotr Rozyczko <piotr.rozyczko@…>, 8 years ago

Initial commit of the refactored SasCalc? module.

  • Property mode set to 100644
File size: 58.3 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.guiframe.events import StatusEvent
30from sas.perspectives.calculator.calculator_widgets import OutputTextCtrl
31from sas.perspectives.calculator.calculator_widgets import InputTextCtrl
32from wx.lib.scrolledpanel import ScrolledPanel
33from math import fabs
34from sas.perspectives.calculator import calculator_widgets as widget
35from sas.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 = "SAS 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/perspectives/calculator/resolution_calculator_help.html"
644        _doc_viewer = DocumentationWindow(self, -1, _TreeLocation, "",
645                                          "Resolution Calculator Help")
646
647    def on_close(self, event):
648        """
649        close the window containing this panel
650        """
651        # get ready for other events
652        if event is not None:
653            event.Skip()
654        # Clear the plot
655        if self.image != None:
656            self.image.clf()
657            # reset image
658            self.image = None
659        # Close panel
660        self.parent.OnClose(None)
661
662    def on_compute(self, event=None):
663        """
664        Execute the computation of resolution
665        """
666        wx.CallAfter(self.on_compute_call, event)
667
668    def on_compute_call(self, event=None):
669        """
670        Execute the computation of resolution
671        """
672        # Skip event for next event
673        if event != None:
674            event.Skip()
675            msg = "Please Check your input values "
676            msg += "before starting the computation..."
677
678        # message
679        status_type = 'progress'
680        msg = 'Calculating...'
681        self._status_info(msg, status_type)
682
683        status_type = 'stop'
684        # Q min max list default
685        qx_min = []
686        qx_max = []
687        qy_min = []
688        qy_max = []
689        # possible max qrange
690        self.resolution.qxmin_limit = 0
691        self.resolution.qxmax_limit = 0
692        self.resolution.qymin_limit = 0
693        self.resolution.qymax_limit = 0
694        # q min and max of the detector
695        try:
696            # Get all the values at set to compute
697            # default num bin of wave list
698            self.num_wave = 10
699            wavelength = self._str2longlist(self.wavelength_tcl.GetValue())
700            source = self.source_cb.GetValue()
701            mass = self.source_mass[str(source)]
702            self.resolution.set_neutron_mass(float(mass))
703            wavelength_spread = self._str2longlist(\
704                        self.wavelength_spread_tcl.GetValue().split(';')[0])
705            # Validate the wave inputs
706            wave_input = self._validate_q_input(wavelength, wavelength_spread)
707            if wave_input != None:
708                wavelength, wavelength_spread = wave_input
709
710            self.resolution.set_wave(wavelength)
711            self.resolution.set_wave_spread(wavelength_spread)
712            source_aperture_size = self.source_aperture_tcl.GetValue()
713            source_aperture_size = self._string2list(source_aperture_size)
714            self.resolution.set_source_aperture_size(source_aperture_size)
715            sample_aperture_size = self.sample_aperture_tcl.GetValue()
716            sample_aperture_size = self._string2list(sample_aperture_size)
717            self.resolution.set_sample_aperture_size(sample_aperture_size)
718            source2sample_distance = self.source2sample_distance_tcl.GetValue()
719            source2sample_distance = self._string2list(source2sample_distance)
720            self.resolution.set_source2sample_distance(source2sample_distance)
721            sample2sample_distance = self.sample2sample_distance_tcl.GetValue()
722            sample2sample_distance = self._string2list(sample2sample_distance)
723            self.resolution.set_sample2sample_distance(sample2sample_distance)
724            sample2detector_distance = \
725                                self.sample2detector_distance_tcl.GetValue()
726            sample2detector_distance = \
727                                self._string2list(sample2detector_distance)
728            self.resolution.set_sample2detector_distance(\
729                                                    sample2detector_distance)
730            detector_size = self.detector_size_tcl.GetValue()
731            detector_size = self._string2list(detector_size)
732            self.resolution.set_detector_size(detector_size)
733            detector_pix_size = self.detector_pix_size_tcl.GetValue()
734            detector_pix_size = self._string2list(detector_pix_size)
735            self.resolution.set_detector_pix_size(detector_pix_size)
736            self.qx = self._string2inputlist(self.qx_tcl.GetValue())
737            self.qy = self._string2inputlist(self.qy_tcl.GetValue())
738
739            # Find min max of qs
740            xmin = min(self.qx)
741            xmax = max(self.qx)
742            ymin = min(self.qy)
743            ymax = max(self.qy)
744            if not self._validate_q_input(self.qx, self.qy):
745                raise
746        except:
747            msg = "An error occured during the resolution computation."
748            msg += "Please check your inputs..."
749            self._status_info(msg, status_type)
750            wx.MessageBox(msg, 'Warning')
751            return
752            #raise ValueError, "Invalid Q Input..."
753
754        # Validate the q inputs
755        q_input = self._validate_q_input(self.qx, self.qy)
756        if q_input != None:
757            self.qx, self.qy = q_input
758
759        # Make list of q min max for mapping
760        for i in range(len(self.qx)):
761            qx_min.append(xmin)
762            qx_max.append(xmax)
763        for i in range(len(self.qy)):
764            qy_min.append(ymin)
765            qy_max.append(ymax)
766
767        # Compute the resolution
768        if self.image != None:
769            #_pylab_helpers.Gcf.set_active(self.fm)
770            _pylab_helpers.Gcf.figs = {}
771            # Clear the image before redraw
772            self.image.clf()
773            # reset the image
774            self.resolution.reset_image()
775
776        # Compute and get the image plot
777        try:
778            from sas.perspectives.calculator.resolcal_thread import CalcRes as thread
779            self.sigma_strings = '\nResolution: Computation is finished. \n'
780            cal_res = thread(func=self._map_func,
781                             qx=self.qx,
782                             qy=self.qy,
783                             qx_min=qx_min,
784                             qx_max=qx_max,
785                             qy_min=qy_min,
786                             qy_max=qy_max,
787                             image=self.image,
788                             completefn=self.complete)
789            cal_res.queue()
790            msg = "Computation is in progress..."
791            status_type = 'progress'
792            self._status_info(msg, status_type)
793        except:
794            raise
795
796    def complete(self, image, elapsed=None):
797        """
798        Callafter complete: wx call after needed for stable output
799        """
800        wx.CallAfter(self.complete_cal, image, elapsed)
801
802    def complete_cal(self, image, elapsed=None):
803        """
804        Complete computation
805        """
806        self.image = image
807        # Draw lines in image before drawing
808        wave_list, _ = self.resolution.get_wave_list()
809        if len(wave_list) > 1 and wave_list[-1] == max(wave_list):
810            # draw a green rectangle(limit for the longest wavelength
811            # to be involved) for tof inputs
812            self._draw_lines(self.image, color='g')
813        self._draw_lines(self.image, color='r')
814        # Draw image
815        self.image.draw()
816
817        # Get and format the sigmas
818        sigma_r = self.format_number(self.resolution.sigma_1)
819        sigma_phi = self.format_number(self.resolution.sigma_2)
820        sigma_lamd = self.format_number(self.resolution.sigma_lamd)
821        sigma_1d = self.format_number(self.resolution.sigma_1d)
822
823        # Set output values
824        self.sigma_r_tcl.SetValue(str(sigma_r))
825        self.sigma_phi_tcl.SetValue(str(sigma_phi))
826        self.sigma_lamd_tcl.SetValue(str(sigma_lamd))
827        self.sigma_1d_tcl.SetValue(str(sigma_1d))
828        msg = self.sigma_strings
829        msg += "\n"
830        status_type = 'stop'
831        self._status_info(msg, status_type)
832
833    def _draw_lines(self, image=None, color='r'):
834        """
835        Draw lines in image if applicable
836        : Param image: pylab object
837        """
838        if image == None:
839            return
840        if color == 'g':
841            # Get the params from resolution
842            # ploting range for largest wavelength
843            qx_min = self.resolution.qx_min
844            qx_max = self.resolution.qx_max
845            qy_min = self.resolution.qy_min
846            qy_max = self.resolution.qy_max
847            # detector range
848            detector_qx_min = self.resolution.detector_qx_min
849            detector_qx_max = self.resolution.detector_qx_max
850            detector_qy_min = self.resolution.detector_qy_min
851            detector_qy_max = self.resolution.detector_qy_max
852        else:
853            qx_min, qx_max, qy_min, qy_max = \
854                                self.resolution.get_detector_qrange()
855            # detector range
856            detector_qx_min = self.resolution.qxmin_limit
857            detector_qx_max = self.resolution.qxmax_limit
858            detector_qy_min = self.resolution.qymin_limit
859            detector_qy_max = self.resolution.qymax_limit
860
861        # Draw zero axis lines
862        if qy_min < 0 and qy_max >= 0:
863            image.axhline(linewidth=1)
864        if qx_min < 0 and qx_max >= 0:
865            image.axvline(linewidth=1)
866
867        # Find x and y ratio values to draw the detector outline
868        x_min = fabs(detector_qx_min - qx_min) / (qx_max - qx_min)
869        x_max = fabs(detector_qx_max - qx_min) / (qx_max - qx_min)
870        y_min = fabs(detector_qy_min - qy_min) / (qy_max - qy_min)
871        y_max = fabs(detector_qy_max - qy_min) / (qy_max - qy_min)
872
873        # Draw Detector outline
874        if detector_qy_min >= qy_min:
875            image.axhline(y=detector_qy_min + 0.0002,
876                          xmin=x_min, xmax=x_max,
877                          linewidth=2, color=color)
878        if detector_qy_max <= qy_max:
879            image.axhline(y=detector_qy_max - 0.0002,
880                          xmin=x_min, xmax=x_max,
881                          linewidth=2, color=color)
882        if detector_qx_min >= qx_min:
883            image.axvline(x=detector_qx_min + 0.0002,
884                          ymin=y_min, ymax=y_max,
885                          linewidth=2, color=color)
886        if detector_qx_max <= qx_max:
887            image.axvline(x=detector_qx_max - 0.0002,
888                          ymin=y_min, ymax=y_max,
889                          linewidth=2, color=color)
890        xmin = min(self.qx)
891        xmax = max(self.qx)
892        ymin = min(self.qy)
893        ymax = max(self.qy)
894        if color != 'g':
895            if xmin < detector_qx_min or xmax > detector_qx_max or \
896                        ymin < detector_qy_min or ymax > detector_qy_max:
897                # message
898                status_type = 'stop'
899                msg = 'At least one q value located out side of\n'
900                msg += " the detector range (%s < qx < %s, %s < qy < %s),\n" % \
901                        (self.format_number(detector_qx_min),
902                         self.format_number(detector_qx_max),
903                         self.format_number(detector_qy_min),
904                         self.format_number(detector_qy_max))
905                msg += " is ignored in computation.\n"
906
907                self._status_info(msg, status_type)
908                wx.MessageBox(msg, 'Warning')
909
910    def _map_func(self, qx, qy, qx_min, qx_max, qy_min, qy_max):
911        """
912        Prepare the Mapping for the computation
913        : params qx, qy, qx_min, qx_max, qy_min, qy_max:
914
915        : return: image (pylab)
916        """
917        try:
918            qx_value = float(qx)
919            qy_value = float(qy)
920        except:
921            raise
922        # calculate 2D resolution distribution image
923        image = self.resolution.compute_and_plot(qx_value, qy_value,
924                                                 qx_min, qx_max, qy_min, qy_max,
925                                                 self.det_coordinate)
926        # record sigmas
927        self.sigma_strings += " At Qx = %s, Qy = %s; \n" % (qx_value, qy_value)
928        self._sigma_strings()
929        return image
930
931    def _sigma_strings(self):
932        """
933        Recode sigmas as strins
934        """
935        sigma_r = self.format_number(self.resolution.sigma_1)
936        sigma_phi = self.format_number(self.resolution.sigma_2)
937        sigma_lamd = self.format_number(self.resolution.sigma_lamd)
938        sigma_1d = self.format_number(self.resolution.sigma_1d)
939        # Set output values
940        self.sigma_strings += "   sigma_x = %s\n" % sigma_r
941        self.sigma_strings += "   sigma_y = %s\n" % sigma_phi
942        self.sigma_strings += "   sigma_lamd = %s\n" % sigma_lamd
943        self.sigma_strings += "   sigma_1D = %s\n" % sigma_1d
944
945    def _validate_q_input(self, qx, qy):
946        """
947        Check if q inputs are valid
948        : params qx:  qx as a list
949        : params qy:  qy as a list
950
951        : return: True/False
952        """
953        # check qualifications
954        if qx.__class__.__name__ != 'list':
955            return None
956        if qy.__class__.__name__ != 'list':
957            return None
958        if len(qx) < 1:
959            return None
960        if len(qy) < 1:
961            return None
962        # allow one input
963        if len(qx) == 1 and len(qy) > 1:
964            qx = [qx[0] for ind in range(len(qy))]
965            #self.qx = qx
966        if len(qy) == 1 and len(qx) > 1:
967            qy = [qy[0] for ind in range(len(qx))]
968            #self.qy = qy
969        # check length
970        if len(qx) != len(qy):
971            return None
972        if qx == None or qy == None:
973            return None
974        return qx, qy
975
976    def on_reset(self, event):
977        """
978        Execute the reset
979        """
980        # skip for another event
981        if event != None:
982            event.Skip()
983        # init resolution_calculator
984        self.resolution = ResolutionCalculator()
985        self.resolution.get_all_instrument_params()
986        # reset all param values
987        self.source_cb.SetValue('Neutron')
988        self._on_source_selection(None)
989        self.wave_color_cb.SetValue('Monochromatic')
990        self._on_source_color(None)
991        #self.intensity_tcl.SetValue(str(self.resolution.intensity))
992        self.wavelength_tcl.SetValue(str(6.0))
993        self.wavelength_spread_tcl.SetValue(str(0.125))
994        self.resolution.set_spectrum(self.spectrum_dic['Flat'])
995        self.spectrum_txt.Show(False)
996        self.spectrum_cb.Show(False)
997        source_aperture_value = str(self.resolution.source_aperture_size[0])
998        if len(self.resolution.source_aperture_size) > 1:
999            source_aperture_value += ", "
1000            source_aperture_value += \
1001                str(self.resolution.source_aperture_size[1])
1002        self.source_aperture_tcl.SetValue(str(source_aperture_value))
1003        sample_aperture_value = str(self.resolution.sample_aperture_size[0])
1004        if len(self.resolution.sample_aperture_size) > 1:
1005            sample_aperture_value += ", "
1006            sample_aperture_value += \
1007                str(self.resolution.sample_aperture_size[1])
1008        self.sample_aperture_tcl.SetValue(sample_aperture_value)
1009        source2sample_distance_value = \
1010            str(self.resolution.source2sample_distance[0])
1011        self.source2sample_distance_tcl.SetValue(source2sample_distance_value)
1012        sample2sample_distance_value = \
1013            str(self.resolution.sample2sample_distance[0])
1014        self.sample2sample_distance_tcl.SetValue(sample2sample_distance_value)
1015        sample2detector_distance_value = \
1016            str(self.resolution.sample2detector_distance[0])
1017        self.sample2detector_distance_tcl.SetValue(\
1018                                            sample2detector_distance_value)
1019        detector_size_value = str(self.resolution.detector_size[0])
1020        if len(self.resolution.detector_size) > 1:
1021            detector_size_value += ", "
1022            detector_size_value += str(self.resolution.detector_size[1])
1023        self.detector_size_tcl.SetValue(detector_size_value)
1024        detector_pix_size_value = str(self.resolution.detector_pix_size[0])
1025        if len(self.resolution.detector_pix_size) > 1:
1026            detector_pix_size_value += ", "
1027            detector_pix_size_value += str(self.resolution.detector_pix_size[1])
1028        self.detector_pix_size_tcl.SetValue(detector_pix_size_value)
1029        #layout attribute
1030        self.hint_sizer = None
1031        # reset q inputs
1032        self.qx_tcl.SetValue(str(_Q_DEFAULT))
1033        self.qy_tcl.SetValue(str(_Q_DEFAULT))
1034        # reset sigma outputs
1035        self.sigma_r_tcl.SetValue('')
1036        self.sigma_phi_tcl.SetValue('')
1037        self.sigma_1d_tcl.SetValue('')
1038        # reset radio button
1039        #self.r_phi_rb.SetValue(True)
1040        # Finally re-compute
1041        self.on_compute()
1042        # msg on info
1043        msg = " Finished the resetting..."
1044        self._status_info(msg, 'stop')
1045
1046    def format_number(self, value=None):
1047        """
1048        Return a float in a standardized, human-readable formatted string
1049        """
1050        try:
1051            value = float(value)
1052        except:
1053            output = None
1054            return output
1055
1056        output = "%-7.4g" % value
1057        return output.lstrip().rstrip()
1058
1059    def _string2list(self, string):
1060        """
1061        Change NNN, NNN to list,ie. [NNN, NNN] where NNN is a number
1062        """
1063        new_string = []
1064        # check the number of floats
1065        try:
1066            strg = float(string)
1067            new_string.append(strg)
1068        except:
1069            string_split = string.split(',')
1070            if len(string_split) == 2:
1071                str_1 = string_split[0]
1072                str_2 = string_split[1]
1073                new_string.append(float(str_1))
1074                new_string.append(float(str_2))
1075            elif len(string_split) == 1:
1076                str_1 = string_split[0]
1077                new_string.append(float(str_1))
1078            else:
1079                msg = "The numbers must be one or two (separated by ',')..."
1080                self._status_info(msg, 'stop')
1081                raise RuntimeError, msg
1082
1083        return new_string
1084
1085    def _string2inputlist(self, string):
1086        """
1087        Change NNN, NNN,... to list,ie. [NNN, NNN,...] where NNN is a number
1088
1089        : return new_string: string like list
1090        """
1091        new_string = []
1092        string_split = string.split(',')
1093        length = len(string_split)
1094        for ind in range(length):
1095            try:
1096                value = float(string_split[ind])
1097                new_string.append(value)
1098            except:
1099                logging.error(sys.exc_value)
1100
1101        return new_string
1102
1103    def _str2longlist(self, string):
1104        """
1105        Change NNN, NNN,... to list, NNN - NNN ; NNN to list, or float to list
1106
1107        : return new_string: string like list
1108        """
1109        msg = "Wrong format of intputs."
1110        try:
1111            # is float
1112            out = [float(string)]
1113            return out
1114        except:
1115            if self.wave_color.lower().count('mono') > 0:
1116                wx.MessageBox(msg, 'Warning')
1117            else:
1118                try:
1119                    # has a '-'
1120                    if string.count('-') > 0:
1121                        value = string.split('-')
1122                        if value[1].count(';') > 0:
1123                            # has a ';'
1124                            last_list = value[1].split(';')
1125                            num = math.ceil(float(last_list[1]))
1126                            max_value = float(last_list[0])
1127                            self.num_wave = num
1128                        else:
1129                            # default num
1130                            num = self.num_wave
1131                            max_value = float(value[1])
1132                        min_value = float(value[0])
1133                        # make a list
1134                        bin_size = math.fabs(max_value - min_value) / (num - 1)
1135                        out = [min_value + bin_size * bnum for bnum in range(num)]
1136                        return out
1137                    if string.count(',') > 0:
1138                        out = self._string2inputlist(string)
1139                        return out
1140                except:
1141                    logging.error(sys.exc_value)
1142
1143    def _on_xy_coordinate(self, event=None):
1144        """
1145        Set the detector coordinate for sigmas to x-y coordinate
1146        """
1147        if event != None:
1148            event.Skip()
1149        # Set the coordinate in Cartesian
1150        self.det_coordinate = 'cartesian'
1151        self.sigma_r_txt.SetLabel('Sigma_x:')
1152        self.sigma_phi_txt.SetLabel('Sigma_y:')
1153        self._onparamEnter()
1154
1155    def _on_rp_coordinate(self, event=None):
1156        """
1157        Set the detector coordinate for sigmas to polar coordinate
1158        """
1159        if event != None:
1160            event.Skip()
1161        # Set the coordinate in polar
1162        self.det_coordinate = 'polar'
1163        self.sigma_r_txt.SetLabel('Sigma_r:   ')
1164        self.sigma_phi_txt.SetLabel('Sigma_phi:')
1165        self._onparamEnter()
1166
1167    def _status_info(self, msg='', type="update"):
1168        """
1169        Status msg
1170        """
1171        if type == "stop":
1172            label = "Compute"
1173            able = True
1174        else:
1175            label = "Wait..."
1176            able = False
1177        self.compute_button.Enable(able)
1178        self.compute_button.SetLabel(label)
1179        self.compute_button.SetToolTipString(label)
1180        if self.parent.parent != None:
1181            wx.PostEvent(self.parent.parent,
1182                         StatusEvent(status=msg, type=type))
1183
1184
1185    def _onparamEnter(self, event=None):
1186        """
1187        On Text_enter_callback, perform compute
1188        """
1189        self.on_compute()
1190
1191    def _on_source_selection(self, event=None):
1192        """
1193        On source combobox selection
1194        """
1195        if event != None:
1196            combo = event.GetEventObject()
1197            event.Skip()
1198        else:
1199            combo = self.source_cb
1200        selection = combo.GetValue()
1201        mass = self.source_mass[selection]
1202        self.resolution.set_neutron_mass(mass)
1203        source_hint = "Source Selection: Affect on"
1204        source_hint += " the gravitational contribution.\n"
1205        source_hint += "Mass of %s: m = %s [g]" % \
1206                            (selection, str(self.resolution.get_neutron_mass()))
1207        #source_tip.SetTip(source_hint)
1208        self.mass_txt.ToolTip.SetTip(source_hint)
1209
1210    def _on_source_color(self, event=None):
1211        """
1212        On source color combobox selection
1213        """
1214        if event != None:
1215            #combo = event.GetEventObject()
1216            event.Skip()
1217        #else:
1218        combo = self.wave_color_cb
1219        selection = combo.GetValue()
1220        self.wave_color = selection
1221        if self.wave_color.lower() == 'tof':
1222            list = self.resolution.get_wave_list()
1223            minw = min(list[0])
1224            if len(list[0]) < 2:
1225                maxw = 2 * minw
1226            else:
1227                maxw = max(list[0])
1228            self.wavelength_tcl.SetValue('%s - %s' % (minw, maxw))
1229            minw = min(list[1])
1230            maxw = max(list[1])
1231            self.wavelength_spread_tcl.SetValue('%s - %s' % (minw, maxw))
1232            spectrum_val = self.spectrum_cb.GetValue()
1233            self.resolution.set_spectrum(self.spectrum_dic[spectrum_val])
1234            self.spectrum_txt.Show(True)
1235            self.spectrum_cb.Show(True)
1236
1237        else:
1238            wavelength = self.resolution.get_wavelength()
1239            wavelength_spread = self.resolution.get_wavelength_spread()
1240            self.wavelength_tcl.SetValue(str(wavelength))
1241            self.wavelength_spread_tcl.SetValue(str(wavelength_spread))
1242            self.resolution.set_spectrum(self.spectrum_dic['Flat'])
1243            self.spectrum_txt.Show(False)
1244            self.spectrum_cb.Show(False)
1245        self.wavelength_sizer.Layout()
1246        self.Layout()
1247
1248    def _on_spectrum_cb(self, event=None):
1249        """
1250        On spectrum ComboBox event
1251        """
1252        if event != None:
1253            #combo = event.GetEventObject()
1254            event.Skip()
1255        else:
1256            raise
1257        selection = self.spectrum_cb.GetValue()
1258        if selection == 'Add new':
1259            path = self._selectDlg()
1260            if path == None:
1261                self.spectrum_cb.SetValue('Flat')
1262                self.resolution.set_spectrum(self.spectrum_dic['Flat'])
1263                msg = "No file has been chosen."
1264                wx.MessageBox(msg, 'Info')
1265                return
1266            try:
1267                basename = os.path.basename(path)
1268                if basename not in self.spectrum_dic.keys():
1269                    self.spectrum_cb.Append(basename)
1270                self.spectrum_dic[basename] = self._read_file(path)
1271                self.spectrum_cb.SetValue(basename)
1272                self.resolution.set_spectrum(self.spectrum_dic[basename])
1273                return
1274            except:
1275                raise
1276
1277        self.resolution.set_spectrum(self.spectrum_dic[selection])
1278
1279    def _selectDlg(self):
1280        """
1281        open a dialog file to select a customized spectrum
1282        """
1283        dlg = wx.FileDialog(self,
1284                            "Choose a wavelength spectrum file: Intensity vs. wavelength",
1285                            self.parent.parent.get_save_location() , "", "*.*", wx.OPEN)
1286        path = None
1287        if dlg.ShowModal() == wx.ID_OK:
1288            path = dlg.GetPath()
1289        dlg.Destroy()
1290        return path
1291
1292    def _read_file(self, path):
1293        """
1294        Read two columns file as tuples of numpy array
1295
1296        :param path: the path to the file to read
1297
1298        """
1299        try:
1300            if path == None:
1301                wx.PostEvent(self.parent.parent, StatusEvent(status=\
1302                            " Selected Distribution was not loaded: %s" % path))
1303                return None, None
1304            input_f = open(path, 'r')
1305            buff = input_f.read()
1306            lines = buff.split('\n')
1307
1308            wavelength = []
1309            intensity = []
1310            for line in lines:
1311                toks = line.split()
1312                try:
1313                    wave = float(toks[0])
1314                    intens = float(toks[1])
1315                    wavelength.append(wave)
1316                    intensity.append(intens)
1317                except:
1318                    # Skip non-data lines
1319                    logging.error(sys.exc_value)
1320
1321            return [wavelength, intensity]
1322        except:
1323            raise
1324
1325class ResolutionWindow(widget.CHILD_FRAME):
1326    """
1327    Resolution Window
1328    """
1329    def __init__(self, parent=None, manager=None,
1330                 title="SAS Resolution Estimator",
1331                 size=(PANEL_WIDTH * 2, PANEL_HEIGHT), *args, **kwds):
1332        kwds['title'] = title
1333        kwds['size'] = size
1334        widget.CHILD_FRAME.__init__(self, parent=parent, *args, **kwds)
1335        self.parent = parent
1336        self.manager = manager
1337        self.panel = ResolutionCalculatorPanel(parent=self)
1338        self.Bind(wx.EVT_CLOSE, self.OnClose)
1339        self.SetPosition((wx.LEFT, PANEL_TOP))
1340        self.Show(True)
1341
1342    def OnClose(self, event):
1343        """
1344        On close event
1345        """
1346        _pylab_helpers.Gcf.figs = {}
1347        if self.manager != None:
1348            self.manager.cal_res_frame = None
1349        self.Destroy()
1350
1351
1352if __name__ == "__main__":
1353    app = wx.PySimpleApp()
1354    widget.CHILD_FRAME = wx.Frame
1355    frame = ResolutionWindow()
1356    frame.Show(True)
1357    app.MainLoop()
Note: See TracBrowser for help on using the repository browser.