Merge branch 'formbuilder-and-function-optimization'

This commit is contained in:
jupfi 2024-04-27 19:39:56 +02:00
commit 668681cdf3
5 changed files with 104 additions and 35 deletions

View file

@ -0,0 +1 @@
"""Empty file to make this directory a package."""

View file

@ -1,3 +1,4 @@
"""Controller of the pulse programmer module."""
import logging import logging
import json import json
import decimal import decimal
@ -8,9 +9,13 @@ from nqrduck_spectrometer.pulsesequence import PulseSequence
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class PulseProgrammerController(ModuleController):
def on_loading(self, pulse_parameter_options : dict) -> None: class PulseProgrammerController(ModuleController):
"""Controller of the pulse programmer module.
This class is responsible for handling the logic of the pulse programmer module.
"""
def on_loading(self, pulse_parameter_options: dict) -> None:
"""This method is called when the module is loaded. It sets the pulse parameter options in the model. """This method is called when the module is loaded. It sets the pulse parameter options in the model.
Args: Args:
@ -20,7 +25,7 @@ class PulseProgrammerController(ModuleController):
self.module.model.pulse_parameter_options = pulse_parameter_options self.module.model.pulse_parameter_options = pulse_parameter_options
@pyqtSlot(str) @pyqtSlot(str)
def delete_event(self, event_name : str) -> None: def delete_event(self, event_name: str) -> None:
"""This method deletes an event from the pulse sequence. """This method deletes an event from the pulse sequence.
Args: Args:
@ -34,7 +39,7 @@ class PulseProgrammerController(ModuleController):
self.module.model.events_changed.emit() self.module.model.events_changed.emit()
@pyqtSlot(str, str) @pyqtSlot(str, str)
def change_event_name(self, old_name : str, new_name : str) -> None: def change_event_name(self, old_name: str, new_name: str) -> None:
"""This method changes the name of an event. """This method changes the name of an event.
Args: Args:
@ -49,7 +54,7 @@ class PulseProgrammerController(ModuleController):
self.module.model.events_changed.emit() self.module.model.events_changed.emit()
@pyqtSlot(str, str) @pyqtSlot(str, str)
def change_event_duration(self, event_name:str, duration) -> None: def change_event_duration(self, event_name: str, duration) -> None:
"""This method changes the duration of an event. """This method changes the duration of an event.
Args: Args:
@ -65,7 +70,9 @@ class PulseProgrammerController(ModuleController):
except decimal.InvalidOperation: except decimal.InvalidOperation:
logger.error("Duration must be a positive number") logger.error("Duration must be a positive number")
# Emit signal to the nqrduck core to show an error message # Emit signal to the nqrduck core to show an error message
self.module.nqrduck_signal.emit("notification", ["Error", "Duration must be a positive number"]) self.module.nqrduck_signal.emit(
"notification", ["Error", "Duration must be a positive number"]
)
break break
self.module.model.events_changed.emit() self.module.model.events_changed.emit()
@ -80,13 +87,18 @@ class PulseProgrammerController(ModuleController):
for i, event in enumerate(self.module.model.pulse_sequence.events): for i, event in enumerate(self.module.model.pulse_sequence.events):
if event.name == event_name: if event.name == event_name:
if i > 0: if i > 0:
self.module.model.pulse_sequence.events[i], self.module.model.pulse_sequence.events[i-1] = self.module.model.pulse_sequence.events[i-1], self.module.model.pulse_sequence.events[i] (
self.module.model.pulse_sequence.events[i],
self.module.model.pulse_sequence.events[i - 1],
) = (
self.module.model.pulse_sequence.events[i - 1],
self.module.model.pulse_sequence.events[i],
)
break break
self.module.model.events_changed.emit() self.module.model.events_changed.emit()
@pyqtSlot(str) @pyqtSlot(str)
def on_move_event_right(self, event_name : str) -> None: def on_move_event_right(self, event_name: str) -> None:
"""This method moves the event one position to the right if possible. """This method moves the event one position to the right if possible.
Args: Args:
@ -96,11 +108,17 @@ class PulseProgrammerController(ModuleController):
for i, event in enumerate(self.module.model.pulse_sequence.events): for i, event in enumerate(self.module.model.pulse_sequence.events):
if event.name == event_name: if event.name == event_name:
if i < len(self.module.model.pulse_sequence.events) - 1: if i < len(self.module.model.pulse_sequence.events) - 1:
self.module.model.pulse_sequence.events[i], self.module.model.pulse_sequence.events[i+1] = self.module.model.pulse_sequence.events[i+1], self.module.model.pulse_sequence.events[i] (
self.module.model.pulse_sequence.events[i],
self.module.model.pulse_sequence.events[i + 1],
) = (
self.module.model.pulse_sequence.events[i + 1],
self.module.model.pulse_sequence.events[i],
)
break break
self.module.model.events_changed.emit() self.module.model.events_changed.emit()
def save_pulse_sequence(self, path :str) -> None: def save_pulse_sequence(self, path: str) -> None:
"""This method saves the pulse sequence to a file. """This method saves the pulse sequence to a file.
Args: Args:
@ -117,8 +135,7 @@ class PulseProgrammerController(ModuleController):
with open(path, "w") as file: with open(path, "w") as file:
file.write(json.dumps(sequence, cls=DecimalEncoder)) file.write(json.dumps(sequence, cls=DecimalEncoder))
def load_pulse_sequence(self, path: str) -> None:
def load_pulse_sequence(self, path : str) -> None:
"""This method loads a pulse sequence from a file. """This method loads a pulse sequence from a file.
Args: Args:
@ -131,7 +148,9 @@ class PulseProgrammerController(ModuleController):
sequence = json.loads(sequence) sequence = json.loads(sequence)
loaded_sequence = PulseSequence.load_sequence(sequence, self.module.model.pulse_parameter_options) loaded_sequence = PulseSequence.load_sequence(
sequence, self.module.model.pulse_parameter_options
)
self.module.model.pulse_sequence = loaded_sequence self.module.model.pulse_sequence = loaded_sequence
self.module.model.events_changed.emit() self.module.model.events_changed.emit()

