Formatting.

This commit is contained in:
jupfi 2024-03-30 16:27:23 +01:00
parent 1e6c4d3083
commit d3594230a0
5 changed files with 162 additions and 91 deletions

View file

@ -1,8 +1,9 @@
"""Controller for the measurement module."""
import logging
import json
import numpy as np
from decimal import Decimal
from decimal import Decimal
from PyQt6.QtCore import pyqtSlot, pyqtSignal
from PyQt6.QtGui import QValidator
from PyQt6.QtWidgets import QApplication
@ -17,16 +18,17 @@ logger = logging.getLogger(__name__)
class MeasurementController(ModuleController):
"""Controller for the measurement module.
This class is responsible for handling the signals from the view and the module and updating the model.
Args:
module (Module): The module instance.
Attributes:
set_frequency_failure (pyqtSignal): Signal emitted when setting the frequency fails.
set_averages_failure (pyqtSignal): Signal emitted when setting the averages fails.
"""
set_frequency_failure = pyqtSignal()
set_averages_failure = pyqtSignal()
@ -45,9 +47,14 @@ class MeasurementController(ModuleController):
ValueError: If value cannot be converted to float.
"""
# Use validator
if self.module.model.validator_measurement_frequency.validate(value, 0) == QValidator.State.Acceptable:
if (
self.module.model.validator_measurement_frequency.validate(value, 0)
== QValidator.State.Acceptable
):
self.module.model.measurement_frequency = float(value) * 1e6
self.module.nqrduck_signal.emit("set_frequency", str(self.module.model.measurement_frequency))
self.module.nqrduck_signal.emit(
"set_frequency", str(self.module.model.measurement_frequency)
)
self.toggle_start_button()
@ -59,11 +66,16 @@ class MeasurementController(ModuleController):
value (str): Number of averages.
"""
logger.debug("Setting averages to: " + value)
#self.module.nqrduck_signal.emit("set_averages", value)
if self.module.model.validator_averages.validate(value, 0) == QValidator.State.Acceptable:
# self.module.nqrduck_signal.emit("set_averages", value)
if (
self.module.model.validator_averages.validate(value, 0)
== QValidator.State.Acceptable
):
self.module.model.averages = int(value)
self.module.nqrduck_signal.emit("set_averages", str(self.module.model.averages))
self.module.nqrduck_signal.emit(
"set_averages", str(self.module.model.averages)
)
self.toggle_start_button()
@pyqtSlot()
@ -83,7 +95,9 @@ class MeasurementController(ModuleController):
self.module.view.measurement_dialog.show()
# Set the measurement parameters again in case the user switches spectrometer
self.module.nqrduck_signal.emit("set_frequency", str(self.module.model.measurement_frequency))
self.module.nqrduck_signal.emit(
"set_frequency", str(self.module.model.measurement_frequency)
)
self.module.nqrduck_signal.emit("set_averages", str(self.module.model.averages))
QApplication.processEvents()
@ -107,7 +121,7 @@ class MeasurementController(ModuleController):
def process_signals(self, key: str, value: object) -> None:
"""Process incoming signal from the nqrduck module.
Args:
key (str): The key of the signal.
value (object): The value of the signal.
@ -132,9 +146,7 @@ class MeasurementController(ModuleController):
):
logger.debug("Received measurement error.")
self.module.view.measurement_dialog.hide()
self.module.nqrduck_signal.emit(
"notification", ["Error", value]
)
self.module.nqrduck_signal.emit("notification", ["Error", value])
elif (
key == "failure_set_frequency"
@ -150,10 +162,11 @@ class MeasurementController(ModuleController):
logger.debug("Received set averages failure.")
self.set_averages_failure.emit()
elif key == "active_spectrometer_changed":
self.module.view._ui_form.spectrometerLabel.setText("Spectrometer: %s" % value)
self.module.view._ui_form.spectrometerLabel.setText(
"Spectrometer: %s" % value
)
def save_measurement(self, file_name : str) -> None:
def save_measurement(self, file_name: str) -> None:
"""Save measurement to file.
Args:
@ -163,13 +176,13 @@ class MeasurementController(ModuleController):
if not self.module.model.measurements:
logger.debug("No measurement to save.")
return
measurement = self.module.model.measurements[-1].to_json()
with open(file_name, "w") as f:
json.dump(measurement, f)
def load_measurement(self, file_name: str) -> None:
def load_measurement(self, file_name: str) -> None:
"""Load measurement from file.
Args:
@ -203,20 +216,26 @@ class MeasurementController(ModuleController):
"notification", ["Error", "No measurement to apodize."]
)
return
# We need to create a event which corresponds to the measurement.
# We need to create a event which corresponds to the measurement.
event_duration = self.module.model.displayed_measurement.tdx[-1] * 1e-6
event = PulseSequence.Event(name="Apodization", duration=str(event_duration))
parameter = Apodization()
parameter.start_x = 0
parameter.start_x = 0
parameter.end_x = event_duration
dialog = OptionsDialog(event, parameter, self.module.view)
result = dialog.exec()
if result:
for option, function in dialog.return_functions.items():
logger.debug("Setting option %s of parameter %s in event %s to %s", option, parameter, event, function())
logger.debug(
"Setting option %s of parameter %s in event %s to %s",
option,
parameter,
event,
function(),
)
option.set_value(function())
# Get the function from the Apodization function
@ -224,9 +243,12 @@ class MeasurementController(ModuleController):
logger.debug("Apodization function: %s", function)
# Get the y data weights from the function
resolution = (self.module.model.displayed_measurement.tdx[1] - self.module.model.displayed_measurement.tdx[0]) * 1e-6
resolution = (
self.module.model.displayed_measurement.tdx[1]
- self.module.model.displayed_measurement.tdx[0]
) * 1e-6
y_weight = function.get_pulse_amplitude(event.duration, Decimal(resolution))
#Append the last point to the end of the array
# Append the last point to the end of the array
y_weight = np.append(y_weight, y_weight[-1])
tdy_measurement = self.module.model.displayed_measurement.tdy * y_weight
@ -239,4 +261,4 @@ class MeasurementController(ModuleController):
)
self.module.model.displayed_measurement = measurement
self.module.model.add_measurement(measurement)
self.module.model.add_measurement(measurement)

View file

@ -1,7 +1,8 @@
"""Module initialization file for the nqrduck-measurement module."""
from nqrduck.module.module import Module
from .model import MeasurementModel
from .view import MeasurementView
from .controller import MeasurementController
Measurement = Module(MeasurementModel, MeasurementView, MeasurementController)
Measurement = Module(MeasurementModel, MeasurementView, MeasurementController)

View file

@ -1,4 +1,5 @@
"""Model for the measurement module."""
import logging
from PyQt6.QtCore import pyqtSignal
from nqrduck_spectrometer.measurement import Measurement
@ -7,29 +8,30 @@ from nqrduck.helpers.validators import DuckFloatValidator, DuckIntValidator
logger = logging.getLogger(__name__)
class MeasurementModel(ModuleModel):
"""Model for the measurement module.
This class is responsible for storing the data of the measurement module.
Attributes:
FILE_EXTENSION (str): The file extension of the measurement files.
FFT_VIEW (str): The view mode for the FFT view.
TIME_VIEW (str): The view mode for the time view.
displayed_measurement_changed (pyqtSignal): Signal emitted when the displayed measurement changes.
measurements_changed (pyqtSignal): Signal emitted when the list of measurements changes.
view_mode_changed (pyqtSignal): Signal emitted when the view mode changes.
measurement_frequency_changed (pyqtSignal): Signal emitted when the measurement frequency changes.
averages_changed (pyqtSignal): Signal emitted when the number of averages changes.
view_mode (str): The view mode of the measurement view.
measurements (list): List of measurements.
displayed_measurement (Measurement): The displayed measurement data.
measurement_frequency (float): The measurement frequency.
averages (int): The number of averages.
validator_measurement_frequency (DuckFloatValidator): Validator for the measurement frequency.
validator_averages (DuckIntValidator): Validator for the number of averages.
"""
@ -53,10 +55,12 @@ class MeasurementModel(ModuleModel):
self.measurements = []
self._displayed_measurement = None
self.validator_measurement_frequency = DuckFloatValidator(self, min_value=20.0, max_value=1000.0)
self.validator_measurement_frequency = DuckFloatValidator(
self, min_value=20.0, max_value=1000.0
)
self.validator_averages = DuckIntValidator(self, min_value=1, max_value=1e6)
self.measurement_frequency = 100.0 # MHz
self.measurement_frequency = 100.0 # MHz
self.averages = 1
@property
@ -66,9 +70,9 @@ class MeasurementModel(ModuleModel):
Can be either "time" or "fft".
"""
return self._view_mode
@view_mode.setter
def view_mode(self, value : str):
def view_mode(self, value: str):
self._view_mode = value
self.view_mode_changed.emit(value)
@ -76,13 +80,13 @@ class MeasurementModel(ModuleModel):
def measurements(self):
"""List of measurements."""
return self._measurements
@measurements.setter
def measurements(self, value : list[Measurement]):
def measurements(self, value: list[Measurement]):
self._measurements = value
self.measurements_changed.emit(value)
def add_measurement(self, measurement : Measurement):
def add_measurement(self, measurement: Measurement):
"""Add a measurement to the list of measurements."""
self.measurements.append(measurement)
self.measurements_changed.emit(self.measurements)
@ -95,9 +99,9 @@ class MeasurementModel(ModuleModel):
It can be data in time domain or frequency domain.
"""
return self._displayed_measurement
@displayed_measurement.setter
def displayed_measurement(self, value : Measurement):
def displayed_measurement(self, value: Measurement):
self._displayed_measurement = value
self.displayed_measurement_changed.emit(value)
@ -105,19 +109,19 @@ class MeasurementModel(ModuleModel):
def measurement_frequency(self):
"""Measurement frequency."""
return self._measurement_frequency
@measurement_frequency.setter
def measurement_frequency(self, value : float):
def measurement_frequency(self, value: float):
# Validator is used to check if the value is in the correct range.
self._measurement_frequency = value
self.measurement_frequency_changed.emit(value)
@property
def averages(self):
"""Number of averages."""
return self._averages
@averages.setter
def averages(self, value : int):
def averages(self, value: int):
self._averages = value
self.averages_changed.emit(value)

View file

@ -1,10 +1,17 @@
"""Signal processing options."""
import sympy
from nqrduck_spectrometer.base_spectrometer_model import BaseSpectrometerModel
from nqrduck_spectrometer.pulseparameters import FunctionOption, GaussianFunction, CustomFunction, Function
from nqrduck_spectrometer.pulseparameters import (
FunctionOption,
GaussianFunction,
CustomFunction,
Function,
)
# We implement the signal processing options as PulseParamterOptions because we can then easily use the automatic UI generation
class FIDFunction(Function):
"""The exponetial FID function."""
@ -22,13 +29,14 @@ class FIDFunction(Function):
class Apodization(BaseSpectrometerModel.PulseParameter):
"""Apodization parameter.
This parameter is used to apply apodization functions to the signal.
The apodization functions are used to reduce the noise in the signal.
Attributes:
APODIZATION_FUNCTIONS (str): The name of the apodization functions option.
"""
APODIZATION_FUNCTIONS = "Apodization functions"
def __init__(self):
@ -41,5 +49,3 @@ class Apodization(BaseSpectrometerModel.PulseParameter):
[FIDFunction(), GaussianFunction(), CustomFunction()],
),
)

View file

@ -1,4 +1,5 @@
"""View for the measurement module."""
import logging
import numpy as np
from PyQt6.QtWidgets import QWidget, QDialog, QLabel, QVBoxLayout
@ -11,19 +12,21 @@ from .widget import Ui_Form
logger = logging.getLogger(__name__)
class MeasurementView(ModuleView):
"""View for the measurement module.
This class is responsible for displaying the measurement data and handling the user input.
Args:
module (Module): The module instance.
Attributes:
widget (QWidget): The widget of the view.
_ui_form (Ui_Form): The form of the widget.
measurement_dialog (MeasurementDialog): The dialog shown when the measurement is started.
"""
def __init__(self, module):
"""Initialize the measurement view."""
super().__init__(module)
@ -35,31 +38,60 @@ class MeasurementView(ModuleView):
# Initialize plotter
self.init_plotter()
logger.debug("Facecolor %s" % str(self._ui_form.plotter.canvas.ax.get_facecolor()))
logger.debug(
"Facecolor %s" % str(self._ui_form.plotter.canvas.ax.get_facecolor())
)
# Measurement dialog
self.measurement_dialog = self.MeasurementDialog()
self.measurement_dialog = self.MeasurementDialog()
# Connect signals
self.module.model.displayed_measurement_changed.connect(self.update_displayed_measurement)
self.module.model.displayed_measurement_changed.connect(
self.update_displayed_measurement
)
self.module.model.view_mode_changed.connect(self.update_displayed_measurement)
self._ui_form.buttonStart.clicked.connect(self.on_measurement_start_button_clicked)
self._ui_form.buttonStart.clicked.connect(
self.on_measurement_start_button_clicked
)
self._ui_form.fftButton.clicked.connect(self.module.controller.change_view_mode)
# Measurement settings controller
self._ui_form.frequencyEdit.textChanged.connect(lambda: self.module.controller.set_frequency(self._ui_form.frequencyEdit.text()))
self._ui_form.averagesEdit.textChanged.connect(lambda: self.module.controller.set_averages(self._ui_form.averagesEdit.text()))
self._ui_form.frequencyEdit.textChanged.connect(
lambda: self.module.controller.set_frequency(
self._ui_form.frequencyEdit.text()
)
)
self._ui_form.averagesEdit.textChanged.connect(
lambda: self.module.controller.set_averages(
self._ui_form.averagesEdit.text()
)
)
# Update fields
self._ui_form.frequencyEdit.textChanged.connect(lambda: self.update_input_widgets(self._ui_form.frequencyEdit, self.module.model.validator_measurement_frequency ))
self._ui_form.averagesEdit.textChanged.connect(lambda: self.update_input_widgets(self._ui_form.averagesEdit, self.module.model.validator_averages))
self._ui_form.frequencyEdit.textChanged.connect(
lambda: self.update_input_widgets(
self._ui_form.frequencyEdit,
self.module.model.validator_measurement_frequency,
)
)
self._ui_form.averagesEdit.textChanged.connect(
lambda: self.update_input_widgets(
self._ui_form.averagesEdit, self.module.model.validator_averages
)
)
self.module.controller.set_frequency_failure.connect(self.on_set_frequency_failure)
self.module.controller.set_averages_failure.connect(self.on_set_averages_failure)
self.module.controller.set_frequency_failure.connect(
self.on_set_frequency_failure
)
self.module.controller.set_averages_failure.connect(
self.on_set_averages_failure
)
self._ui_form.apodizationButton.clicked.connect(
self.module.controller.show_apodization_dialog
)
self._ui_form.apodizationButton.clicked.connect(self.module.controller.show_apodization_dialog)
# Add logos
self._ui_form.buttonStart.setIcon(Logos.Play_16x16())
self._ui_form.buttonStart.setIconSize(self._ui_form.buttonStart.size())
@ -72,8 +104,12 @@ class MeasurementView(ModuleView):
self._ui_form.importButton.setIconSize(self._ui_form.importButton.size())
# Connect measurement save and load buttons
self._ui_form.exportButton.clicked.connect(self.on_measurement_save_button_clicked)
self._ui_form.importButton.clicked.connect(self.on_measurement_load_button_clicked)
self._ui_form.exportButton.clicked.connect(
self.on_measurement_save_button_clicked
)
self._ui_form.importButton.clicked.connect(
self.on_measurement_load_button_clicked
)
# Make title label bold
self._ui_form.titleLabel.setStyleSheet("font-weight: bold;")
@ -90,7 +126,7 @@ class MeasurementView(ModuleView):
plotter.canvas.ax.set_ylabel("Amplitude (a.u.)")
plotter.canvas.ax.set_title("Measurement data - Time domain")
plotter.canvas.ax.grid()
def change_to_time_view(self) -> None:
"""Change plotter to time domain view."""
plotter = self._ui_form.plotter
@ -101,7 +137,7 @@ class MeasurementView(ModuleView):
plotter.canvas.ax.set_title("Measurement data - Time domain")
plotter.canvas.ax.grid()
def change_to_fft_view(self)-> None:
def change_to_fft_view(self) -> None:
"""Change plotter to frequency domain view."""
plotter = self._ui_form.plotter
self._ui_form.fftButton.setText("iFFT")
@ -127,11 +163,16 @@ class MeasurementView(ModuleView):
x = self.module.model.displayed_measurement.tdx
y = self.module.model.displayed_measurement.tdy
self._ui_form.plotter.canvas.ax.plot(x, y.real, label="Real", linestyle="-", alpha=0.35, color="red")
self._ui_form.plotter.canvas.ax.plot(x, y.imag, label="Imaginary", linestyle="-", alpha=0.35, color="green")
self._ui_form.plotter.canvas.ax.plot(
x, y.real, label="Real", linestyle="-", alpha=0.35, color="red"
)
self._ui_form.plotter.canvas.ax.plot(
x, y.imag, label="Imaginary", linestyle="-", alpha=0.35, color="green"
)
# Magnitude
self._ui_form.plotter.canvas.ax.plot(x, np.abs(y), label="Magnitude", color="blue")
self._ui_form.plotter.canvas.ax.plot(
x, np.abs(y), label="Magnitude", color="blue"
)
# Add legend
self._ui_form.plotter.canvas.ax.legend()
@ -163,7 +204,9 @@ class MeasurementView(ModuleView):
"""Slot for when the measurement save button is clicked."""
logger.debug("Measurement save button clicked.")
file_manager = self.QFileManager(self.module.model.FILE_EXTENSION, parent=self.widget)
file_manager = self.QFileManager(
self.module.model.FILE_EXTENSION, parent=self.widget
)
file_name = file_manager.saveFileDialog()
if file_name:
self.module.controller.save_measurement(file_name)
@ -173,7 +216,9 @@ class MeasurementView(ModuleView):
"""Slot for when the measurement load button is clicked."""
logger.debug("Measurement load button clicked.")
file_manager = self.QFileManager(self.module.model.FILE_EXTENSION, parent=self.widget)
file_manager = self.QFileManager(
self.module.model.FILE_EXTENSION, parent=self.widget
)
file_name = file_manager.loadFileDialog()
if file_name:
self.module.controller.load_measurement(file_name)
@ -181,25 +226,18 @@ class MeasurementView(ModuleView):
@pyqtSlot()
def update_input_widgets(self, widget, validator) -> None:
"""Update the style of the QLineEdit widget to indicate if the value is valid.
Args:
widget (QLineEdit): The widget to update.
validator (QValidator): The validator to use for the widget.
"""
if (
validator.validate(widget.text(), 0)
== QValidator.State.Acceptable
):
if validator.validate(widget.text(), 0) == QValidator.State.Acceptable:
widget.setStyleSheet("QLineEdit { background-color: white; }")
elif (
validator.validate(widget.text(), 0)
== QValidator.State.Intermediate
):
elif validator.validate(widget.text(), 0) == QValidator.State.Intermediate:
widget.setStyleSheet("QLineEdit { background-color: yellow; }")
else:
widget.setStyleSheet("QLineEdit { background-color: red; }")
class MeasurementDialog(QDialog):
"""This Dialog is shown when the measurement is started and therefore blocks the main window.
@ -208,6 +246,7 @@ class MeasurementView(ModuleView):
Attributes:
finished (bool): True if the spinner movie is finished.
"""
def __init__(self):
"""Initialize the dialog."""
super().__init__()
@ -216,7 +255,7 @@ class MeasurementView(ModuleView):
self.setWindowFlag(Qt.WindowType.FramelessWindowHint)
self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
self.message_label = ("Measuring...")
self.message_label = "Measuring..."
self.spinner_movie = DuckAnimations.DuckKick128x128()
self.spinner_label = QLabel(self)
self.spinner_label.setMovie(self.spinner_movie)
@ -239,4 +278,3 @@ class MeasurementView(ModuleView):
continue
self.spinner_movie.stop()
super().hide()