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

Last change on this file since 8d39961 was 170e95d, checked in by celinedurniak <celine.durniak@…>, 7 years ago

Corrected layout of Resolution panel

  • Property mode set to 100644
File size: 31.0 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 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)
233                logging.info('Qx and Qy should contain one or more comma-separated numbers.')
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(
252                        'Qx and Qy should have the same number of elements.')
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):
491                raise ValueError("Invalid Q input")
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:
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...")
528            self.cmdCompute.setText('Wait...')
529            self.cmdCompute.setEnabled(False)
530        except:
531            raise
532
533    def complete(self, image):
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)
553
554        self.new2DPlot()
555
556        return
557
558    def map_wrapper(self, func, qx, qy, qx_min, qx_max, qy_min, qy_max):
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        """
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        """
574        try:
575            qx_value = float(qx)
576            qy_value = float(qy)
577        except :
578            raise ValueError
579
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    # #################################
590    def _string2list(self, input_string):
591        """
592        Change NNN, NNN to list,ie. [NNN, NNN] where NNN is a number
593        """
594        new_numbers_list = []
595        # check the number of floats
596        try:
597            strg = float(input_string)
598            new_numbers_list.append(strg)
599        except:
600            string_split = input_string.split(',')
601            if len(string_split) == 1 or len(string_split) == 2:
602                new_numbers_list = [float(item) for item in string_split]
603            else:
604                msg = "The numbers must be one or two (separated by ',')"
605                logging.info(msg)
606                raise RuntimeError, msg
607
608        return new_numbers_list
609
610    def _string2inputlist(self, input_string):
611        """
612        Change NNN, NNN,... to list,ie. [NNN, NNN,...] where NNN is a number
613        : return new_list: string like list
614        """
615        new_list = []
616        string_split = input_string.split(',')
617        try:
618            new_list = [float(t) for t in string_split]
619        except:
620            logging.error(sys.exc_value)
621        return new_list
622
623    def _str2longlist(self, input_string):
624        """
625          Change NNN, NNN,... to list, NNN - NNN ; NNN to list, or float to list
626          : return new_string: string like list
627          """
628        try:
629            # is float
630            out = [float(input_string)]
631            return out
632        except:
633            if self.cbWaveColor.currentText() == 'Monochromatic':
634                logging.warning("Wrong format of inputs.")
635            else:
636                try:
637                    # has a '-'
638                    if input_string.count('-') > 0:
639                        value = input_string.split('-')
640                        if value[1].count(';') > 0:
641                            # has a ';'
642                            last_list = value[1].split(';')
643                            num = numpy.ceil(float(last_list[1]))
644                            max_value = float(last_list[0])
645                            self.num_wave = num
646                        else:
647                            # default num
648                            num = self.num_wave
649                            max_value = float(value[1])
650                        min_value = float(value[0])
651                        # make a list
652                        bin_size = numpy.fabs(max_value - min_value) / (num - 1)
653                        out = [min_value + bin_size * bnum for bnum in
654                               range(num)]
655                        return out
656                    if input_string.count(',') > 0:
657                        out = self._string2inputlist(input_string)
658                        return out
659                except:
660                    logging.error(sys.exc_value)
661
662    def _validate_q_input(self, qx, qy):
663        """
664        Check if q inputs are valid
665        : params qx:  qx as a list
666        : params qy:  qy as a list
667        : return: True/False
668        """
669        # check qualifications
670        if qx.__class__.__name__ != 'list':
671            return None
672        if qy.__class__.__name__ != 'list':
673            return None
674        if len(qx) < 1:
675            return None
676        if len(qy) < 1:
677            return None
678        # allow one input
679        if len(qx) == 1 and len(qy) > 1:
680            qx = [qx[0] for ind in range(len(qy))]
681
682        if len(qy) == 1 and len(qx) > 1:
683            qy = [qy[0] for ind in range(len(qx))]
684        # check length
685        if len(qx) != len(qy):
686            return None
687        if qx is None or qy is None:
688            return None
689        return qx, qy
690
691    def formatNumber(self, value=None):
692        """
693        Return a float in a standardized, human-readable formatted string
694        """
695        try:
696            value = float(value)
697        except:
698            output = None
699            return output
700
701        output = "%-7.4g" % value
702        return output.lstrip().rstrip()
703
704    # #################################
705    # Plot
706    # #################################
707
708    def createTemplate2DPlot(self):
709        """
710        Create a template for 2D data
711        """
712        self.plotter = Plotter2DWidget(self, quickplot=True)
713        self.plotter.scale = 'linear'
714        self.plotter.cmap = None
715        layout = QtGui.QHBoxLayout()
716        layout.setContentsMargins(0, 0, 0, 0)
717        self.graphicsView.setLayout(layout)
718        layout.addWidget(self.plotter)
719
720    def new2DPlot(self):
721        """
722        Create a new 2D data instance based on computing results
723        """
724        qx_min, qx_max, qy_min, qy_max = self.resolution.get_detector_qrange()
725
726        dx_size = (qx_max - qx_min) / (1000 - 1)
727        dy_size = (qy_max - qy_min) / (1000 - 1)
728        x_val = numpy.arange(qx_min, qx_max, dx_size)
729        y_val = numpy.arange(qy_max, qy_min, -dy_size)
730
731        if len(self.plotter.ax.patches):
732            self.plotter.ax.patches[0].remove()
733
734        self.drawLines()
735
736        self.plotter.data = Data2D(image=self.image,
737                      qx_data=x_val,
738                      qy_data=y_val,
739                      xmin=qx_min, xmax=qx_max,
740                      ymin=qy_min, ymax=qy_max)
741
742        self.plotter.plot()
743        self.plotter.show()
744
745    def drawLines(self):
746        """
747        Draw lines in image if applicable
748        """
749        wave_list, _ = self.resolution.get_wave_list()
750        if len(wave_list) > 1 and wave_list[-1] == max(wave_list):
751            color = 'g'
752            # draw a green rectangle(limit for the longest wavelength
753            # to be involved) for tof inputs
754            # Get the params from resolution
755            # plotting range for largest wavelength
756            qx_min = self.resolution.qx_min
757            qx_max = self.resolution.qx_max
758            qy_min = self.resolution.qy_min
759            qy_max = self.resolution.qy_max
760            # detector range
761            detector_qx_min = self.resolution.detector_qx_min
762            detector_qx_max = self.resolution.detector_qx_max
763            detector_qy_min = self.resolution.detector_qy_min
764            detector_qy_max = self.resolution.detector_qy_max
765
766            rect = patches.Rectangle((detector_qx_min + 0.0002,
767                                      detector_qy_min + 0.0002),
768                                     detector_qx_max - detector_qx_min,
769                                     detector_qy_max - detector_qy_min,
770                                     linewidth=2,
771                                     edgecolor=color, facecolor='none')
772            self.plotter.ax.add_patch(rect)
773        else:
774            qx_min, qx_max, qy_min, qy_max = self.resolution.get_detector_qrange()
775            # detector range
776            detector_qx_min = self.resolution.qxmin_limit
777            detector_qx_max = self.resolution.qxmax_limit
778            detector_qy_min = self.resolution.qymin_limit
779            detector_qy_max = self.resolution.qymax_limit
780
781            xmin = min(self.qx)
782            xmax = max(self.qx)
783            ymin = min(self.qy)
784            ymax = max(self.qy)
785
786            if xmin < detector_qx_min or xmax > detector_qx_max or \
787                            ymin < detector_qy_min or ymax > detector_qy_max:
788                # message
789                msg = 'At least one q value located out side of\n'
790                msg += " the detector range (%s < qx < %s, %s < qy < %s),\n" % \
791                       (self.formatNumber(detector_qx_min),
792                        self.formatNumber(detector_qx_max),
793                        self.formatNumber(detector_qy_min),
794                        self.formatNumber(detector_qy_max))
795                msg += " is ignored in computation.\n"
796
797                logging.warning(msg)
798
799        # Draw zero axis lines.
800        if qy_min < 0 <= qy_max:
801            self.plotter.ax.axhline(linewidth=1)
802
803        if qx_min < 0 <= qx_max:
804            self.plotter.ax.axvline(linewidth=1)
Note: See TracBrowser for help on using the repository browser.