View file

@ -1,3 +1,4 @@
"""Model for the pulse programmer module."""
import logging import logging
from collections import OrderedDict from collections import OrderedDict
from PyQt6.QtCore import pyqtSignal from PyQt6.QtCore import pyqtSignal
@ -8,11 +9,25 @@ logger = logging.getLogger(__name__)
class PulseProgrammerModel(ModuleModel): class PulseProgrammerModel(ModuleModel):
"""Model for the pulse programmer module.
This class is responsible for storing the data of the pulse programmer module.
Signals:
pulse_parameter_options_changed: Emitted when the pulse parameter options change.
events_changed: Emitted when the events in the pulse sequence change.
pulse_sequence_changed: Emitted when the pulse sequence changes.
"""
pulse_parameter_options_changed = pyqtSignal() pulse_parameter_options_changed = pyqtSignal()
events_changed = pyqtSignal() events_changed = pyqtSignal()
pulse_sequence_changed = pyqtSignal() pulse_sequence_changed = pyqtSignal()
def __init__(self, module): def __init__(self, module):
"""Initializes the pulse programmer model.
Args:
module (Module): The module to which this model belongs.
"""
super().__init__(module) super().__init__(module)
self.pulse_parameter_options = OrderedDict() self.pulse_parameter_options = OrderedDict()
self.pulse_sequence = PulseSequence("Untitled pulse sequence") self.pulse_sequence = PulseSequence("Untitled pulse sequence")
@ -25,7 +40,7 @@ class PulseProgrammerModel(ModuleModel):
duration (float): The duration of the event in µs. Defaults to 20. duration (float): The duration of the event in µs. Defaults to 20.
""" """
self.pulse_sequence.events.append( self.pulse_sequence.events.append(
PulseSequence.Event(event_name, "%.16gu" % float(duration)) PulseSequence.Event(event_name, f"{float(duration):.16g}u")
) )
logger.debug( logger.debug(
"Creating event %s with object id %s", "Creating event %s with object id %s",
@ -50,6 +65,7 @@ class PulseProgrammerModel(ModuleModel):
@property @property
def pulse_parameter_options(self): def pulse_parameter_options(self):
"""dict: The pulse parameter options."""
return self._pulse_parameter_options return self._pulse_parameter_options
@pulse_parameter_options.setter @pulse_parameter_options.setter
@ -60,6 +76,7 @@ class PulseProgrammerModel(ModuleModel):
@property @property
def pulse_sequence(self): def pulse_sequence(self):
"""PulseSequence: The pulse sequence."""
return self._pulse_sequence return self._pulse_sequence
@pulse_sequence.setter @pulse_sequence.setter

View file

@ -1,10 +1,19 @@
"""Initialize the PulseProgrammer module."""
from nqrduck.module.module import Module from nqrduck.module.module import Module
from .model import PulseProgrammerModel from .model import PulseProgrammerModel
from .controller import PulseProgrammerController from .controller import PulseProgrammerController
from .view import PulseProgrammerView from .view import PulseProgrammerView
class PulseProgrammer(Module): class PulseProgrammer(Module):
"""The pulse programmer module."""
def __init__(self, model, view, controller): def __init__(self, model, view, controller):
"""Initializes the pulse programmer module.
Args:
model (PulseProgrammerModel): The model of the pulse programmer module.
view (PulseProgrammerView): The view of the pulse programmer module.
controller (PulseProgrammerController): The controller of the pulse programmer module.
"""
super().__init__(model, None, controller) super().__init__(model, None, controller)
self.view = None self.view = None
self.pulse_programmer_view = view(self) self.pulse_programmer_view = view(self)

View file

@ -1,3 +1,5 @@
"""This module contains the view for the pulse programmer module. It is responsible for displaying the pulse sequence and the pulse parameter options."""
import logging import logging
import functools import functools
from PyQt6.QtGui import QValidator from PyQt6.QtGui import QValidator
@ -29,17 +31,21 @@ from nqrduck.helpers.formbuilder import (
DuckFormBuilder, DuckFormBuilder,
DuckFormFunctionSelectionField, DuckFormFunctionSelectionField,
DuckFormCheckboxField, DuckFormCheckboxField,
DuckFormDropdownField,
DuckFormFloatField, DuckFormFloatField,
DuckFormIntField,
) )
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class PulseProgrammerView(ModuleView): class PulseProgrammerView(ModuleView):
"""View for the pulse programmer module."""
def __init__(self, module): def __init__(self, module):
"""Initializes the pulse programmer view.
Args:
module (Module): The module to which this view belongs.
"""
super().__init__(module) super().__init__(module)
self.setup_pulsetable() self.setup_pulsetable()
@ -58,11 +64,9 @@ class PulseProgrammerView(ModuleView):
pass pass
def setup_pulsetable(self) -> None: def setup_pulsetable(self) -> None:
"""Setup the table for the pulse sequence. Also add buttons for saving and loading pulse sequences and editing and creation of events""" """Setup the table for the pulse sequence. Also add buttons for saving and loading pulse sequences and editing and creation of events."""
# Create pulse table # Create pulse table
self.title = QLabel( self.title = QLabel(f"Pulse Sequence: {self.module.model.pulse_sequence.name}")
"Pulse Sequence: %s" % self.module.model.pulse_sequence.name
)
# Make title bold # Make title bold
font = self.title.font() font = self.title.font()
font.setBold(True) font.setBold(True)
@ -133,7 +137,7 @@ class PulseProgrammerView(ModuleView):
logger.debug( logger.debug(
"Updating pulse sequence to %s", self.module.model.pulse_sequence.name "Updating pulse sequence to %s", self.module.model.pulse_sequence.name
) )
self.title.setText("Pulse Sequence: %s" % self.module.model.pulse_sequence.name) self.title.setText(f"Pulse Sequence: {self.module.model.pulse_sequence.name}")
@pyqtSlot() @pyqtSlot()
def on_pulse_parameter_options_changed(self) -> None: def on_pulse_parameter_options_changed(self) -> None:
@ -177,9 +181,7 @@ class PulseProgrammerView(ModuleView):
for event in self.module.model.pulse_sequence.events: for event in self.module.model.pulse_sequence.events:
logger.debug("Adding event to pulseprogrammer view: %s", event.name) logger.debug("Adding event to pulseprogrammer view: %s", event.name)
# Create a label for the event # Create a label for the event
event_label = QLabel( event_label = QLabel(f"{event.name} : {event.duration * 1e6:.16g} µs")
"%s : %.16g µs" % (event.name, (event.duration * 1e6))
)
event_layout.addWidget(event_label) event_layout.addWidget(event_label)
# Delete the old widget and create a new one # Delete the old widget and create a new one
@ -260,7 +262,14 @@ class PulseProgrammerView(ModuleView):
@pyqtSlot() @pyqtSlot()
def on_table_button_clicked(self, event, parameter) -> None: def on_table_button_clicked(self, event, parameter) -> None:
"""This method is called whenever a button in the pulse table is clicked. It opens a dialog to set the options for the parameter.""" """This method is called whenever a button in the pulse table is clicked.
It opens a dialog to set the options for the parameter.
Args:
event (PulseSequence.Event): The event for which the parameter options should be set.
parameter (str): The name of the parameter for which the options should be set.
"""
logger.debug("Button for event %s and parameter %s clicked", event, parameter) logger.debug("Button for event %s and parameter %s clicked", event, parameter)
# Create a QDialog to set the options for the parameter. # Create a QDialog to set the options for the parameter.
description = f"Set options for {parameter}" description = f"Set options for {parameter}"
@ -346,9 +355,17 @@ class PulseProgrammerView(ModuleView):
class EventOptionsWidget(QWidget): class EventOptionsWidget(QWidget):
"""This class is a widget that can be used to set the options for a pulse parameter. """This class is a widget that can be used to set the options for a pulse parameter.
This widget is then added to the the first row of the according event column in the pulse table. This widget is then added to the the first row of the according event column in the pulse table.
It has a edit button that opens a dialog that allows the user to change the options for the event (name and duration). It has a edit button that opens a dialog that allows the user to change the options for the event (name and duration).
Furthermore it has a delete button that deletes the event from the pulse sequence. Furthermore it has a delete button that deletes the event from the pulse sequence.
Signals:
delete_event: Emitted when the delete button is clicked.
change_event_duration: Emitted when the duration of the event is changed.
change_event_name: Emitted when the name of the event is changed.
move_event_left: Emitted when the move left button is clicked.
move_event_right: Emitted when the move right button is clicked.
""" """
delete_event = pyqtSignal(str) delete_event = pyqtSignal(str)
@ -358,6 +375,7 @@ class EventOptionsWidget(QWidget):
move_event_right = pyqtSignal(str) move_event_right = pyqtSignal(str)
def __init__(self, event): def __init__(self, event):
"""Initializes the EventOptionsWidget."""
super().__init__() super().__init__()
self.event = event self.event = event
@ -411,6 +429,7 @@ class EventOptionsWidget(QWidget):
@pyqtSlot() @pyqtSlot()
def edit_event(self) -> None: def edit_event(self) -> None:
"""This method is called when the edit button is clicked. It opens a dialog that allows the user to change the event name and duration. """This method is called when the edit button is clicked. It opens a dialog that allows the user to change the event name and duration.
If the user clicks ok, the change_event_name and change_event_duration signals are emitted. If the user clicks ok, the change_event_name and change_event_duration signals are emitted.
""" """
logger.debug("Edit button clicked for event %s", self.event.name) logger.debug("Edit button clicked for event %s", self.event.name)
@ -419,7 +438,7 @@ class EventOptionsWidget(QWidget):
dialog = QDialog(self) dialog = QDialog(self)
dialog.setWindowTitle("Edit event") dialog.setWindowTitle("Edit event")
layout = QVBoxLayout() layout = QVBoxLayout()
label = QLabel("Edit event %s" % self.event.name) label = QLabel(f"Edit event {self.event.name}")
layout.addWidget(label) layout.addWidget(label)
# Create the inputs for event name, duration # Create the inputs for event name, duration
@ -457,7 +476,9 @@ class EventOptionsWidget(QWidget):
@pyqtSlot() @pyqtSlot()
def create_delete_event_dialog(self) -> None: def create_delete_event_dialog(self) -> None:
"""This method is called when the delete button is clicked. It creates a dialog that asks the user if he is sure he wants to delete the event. """This method is called when the delete button is clicked.
It creates a dialog that asks the user if he is sure he wants to delete the event.
If the user clicks yes, the delete_event signal is emitted. If the user clicks yes, the delete_event signal is emitted.
""" """
# Create an 'are you sure' dialog # Create an 'are you sure' dialog
@ -465,7 +486,7 @@ class EventOptionsWidget(QWidget):
dialog = QDialog(self) dialog = QDialog(self)
dialog.setWindowTitle("Delete event") dialog.setWindowTitle("Delete event")
layout = QVBoxLayout() layout = QVBoxLayout()
label = QLabel("Are you sure you want to delete event %s?" % self.event.name) label = QLabel(f"Are you sure you want to delete event {self.event.name}?")
layout.addWidget(label) layout.addWidget(label)
buttons = QDialogButtonBox( buttons = QDialogButtonBox(
QDialogButtonBox.StandardButton.Yes | QDialogButtonBox.StandardButton.No QDialogButtonBox.StandardButton.Yes | QDialogButtonBox.StandardButton.No
@ -494,6 +515,7 @@ class AddEventDialog(QDialog):
"""This dialog is created whenever a new event is added to the pulse sequence. It allows the user to enter a name for the event.""" """This dialog is created whenever a new event is added to the pulse sequence. It allows the user to enter a name for the event."""
def __init__(self, parent=None): def __init__(self, parent=None):
"""Initializes the AddEventDialog."""
super().__init__(parent) super().__init__(parent)
self.setWindowTitle("Add Event") self.setWindowTitle("Add Event")
@ -544,10 +566,10 @@ class AddEventDialog(QDialog):
return self.name_input.text() return self.name_input.text()
def get_duration(self) -> float: def get_duration(self) -> float:
"""Returns the duration entered by the user, or a fallback value." """Returns the duration entered by the user, or a fallback value.
Returns: Returns:
float: The duration value provided by the user, or 20 float: The duration value provided by the user, or 20
""" """
return self.duration_lineedit.text() or 20 return self.duration_lineedit.text() or 20
@ -600,6 +622,7 @@ class QFileManager:
"""This class provides methods for opening and saving files.""" """This class provides methods for opening and saving files."""
def __init__(self, parent=None): def __init__(self, parent=None):
"""Initializes the QFileManager."""
self.parent = parent self.parent = parent
def loadFileDialog(self) -> str: def loadFileDialog(self) -> str: