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

ticket-1249
Last change on this file since a5e1b6ca was 5251ec6, checked in by Paul Kienzle <pkienzle@…>, 6 years ago

improved support for py37 in sasgui

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