This commit is contained in:
jupfi 2024-04-01 10:55:46 +02:00
parent 3a455428e5
commit a758d20c03
15 changed files with 661 additions and 195 deletions

View file

@ -32,5 +32,31 @@ dependencies = [
"scipy", "scipy",
] ]
[project.optional-dependencies]
dev = [
"black",
"pydocstyle",
"pyupgrade",
"ruff",
]
[tool.ruff]
exclude = [
"widget.py",
"base_spectrometer_widget.py",
]
[tool.ruff.lint]
extend-select = [
"UP", # pyupgrade
"D", # pydocstyle
]
[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["F401"]
[tool.ruff.lint.pydocstyle]
convention = "google"
[project.entry-points."nqrduck"] [project.entry-points."nqrduck"]
"nqrduck-spectrometer" = "nqrduck_spectrometer.spectrometer:Spectrometer" "nqrduck-spectrometer" = "nqrduck_spectrometer.spectrometer:Spectrometer"

View file

@ -1 +1,2 @@
"""Init for importing the module."""
from .spectrometer import Spectrometer as Module from .spectrometer import Spectrometer as Module

View file

@ -1,25 +1,37 @@
"""Base Spectrometer Module."""
from PyQt6.QtCore import pyqtSignal from PyQt6.QtCore import pyqtSignal
from nqrduck.module.module import Module from nqrduck.module.module import Module
class BaseSpectrometer(Module): class BaseSpectrometer(Module):
"""Base class for all spectrometers. All spectrometers should inherit from this class.""" """Base class for all spectrometers. All spectrometers should inherit from this class.
Args:
Model (SpectrometerModel) : The model of the spectrometer
View (SpectrometerView) : The view of the spectrometer
Controller (SpectrometerController) : The controller of the spectrometer
Signals:
change_spectrometer (str) : Signal emitted when the spectrometer is changed
"""
change_spectrometer = pyqtSignal(str) change_spectrometer = pyqtSignal(str)
def __init__(self, model, view, controller): def __init__(self, model, view, controller):
"""Initializes the spectrometer."""
super().__init__(model, None, controller) super().__init__(model, None, controller)
# This stops the view from being added to the main window. # This stops the view from being added to the main window.
self.view = None self.view = None
self.settings_view = view(self) self.settings_view = view(self)
def set_active(self): def set_active(self):
"""Sets the spectrometer as the active spectrometer. """Sets the spectrometer as the active spectrometer."""
"""
self.change_spectrometer.emit(self._model.name) self.change_spectrometer.emit(self._model.name)
@property @property
def settings_view(self): def settings_view(self):
"""The settings view of the spectrometer. """The settings view of the spectrometer."""
"""
return self._settings_view return self._settings_view
@settings_view.setter @settings_view.setter

View file

@ -1,13 +1,18 @@
"""Base class for all spectrometer controllers."""
from nqrduck.module.module_controller import ModuleController from nqrduck.module.module_controller import ModuleController
class BaseSpectrometerController(ModuleController): class BaseSpectrometerController(ModuleController):
"""The base class for all spectrometer controllers.""" """The base class for all spectrometer controllers."""
def __init__(self, module): def __init__(self, module):
"""Initializes the spectrometer controller."""
super().__init__(module) super().__init__(module)
def start_measurement(self): def start_measurement(self):
"""Starts the measurement. """Starts the measurement.
This method should be called when the measurement is started. This method should be called when the measurement is started.
""" """
raise NotImplementedError raise NotImplementedError
@ -17,4 +22,5 @@ class BaseSpectrometerController(ModuleController):
raise NotImplementedError raise NotImplementedError
def set_averages(self, value): def set_averages(self, value):
"""Sets the number of averages."""
raise NotImplementedError raise NotImplementedError

View file

@ -1,53 +1,67 @@
"""The base class for all spectrometer models."""
import logging import logging
from collections import OrderedDict from collections import OrderedDict
from PyQt6.QtCore import pyqtSlot, pyqtSignal, QObject from PyQt6.QtGui import QPixmap
from nqrduck.module.module_model import ModuleModel from nqrduck.module.module_model import ModuleModel
from .settings import Setting from .settings import Setting
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class BaseSpectrometerModel(ModuleModel): class BaseSpectrometerModel(ModuleModel):
"""The base class for all spectrometer models. """The base class for all spectrometer models.
It contains the settings and pulse parameters of the spectrometer. It contains the settings and pulse parameters of the spectrometer.
Arguments: Args:
module (Module) -- The module that the spectrometer is connected to module (Module) : The module that the spectrometer is connected to
Attributes: Attributes:
settings (OrderedDict) -- The settings of the spectrometer settings (OrderedDict) : The settings of the spectrometer
pulse_parameter_options (OrderedDict) -- The pulse parameter options of the spectrometer pulse_parameter_options (OrderedDict) : The pulse parameter options of the spectrometer
""" """
settings: OrderedDict settings: OrderedDict
pulse_parameter_options: OrderedDict pulse_parameter_options: OrderedDict
class PulseParameter: class PulseParameter:
"""A pulse parameter is a value that can be different for each event in a pulse sequence. """A pulse parameter is a value that can be different for each event in a pulse sequence.
E.g. the transmit pulse power or the phase of the transmit pulse. E.g. the transmit pulse power or the phase of the transmit pulse.
Arguments: Args:
name (str) -- The name of the pulse parameter name (str) : The name of the pulse parameter
Attributes: Attributes:
name (str) -- The name of the pulse parameter name (str) : The name of the pulse parameter
options (OrderedDict) -- The options of the pulse parameter options (OrderedDict) : The options of the pulse parameter
""" """
def __init__(self, name: str): def __init__(self, name: str):
"""Initializes the pulse parameter. """Initializes the pulse parameter.
Arguments: Arguments:
name (str) -- The name of the pulse parameter name (str) : The name of the pulse parameter
""" """
self.name = name self.name = name
self.options = list() self.options = list()
def get_pixmap(self): def get_pixmap(self) -> QPixmap:
"""Gets the pixmap of the pulse parameter.
Implment this method in the derived class.
Returns:
QPixmap : The pixmap of the pulse parameter
"""
raise NotImplementedError raise NotImplementedError
def add_option(self, option: "Option") -> None: def add_option(self, option: "Option") -> None:
"""Adds an option to the pulse parameter. """Adds an option to the pulse parameter.
Arguments: Args:
option (Option) -- The option to add option (Option) : The option to add
""" """
self.options.append(option) self.options.append(option)
@ -55,41 +69,44 @@ class BaseSpectrometerModel(ModuleModel):
"""Gets the options of the pulse parameter. """Gets the options of the pulse parameter.
Returns: Returns:
list -- The options of the pulse parameter list : The options of the pulse parameter
""" """
return self.options return self.options
def get_option_by_name(self, name: str) -> "Option": def get_option_by_name(self, name: str) -> "Option":
"""Gets an option by its name. """Gets an option by its name.
Arguments: Args:
name (str) -- The name of the option name (str) : The name of the option
Returns: Returns:
Option -- The option with the specified name Option : The option with the specified name
Raises: Raises:
ValueError -- If no option with the specified name is found ValueError : If no option with the specified name is found
""" """
for option in self.options: for option in self.options:
if option.name == name: if option.name == name:
return option return option
raise ValueError("Option with name %s not found" % name) raise ValueError("Option with name %s not found" % name)
def __init__(self, module): def __init__(self, module):
"""Initializes the spectrometer model. """Initializes the spectrometer model.
Arguments: Args:
module (Module) -- The module that the spectrometer is connected to module (Module) : The module that the spectrometer is connected to
""" """
super().__init__(module) super().__init__(module)
self.settings = OrderedDict() self.settings = OrderedDict()
self.pulse_parameter_options = OrderedDict() self.pulse_parameter_options = OrderedDict()
def add_setting(self, setting: Setting, category: str) -> None: def add_setting(self, setting: Setting, category: str) -> None:
"""Adds a setting to the spectrometer.
Args:
setting (Setting) : The setting to add
category (str) : The category of the setting
"""
if category not in self.settings.keys(): if category not in self.settings.keys():
self.settings[category] = [] self.settings[category] = []
self.settings[category].append(setting) self.settings[category].append(setting)
@ -97,14 +114,14 @@ class BaseSpectrometerModel(ModuleModel):
def get_setting_by_name(self, name: str) -> Setting: def get_setting_by_name(self, name: str) -> Setting:
"""Gets a setting by its name. """Gets a setting by its name.
Arguments: Args:
name (str) -- The name of the setting name (str) : The name of the setting
Returns: Returns:
Setting -- The setting with the specified name Setting : The setting with the specified name
Raises: Raises:
ValueError -- If no setting with the specified name is found ValueError : If no setting with the specified name is found
""" """
for category in self.settings.keys(): for category in self.settings.keys():
for setting in self.settings[category]: for setting in self.settings[category]:
@ -112,12 +129,15 @@ class BaseSpectrometerModel(ModuleModel):
return setting return setting
raise ValueError("Setting with name %s not found" % name) raise ValueError("Setting with name %s not found" % name)
def add_pulse_parameter_option(self, name : str, pulse_parameter_class : PulseParameter) -> None: def add_pulse_parameter_option(
self, name: str, pulse_parameter_class: PulseParameter
) -> None:
"""Adds a pulse parameter option to the spectrometer. """Adds a pulse parameter option to the spectrometer.
Arguments: Args:
name (str) -- The name of the pulse parameter name (str) : The name of the pulse parameter
pulse_parameter_class (PulseParameter) -- The pulse parameter class""" pulse_parameter_class (PulseParameter) : The pulse parameter class
"""
self.pulse_parameter_options[name] = pulse_parameter_class self.pulse_parameter_options[name] = pulse_parameter_class
@property @property

View file

@ -1,11 +1,16 @@
"""The Base Class for all Spectrometer Views."""
import logging import logging
from pathlib import Path from PyQt6.QtWidgets import (
from PyQt6.QtWidgets import QWidget, QLabel, QLineEdit, QHBoxLayout, QSizePolicy, QSpacerItem, QVBoxLayout, QCheckBox, QComboBox QWidget,
from PyQt6.QtGui import QIcon QLabel,
from PyQt6.QtCore import Qt, pyqtSlot QHBoxLayout,
QSizePolicy,
QSpacerItem,
QVBoxLayout,
)
from nqrduck.module.module_view import ModuleView from nqrduck.module.module_view import ModuleView
from nqrduck.assets.icons import Logos from nqrduck.assets.icons import Logos
from .settings import FloatSetting, IntSetting, BooleanSetting, SelectionSetting, StringSetting
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -14,13 +19,16 @@ class BaseSpectrometerView(ModuleView):
"""The View Class for all Spectrometers.""" """The View Class for all Spectrometers."""
def __init__(self, module): def __init__(self, module):
"""Initializes the spectrometer view."""
super().__init__(module) super().__init__(module)
def load_settings_ui(self) -> None: def load_settings_ui(self) -> None:
"""This method automatically generates a view for the settings of the module. """This method automatically generates a view for the settings of the module.
If there is a widget file that has been generated by Qt Designer, it will be used. Otherwise, a default view will be generated."""
If there is a widget file that has been generated by Qt Designer, it will be used. Otherwise, a default view will be generated.
"""
from .base_spectrometer_widget import Ui_Form from .base_spectrometer_widget import Ui_Form
widget = QWidget() widget = QWidget()
widget.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) widget.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
self._ui_form = Ui_Form() self._ui_form = Ui_Form()
@ -59,7 +67,6 @@ class BaseSpectrometerView(ModuleView):
# Add a icon that can be used as a tooltip # Add a icon that can be used as a tooltip
if setting.description is not None: if setting.description is not None:
logger.debug("Adding tooltip to setting: %s", setting.name) logger.debug("Adding tooltip to setting: %s", setting.name)
self_path = Path(__file__).parent
icon = Logos.QuestionMark_16x16() icon = Logos.QuestionMark_16x16()
icon_label = QLabel() icon_label = QLabel()
icon_label.setPixmap(icon.pixmap(icon.availableSizes()[0])) icon_label.setPixmap(icon.pixmap(icon.availableSizes()[0]))
@ -81,8 +88,5 @@ class BaseSpectrometerView(ModuleView):
category_layout.addStretch(1) category_layout.addStretch(1)
self._ui_form.gridLayout.addLayout(category_layout, row, column) self._ui_form.gridLayout.addLayout(category_layout, row, column)
# Push all the settings to the top of the widget # Push all the settings to the top of the widget
self._ui_form.verticalLayout.addStretch(1) self._ui_form.verticalLayout.addStretch(1)

