mirror of
https://github.com/nqrduck/nqrduck-measurement.git
synced 2024-12-22 07:40:29 +00:00
commit
b5d5559cfb
7 changed files with 223 additions and 27 deletions
|
@ -1,5 +1,9 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## Version 0.0.6 (27-05-2024)
|
||||||
|
|
||||||
|
- Added fitting functions to the measurement module (`dca1c6816f0697ca3c6827fd07a0236a3189b922`).
|
||||||
|
|
||||||
## Version 0.0.5 (20-05-2024)
|
## Version 0.0.5 (20-05-2024)
|
||||||
|
|
||||||
- Fixed measurement dialog not showing in wayland (`f5705e4efcbaf1aa0efd558b1ec1dacf42a53944`)
|
- Fixed measurement dialog not showing in wayland (`f5705e4efcbaf1aa0efd558b1ec1dacf42a53944`)
|
||||||
|
|
12
README.md
12
README.md
|
@ -5,12 +5,15 @@ A module for the [nqrduck](https://github.com/nqrduck/nqrduck) project. This mod
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
|
|
||||||
Dependencies are handled via the pyproject.toml file.
|
Dependencies are handled via the pyproject.toml file.
|
||||||
|
|
||||||
### Setup
|
### Setup
|
||||||
|
|
||||||
To install the module you need the NQRduck core. You can find the installation instructions for the NQRduck core [here](https://github.com/nqrduck/nqrduck).
|
To install the module you need the NQRduck core. You can find the installation instructions for the NQRduck core [here](https://github.com/nqrduck/nqrduck).
|
||||||
|
|
||||||
Ideally you should install the module in a virtual environment. You can create a virtual environment by running the following command in the terminal:
|
Ideally you should install the module in a virtual environment. You can create a virtual environment by running the following command in the terminal:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python -m venv nqrduck
|
python -m venv nqrduck
|
||||||
# Activate the virtual environment
|
# Activate the virtual environment
|
||||||
|
@ -18,22 +21,25 @@ python -m venv nqrduck
|
||||||
```
|
```
|
||||||
|
|
||||||
You can install this module and the dependencies by running the following command in the terminal while the virtual environment is activated and you are in the root directory of this module:
|
You can install this module and the dependencies by running the following command in the terminal while the virtual environment is activated and you are in the root directory of this module:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install .
|
pip install .
|
||||||
```
|
```
|
||||||
|
|
||||||
Alternatively, you can install the module and the dependencies by running the following command in the terminal while the virtual environment is activated:
|
Alternatively, you can install the module and the dependencies by running the following command in the terminal while the virtual environment is activated:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install nqrduck-measurement
|
pip install nqrduck-measurement
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
The module is used with the [Spectrometer](https://github.com/nqrduck/nqrduck-spectrometer) module. However you need to use an actual submodule of the spectrometer module like:
|
The module is used with the [Spectrometer](https://github.com/nqrduck/nqrduck-spectrometer) module. However you need to use an actual submodule of the spectrometer module like:
|
||||||
|
|
||||||
- [nqrduck-spectrometer-limenqr](https://github.com/nqrduck/nqrduck-spectrometer-limenqr) A module used for magnetic resonance experiments with the LimeSDR (USB or Mini 2.0).
|
- [nqrduck-spectrometer-limenqr](https://github.com/nqrduck/nqrduck-spectrometer-limenqr) A module used for magnetic resonance experiments with the LimeSDR (USB or Mini 2.0).
|
||||||
- [nqrduck-spectrometer-simulator](https://github.com/nqrduck/nqrduck-spectrometer-simulator) A module used for simulating magnetic resonance experiments.
|
- [nqrduck-spectrometer-simulator](https://github.com/nqrduck/nqrduck-spectrometer-simulator) A module used for simulating magnetic resonance experiments.
|
||||||
|
|
||||||
The pulse sequence and spectrometer settings can be adjusted using the 'Spectrometer' tab.
|
The pulse sequence and spectrometer settings can be adjusted using the 'Spectrometer' tab.
|
||||||
|
|
||||||
<img src="https://github.com/nqrduck/nqrduck-measurement/raw/0b28ae6b33230c6ca9eda85bd18de7cbcade27d1/docs/img/measurement_ui_labeled_v2.png" alt="drawing" width="800">
|
<img src="https://github.com/nqrduck/nqrduck-measurement/raw/0b28ae6b33230c6ca9eda85bd18de7cbcade27d1/docs/img/measurement_ui_labeled_v2.png" alt="drawing" width="800">
|
||||||
|
|
||||||
|
@ -42,8 +48,12 @@ The pulse sequence and spectrometer settings can be adjusted using the 'Spectrom
|
||||||
- c.) The 'Measurement Plot'. Here the measured data is displayed. One can switch time and frequency domain plots.
|
- c.) The 'Measurement Plot'. Here the measured data is displayed. One can switch time and frequency domain plots.
|
||||||
- d.) The import and export buttons for the measurement data.
|
- d.) The import and export buttons for the measurement data.
|
||||||
|
|
||||||
|
You can then remove the folder of the virtual environment.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details
|
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
If you're interested in contributing to the project, start by checking out our [nqrduck-module template](https://github.com/nqrduck/nqrduck-module). To contribute to existing modules, please first open an issue in the respective module repository to discuss your ideas or report bugs.
|
If you're interested in contributing to the project, start by checking out our [nqrduck-module template](https://github.com/nqrduck/nqrduck-module). To contribute to existing modules, please first open an issue in the respective module repository to discuss your ideas or report bugs.
|
||||||
|
|
|
@ -7,7 +7,7 @@ allow-direct-references = true
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "nqrduck-measurement"
|
name = "nqrduck-measurement"
|
||||||
version = "0.0.5"
|
version = "0.0.6"
|
||||||
authors = [
|
authors = [
|
||||||
{ name="jupfi", email="support@nqrduck.cool" },
|
{ name="jupfi", email="support@nqrduck.cool" },
|
||||||
]
|
]
|
||||||
|
|
|
@ -4,7 +4,7 @@ import logging
|
||||||
import json
|
import json
|
||||||
from PyQt6.QtCore import pyqtSlot, pyqtSignal
|
from PyQt6.QtCore import pyqtSlot, pyqtSignal
|
||||||
from PyQt6.QtWidgets import QApplication
|
from PyQt6.QtWidgets import QApplication
|
||||||
from .signalprocessing_options import Apodization
|
from .signalprocessing_options import Apodization, Fitting
|
||||||
from nqrduck.module.module_controller import ModuleController
|
from nqrduck.module.module_controller import ModuleController
|
||||||
from nqrduck_spectrometer.measurement import Measurement
|
from nqrduck_spectrometer.measurement import Measurement
|
||||||
|
|
||||||
|
@ -231,6 +231,36 @@ class MeasurementController(ModuleController):
|
||||||
|
|
||||||
self.module.model.displayed_measurement = apodized_measurement
|
self.module.model.displayed_measurement = apodized_measurement
|
||||||
self.module.model.add_measurement(apodized_measurement)
|
self.module.model.add_measurement(apodized_measurement)
|
||||||
|
|
||||||
|
def show_fitting_dialog(self) -> None:
|
||||||
|
"""Show fitting dialog."""
|
||||||
|
logger.debug("Showing fitting dialog.")
|
||||||
|
# First we check if there is a measurement.
|
||||||
|
if not self.module.model.displayed_measurement:
|
||||||
|
logger.debug("No measurement to fit.")
|
||||||
|
self.module.nqrduck_signal.emit(
|
||||||
|
"notification", ["Error", "No measurement to fit."]
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
measurement = self.module.model.displayed_measurement
|
||||||
|
|
||||||
|
dialog = Fitting(measurement, parent=self.module.view)
|
||||||
|
result = dialog.exec()
|
||||||
|
|
||||||
|
logger.debug("Dialog result: %s", result)
|
||||||
|
if not result:
|
||||||
|
return
|
||||||
|
|
||||||
|
fit = dialog.get_fit()[1]
|
||||||
|
|
||||||
|
logger.debug("Fitting function: %s", fit)
|
||||||
|
|
||||||
|
measurement.add_fit(fit)
|
||||||
|
|
||||||
|
self.module.view.update_displayed_measurement()
|
||||||
|
|
||||||
|
dialog.deleteLater()
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def change_displayed_measurement(self, measurement=None) -> None:
|
def change_displayed_measurement(self, measurement=None) -> None:
|
||||||
|
|
|
@ -45,7 +45,7 @@ class MeasurementModel(ModuleModel):
|
||||||
|
|
||||||
FILE_EXTENSION = "meas"
|
FILE_EXTENSION = "meas"
|
||||||
# This constants are used to determine which view is currently displayed.
|
# This constants are used to determine which view is currently displayed.
|
||||||
FFT_VIEW = "fft"
|
FFT_VIEW = "frequency"
|
||||||
TIME_VIEW = "time"
|
TIME_VIEW = "time"
|
||||||
|
|
||||||
displayed_measurement_changed = pyqtSignal(Measurement)
|
displayed_measurement_changed = pyqtSignal(Measurement)
|
||||||
|
|
|
@ -2,9 +2,13 @@
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import sympy
|
import sympy
|
||||||
from nqrduck_spectrometer.measurement import Measurement
|
from nqrduck_spectrometer.measurement import Measurement, Fit, T2StarFit, LorentzianFit
|
||||||
from nqrduck.helpers.functions import Function, GaussianFunction, CustomFunction
|
from nqrduck.helpers.functions import Function, GaussianFunction, CustomFunction
|
||||||
from nqrduck.helpers.formbuilder import DuckFormBuilder, DuckFormFunctionSelectionField
|
from nqrduck.helpers.formbuilder import (
|
||||||
|
DuckFormBuilder,
|
||||||
|
DuckFormFunctionSelectionField,
|
||||||
|
DuckFormDropdownField,
|
||||||
|
)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -23,7 +27,6 @@ class FIDFunction(Function):
|
||||||
|
|
||||||
self.add_parameter(Function.Parameter("T2star (microseconds)", "T2star", 10))
|
self.add_parameter(Function.Parameter("T2star (microseconds)", "T2star", 10))
|
||||||
|
|
||||||
|
|
||||||
class Apodization(DuckFormBuilder):
|
class Apodization(DuckFormBuilder):
|
||||||
"""Apodization parameter.
|
"""Apodization parameter.
|
||||||
|
|
||||||
|
@ -51,6 +54,8 @@ class Apodization(DuckFormBuilder):
|
||||||
duration=self.duration,
|
duration=self.duration,
|
||||||
parent=parent,
|
parent=parent,
|
||||||
default_function=0,
|
default_function=0,
|
||||||
|
view_mode="time",
|
||||||
|
mode_selection=0,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.add_field(function_selection_field)
|
self.add_field(function_selection_field)
|
||||||
|
@ -62,3 +67,38 @@ class Apodization(DuckFormBuilder):
|
||||||
Function: The selected function.
|
Function: The selected function.
|
||||||
"""
|
"""
|
||||||
return self.get_values()[0]
|
return self.get_values()[0]
|
||||||
|
|
||||||
|
|
||||||
|
class Fitting(DuckFormBuilder):
|
||||||
|
"""Fitting parameter.
|
||||||
|
|
||||||
|
This parameter is used to apply fitting functions to the signal.
|
||||||
|
The fitting functions are used to reduce the noise in the signal.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, measurement: Measurement, parent=None) -> None:
|
||||||
|
"""Fitting parameter."""
|
||||||
|
super().__init__("Fitting", parent=parent)
|
||||||
|
|
||||||
|
self.measurement = measurement
|
||||||
|
|
||||||
|
fits = {}
|
||||||
|
fits["T2*"] = T2StarFit(self.measurement)
|
||||||
|
fits["Lorentzian"] = LorentzianFit(self.measurement)
|
||||||
|
|
||||||
|
selection_field = DuckFormDropdownField(
|
||||||
|
text=None,
|
||||||
|
tooltip=None,
|
||||||
|
options=fits,
|
||||||
|
default_option=0,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.add_field(selection_field)
|
||||||
|
|
||||||
|
def get_fit(self) -> Fit:
|
||||||
|
"""Get the selected fit.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Fit: The selected fit.
|
||||||
|
"""
|
||||||
|
return self.get_values()[0]
|
||||||
|
|
|
@ -89,6 +89,10 @@ class MeasurementView(ModuleView):
|
||||||
self.module.controller.show_apodization_dialog
|
self.module.controller.show_apodization_dialog
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self._ui_form.fittingButton.clicked.connect(
|
||||||
|
self.module.controller.show_fitting_dialog
|
||||||
|
)
|
||||||
|
|
||||||
# Add logos
|
# Add logos
|
||||||
self._ui_form.buttonStart.setIcon(Logos.Play_16x16())
|
self._ui_form.buttonStart.setIcon(Logos.Play_16x16())
|
||||||
self._ui_form.buttonStart.setIconSize(self._ui_form.buttonStart.size())
|
self._ui_form.buttonStart.setIconSize(self._ui_form.buttonStart.size())
|
||||||
|
@ -120,7 +124,7 @@ class MeasurementView(ModuleView):
|
||||||
self._ui_form.averagesEdit.set_min_value(1)
|
self._ui_form.averagesEdit.set_min_value(1)
|
||||||
self._ui_form.averagesEdit.set_max_value(1e6)
|
self._ui_form.averagesEdit.set_max_value(1e6)
|
||||||
|
|
||||||
# Connect selectionBox signal fors switching the displayed measurement
|
# Connect selectionBox signal for switching the displayed measurement
|
||||||
self._ui_form.selectionBox.valueChanged.connect(
|
self._ui_form.selectionBox.valueChanged.connect(
|
||||||
self.module.controller.change_displayed_measurement
|
self.module.controller.change_displayed_measurement
|
||||||
)
|
)
|
||||||
|
@ -207,6 +211,9 @@ class MeasurementView(ModuleView):
|
||||||
x, np.abs(y), label="Magnitude", color="blue"
|
x, np.abs(y), label="Magnitude", color="blue"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Plot fits
|
||||||
|
self.plot_fits()
|
||||||
|
|
||||||
# Add legend
|
# Add legend
|
||||||
self._ui_form.plotter.canvas.ax.legend()
|
self._ui_form.plotter.canvas.ax.legend()
|
||||||
|
|
||||||
|
@ -230,10 +237,50 @@ class MeasurementView(ModuleView):
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
|
|
||||||
except AttributeError:
|
except AttributeError as e:
|
||||||
logger.debug("No measurement data to display.")
|
logger.debug(f"No measurement data to display: {e}")
|
||||||
|
|
||||||
self._ui_form.plotter.canvas.draw()
|
self._ui_form.plotter.canvas.draw()
|
||||||
|
|
||||||
|
def plot_fits(self):
|
||||||
|
"""Plots the according fits to the displayed measurement if there are any and if the view mode is correct."""
|
||||||
|
measurement = self.module.model.displayed_measurement
|
||||||
|
|
||||||
|
if not measurement.fits:
|
||||||
|
logger.debug("No fits to plot.")
|
||||||
|
return
|
||||||
|
|
||||||
|
for fit in measurement.fits:
|
||||||
|
if fit.domain == self.module.model.view_mode:
|
||||||
|
logger.debug(f"Plotting {fit.name} fit in domain {fit.domain}.")
|
||||||
|
x = fit.x
|
||||||
|
y = fit.y
|
||||||
|
# Shift the x values if the view mode is FFT
|
||||||
|
if fit.domain == self.module.model.FFT_VIEW:
|
||||||
|
x = x + float(
|
||||||
|
measurement.target_frequency - measurement.IF_frequency
|
||||||
|
) * 1e-6
|
||||||
|
|
||||||
|
self._ui_form.plotter.canvas.ax.plot(
|
||||||
|
x, y, label=f"{fit.name} Fit", linestyle="--"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add the parameters to the plot
|
||||||
|
offset = 0
|
||||||
|
for name, value in fit.parameters.items():
|
||||||
|
if name == "covariance":
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Only two digits after the comma
|
||||||
|
value = round(value, 2)
|
||||||
|
|
||||||
|
self._ui_form.plotter.canvas.ax.text(
|
||||||
|
max(x) / 90,
|
||||||
|
max(y) / 2 + offset,
|
||||||
|
f"{name}: {value}",
|
||||||
|
)
|
||||||
|
offset += max(y) / 10
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def on_measurement_start_button_clicked(self) -> None:
|
def on_measurement_start_button_clicked(self) -> None:
|
||||||
"""Slot for when the measurement start button is clicked."""
|
"""Slot for when the measurement start button is clicked."""
|
||||||
|
@ -258,7 +305,7 @@ class MeasurementView(ModuleView):
|
||||||
logger.debug("Measurement save button clicked.")
|
logger.debug("Measurement save button clicked.")
|
||||||
|
|
||||||
file_manager = self.FileManager(
|
file_manager = self.FileManager(
|
||||||
self.module.model.FILE_EXTENSION, parent=self.widget
|
self.module.model.FILE_EXTENSION, parent=self
|
||||||
)
|
)
|
||||||
file_name = file_manager.saveFileDialog()
|
file_name = file_manager.saveFileDialog()
|
||||||
if file_name:
|
if file_name:
|
||||||
|
@ -270,7 +317,7 @@ class MeasurementView(ModuleView):
|
||||||
logger.debug("Measurement load button clicked.")
|
logger.debug("Measurement load button clicked.")
|
||||||
|
|
||||||
file_manager = self.FileManager(
|
file_manager = self.FileManager(
|
||||||
self.module.model.FILE_EXTENSION, parent=self.widget
|
self.module.model.FILE_EXTENSION, parent=self
|
||||||
)
|
)
|
||||||
file_name = file_manager.loadFileDialog()
|
file_name = file_manager.loadFileDialog()
|
||||||
if file_name:
|
if file_name:
|
||||||
|
@ -387,7 +434,9 @@ class MeasurementView(ModuleView):
|
||||||
self.setModal(True)
|
self.setModal(True)
|
||||||
self.setWindowFlag(Qt.WindowType.FramelessWindowHint)
|
self.setWindowFlag(Qt.WindowType.FramelessWindowHint)
|
||||||
self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
|
self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
|
||||||
self.setWindowFlag(Qt.WindowType.WindowStaysOnTopHint) # Ensure the window stays on top
|
self.setWindowFlag(
|
||||||
|
Qt.WindowType.WindowStaysOnTopHint
|
||||||
|
) # Ensure the window stays on top
|
||||||
|
|
||||||
self.message_label = QLabel("Measuring...")
|
self.message_label = QLabel("Measuring...")
|
||||||
# Make label bold and text larger
|
# Make label bold and text larger
|
||||||
|
@ -398,7 +447,7 @@ class MeasurementView(ModuleView):
|
||||||
|
|
||||||
self.spinner_movie = DuckAnimations.DuckKick128x128()
|
self.spinner_movie = DuckAnimations.DuckKick128x128()
|
||||||
self.spinner_label = QLabel(self)
|
self.spinner_label = QLabel(self)
|
||||||
# Make spinner label
|
# Make spinner label
|
||||||
self.spinner_label.setMovie(self.spinner_movie)
|
self.spinner_label.setMovie(self.spinner_movie)
|
||||||
|
|
||||||
self.layout = QVBoxLayout(self)
|
self.layout = QVBoxLayout(self)
|
||||||
|
@ -425,8 +474,8 @@ class MeasurementView(ModuleView):
|
||||||
|
|
||||||
class MeasurementEdit(QDialog):
|
class MeasurementEdit(QDialog):
|
||||||
"""This dialog is displayed when the measurement edit button is clicked.
|
"""This dialog is displayed when the measurement edit button is clicked.
|
||||||
|
|
||||||
It allows the user to edit the measurement parameters (e.g. name, ...)
|
It allows the user to edit the measurement parameters (e.g. name, ...).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, measurement, parent=None) -> None:
|
def __init__(self, measurement, parent=None) -> None:
|
||||||
|
@ -435,43 +484,105 @@ class MeasurementView(ModuleView):
|
||||||
self.setParent(parent)
|
self.setParent(parent)
|
||||||
|
|
||||||
self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
|
self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
|
||||||
|
|
||||||
logger.debug("Edit measurement dialog started.")
|
logger.debug("Edit measurement dialog started.")
|
||||||
|
|
||||||
self.measurement = measurement
|
self.measurement = measurement
|
||||||
|
|
||||||
self.setWindowTitle("Edit Measurement")
|
self.setWindowTitle("Edit Measurement")
|
||||||
|
|
||||||
self.layout = QVBoxLayout(self)
|
self.layout = QVBoxLayout(self)
|
||||||
self.setLayout(self.layout)
|
self.setLayout(self.layout)
|
||||||
|
|
||||||
|
self.setup_name_section()
|
||||||
|
self.setup_fit_section()
|
||||||
|
self.setup_buttons()
|
||||||
|
|
||||||
|
# Resize the dialog
|
||||||
|
self.adjustSize()
|
||||||
|
|
||||||
|
def setup_name_section(self):
|
||||||
|
"""Sets up the name layout section."""
|
||||||
self.name_layout = QHBoxLayout()
|
self.name_layout = QHBoxLayout()
|
||||||
self.name_label = QLabel("Name:")
|
self.name_label = QLabel("Name:")
|
||||||
self.name_edit = QLineEdit(measurement.name)
|
|
||||||
|
self.name_edit = QLineEdit(self.measurement.name)
|
||||||
font_metrics = self.name_edit.fontMetrics()
|
font_metrics = self.name_edit.fontMetrics()
|
||||||
self.name_edit.setFixedWidth(
|
self.name_edit.setFixedWidth(font_metrics.horizontalAdvance(
|
||||||
font_metrics.horizontalAdvance(self.name_edit.text()) + 10
|
self.name_edit.text()) + 10)
|
||||||
)
|
|
||||||
self.name_edit.adjustSize()
|
self.name_edit.adjustSize()
|
||||||
|
|
||||||
self.name_layout.addWidget(self.name_label)
|
self.name_layout.addWidget(self.name_label)
|
||||||
self.name_layout.addWidget(self.name_edit)
|
self.name_layout.addWidget(self.name_edit)
|
||||||
|
self.layout.addLayout(self.name_layout)
|
||||||
|
|
||||||
|
def setup_fit_section(self):
|
||||||
|
"""Sets up the fit layout section."""
|
||||||
|
self.fit_layout = QVBoxLayout()
|
||||||
|
self.update_fit_info()
|
||||||
|
self.layout.addLayout(self.fit_layout)
|
||||||
|
|
||||||
|
def setup_buttons(self):
|
||||||
|
"""Sets up the OK and Cancel buttons."""
|
||||||
self.ok_button = QPushButton("OK")
|
self.ok_button = QPushButton("OK")
|
||||||
self.ok_button.clicked.connect(self.on_ok_button_clicked)
|
self.ok_button.clicked.connect(self.on_ok_button_clicked)
|
||||||
|
|
||||||
self.cancel_button = QPushButton("Cancel")
|
self.cancel_button = QPushButton("Cancel")
|
||||||
self.cancel_button.clicked.connect(self.close)
|
self.cancel_button.clicked.connect(self.close)
|
||||||
|
|
||||||
self.layout.addLayout(self.name_layout)
|
|
||||||
|
|
||||||
button_layout = QHBoxLayout()
|
button_layout = QHBoxLayout()
|
||||||
button_layout.addWidget(self.cancel_button)
|
button_layout.addWidget(self.cancel_button)
|
||||||
button_layout.addWidget(self.ok_button)
|
button_layout.addWidget(self.ok_button)
|
||||||
|
|
||||||
self.layout.addLayout(button_layout)
|
self.layout.addLayout(button_layout)
|
||||||
|
|
||||||
# Resize the dialog
|
def update_fit_info(self) -> None:
|
||||||
self.adjustSize()
|
"""Adds the associated fits to the dialog."""
|
||||||
|
# Clear the layout from previous fits
|
||||||
|
while self.fit_layout.count():
|
||||||
|
item = self.fit_layout.takeAt(0)
|
||||||
|
if item.widget():
|
||||||
|
item.widget().deleteLater()
|
||||||
|
elif item.layout():
|
||||||
|
self.clearLayout(item.layout())
|
||||||
|
|
||||||
|
if not self.measurement.fits:
|
||||||
|
logger.debug("No fits to display.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Adds the fit information
|
||||||
|
fit_label = QLabel("Fits:")
|
||||||
|
self.fit_layout.addWidget(fit_label)
|
||||||
|
|
||||||
|
for fit in self.measurement.fits:
|
||||||
|
specific_fit_layout = QHBoxLayout()
|
||||||
|
specific_fit_layout.addStretch()
|
||||||
|
logger.debug(f"Fit: {fit.name}")
|
||||||
|
|
||||||
|
fit_name_edit = QLineEdit(fit.name)
|
||||||
|
fit_name_edit.textChanged.connect(
|
||||||
|
lambda text, fit=fit: self.measurement.edit_fit_name(fit, text)
|
||||||
|
)
|
||||||
|
|
||||||
|
fit_delete_button = QPushButton()
|
||||||
|
fit_delete_button.setIcon(Logos.Garbage12x12())
|
||||||
|
fit_delete_button.clicked.connect(partial(self.on_delete_fit, fit))
|
||||||
|
|
||||||
|
specific_fit_layout.addWidget(fit_name_edit)
|
||||||
|
specific_fit_layout.addWidget(fit_delete_button)
|
||||||
|
self.fit_layout.addLayout(specific_fit_layout)
|
||||||
|
|
||||||
|
def clearLayout(self, layout):
|
||||||
|
"""Clears all items in the given layout."""
|
||||||
|
while layout.count():
|
||||||
|
item = layout.takeAt(0)
|
||||||
|
if item.widget():
|
||||||
|
item.widget().deleteLater()
|
||||||
|
elif item.layout():
|
||||||
|
self.clearLayout(item.layout())
|
||||||
|
|
||||||
|
def on_delete_fit(self, fit) -> None:
|
||||||
|
"""Slot for when the delete fit button is clicked."""
|
||||||
|
logger.debug(f"Delete fit {fit.name}.")
|
||||||
|
self.measurement.delete_fit(fit)
|
||||||
|
self.update_fit_info() # Update the dialog with the changes
|
||||||
|
|
||||||
def on_ok_button_clicked(self) -> None:
|
def on_ok_button_clicked(self) -> None:
|
||||||
"""Slot for when the OK button is clicked."""
|
"""Slot for when the OK button is clicked."""
|
||||||
|
@ -479,3 +590,4 @@ class MeasurementView(ModuleView):
|
||||||
self.measurement.name = self.name_edit.text()
|
self.measurement.name = self.name_edit.text()
|
||||||
self.accept()
|
self.accept()
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue