mirror of
https://github.com/nqrduck/nqrduck-pulseprogrammer.git
synced 2024-11-09 11:20:01 +00:00
Merge pull request #7 from kumitterer/number-display-and-duration
Event duration changes. I will trust you.
This commit is contained in:
commit
627750da34
2 changed files with 275 additions and 124 deletions
|
@ -1,4 +1,5 @@
|
||||||
import logging
|
import logging
|
||||||
|
from decimal import Decimal
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from PyQt6.QtCore import pyqtSignal, pyqtSlot
|
from PyQt6.QtCore import pyqtSignal, pyqtSlot
|
||||||
from nqrduck.module.module_model import ModuleModel
|
from nqrduck.module.module_model import ModuleModel
|
||||||
|
@ -6,6 +7,7 @@ from nqrduck_spectrometer.pulsesequence import PulseSequence
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class PulseProgrammerModel(ModuleModel):
|
class PulseProgrammerModel(ModuleModel):
|
||||||
pulse_parameter_options_changed = pyqtSignal()
|
pulse_parameter_options_changed = pyqtSignal()
|
||||||
events_changed = pyqtSignal()
|
events_changed = pyqtSignal()
|
||||||
|
@ -16,15 +18,33 @@ class PulseProgrammerModel(ModuleModel):
|
||||||
self.pulse_parameter_options = OrderedDict()
|
self.pulse_parameter_options = OrderedDict()
|
||||||
self.pulse_sequence = PulseSequence("Untitled pulse sequence")
|
self.pulse_sequence = PulseSequence("Untitled pulse sequence")
|
||||||
|
|
||||||
def add_event(self, event_name):
|
def add_event(self, event_name: str, duration: Decimal = 20):
|
||||||
self.pulse_sequence.events.append(PulseSequence.Event(event_name, "20u"))
|
"""Add a new event to the current pulse sequence.
|
||||||
logger.debug("Creating event %s with object id %s", event_name, id(self.pulse_sequence.events[-1]))
|
|
||||||
|
Args:
|
||||||
|
event_name (str): A human-readable name for the event
|
||||||
|
duration (Decimal): The duration of the event in µs. Defaults to 20.
|
||||||
|
"""
|
||||||
|
self.pulse_sequence.events.append(
|
||||||
|
PulseSequence.Event(event_name, "%.16gu" % 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
|
# 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():
|
for name, pulse_parameter_class in self.pulse_parameter_options.items():
|
||||||
logger.debug("Adding pulse parameter %s to event %s", name, event_name)
|
logger.debug("Adding pulse parameter %s to event %s", name, event_name)
|
||||||
self.pulse_sequence.events[-1].parameters[name] = pulse_parameter_class(name)
|
self.pulse_sequence.events[-1].parameters[name] = pulse_parameter_class(
|
||||||
logger.debug("Created pulse parameter %s with object id %s", name, id(self.pulse_sequence.events[-1].parameters[name]))
|
name
|
||||||
|
)
|
||||||
|
logger.debug(
|
||||||
|
"Created pulse parameter %s with object id %s",
|
||||||
|
name,
|
||||||
|
id(self.pulse_sequence.events[-1].parameters[name]),
|
||||||
|
)
|
||||||
|
|
||||||
logger.debug(self.pulse_sequence.to_json())
|
logger.debug(self.pulse_sequence.to_json())
|
||||||
self.events_changed.emit()
|
self.events_changed.emit()
|
||||||
|
|
|
@ -3,12 +3,34 @@ import functools
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from PyQt6.QtGui import QIcon
|
from PyQt6.QtGui import QIcon, QValidator
|
||||||
from PyQt6.QtWidgets import QMessageBox, QGroupBox, QFormLayout, QTableWidget, QVBoxLayout, QPushButton, QHBoxLayout, QLabel, QDialog, QLineEdit, QDialogButtonBox, QWidget, QCheckBox, QToolButton, QFileDialog, QSizePolicy
|
from PyQt6.QtWidgets import (
|
||||||
|
QMessageBox,
|
||||||
|
QGroupBox,
|
||||||
|
QFormLayout,
|
||||||
|
QTableWidget,
|
||||||
|
QVBoxLayout,
|
||||||
|
QPushButton,
|
||||||
|
QHBoxLayout,
|
||||||
|
QLabel,
|
||||||
|
QDialog,
|
||||||
|
QLineEdit,
|
||||||
|
QDialogButtonBox,
|
||||||
|
QWidget,
|
||||||
|
QCheckBox,
|
||||||
|
QToolButton,
|
||||||
|
QFileDialog,
|
||||||
|
QSizePolicy,
|
||||||
|
)
|
||||||
from PyQt6.QtCore import pyqtSlot, pyqtSignal
|
from PyQt6.QtCore import pyqtSlot, pyqtSignal
|
||||||
from nqrduck.module.module_view import ModuleView
|
from nqrduck.module.module_view import ModuleView
|
||||||
from nqrduck.assets.icons import Logos
|
from nqrduck.assets.icons import Logos
|
||||||
from nqrduck_spectrometer.pulseparameters import BooleanOption, NumericOption, FunctionOption
|
from nqrduck.helpers.duckwidgets import DuckFloatEdit, DuckEdit
|
||||||
|
from nqrduck_spectrometer.pulseparameters import (
|
||||||
|
BooleanOption,
|
||||||
|
NumericOption,
|
||||||
|
FunctionOption,
|
||||||
|
)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -22,21 +44,23 @@ class PulseProgrammerView(ModuleView):
|
||||||
|
|
||||||
self.setup_variabletables()
|
self.setup_variabletables()
|
||||||
|
|
||||||
logger.debug("Connecting pulse parameter options changed signal to on_pulse_parameter_options_changed")
|
logger.debug(
|
||||||
self.module.model.pulse_parameter_options_changed.connect(self.on_pulse_parameter_options_changed)
|
"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:
|
def setup_variabletables(self) -> None:
|
||||||
"""Setup the table for the variables.
|
"""Setup the table for the variables."""
|
||||||
"""
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def setup_pulsetable(self) -> None:
|
def setup_pulsetable(self) -> None:
|
||||||
"""Setup the table for the pulse sequence. Also add buttons for saving and loading pulse sequences and editing and creation of events
|
"""Setup the table for the pulse sequence. Also add buttons for saving and loading pulse sequences and editing and creation of events"""
|
||||||
"""
|
|
||||||
# Create pulse table
|
# Create pulse table
|
||||||
self.title = QLabel("Pulse Sequence: %s" % self.module.model.pulse_sequence.name)
|
self.title = QLabel(
|
||||||
|
"Pulse Sequence: %s" % self.module.model.pulse_sequence.name
|
||||||
|
)
|
||||||
# Make title bold
|
# Make title bold
|
||||||
font = self.title.font()
|
font = self.title.font()
|
||||||
font.setBold(True)
|
font.setBold(True)
|
||||||
|
@ -44,7 +68,9 @@ class PulseProgrammerView(ModuleView):
|
||||||
|
|
||||||
# Table setup
|
# Table setup
|
||||||
self.pulse_table = QTableWidget(self)
|
self.pulse_table = QTableWidget(self)
|
||||||
self.pulse_table.setSizeAdjustPolicy(QTableWidget.SizeAdjustPolicy.AdjustToContents)
|
self.pulse_table.setSizeAdjustPolicy(
|
||||||
|
QTableWidget.SizeAdjustPolicy.AdjustToContents
|
||||||
|
)
|
||||||
self.pulse_table.setAlternatingRowColors(True)
|
self.pulse_table.setAlternatingRowColors(True)
|
||||||
layout = QVBoxLayout()
|
layout = QVBoxLayout()
|
||||||
button_layout = QHBoxLayout()
|
button_layout = QHBoxLayout()
|
||||||
|
@ -62,7 +88,9 @@ class PulseProgrammerView(ModuleView):
|
||||||
|
|
||||||
# Add button for save pulse sequence
|
# Add button for save pulse sequence
|
||||||
self.save_pulse_sequence_button = QPushButton("Save pulse sequence")
|
self.save_pulse_sequence_button = QPushButton("Save pulse sequence")
|
||||||
self.save_pulse_sequence_button.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
|
self.save_pulse_sequence_button.setSizePolicy(
|
||||||
|
QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed
|
||||||
|
)
|
||||||
# Add the Save Icon to the button
|
# Add the Save Icon to the button
|
||||||
icon = Logos.Save16x16()
|
icon = Logos.Save16x16()
|
||||||
self.save_pulse_sequence_button.setIconSize(icon.availableSizes()[0])
|
self.save_pulse_sequence_button.setIconSize(icon.availableSizes()[0])
|
||||||
|
@ -83,7 +111,6 @@ class PulseProgrammerView(ModuleView):
|
||||||
self.module.model.events_changed.connect(self.on_events_changed)
|
self.module.model.events_changed.connect(self.on_events_changed)
|
||||||
self.module.model.pulse_sequence_changed.connect(self.on_pulse_sequence_changed)
|
self.module.model.pulse_sequence_changed.connect(self.on_pulse_sequence_changed)
|
||||||
|
|
||||||
|
|
||||||
button_layout.addStretch(1)
|
button_layout.addStretch(1)
|
||||||
layout.addWidget(self.title)
|
layout.addWidget(self.title)
|
||||||
layout.addLayout(button_layout)
|
layout.addLayout(button_layout)
|
||||||
|
@ -98,19 +125,21 @@ class PulseProgrammerView(ModuleView):
|
||||||
|
|
||||||
self.on_events_changed()
|
self.on_events_changed()
|
||||||
|
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def on_pulse_sequence_changed(self) -> None:
|
def on_pulse_sequence_changed(self) -> None:
|
||||||
"""This method is called whenever the pulse sequence changes. It updates the view to reflect the changes.
|
"""This method is called whenever the pulse sequence changes. It updates the view to reflect the changes."""
|
||||||
"""
|
logger.debug(
|
||||||
logger.debug("Updating pulse sequence to %s", self.module.model.pulse_sequence.name)
|
"Updating pulse sequence to %s", self.module.model.pulse_sequence.name
|
||||||
|
)
|
||||||
self.title.setText("Pulse Sequence: %s" % self.module.model.pulse_sequence.name)
|
self.title.setText("Pulse Sequence: %s" % self.module.model.pulse_sequence.name)
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def on_pulse_parameter_options_changed(self) -> None:
|
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.
|
"""This method is called whenever the pulse parameter options change. It updates the view to reflect the changes."""
|
||||||
"""
|
logger.debug(
|
||||||
logger.debug("Updating pulse parameter options to %s", self.module.model.pulse_parameter_options.keys())
|
"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
|
# 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)
|
self.pulse_table.setRowCount(len(self.module.model.pulse_parameter_options) + 1)
|
||||||
# Move the vertical header labels on row down
|
# Move the vertical header labels on row down
|
||||||
|
@ -120,21 +149,22 @@ class PulseProgrammerView(ModuleView):
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def on_new_event_button_clicked(self) -> None:
|
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.
|
"""This method is called whenever the new event button is clicked. It creates a new event and adds it to the pulse sequence."""
|
||||||
"""
|
|
||||||
# Create a QDialog for the new event
|
# Create a QDialog for the new event
|
||||||
logger.debug("New event button clicked")
|
logger.debug("New event button clicked")
|
||||||
dialog = AddEventDialog(self)
|
dialog = AddEventDialog(self)
|
||||||
result = dialog.exec()
|
result = dialog.exec()
|
||||||
if result:
|
if result:
|
||||||
event_name = dialog.get_name()
|
event_name = dialog.get_name()
|
||||||
logger.debug("Adding new event with name %s", event_name)
|
duration = dialog.get_duration()
|
||||||
self.module.model.add_event(event_name)
|
logger.debug(
|
||||||
|
"Adding new event with name %s, duration %g", event_name, duration
|
||||||
|
)
|
||||||
|
self.module.model.add_event(event_name, duration)
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def on_events_changed(self) -> None:
|
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.
|
"""This method is called whenever the events in the pulse sequence change. It updates the view to reflect the changes."""
|
||||||
"""
|
|
||||||
logger.debug("Updating events to %s", self.module.model.pulse_sequence.events)
|
logger.debug("Updating events to %s", self.module.model.pulse_sequence.events)
|
||||||
|
|
||||||
# Add label for the event lengths
|
# Add label for the event lengths
|
||||||
|
@ -145,7 +175,9 @@ class PulseProgrammerView(ModuleView):
|
||||||
for event in self.module.model.pulse_sequence.events:
|
for event in self.module.model.pulse_sequence.events:
|
||||||
logger.debug("Adding event to pulseprogrammer view: %s", event.name)
|
logger.debug("Adding event to pulseprogrammer view: %s", event.name)
|
||||||
# Create a label for the event
|
# Create a label for the event
|
||||||
event_label = QLabel("%s : %s µs" % (event.name, str(event.duration * Decimal(1e6))))
|
event_label = QLabel(
|
||||||
|
"%s : %.16g µs" % (event.name, (event.duration * Decimal(1e6)))
|
||||||
|
)
|
||||||
event_layout.addWidget(event_label)
|
event_layout.addWidget(event_label)
|
||||||
|
|
||||||
# Delete the old widget and create a new one
|
# Delete the old widget and create a new one
|
||||||
|
@ -155,33 +187,52 @@ class PulseProgrammerView(ModuleView):
|
||||||
self.layout().addWidget(self.event_widget)
|
self.layout().addWidget(self.event_widget)
|
||||||
|
|
||||||
self.pulse_table.setColumnCount(len(self.module.model.pulse_sequence.events))
|
self.pulse_table.setColumnCount(len(self.module.model.pulse_sequence.events))
|
||||||
self.pulse_table.setHorizontalHeaderLabels([event.name for event in self.module.model.pulse_sequence.events])
|
self.pulse_table.setHorizontalHeaderLabels(
|
||||||
|
[event.name for event in self.module.model.pulse_sequence.events]
|
||||||
|
)
|
||||||
|
|
||||||
self.set_parameter_icons()
|
self.set_parameter_icons()
|
||||||
|
|
||||||
def set_parameter_icons(self) -> None:
|
def set_parameter_icons(self) -> None:
|
||||||
"""This method sets the icons for the pulse parameter options.
|
"""This method sets the icons for the pulse parameter options."""
|
||||||
"""
|
|
||||||
for column_idx, event in enumerate(self.module.model.pulse_sequence.events):
|
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(
|
||||||
|
self.module.model.pulse_parameter_options.keys()
|
||||||
|
):
|
||||||
if row_idx == 0:
|
if row_idx == 0:
|
||||||
event_options_widget = EventOptionsWidget(event)
|
event_options_widget = EventOptionsWidget(event)
|
||||||
# Connect the delete_event signal to the on_delete_event slot
|
# Connect the delete_event signal to the on_delete_event slot
|
||||||
func = functools.partial(self.module.controller.delete_event, event_name=event.name)
|
func = functools.partial(
|
||||||
|
self.module.controller.delete_event, event_name=event.name
|
||||||
|
)
|
||||||
event_options_widget.delete_event.connect(func)
|
event_options_widget.delete_event.connect(func)
|
||||||
# Connect the change_event_duration signal to the on_change_event_duration slot
|
# Connect the change_event_duration signal to the on_change_event_duration slot
|
||||||
event_options_widget.change_event_duration.connect(self.module.controller.change_event_duration)
|
event_options_widget.change_event_duration.connect(
|
||||||
|
self.module.controller.change_event_duration
|
||||||
|
)
|
||||||
# Connect the change_event_name signal to the on_change_event_name slot
|
# Connect the change_event_name signal to the on_change_event_name slot
|
||||||
event_options_widget.change_event_name.connect(self.module.controller.change_event_name)
|
event_options_widget.change_event_name.connect(
|
||||||
|
self.module.controller.change_event_name
|
||||||
|
)
|
||||||
# Connect the move_event_left signal to the on_move_event_left slot
|
# Connect the move_event_left signal to the on_move_event_left slot
|
||||||
event_options_widget.move_event_left.connect(self.module.controller.on_move_event_left)
|
event_options_widget.move_event_left.connect(
|
||||||
|
self.module.controller.on_move_event_left
|
||||||
|
)
|
||||||
# Connect the move_event_right signal to the on_move_event_right slot
|
# Connect the move_event_right signal to the on_move_event_right slot
|
||||||
event_options_widget.move_event_right.connect(self.module.controller.on_move_event_right)
|
event_options_widget.move_event_right.connect(
|
||||||
|
self.module.controller.on_move_event_right
|
||||||
|
)
|
||||||
|
|
||||||
self.pulse_table.setCellWidget(row_idx, column_idx, event_options_widget)
|
self.pulse_table.setCellWidget(
|
||||||
self.pulse_table.setRowHeight(row_idx, event_options_widget.layout().sizeHint().height())
|
row_idx, column_idx, event_options_widget
|
||||||
|
)
|
||||||
|
self.pulse_table.setRowHeight(
|
||||||
|
row_idx, event_options_widget.layout().sizeHint().height()
|
||||||
|
)
|
||||||
|
|
||||||
logger.debug("Adding button for event %s and parameter %s", event, parameter)
|
logger.debug(
|
||||||
|
"Adding button for event %s and parameter %s", event, parameter
|
||||||
|
)
|
||||||
logger.debug("Parameter object id: %s", id(event.parameters[parameter]))
|
logger.debug("Parameter object id: %s", id(event.parameters[parameter]))
|
||||||
button = QPushButton()
|
button = QPushButton()
|
||||||
icon = event.parameters[parameter].get_pixmap()
|
icon = event.parameters[parameter].get_pixmap()
|
||||||
|
@ -192,15 +243,21 @@ class PulseProgrammerView(ModuleView):
|
||||||
|
|
||||||
# We add 1 to the row index because the first row is used for the event options
|
# We add 1 to the row index because the first row is used for the event options
|
||||||
self.pulse_table.setCellWidget(row_idx + 1, column_idx, button)
|
self.pulse_table.setCellWidget(row_idx + 1, column_idx, button)
|
||||||
self.pulse_table.setRowHeight(row_idx + 1, icon.availableSizes()[0].height())
|
self.pulse_table.setRowHeight(
|
||||||
self.pulse_table.setColumnWidth(column_idx, icon.availableSizes()[0].width())
|
row_idx + 1, icon.availableSizes()[0].height()
|
||||||
|
)
|
||||||
|
self.pulse_table.setColumnWidth(
|
||||||
|
column_idx, icon.availableSizes()[0].width()
|
||||||
|
)
|
||||||
|
|
||||||
# Connect the button to the on_button_clicked slot
|
# Connect the button to the on_button_clicked slot
|
||||||
func = functools.partial(self.on_table_button_clicked, event=event, parameter=parameter)
|
func = functools.partial(
|
||||||
|
self.on_table_button_clicked, event=event, parameter=parameter
|
||||||
|
)
|
||||||
button.clicked.connect(func)
|
button.clicked.connect(func)
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def on_table_button_clicked(self, event , parameter) -> None:
|
def on_table_button_clicked(self, event, parameter) -> None:
|
||||||
"""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.
|
||||||
|
@ -209,7 +266,13 @@ class PulseProgrammerView(ModuleView):
|
||||||
|
|
||||||
if result:
|
if result:
|
||||||
for option, function in dialog.return_functions.items():
|
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())
|
logger.debug(
|
||||||
|
"Setting option %s of parameter %s in event %s to %s",
|
||||||
|
option,
|
||||||
|
parameter,
|
||||||
|
event,
|
||||||
|
function(),
|
||||||
|
)
|
||||||
option.set_value(function())
|
option.set_value(function())
|
||||||
|
|
||||||
self.set_parameter_icons()
|
self.set_parameter_icons()
|
||||||
|
@ -232,8 +295,9 @@ class PulseProgrammerView(ModuleView):
|
||||||
if file_name:
|
if file_name:
|
||||||
self.module.controller.load_pulse_sequence(file_name)
|
self.module.controller.load_pulse_sequence(file_name)
|
||||||
|
|
||||||
|
|
||||||
class EventOptionsWidget(QWidget):
|
class EventOptionsWidget(QWidget):
|
||||||
""" This class is a widget that can be used to set the options for a pulse parameter.
|
"""This class is a widget that can be used to set the options for a pulse parameter.
|
||||||
This widget is then added to the the first row of the according event column in the pulse table.
|
This widget is then added to the the first row of the according event column in the pulse table.
|
||||||
It has a edit button that opens a dialog that allows the user to change the options for the event (name and duration).
|
It has a edit button that opens a dialog that allows the user to change the options for the event (name and duration).
|
||||||
Furthermore it has a delete button that deletes the event from the pulse sequence.
|
Furthermore it has a delete button that deletes the event from the pulse sequence.
|
||||||
|
@ -299,7 +363,8 @@ class EventOptionsWidget(QWidget):
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def edit_event(self) -> None:
|
def edit_event(self) -> None:
|
||||||
"""This method is called when the edit button is clicked. It opens a dialog that allows the user to change the event name and duration.
|
"""This method is called when the edit button is clicked. It opens a dialog that allows the user to change the event name and duration.
|
||||||
If the user clicks ok, the change_event_name and change_event_duration signals are emitted."""
|
If the user clicks ok, the change_event_name and change_event_duration signals are emitted.
|
||||||
|
"""
|
||||||
logger.debug("Edit button clicked for event %s", self.event.name)
|
logger.debug("Edit button clicked for event %s", self.event.name)
|
||||||
|
|
||||||
# Create a QDialog to edit the event
|
# Create a QDialog to edit the event
|
||||||
|
@ -325,7 +390,9 @@ class EventOptionsWidget(QWidget):
|
||||||
event_form_layout.addRow(duration_layout)
|
event_form_layout.addRow(duration_layout)
|
||||||
layout.addLayout(event_form_layout)
|
layout.addLayout(event_form_layout)
|
||||||
|
|
||||||
buttons = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel)
|
buttons = QDialogButtonBox(
|
||||||
|
QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel
|
||||||
|
)
|
||||||
buttons.accepted.connect(dialog.accept)
|
buttons.accepted.connect(dialog.accept)
|
||||||
buttons.rejected.connect(dialog.reject)
|
buttons.rejected.connect(dialog.reject)
|
||||||
layout.addWidget(buttons)
|
layout.addWidget(buttons)
|
||||||
|
@ -336,8 +403,9 @@ class EventOptionsWidget(QWidget):
|
||||||
if name_lineedit.text() != self.event.name:
|
if name_lineedit.text() != self.event.name:
|
||||||
self.change_event_name.emit(self.event.name, name_lineedit.text())
|
self.change_event_name.emit(self.event.name, name_lineedit.text())
|
||||||
if duration_lineedit.text() != str(self.event.duration):
|
if duration_lineedit.text() != str(self.event.duration):
|
||||||
self.change_event_duration.emit(self.event.name, duration_lineedit.text())
|
self.change_event_duration.emit(
|
||||||
|
self.event.name, duration_lineedit.text()
|
||||||
|
)
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def create_delete_event_dialog(self) -> None:
|
def create_delete_event_dialog(self) -> None:
|
||||||
|
@ -351,7 +419,9 @@ class EventOptionsWidget(QWidget):
|
||||||
layout = QVBoxLayout()
|
layout = QVBoxLayout()
|
||||||
label = QLabel("Are you sure you want to delete event %s?" % self.event.name)
|
label = QLabel("Are you sure you want to delete event %s?" % self.event.name)
|
||||||
layout.addWidget(label)
|
layout.addWidget(label)
|
||||||
buttons = QDialogButtonBox(QDialogButtonBox.StandardButton.Yes | QDialogButtonBox.StandardButton.No)
|
buttons = QDialogButtonBox(
|
||||||
|
QDialogButtonBox.StandardButton.Yes | QDialogButtonBox.StandardButton.No
|
||||||
|
)
|
||||||
buttons.accepted.connect(dialog.accept)
|
buttons.accepted.connect(dialog.accept)
|
||||||
buttons.rejected.connect(dialog.reject)
|
buttons.rejected.connect(dialog.reject)
|
||||||
layout.addWidget(buttons)
|
layout.addWidget(buttons)
|
||||||
|
@ -373,8 +443,9 @@ class EventOptionsWidget(QWidget):
|
||||||
|
|
||||||
|
|
||||||
class OptionsDialog(QDialog):
|
class OptionsDialog(QDialog):
|
||||||
""" This dialog is created whenever the edit button for a pulse option is clicked.
|
"""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."""
|
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):
|
def __init__(self, event, parameter, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
@ -444,6 +515,7 @@ class OptionsDialog(QDialog):
|
||||||
|
|
||||||
self.layout.addWidget(self.buttons)
|
self.layout.addWidget(self.buttons)
|
||||||
|
|
||||||
|
|
||||||
class FunctionOptionWidget(QWidget):
|
class FunctionOptionWidget(QWidget):
|
||||||
"""This class is a widget that can be used to set the options for a pulse parameter.
|
"""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.
|
It plots the given function in time and frequency domain.
|
||||||
|
@ -459,7 +531,9 @@ class FunctionOptionWidget(QWidget):
|
||||||
inner_layout = QHBoxLayout()
|
inner_layout = QHBoxLayout()
|
||||||
for function in function_option.functions:
|
for function in function_option.functions:
|
||||||
button = QPushButton(function.name)
|
button = QPushButton(function.name)
|
||||||
button.clicked.connect(functools.partial(self.on_functionbutton_clicked, function=function))
|
button.clicked.connect(
|
||||||
|
functools.partial(self.on_functionbutton_clicked, function=function)
|
||||||
|
)
|
||||||
inner_layout.addWidget(button)
|
inner_layout.addWidget(button)
|
||||||
|
|
||||||
layout.addLayout(inner_layout)
|
layout.addLayout(inner_layout)
|
||||||
|
@ -467,11 +541,13 @@ class FunctionOptionWidget(QWidget):
|
||||||
|
|
||||||
# Add Advanced settings button
|
# Add Advanced settings button
|
||||||
self.advanced_settings_button = QPushButton("Show Advanced settings")
|
self.advanced_settings_button = QPushButton("Show Advanced settings")
|
||||||
self.advanced_settings_button.clicked.connect(self.on_advanced_settings_button_clicked)
|
self.advanced_settings_button.clicked.connect(
|
||||||
|
self.on_advanced_settings_button_clicked
|
||||||
|
)
|
||||||
layout.addWidget(self.advanced_settings_button)
|
layout.addWidget(self.advanced_settings_button)
|
||||||
|
|
||||||
# Add advanced settings widget
|
# Add advanced settings widget
|
||||||
self.advanced_settings = QGroupBox('Advanced Settings')
|
self.advanced_settings = QGroupBox("Advanced Settings")
|
||||||
self.advanced_settings.setHidden(True)
|
self.advanced_settings.setHidden(True)
|
||||||
self.advanced_settings_layout = QFormLayout()
|
self.advanced_settings_layout = QFormLayout()
|
||||||
self.advanced_settings.setLayout(self.advanced_settings_layout)
|
self.advanced_settings.setLayout(self.advanced_settings_layout)
|
||||||
|
@ -535,13 +611,14 @@ class FunctionOptionWidget(QWidget):
|
||||||
logger.debug("Invalid expression: %s", self.expr_lineedit.text())
|
logger.debug("Invalid expression: %s", self.expr_lineedit.text())
|
||||||
self.expr_lineedit.setText(str(self.function_option.value.expr))
|
self.expr_lineedit.setText(str(self.function_option.value.expr))
|
||||||
# Create message box that tells the user that the expression is invalid
|
# 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.create_message_box(
|
||||||
|
"Invalid expression",
|
||||||
|
"The expression you entered is invalid. Please enter a valid expression.",
|
||||||
|
)
|
||||||
|
|
||||||
self.delete_active_function()
|
self.delete_active_function()
|
||||||
self.load_active_function()
|
self.load_active_function()
|
||||||
|
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def on_advanced_settings_button_clicked(self) -> None:
|
def on_advanced_settings_button_clicked(self) -> None:
|
||||||
"""This function is called when the advanced settings button is clicked.
|
"""This function is called when the advanced settings button is clicked.
|
||||||
|
@ -549,11 +626,10 @@ class FunctionOptionWidget(QWidget):
|
||||||
"""
|
"""
|
||||||
if self.advanced_settings.isHidden():
|
if self.advanced_settings.isHidden():
|
||||||
self.advanced_settings.setHidden(False)
|
self.advanced_settings.setHidden(False)
|
||||||
self.advanced_settings_button.setText('Hide Advanced Settings')
|
self.advanced_settings_button.setText("Hide Advanced Settings")
|
||||||
else:
|
else:
|
||||||
self.advanced_settings.setHidden(True)
|
self.advanced_settings.setHidden(True)
|
||||||
self.advanced_settings_button.setText('Show Advanced Settings')
|
self.advanced_settings_button.setText("Show Advanced Settings")
|
||||||
|
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def on_functionbutton_clicked(self, function) -> None:
|
def on_functionbutton_clicked(self, function) -> None:
|
||||||
|
@ -613,7 +689,9 @@ class FunctionOptionWidget(QWidget):
|
||||||
parameter_label = QLabel(parameter.name)
|
parameter_label = QLabel(parameter.name)
|
||||||
parameter_lineedit = QLineEdit(str(parameter.value))
|
parameter_lineedit = QLineEdit(str(parameter.value))
|
||||||
# Add the parameter_lineedit editingFinished signal to the paramter.set_value slot
|
# Add the parameter_lineedit editingFinished signal to the paramter.set_value slot
|
||||||
parameter_lineedit.editingFinished.connect(lambda: parameter.set_value(parameter_lineedit.text()))
|
parameter_lineedit.editingFinished.connect(
|
||||||
|
lambda: parameter.set_value(parameter_lineedit.text())
|
||||||
|
)
|
||||||
|
|
||||||
# Create a QHBoxLayout
|
# Create a QHBoxLayout
|
||||||
hbox = QHBoxLayout()
|
hbox = QHBoxLayout()
|
||||||
|
@ -636,7 +714,7 @@ class FunctionOptionWidget(QWidget):
|
||||||
self.end_x_lineedit.setText(str(self.function_option.value.end_x))
|
self.end_x_lineedit.setText(str(self.function_option.value.end_x))
|
||||||
self.expr_lineedit.setText(str(self.function_option.value.expr))
|
self.expr_lineedit.setText(str(self.function_option.value.expr))
|
||||||
|
|
||||||
def create_message_box(self, message : str, information : str) -> None:
|
def create_message_box(self, message: str, information: str) -> None:
|
||||||
"""Creates a message box with the given message and information and shows it.
|
"""Creates a message box with the given message and information and shows it.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
@ -652,25 +730,47 @@ class FunctionOptionWidget(QWidget):
|
||||||
|
|
||||||
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."""
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
|
||||||
self.setWindowTitle("Add Event")
|
self.setWindowTitle("Add Event")
|
||||||
|
|
||||||
self.layout = QVBoxLayout(self)
|
self.layout = QFormLayout(self)
|
||||||
|
|
||||||
|
self.name_layout = QHBoxLayout()
|
||||||
|
|
||||||
self.label = QLabel("Enter event name:")
|
self.label = QLabel("Enter event name:")
|
||||||
self.name_input = QLineEdit()
|
self.name_input = DuckEdit()
|
||||||
|
self.name_input.validator = self.NameInputValidator(self)
|
||||||
|
|
||||||
|
self.name_layout.addWidget(self.label)
|
||||||
|
self.name_layout.addWidget(self.name_input)
|
||||||
|
|
||||||
|
self.layout.addRow(self.name_layout)
|
||||||
|
|
||||||
|
self.duration_layout = QHBoxLayout()
|
||||||
|
|
||||||
|
self.duration_label = QLabel("Duration:")
|
||||||
|
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.addWidget(self.duration_lineedit)
|
||||||
|
|
||||||
|
self.duration_layout.addWidget(self.unit_label)
|
||||||
|
|
||||||
|
self.layout.addRow(self.duration_layout)
|
||||||
|
|
||||||
self.buttons = QDialogButtonBox(
|
self.buttons = QDialogButtonBox(
|
||||||
QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel,
|
QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel,
|
||||||
self,
|
self,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.buttons.accepted.connect(self.check_input)
|
self.buttons.accepted.connect(self.check_input)
|
||||||
self.buttons.rejected.connect(self.reject)
|
self.buttons.rejected.connect(self.reject)
|
||||||
|
|
||||||
self.layout.addWidget(self.label)
|
|
||||||
self.layout.addWidget(self.name_input)
|
|
||||||
self.layout.addWidget(self.buttons)
|
self.layout.addWidget(self.buttons)
|
||||||
|
|
||||||
def get_name(self) -> str:
|
def get_name(self) -> str:
|
||||||
|
@ -680,20 +780,48 @@ class AddEventDialog(QDialog):
|
||||||
str: The name entered by the user"""
|
str: The name entered by the user"""
|
||||||
return self.name_input.text()
|
return self.name_input.text()
|
||||||
|
|
||||||
def check_input(self) -> None:
|
def get_duration(self) -> Decimal:
|
||||||
"""Checks if the name entered by the user is valid. If it is, the dialog is accepted. If not, the user is informed of the error.
|
"""Returns the duration entered by the user, or a fallback value."
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Decimal: The duration value provided by the user, or 20"""
|
||||||
|
return Decimal(self.duration_lineedit.text() or 20)
|
||||||
|
|
||||||
|
class NameInputValidator(QValidator):
|
||||||
|
"""A validator for the name input field.
|
||||||
|
|
||||||
|
This is used to validate the input of the QLineEdit widget.
|
||||||
"""
|
"""
|
||||||
# Make sure that name is not empty and that event name doesn't already exist.
|
def validate(self, value, position):
|
||||||
if self.name_input.text() == "":
|
"""Validates the input value.
|
||||||
self.label.setText("Please enter a name for the event.")
|
|
||||||
elif self.name_input.text() in self.parent().module.model.pulse_sequence.events:
|
Args:
|
||||||
self.label.setText("Event name already exists. Please enter a different name.")
|
value (str): The input value
|
||||||
else:
|
position (int): The position of the cursor
|
||||||
self.accept()
|
|
||||||
|
Returns:
|
||||||
|
Tuple[QValidator.State, str, int]: The validation state, the fixed value, and the position
|
||||||
|
"""
|
||||||
|
if not value:
|
||||||
|
return (QValidator.State.Intermediate, value, position)
|
||||||
|
|
||||||
|
if any(
|
||||||
|
[
|
||||||
|
event.name == value
|
||||||
|
for event in self.parent()
|
||||||
|
.parent()
|
||||||
|
.module.model.pulse_sequence.events
|
||||||
|
]
|
||||||
|
):
|
||||||
|
return (QValidator.State.Invalid, value, position)
|
||||||
|
|
||||||
|
return (QValidator.State.Acceptable, value, position)
|
||||||
|
|
||||||
|
|
||||||
# This class should be refactored in the module view so it can be used by all modules
|
# This class should be refactored in the module view so it can be used by all modules
|
||||||
class QFileManager:
|
class QFileManager:
|
||||||
"""This class provides methods for opening and saving files."""
|
"""This class provides methods for opening and saving files."""
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
|
|
||||||
|
@ -703,11 +831,13 @@ class QFileManager:
|
||||||
Returns:
|
Returns:
|
||||||
str: The path of the file selected by the user.
|
str: The path of the file selected by the user.
|
||||||
"""
|
"""
|
||||||
fileName, _ = QFileDialog.getOpenFileName(self.parent,
|
fileName, _ = QFileDialog.getOpenFileName(
|
||||||
"QFileManager - Open File",
|
self.parent,
|
||||||
"",
|
"QFileManager - Open File",
|
||||||
"Quack Files (*.quack);;All Files (*)",
|
"",
|
||||||
options = QFileDialog.Option.ReadOnly)
|
"Quack Files (*.quack);;All Files (*)",
|
||||||
|
options=QFileDialog.Option.ReadOnly,
|
||||||
|
)
|
||||||
if fileName:
|
if fileName:
|
||||||
return fileName
|
return fileName
|
||||||
else:
|
else:
|
||||||
|
@ -719,16 +849,17 @@ class QFileManager:
|
||||||
Returns:
|
Returns:
|
||||||
str: The path of the file selected by the user.
|
str: The path of the file selected by the user.
|
||||||
"""
|
"""
|
||||||
fileName, _ = QFileDialog.getSaveFileName(self.parent,
|
fileName, _ = QFileDialog.getSaveFileName(
|
||||||
"QFileManager - Save File",
|
self.parent,
|
||||||
"",
|
"QFileManager - Save File",
|
||||||
"Quack Files (*.quack);;All Files (*)",
|
"",
|
||||||
options=QFileDialog.Option.DontUseNativeDialog)
|
"Quack Files (*.quack);;All Files (*)",
|
||||||
|
options=QFileDialog.Option.DontUseNativeDialog,
|
||||||
|
)
|
||||||
if fileName:
|
if fileName:
|
||||||
# Append the .quack extension if not present
|
# Append the .quack extension if not present
|
||||||
if not fileName.endswith('.quack'):
|
if not fileName.endswith(".quack"):
|
||||||
fileName += '.quack'
|
fileName += ".quack"
|
||||||
return fileName
|
return fileName
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
Loading…
Reference in a new issue