mirror of
https://github.com/nqrduck/nqrduck-pulseprogrammer.git
synced 2024-12-22 15:47:47 +00:00
Updated to new formbuilder.
This commit is contained in:
parent
d2a05452f9
commit
bc255b0fe3
1 changed files with 42 additions and 301 deletions
|
@ -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."""
|
||||
|
||||
|
|
Loading…
Reference in a new issue