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

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 01cda57 was 01cda57, checked in by celinedurniak <celine.durniak@…>, 7 years ago

Added new GUI for Q resolution estimator

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