Merge pull request #14 from nqrduck/linting

Linting
This commit is contained in:
Julia P 2024-04-01 10:56:32 +02:00 committed by GitHub
commit 469bba962d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 864 additions and 263 deletions

View file

@ -32,5 +32,31 @@ dependencies = [
"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"]
"nqrduck-spectrometer" = "nqrduck_spectrometer.spectrometer:Spectrometer"

View file

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

View file

@ -1,27 +1,39 @@
"""Base Spectrometer Module."""
from PyQt6.QtCore import pyqtSignal
from nqrduck.module.module import 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)
def __init__(self, model, view, controller):
"""Initializes the spectrometer."""
super().__init__(model, None, controller)
# This stops the view from being added to the main window.
self.view = None
self.settings_view = view(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)
@property
def settings_view(self):
"""The settings view of the spectrometer.
"""
"""The settings view of the spectrometer."""
return self._settings_view
@settings_view.setter
def settings_view(self, value):
self._settings_view = value

View file

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

View file

@ -1,110 +1,127 @@
"""The base class for all spectrometer models."""
import logging
from collections import OrderedDict
from PyQt6.QtCore import pyqtSlot, pyqtSignal, QObject
from PyQt6.QtGui import QPixmap
from nqrduck.module.module_model import ModuleModel
from.settings import Setting
from .settings import Setting
logger = logging.getLogger(__name__)
class BaseSpectrometerModel(ModuleModel):
"""The base class for all spectrometer models.
It contains the settings and pulse parameters of the spectrometer.
Arguments:
module (Module) -- The module that the spectrometer is connected to
Args:
module (Module) : The module that the spectrometer is connected to
Attributes:
settings (OrderedDict) -- The settings of the spectrometer
pulse_parameter_options (OrderedDict) -- The pulse parameter options of the spectrometer
settings (OrderedDict) : The settings of the spectrometer
pulse_parameter_options (OrderedDict) : The pulse parameter options of the spectrometer
"""
settings : OrderedDict
pulse_parameter_options : OrderedDict
settings: OrderedDict
pulse_parameter_options: OrderedDict
class PulseParameter:
"""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.
Arguments:
name (str) -- The name of the pulse parameter
Args:
name (str) : The name of the pulse parameter
Attributes:
name (str) -- The name of the pulse parameter
options (OrderedDict) -- The options of the pulse parameter
name (str) : The name 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.
Arguments:
name (str) -- The name of the pulse parameter
name (str) : The name of the pulse parameter
"""
self.name = name
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
def add_option(self, option : "Option") -> None:
def add_option(self, option: "Option") -> None:
"""Adds an option to the pulse parameter.
Arguments:
option (Option) -- The option to add
Args:
option (Option) : The option to add
"""
self.options.append(option)
def get_options(self) -> list:
""" Gets the options of the pulse parameter.
"""Gets the options of the pulse parameter.
Returns:
list -- The options of the pulse parameter
list : The options of the pulse parameter
"""
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.
Arguments:
name (str) -- The name of the option
Args:
name (str) : The name of the option
Returns:
Option -- The option with the specified name
Option : The option with the specified name
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:
if option.name == name:
return option
raise ValueError("Option with name %s not found" % name)
def __init__(self, module):
"""Initializes the spectrometer model.
Arguments:
module (Module) -- The module that the spectrometer is connected to
Args:
module (Module) : The module that the spectrometer is connected to
"""
super().__init__(module)
self.settings = 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():
self.settings[category] = []
self.settings[category].append(setting)
def get_setting_by_name(self, name : str) -> Setting:
def get_setting_by_name(self, name: str) -> Setting:
"""Gets a setting by its name.
Arguments:
name (str) -- The name of the setting
Args:
name (str) : The name of the setting
Returns:
Setting -- The setting with the specified name
Setting : The setting with the specified name
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 setting in self.settings[category]:
@ -112,28 +129,31 @@ class BaseSpectrometerModel(ModuleModel):
return setting
raise ValueError("Setting with name %s not found" % name)
def add_pulse_parameter_option(self, name : str, pulse_parameter_class : PulseParameter) -> None:
""" Adds a pulse parameter option to the spectrometer.
Arguments:
name (str) -- The name of the pulse parameter
pulse_parameter_class (PulseParameter) -- The pulse parameter class"""
def add_pulse_parameter_option(
self, name: str, pulse_parameter_class: PulseParameter
) -> None:
"""Adds a pulse parameter option to the spectrometer.
Args:
name (str) : The name of the pulse parameter
pulse_parameter_class (PulseParameter) : The pulse parameter class
"""
self.pulse_parameter_options[name] = pulse_parameter_class
@property
def target_frequency(self):
""" The target frequency of the spectrometer in Hz. This is the frequency where the magnetic resonance experiment is performed. """
"""The target frequency of the spectrometer in Hz. This is the frequency where the magnetic resonance experiment is performed."""
raise NotImplementedError
@target_frequency.setter
def target_frequency(self, value):
raise NotImplementedError
@property
def averages(self):
""" The number of averages for the spectrometer."""
"""The number of averages for the spectrometer."""
raise NotImplementedError
@averages.setter
def averages(self, value):
raise NotImplementedError