View file

@ -6,10 +6,10 @@
# run again. Do not edit this file unless you know what you are doing. # run again. Do not edit this file unless you know what you are doing.
from PyQt6 import QtCore, QtGui, QtWidgets from PyQt6 import QtCore, QtWidgets
class Ui_Form(object): class Ui_Form:
def setupUi(self, Form): def setupUi(self, Form):
Form.setObjectName("Form") Form.setObjectName("Form")
Form.resize(800, 647) Form.resize(800, 647)

View file

@ -1,5 +1,9 @@
"""The Controller for the Spectrometer Module.
Careful - this is not the base class for the spectrometer submodules, but the controller for the spectrometer module itself.
"""
import logging import logging
from PyQt6.QtCore import pyqtSlot
from nqrduck.module.module_controller import ModuleController from nqrduck.module.module_controller import ModuleController
from nqrduck.core.main_controller import MainController from nqrduck.core.main_controller import MainController
from nqrduck_spectrometer.base_spectrometer import BaseSpectrometer from nqrduck_spectrometer.base_spectrometer import BaseSpectrometer
@ -40,6 +44,7 @@ class SpectrometerController(ModuleController):
def process_signals(self, key: str, value: object) -> None: def process_signals(self, key: str, value: object) -> None:
"""This method processes the signals from the nqrduck module. """This method processes the signals from the nqrduck module.
It is called by the nqrduck module when a signal is emitted. It is called by the nqrduck module when a signal is emitted.
It then calls the corresponding method of the spectrometer model. It then calls the corresponding method of the spectrometer model.
@ -59,6 +64,7 @@ class SpectrometerController(ModuleController):
def on_loading(self) -> None: def on_loading(self) -> None:
"""This method is called when the module is loaded. """This method is called when the module is loaded.
It connects the signals from the spectrometer model to the view. It connects the signals from the spectrometer model to the view.
""" """
self._module.model.spectrometer_added.connect( self._module.model.spectrometer_added.connect(
@ -71,6 +77,7 @@ class SpectrometerController(ModuleController):
def on_measurement_start(self) -> None: def on_measurement_start(self) -> None:
"""This method is called when a measurement is started. """This method is called when a measurement is started.
It calls the on_measurement_start method of the active spectrometer. It calls the on_measurement_start method of the active spectrometer.
""" """
logger.debug( logger.debug(

View file

@ -1,14 +1,25 @@
"""Class for handling measurement data."""
import logging import logging
import numpy as np import numpy as np
from nqrduck.helpers.signalprocessing import SignalProcessing as sp from nqrduck.helpers.signalprocessing import SignalProcessing as sp
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class Measurement():
class Measurement:
"""This class defines how measurement data should look. """This class defines how measurement data should look.
It includes pulse parameters necessary for further signal processing. It includes pulse parameters necessary for further signal processing.
Every spectrometer should adhere to this data structure in order to be compatible with the rest of the nqrduck. Every spectrometer should adhere to this data structure in order to be compatible with the rest of the nqrduck.
Args:
tdx (np.array): Time axis for the x axis of the measurement data.
tdy (np.array): Time axis for the y axis of the measurement data.
target_frequency (float): Target frequency of the measurement.
frequency_shift (float, optional): Frequency shift of the measurement. Defaults to 0.
IF_frequency (float, optional): Intermediate frequency of the measurement. Defaults to 0.
Attributes: Attributes:
tdx (np.array): Time axis for the x axis of the measurement data. tdx (np.array): Time axis for the x axis of the measurement data.
tdy (np.array): Time axis for the y axis of the measurement data. tdy (np.array): Time axis for the y axis of the measurement data.
@ -19,7 +30,15 @@ class Measurement():
yf (np.array): Frequency axis for the y axis of the measurement data. yf (np.array): Frequency axis for the y axis of the measurement data.
""" """
def __init__(self, tdx, tdy, target_frequency, frequency_shift : float = 0, IF_frequency : float = 0) -> None: def __init__(
self,
tdx,
tdy,
target_frequency,
frequency_shift: float = 0,
IF_frequency: float = 0,
) -> None:
"""Initializes the measurement."""
self.tdx = tdx self.tdx = tdx
self.tdy = tdy self.tdy = tdy
self.target_frequency = target_frequency self.target_frequency = target_frequency
@ -32,31 +51,33 @@ class Measurement():
"""Converts the measurement to a json-compatible format. """Converts the measurement to a json-compatible format.
Returns: Returns:
dict -- The measurement in json-compatible format. dict : The measurement in json-compatible format.
""" """
return { return {
"tdx": self.tdx.tolist(), "tdx": self.tdx.tolist(),
"tdy": [[x.real, x.imag] for x in self.tdy], # Convert complex numbers to list "tdy": [
[x.real, x.imag] for x in self.tdy
], # Convert complex numbers to list
"target_frequency": self.target_frequency, "target_frequency": self.target_frequency,
"IF_frequency": self.IF_frequency "IF_frequency": self.IF_frequency,
} }
@classmethod @classmethod
def from_json(cls, json): def from_json(cls, json: dict):
"""Converts the json format to a measurement. """Converts the json format to a measurement.
Arguments: Args:
json (dict) -- The measurement in json-compatible format. json (dict) : The measurement in json-compatible format.
Returns: Returns:
Measurement -- The measurement. Measurement : The measurement.
""" """
tdy = np.array([complex(y[0], y[1]) for y in json["tdy"]]) tdy = np.array([complex(y[0], y[1]) for y in json["tdy"]])
return cls( return cls(
np.array(json["tdx"]), np.array(json["tdx"]),
tdy, tdy,
target_frequency=json["target_frequency"], target_frequency=json["target_frequency"],
IF_frequency = json["IF_frequency"] IF_frequency=json["IF_frequency"],
) )
# Measurement data # Measurement data

View file

@ -1,3 +1,5 @@
"""The model for the nqrduck-spectrometer module. This module is responsible for managing the spectrometers."""
import logging import logging
from PyQt6.QtCore import pyqtSignal from PyQt6.QtCore import pyqtSignal
from nqrduck.module.module_model import ModuleModel from nqrduck.module.module_model import ModuleModel
@ -5,19 +7,36 @@ from .base_spectrometer import BaseSpectrometer
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class SpectrometerModel(ModuleModel): class SpectrometerModel(ModuleModel):
"""The model for the spectrometer module.
This class is responsible for managing the spectrometers.
Args:
module (Module) : The module that the spectrometer is connected to
Attributes:
active_spectrometer (BaseSpectrometer) : The currently active spectrometer
available_spectrometers (dict) : A dictionary of all available spectrometers
Signals:
spectrometer_added (BaseSpectrometer) : Signal emitted when a spectrometer is added
active_spectrometer_changed (BaseSpectrometer) : Signal emitted when the active spectrometer is changed
"""
spectrometer_added = pyqtSignal(BaseSpectrometer) spectrometer_added = pyqtSignal(BaseSpectrometer)
active_spectrometer_changed = pyqtSignal(BaseSpectrometer) active_spectrometer_changed = pyqtSignal(BaseSpectrometer)
def __init__(self, module) -> None: def __init__(self, module) -> None:
"""Initializes the spectrometer model."""
super().__init__(module) super().__init__(module)
self._active_spectrometer = None self._active_spectrometer = None
self._available_spectrometers = dict() self._available_spectrometers = dict()
@property @property
def active_spectrometer(self): def active_spectrometer(self):
"""The currently active spectrometer. This is the one that is currently being used. """The currently active spectrometer. This is the one that is currently being used."""
"""
return self._active_spectrometer return self._active_spectrometer
@active_spectrometer.setter @active_spectrometer.setter
@ -26,19 +45,24 @@ class SpectrometerModel(ModuleModel):
self.active_spectrometer_changed.emit(value) self.active_spectrometer_changed.emit(value)
spectrometer_module_name = value.model.toolbar_name spectrometer_module_name = value.model.toolbar_name
logger.debug("Active spectrometer changed to %s", spectrometer_module_name) logger.debug("Active spectrometer changed to %s", spectrometer_module_name)
self.module.nqrduck_signal.emit("active_spectrometer_changed", spectrometer_module_name) self.module.nqrduck_signal.emit(
"active_spectrometer_changed", spectrometer_module_name
)
@property @property
def available_spectrometers(self): def available_spectrometers(self):
"""A dictionary of all available spectrometers. The key is the name of the spectrometer and the value is the module. """A dictionary of all available spectrometers. The key is the name of the spectrometer and the value is the module."""
"""
return self._available_spectrometers return self._available_spectrometers
def add_spectrometers(self, spectrometer_module_name, module): def add_spectrometers(self, spectrometer_module_name, module):
"""Adds a spectrometer to the available spectrometers.
Args:
spectrometer_module_name (str) : The name of the spectrometer
module (BaseSpectrometer) : The module of the spectrometer
"""
self._available_spectrometers[spectrometer_module_name] = module self._available_spectrometers[spectrometer_module_name] = module
logger.debug("Added module: %s", spectrometer_module_name) logger.debug("Added module: %s", spectrometer_module_name)
self.spectrometer_added.emit(module) self.spectrometer_added.emit(module)
self.active_spectrometer = module self.active_spectrometer = module
self.add_submodule(spectrometer_module_name) self.add_submodule(spectrometer_module_name)

View file

@ -1,9 +1,14 @@
"""Contains the classes for the pulse parameters of the spectrometer. It includes the functions and the options for the pulse parameters.
Todo:
* This shouldn't be in the spectrometer module. It should be in it"s own pulse sequence module.
"""
from __future__ import annotations from __future__ import annotations
import logging import logging
import numpy as np import numpy as np
import sympy import sympy
from decimal import Decimal from decimal import Decimal
from pathlib import Path
from PyQt6.QtGui import QPixmap from PyQt6.QtGui import QPixmap
from nqrduck.contrib.mplwidget import MplWidget from nqrduck.contrib.mplwidget import MplWidget
from nqrduck.helpers.signalprocessing import SignalProcessing as sp from nqrduck.helpers.signalprocessing import SignalProcessing as sp
@ -14,6 +19,22 @@ logger = logging.getLogger(__name__)
class Function: class Function:
"""A function that can be used as a pulse parameter.
This class is the base class for all functions that can be used as pulse parameters. Functions can be used for pulse shapes, for example.
Args:
expr (str | sympy.Expr): The expression of the function.
Attributes:
name (str): The name of the function.
parameters (list): The parameters of the function.
expr (sympy.Expr): The sympy expression of the function.
resolution (Decimal): The resolution of the function in seconds.
start_x (float): The x value where the evalution of the function starts.
end_x (float): The x value where the evalution of the function ends.
"""
name: str name: str
parameters: list parameters: list
expression: str | sympy.Expr expression: str | sympy.Expr
@ -22,6 +43,7 @@ class Function:
end_x: float end_x: float
def __init__(self, expr) -> None: def __init__(self, expr) -> None:
"""Initializes the function."""
self.parameters = [] self.parameters = []
self.expr = expr self.expr = expr
self.resolution = Decimal(1 / 30.72e6) self.resolution = Decimal(1 / 30.72e6)
@ -121,7 +143,9 @@ class Function:
mpl_widget.canvas.ax.grid(True) mpl_widget.canvas.ax.grid(True)
return mpl_widget return mpl_widget
def get_pulse_amplitude(self, pulse_length: Decimal, resolution : Decimal = None) -> np.array: def get_pulse_amplitude(
self, pulse_length: Decimal, resolution: Decimal = None
) -> np.array:
"""Returns the pulse amplitude in the time domain. """Returns the pulse amplitude in the time domain.
Args: Args:
@ -133,11 +157,12 @@ class Function:
""" """
return self.evaluate(pulse_length, resolution=resolution) return self.evaluate(pulse_length, resolution=resolution)
def add_parameter(self, parameter: "Function.Parameter") -> None: def add_parameter(self, parameter: Function.Parameter) -> None:
"""Adds a parameter to the function. """Adds a parameter to the function.
Args: Args:
parameter (Function.Parameter): The parameter to add.""" parameter (Function.Parameter): The parameter to add.
"""
self.parameters.append(parameter) self.parameters.append(parameter)
def to_json(self) -> dict: def to_json(self) -> dict:
@ -156,7 +181,7 @@ class Function:
} }
@classmethod @classmethod
def from_json(cls, data: dict) -> "Function": def from_json(cls, data: dict) -> Function:
"""Creates a function from a json representation. """Creates a function from a json representation.
Args: Args:
@ -193,7 +218,7 @@ class Function:
if isinstance(expr, str): if isinstance(expr, str):
try: try:
self._expr = sympy.sympify(expr) self._expr = sympy.sympify(expr)
except: except ValueError:
logger.error("Could not convert %s to a sympy expression", expr) logger.error("Could not convert %s to a sympy expression", expr)
raise SyntaxError("Could not convert %s to a sympy expression" % expr) raise SyntaxError("Could not convert %s to a sympy expression" % expr)
elif isinstance(expr, sympy.Expr): elif isinstance(expr, sympy.Expr):
@ -208,7 +233,7 @@ class Function:
def resolution(self, resolution): def resolution(self, resolution):
try: try:
self._resolution = Decimal(resolution) self._resolution = Decimal(resolution)
except: except ValueError:
logger.error("Could not convert %s to a decimal", resolution) logger.error("Could not convert %s to a decimal", resolution)
raise SyntaxError("Could not convert %s to a decimal" % resolution) raise SyntaxError("Could not convert %s to a decimal" % resolution)
@ -221,7 +246,7 @@ class Function:
def start_x(self, start_x): def start_x(self, start_x):
try: try:
self._start_x = float(start_x) self._start_x = float(start_x)
except: except ValueError:
logger.error("Could not convert %s to a float", start_x) logger.error("Could not convert %s to a float", start_x)
raise SyntaxError("Could not convert %s to a float" % start_x) raise SyntaxError("Could not convert %s to a float" % start_x)
@ -234,7 +259,7 @@ class Function:
def end_x(self, end_x): def end_x(self, end_x):
try: try:
self._end_x = float(end_x) self._end_x = float(end_x)
except: except ValueError:
logger.error("Could not convert %s to a float", end_x) logger.error("Could not convert %s to a float", end_x)
raise SyntaxError("Could not convert %s to a float" % end_x) raise SyntaxError("Could not convert %s to a float" % end_x)
@ -242,12 +267,30 @@ class Function:
"""This is the default pixmap for every function. If one wants to have a custom pixmap, this method has to be overwritten. """This is the default pixmap for every function. If one wants to have a custom pixmap, this method has to be overwritten.
Returns: Returns:
QPixmap -- The default pixmap for every function""" QPixmap : The default pixmap for every function
"""
pixmap = PulseParamters.TXCustom() pixmap = PulseParamters.TXCustom()
return pixmap return pixmap
class Parameter: class Parameter:
"""A parameter of a function.
This can be for example the standard deviation of a Gaussian function.
Args:
name (str): The name of the parameter.
symbol (str): The symbol of the parameter.
value (float): The value of the parameter.
Attributes:
name (str): The name of the parameter.
symbol (str): The symbol of the parameter.
value (float): The value of the parameter.
default (float): The default value of the parameter.
"""
def __init__(self, name: str, symbol: str, value: float) -> None: def __init__(self, name: str, symbol: str, value: float) -> None:
"""Initializes the parameter."""
self.name = name self.name = name
self.symbol = symbol self.symbol = symbol
self.value = value self.value = value
@ -296,20 +339,31 @@ class RectFunction(Function):
name = "Rectangular" name = "Rectangular"
def __init__(self) -> None: def __init__(self) -> None:
"""Initializes the RecFunction."""
expr = sympy.sympify("1") expr = sympy.sympify("1")
super().__init__(expr) super().__init__(expr)
def get_pixmap(self): def get_pixmap(self) -> QPixmap:
"""Returns the pixmap of the rectangular function.
Returns:
QPixmap: The pixmap of the rectangular function.
"""
pixmap = PulseParamters.TXRect() pixmap = PulseParamters.TXRect()
return pixmap return pixmap
class SincFunction(Function): class SincFunction(Function):
"""The sinc function.""" """The sinc function.
The sinc function is defined as sin(x * l) / (x * l).
The parameter is the scale factor l.
"""
name = "Sinc" name = "Sinc"
def __init__(self) -> None: def __init__(self) -> None:
"""Initializes the SincFunction."""
expr = sympy.sympify("sin(x * l)/ (x * l)") expr = sympy.sympify("sin(x * l)/ (x * l)")
super().__init__(expr) super().__init__(expr)
self.add_parameter(Function.Parameter("Scale Factor", "l", 2)) self.add_parameter(Function.Parameter("Scale Factor", "l", 2))
@ -317,16 +371,26 @@ class SincFunction(Function):
self.end_x = np.pi self.end_x = np.pi
def get_pixmap(self): def get_pixmap(self):
"""Returns the pixmap of the sinc function.
Returns:
QPixmap: The pixmap of the sinc function.
"""
pixmap = PulseParamters.TXSinc() pixmap = PulseParamters.TXSinc()
return pixmap return pixmap
class GaussianFunction(Function): class GaussianFunction(Function):
"""The Gaussian function.""" """The Gaussian function.
The Gaussian function is defined as exp(-0.5 * ((x - mu) / sigma)**2).
The parameters are the mean and the standard deviation.
"""
name = "Gaussian" name = "Gaussian"
def __init__(self) -> None: def __init__(self) -> None:
"""Initializes the GaussianFunction."""
expr = sympy.sympify("exp(-0.5 * ((x - mu) / sigma)**2)") expr = sympy.sympify("exp(-0.5 * ((x - mu) / sigma)**2)")
super().__init__(expr) super().__init__(expr)
self.add_parameter(Function.Parameter("Mean", "mu", 0)) self.add_parameter(Function.Parameter("Mean", "mu", 0))
@ -335,6 +399,11 @@ class GaussianFunction(Function):
self.end_x = np.pi self.end_x = np.pi
def get_pixmap(self): def get_pixmap(self):
"""Returns the QPixmap of the Gaussian function.
Returns:
QPixmap: The QPixmap of the Gaussian function.
"""
pixmap = PulseParamters.TXGauss() pixmap = PulseParamters.TXGauss()
return pixmap return pixmap
@ -351,25 +420,55 @@ class CustomFunction(Function):
name = "Custom" name = "Custom"
def __init__(self) -> None: def __init__(self) -> None:
"""Initializes the Custom Function."""
expr = sympy.sympify(" 2 * x**2 + 3 * x + 1") expr = sympy.sympify(" 2 * x**2 + 3 * x + 1")
super().__init__(expr) super().__init__(expr)
class Option: class Option:
"""Defines options for the pulse parameters which can then be set accordingly.""" """Defines options for the pulse parameters which can then be set accordingly.
Options can be of different types, for example boolean, numeric or function.
Args:
name (str): The name of the option.
value: The value of the option.
Attributes:
name (str): The name of the option.
value: The value of the option.
"""
def __init__(self, name: str, value) -> None: def __init__(self, name: str, value) -> None:
"""Initializes the option."""
self.name = name self.name = name
self.value = value self.value = value
def set_value(self): def set_value(self):
"""Sets the value of the option.
This method has to be implemented in the derived classes.
"""
raise NotImplementedError raise NotImplementedError
def to_json(self): def to_json(self):
"""Returns a json representation of the option.
Returns:
dict: The json representation of the option.
"""
return {"name": self.name, "value": self.value, "type": self.TYPE} return {"name": self.name, "value": self.value, "type": self.TYPE}
@classmethod @classmethod
def from_json(cls, data) -> "Option": def from_json(cls, data) -> Option:
"""Creates an option from a json representation.
Args:
data (dict): The json representation of the option.
Returns:
Option: The option.
"""
for subclass in cls.__subclasses__(): for subclass in cls.__subclasses__():
if subclass.TYPE == data["type"]: if subclass.TYPE == data["type"]:
cls = subclass cls = subclass
@ -390,6 +489,7 @@ class BooleanOption(Option):
TYPE = "Boolean" TYPE = "Boolean"
def set_value(self, value): def set_value(self, value):
"""Sets the value of the option."""
self.value = value self.value = value
@ -399,49 +499,102 @@ class NumericOption(Option):
TYPE = "Numeric" TYPE = "Numeric"
def set_value(self, value): def set_value(self, value):
"""Sets the value of the option."""
self.value = float(value) self.value = float(value)
class FunctionOption(Option): class FunctionOption(Option):
"""Defines a selection option for a pulse parameter option. """Defines a selection option for a pulse parameter option.
It takes different function objects."""
It takes different function objects.
Args:
name (str): The name of the option.
functions (list): The functions that can be selected.
Attributes:
name (str): The name of the option.
functions (list): The functions that can be selected.
"""
TYPE = "Function" TYPE = "Function"
def __init__(self, name, functions) -> None: def __init__(self, name, functions) -> None:
"""Initializes the FunctionOption."""
super().__init__(name, functions[0]) super().__init__(name, functions[0])
self.functions = functions self.functions = functions
def set_value(self, value): def set_value(self, value):
"""Sets the value of the option.
Args:
value: The value of the option.
"""
self.value = value self.value = value
def get_function_by_name(self, name): def get_function_by_name(self, name):
"""Returns the function with the given name.
Args:
name (str): The name of the function.
Returns:
Function: The function with the given name.
"""
for function in self.functions: for function in self.functions:
if function.name == name: if function.name == name:
return function return function
raise ValueError("Function with name %s not found" % name) raise ValueError("Function with name %s not found" % name)
def to_json(self): def to_json(self):
"""Returns a json representation of the option.
Returns:
dict: The json representation of the option.
"""
return {"name": self.name, "value": self.value.to_json(), "type": self.TYPE} return {"name": self.name, "value": self.value.to_json(), "type": self.TYPE}
@classmethod @classmethod
def from_json(cls, data): def from_json(cls, data):
"""Creates a FunctionOption from a json representation.
Args:
data (dict): The json representation of the FunctionOption.
Returns:
FunctionOption: The FunctionOption.
"""
functions = [function() for function in Function.__subclasses__()] functions = [function() for function in Function.__subclasses__()]
obj = cls(data["name"], functions) obj = cls(data["name"], functions)
obj.value = Function.from_json(data["value"]) obj.value = Function.from_json(data["value"])
return obj return obj
def get_pixmap(self): def get_pixmap(self):
"""Returns the pixmap of the function."""
return self.value.get_pixmap() return self.value.get_pixmap()
class TXPulse(BaseSpectrometerModel.PulseParameter): class TXPulse(BaseSpectrometerModel.PulseParameter):
""" Basic TX Pulse Parameter. It includes options for the relative amplitude, the phase and the pulse shape.""" """Basic TX Pulse Parameter. It includes options for the relative amplitude, the phase and the pulse shape.
Args:
name (str): The name of the pulse parameter.
Attributes:
RELATIVE_AMPLITUDE (str): The relative amplitude of the pulse.
TX_PHASE (str): The phase of the pulse.
TX_PULSE_SHAPE (str): The pulse shape.
"""
RELATIVE_AMPLITUDE = "Relative TX Amplitude" RELATIVE_AMPLITUDE = "Relative TX Amplitude"
TX_PHASE = "TX Phase" TX_PHASE = "TX Phase"
TX_PULSE_SHAPE = "TX Pulse Shape" TX_PULSE_SHAPE = "TX Pulse Shape"
def __init__(self, name) -> None: def __init__(self, name) -> None:
"""Initializes the TX Pulse Parameter.
It adds the options for the relative amplitude, the phase and the pulse shape.
"""
super().__init__(name) super().__init__(name)
self.add_option(NumericOption(self.RELATIVE_AMPLITUDE, 0)) self.add_option(NumericOption(self.RELATIVE_AMPLITUDE, 0))
self.add_option(NumericOption(self.TX_PHASE, 0)) self.add_option(NumericOption(self.TX_PHASE, 0))
@ -453,6 +606,11 @@ class TXPulse(BaseSpectrometerModel.PulseParameter):
) )
def get_pixmap(self): def get_pixmap(self):
"""Returns the pixmap of the TX Pulse Parameter.
Returns:
QPixmap: The pixmap of the TX Pulse Parameter depending on the relative amplitude.
"""
if self.get_option_by_name(self.RELATIVE_AMPLITUDE).value > 0: if self.get_option_by_name(self.RELATIVE_AMPLITUDE).value > 0:
return self.get_option_by_name(self.TX_PULSE_SHAPE).get_pixmap() return self.get_option_by_name(self.TX_PULSE_SHAPE).get_pixmap()
else: else:
@ -461,15 +619,32 @@ class TXPulse(BaseSpectrometerModel.PulseParameter):
class RXReadout(BaseSpectrometerModel.PulseParameter): class RXReadout(BaseSpectrometerModel.PulseParameter):
"""Basic PulseParameter for the RX Readout. It includes an option for the RX Readout state.""" """Basic PulseParameter for the RX Readout. It includes an option for the RX Readout state.
Args:
name (str): The name of the pulse parameter.
Attributes:
RX (str): The RX Readout state.
"""
RX = "RX" RX = "RX"
def __init__(self, name) -> None: def __init__(self, name) -> None:
"""Initializes the RX Readout PulseParameter.
It adds an option for the RX Readout state.
"""
super().__init__(name) super().__init__(name)
self.add_option(BooleanOption(self.RX, False)) self.add_option(BooleanOption(self.RX, False))
def get_pixmap(self): def get_pixmap(self):
if self.get_option_by_name(self.RX).value == False: """Returns the pixmap of the RX Readout PulseParameter.
Returns:
QPixmap: The pixmap of the RX Readout PulseParameter depending on the RX Readout state.
"""
if self.get_option_by_name(self.RX).value is False:
pixmap = PulseParamters.RXOff() pixmap = PulseParamters.RXOff()
else: else:
pixmap = PulseParamters.RXOn() pixmap = PulseParamters.RXOn()
@ -477,14 +652,32 @@ class RXReadout(BaseSpectrometerModel.PulseParameter):
class Gate(BaseSpectrometerModel.PulseParameter): class Gate(BaseSpectrometerModel.PulseParameter):
"""Basic PulseParameter for the Gate. It includes an option for the Gate state.
Args:
name (str): The name of the pulse parameter.
Attributes:
GATE_STATE (str): The Gate state.
"""
GATE_STATE = "Gate State" GATE_STATE = "Gate State"
def __init__(self, name) -> None: def __init__(self, name) -> None:
"""Initializes the Gate PulseParameter.
It adds an option for the Gate state.
"""
super().__init__(name) super().__init__(name)
self.add_option(BooleanOption(self.GATE_STATE, False)) self.add_option(BooleanOption(self.GATE_STATE, False))
def get_pixmap(self): def get_pixmap(self):
if self.get_option_by_name(self.GATE_STATE).value == False: """Returns the pixmap of the Gate PulseParameter.
Returns:
QPixmap: The pixmap of the Gate PulseParameter depending on the Gate state.
"""
if self.get_option_by_name(self.GATE_STATE).value is False:
pixmap = PulseParamters.GateOff() pixmap = PulseParamters.GateOff()
else: else:
pixmap = PulseParamters.GateOn() pixmap = PulseParamters.GateOn()

