""" This object is a small tool to allow user to quickly determine the variance in q from the instrumental parameters. """ from PyQt4 import QtGui from PyQt4 import QtCore from twisted.internet import threads import sas.qtgui.Utilities.GuiUtils as GuiUtils from sas.qtgui.Plotting.PlotterData import Data2D from sas.qtgui.Plotting.Plotter2D import Plotter2DWidget from sas.sascalc.calculator.resolution_calculator import ResolutionCalculator import matplotlib.patches as patches import numpy import sys import logging import math import os import re from UI.ResolutionCalculatorPanelUI import Ui_ResolutionCalculatorPanel _SOURCE_MASS = {'Alpha': 6.64465620E-24, 'Deuteron': 3.34358320E-24, 'Neutron': 1.67492729E-24, 'Photon': 0.0, 'Proton': 1.67262137E-24, 'Triton': 5.00826667E-24} BG_WHITE = "background-color: rgb(255, 255, 255);" BG_RED = "background-color: rgb(244, 170, 164);" _INTENSITY = 368428 _LAMBDA_ARRAY = [[0, 1e+16], [_INTENSITY, _INTENSITY]] class ResolutionCalculatorPanel(QtGui.QDialog, Ui_ResolutionCalculatorPanel): """ compute resolution in 2D """ def __init__(self, parent=None): super(ResolutionCalculatorPanel, self).__init__() self.setupUi(self) self.manager = parent # New font to display angstrom symbol new_font = 'font-family: -apple-system, "Helvetica Neue", "Ubuntu";' self.lblUnitWavelength.setStyleSheet(new_font) self.lblUnitQx.setStyleSheet(new_font) self.lblUnitQy.setStyleSheet(new_font) self.lblUnitSigmax.setStyleSheet(new_font) self.lblUnitSigmay.setStyleSheet(new_font) self.lblUnitSigmalamd.setStyleSheet(new_font) self.lblUnit1DSigma.setStyleSheet(new_font) # by default Spectrum label and cbCustomSpectrum are not visible self.cbCustomSpectrum.setVisible(False) self.lblSpectrum.setVisible(False) # self.onReset() # change index of comboboxes self.cbWaveColor.currentIndexChanged.connect(self.onSelectWaveColor) self.cbCustomSpectrum.currentIndexChanged.connect(self.onSelectCustomSpectrum) # push buttons self.cmdClose.clicked.connect(self.accept) self.cmdHelp.clicked.connect(self.onHelp) self.cmdCompute.clicked.connect(self.onCompute) self.cmdReset.clicked.connect(self.onReset) # input defaults self.qx = [] self.qy = [] # dQ defaults self.sigma_r = None self.sigma_phi = None self.sigma_1d = None # number of bins for wavelength and wavelength spread self.num_wave = 10 self.spectrum_dic = {} # dQ 2d image self.image = None # results of sigmas self.sigma_strings = ' ' # Source selection dic self.source_mass = _SOURCE_MASS # detector coordinate of estimation of sigmas self.det_coordinate = 'cartesian' self.resolution = ResolutionCalculator() self.spectrum_dic['Add new'] = '' self.spectrum_dic['Flat'] = self.resolution.get_default_spectrum() self.resolution.set_spectrum(self.spectrum_dic['Flat']) # validators self.txtWavelength.editingFinished.connect(self.checkWavelength) self.txtWavelengthSpread.editingFinished.connect(self.checkWavelengthSpread) self.txtDetectorPixSize.editingFinished.connect(self.checkPixels) self.txtDetectorSize.editingFinished.connect(self.checkPixels) self.txtSourceApertureSize.editingFinished.connect(self.checkAperture) self.txtSampleApertureSize.editingFinished.connect(self.checkAperture) self.txtQx.editingFinished.connect(self.checkQx_y) self.txtQy.editingFinished.connect(self.checkQx_y) # double validator self.txtSource2SampleDistance.setValidator(QtGui.QDoubleValidator()) self.txtSample2DetectorDistance.setValidator(QtGui.QDoubleValidator()) self.txtSampleOffset.setValidator(QtGui.QDoubleValidator()) # call compute to calculate with default values self.createTemplate2DPlot() self.onCompute() # ################################# # Validators: red background in line edits when wrong input # and display of info logging message # ################################# def checkWavelength(self): """ Validator for Wavelength if TOF, wavelength = min - max else only one number """ text_edit = self.txtWavelength # self.sender() if text_edit.isModified(): text_edit.setStyleSheet(QtCore.QString(BG_WHITE)) input_string = str(text_edit.text()) if self.cbWaveColor.currentText() != 'TOF': input_wavelength = re.match('\d+\.?\d*', input_string) if input_wavelength is None: text_edit.setStyleSheet(QtCore.QString(BG_RED)) self.cmdCompute.setEnabled(False) logging.info('Wavelength has to be a number.') else: text_edit.setStyleSheet(QtCore.QString(BG_WHITE)) self.cmdCompute.setEnabled(True) else: interval_wavelength = re.match('^\d+\.?\d*\s*-\s*\d+\.?\d*$', input_string) if interval_wavelength is None: text_edit.setStyleSheet(QtCore.QString(BG_RED)) self.cmdCompute.setEnabled(False) logging.info("Wavelength's input has to be an interval: " "min - max.") else: # check on min < max [wavelength_min, wavelength_max] = \ re.findall('\d+\.?\d*', interval_wavelength.group()) if float(wavelength_min) >= float(wavelength_max): text_edit.setStyleSheet(QtCore.QString(BG_RED)) self.cmdCompute.setEnabled(False) logging.info("Wavelength: min must be smaller than max.") else: text_edit.setStyleSheet(QtCore.QString(BG_WHITE)) self.cmdCompute.setEnabled(True) def checkWavelengthSpread(self): """ Validator for WavelengthSpread Input can be a 'number or min - max (; Number of bins)' """ text_edit = self.sender() if text_edit.isModified(): text_edit.setStyleSheet(QtCore.QString(BG_WHITE)) if self.cbWaveColor.currentText() != 'TOF': pattern = '^\d+\.?\d*(|;\s*\d+)$' input_string = str(text_edit.text()) wavelength_spread_input = re.match(pattern, input_string) if wavelength_spread_input is None: text_edit.setStyleSheet(QtCore.QString(BG_RED)) self.cmdCompute.setEnabled(False) logging.info('Wavelength spread has to be specified: ' 'single value or value; integer number of bins.') else: split_input = wavelength_spread_input.group().split(';') self.num_wave = split_input[1] if len(split_input) > 1 else 10 text_edit.setStyleSheet(QtCore.QString(BG_WHITE)) self.cmdCompute.setEnabled(True) else: pattern = '^\d+\.?\d*\s*-\s*\d+\.?\d*(|;\s*\d+)$' input_string = str(text_edit.text()) wavelength_spread_input = re.match(pattern, input_string) if wavelength_spread_input is None: text_edit.setStyleSheet(QtCore.QString(BG_RED)) self.cmdCompute.setEnabled(False) logging.info("Wavelength spread has to be specified: " "doublet separated by '-' with optional " "number of bins (given after ';'). " "For example, 0.1 - 0.1 (; 20).") else: split_input = wavelength_spread_input.group().split(';') self.num_wave = split_input[1] if len( split_input) > 1 else 10 text_edit.setStyleSheet(QtCore.QString(BG_WHITE)) self.cmdCompute.setEnabled(True) def checkPixels(self): """ Validator for detector pixel size and number """ text_edit = self.sender() if text_edit.isModified(): text_edit.setStyleSheet(QtCore.QString(BG_WHITE)) pattern = '^\d+\.?\d*,\s*\d+\.?\d*$' input_string = str(text_edit.text()) pixels_input = re.match(pattern, input_string) if pixels_input is None: text_edit.setStyleSheet(QtCore.QString(BG_RED)) self.cmdCompute.setEnabled(False) logging.info('The input for the detector should contain 2 ' 'values separated by a comma.') else: text_edit.setStyleSheet(QtCore.QString(BG_WHITE)) self.cmdCompute.setEnabled(True) def checkQx_y(self): """ Validator for qx and qy inputs """ Q_modified = [self.txtQx.isModified(), self.txtQy.isModified()] if any(Q_modified): pattern = '^-?\d+\.?\d*(,\s*-?\d+\.?\d*)*$' text_edit = self.txtQx if Q_modified[0] else self.txtQy input_string = str(text_edit.text()) q_input = re.match(pattern, input_string) if q_input is None: text_edit.setStyleSheet(QtCore.QString(BG_RED)) self.cmdCompute.setEnabled(False) logging.info('Qx and Qy are lists of comma-separated numbers.') else: text_edit.setStyleSheet(QtCore.QString(BG_WHITE)) self.cmdCompute.setEnabled(True) qx = str(self.txtQx.text()).split(',') qy = str(self.txtQy.text()).split(',') if len(qx) == 1 and len(qy) > 1: fill_qx = ', '.join([qx[0]] * len(qy)) self.txtQx.setText(fill_qx) elif len(qy) == 1 and len(qx) > 1: fill_qy = ', '.join([qy[0]] * len(qx)) self.txtQy.setText(fill_qy) elif len(qx) != len(qy): text_edit.setStyleSheet(QtCore.QString(BG_RED)) self.cmdCompute.setEnabled(False) logging.info( 'Qx and Qy have the same number of elements.') else: text_edit.setStyleSheet(QtCore.QString(BG_WHITE)) self.cmdCompute.setEnabled(True) def checkAperture(self): """ Validator for Sample and Source apertures""" text_edit = self.sender() if text_edit.isModified(): text_edit.setStyleSheet(QtCore.QString(BG_WHITE)) input_string = str(text_edit.text()) pattern = '^\d+\.?\d*(|,\s*\d+)$' aperture_input = re.match(pattern, input_string) if aperture_input is None: text_edit.setStyleSheet(QtCore.QString(BG_RED)) self.cmdCompute.setEnabled(False) logging.info('A circular aperture is defined by a single ' 'value (diameter). A rectangular aperture is ' 'defined by 2 values separated by a comma.') else: text_edit.setStyleSheet(QtCore.QString(BG_WHITE)) self.cmdCompute.setEnabled(True) # ################################# # Slots associated with signals from comboboxes # ################################# def onSelectWaveColor(self): """ Modify layout of GUI when TOF selected: add elements and modify default entry of Wavelength """ list_wdata = self.resolution.get_wave_list() min_lambda = min(list_wdata[0]) min_wspread = min(list_wdata[1]) max_wspread = max(list_wdata[1]) if self.cbWaveColor.currentText() == 'TOF': self.cbCustomSpectrum.setVisible(True) self.lblSpectrum.setVisible(True) # Get information about wavelength and spread if len(list_wdata[0]) < 2: max_lambda = 2 * min_lambda else: max_lambda = max(list_wdata[0]) self.txtWavelength.setText('{} - {}'.format(min_lambda, max_lambda)) self.txtWavelengthSpread.setText('{} - {}'.format(min_wspread, max_wspread)) else: self.cbCustomSpectrum.setVisible(False) self.lblSpectrum.setVisible(False) # modify Wavelength line edit only if set for TOF (2 elements) if len(self.txtWavelength.text().split('-')) >= 2: self.txtWavelength.setText(str(min_lambda)) self.txtWavelengthSpread.setText(str(min_wspread)) def onSelectCustomSpectrum(self): """ On Spectrum Combobox event""" if self.cbCustomSpectrum.currentText() == 'Add New': datafile = QtGui.QFileDialog.getOpenFileName( self, "Choose a spectral distribution file", "", "All files (*.*)", None, QtGui.QFileDialog.DontUseNativeDialog) if datafile is None or str(datafile) == '': logging.info("No spectral distribution data chosen.") self.cbCustomSpectrum.setCurrentIndex(0) self.resolution.set_spectrum(self.spectrum_dic['Flat']) return try: basename = os.path.basename(datafile) if basename not in self.spectrum_dic.keys(): self.cbCustomSpectrum.addItem(basename) input_f = open(datafile, 'r') buff = input_f.read() lines = buff.split('\n') wavelength = [] intensity = [] for line in lines: toks = line.split() try: wave = float(toks[0]) intens = float(toks[1]) wavelength.append(wave) intensity.append(intens) except: logging.info('Could not extract values from file') except: raise self.spectrum_dic[basename] = [wavelength, intensity] self.resolution.set_spectrum(self.spectrum_dic[basename]) return # ################################# # Slots associated with signals from push buttons # ################################# def onHelp(self): """ Bring up the Resolution Calculator Documentation whenever the HELP button is clicked. Calls Documentation Window with the path of the location within the documentation tree (after /doc/ ....". """ try: location = GuiUtils.HELP_DIRECTORY_LOCATION + \ "/user/sasgui/perspectives/calculator/resolution_calculator_help.html" self.manager._helpView.load(QtCore.QUrl(location)) self.manager._helpView.show() except AttributeError: # No manager defined - testing and standalone runs pass def onReset(self): # by default Spectrum label and cbCustomSpectrum are not visible self.cbCustomSpectrum.setVisible(False) self.lblSpectrum.setVisible(False) # Comboboxes self.cbCustomSpectrum.setCurrentIndex([self.cbCustomSpectrum.itemText(i) for i in range(self.cbCustomSpectrum.count())].index('Flat')) self.cbSource.setCurrentIndex([self.cbSource.itemText(i) for i in range(self.cbSource.count())].index('Neutron')) self.cbWaveColor.setCurrentIndex([self.cbWaveColor.itemText(i) for i in range(self.cbWaveColor.count())].index('Monochromatic')) # LineEdits self.txtDetectorPixSize.setText('0.5, 0.5') self.txtDetectorSize.setText('128, 128') self.txtSample2DetectorDistance.setText('1000') self.txtSampleApertureSize.setText('1.27') self.txtSampleOffset.setText('0') self.txtSource2SampleDistance.setText('1627') self.txtSourceApertureSize.setText('3.81') self.txtWavelength.setText('6.0') self.txtWavelengthSpread.setText('0.125') self.txtQx.setText('0.0') self.txtQy.setText('0.0') self.txt1DSigma.setText('0.0008289') self.txtSigma_x.setText('0.0008288') self.txtSigma_y.setText('0.0008288') self.txtSigma_lamd.setText('3.168e-05') self.image = None self.sigma_strings = ' ' self.source_mass = _SOURCE_MASS self.det_coordinate = 'cartesian' self.num_wave = 10 self.spectrum_dic = {} self.spectrum_dic['Add new'] = '' self.spectrum_dic['Flat'] = self.resolution.get_default_spectrum() self.resolution.set_spectrum(self.spectrum_dic['Flat']) # Reset plot self.onCompute() # TODO Keep legacy validators?? def onCompute(self): """ Execute the computation of resolution """ # Q min max list default qx_min = [] qx_max = [] qy_min = [] qy_max = [] # possible max qrange self.resolution.qxmin_limit = 0 self.resolution.qxmax_limit = 0 self.resolution.qymin_limit = 0 self.resolution.qymax_limit = 0 try: # Get all the values to compute wavelength = self._str2longlist(self.txtWavelength.text()) source = self.cbSource.currentText() mass = self.source_mass[str(source)] self.resolution.set_neutron_mass(float(mass)) wavelength_spread = self._str2longlist(\ self.txtWavelengthSpread.text().split(';')[0]) # Validate the wave inputs wave_input = self._validate_q_input(wavelength, wavelength_spread) if wave_input is not None: wavelength, wavelength_spread = wave_input self.resolution.set_wave(wavelength) self.resolution.set_wave_spread(wavelength_spread) # use legacy validator for correct input assignment source_aperture_size = self.txtSourceApertureSize.text() source_aperture_size = self._str2longlist(source_aperture_size) self.resolution.set_source_aperture_size(source_aperture_size) sample_aperture_size = self.txtSampleApertureSize.text() sample_aperture_size = self._string2list(sample_aperture_size) self.resolution.set_sample_aperture_size(sample_aperture_size) source2sample_distance = self.txtSource2SampleDistance.text() source2sample_distance = self._string2list(source2sample_distance) self.resolution.set_source2sample_distance(source2sample_distance) sample2sample_distance = self.txtSampleOffset.text() sample2sample_distance = self._string2list(sample2sample_distance) self.resolution.set_sample2sample_distance(sample2sample_distance) sample2detector_distance = self.txtSample2DetectorDistance.text() sample2detector_distance = self._string2list( sample2detector_distance) self.resolution.set_sample2detector_distance( sample2detector_distance) detector_size = self.txtDetectorSize.text() detector_size = self._string2list(detector_size) self.resolution.set_detector_size(detector_size) detector_pix_size = self.txtDetectorPixSize.text() detector_pix_size = self._string2list(detector_pix_size) self.resolution.set_detector_pix_size(detector_pix_size) self.qx = self._string2inputlist(self.txtQx.text()) self.qy = self._string2inputlist(self.txtQy.text()) # Find min max of qs xmin = min(self.qx) xmax = max(self.qx) ymin = min(self.qy) ymax = max(self.qy) if not self._validate_q_input(self.qx, self.qy): raise except: msg = "An error occurred during the resolution computation." msg += "Please check your inputs..." logging.warning(msg) return # raise ValueError, "Invalid Q Input..." # Validate the q inputs q_input = self._validate_q_input(self.qx, self.qy) if q_input is not None: self.qx, self.qy = q_input # Make list of q min max for mapping for i in range(len(self.qx)): qx_min.append(xmin) qx_max.append(xmax) for i in range(len(self.qy)): qy_min.append(ymin) qy_max.append(ymax) # Compute the resolution if self.image is not None: self.resolution.reset_image() # Compute and get the image plot try: self.sigma_strings = '\nResolution: Computation is finished. \n' cal_res = threads.deferToThread(self.complete, map(self._map_func, self.qx, self.qy, qx_min, qx_max, qy_min, qy_max)[0], self.image) cal_res.addCallback(self.new2DPlot) logging.info("Computation is in progress...") self.cmdCompute.setText('Wait...') self.cmdCompute.setEnabled(False) except: raise def complete(self, image, elapsed=None): """ Complete computation """ self.image = image # Get and format the sigmas sigma_r = self.formatNumber(self.resolution.sigma_1) sigma_phi = self.formatNumber(self.resolution.sigma_2) sigma_lamd = self.formatNumber(self.resolution.sigma_lamd) sigma_1d = self.formatNumber(self.resolution.sigma_1d) # Set output values self.txtSigma_x.setText(str(sigma_r)) self.txtSigma_y.setText(str(sigma_phi)) self.txtSigma_lamd.setText(str(sigma_lamd)) self.txt1DSigma.setText(str(sigma_1d)) msg = self.sigma_strings logging.info(msg + '\n stop') self.cmdCompute.setText('Compute') self.cmdCompute.setEnabled(True) return def _map_func(self, qx, qy, qx_min, qx_max, qy_min, qy_max): """ Prepare the Mapping for the computation : params qx, qy, qx_min, qx_max, qy_min, qy_max: : return: image (numpy array) """ try: qx_value = float(qx) qy_value = float(qy) except: raise # calculate 2D resolution distribution image image = self.resolution.compute_and_plot(qx_value, qy_value, qx_min, qx_max, qy_min, qy_max, self.det_coordinate) # record sigmas self.sigma_strings += " At Qx = %s, Qy = %s; \n" % (qx_value, qy_value) self._sigma_strings() logging.info(self.sigma_strings) return image # ################################# # Legacy validators # ################################# def _sigma_strings(self): """ Recode sigmas as strings """ sigma_r = self.formatNumber(self.resolution.sigma_1) sigma_phi = self.formatNumber(self.resolution.sigma_2) sigma_lamd = self.formatNumber(self.resolution.sigma_lamd) sigma_1d = self.formatNumber(self.resolution.sigma_1d) # Set output values self.sigma_strings += " sigma_x = %s\n" % sigma_r self.sigma_strings += " sigma_y = %s\n" % sigma_phi self.sigma_strings += " sigma_lamd = %s\n" % sigma_lamd self.sigma_strings += " sigma_1D = %s\n" % sigma_1d def _string2list(self, string): """ Change NNN, NNN to list,ie. [NNN, NNN] where NNN is a number """ new_string = [] # check the number of floats try: strg = float(string) new_string.append(strg) except: string_split = string.split(',') if len(string_split) == 2: str_1 = string_split[0] str_2 = string_split[1] new_string.append(float(str_1)) new_string.append(float(str_2)) elif len(string_split) == 1: str_1 = string_split[0] new_string.append(float(str_1)) else: msg = "The numbers must be one or two (separated by ',')..." logging.info(msg) raise RuntimeError, msg return new_string def _string2inputlist(self, string): """ Change NNN, NNN,... to list,ie. [NNN, NNN,...] where NNN is a number : return new_string: string like list """ new_string = [] string_split = string.split(',') length = len(string_split) for ind in range(length): try: value = float(string_split[ind]) new_string.append(value) except: logging.error(sys.exc_value) return new_string def _str2longlist(self, string): """ Change NNN, NNN,... to list, NNN - NNN ; NNN to list, or float to list : return new_string: string like list """ try: # is float out = [float(string)] return out except: if self.cbWaveColor.currentText() == 'Monochromatic': logging.warning("Wrong format of inputs.") else: try: # has a '-' if string.count('-') > 0: value = string.split('-') if value[1].count(';') > 0: # has a ';' last_list = value[1].split(';') num = math.ceil(float(last_list[1])) max_value = float(last_list[0]) self.num_wave = num else: # default num num = self.num_wave max_value = float(value[1]) min_value = float(value[0]) # make a list bin_size = math.fabs(max_value - min_value) / (num - 1) out = [min_value + bin_size * bnum for bnum in range(num)] return out if string.count(',') > 0: out = self._string2inputlist(string) return out except: logging.error(sys.exc_value) def _validate_q_input(self, qx, qy): """ Check if q inputs are valid : params qx: qx as a list : params qy: qy as a list : return: True/False """ # check qualifications if qx.__class__.__name__ != 'list': return None if qy.__class__.__name__ != 'list': return None if len(qx) < 1: return None if len(qy) < 1: return None # allow one input if len(qx) == 1 and len(qy) > 1: qx = [qx[0] for ind in range(len(qy))] if len(qy) == 1 and len(qx) > 1: qy = [qy[0] for ind in range(len(qx))] # check length if len(qx) != len(qy): return None if qx is None or qy is None: return None return qx, qy def formatNumber(self, value=None): """ Return a float in a standardized, human-readable formatted string """ try: value = float(value) except: output = None return output output = "%-7.4g" % value return output.lstrip().rstrip() # ################################# # Plot # ################################# def createTemplate2DPlot(self): """ Create a template for 2D data """ self.plotter = Plotter2DWidget(self, quickplot=True) self.plotter.scale = 'linear' layout = QtGui.QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) self.graphicsView.setLayout(layout) layout.addWidget(self.plotter) def new2DPlot(self, res_cal): """ Create a new 2D data instance based on computing results """ qx_min, qx_max, qy_min, qy_max = self.resolution.get_detector_qrange() dx_size = (qx_max - qx_min) / (1000 - 1) dy_size = (qy_max - qy_min) / (1000 - 1) x_val = numpy.arange(qx_min, qx_max, dx_size) y_val = numpy.arange(qy_max, qy_min, -dy_size) self.drawLines() self.plotter.data = Data2D(image=self.image, qx_data=x_val, qy_data=y_val, xmin=qx_min, xmax=qx_max, ymin=qy_min, ymax=qy_max) self.plotter.plot() self.plotter.show() def drawLines(self): """ Draw lines in image if applicable """ wave_list, _ = self.resolution.get_wave_list() if len(wave_list) > 1 and wave_list[-1] == max(wave_list): color = 'g' # draw a green rectangle(limit for the longest wavelength # to be involved) for tof inputs # Get the params from resolution # plotting range for largest wavelength qx_min = self.resolution.qx_min qx_max = self.resolution.qx_max qy_min = self.resolution.qy_min qy_max = self.resolution.qy_max # detector range detector_qx_min = self.resolution.detector_qx_min detector_qx_max = self.resolution.detector_qx_max detector_qy_min = self.resolution.detector_qy_min detector_qy_max = self.resolution.detector_qy_max rect = patches.Rectangle((detector_qx_min + 0.0002, detector_qy_min + 0.0002), detector_qx_max - detector_qx_min, detector_qy_max - detector_qy_min, linewidth=2, edgecolor=color, facecolor='none') self.plotter.ax.add_patch(rect) else: qx_min, qx_max, qy_min, qy_max = self.resolution.get_detector_qrange() # detector range detector_qx_min = self.resolution.qxmin_limit detector_qx_max = self.resolution.qxmax_limit detector_qy_min = self.resolution.qymin_limit detector_qy_max = self.resolution.qymax_limit xmin = min(self.qx) xmax = max(self.qx) ymin = min(self.qy) ymax = max(self.qy) if xmin < detector_qx_min or xmax > detector_qx_max or \ ymin < detector_qy_min or ymax > detector_qy_max: # message msg = 'At least one q value located out side of\n' msg += " the detector range (%s < qx < %s, %s < qy < %s),\n" % \ (self.formatNumber(detector_qx_min), self.formatNumber(detector_qx_max), self.formatNumber(detector_qy_min), self.formatNumber(detector_qy_max)) msg += " is ignored in computation.\n" logging.warning(msg) # Draw zero axis lines. if qy_min < 0 <= qy_max: self.plotter.ax.axhline(linewidth=1) if qx_min < 0 <= qx_max: self.plotter.ax.axvline(linewidth=1)