diff --git a/pyproject.toml b/pyproject.toml index 3817701..dcf5e9c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,6 +24,7 @@ dependencies = [ "matplotlib", "pyqt6", "nqrduck-spectrometer", + "quackseq", ] [project.entry-points."nqrduck"] diff --git a/src/nqrduck_pulseprogrammer/controller.py b/src/nqrduck_pulseprogrammer/controller.py index c816d37..810e840 100644 --- a/src/nqrduck_pulseprogrammer/controller.py +++ b/src/nqrduck_pulseprogrammer/controller.py @@ -6,7 +6,7 @@ import decimal from PyQt6.QtCore import pyqtSlot from nqrduck.helpers.serializer import DecimalEncoder from nqrduck.module.module_controller import ModuleController -from nqrduck_spectrometer.pulsesequence import PulseSequence +from quackseq.pulsesequence import PulseSequence logger = logging.getLogger(__name__) @@ -17,14 +17,9 @@ class PulseProgrammerController(ModuleController): 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. - - Args: - pulse_parameter_options (dict): The pulse parameter options. - """ + def on_loading(self) -> None: + """This method is called when the module is loaded. It sets the pulse parameter options in the model.""" logger.debug("Pulse programmer controller on loading") - self.module.model.pulse_parameter_options = pulse_parameter_options @pyqtSlot(str) def delete_event(self, event_name: str) -> None: diff --git a/src/nqrduck_pulseprogrammer/model.py b/src/nqrduck_pulseprogrammer/model.py index 0bb73e6..c74beea 100644 --- a/src/nqrduck_pulseprogrammer/model.py +++ b/src/nqrduck_pulseprogrammer/model.py @@ -1,10 +1,10 @@ """Model for the pulse programmer module.""" import logging -from collections import OrderedDict from PyQt6.QtCore import pyqtSignal from nqrduck.module.module_model import ModuleModel -from nqrduck_spectrometer.pulsesequence import PulseSequence +from quackseq.pulsesequence import QuackSequence +from quackseq.event import Event logger = logging.getLogger(__name__) @@ -25,7 +25,6 @@ class PulseProgrammerModel(ModuleModel): FILE_EXTENSION = "quack" - pulse_parameter_options_changed = pyqtSignal() events_changed = pyqtSignal() pulse_sequence_changed = pyqtSignal() @@ -36,8 +35,7 @@ class PulseProgrammerModel(ModuleModel): module (Module): The module to which this model belongs. """ super().__init__(module) - self.pulse_parameter_options = OrderedDict() - self.pulse_sequence = PulseSequence("Untitled pulse sequence") + self.pulse_sequence = QuackSequence("Untitled pulse sequence") def add_event(self, event_name: str, duration: float = 20): """Add a new event to the current pulse sequence. @@ -46,40 +44,14 @@ class PulseProgrammerModel(ModuleModel): event_name (str): A human-readable name for the event duration (float): The duration of the event in µs. Defaults to 20. """ - self.pulse_sequence.add_event(event_name, duration) + logger.debug(f"Adding event {event_name} with duration {duration}") - logger.debug( - "Creating event %s with object id %s", - event_name, - id(self.pulse_sequence.events[-1]), - ) - - # Create a default instance of the pulse parameter options and add it to the event - for name, pulse_parameter_class in self.pulse_parameter_options.items(): - logger.debug("Adding pulse parameter %s to event %s", name, event_name) - self.pulse_sequence.events[-1].parameters[name] = pulse_parameter_class( - name - ) - logger.debug( - "Created pulse parameter %s with object id %s", - name, - id(self.pulse_sequence.events[-1].parameters[name]), - ) + event = Event(event_name, f"{duration}u", self.pulse_sequence) + self.pulse_sequence.add_event(event) logger.debug(self.pulse_sequence.to_json()) self.events_changed.emit() - @property - def pulse_parameter_options(self): - """dict: The pulse parameter options.""" - return self._pulse_parameter_options - - @pulse_parameter_options.setter - def pulse_parameter_options(self, value): - self._pulse_parameter_options = value - logger.debug("Pulse parameter options changed - emitting signal") - self.pulse_parameter_options_changed.emit() - @property def pulse_sequence(self): """PulseSequence: The pulse sequence.""" diff --git a/src/nqrduck_pulseprogrammer/view.py b/src/nqrduck_pulseprogrammer/view.py index 16b3419..1c29951 100644 --- a/src/nqrduck_pulseprogrammer/view.py +++ b/src/nqrduck_pulseprogrammer/view.py @@ -21,7 +21,8 @@ from PyQt6.QtCore import pyqtSlot, pyqtSignal from nqrduck.module.module_view import ModuleView from nqrduck.assets.icons import Logos from nqrduck.helpers.duckwidgets import DuckFloatEdit, DuckEdit -from nqrduck_spectrometer.pulseparameters import ( + +from quackseq.pulseparameters import ( BooleanOption, NumericOption, FunctionOption, @@ -33,6 +34,8 @@ from nqrduck.helpers.formbuilder import ( DuckFormFloatField, ) +from .visual_parameter import VisualParameter + logger = logging.getLogger(__name__) @@ -51,13 +54,6 @@ class PulseProgrammerView(ModuleView): self.setup_variabletables() - logger.debug( - "Connecting pulse parameter options changed signal to on_pulse_parameter_options_changed" - ) - self.module.model.pulse_parameter_options_changed.connect( - self.on_pulse_parameter_options_changed - ) - def setup_variabletables(self) -> None: """Setup the table for the variables.""" pass @@ -138,20 +134,6 @@ class PulseProgrammerView(ModuleView): ) self.title.setText(f"Pulse Sequence: {self.module.model.pulse_sequence.name}") - @pyqtSlot() - def on_pulse_parameter_options_changed(self) -> None: - """This method is called whenever the pulse parameter options change. It updates the view to reflect the changes.""" - logger.debug( - "Updating pulse parameter options to %s", - self.module.model.pulse_parameter_options.keys(), - ) - # We set it to the length of the pulse parameter options + 1 because we want to add a row for the parameter option buttons - self.pulse_table.setRowCount(len(self.module.model.pulse_parameter_options) + 1) - # Move the vertical header labels on row down - pulse_options = [""] - pulse_options.extend(list(self.module.model.pulse_parameter_options.keys())) - self.pulse_table.setVerticalHeaderLabels(pulse_options) - @pyqtSlot() def on_new_event_button_clicked(self) -> None: """This method is called whenever the new event button is clicked. It creates a new event and adds it to the pulse sequence.""" @@ -170,6 +152,22 @@ class PulseProgrammerView(ModuleView): @pyqtSlot() def on_events_changed(self) -> None: """This method is called whenever the events in the pulse sequence change. It updates the view to reflect the changes.""" + + pulse_parameter_options = ( + self.module.model.pulse_sequence.pulse_parameter_options + ) + + logger.debug( + "Updating pulse parameter options to %s", + pulse_parameter_options.keys(), + ) + # We set it to the length of the pulse parameter options + 1 because we want to add a row for the parameter option buttons + self.pulse_table.setRowCount(len(pulse_parameter_options) + 1) + # Move the vertical header labels on row down + pulse_options = [""] + pulse_options.extend(list(pulse_parameter_options.keys())) + self.pulse_table.setVerticalHeaderLabels(pulse_options) + logger.debug("Updating events to %s", self.module.model.pulse_sequence.events) # Add label for the event lengths @@ -198,10 +196,12 @@ class PulseProgrammerView(ModuleView): def set_parameter_icons(self) -> None: """This method sets the icons for the pulse parameter options.""" + pulse_parrameter_options = ( + self.module.model.pulse_sequence.pulse_parameter_options + ) + for column_idx, event in enumerate(self.module.model.pulse_sequence.events): - for row_idx, parameter in enumerate( - self.module.model.pulse_parameter_options.keys() - ): + for row_idx, parameter in enumerate(pulse_parrameter_options.keys()): if row_idx == 0: event_options_widget = EventOptionsWidget(event) # Connect the delete_event signal to the on_delete_event slot @@ -238,7 +238,8 @@ class PulseProgrammerView(ModuleView): ) logger.debug("Parameter object id: %s", id(event.parameters[parameter])) button = QPushButton() - icon = event.parameters[parameter].get_pixmap() + + icon = VisualParameter(event.parameters[parameter]).get_pixmap() logger.debug("Icon size: %s", icon.availableSizes()) button.setIcon(icon) button.setIconSize(icon.availableSizes()[0]) @@ -290,11 +291,8 @@ class PulseProgrammerView(ModuleView): else: slider = False - if not slider: - assert not option.slider, "Setting a slider is not possible without min and max values" - - slider = option.slider - + if slider: + slider = option.slider numeric_field = DuckFormFloatField( option.name, @@ -452,7 +450,7 @@ class EventOptionsWidget(QWidget): dialog = QDialog(self) dialog.setWindowTitle("Edit event") layout = QVBoxLayout() - label = QLabel(f"Edit event {self.event.name}") + label = QLabel(f"Edit event: {self.event.name}") layout.addWidget(label) # Create the inputs for event name, duration @@ -460,15 +458,13 @@ class EventOptionsWidget(QWidget): name_label = QLabel("Name:") name_lineedit = QLineEdit(self.event.name) event_form_layout.addRow(name_label, name_lineedit) - duration_layout = QHBoxLayout() - duration_label = QLabel("Duration:") + + duration_label = QLabel("Duration (µs):") duration_lineedit = QLineEdit() - unit_label = QLabel("µs") + duration_lineedit.setText("%.16g" % (self.event.duration * 1e6)) - duration_layout.addWidget(duration_label) - duration_layout.addWidget(duration_lineedit) - duration_layout.addWidget(unit_label) - event_form_layout.addRow(duration_layout) + + event_form_layout.addRow(duration_label, duration_lineedit) layout.addLayout(event_form_layout) buttons = QDialogButtonBox( @@ -543,22 +539,21 @@ class AddEventDialog(QDialog): self.name_input.validator = self.NameInputValidator(self) self.name_layout.addWidget(self.label) + self.name_layout.addStretch(1) self.name_layout.addWidget(self.name_input) self.layout.addRow(self.name_layout) self.duration_layout = QHBoxLayout() - self.duration_label = QLabel("Duration:") + self.duration_label = QLabel("Duration (µs):") self.duration_lineedit = DuckFloatEdit(min_value=0) self.duration_lineedit.setText("20") - self.unit_label = QLabel("µs") self.duration_layout.addWidget(self.duration_label) + self.duration_layout.addStretch(1) self.duration_layout.addWidget(self.duration_lineedit) - self.duration_layout.addWidget(self.unit_label) - self.layout.addRow(self.duration_layout) self.buttons = QDialogButtonBox( diff --git a/src/nqrduck_pulseprogrammer/visual_parameter.py b/src/nqrduck_pulseprogrammer/visual_parameter.py new file mode 100644 index 0000000..aaa258a --- /dev/null +++ b/src/nqrduck_pulseprogrammer/visual_parameter.py @@ -0,0 +1,45 @@ +from nqrduck.assets.icons import PulseParamters +from quackseq.pulseparameters import TXPulse, RXReadout, PulseParameter +from quackseq.functions import Function, RectFunction, SincFunction, GaussianFunction, CustomFunction + +class VisualParameter(): + + def __init__(self, pulse_parameter : PulseParameter): + self.pulse_parameter = pulse_parameter + + 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. + """ + # Check the instance of the pulse parameter + if isinstance(self.pulse_parameter, TXPulse): + amplitude = self.pulse_parameter.get_option_by_name(TXPulse.RELATIVE_AMPLITUDE).value + if amplitude > 0: + # Get the shape + shape = self.pulse_parameter.get_option_by_name(TXPulse.TX_PULSE_SHAPE).value + if isinstance(shape, RectFunction): + pixmap = PulseParamters.TXRect() + return pixmap + elif isinstance(shape, SincFunction): + pixmap = PulseParamters.TXSinc() + return pixmap + elif isinstance(shape, GaussianFunction): + pixmap = PulseParamters.TXGauss() + return pixmap + else: + pixmap = PulseParamters.TXCustom() + return pixmap + else: + pixmap = PulseParamters.TXOff() + return pixmap + + elif isinstance(self.pulse_parameter, RXReadout): + rx = self.pulse_parameter.get_option_by_name(RXReadout.RX).value + if rx: + pixmap = PulseParamters.RXOn() + return pixmap + else: + pixmap = PulseParamters.RXOff() + return pixmap