From bc255b0fe3cd47ab76032216580b6dc3a8a47eeb Mon Sep 17 00:00:00 2001 From: jupfi Date: Fri, 26 Apr 2024 11:00:41 +0200 Subject: [PATCH] Updated to new formbuilder. --- src/nqrduck_pulseprogrammer/view.py | 343 ++++------------------------ 1 file changed, 42 insertions(+), 301 deletions(-) diff --git a/src/nqrduck_pulseprogrammer/view.py b/src/nqrduck_pulseprogrammer/view.py index 61978aa..02e6cd7 100644 --- a/src/nqrduck_pulseprogrammer/view.py +++ b/src/nqrduck_pulseprogrammer/view.py @@ -4,8 +4,6 @@ from collections import OrderedDict from decimal import Decimal from PyQt6.QtGui import QValidator from PyQt6.QtWidgets import ( - QMessageBox, - QGroupBox, QFormLayout, QTableWidget, QVBoxLayout, @@ -16,7 +14,6 @@ from PyQt6.QtWidgets import ( QLineEdit, QDialogButtonBox, QWidget, - QCheckBox, QToolButton, QFileDialog, QSizePolicy, @@ -30,6 +27,14 @@ from nqrduck_spectrometer.pulseparameters import ( NumericOption, FunctionOption, ) +from nqrduck.helpers.formbuilder import ( + DuckFormBuilder, + DuckFormFunctionSelectionField, + DuckFormCheckboxField, + DuckFormDropdownField, + DuckFormFloatField, + DuckFormIntField, +) logger = logging.getLogger(__name__) @@ -260,19 +265,43 @@ class PulseProgrammerView(ModuleView): """This method is called whenever a button in the pulse table is clicked. It opens a dialog to set the options for the parameter.""" logger.debug("Button for event %s and parameter %s clicked", event, parameter) # Create a QDialog to set the options for the parameter. - dialog = OptionsDialog(event, parameter, self) + description = f"Set options for {parameter}" + dialog = DuckFormBuilder(description, description=description, parent=self) + + # 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): + numeric_field = DuckFormFloatField( + option.name, tooltip=None, default=option.value + ) + dialog.add_field(numeric_field) + form_options.append(option) + elif isinstance(option, FunctionOption): + function_field = DuckFormFunctionSelectionField( + option.name, + tooltip=None, + functions=option.functions, + duration = event.duration, + default_function=option.functions.index(option.value), + ) + dialog.add_field(function_field) + form_options.append(option) + result = dialog.exec() + options = event.parameters[parameter].options + if result: - for option, function in dialog.return_functions.items(): - logger.debug( - "Setting option %s of parameter %s in event %s to %s", - option, - parameter, - event, - function(), - ) - option.set_value(function()) + values = dialog.get_values() + for i, value in enumerate(values): + options[i].value = value self.set_parameter_icons() @@ -441,294 +470,6 @@ class EventOptionsWidget(QWidget): self.move_event_right.emit(self.event.name) -class OptionsDialog(QDialog): - """This dialog is created whenever the edit button for a pulse option is clicked. - It allows the user to change the options for the pulse parameter and creates the dialog in accordance to what can be set. - """ - - def __init__(self, event, parameter, parent=None): - super().__init__(parent) - self.parent = parent - - self.setWindowTitle("Options") - - self.layout = QVBoxLayout(self) - - numeric_layout = QFormLayout() - numeric_layout.setHorizontalSpacing(30) - - self.label = QLabel("Change options for the pulse parameter: %s" % parameter) - self.layout.addWidget(self.label) - - self.layout.addLayout(numeric_layout) - - # If the parameter is a string, we first need to get the parameter object from the according event - if isinstance(parameter, str): - parameter = event.parameters[parameter] - - options = parameter.get_options() - - # Based on these options we will now create our selection widget - self.return_functions = OrderedDict() - - # If the options are a list , we will create a QComboBox - for option in options: - if option == list: - pass - # If the options are boolean, we will create a QCheckBox - elif isinstance(option, BooleanOption): - check_box = QCheckBox() - - def checkbox_result(): - return check_box.isChecked() - - check_box.setChecked(option.value) - self.layout.addWidget(check_box) - self.return_functions[option] = checkbox_result - - # If the options are a float/int we will create a QSpinBox - elif isinstance(option, NumericOption): - numeric_label = QLabel(option.name) - numeric_lineedit = QLineEdit(str(option.value)) - numeric_lineedit.setMaximumWidth(300) - numeric_layout.addRow(numeric_label, numeric_lineedit) - - self.return_functions[option] = numeric_lineedit.text - - # If the options are a string we will create a QLineEdit - elif option == str: - pass - - elif isinstance(option, FunctionOption): - function_option = FunctionOptionWidget(option, event, parent) - self.layout.addWidget(function_option) - - logger.debug("Return functions are: %s" % self.return_functions.items()) - - self.buttons = QDialogButtonBox( - QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel, - self, - ) - self.buttons.accepted.connect(self.accept) - self.buttons.rejected.connect(self.reject) - - self.layout.addWidget(self.buttons) - - -class FunctionOptionWidget(QWidget): - """This class is a widget that can be used to set the options for a pulse parameter. - It plots the given function in time and frequency domain. - One can also select the function from a list of functions represented as buttons. - """ - - def __init__(self, function_option, event, parent=None): - super().__init__(parent) - self.parent = parent - - self.function_option = function_option - self.event = event - layout = QVBoxLayout() - inner_layout = QHBoxLayout() - for function in function_option.functions: - button = QPushButton(function.name) - button.clicked.connect( - functools.partial(self.on_functionbutton_clicked, function=function) - ) - inner_layout.addWidget(button) - - layout.addLayout(inner_layout) - self.setLayout(layout) - - # Add Advanced settings button - self.advanced_settings_button = QPushButton("Show Advanced settings") - self.advanced_settings_button.clicked.connect( - self.on_advanced_settings_button_clicked - ) - layout.addWidget(self.advanced_settings_button) - - # Add advanced settings widget - self.advanced_settings = QGroupBox("Advanced Settings") - self.advanced_settings.setHidden(True) - self.advanced_settings_layout = QFormLayout() - self.advanced_settings.setLayout(self.advanced_settings_layout) - layout.addWidget(self.advanced_settings) - - # Add the advanced settings - # Advanced settings are resolution, start_x = -1, end_x and the expr of the function_option.value - resolution_layout = QHBoxLayout() - resolution_label = QLabel("Resolution:") - self.resolution_lineedit = QLineEdit(str(function_option.value.resolution)) - resolution_layout.addWidget(resolution_label) - resolution_layout.addWidget(self.resolution_lineedit) - resolution_layout.addStretch(1) - self.advanced_settings_layout.addRow(resolution_label, resolution_layout) - - start_x_layout = QHBoxLayout() - start_x_label = QLabel("Start x:") - self.start_x_lineedit = QLineEdit(str(function_option.value.start_x)) - start_x_layout.addWidget(start_x_label) - start_x_layout.addWidget(self.start_x_lineedit) - start_x_layout.addStretch(1) - self.advanced_settings_layout.addRow(start_x_label, start_x_layout) - - end_x_layout = QHBoxLayout() - end_x_label = QLabel("End x:") - self.end_x_lineedit = QLineEdit(str(function_option.value.end_x)) - end_x_layout.addWidget(end_x_label) - end_x_layout.addWidget(self.end_x_lineedit) - end_x_layout.addStretch(1) - self.advanced_settings_layout.addRow(end_x_label, end_x_layout) - - expr_layout = QHBoxLayout() - expr_label = QLabel("Expression:") - self.expr_lineedit = QLineEdit(str(function_option.value.expr)) - expr_layout.addWidget(expr_label) - expr_layout.addWidget(self.expr_lineedit) - expr_layout.addStretch(1) - self.advanced_settings_layout.addRow(expr_label, expr_layout) - - # Add buttton for replotting of the active function with the new parameters - self.replot_button = QPushButton("Replot") - self.replot_button.clicked.connect(self.on_replot_button_clicked) - layout.addWidget(self.replot_button) - - # Display the active function - self.load_active_function() - - @pyqtSlot() - def on_replot_button_clicked(self) -> None: - """This function is called when the replot button is clicked. - It will update the parameters of the function and replots the function. - """ - logger.debug("Replot button clicked") - # Update the resolution, start_x, end_x and expr lineedits - self.function_option.value.resolution = self.resolution_lineedit.text() - self.function_option.value.start_x = self.start_x_lineedit.text() - self.function_option.value.end_x = self.end_x_lineedit.text() - try: - self.function_option.value.expr = self.expr_lineedit.text() - except SyntaxError: - logger.debug("Invalid expression: %s", self.expr_lineedit.text()) - self.expr_lineedit.setText(str(self.function_option.value.expr)) - # Create message box that tells the user that the expression is invalid - self.create_message_box( - "Invalid expression", - "The expression you entered is invalid. Please enter a valid expression.", - ) - - self.delete_active_function() - self.load_active_function() - - @pyqtSlot() - def on_advanced_settings_button_clicked(self) -> None: - """This function is called when the advanced settings button is clicked. - It will show or hide the advanced settings. - """ - if self.advanced_settings.isHidden(): - self.advanced_settings.setHidden(False) - self.advanced_settings_button.setText("Hide Advanced Settings") - else: - self.advanced_settings.setHidden(True) - self.advanced_settings_button.setText("Show Advanced Settings") - - @pyqtSlot() - def on_functionbutton_clicked(self, function) -> None: - """This function is called when a function button is clicked. - It will update the function_option.value to the function that was clicked. - """ - logger.debug("Button for function %s clicked", function.name) - self.function_option.set_value(function) - self.delete_active_function() - self.load_active_function() - - def delete_active_function(self) -> None: - """This function is called when the active function is deleted. - It will remove the active function from the layout. - """ - # Remove the plotter with object name "plotter" from the layout - for i in reversed(range(self.layout().count())): - item = self.layout().itemAt(i) - if item.widget() and item.widget().objectName() == "active_function": - item.widget().deleteLater() - break - - def load_active_function(self) -> None: - """This function is called when the active function is loaded. - It will add the active function to the layout. - """ - # New QWidget for the active function - active_function_Widget = QWidget() - active_function_Widget.setObjectName("active_function") - - function_layout = QVBoxLayout() - - plot_layout = QHBoxLayout() - - # Add plot for time domain - time_domain_layout = QVBoxLayout() - time_domain_label = QLabel("Time domain:") - time_domain_layout.addWidget(time_domain_label) - plot = self.function_option.value.time_domain_plot(self.event.duration) - time_domain_layout.addWidget(plot) - plot_layout.addLayout(time_domain_layout) - - # Add plot for frequency domain - frequency_domain_layout = QVBoxLayout() - frequency_domain_label = QLabel("Frequency domain:") - frequency_domain_layout.addWidget(frequency_domain_label) - plot = self.function_option.value.frequency_domain_plot(self.event.duration) - frequency_domain_layout.addWidget(plot) - plot_layout.addLayout(frequency_domain_layout) - - function_layout.addLayout(plot_layout) - - parameter_layout = QFormLayout() - parameter_label = QLabel("Parameters:") - parameter_layout.addRow(parameter_label) - for parameter in self.function_option.value.parameters: - parameter_label = QLabel(parameter.name) - parameter_lineedit = QLineEdit(str(parameter.value)) - # Add the parameter_lineedit editingFinished signal to the paramter.set_value slot - parameter_lineedit.editingFinished.connect( - lambda: parameter.set_value(parameter_lineedit.text()) - ) - - # Create a QHBoxLayout - hbox = QHBoxLayout() - - # Add your QLineEdit and a stretch to the QHBoxLayout - hbox.addWidget(parameter_lineedit) - hbox.addStretch(1) - - # Use addRow() method to add label and the QHBoxLayout next to each other - parameter_layout.addRow(parameter_label, hbox) - - function_layout.addLayout(parameter_layout) - function_layout.addStretch(1) - active_function_Widget.setLayout(function_layout) - self.layout().addWidget(active_function_Widget) - - # Update the resolution, start_x, end_x and expr lineedits - self.resolution_lineedit.setText(str(self.function_option.value.resolution)) - self.start_x_lineedit.setText(str(self.function_option.value.start_x)) - self.end_x_lineedit.setText(str(self.function_option.value.end_x)) - self.expr_lineedit.setText(str(self.function_option.value.expr)) - - def create_message_box(self, message: str, information: str) -> None: - """Creates a message box with the given message and information and shows it. - - Args: - message (str): The message to be shown in the message box - information (str): The information to be shown in the message box - """ - msg = QMessageBox(parent=self.parent) - msg.setIcon(QMessageBox.Icon.Warning) - msg.setText(message) - msg.setInformativeText(information) - msg.setWindowTitle("Warning") - msg.exec() - - 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."""