View file

@ -1,3 +1,5 @@
"""Contains the PulseSequence class that is used to store a pulse sequence and its events."""
import logging import logging
from collections import OrderedDict from collections import OrderedDict
from nqrduck.helpers.unitconverter import UnitConverter from nqrduck.helpers.unitconverter import UnitConverter
@ -7,34 +9,70 @@ logger = logging.getLogger(__name__)
class PulseSequence: class PulseSequence:
"""A pulse sequence is a collection of events that are executed in a certain order.""" """A pulse sequence is a collection of events that are executed in a certain order.
Args:
name (str): The name of the pulse sequence
Attributes:
name (str): The name of the pulse sequence
events (list): The events of the pulse sequence
"""
def __init__(self, name) -> None: def __init__(self, name) -> None:
"""Initializes the pulse sequence."""
self.name = name self.name = name
self.events = list() self.events = list()
def get_event_names(self) -> list: def get_event_names(self) -> list:
"""Returns a list of the names of the events in the pulse sequence.
Returns:
list: The names of the events
"""
return [event.name for event in self.events] return [event.name for event in self.events]
class Event: class Event:
"""An event is a part of a pulse sequence. It has a name and a duration and different parameters that have to be set.""" """An event is a part of a pulse sequence. It has a name and a duration and different parameters that have to be set.
Args:
name (str): The name of the event
duration (str): The duration of the event
Attributes:
name (str): The name of the event
duration (str): The duration of the event
parameters (OrderedDict): The parameters of the event
"""
def __init__(self, name: str, duration: str) -> None: def __init__(self, name: str, duration: str) -> None:
"""Initializes the event."""
self.parameters = OrderedDict() self.parameters = OrderedDict()
self.name = name self.name = name
self.duration = duration self.duration = duration
def add_parameter(self, parameter) -> None: def add_parameter(self, parameter) -> None:
"""Adds a parameter to the event.
Args:
parameter: The parameter to add
"""
self.parameters.append(parameter) self.parameters.append(parameter)
def on_duration_changed(self, duration: str) -> None: def on_duration_changed(self, duration: str) -> None:
"""This method is called when the duration of the event is changed.
Args:
duration (str): The new duration of the event
"""
logger.debug("Duration of event %s changed to %s", self.name, duration) logger.debug("Duration of event %s changed to %s", self.name, duration)
self.duration = duration self.duration = duration
@classmethod @classmethod
def load_event(cls, event, pulse_parameter_options): def load_event(cls, event, pulse_parameter_options):
""" """Loads an event from a dict.
Loads an event from a dict. The pulse paramter options are needed to load the parameters
The pulse paramter options are needed to load the parameters
and determine if the correct spectrometer is active. and determine if the correct spectrometer is active.
Args: Args:
@ -66,6 +104,7 @@ class PulseSequence:
@property @property
def duration(self): def duration(self):
"""The duration of the event."""
return self._duration return self._duration
@duration.setter @duration.setter
@ -80,12 +119,12 @@ class PulseSequence:
self._duration = duration self._duration = duration
def to_json(self): def to_json(self):
"""Returns a dict with all the data in the pulse sequence """Returns a dict with all the data in the pulse sequence.
Returns: Returns:
dict: The dict with the sequence data""" dict: The dict with the sequence data
"""
data = {"name": self.name, "events": []} data = {"name": self.name, "events": []}
for event in self.events: for event in self.events:
event_data = { event_data = {
@ -102,7 +141,9 @@ class PulseSequence:
@classmethod @classmethod
def load_sequence(cls, sequence, pulse_parameter_options): def load_sequence(cls, sequence, pulse_parameter_options):
"""Loads a pulse sequence from a dict. The pulse paramter options are needed to load the parameters """Loads a pulse sequence from a dict.
The pulse paramter options are needed to load the parameters
and make sure the correct spectrometer is active. and make sure the correct spectrometer is active.
Args: Args:
@ -123,13 +164,16 @@ class PulseSequence:
class Variable: class Variable:
"""A variable is a parameter that can be used within a pulsesequence as a placeholder. """A variable is a parameter that can be used within a pulsesequence as a placeholder.
For example the event duration a Variable with name a can be set. This variable can then be set to a list of different values. For example the event duration a Variable with name a can be set. This variable can then be set to a list of different values.
On execution of the pulse sequence the event duration will be set to the first value in the list. On execution of the pulse sequence the event duration will be set to the first value in the list.
Then the pulse sequence will be executed with the second value of the list. This is repeated until the pulse sequence has Then the pulse sequence will be executed with the second value of the list. This is repeated until the pulse sequence has
been executed with all values in the list.""" been executed with all values in the list.
"""
@property @property
def name(self): def name(self):
"""The name of the variable."""
return self._name return self._name
@name.setter @name.setter
@ -140,6 +184,7 @@ class PulseSequence:
@property @property
def values(self): def values(self):
"""The values of the variable. This is a list of values that the variable can take."""
return self._values return self._values
@values.setter @values.setter
@ -150,10 +195,13 @@ class PulseSequence:
class VariableGroup: class VariableGroup:
"""Variables can be grouped together. """Variables can be grouped together.
If we have groups a and b the pulse sequence will be executed for all combinations of variables in a and b."""
If we have groups a and b the pulse sequence will be executed for all combinations of variables in a and b.
"""
@property @property
def name(self): def name(self):
"""The name of the variable group."""
return self._name return self._name
@name.setter @name.setter
@ -164,6 +212,7 @@ class PulseSequence:
@property @property
def variables(self): def variables(self):
"""The variables in the group. This is a list of variables."""
return self._variables return self._variables
@variables.setter @variables.setter

View file

@ -1,3 +1,5 @@
"""Settings for the different spectrometers."""
import logging import logging
import ipaddress import ipaddress
from PyQt6.QtCore import QObject, pyqtSignal, pyqtSlot from PyQt6.QtCore import QObject, pyqtSignal, pyqtSlot
@ -10,7 +12,20 @@ logger = logging.getLogger(__name__)
class Setting(QObject): class Setting(QObject):
"""A setting for the spectrometer is a value that is the same for all events in a pulse sequence. """A setting for the spectrometer is a value that is the same for all events in a pulse sequence.
E.g. the Transmit gain or the number of points in a spectrum."""
E.g. the Transmit gain or the number of points in a spectrum.
Args:
name (str) : The name of the setting
description (str) : A description of the setting
default : The default value of the setting
Attributes:
name (str) : The name of the setting
description (str) : A description of the setting
value : The value of the setting
widget : The widget that is used to change the setting
"""
settings_changed = pyqtSignal() settings_changed = pyqtSignal()
@ -20,6 +35,7 @@ class Setting(QObject):
Args: Args:
name (str): The name of the setting. name (str): The name of the setting.
description (str): A description of the setting. description (str): A description of the setting.
default: The default value of the setting.
""" """
super().__init__() super().__init__()
self.name = name self.name = name
@ -32,21 +48,31 @@ class Setting(QObject):
@pyqtSlot(str) @pyqtSlot(str)
def on_value_changed(self, value): def on_value_changed(self, value):
"""This method is called when the value of the setting is changed.
Args:
value (str): The new value of the setting.
"""
logger.debug("Setting %s changed to %s", self.name, value) logger.debug("Setting %s changed to %s", self.name, value)
self.value = value self.value = value
self.settings_changed.emit() self.settings_changed.emit()
def get_setting(self): def get_setting(self):
"""Return the value of the setting.
Returns:
The value of the setting.
"""
return float(self.value) return float(self.value)
def get_widget(self): def get_widget(self):
"""Return a widget for the setting. """Return a widget for the setting.
The default widget is simply a QLineEdit. The default widget is simply a QLineEdit.
This method can be overwritten by subclasses to return a different widget. This method can be overwritten by subclasses to return a different widget.
Returns: Returns:
QLineEdit: A QLineEdit widget that can be used to change the setting. QLineEdit: A QLineEdit widget that can be used to change the setting.
""" """
widget = QLineEdit(str(self.value)) widget = QLineEdit(str(self.value))
widget.setMinimumWidth(100) widget.setMinimumWidth(100)
@ -72,7 +98,16 @@ class Setting(QObject):
class FloatSetting(Setting): class FloatSetting(Setting):
"""A setting that is a Float.""" """A setting that is a Float.
Args:
name (str) : The name of the setting
default : The default value of the setting
description (str) : A description of the setting
validator (QValidator) : A validator for the setting
min_value : The minimum value of the setting
max_value : The maximum value of the setting
"""
DEFAULT_LENGTH = 100 DEFAULT_LENGTH = 100
@ -85,6 +120,7 @@ class FloatSetting(Setting):
min_value: float = None, min_value: float = None,
max_value: float = None, max_value: float = None,
) -> None: ) -> None:
"""Create a new float setting."""
super().__init__(name, description, default) super().__init__(name, description, default)
# If a validator is given, set it for the QLineEdit widget # If a validator is given, set it for the QLineEdit widget
@ -100,6 +136,7 @@ class FloatSetting(Setting):
@property @property
def value(self): def value(self):
"""The value of the setting. In this case, a float."""
return self._value return self._value
@value.setter @value.setter
@ -118,7 +155,16 @@ class FloatSetting(Setting):
class IntSetting(Setting): class IntSetting(Setting):
"""A setting that is an Integer.""" """A setting that is an Integer.
Args:
name (str) : The name of the setting
default : The default value of the setting
description (str) : A description of the setting
validator (QValidator) : A validator for the setting
min_value : The minimum value of the setting
max_value : The maximum value of the setting
"""
def __init__( def __init__(
self, self,
@ -129,6 +175,7 @@ class IntSetting(Setting):
min_value=None, min_value=None,
max_value=None, max_value=None,
) -> None: ) -> None:
"""Create a new int setting."""
super().__init__(name, description, default) super().__init__(name, description, default)
# If a validator is given, set it for the QLineEdit widget # If a validator is given, set it for the QLineEdit widget
@ -146,6 +193,7 @@ class IntSetting(Setting):
@property @property
def value(self): def value(self):
"""The value of the setting. In this case, an int."""
return self._value return self._value
@value.setter @value.setter
@ -166,9 +214,16 @@ class IntSetting(Setting):
class BooleanSetting(Setting): class BooleanSetting(Setting):
"""A setting that is a Boolean.""" """A setting that is a Boolean.
Args:
name (str) : The name of the setting
default : The default value of the setting
description (str) : A description of the setting
"""
def __init__(self, name: str, default: bool, description: str) -> None: def __init__(self, name: str, default: bool, description: str) -> None:
"""Create a new boolean setting."""
super().__init__(name, description, default) super().__init__(name, description, default)
# Overrides the default widget # Overrides the default widget
@ -176,6 +231,7 @@ class BooleanSetting(Setting):
@property @property
def value(self): def value(self):
"""The value of the setting. In this case, a bool."""
return self._value return self._value
@value.setter @value.setter
@ -188,6 +244,7 @@ class BooleanSetting(Setting):
def get_widget(self): def get_widget(self):
"""Return a widget for the setting. """Return a widget for the setting.
This returns a QCheckBox widget. This returns a QCheckBox widget.
Returns: Returns:
@ -202,11 +259,19 @@ class BooleanSetting(Setting):
class SelectionSetting(Setting): class SelectionSetting(Setting):
"""A setting that is a selection from a list of options.""" """A setting that is a selection from a list of options.
Args:
name (str) : The name of the setting
options (list) : A list of options to choose from
default : The default value of the setting
description (str) : A description of the setting
"""
def __init__( def __init__(
self, name: str, options: list, default: str, description: str self, name: str, options: list, default: str, description: str
) -> None: ) -> None:
"""Create a new selection setting."""
super().__init__(name, description, default) super().__init__(name, description, default)
# Check if default is in options # Check if default is in options
if default not in options: if default not in options:
@ -219,6 +284,7 @@ class SelectionSetting(Setting):
@property @property
def value(self): def value(self):
"""The value of the setting. In this case, a string."""
return self._value return self._value
@value.setter @value.setter
@ -237,6 +303,7 @@ class SelectionSetting(Setting):
def get_widget(self): def get_widget(self):
"""Return a widget for the setting. """Return a widget for the setting.
This returns a QComboBox widget. This returns a QComboBox widget.
Returns: Returns:
@ -252,14 +319,22 @@ class SelectionSetting(Setting):
class IPSetting(Setting): class IPSetting(Setting):
"""A setting that is an IP address.""" """A setting that is an IP address.
Args:
name (str) : The name of the setting
default : The default value of the setting
description (str) : A description of the setting
"""
def __init__(self, name: str, default: str, description: str) -> None: def __init__(self, name: str, default: str, description: str) -> None:
"""Create a new IP setting."""
super().__init__(name, description) super().__init__(name, description)
self.value = default self.value = default
@property @property
def value(self): def value(self):
"""The value of the setting. In this case, an IP address."""
return self._value return self._value
@value.setter @value.setter
@ -273,13 +348,21 @@ class IPSetting(Setting):
class StringSetting(Setting): class StringSetting(Setting):
"""A setting that is a string.""" """A setting that is a string.
Args:
name (str) : The name of the setting
default : The default value of the setting
description (str) : A description of the setting
"""
def __init__(self, name: str, default: str, description: str) -> None: def __init__(self, name: str, default: str, description: str) -> None:
"""Create a new string setting."""
super().__init__(name, description, default) super().__init__(name, description, default)
@property @property
def value(self): def value(self):
"""The value of the setting. In this case, a string."""
return self._value return self._value
@value.setter @value.setter