View file

@ -1,26 +1,34 @@
"""The Base Class for all Spectrometer Views."""
import logging
from pathlib import Path
from PyQt6.QtWidgets import QWidget, QLabel, QLineEdit, QHBoxLayout, QSizePolicy, QSpacerItem, QVBoxLayout, QCheckBox, QComboBox
from PyQt6.QtGui import QIcon
from PyQt6.QtCore import Qt, pyqtSlot
from PyQt6.QtWidgets import (
QWidget,
QLabel,
QHBoxLayout,
QSizePolicy,
QSpacerItem,
QVBoxLayout,
)
from nqrduck.module.module_view import ModuleView
from nqrduck.assets.icons import Logos
from .settings import FloatSetting, IntSetting, BooleanSetting, SelectionSetting, StringSetting
logger = logging.getLogger(__name__)
class BaseSpectrometerView(ModuleView):
"""The View Class for all Spectrometers."""
def __init__(self, module):
"""Initializes the spectrometer view."""
super().__init__(module)
def load_settings_ui(self) -> None:
"""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
widget = QWidget()
widget.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
self._ui_form = Ui_Form()
@ -53,37 +61,20 @@ class BaseSpectrometerView(ModuleView):
setting_label = QLabel(setting.name)
setting_label.setMinimumWidth(200)
# Depending on the setting type we add different widgets to the view
if isinstance(setting, FloatSetting) or isinstance(setting, IntSetting) or isinstance(setting, StringSetting):
edit_widget = QLineEdit(str(setting.value))
edit_widget.setMinimumWidth(100)
# Connect the editingFinished signal to the on_value_changed slot of the setting
edit_widget.editingFinished.connect(lambda x=edit_widget, s=setting: s.on_value_changed(x.text()))
elif isinstance(setting, BooleanSetting):
edit_widget = QCheckBox()
edit_widget.setChecked(setting.value)
edit_widget.stateChanged.connect(lambda x=edit_widget, s=setting: s.on_value_changed(x))
elif isinstance(setting, SelectionSetting):
edit_widget = QComboBox()
edit_widget.addItems(setting.options)
edit_widget.setCurrentText(setting.value)
edit_widget.currentTextChanged.connect(lambda x=edit_widget, s=setting: s.on_value_changed(x))
edit_widget = setting.widget
logger.debug("Setting widget: %s", edit_widget)
# Add a icon that can be used as a tooltip
if setting.description is not None:
logger.debug("Adding tooltip to setting: %s", setting.name)
self_path = Path(__file__).parent
icon = Logos.QuestionMark_16x16()
icon_label = QLabel()
icon_label.setPixmap(icon.pixmap(icon.availableSizes()[0]))
icon_label.setFixedSize(icon.availableSizes()[0])
icon_label.setToolTip(setting.description)
# Add a horizontal layout for the setting
# Add a horizontal layout for the setting
layout = QHBoxLayout()
# Add the label and the line edit to the layout
layout.addItem(spacer)
@ -93,12 +84,9 @@ class BaseSpectrometerView(ModuleView):
layout.addStretch(1)
# Add the layout to the vertical layout of the widget
category_layout.addLayout(layout)
category_layout.addStretch(1)
self._ui_form.gridLayout.addLayout(category_layout, row, column)
# Push all the settings to the top of the widget
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.
from PyQt6 import QtCore, QtGui, QtWidgets
from PyQt6 import QtCore, QtWidgets
class Ui_Form(object):
class Ui_Form:
def setupUi(self, Form):
Form.setObjectName("Form")
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
from PyQt6.QtCore import pyqtSlot
from nqrduck.module.module_controller import ModuleController
from nqrduck.core.main_controller import MainController
from nqrduck_spectrometer.base_spectrometer import BaseSpectrometer
@ -40,6 +44,7 @@ class SpectrometerController(ModuleController):
def process_signals(self, key: str, value: object) -> None:
"""This method processes the signals from the nqrduck module.
It is called by the nqrduck module when a signal is emitted.
It then calls the corresponding method of the spectrometer model.
@ -59,6 +64,7 @@ class SpectrometerController(ModuleController):
def on_loading(self) -> None:
"""This method is called when the module is loaded.
It connects the signals from the spectrometer model to the view.
"""
self._module.model.spectrometer_added.connect(
@ -71,6 +77,7 @@ class SpectrometerController(ModuleController):
def on_measurement_start(self) -> None:
"""This method is called when a measurement is started.
It calls the on_measurement_start method of the active spectrometer.
"""
logger.debug(

View file

@ -1,14 +1,25 @@
"""Class for handling measurement data."""
import logging
import numpy as np
from nqrduck.helpers.signalprocessing import SignalProcessing as sp
logger = logging.getLogger(__name__)
class Measurement():
"""This class defines how measurement data should look.
class Measurement:
"""This class defines how measurement data should look.
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.
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:
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.
@ -19,7 +30,15 @@ class Measurement():
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.tdy = tdy
self.target_frequency = target_frequency
@ -30,33 +49,35 @@ class Measurement():
def to_json(self):
"""Converts the measurement to a json-compatible format.
Returns:
dict -- The measurement in json-compatible format.
dict : The measurement in json-compatible format.
"""
return {
"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,
"IF_frequency": self.IF_frequency
"IF_frequency": self.IF_frequency,
}
@classmethod
def from_json(cls, json):
def from_json(cls, json: dict):
"""Converts the json format to a measurement.
Arguments:
json (dict) -- The measurement in json-compatible format.
Args:
json (dict) : The measurement in json-compatible format.
Returns:
Measurement -- The measurement.
Measurement : The measurement.
"""
tdy = np.array([complex(y[0], y[1]) for y in json["tdy"]])
return cls(
np.array(json["tdx"]),
tdy,
target_frequency = json["target_frequency"],
IF_frequency = json["IF_frequency"]
target_frequency=json["target_frequency"],
IF_frequency=json["IF_frequency"],
)
# Measurement data
@ -64,7 +85,7 @@ class Measurement():
def tdx(self):
"""Time axis for the x axis of the measurement data."""
return self._tdx
@tdx.setter
def tdx(self, value):
self._tdx = value
@ -73,7 +94,7 @@ class Measurement():
def tdy(self):
"""Time axis for the y axis of the measurement data."""
return self._tdy
@tdy.setter
def tdy(self, value):
self._tdy = value
@ -82,7 +103,7 @@ class Measurement():
def fdx(self):
"""Frequency axis for the x axis of the measurement data."""
return self._fdx
@fdx.setter
def fdx(self, value):
self._fdx = value
@ -91,7 +112,7 @@ class Measurement():
def fdy(self):
"""Frequency axis for the y axis of the measurement data."""
return self._fdy
@fdy.setter
def fdy(self, value):
self._fdy = value
@ -101,7 +122,7 @@ class Measurement():
def target_frequency(self):
"""Target frequency of the measurement."""
return self._target_frequency
@target_frequency.setter
def target_frequency(self, value):
self._target_frequency = value

View file

@ -1,3 +1,5 @@
"""The model for the nqrduck-spectrometer module. This module is responsible for managing the spectrometers."""
import logging
from PyQt6.QtCore import pyqtSignal
from nqrduck.module.module_model import ModuleModel
@ -5,39 +7,62 @@ from .base_spectrometer import BaseSpectrometer
logger = logging.getLogger(__name__)
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)
active_spectrometer_changed = pyqtSignal(BaseSpectrometer)
def __init__(self, module) -> None:
"""Initializes the spectrometer model."""
super().__init__(module)
self._active_spectrometer = None
self._available_spectrometers = dict()
@property
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
@active_spectrometer.setter
def active_spectrometer(self, value):
self._active_spectrometer = value
self.active_spectrometer_changed.emit(value)
spectrometer_module_name = value.model.toolbar_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
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
def add_spectrometers(self, spectrometer_module_name, module):
self._available_spectrometers [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
logger.debug("Added module: %s", spectrometer_module_name)
self.spectrometer_added.emit(module)
self.active_spectrometer = module
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
import logging
import numpy as np
import sympy
from decimal import Decimal
from pathlib import Path
from PyQt6.QtGui import QPixmap
from nqrduck.contrib.mplwidget import MplWidget
from nqrduck.helpers.signalprocessing import SignalProcessing as sp
@ -14,6 +19,22 @@ logger = logging.getLogger(__name__)
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
parameters: list
expression: str | sympy.Expr
@ -22,6 +43,7 @@ class Function:
end_x: float
def __init__(self, expr) -> None:
"""Initializes the function."""
self.parameters = []
self.expr = expr
self.resolution = Decimal(1 / 30.72e6)
@ -42,7 +64,7 @@ class Function:
t = np.linspace(0, float(pulse_length), n)
return t
def evaluate(self, pulse_length: Decimal, resolution : Decimal = None) -> np.ndarray:
def evaluate(self, pulse_length: Decimal, resolution: Decimal = None) -> np.ndarray:
"""Evaluates the function for the given pulse length.
Args:
@ -121,7 +143,9 @@ class Function:
mpl_widget.canvas.ax.grid(True)
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.
Args:
@ -133,11 +157,12 @@ class Function:
"""
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.
Args:
parameter (Function.Parameter): The parameter to add."""
parameter (Function.Parameter): The parameter to add.
"""
self.parameters.append(parameter)
def to_json(self) -> dict:
@ -156,7 +181,7 @@ class Function:
}
@classmethod
def from_json(cls, data: dict) -> "Function":
def from_json(cls, data: dict) -> Function:
"""Creates a function from a json representation.
Args:
@ -193,7 +218,7 @@ class Function:
if isinstance(expr, str):
try:
self._expr = sympy.sympify(expr)
except:
except ValueError:
logger.error("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):
@ -208,7 +233,7 @@ class Function:
def resolution(self, resolution):
try:
self._resolution = Decimal(resolution)
except:
except ValueError:
logger.error("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):
try:
self._start_x = float(start_x)
except:
except ValueError:
logger.error("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):
try:
self._end_x = float(end_x)
except:
except ValueError:
logger.error("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.
Returns:
QPixmap -- The default pixmap for every function"""
QPixmap : The default pixmap for every function
"""
pixmap = PulseParamters.TXCustom()
return pixmap
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:
"""Initializes the parameter."""
self.name = name
self.symbol = symbol
self.value = value
@ -296,20 +339,31 @@ class RectFunction(Function):
name = "Rectangular"
def __init__(self) -> None:
"""Initializes the RecFunction."""
expr = sympy.sympify("1")
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()
return pixmap
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"
def __init__(self) -> None:
"""Initializes the SincFunction."""
expr = sympy.sympify("sin(x * l)/ (x * l)")
super().__init__(expr)
self.add_parameter(Function.Parameter("Scale Factor", "l", 2))
@ -317,16 +371,26 @@ class SincFunction(Function):
self.end_x = np.pi
def get_pixmap(self):
"""Returns the pixmap of the sinc function.
Returns:
QPixmap: The pixmap of the sinc function.
"""
pixmap = PulseParamters.TXSinc()
return pixmap
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"
def __init__(self) -> None:
"""Initializes the GaussianFunction."""
expr = sympy.sympify("exp(-0.5 * ((x - mu) / sigma)**2)")
super().__init__(expr)
self.add_parameter(Function.Parameter("Mean", "mu", 0))
@ -335,6 +399,11 @@ class GaussianFunction(Function):
self.end_x = np.pi
def get_pixmap(self):
"""Returns the QPixmap of the Gaussian function.
Returns:
QPixmap: The QPixmap of the Gaussian function.
"""
pixmap = PulseParamters.TXGauss()
return pixmap
@ -351,25 +420,55 @@ class CustomFunction(Function):
name = "Custom"
def __init__(self) -> None:
"""Initializes the Custom Function."""
expr = sympy.sympify(" 2 * x**2 + 3 * x + 1")
super().__init__(expr)
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:
"""Initializes the option."""
self.name = name
self.value = value
def set_value(self):
"""Sets the value of the option.
This method has to be implemented in the derived classes.
"""
raise NotImplementedError
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}
@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__():
if subclass.TYPE == data["type"]:
cls = subclass
@ -390,6 +489,7 @@ class BooleanOption(Option):
TYPE = "Boolean"
def set_value(self, value):
"""Sets the value of the option."""
self.value = value
@ -399,49 +499,102 @@ class NumericOption(Option):
TYPE = "Numeric"
def set_value(self, value):
"""Sets the value of the option."""
self.value = float(value)
class FunctionOption(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"
def __init__(self, name, functions) -> None:
"""Initializes the FunctionOption."""
super().__init__(name, functions[0])
self.functions = functions
def set_value(self, value):
"""Sets the value of the option.
Args:
value: The value of the option.
"""
self.value = value
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:
if function.name == name:
return function
raise ValueError("Function with name %s not found" % name)
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}
@classmethod
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__()]
obj = cls(data["name"], functions)
obj.value = Function.from_json(data["value"])
return obj
def get_pixmap(self):
"""Returns the pixmap of the function."""
return self.value.get_pixmap()
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"
TX_PHASE = "TX Phase"
TX_PULSE_SHAPE = "TX Pulse Shape"
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)
self.add_option(NumericOption(self.RELATIVE_AMPLITUDE, 0))
self.add_option(NumericOption(self.TX_PHASE, 0))
@ -453,6 +606,11 @@ class TXPulse(BaseSpectrometerModel.PulseParameter):
)
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:
return self.get_option_by_name(self.TX_PULSE_SHAPE).get_pixmap()
else:
@ -461,15 +619,32 @@ class TXPulse(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"
def __init__(self, name) -> None:
"""Initializes the RX Readout PulseParameter.
It adds an option for the RX Readout state.
"""
super().__init__(name)
self.add_option(BooleanOption(self.RX, False))
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()
else:
pixmap = PulseParamters.RXOn()
@ -477,14 +652,32 @@ class RXReadout(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"
def __init__(self, name) -> None:
"""Initializes the Gate PulseParameter.
It adds an option for the Gate state.
"""
super().__init__(name)
self.add_option(BooleanOption(self.GATE_STATE, False))
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()
else:
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
from collections import OrderedDict
from nqrduck.helpers.unitconverter import UnitConverter
@ -7,34 +9,70 @@ logger = logging.getLogger(__name__)
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:
"""Initializes the pulse sequence."""
self.name = name
self.events = 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]
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:
"""Initializes the event."""
self.parameters = OrderedDict()
self.name = name
self.duration = duration
def add_parameter(self, parameter) -> None:
"""Adds a parameter to the event.
Args:
parameter: The parameter to add
"""
self.parameters.append(parameter)
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)
self.duration = duration
@classmethod
def load_event(cls, event, pulse_parameter_options):
"""
Loads an event from a dict. The pulse paramter options are needed to load the parameters
"""Loads an event from a dict.
The pulse paramter options are needed to load the parameters
and determine if the correct spectrometer is active.
Args:
@ -63,13 +101,14 @@ class PulseSequence:
)
return obj
@property
def duration(self):
"""The duration of the event."""
return self._duration
@duration.setter
def duration(self, duration : str):
def duration(self, duration: str):
# Duration needs to be a positive number
try:
duration = UnitConverter.to_decimal(duration)
@ -77,15 +116,15 @@ class PulseSequence:
raise ValueError("Duration needs to be a number")
if duration < 0:
raise ValueError("Duration needs to be a positive number")
self._duration = duration
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:
dict: The dict with the sequence data"""
dict: The dict with the sequence data
"""
data = {"name": self.name, "events": []}
for event in self.events:
event_data = {
@ -102,7 +141,9 @@ class PulseSequence:
@classmethod
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.
Args:
@ -120,54 +161,62 @@ class PulseSequence:
obj.events.append(cls.Event.load_event(event_data, pulse_parameter_options))
return obj
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.
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
been executed with all values in the list."""
been executed with all values in the list.
"""
@property
def name(self):
"""The name of the variable."""
return self._name
@name.setter
def name(self, name : str):
def name(self, name: str):
if not isinstance(name, str):
raise TypeError("Name needs to be a string")
self._name = name
@property
def values(self):
"""The values of the variable. This is a list of values that the variable can take."""
return self._values
@values.setter
def values(self, values : list):
def values(self, values: list):
if not isinstance(values, list):
raise TypeError("Values needs to be a list")
self._values = values
class VariableGroup:
""" 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."""
"""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.
"""
@property
def name(self):
"""The name of the variable group."""
return self._name
@name.setter
def name(self, name : str):
def name(self, name: str):
if not isinstance(name, str):
raise TypeError("Name needs to be a string")
self._name = name
@property
def variables(self):
"""The variables in the group. This is a list of variables."""
return self._variables
@variables.setter
def variables(self, variables : list):
def variables(self, variables: list):
if not isinstance(variables, list):
raise TypeError("Variables needs to be a list")
self._variables = variables
self._variables = variables

View file

@ -1,117 +1,342 @@
"""Settings for the different spectrometers."""
import logging
import ipaddress
from PyQt6.QtCore import QObject, pyqtSignal, pyqtSlot
from PyQt6.QtWidgets import QLineEdit, QComboBox, QCheckBox
from PyQt6.QtGui import QValidator
from nqrduck.helpers.validators import DuckIntValidator, DuckFloatValidator
logger = logging.getLogger(__name__)
class Setting(QObject):
"""A setting for the spectrometer is a value that is the same for all events in a pulse sequence.
E.g. the number of averages or the number of points in a spectrum."""
settings_changed = pyqtSignal()
"""A setting for the spectrometer is a value that is the same for all events in a pulse sequence.
def __init__(self, name, description) -> None:
super().__init__()
self.name = name
self.description = description
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()
def __init__(self, name: str, description: str, default=None) -> None:
"""Create a new setting.
Args:
name (str): The name of the setting.
description (str): A description of the setting.
default: The default value of the setting.
"""
super().__init__()
self.name = name
self.description = description
if default is not None:
self.value = default
# This can be overridden by subclasses
self.widget = self.get_widget()
@pyqtSlot(str)
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)
self.value = value
self.settings_changed.emit()
def get_setting(self):
"""Return the value of the setting.
Returns:
The value of the setting.
"""
return float(self.value)
def get_widget(self):
"""Return a widget for the setting.
The default widget is simply a QLineEdit.
This method can be overwritten by subclasses to return a different widget.
Returns:
QLineEdit: A QLineEdit widget that can be used to change the setting.
"""
widget = QLineEdit(str(self.value))
widget.setMinimumWidth(100)
widget.editingFinished.connect(
lambda x=widget, s=self: s.on_value_changed(x.text())
)
return widget
def update_widget_style(self):
"""Update the style of the QLineEdit widget to indicate if the value is valid."""
if (
self.validator.validate(self.widget.text(), 0)
== QValidator.State.Acceptable
):
self.widget.setStyleSheet("QLineEdit { background-color: white; }")
elif (
self.validator.validate(self.widget.text(), 0)
== QValidator.State.Intermediate
):
self.widget.setStyleSheet("QLineEdit { background-color: yellow; }")
else:
self.widget.setStyleSheet("QLineEdit { background-color: red; }")
@pyqtSlot(str)
def on_value_changed(self, value):
logger.debug("Setting %s changed to %s", self.name, value)
self.value = value
self.settings_changed.emit()
def get_setting(self):
return float(self.value)
class FloatSetting(Setting):
""" A setting that is a Float. """
def __init__(self, name : str, default : float, description : str) -> None:
super().__init__(name, description)
self.value = default
"""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
def __init__(
self,
name: str,
default: float,
description: str,
validator: QValidator = None,
min_value: float = None,
max_value: float = None,
) -> None:
"""Create a new float setting."""
super().__init__(name, description, default)
# If a validator is given, set it for the QLineEdit widget
if validator:
self.validator = validator
else:
self.validator = DuckFloatValidator(self, min_value, max_value)
self.widget = self.get_widget()
# self.widget.setValidator(self.validator)
# Connect the update_widget_style method to the textChanged signal
self.widget.textChanged.connect(self.update_widget_style)
@property
def value(self):
"""The value of the setting. In this case, a float."""
return self._value
@value.setter
def value(self, value):
try:
self._value = float(value)
if self.validator.validate(value, 0) == QValidator.State.Acceptable:
self._value = float(value)
self.settings_changed.emit()
# This should never be reached because the validator should prevent this
except ValueError:
raise ValueError("Value must be a float")
self.settings_changed.emit()
# This happens when the validator has not yet been set
except AttributeError:
self._value = float(value)
self.settings_changed.emit()
class IntSetting(Setting):
""" A setting that is an Integer."""
def __init__(self, name : str, default : int, description : str) -> None:
super().__init__(name, description)
self.value = default
"""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__(
self,
name: str,
default: int,
description: str,
validator: QValidator = None,
min_value=None,
max_value=None,
) -> None:
"""Create a new int setting."""
super().__init__(name, description, default)
# If a validator is given, set it for the QLineEdit widget
if validator:
self.validator = validator
else:
self.validator = DuckIntValidator(self, min_value, max_value)
self.widget = self.get_widget()
# Connect the update_widget_style method to the textChanged signal
self.widget.textChanged.connect(self.update_widget_style)
self.min_value = min_value
self.max_value = max_value
@property
def value(self):
"""The value of the setting. In this case, an int."""
return self._value
@value.setter
def value(self, value):
try:
self._value = int(value)
if self.validator.validate(value, 0) == QValidator.State.Acceptable:
value = int(float(value))
self._value = value
self.settings_changed.emit()
except ValueError:
raise ValueError("Value must be an int")
self.settings_changed.emit()
# This happens when the validator has not yet been set
except AttributeError as e:
logger.debug(e)
self._value = int(float(value))
self.settings_changed.emit()
class BooleanSetting(Setting):
""" A setting that is a Boolean."""
def __init__(self, name : str, default : bool, description : str) -> None:
super().__init__(name, description)
self.value = default
"""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:
"""Create a new boolean setting."""
super().__init__(name, description, default)
# Overrides the default widget
self.widget = self.get_widget()
@property
def value(self):
"""The value of the setting. In this case, a bool."""
return self._value
@value.setter
def value(self, value):
try:
self._value = bool(value)
self.settings_changed.emit()
except ValueError:
raise ValueError("Value must be a bool")
self.settings_changed.emit()
def get_widget(self):
"""Return a widget for the setting.
This returns a QCheckBox widget.
Returns:
QCheckBox: A QCheckBox widget that can be used to change the setting.
"""
widget = QCheckBox()
widget.setChecked(self.value)
widget.stateChanged.connect(
lambda x=widget, s=self: s.on_value_changed(bool(x))
)
return widget
class SelectionSetting(Setting):
""" A setting that is a selection from a list of options."""
def __init__(self, name : str, options : list, default : str, description : str) -> None:
super().__init__(name, description)
"""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__(
self, name: str, options: list, default: str, description: str
) -> None:
"""Create a new selection setting."""
super().__init__(name, description, default)
# Check if default is in options
if default not in options:
raise ValueError("Default value must be one of the options")
self.options = options
self.value = default
# Overrides the default widget
self.widget = self.get_widget()
@property
def value(self):
"""The value of the setting. In this case, a string."""
return self._value
@value.setter
def value(self, value):
if value in self.options:
try:
if value in self.options:
self._value = value
self.settings_changed.emit()
else:
raise ValueError("Value must be one of the options")
# This fixes a bug when creating the widget when the options are not yet set
except AttributeError:
self._value = value
else:
raise ValueError("Value must be one of the options")
self.settings_changed.emit()
self.options = [value]
self.settings_changed.emit()
def get_widget(self):
"""Return a widget for the setting.
This returns a QComboBox widget.
Returns:
QComboBox: A QComboBox widget that can be used to change the setting.
"""
widget = QComboBox()
widget.addItems(self.options)
widget.setCurrentText(self.value)
widget.currentTextChanged.connect(
lambda x=widget, s=self: s.on_value_changed(x)
)
return widget
class IPSetting(Setting):
""" A setting that is an IP address."""
def __init__(self, name : str, default : str, description : str) -> None:
"""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:
"""Create a new IP setting."""
super().__init__(name, description)
self.value = default
@property
def value(self):
"""The value of the setting. In this case, an IP address."""
return self._value
@value.setter
def value(self, value):
try:
@ -121,21 +346,29 @@ class IPSetting(Setting):
raise ValueError("Value must be a valid IP address")
self.settings_changed.emit()
class StringSetting(Setting):
""" A setting that is a string."""
def __init__(self, name : str, default : str, description : str) -> None:
super().__init__(name, description)
self.value = default
"""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:
"""Create a new string setting."""
super().__init__(name, description, default)
@property
def value(self):
"""The value of the setting. In this case, a string."""
return self._value
@value.setter
def value(self, value):
try:
self._value = str(value)
self.settings_changed.emit()
except ValueError:
raise ValueError("Value must be a string")
self.settings_changed.emit()

