Dioptas/dioptas/controller/CalibrationController.py

933 lines
36 KiB
Python
Executable File

# Dioptas - GUI program for fast processing of 2D X-ray diffraction data
# Principal author: Clemens Prescher (clemens.prescher@gmail.com)
# Copyright (C) 2014-2019 GSECARS, University of Chicago, USA
# Copyright (C) 2015-2018 Institute for Geology and Mineralogy, University of Cologne, Germany
# Copyright (C) 2019-2020 DESY, Hamburg, Germany
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
from qtpy import QtWidgets, QtCore
import numpy as np
from ..widgets.UtilityWidgets import open_file_dialog, save_file_dialog
from .. import calibrants_path
# imports for type hinting in PyCharm -- DO NOT DELETE
from ..widgets.CalibrationWidget import CalibrationWidget
from ..widgets.UtilityWidgets import open_file_dialog
from ..model.DioptasModel import DioptasModel
from ..model.CalibrationModel import (
NotEnoughSpacingsInCalibrant,
get_available_detectors,
DetectorModes,
)
class CalibrationController(object):
"""
CalibrationController handles all the interaction between the CalibrationView and the CalibrationData class
"""
def __init__(self, widget, dioptas_model):
"""Manages the connection between the calibration GUI and data
:param widget: Gives the Calibration Widget
:type widget: CalibrationWidget
:param dioptas_model: Reference to DioptasModel Object
:type dioptas_model: DioptasModel
"""
self.widget = widget
self.model = dioptas_model
self.widget.set_start_values(self.model.calibration_model.start_values)
self.create_signals()
self.load_detectors_list()
self.load_calibrants_list()
def create_signals(self):
"""
Connects the GUI signals to the appropriate Controller methods.
"""
self.model.img_changed.connect(self.plot_image)
self.model.configuration_selected.connect(
self.update_calibration_parameter_in_view
)
self.model.configuration_selected.connect(
self.update_detector_parameters_in_view
)
self.model.calibration_model.detector_reset.connect(
self.update_detector_parameters_in_view
)
self.model.calibration_model.detector_reset.connect(
self.show_detector_reset_message_box
)
self.create_transformation_signals()
self.create_update_signals()
self.create_mouse_signals()
self.widget.detectors_cb.currentIndexChanged.connect(self.load_detector)
self.widget.detector_load_btn.clicked.connect(self.load_detector_from_file)
self.widget.detector_reset_btn.clicked.connect(self.reset_detector_from_file)
self.widget.calibrant_cb.currentIndexChanged.connect(self.load_calibrant)
self.widget.load_img_btn.clicked.connect(self.load_img)
self.widget.load_next_img_btn.clicked.connect(self.load_next_img)
self.widget.load_previous_img_btn.clicked.connect(self.load_previous_img)
self.widget.filename_txt.editingFinished.connect(self.update_filename_txt)
self.widget.save_calibration_btn.clicked.connect(self.save_calibration)
self.widget.load_calibration_btn.clicked.connect(self.load_calibration)
self.widget.calibrate_btn.clicked.connect(self.calibrate)
self.widget.refine_btn.clicked.connect(self.refine)
self.widget.clear_peaks_btn.clicked.connect(self.clear_peaks)
self.widget.undo_peaks_btn.clicked.connect(self.undo_peaks_btn_clicked)
self.widget.load_spline_btn.clicked.connect(self.load_spline_btn_click)
self.widget.spline_reset_btn.clicked.connect(self.reset_spline_btn_click)
self.widget.f2_wavelength_cb.stateChanged.connect(self.wavelength_cb_changed)
self.widget.pf_wavelength_cb.stateChanged.connect(self.wavelength_cb_changed)
self.widget.sv_wavelength_cb.stateChanged.connect(self.wavelength_cb_changed)
self.widget.f2_distance_cb.stateChanged.connect(self.distance_cb_changed)
self.widget.pf_distance_cb.stateChanged.connect(self.distance_cb_changed)
self.widget.sv_distance_cb.stateChanged.connect(self.distance_cb_changed)
self.widget.use_mask_cb.stateChanged.connect(self.plot_mask)
self.widget.mask_transparent_cb.stateChanged.connect(
self.mask_transparent_status_changed
)
def create_transformation_signals(self):
"""
Connects all the rotation GUI controls.
"""
self.widget.rotate_m90_btn.clicked.connect(self.rotate_m90_btn_clicked)
self.widget.rotate_p90_btn.clicked.connect(self.rotate_p90_btn_clicked)
self.widget.invert_horizontal_btn.clicked.connect(
self.invert_horizontal_btn_clicked
)
self.widget.invert_vertical_btn.clicked.connect(
self.invert_vertical_btn_clicked
)
self.widget.reset_transformations_btn.clicked.connect(
self.reset_transformations_btn_clicked
)
def activate(self):
if not self.model.img_changed.has_listener(self.plot_image):
self.model.img_changed.connect(self.plot_image)
self.update_img_plot()
def update_img_plot(self):
self.plot_image()
self.plot_mask()
def deactivate(self):
if self.model.img_changed.has_listener(self.plot_image):
self.model.img_changed.disconnect(self.plot_image)
def rotate_m90_btn_clicked(self):
self.model.calibration_model.rotate_detector_m90()
self.model.img_model.rotate_img_m90()
self.clear_peaks()
def rotate_p90_btn_clicked(self):
self.model.calibration_model.rotate_detector_p90()
self.model.img_model.rotate_img_p90()
self.clear_peaks()
def invert_horizontal_btn_clicked(self):
self.model.calibration_model.flip_detector_horizontally()
self.model.img_model.flip_img_horizontally()
self.clear_peaks()
def invert_vertical_btn_clicked(self):
self.model.calibration_model.flip_detector_vertically()
self.model.img_model.flip_img_vertically()
self.clear_peaks()
def reset_transformations_btn_clicked(self):
self.model.calibration_model.reset_transformations()
self.model.img_model.reset_transformations()
self.clear_peaks()
def create_update_signals(self):
"""
Connects all the txt box signals. Which specifically are the update buttons here.
"""
self.widget.f2_update_btn.clicked.connect(self.update_f2_btn_click)
self.widget.pf_update_btn.clicked.connect(self.update_pyFAI_btn_click)
def create_mouse_signals(self):
"""
Creates the mouse_move connections to show the current position of the mouse pointer.
"""
self.widget.img_widget.mouse_moved.connect(self.update_img_mouse_position_lbl)
self.widget.cake_widget.mouse_moved.connect(self.update_cake_mouse_position_lbl)
self.widget.pattern_widget.mouse_moved.connect(
self.update_pattern_mouse_position_lbl
)
self.widget.img_widget.mouse_left_clicked.connect(self.search_peaks)
def update_f2_btn_click(self):
"""
Takes all parameters inserted into the fit2d txt-fields and updates the current calibration accordingly.
"""
fit2d_parameter = self.widget.get_fit2d_parameter()
self.model.calibration_model.set_fit2d(fit2d_parameter)
self.update_all()
def update_pyFAI_btn_click(self):
"""
Takes all parameters inserted into the fit2d txt-fields and updates the current calibration accordingly.
"""
pyFAI_parameter = self.widget.get_pyFAI_parameter()
self.model.calibration_model.set_pyFAI(pyFAI_parameter)
self.update_all()
def load_img(self):
"""
Loads an image file.
"""
filename = open_file_dialog(
self.widget,
caption="Load Calibration Image",
directory=self.model.working_directories["image"],
)
if filename != "":
self.model.working_directories["image"] = os.path.dirname(filename)
self.model.img_model.load(filename)
def load_next_img(self):
self.model.img_model.load_next_file()
def load_previous_img(self):
self.model.img_model.load_previous_file()
def update_filename_txt(self):
"""
Updates the filename in the GUI corresponding to the filename in img_data
"""
current_filename = os.path.basename(self.model.img_model.filename)
current_directory = os.path.dirname(self.model.img_model.filename)
new_filename = str(self.widget.filename_txt.text())
if current_filename == new_filename:
return
if os.path.exists(os.path.join(current_directory, new_filename)):
try:
self.load_img(os.path.join(current_directory, new_filename))
except TypeError:
self.widget.filename_txt.setText(current_filename)
else:
self.widget.filename_txt.setText(current_filename)
def load_detectors_list(self):
self._detectors_list, _ = get_available_detectors()
self._detectors_list.insert(0, "Custom")
self.widget.detectors_cb.blockSignals(True)
self.widget.detectors_cb.clear()
self.widget.detectors_cb.addItems(self._detectors_list)
self.widget.detectors_cb.insertSeparator(1)
self.widget.detectors_cb.insertSeparator(1)
self.widget.detectors_cb.blockSignals(False)
def load_detector(self, ind):
"""
Loads the selected Detector from the Detector combobox into the calibration model. This blackout disable the
controls for pixel widths, unless "custom" (the first element) is selected.
"""
if ind != 0:
self.model.calibration_model.load_detector(
self.widget.detectors_cb.currentText()
)
emit_img_changed = (
self.model.calibration_model.detector.shape
== self.model.img_model.img_data.shape
)
# makes no sense to have transformations when loading a detector, however only emitting that the img changed
# if detector and image have same size, otherwise the user should have the possibility to load an image
# without error
self.model.img_model.reset_transformations(emit_img_changed)
else:
self.model.calibration_model.reset_detector()
self.update_detector_parameters_in_view()
def load_detector_from_file(self):
filename = open_file_dialog(
self.widget,
caption="Load Nexus Detector",
directory=self.model.working_directories["image"],
filter="*.h5",
)
if filename != "":
self.model.calibration_model.load_detector_from_file(filename)
self.update_detector_parameters_in_view()
def reset_detector_from_file(self):
self.model.calibration_model.reset_detector()
self.model.img_model.reset_transformations()
self.update_detector_parameters_in_view()
def _update_pixel_size_in_gui(self):
self.widget.set_pixel_size(
self.model.calibration_model.orig_pixel2,
self.model.calibration_model.orig_pixel1,
)
def _update_spline_in_gui(self):
if self.model.calibration_model.detector.splineFile is not None:
self.widget.spline_filename_txt.setText(
os.path.basename(self.model.calibration_model.detector.splineFile)
)
elif not self.model.calibration_model.detector.uniform_pixel:
self.widget.spline_filename_txt.setText("from Detector")
else:
self.widget.spline_filename_txt.setText("None")
def load_calibrants_list(self):
"""
Loads all calibrants from the ExampleData/calibrants directory into the calibrants combobox. And loads number 7.
"""
self._calibrants_file_list = []
self._calibrants_file_names_list = []
for file in os.listdir(calibrants_path):
if file.endswith(".D"):
self._calibrants_file_list.append(file)
self._calibrants_file_names_list.append(file.split(".")[:-1][0])
self._calibrants_file_list.sort()
self._calibrants_file_names_list.sort()
self.widget.calibrant_cb.blockSignals(True)
self.widget.calibrant_cb.clear()
self.widget.calibrant_cb.addItems(self._calibrants_file_names_list)
self.widget.calibrant_cb.blockSignals(False)
self.widget.calibrant_cb.setCurrentIndex(
self._calibrants_file_names_list.index("LaB6")
) # to LaB6
self.load_calibrant()
def load_calibrant(self, wavelength_from="start_values"):
"""
Loads the selected calibrant in the calibrant combobox into the calibration data.
:param wavelength_from: determines which wavelength to use possible values: "start_values", "pyFAI"
"""
current_index = self.widget.calibrant_cb.currentIndex()
filename = os.path.join(
self.model.calibration_model._calibrants_working_dir,
self._calibrants_file_list[current_index],
)
self.model.calibration_model.set_calibrant(filename)
if wavelength_from == "start_values":
start_values = self.widget.get_start_values()
wavelength = start_values["wavelength"]
elif wavelength_from == "pyFAI":
pyFAI_parameter, _ = (
self.model.calibration_model.get_calibration_parameter()
)
if pyFAI_parameter["wavelength"] != 0:
wavelength = pyFAI_parameter["wavelength"]
else:
start_values = self.widget.get_start_values()
wavelength = start_values["wavelength"]
else:
start_values = self.widget.get_start_values()
wavelength = start_values["wavelength"]
self.model.calibration_model.calibrant.setWavelength_change2th(wavelength)
try:
integration_unit = self.model.current_configuration.integration_unit
except:
integration_unit = "2th_deg"
calibrant_line_positions = self.convert_x_value(
np.array(self.model.calibration_model.calibrant.get_2th()) / np.pi * 180,
"2th_deg",
integration_unit,
wavelength,
)
# filter them to only show the ones visible with the current pattern
if len(self.model.pattern.x) > 0:
pattern_min = np.min(self.model.pattern.x)
pattern_max = np.max(self.model.pattern.x)
calibrant_line_positions = calibrant_line_positions[
calibrant_line_positions > pattern_min
]
calibrant_line_positions = calibrant_line_positions[
calibrant_line_positions < pattern_max
]
self.widget.pattern_widget.plot_vertical_lines(
positions=calibrant_line_positions,
name=self._calibrants_file_names_list[current_index],
)
def set_calibrant(self, index):
"""
:param index:
index of a specific calibrant in the calibrant combobox
"""
self.widget.calibrant_cb.setCurrentIndex(index)
self.load_calibrant()
def plot_image(self, autoscale=True):
"""
Plots the current image loaded in img_data and autoscales the intensity.
"""
self.widget.img_widget.plot_image(self.model.img_data, autoscale)
self.widget.set_img_filename(self.model.img_model.filename)
def search_peaks(self, x, y):
"""
Searches peaks around a specific points (x,y) in the current image file. The algorithm for searching
(either automatic or single peaksearch) is set in the GUI.
:param x:
x-Position for the search.
:param y:
y-Position for the search
"""
x, y = (
y,
x,
) # indices for the img array are transposed compared to the mouse position
# convert pixel coord into pixel index
x, y = int(x), int(y)
# filter events outside the image
shape = self.model.img_model.img_data.shape
if not (0 <= x < shape[0]):
return
if not (0 <= y < shape[1]):
return
peak_ind = self.widget.peak_num_sb.value()
if self.widget.automatic_peak_search_rb.isChecked():
points = self.model.calibration_model.find_peaks_automatic(
x, y, peak_ind - 1
)
else:
search_size = int(self.widget.search_size_sb.value())
points = self.model.calibration_model.find_peak(
x, y, search_size, peak_ind - 1
)
if len(points):
self.plot_points(points)
if self.widget.automatic_peak_num_inc_cb.isChecked():
self.widget.peak_num_sb.setValue(peak_ind + 1)
def plot_points(self, points=None):
"""
Plots points into the image view.
:param points:
list of points, whereby a point is a [x,y] element. If it is none it will plot the points stored in the
calibration_data
"""
if points is None:
try:
points = self.model.calibration_model.get_point_array()
except IndexError:
points = []
if len(points):
self.widget.img_widget.add_scatter_data(
points[:, 0] + 0.5, points[:, 1] + 0.5
)
def clear_peaks(self):
"""
Deletes all points/peaks in the calibration_data and in the GUI.
"""
self.model.calibration_model.clear_peaks()
self.widget.img_widget.clear_scatter_plot()
self.widget.peak_num_sb.setValue(1)
def undo_peaks_btn_clicked(self):
"""
undoes clicked peaks
"""
num_points = self.model.calibration_model.remove_last_peak()
self.widget.img_widget.remove_last_scatter_points(num_points)
if self.widget.automatic_peak_num_inc_cb.isChecked():
self.widget.peak_num_sb.setValue(self.widget.peak_num_sb.value() - 1)
def load_spline_btn_click(self):
filename = open_file_dialog(
self.widget,
caption="Load Distortion Spline File",
directory=self.model.working_directories["image"],
filter="*.spline",
)
if filename != "":
self.model.calibration_model.load_distortion(filename)
self._update_spline_in_gui()
self.widget.spline_reset_btn.setEnabled(True)
def reset_spline_btn_click(self):
self.model.calibration_model.reset_distortion_correction()
self.widget.spline_filename_txt.setText("None")
self.widget.spline_reset_btn.setEnabled(False)
def wavelength_cb_changed(self, value):
"""
Sets the fit_wavelength parameter in the calibration data according to the GUI state.
"""
self.widget.f2_wavelength_cb.blockSignals(True)
self.widget.pf_wavelength_cb.blockSignals(True)
self.widget.sv_wavelength_cb.blockSignals(True)
self.widget.f2_wavelength_cb.setChecked(value)
self.widget.pf_wavelength_cb.setChecked(value)
self.widget.sv_wavelength_cb.setChecked(value)
self.widget.f2_wavelength_cb.blockSignals(False)
self.widget.pf_wavelength_cb.blockSignals(False)
self.widget.sv_wavelength_cb.blockSignals(False)
self.model.calibration_model.fit_wavelength = value
def distance_cb_changed(self, value):
"""
Sets the fit_distance parameter int he calibration model according to the GUI state
"""
self.widget.f2_distance_cb.blockSignals(True)
self.widget.pf_distance_cb.blockSignals(True)
self.widget.sv_distance_cb.blockSignals(True)
self.widget.f2_distance_cb.setChecked(value)
self.widget.pf_distance_cb.setChecked(value)
self.widget.sv_distance_cb.setChecked(value)
self.widget.f2_distance_cb.blockSignals(False)
self.widget.pf_distance_cb.blockSignals(False)
self.widget.sv_distance_cb.blockSignals(False)
self.model.calibration_model.fit_distance = value
def update_fixed_values(self):
self.model.calibration_model.set_fixed_values(self.widget.get_fixed_values())
def calibrate(self):
"""
Performs calibration based on the previously inputted/searched peaks and start values.
"""
if len(self.model.calibration_model.points) == 0:
QtWidgets.QMessageBox.critical(
self.widget,
"No peaks defined!",
"No peaks defined. Please define initial peaks first as a starting point.<br><br><a href='https://dioptas.readthedocs.io/en/stable/calibration.html'>Consult the manual for more information. </a>",
QtWidgets.QMessageBox.Ok,
)
return
self.load_calibrant() # load the right calibration file...
self.model.calibration_model.set_start_values(self.widget.get_start_values())
self.model.calibration_model.set_pixel_size(self.widget.get_pixel_size())
self.model.calibration_model.set_fixed_values(self.widget.get_fixed_values())
progress_dialog = self.create_progress_dialog(
"Calibrating.", "", 0, show_cancel_btn=False
)
self.model.calibration_model.calibrate()
progress_dialog.close()
if self.widget.options_automatic_refinement_cb.isChecked():
self.automatic_refinement()
else:
self.update_all()
self.update_calibration_parameter_in_view()
def refine(self):
self.model.calibration_model.set_fixed_values(self.widget.get_fixed_values())
if self.widget.options_automatic_refinement_cb.isChecked():
self.automatic_refinement()
else:
progress_dialog = self.create_progress_dialog(
"Refining.", "", 0, show_cancel_btn=False
)
self.model.calibration_model.refine()
progress_dialog.close()
self.update_all()
self.update_calibration_parameter_in_view()
def create_progress_dialog(
self, text_str, abort_str, end_value, show_cancel_btn=True
):
"""Creates a Progress Bar Dialog.
:param text_str: Main message string
:param abort_str: Text on the abort button
:param end_value: Number of steps for which the progressbar is being used
:param show_cancel_btn: Whether the cancel button should be shown.
:return: ProgressDialog reference which is already shown in the interface
:rtype: QtWidgets.ProgressDialog
"""
progress_dialog = QtWidgets.QProgressDialog(
text_str, abort_str, 0, end_value, self.widget
)
progress_dialog.move(
int(
self.widget.tab_widget.x()
+ self.widget.tab_widget.size().width() / 2.0
- progress_dialog.size().width() / 2.0
),
int(
self.widget.tab_widget.y()
+ self.widget.tab_widget.size().height() / 2.0
- progress_dialog.size().height() / 2.0
),
)
progress_dialog.setWindowTitle(" ")
progress_dialog.setWindowModality(QtCore.Qt.WindowModal)
progress_dialog.setWindowFlags(QtCore.Qt.FramelessWindowHint)
if not show_cancel_btn:
progress_dialog.setCancelButton(None)
progress_dialog.show()
QtWidgets.QApplication.processEvents()
return progress_dialog
def automatic_refinement(self):
"""
Refines the current calibration parameters by searching peaks in the approximate positions and subsequent
refinement. Parameters for this search are set in the GUI.
"""
# Basic Algorithm:
# search peaks on first and second ring
# calibrate based on those two rings
# repeat until ring_ind = max_ind:
# search next ring
# calibrate based on all previous found points
num_rings = self.widget.options_num_rings_sb.value()
progress_dialog = self.create_progress_dialog(
"Refining Calibration.", "Abort", num_rings
)
self.clear_peaks()
self.load_calibrant(wavelength_from="pyFAI") # load right calibration file
# get options
algorithm = str(self.widget.options_peaksearch_algorithm_cb.currentText())
delta_tth = float(self.widget.options_delta_tth_txt.text())
intensity_min_factor = float(
self.widget.options_intensity_mean_factor_sb.value()
)
intensity_max = float(self.widget.options_intensity_limit_txt.text())
self.model.calibration_model.setup_peak_search_algorithm(algorithm)
if self.widget.use_mask_cb.isChecked():
mask = self.model.mask_model.get_img()
else:
mask = None
self.model.calibration_model.search_peaks_on_ring(
0, delta_tth, intensity_min_factor, intensity_max, mask
)
self.widget.peak_num_sb.setValue(2)
progress_dialog.setValue(1)
self.model.calibration_model.search_peaks_on_ring(
1, delta_tth, intensity_min_factor, intensity_max, mask
)
self.widget.peak_num_sb.setValue(3)
if len(self.model.calibration_model.points):
self.model.calibration_model.refine()
self.plot_points()
else:
print(
"Did not find any Points with the specified parameters for the first two rings!"
)
progress_dialog.setValue(2)
refinement_canceled = False
for i in range(num_rings - 2):
try:
points = self.model.calibration_model.search_peaks_on_ring(
i + 2, delta_tth, intensity_min_factor, intensity_max, mask
)
except NotEnoughSpacingsInCalibrant:
QtWidgets.QMessageBox.critical(
self.widget,
"Not enough d-spacings!.",
"The calibrant file does not contain enough d-spacings.",
QtWidgets.QMessageBox.Ok,
)
break
self.widget.peak_num_sb.setValue(i + 4)
if len(self.model.calibration_model.points):
self.plot_points(points)
QtWidgets.QApplication.processEvents()
QtWidgets.QApplication.processEvents()
self.model.calibration_model.refine()
else:
print("Did not find enough points with the specified parameters!")
progress_dialog.setLabelText(
"Refining Calibration. \n" "Finding peaks on Ring {0}.".format(i + 3)
)
progress_dialog.setValue(i + 3)
if progress_dialog.wasCanceled():
refinement_canceled = True
break
progress_dialog.close()
del progress_dialog
QtWidgets.QApplication.processEvents()
if len(self.model.calibration_model.points) == 0:
QtWidgets.QMessageBox.critical(
self.widget,
"No peaks found!",
"Automatic peak search did not find any peaks.\nThis might be due to the peak search settings or the wrong calibrant being selected.",
QtWidgets.QMessageBox.Ok,
)
if not refinement_canceled and len(self.model.calibration_model.points) > 0:
self.update_all()
def load_calibration(self):
"""
Loads a '*.poni' file and updates the calibration data class
"""
filename = open_file_dialog(
self.widget,
caption="Load calibration...",
directory=self.model.working_directories["calibration"],
filter="*.poni",
)
if filename != "":
self.model.working_directories["calibration"] = os.path.dirname(filename)
self.model.calibration_model.load(filename)
self.update_all(integrate=self.model.img_model.filename != "")
def plot_mask(self):
"""
Plots the mask
"""
state = self.widget.use_mask_cb.isChecked()
if state:
self.widget.img_widget.activate_mask()
self.widget.img_widget.plot_mask(self.model.mask_model.get_img())
else:
self.widget.img_widget.deactivate_mask()
def mask_transparent_status_changed(self, state):
"""
:param state: Boolean value whether the mask is being transparent
:type state: bool
"""
if state:
self.widget.img_widget.set_mask_color([255, 0, 0, 100])
else:
self.widget.img_widget.set_mask_color([255, 0, 0, 255])
def update_all(self, integrate=True):
"""
Performs 1d and 2d integration based on the current calibration parameter set. Updates the GUI interface
accordingly with the new diffraction pattern and cake image.
"""
if integrate:
progress_dialog = self.create_progress_dialog(
"Integrating to cake.", "", 0, show_cancel_btn=False
)
QtWidgets.QApplication.processEvents()
self.model.current_configuration.integrate_image_2d()
progress_dialog.setLabelText("Integrating to pattern.")
QtWidgets.QApplication.processEvents()
QtWidgets.QApplication.processEvents()
self.model.current_configuration.integrate_image_1d()
progress_dialog.close()
self.widget.cake_widget.plot_image(self.model.cake_data, False)
self.widget.cake_widget.auto_level()
self.widget.pattern_widget.plot_data(*self.model.pattern.data)
self.widget.pattern_widget.plot_vertical_lines(
self.convert_x_value(
np.array(self.model.calibration_model.calibrant.get_2th())
/ np.pi
* 180,
"2th_deg",
self.model.current_configuration.integration_unit,
None,
)
)
if self.model.current_configuration.integration_unit == "2th_deg":
self.widget.pattern_widget.pattern_plot.setLabel("bottom", "", "°")
elif self.model.current_configuration.integration_unit == "q_A^-1":
self.widget.pattern_widget.pattern_plot.setLabel(
"bottom", "Q", "A<sup>-1</sup>"
)
elif self.model.current_configuration.integration_unit == "d_A":
self.widget.pattern_widget.pattern_plot.setLabel("bottom", "d", "A")
self.widget.pattern_widget.view_box.autoRange()
if self.widget.tab_widget.currentIndex() == 0:
self.widget.tab_widget.setCurrentIndex(1)
if (
self.widget.ToolBox.currentIndex() != 2
or self.widget.ToolBox.currentIndex() != 3
):
self.widget.ToolBox.setCurrentIndex(2)
self.update_calibration_parameter_in_view()
self.load_calibrant("pyFAI")
def update_calibration_parameter_in_view(self):
"""
Reads the calibration parameter from the calibration_data object and displays them in the GUI.
:return:
"""
pyFAI_parameter, fit2d_parameter = (
self.model.calibration_model.get_calibration_parameter()
)
self.widget.set_calibration_parameters(pyFAI_parameter, fit2d_parameter)
self._update_spline_in_gui()
def update_detector_parameters_in_view(self):
detector_mode = self.model.calibration_model.detector_mode
self.widget.enable_pixel_size_txt(detector_mode == DetectorModes.CUSTOM)
self.widget.detectors_cb.setVisible(
detector_mode in (DetectorModes.CUSTOM, DetectorModes.PREDEFINED)
)
self.widget.detector_name_lbl.setVisible(detector_mode == DetectorModes.NEXUS)
self.widget.detector_reset_btn.setEnabled(detector_mode == DetectorModes.NEXUS)
if detector_mode == DetectorModes.CUSTOM:
self.widget.detectors_cb.blockSignals(True)
self.widget.detectors_cb.setCurrentText("Custom")
self.widget.detectors_cb.blockSignals(False)
if detector_mode == DetectorModes.PREDEFINED:
self.widget.detectors_cb.blockSignals(True)
self.widget.detectors_cb.setCurrentText(
self.model.calibration_model.detector.name
)
self.widget.detectors_cb.blockSignals(False)
if detector_mode == DetectorModes.NEXUS:
self.widget.detector_name_lbl.setText(
os.path.basename(self.model.calibration_model.detector.filename)
)
self._update_pixel_size_in_gui()
self._update_spline_in_gui()
def show_detector_reset_message_box(self):
QtWidgets.QMessageBox.critical(
self.widget,
"Shape mismatch.",
"Image and detector definition do not have the same shape!\n"
+ "The Detector has been reset.",
QtWidgets.QMessageBox.Ok,
)
def save_calibration(self):
"""
Saves the current calibration in a file.
:return:
"""
filename = save_file_dialog(
self.widget,
"Save calibration...",
self.model.working_directories["calibration"],
"*.poni",
)
if filename != "":
self.model.working_directories["calibration"] = os.path.dirname(filename)
if not filename.rsplit(".", 1)[-1] == "poni":
filename = filename + ".poni"
self.model.calibration_model.save(filename)
def update_img_mouse_position_lbl(self, x, y):
"""
Displays the values of x, y (usually mouse -position) and their image intensity in the GUI.
"""
# x, y = pos
try:
if x > 0 and y > 0:
str = "x: %.1f y: %.1f I: %.0f" % (
x,
y,
self.widget.img_widget.img_data.T[
int(np.round(x)), int(np.round(y))
],
)
else:
str = "x: %.1f y: %.1f" % (x, y)
except (IndexError, AttributeError):
str = "x: %.1f y: %.1f" % (x, y)
self.widget.pos_lbl.setText(str)
def update_cake_mouse_position_lbl(self, x, y):
"""
Displays the values of x, y (usually mouse -position) and their cake intensity in the GUI.
"""
# x, y = pos
try:
if x > 0 and y > 0:
str = "x: %.1f y: %.1f I: %.0f" % (
x,
y,
self.widget.cake_widget.img_data.T[
int(np.round(x)), int(np.round(y))
],
)
else:
str = "x: %.1f y: %.1f" % (x, y)
except (IndexError, AttributeError):
str = "x: %.1f y: %.1f" % (x, y)
self.widget.pos_lbl.setText(str)
def update_pattern_mouse_position_lbl(self, x, y):
"""
Displays the values of x, y (pattern mouse-position) in the GUI.
"""
# x, y = pos
str = "x: %.1f y: %.1f" % (x, y)
self.widget.pos_lbl.setText(str)
def convert_x_value(self, value, previous_unit, new_unit, wavelength):
if wavelength is None:
wavelength = self.model.calibration_model.wavelength
if previous_unit == "2th_deg":
tth = value
elif previous_unit == "q_A^-1":
tth = np.arcsin(value * 1e10 * wavelength / (4 * np.pi)) * 360 / np.pi
elif previous_unit == "d_A":
tth = 2 * np.arcsin(wavelength / (2 * value * 1e-10)) * 180 / np.pi
else:
tth = 0
if new_unit == "2th_deg":
res = tth
elif new_unit == "q_A^-1":
res = 4 * np.pi * np.sin(tth / 360 * np.pi) / wavelength / 1e10
elif new_unit == "d_A":
res = wavelength / (2 * np.sin(tth / 360 * np.pi)) * 1e10
else:
res = 0
return res