View file

@ -1,3 +1,4 @@
"""Module creation for the spectrometer module."""
from nqrduck.module.module import Module from nqrduck.module.module import Module
from .model import SpectrometerModel from .model import SpectrometerModel
from .view import SpectrometerView from .view import SpectrometerView

View file

@ -1,7 +1,9 @@
"""View for the Spectrometer Module. Careful - this is not the base class for the spectrometer submodules, but the view for the spectrometer module itself."""
import logging import logging
from PyQt6.QtWidgets import QWidget, QToolButton, QToolBar, QMenu from PyQt6.QtWidgets import QWidget, QMenu
from PyQt6.QtGui import QAction from PyQt6.QtGui import QAction
from PyQt6.QtCore import pyqtSlot, pyqtSignal from PyQt6.QtCore import pyqtSlot
from nqrduck.module.module_view import ModuleView from nqrduck.module.module_view import ModuleView
from .widget import Ui_Form from .widget import Ui_Form
@ -9,11 +11,16 @@ logger = logging.getLogger(__name__)
class SpectrometerView(ModuleView): class SpectrometerView(ModuleView):
def __init__(self, module): """The view for the spectrometer module. This class is responsible for displaying the spectrometer module in the main window.
"""This class is the view for the spectrometer module. It contains the menu buttons for the different spectrometers.
It also contains the stacked widget that shows the different spectrometer views. It contains the menu buttons for the different spectrometers and the stacked widget that shows the different spectrometer views.
:param module: The spectrometer module that this view belongs to.
Args:
module (Module) : The spectrometer module that this view belongs to
""" """
def __init__(self, module):
"""Initializes the spectrometer view. It sets up the UI and the stacked widget for the spectrometers."""
super().__init__(module) super().__init__(module)
widget = QWidget() widget = QWidget()
@ -34,8 +41,11 @@ class SpectrometerView(ModuleView):
def on_active_spectrometer_changed(self, module): def on_active_spectrometer_changed(self, module):
"""This method is called when the active spectrometer is changed. """This method is called when the active spectrometer is changed.
It changes the active view in the stacked widget to the one that was just activated. It changes the active view in the stacked widget to the one that was just activated.
:param module: The BaseSpectrometer module that was just activated.
Args:
module (BaseSpectrometer) : The spectrometer module that was just activated
""" """
self._ui_form.stackedWidgetSettings.setCurrentWidget(module.settings_view) self._ui_form.stackedWidgetSettings.setCurrentWidget(module.settings_view)
@ -52,8 +62,11 @@ class SpectrometerView(ModuleView):
def on_spectrometer_widget_changed(self, module): def on_spectrometer_widget_changed(self, module):
"""This method is called when a new spectrometer widget is added to the module. """This method is called when a new spectrometer widget is added to the module.
It adds the widget to the stacked widget and sets it as the current widget. It adds the widget to the stacked widget and sets it as the current widget.
:param widget: The widget that was added to the module.
Args:
module (BaseSpectrometer) : The spectrometer module that was just added
""" """
logger.debug( logger.debug(
"Adding settings widget to stacked widget: %s", module.settings_view "Adding settings widget to stacked widget: %s", module.settings_view
@ -72,7 +85,7 @@ class SpectrometerView(ModuleView):
self._ui_form.stackedWidgetPulseProgrammer.setCurrentWidget( self._ui_form.stackedWidgetPulseProgrammer.setCurrentWidget(
module.model.pulse_programmer.pulse_programmer_view module.model.pulse_programmer.pulse_programmer_view
) )
except AttributeError as e: except AttributeError:
logger.debug( logger.debug(
"No pulse programmer widget to add for spectrometer %s", "No pulse programmer widget to add for spectrometer %s",
module.model.name, module.model.name,
@ -83,7 +96,9 @@ class SpectrometerView(ModuleView):
def on_spectrometer_added(self, module): def on_spectrometer_added(self, module):
"""This method changes the active spectrometer to the one that was just added. """This method changes the active spectrometer to the one that was just added.
:param module: The BaseSpectrometer module that was just added.
Args:
module (BaseSpectrometer) : The spectrometer module that was just added
""" """
module.change_spectrometer.connect(self.on_menu_button_clicked) module.change_spectrometer.connect(self.on_menu_button_clicked)
self.on_spectrometer_widget_changed(module) self.on_spectrometer_widget_changed(module)
@ -119,10 +134,14 @@ class SpectrometerView(ModuleView):
@pyqtSlot(str) @pyqtSlot(str)
def on_menu_button_clicked(self, spectrometer_name): def on_menu_button_clicked(self, spectrometer_name):
"""This method is called when a menu button is clicked. It changes the active spectrometer to the one that was clicked. """This method is called when a menu button is clicked.
It changes the active spectrometer to the one that was clicked.
It also unchecks all other menu buttons. It also unchecks all other menu buttons.
:param spectrometer_name: The name of the spectrometer that was clicked.""" Args:
spectrometer_name (str) : The name of the spectrometer that was clicked
"""
logger.debug("Active module changed to: %s", spectrometer_name) logger.debug("Active module changed to: %s", spectrometer_name)
for action in self._actions.values(): for action in self._actions.values():
action.setChecked(False) action.setChecked(False)