source: sasview/src/sas/qtgui/Calculators/ResolutionCalculatorPanel.py @ fc4fec8

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalc
Last change on this file since fc4fec8 was fc4fec8, checked in by celinedurniak <celine.durniak@…>, 7 years ago

Implemented corrections from review of Resolution Calculator Panel

  • Property mode set to 100644
File size: 31.1 KB
RevLine 
[01cda57]1"""
2This object is a small tool to allow user to quickly
3determine the variance in q  from the
4instrumental parameters.
5"""
6from PyQt4 import QtGui
7from PyQt4 import QtCore
8
9from twisted.internet import threads
10import sas.qtgui.Utilities.GuiUtils as GuiUtils
11from sas.qtgui.Plotting.PlotterData import Data2D
12from sas.qtgui.Plotting.Plotter2D import Plotter2DWidget
13from sas.sascalc.calculator.resolution_calculator import ResolutionCalculator
14import matplotlib.patches as patches
15
16import numpy
17import sys
18import logging
19import os
20import re
21
22from UI.ResolutionCalculatorPanelUI import Ui_ResolutionCalculatorPanel
23
24_SOURCE_MASS = {'Alpha': 6.64465620E-24,
25                'Deuteron': 3.34358320E-24,
26                'Neutron': 1.67492729E-24,
27                'Photon': 0.0,
28                'Proton': 1.67262137E-24,
29                'Triton': 5.00826667E-24}
30
31BG_WHITE = "background-color: rgb(255, 255, 255);"
32BG_RED = "background-color: rgb(244, 170, 164);"
33
34
35class ResolutionCalculatorPanel(QtGui.QDialog, Ui_ResolutionCalculatorPanel):
36    """
37    compute resolution in 2D
38    """
39    def __init__(self, parent=None):
40        super(ResolutionCalculatorPanel, self).__init__()
41        self.setupUi(self)
42        self.manager = parent
43
44        # New font to display angstrom symbol
45        new_font = 'font-family: -apple-system, "Helvetica Neue", "Ubuntu";'
46        self.lblUnitWavelength.setStyleSheet(new_font)
47        self.lblUnitQx.setStyleSheet(new_font)
48        self.lblUnitQy.setStyleSheet(new_font)
49        self.lblUnitSigmax.setStyleSheet(new_font)
50        self.lblUnitSigmay.setStyleSheet(new_font)
51        self.lblUnitSigmalamd.setStyleSheet(new_font)
52        self.lblUnit1DSigma.setStyleSheet(new_font)
53
54        # by default Spectrum label and cbCustomSpectrum are not visible
55        self.cbCustomSpectrum.setVisible(False)
56        self.lblSpectrum.setVisible(False)
57        # self.onReset()
58
59        # change index of comboboxes
60        self.cbWaveColor.currentIndexChanged.connect(self.onSelectWaveColor)
61        self.cbCustomSpectrum.currentIndexChanged.connect(self.onSelectCustomSpectrum)
62
63        # push buttons
64        self.cmdClose.clicked.connect(self.accept)
65        self.cmdHelp.clicked.connect(self.onHelp)
66        self.cmdCompute.clicked.connect(self.onCompute)
67        self.cmdReset.clicked.connect(self.onReset)
68
69        # input defaults
70        self.qx = []
71        self.qy = []
72        # dQ defaults
73        self.sigma_r = None
74        self.sigma_phi = None
75        self.sigma_1d = None
76
77        # number of bins for wavelength and wavelength spread
78        self.num_wave = 10
79        self.spectrum_dic = {}
80
81        # dQ 2d image
82        self.image = None
83        # Source selection dic
84        self.source_mass = _SOURCE_MASS
85        # detector coordinate of estimation of sigmas
86        self.det_coordinate = 'cartesian'
87
88        self.resolution = ResolutionCalculator()
89        self.spectrum_dic['Add new'] = ''
90        self.spectrum_dic['Flat'] = self.resolution.get_default_spectrum()
91        self.resolution.set_spectrum(self.spectrum_dic['Flat'])
92
93        # validators
94        self.txtWavelength.editingFinished.connect(self.checkWavelength)
95        self.txtWavelengthSpread.editingFinished.connect(self.checkWavelengthSpread)
96
97        self.txtDetectorPixSize.editingFinished.connect(self.checkPixels)
98        self.txtDetectorSize.editingFinished.connect(self.checkPixels)
99
100        self.txtSourceApertureSize.editingFinished.connect(self.checkAperture)
101        self.txtSampleApertureSize.editingFinished.connect(self.checkAperture)
102
103        self.txtQx.editingFinished.connect(self.checkQx_y)
104        self.txtQy.editingFinished.connect(self.checkQx_y)
105
106        # double validator
107        self.txtSource2SampleDistance.setValidator(QtGui.QDoubleValidator())
108        self.txtSample2DetectorDistance.setValidator(QtGui.QDoubleValidator())
109        self.txtSampleOffset.setValidator(QtGui.QDoubleValidator())
110
111        # call compute to calculate with default values
112        self.createTemplate2DPlot()
113        self.onCompute()
114
115    # #################################
116    # Validators: red background in line edits when wrong input
117    # and display of info logging message
118    # #################################
119
120    def checkWavelength(self):
121        """ Validator for Wavelength
122         if TOF, wavelength = min - max else only one number """
123        text_edit = self.txtWavelength  # self.sender()
124        if text_edit.isModified():
125            text_edit.setStyleSheet(QtCore.QString(BG_WHITE))
126            input_string = str(text_edit.text())
127            if self.cbWaveColor.currentText() != 'TOF':
128                input_wavelength = re.match('\d+\.?\d*', input_string)
129                if input_wavelength is None:
130                    text_edit.setStyleSheet(QtCore.QString(BG_RED))
131                    self.cmdCompute.setEnabled(False)
132                    logging.info('Wavelength has to be a number.')
133                else:
134                    text_edit.setStyleSheet(QtCore.QString(BG_WHITE))
135                    self.cmdCompute.setEnabled(True)
136            else:
137                interval_wavelength = re.match('^\d+\.?\d*\s*-\s*\d+\.?\d*$',
138                                               input_string)
139
140                if interval_wavelength is None:
141                    text_edit.setStyleSheet(QtCore.QString(BG_RED))
142                    self.cmdCompute.setEnabled(False)
143                    logging.info("Wavelength's input has to be an interval: "
144                                 "min - max.")
145                else:
146                    # check on min < max
147                    [wavelength_min, wavelength_max] = \
148                        re.findall('\d+\.?\d*', interval_wavelength.group())
149
150                    if float(wavelength_min) >= float(wavelength_max):
151                        text_edit.setStyleSheet(QtCore.QString(BG_RED))
152                        self.cmdCompute.setEnabled(False)
153                        logging.info("Wavelength: min must be smaller than max.")
154
155                    else:
156                        text_edit.setStyleSheet(QtCore.QString(BG_WHITE))
157                        self.cmdCompute.setEnabled(True)
158
159    def checkWavelengthSpread(self):
160        """ Validator for WavelengthSpread
161         Input can be a 'number or min - max (; Number of bins)' """
162        text_edit = self.sender()
163
164        if text_edit.isModified():
165            text_edit.setStyleSheet(QtCore.QString(BG_WHITE))
166            if self.cbWaveColor.currentText() != 'TOF':
167                pattern = '^\d+\.?\d*(|;\s*\d+)$'
168                input_string = str(text_edit.text())
169                wavelength_spread_input = re.match(pattern, input_string)
170
171                if wavelength_spread_input is None:
172                    text_edit.setStyleSheet(QtCore.QString(BG_RED))
173                    self.cmdCompute.setEnabled(False)
174                    logging.info('Wavelength spread has to be specified: '
175                                 'single value or value; integer number of bins.')
176
177                else:
178                    split_input = wavelength_spread_input.group().split(';')
179                    self.num_wave = split_input[1] if len(split_input) > 1 else 10
180                    text_edit.setStyleSheet(QtCore.QString(BG_WHITE))
181                    self.cmdCompute.setEnabled(True)
182            else:
183                pattern = '^\d+\.?\d*\s*-\s*\d+\.?\d*(|;\s*\d+)$'
184                input_string = str(text_edit.text())
185                wavelength_spread_input = re.match(pattern, input_string)
186
187                if wavelength_spread_input is None:
188                    text_edit.setStyleSheet(QtCore.QString(BG_RED))
189                    self.cmdCompute.setEnabled(False)
190                    logging.info("Wavelength spread has to be specified: "
191                                 "doublet separated by '-' with optional "
192                                 "number of bins (given after ';'). "
193                                 "For example, 0.1 - 0.1 (; 20).")
194
195                else:
196                    split_input = wavelength_spread_input.group().split(';')
197                    self.num_wave = split_input[1] if len(
198                        split_input) > 1 else 10
199                    text_edit.setStyleSheet(QtCore.QString(BG_WHITE))
200                    self.cmdCompute.setEnabled(True)
201
202    def checkPixels(self):
203        """ Validator for detector pixel size and number """
204        text_edit = self.sender()
205
206        if text_edit.isModified():
207            text_edit.setStyleSheet(QtCore.QString(BG_WHITE))
208            pattern = '^\d+\.?\d*,\s*\d+\.?\d*$'
209            input_string = str(text_edit.text())
210            pixels_input = re.match(pattern, input_string)
211
212            if pixels_input is None:
213                text_edit.setStyleSheet(QtCore.QString(BG_RED))
214                self.cmdCompute.setEnabled(False)
215                logging.info('The input for the detector should contain 2 '
216                             'values separated by a comma.')
217
218            else:
219                text_edit.setStyleSheet(QtCore.QString(BG_WHITE))
220                self.cmdCompute.setEnabled(True)
221
222    def checkQx_y(self):
223        """ Validator for qx and qy inputs """
224        Q_modified = [self.txtQx.isModified(), self.txtQy.isModified()]
225        if any(Q_modified):
226            pattern = '^-?\d+\.?\d*(,\s*-?\d+\.?\d*)*$'
227            text_edit = self.txtQx if Q_modified[0] else self.txtQy
228            input_string = str(text_edit.text())
229            q_input = re.match(pattern, input_string)
230            if q_input is None:
231                text_edit.setStyleSheet(QtCore.QString(BG_RED))
232                self.cmdCompute.setEnabled(False)
[fc4fec8]233                logging.info('Qx and Qy should contain one or more comma-separated numbers.')
[01cda57]234            else:
235                text_edit.setStyleSheet(QtCore.QString(BG_WHITE))
236                self.cmdCompute.setEnabled(True)
237                qx = str(self.txtQx.text()).split(',')
238                qy = str(self.txtQy.text()).split(',')
239
240                if len(qx) == 1 and len(qy) > 1:
241                    fill_qx = ', '.join([qx[0]] * len(qy))
242                    self.txtQx.setText(fill_qx)
243
244                elif len(qy) == 1 and len(qx) > 1:
245                    fill_qy = ', '.join([qy[0]] * len(qx))
246                    self.txtQy.setText(fill_qy)
247
248                elif len(qx) != len(qy):
249                    text_edit.setStyleSheet(QtCore.QString(BG_RED))
250                    self.cmdCompute.setEnabled(False)
251                    logging.info(
[fc4fec8]252                        'Qx and Qy should have the same number of elements.')
[01cda57]253
254                else:
255                    text_edit.setStyleSheet(QtCore.QString(BG_WHITE))
256                    self.cmdCompute.setEnabled(True)
257
258    def checkAperture(self):
259        """ Validator for Sample and Source apertures"""
260        text_edit = self.sender()
261
262        if text_edit.isModified():
263            text_edit.setStyleSheet(QtCore.QString(BG_WHITE))
264            input_string = str(text_edit.text())
265            pattern = '^\d+\.?\d*(|,\s*\d+)$'
266            aperture_input = re.match(pattern, input_string)
267
268            if aperture_input is None:
269                text_edit.setStyleSheet(QtCore.QString(BG_RED))
270                self.cmdCompute.setEnabled(False)
271                logging.info('A circular aperture is defined by a single '
272                             'value (diameter). A rectangular aperture is '
273                             'defined by 2 values separated by a comma.')
274
275            else:
276                text_edit.setStyleSheet(QtCore.QString(BG_WHITE))
277                self.cmdCompute.setEnabled(True)
278
279    # #################################
280    # Slots associated with signals from comboboxes
281    # #################################
282
283    def onSelectWaveColor(self):
284        """ Modify layout of GUI when TOF selected: add elements
285        and modify default entry of Wavelength """
286        list_wdata = self.resolution.get_wave_list()
287        min_lambda = min(list_wdata[0])
288
289        min_wspread = min(list_wdata[1])
290        max_wspread = max(list_wdata[1])
291
292        if self.cbWaveColor.currentText() == 'TOF':
293            self.cbCustomSpectrum.setVisible(True)
294            self.lblSpectrum.setVisible(True)
295            # Get information about wavelength and spread
296
297            if len(list_wdata[0]) < 2:
298                max_lambda = 2 * min_lambda
299            else:
300                max_lambda = max(list_wdata[0])
301            self.txtWavelength.setText('{} - {}'.format(min_lambda, max_lambda))
302            self.txtWavelengthSpread.setText('{} - {}'.format(min_wspread,
303                                                    max_wspread))
304
305        else:
306            self.cbCustomSpectrum.setVisible(False)
307            self.lblSpectrum.setVisible(False)
308            # modify Wavelength line edit only if set for TOF (2 elements)
309
310            if len(self.txtWavelength.text().split('-')) >= 2:
311                self.txtWavelength.setText(str(min_lambda))
312                self.txtWavelengthSpread.setText(str(min_wspread))
313
314    def onSelectCustomSpectrum(self):
315        """ On Spectrum Combobox event"""
316        if self.cbCustomSpectrum.currentText() == 'Add New':
317            datafile = QtGui.QFileDialog.getOpenFileName(
318                self, "Choose a spectral distribution file", "",
319                "All files (*.*)",
320                None, QtGui.QFileDialog.DontUseNativeDialog)
321
322            if datafile is None or str(datafile) == '':
323                logging.info("No spectral distribution data chosen.")
324                self.cbCustomSpectrum.setCurrentIndex(0)
325                self.resolution.set_spectrum(self.spectrum_dic['Flat'])
326                return
327
328            try:
329                basename = os.path.basename(datafile)
330                if basename not in self.spectrum_dic.keys():
331                    self.cbCustomSpectrum.addItem(basename)
332
333                input_f = open(datafile, 'r')
334                buff = input_f.read()
335                lines = buff.split('\n')
336
337                wavelength = []
338                intensity = []
339
340                for line in lines:
341                    toks = line.split()
342                    try:
343                        wave = float(toks[0])
344                        intens = float(toks[1])
345                        wavelength.append(wave)
346                        intensity.append(intens)
347                    except:
348                        logging.info('Could not extract values from file')
349            except:
350                raise
351
352            self.spectrum_dic[basename] = [wavelength, intensity]
353            self.resolution.set_spectrum(self.spectrum_dic[basename])
354        return
355
356    # #################################
357    # Slots associated with signals from push buttons
358    # #################################
359
360    def onHelp(self):
361        """
362        Bring up the Resolution Calculator Documentation whenever
363        the HELP button is clicked.
364        Calls Documentation Window with the path of the location within the
365        documentation tree (after /doc/ ....".
366        """
367        try:
368            location = GuiUtils.HELP_DIRECTORY_LOCATION + \
369                       "/user/sasgui/perspectives/calculator/resolution_calculator_help.html"
370            self.manager._helpView.load(QtCore.QUrl(location))
371            self.manager._helpView.show()
372
373        except AttributeError:
374            # No manager defined - testing and standalone runs
375            pass
376
377    def onReset(self):
378        # by default Spectrum label and cbCustomSpectrum are not visible
379        self.cbCustomSpectrum.setVisible(False)
380        self.lblSpectrum.setVisible(False)
381        # Comboboxes
382        self.cbCustomSpectrum.setCurrentIndex([self.cbCustomSpectrum.itemText(i)
383                                               for i in range(self.cbCustomSpectrum.count())].index('Flat'))
384        self.cbSource.setCurrentIndex([self.cbSource.itemText(i) for i in
385                                       range(self.cbSource.count())].index('Neutron'))
386        self.cbWaveColor.setCurrentIndex([self.cbWaveColor.itemText(i) for i
387                                          in range(self.cbWaveColor.count())].index('Monochromatic'))
388        # LineEdits
389        self.txtDetectorPixSize.setText('0.5, 0.5')
390        self.txtDetectorSize.setText('128, 128')
391        self.txtSample2DetectorDistance.setText('1000')
392        self.txtSampleApertureSize.setText('1.27')
393        self.txtSampleOffset.setText('0')
394        self.txtSource2SampleDistance.setText('1627')
395        self.txtSourceApertureSize.setText('3.81')
396        self.txtWavelength.setText('6.0')
397        self.txtWavelengthSpread.setText('0.125')
398        self.txtQx.setText('0.0')
399        self.txtQy.setText('0.0')
400        self.txt1DSigma.setText('0.0008289')
401        self.txtSigma_x.setText('0.0008288')
402        self.txtSigma_y.setText('0.0008288')
403        self.txtSigma_lamd.setText('3.168e-05')
404
405        self.image = None
406        self.source_mass = _SOURCE_MASS
407        self.det_coordinate = 'cartesian'
408        self.num_wave = 10
409        self.spectrum_dic = {}
410        self.spectrum_dic['Add new'] = ''
411        self.spectrum_dic['Flat'] = self.resolution.get_default_spectrum()
412        self.resolution.set_spectrum(self.spectrum_dic['Flat'])
413        # Reset plot
414        self.onCompute()
415
416    # TODO Keep legacy validators??
417    def onCompute(self):
418        """
419        Execute the computation of resolution
420        """
421        # Q min max list default
422        qx_min = []
423        qx_max = []
424        qy_min = []
425        qy_max = []
426        # possible max qrange
427        self.resolution.qxmin_limit = 0
428        self.resolution.qxmax_limit = 0
429        self.resolution.qymin_limit = 0
430        self.resolution.qymax_limit = 0
431
432        try:
433            # Get all the values to compute
434            wavelength = self._str2longlist(self.txtWavelength.text())
435
436            source = self.cbSource.currentText()
437            mass = self.source_mass[str(source)]
438            self.resolution.set_neutron_mass(float(mass))
439
440            wavelength_spread = self._str2longlist(\
441                        self.txtWavelengthSpread.text().split(';')[0])
442            # Validate the wave inputs
443            wave_input = self._validate_q_input(wavelength, wavelength_spread)
444            if wave_input is not None:
445                wavelength, wavelength_spread = wave_input
446
447            self.resolution.set_wave(wavelength)
448            self.resolution.set_wave_spread(wavelength_spread)
449
450            # use legacy validator for correct input assignment
451
452            source_aperture_size = self.txtSourceApertureSize.text()
453            source_aperture_size = self._str2longlist(source_aperture_size)
454            self.resolution.set_source_aperture_size(source_aperture_size)
455
456            sample_aperture_size = self.txtSampleApertureSize.text()
457            sample_aperture_size = self._string2list(sample_aperture_size)
458            self.resolution.set_sample_aperture_size(sample_aperture_size)
459
460            source2sample_distance = self.txtSource2SampleDistance.text()
461            source2sample_distance = self._string2list(source2sample_distance)
462            self.resolution.set_source2sample_distance(source2sample_distance)
463
464            sample2sample_distance = self.txtSampleOffset.text()
465            sample2sample_distance = self._string2list(sample2sample_distance)
466            self.resolution.set_sample2sample_distance(sample2sample_distance)
467
468            sample2detector_distance = self.txtSample2DetectorDistance.text()
469            sample2detector_distance = self._string2list(
470                sample2detector_distance)
471            self.resolution.set_sample2detector_distance(
472                sample2detector_distance)
473
474            detector_size = self.txtDetectorSize.text()
475            detector_size = self._string2list(detector_size)
476            self.resolution.set_detector_size(detector_size)
477
478            detector_pix_size = self.txtDetectorPixSize.text()
479            detector_pix_size = self._string2list(detector_pix_size)
480            self.resolution.set_detector_pix_size(detector_pix_size)
481
482            self.qx = self._string2inputlist(self.txtQx.text())
483            self.qy = self._string2inputlist(self.txtQy.text())
484
485            # Find min max of qs
486            xmin = min(self.qx)
487            xmax = max(self.qx)
488            ymin = min(self.qy)
489            ymax = max(self.qy)
490            if not self._validate_q_input(self.qx, self.qy):
[fc4fec8]491                raise ValueError("Invalid Q input")
[01cda57]492        except:
493            msg = "An error occurred during the resolution computation."
494            msg += "Please check your inputs..."
495            logging.warning(msg)
496            return
497
498        # Validate the q inputs
499        q_input = self._validate_q_input(self.qx, self.qy)
500        if q_input is not None:
501            self.qx, self.qy = q_input
502
503        # Make list of q min max for mapping
504        for i in range(len(self.qx)):
505            qx_min.append(xmin)
506            qx_max.append(xmax)
507        for i in range(len(self.qy)):
508            qy_min.append(ymin)
509            qy_max.append(ymax)
510
511        # Compute the resolution
512        if self.image is not None:
513            self.resolution.reset_image()
514
515        # Compute and get the image plot
516        try:
[fc4fec8]517            cal_res = threads.deferToThread(self.map_wrapper,
518                                            self.calc_func,
519                                            self.qx,
520                                            self.qy,
521                                            qx_min,
522                                            qx_max,
523                                            qy_min, qy_max)
524
525            cal_res.addCallback(self.complete)
526
527            # logging.info("Computation is in progress...")
[01cda57]528            self.cmdCompute.setText('Wait...')
529            self.cmdCompute.setEnabled(False)
530        except:
531            raise
532
[fc4fec8]533    def complete(self, image):
[01cda57]534        """
535        Complete computation
536        """
537        self.image = image
538
539        # Get and format the sigmas
540        sigma_r = self.formatNumber(self.resolution.sigma_1)
541        sigma_phi = self.formatNumber(self.resolution.sigma_2)
542        sigma_lamd = self.formatNumber(self.resolution.sigma_lamd)
543        sigma_1d = self.formatNumber(self.resolution.sigma_1d)
544
545        # Set output values
546        self.txtSigma_x.setText(str(sigma_r))
547        self.txtSigma_y.setText(str(sigma_phi))
548        self.txtSigma_lamd.setText(str(sigma_lamd))
549        self.txt1DSigma.setText(str(sigma_1d))
550
551        self.cmdCompute.setText('Compute')
552        self.cmdCompute.setEnabled(True)
[fc4fec8]553
554        self.new2DPlot()
555
[01cda57]556        return
557
[fc4fec8]558    def map_wrapper(self, func, qx, qy, qx_min, qx_max, qy_min, qy_max):
[01cda57]559        """
560        Prepare the Mapping for the computation
561        : params qx, qy, qx_min, qx_max, qy_min, qy_max:
562        : return: image (numpy array)
563        """
[fc4fec8]564        image = map(func, qx, qy,
565                    qx_min, qx_max,
566                    qy_min, qy_max)[0]
567        return image
568
569    def calc_func(self, qx, qy, qx_min, qx_max, qy_min, qy_max):
570        """
571        Perform the calculation for a given set of Q values.
572        : return: image (numpy array)
573        """
[01cda57]574        try:
575            qx_value = float(qx)
576            qy_value = float(qy)
[fc4fec8]577        except :
578            raise ValueError
579
[01cda57]580        # calculate 2D resolution distribution image
581        image = self.resolution.compute_and_plot(qx_value, qy_value,
582                                                 qx_min, qx_max, qy_min,
583                                                 qy_max,
584                                                 self.det_coordinate)
585        return image
586
587    # #################################
588    # Legacy validators
589    # #################################
[fc4fec8]590    def _string2list(self, input_string):
[01cda57]591        """
592        Change NNN, NNN to list,ie. [NNN, NNN] where NNN is a number
593        """
[fc4fec8]594        new_numbers_list = []
[01cda57]595        # check the number of floats
596        try:
[fc4fec8]597            strg = float(input_string)
598            new_numbers_list.append(strg)
[01cda57]599        except:
[fc4fec8]600            string_split = input_string.split(',')
[01cda57]601            if len(string_split) == 2:
602                str_1 = string_split[0]
603                str_2 = string_split[1]
[fc4fec8]604                new_numbers_list.append(float(str_1))
605                new_numbers_list.append(float(str_2))
[01cda57]606            elif len(string_split) == 1:
607                str_1 = string_split[0]
[fc4fec8]608                new_numbers_list.append(float(str_1))
[01cda57]609            else:
[fc4fec8]610                msg = "The numbers must be one or two (separated by ',')"
[01cda57]611                logging.info(msg)
612                raise RuntimeError, msg
613
[fc4fec8]614        return new_numbers_list
[01cda57]615
[fc4fec8]616    def _string2inputlist(self, input_string):
[01cda57]617        """
618        Change NNN, NNN,... to list,ie. [NNN, NNN,...] where NNN is a number
[fc4fec8]619        : return new_list: string like list
[01cda57]620        """
[fc4fec8]621        new_list = []
622        string_split = input_string.split(',')
623        try:
624            new_list = [float(t) for t in string_split]
625        except:
626            logging.error(sys.exc_value)
627        return new_list
[01cda57]628
[fc4fec8]629    def _str2longlist(self, input_string):
[01cda57]630        """
[fc4fec8]631          Change NNN, NNN,... to list, NNN - NNN ; NNN to list, or float to list
632          : return new_string: string like list
633          """
[01cda57]634        try:
635            # is float
[fc4fec8]636            out = [float(input_string)]
[01cda57]637            return out
638        except:
639            if self.cbWaveColor.currentText() == 'Monochromatic':
640                logging.warning("Wrong format of inputs.")
641            else:
642                try:
643                    # has a '-'
[fc4fec8]644                    if input_string.count('-') > 0:
645                        value = input_string.split('-')
[01cda57]646                        if value[1].count(';') > 0:
647                            # has a ';'
648                            last_list = value[1].split(';')
[fc4fec8]649                            num = numpy.ceil(float(last_list[1]))
[01cda57]650                            max_value = float(last_list[0])
651                            self.num_wave = num
652                        else:
653                            # default num
654                            num = self.num_wave
655                            max_value = float(value[1])
656                        min_value = float(value[0])
657                        # make a list
[fc4fec8]658                        bin_size = numpy.fabs(max_value - min_value) / (num - 1)
[01cda57]659                        out = [min_value + bin_size * bnum for bnum in
660                               range(num)]
661                        return out
[fc4fec8]662                    if input_string.count(',') > 0:
663                        out = self._string2inputlist(input_string)
[01cda57]664                        return out
665                except:
666                    logging.error(sys.exc_value)
667
668    def _validate_q_input(self, qx, qy):
669        """
670        Check if q inputs are valid
671        : params qx:  qx as a list
672        : params qy:  qy as a list
673        : return: True/False
674        """
675        # check qualifications
676        if qx.__class__.__name__ != 'list':
677            return None
678        if qy.__class__.__name__ != 'list':
679            return None
680        if len(qx) < 1:
681            return None
682        if len(qy) < 1:
683            return None
684        # allow one input
685        if len(qx) == 1 and len(qy) > 1:
686            qx = [qx[0] for ind in range(len(qy))]
687
688        if len(qy) == 1 and len(qx) > 1:
689            qy = [qy[0] for ind in range(len(qx))]
690        # check length
691        if len(qx) != len(qy):
692            return None
693        if qx is None or qy is None:
694            return None
695        return qx, qy
696
697    def formatNumber(self, value=None):
698        """
699        Return a float in a standardized, human-readable formatted string
700        """
701        try:
702            value = float(value)
703        except:
704            output = None
705            return output
706
707        output = "%-7.4g" % value
708        return output.lstrip().rstrip()
709
710    # #################################
711    # Plot
712    # #################################
713
714    def createTemplate2DPlot(self):
715        """
716        Create a template for 2D data
717        """
718        self.plotter = Plotter2DWidget(self, quickplot=True)
719        self.plotter.scale = 'linear'
[fc4fec8]720        self.plotter.cmap = None
[01cda57]721        layout = QtGui.QHBoxLayout()
722        layout.setContentsMargins(0, 0, 0, 0)
723        self.graphicsView.setLayout(layout)
724        layout.addWidget(self.plotter)
725
[fc4fec8]726    def new2DPlot(self):
[01cda57]727        """
728        Create a new 2D data instance based on computing results
729        """
730        qx_min, qx_max, qy_min, qy_max = self.resolution.get_detector_qrange()
731
732        dx_size = (qx_max - qx_min) / (1000 - 1)
733        dy_size = (qy_max - qy_min) / (1000 - 1)
734        x_val = numpy.arange(qx_min, qx_max, dx_size)
735        y_val = numpy.arange(qy_max, qy_min, -dy_size)
736
737        self.drawLines()
738
739        self.plotter.data = Data2D(image=self.image,
740                      qx_data=x_val,
741                      qy_data=y_val,
742                      xmin=qx_min, xmax=qx_max,
743                      ymin=qy_min, ymax=qy_max)
744
745        self.plotter.plot()
746        self.plotter.show()
747
748    def drawLines(self):
749        """
750        Draw lines in image if applicable
751        """
752        wave_list, _ = self.resolution.get_wave_list()
753        if len(wave_list) > 1 and wave_list[-1] == max(wave_list):
754            color = 'g'
755            # draw a green rectangle(limit for the longest wavelength
756            # to be involved) for tof inputs
757            # Get the params from resolution
758            # plotting range for largest wavelength
759            qx_min = self.resolution.qx_min
760            qx_max = self.resolution.qx_max
761            qy_min = self.resolution.qy_min
762            qy_max = self.resolution.qy_max
763            # detector range
764            detector_qx_min = self.resolution.detector_qx_min
765            detector_qx_max = self.resolution.detector_qx_max
766            detector_qy_min = self.resolution.detector_qy_min
767            detector_qy_max = self.resolution.detector_qy_max
768
769            rect = patches.Rectangle((detector_qx_min + 0.0002,
770                                      detector_qy_min + 0.0002),
771                                     detector_qx_max - detector_qx_min,
772                                     detector_qy_max - detector_qy_min,
773                                     linewidth=2,
774                                     edgecolor=color, facecolor='none')
775            self.plotter.ax.add_patch(rect)
776        else:
777            qx_min, qx_max, qy_min, qy_max = self.resolution.get_detector_qrange()
778            # detector range
779            detector_qx_min = self.resolution.qxmin_limit
780            detector_qx_max = self.resolution.qxmax_limit
781            detector_qy_min = self.resolution.qymin_limit
782            detector_qy_max = self.resolution.qymax_limit
783
784            xmin = min(self.qx)
785            xmax = max(self.qx)
786            ymin = min(self.qy)
787            ymax = max(self.qy)
788
789            if xmin < detector_qx_min or xmax > detector_qx_max or \
790                            ymin < detector_qy_min or ymax > detector_qy_max:
791                # message
792                msg = 'At least one q value located out side of\n'
793                msg += " the detector range (%s < qx < %s, %s < qy < %s),\n" % \
794                       (self.formatNumber(detector_qx_min),
795                        self.formatNumber(detector_qx_max),
796                        self.formatNumber(detector_qy_min),
797                        self.formatNumber(detector_qy_max))
798                msg += " is ignored in computation.\n"
799
800                logging.warning(msg)
801
802        # Draw zero axis lines.
803        if qy_min < 0 <= qy_max:
804            self.plotter.ax.axhline(linewidth=1)
805
806        if qx_min < 0 <= qx_max:
807            self.plotter.ax.axvline(linewidth=1)
Note: See TracBrowser for help on using the repository browser.