mirror of
https://github.com/nqrduck/nqrduck-pulseprogrammer.git
synced 2024-11-09 11:20:01 +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 decimal import Decimal
|
||||||
from PyQt6.QtGui import QValidator
|
from PyQt6.QtGui import QValidator
|
||||||
from PyQt6.QtWidgets import (
|
from PyQt6.QtWidgets import (
|
||||||
QMessageBox,
|
|
||||||
QGroupBox,
|
|
||||||
QFormLayout,
|
QFormLayout,
|
||||||
QTableWidget,
|
QTableWidget,
|
||||||
QVBoxLayout,
|
QVBoxLayout,
|
||||||
|
@ -16,7 +14,6 @@ from PyQt6.QtWidgets import (
|
||||||
QLineEdit,
|
QLineEdit,
|
||||||
QDialogButtonBox,
|
QDialogButtonBox,
|
||||||
QWidget,
|
QWidget,
|
||||||
QCheckBox,
|
|
||||||
QToolButton,
|
QToolButton,
|
||||||
QFileDialog,
|
QFileDialog,
|
||||||
QSizePolicy,
|
QSizePolicy,
|
||||||
|
@ -30,6 +27,14 @@ from nqrduck_spectrometer.pulseparameters import (
|
||||||
NumericOption,
|
NumericOption,
|
||||||
FunctionOption,
|
FunctionOption,
|
||||||
)
|
)
|
||||||
|
from nqrduck.helpers.formbuilder import (
|
||||||
|
DuckFormBuilder,
|
||||||
|
DuckFormFunctionSelectionField,
|
||||||
|
DuckFormCheckboxField,
|
||||||
|
DuckFormDropdownField,
|
||||||
|
DuckFormFloatField,
|
||||||
|
DuckFormIntField,
|
||||||
|
)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
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."""
|
"""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)
|
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.
|
||||||
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()
|
result = dialog.exec()
|
||||||
|
|
||||||
|
options = event.parameters[parameter].options
|
||||||
|
|
||||||
if result:
|
if result:
|
||||||
for option, function in dialog.return_functions.items():
|
values = dialog.get_values()
|
||||||
logger.debug(
|
for i, value in enumerate(values):
|
||||||
"Setting option %s of parameter %s in event %s to %s",
|
options[i].value = value
|
||||||
option,
|
|
||||||
parameter,
|
|
||||||
event,
|
|
||||||
function(),
|
|
||||||
)
|
|
||||||
option.set_value(function())
|
|
||||||
|
|
||||||
self.set_parameter_icons()
|
self.set_parameter_icons()
|
||||||
|
|
||||||
|
@ -441,294 +470,6 @@ class EventOptionsWidget(QWidget):
|
||||||
self.move_event_right.emit(self.event.name)
|
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):
|
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."""
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue