From 0f384787568ec367976eeccd09f632aaa230e0fa Mon Sep 17 00:00:00 2001 From: jupfi Date: Tue, 28 May 2024 15:54:11 +0200 Subject: [PATCH 1/8] Added setting slider as optional for NumericOptions. --- src/nqrduck_pulseprogrammer/view.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/nqrduck_pulseprogrammer/view.py b/src/nqrduck_pulseprogrammer/view.py index 479eba0..16b3419 100644 --- a/src/nqrduck_pulseprogrammer/view.py +++ b/src/nqrduck_pulseprogrammer/view.py @@ -290,6 +290,12 @@ 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 + + numeric_field = DuckFormFloatField( option.name, tooltip=None, From eb00db8f236cfe4dcdf31eab3f4bed6666895ff6 Mon Sep 17 00:00:00 2001 From: jupfi Date: Tue, 28 May 2024 16:47:55 +0200 Subject: [PATCH 2/8] Moved event creation and deletion to PulseSequence ... --- src/nqrduck_pulseprogrammer/controller.py | 5 +---- src/nqrduck_pulseprogrammer/model.py | 5 ++--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/nqrduck_pulseprogrammer/controller.py b/src/nqrduck_pulseprogrammer/controller.py index 974e522..c816d37 100644 --- a/src/nqrduck_pulseprogrammer/controller.py +++ b/src/nqrduck_pulseprogrammer/controller.py @@ -34,10 +34,7 @@ class PulseProgrammerController(ModuleController): event_name (str): The name of the event to be deleted. """ logger.debug("Deleting event %s", event_name) - for event in self.module.model.pulse_sequence.events: - if event.name == event_name: - self.module.model.pulse_sequence.events.remove(event) - break + self.module.model.pulse_sequence.delete_event(event_name) self.module.model.events_changed.emit() @pyqtSlot(str, str) diff --git a/src/nqrduck_pulseprogrammer/model.py b/src/nqrduck_pulseprogrammer/model.py index bca231e..0bb73e6 100644 --- a/src/nqrduck_pulseprogrammer/model.py +++ b/src/nqrduck_pulseprogrammer/model.py @@ -46,9 +46,8 @@ 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.events.append( - PulseSequence.Event(event_name, f"{float(duration):.16g}u") - ) + self.pulse_sequence.add_event(event_name, duration) + logger.debug( "Creating event %s with object id %s", event_name, From 5d5e22d33304ca3554e7b729484cc2e085206bff Mon Sep 17 00:00:00 2001 From: jupfi Date: Wed, 29 May 2024 16:03:34 +0200 Subject: [PATCH 3/8] Change to quackseq structure. --- pyproject.toml | 1 + src/nqrduck_pulseprogrammer/controller.py | 11 +-- src/nqrduck_pulseprogrammer/model.py | 40 ++------- src/nqrduck_pulseprogrammer/view.py | 81 +++++++++---------- .../visual_parameter.py | 45 +++++++++++ 5 files changed, 93 insertions(+), 85 deletions(-) create mode 100644 src/nqrduck_pulseprogrammer/visual_parameter.py 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 From c32d08fcd02f73d2f087c600274ef4997ea43d21 Mon Sep 17 00:00:00 2001 From: jupfi Date: Wed, 29 May 2024 16:12:00 +0200 Subject: [PATCH 4/8] Fixed loading and saving of sequences. --- src/nqrduck_pulseprogrammer/controller.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/nqrduck_pulseprogrammer/controller.py b/src/nqrduck_pulseprogrammer/controller.py index 810e840..b847c2c 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 quackseq.pulsesequence import PulseSequence +from quackseq.pulsesequence import QuackSequence logger = logging.getLogger(__name__) @@ -142,8 +142,8 @@ class PulseProgrammerController(ModuleController): sequence = json.loads(sequence) - loaded_sequence = PulseSequence.load_sequence( - sequence, self.module.model.pulse_parameter_options + loaded_sequence = QuackSequence.load_sequence( + sequence ) self.module.model.pulse_sequence = loaded_sequence From 7577c245db43a10ad41bf16ba768ae95b3f59bad Mon Sep 17 00:00:00 2001 From: jupfi Date: Sat, 1 Jun 2024 21:01:55 +0200 Subject: [PATCH 5/8] Removed unnecessary linting ignore setting. --- pyproject.toml | 3 --- src/nqrduck_pulseprogrammer/__init__.py | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index dcf5e9c..24a656b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,9 +41,6 @@ extend-select = [ "D", # pydocstyle ] -[tool.ruff.lint.per-file-ignores] -"__init__.py" = ["F401"] - [tool.ruff.lint.pydocstyle] convention = "google" diff --git a/src/nqrduck_pulseprogrammer/__init__.py b/src/nqrduck_pulseprogrammer/__init__.py index 40b8254..229bf24 100644 --- a/src/nqrduck_pulseprogrammer/__init__.py +++ b/src/nqrduck_pulseprogrammer/__init__.py @@ -1 +1 @@ -"""Empty file to make this directory a package.""" \ No newline at end of file +"""The visual pulse programming module for quackseq in the NQRduck project.""" \ No newline at end of file From 04ff7370043f3ae7315e0fea869506a7512f57bd Mon Sep 17 00:00:00 2001 From: jupfi Date: Thu, 20 Jun 2024 20:57:50 +0200 Subject: [PATCH 6/8] Fixed typo. --- src/nqrduck_pulseprogrammer/view.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nqrduck_pulseprogrammer/view.py b/src/nqrduck_pulseprogrammer/view.py index 1c29951..598593a 100644 --- a/src/nqrduck_pulseprogrammer/view.py +++ b/src/nqrduck_pulseprogrammer/view.py @@ -145,7 +145,7 @@ class PulseProgrammerView(ModuleView): event_name = dialog.get_name() duration = dialog.get_duration() logger.debug( - "Adding new event with name %s, duration %g", event_name, duration + "Adding new event with name %s, duration %s", event_name, duration ) self.module.model.add_event(event_name, duration) From d5f48d6785255ccef02126ea9cf57d0dc922da66 Mon Sep 17 00:00:00 2001 From: jupfi Date: Sat, 22 Jun 2024 18:39:27 +0200 Subject: [PATCH 7/8] Fixed typo. --- .../visual_parameter.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/nqrduck_pulseprogrammer/visual_parameter.py b/src/nqrduck_pulseprogrammer/visual_parameter.py index aaa258a..7601e2b 100644 --- a/src/nqrduck_pulseprogrammer/visual_parameter.py +++ b/src/nqrduck_pulseprogrammer/visual_parameter.py @@ -1,6 +1,6 @@ -from nqrduck.assets.icons import PulseParamters +from nqrduck.assets.icons import PulseParameters from quackseq.pulseparameters import TXPulse, RXReadout, PulseParameter -from quackseq.functions import Function, RectFunction, SincFunction, GaussianFunction, CustomFunction +from quackseq.functions import RectFunction, SincFunction, GaussianFunction class VisualParameter(): @@ -20,26 +20,26 @@ class VisualParameter(): # Get the shape shape = self.pulse_parameter.get_option_by_name(TXPulse.TX_PULSE_SHAPE).value if isinstance(shape, RectFunction): - pixmap = PulseParamters.TXRect() + pixmap = PulseParameters.TXRect() return pixmap elif isinstance(shape, SincFunction): - pixmap = PulseParamters.TXSinc() + pixmap = PulseParameters.TXSinc() return pixmap elif isinstance(shape, GaussianFunction): - pixmap = PulseParamters.TXGauss() + pixmap = PulseParameters.TXGauss() return pixmap else: - pixmap = PulseParamters.TXCustom() + pixmap = PulseParameters.TXCustom() return pixmap else: - pixmap = PulseParamters.TXOff() + pixmap = PulseParameters.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() + pixmap = PulseParameters.RXOn() return pixmap else: - pixmap = PulseParamters.RXOff() + pixmap = PulseParameters.RXOff() return pixmap From 43b49998a3718841426c485b09597226db17d739 Mon Sep 17 00:00:00 2001 From: jupfi Date: Sat, 16 Nov 2024 13:00:10 +0100 Subject: [PATCH 8/8] Table view generation. --- src/nqrduck_pulseprogrammer/view.py | 126 ++++++++++++++++++---------- 1 file changed, 84 insertions(+), 42 deletions(-) diff --git a/src/nqrduck_pulseprogrammer/view.py b/src/nqrduck_pulseprogrammer/view.py index 598593a..a96cfa9 100644 --- a/src/nqrduck_pulseprogrammer/view.py +++ b/src/nqrduck_pulseprogrammer/view.py @@ -26,12 +26,14 @@ from quackseq.pulseparameters import ( BooleanOption, NumericOption, FunctionOption, + TableOption, ) from nqrduck.helpers.formbuilder import ( DuckFormBuilder, DuckFormFunctionSelectionField, DuckFormCheckboxField, DuckFormFloatField, + DuckTableField, ) from .visual_parameter import VisualParameter @@ -271,6 +273,9 @@ class PulseProgrammerView(ModuleView): 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) + # We assume the pulse sequence was updated + self.module.model.pulse_sequence.update_options() + # Create a QDialog to set the options for the parameter. description = f"Set options for {parameter}" dialog = DuckFormBuilder(parameter, description=description, parent=self) @@ -278,53 +283,33 @@ class PulseProgrammerView(ModuleView): # Adding fields for the options form_options = [] for option in event.parameters[parameter].options: - if isinstance(option, BooleanOption): - boolean_form = DuckFormCheckboxField( - option.name, tooltip=None, default=option.value - ) - dialog.add_field(boolean_form) - form_options.append(option) - elif isinstance(option, NumericOption): - # We only show the slider if both min and max values are set - if option.min_value is not None and option.max_value is not None: - slider = True - else: - slider = False + logger.debug(f"Option value is {option.value}") + if isinstance(option, TableOption): + # Every option is it's own column. Every column has a dedicated number of rows. + # Get the option name: + name = option.name + table = DuckTableField(name, tooltip=None) - if slider: - slider = option.slider + columns = option.columns - numeric_field = DuckFormFloatField( - option.name, - tooltip=None, - default=option.value, - min_value=option.min_value, - max_value=option.max_value, - slider=slider, - ) + for column in columns: + # Every table option has a number of rows + fields = [] + for row in column.options: + fields.append(self.get_field_for_option(row, event)) - dialog.add_field(numeric_field) - form_options.append(option) - elif isinstance(option, FunctionOption): - logger.debug(f"Functions: {option.functions}") + name = column.name - # When loading a pulse sequence, the instance of the objects will be different - # Therefore we need to operate on the classes - for function in option.functions: - if function.__class__.__name__ == option.value.__class__.__name__: - default_function = function + logger.debug(f"Adding column {name} with fields {fields}") + table.add_column(option=column, fields=fields) - index = option.functions.index(default_function) + form_options.append(table) + dialog.add_field(table) - function_field = DuckFormFunctionSelectionField( - option.name, - tooltip=None, - functions=option.functions, - duration=event.duration, - default_function=index, - ) - dialog.add_field(function_field) - form_options.append(option) + else: + field = self.get_field_for_option(option, event) + form_options.append(field) + dialog.add_field(field) result = dialog.exec() @@ -333,10 +318,67 @@ class PulseProgrammerView(ModuleView): if result: values = dialog.get_values() for i, value in enumerate(values): - options[i].value = value + logger.debug(f"Setting value {value} for option {options[i]}") + options[i].set_value(value) self.set_parameter_icons() + def get_field_for_option(self, option, event): + """Returns the field for the given option. + + Args: + option (Option): The option for which the field should be created. + event (PulseSequence.Event): The event for which the option should be created. + + Returns: + DuckFormField: The field for the option + """ + logger.debug(f"Creating field with value {option.value}") + if isinstance(option, BooleanOption): + field = DuckFormCheckboxField( + option.name, tooltip=None, default=option.value + ) + elif isinstance(option, NumericOption): + # We only show the slider if both min and max values are set + if option.min_value is not None and option.max_value is not None: + slider = True + else: + slider = False + + if slider: + slider = option.slider + + field = DuckFormFloatField( + option.name, + tooltip=None, + default=option.value, + min_value=option.min_value, + max_value=option.max_value, + slider=slider, + ) + + elif isinstance(option, FunctionOption): + logger.debug(f"Functions: {option.functions}") + + # When loading a pulse sequence, the instance of the objects will be different + # Therefore we need to operate on the classes + for function in option.functions: + if function.__class__.__name__ == option.value.__class__.__name__: + default_function = function + + index = option.functions.index(default_function) + + field = DuckFormFunctionSelectionField( + option.name, + tooltip=None, + functions=option.functions, + duration=event.duration, + default_function=index, + ) + + logger.debug(f"Returning Field: {field}") + return field + @pyqtSlot() def on_save_button_clicked(self) -> None: """This method is called whenever the save button is clicked. It opens a dialog to select a file to save the pulse sequence to."""