View file

@ -1,3 +1,4 @@
"""Module creation for the spectrometer module."""
from nqrduck.module.module import Module
from .model import SpectrometerModel
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
from PyQt6.QtWidgets import QWidget, QToolButton, QToolBar, QMenu
from PyQt6.QtWidgets import QWidget, QMenu
from PyQt6.QtGui import QAction
from PyQt6.QtCore import pyqtSlot, pyqtSignal
from PyQt6.QtCore import pyqtSlot
from nqrduck.module.module_view import ModuleView
from .widget import Ui_Form
@ -9,11 +11,16 @@ logger = logging.getLogger(__name__)
class SpectrometerView(ModuleView):
"""The view for the spectrometer module. This class is responsible for displaying the spectrometer module in the main window.
It contains the menu buttons for the different spectrometers and the stacked widget that shows the different spectrometer views.
Args:
module (Module) : The spectrometer module that this view belongs to
"""
def __init__(self, module):
"""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.
:param module: The spectrometer module that this view belongs to.
"""
"""Initializes the spectrometer view. It sets up the UI and the stacked widget for the spectrometers."""
super().__init__(module)
widget = QWidget()
@ -25,17 +32,20 @@ class SpectrometerView(ModuleView):
self.blank = QWidget()
self._ui_form.stackedWidgetSettings.setStyleSheet(
"QStackedWidget { background-color: #fafafa; border: 2px solid #000; }"
"QStackedWidget { border: 2px solid #000; }"
)
self._ui_form.stackedWidgetPulseProgrammer.setStyleSheet(
"QStackedWidget { background-color: #fafafa; border: 2px solid #000; }"
"QStackedWidget { border: 2px solid #000; }"
)
def on_active_spectrometer_changed(self, module):
"""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.
: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)
@ -52,8 +62,11 @@ class SpectrometerView(ModuleView):
def on_spectrometer_widget_changed(self, 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.
:param widget: The widget that was added to the module.
Args:
module (BaseSpectrometer) : The spectrometer module that was just added
"""
logger.debug(
"Adding settings widget to stacked widget: %s", module.settings_view
@ -72,7 +85,7 @@ class SpectrometerView(ModuleView):
self._ui_form.stackedWidgetPulseProgrammer.setCurrentWidget(
module.model.pulse_programmer.pulse_programmer_view
)
except AttributeError as e:
except AttributeError:
logger.debug(
"No pulse programmer widget to add for spectrometer %s",
module.model.name,
@ -83,7 +96,9 @@ class SpectrometerView(ModuleView):
def on_spectrometer_added(self, module):
"""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)
self.on_spectrometer_widget_changed(module)
@ -119,10 +134,14 @@ class SpectrometerView(ModuleView):
@pyqtSlot(str)
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.
: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)
for action in self._actions.values():
action.setChecked(False)