Implemented loading and saving of settings.

This commit is contained in:
jupfi 2024-05-24 17:28:57 +02:00
parent 9187ffb9ec
commit 6496ec6824
4 changed files with 133 additions and 18 deletions

View file

@ -1,5 +1,6 @@
"""Base class for all spectrometer controllers.""" """Base class for all spectrometer controllers."""
import ast
from nqrduck.module.module_controller import ModuleController from nqrduck.module.module_controller import ModuleController
@ -10,6 +11,47 @@ class BaseSpectrometerController(ModuleController):
"""Initializes the spectrometer controller.""" """Initializes the spectrometer controller."""
super().__init__(module) super().__init__(module)
def save_settings(self, path: str) -> None:
"""Saves the settings of the spectrometer."""
# We get the different settings objects from the model
settings = self.module.model.settings
json = {}
json["name"] = self.module.model.name
for category in settings.keys():
for setting in settings[category]:
json[setting.name] = setting.value
with open(path, "w") as f:
f.write(str(json))
def load_settings(self, path: str) -> None:
"""Loads the settings of the spectrometer."""
with open(path, "r") as f:
json = f.read()
# string to dict
json = ast.literal_eval(json)
module_name = self.module.model.name
json_name = json["name"]
# For some reason the notification is shown twice
if module_name != json_name:
message = f"Module: {module_name} not compatible with module specified in settings file: {json_name}. Did you select the correct settings file?"
self.module.nqrduck_signal.emit("notification", ["Error", message])
return
settings = self.module.model.settings
for category in settings.keys():
for setting in settings[category]:
if setting.name in json:
setting.value = json[setting.name]
else:
message = f"Setting {setting.name} not found in settings file. A change in settings might have broken compatibility."
self.module.nqrduck_signal.emit("notification", ["Error", message])
def start_measurement(self): def start_measurement(self):
"""Starts the measurement. """Starts the measurement.

View file

@ -22,6 +22,8 @@ class BaseSpectrometerModel(ModuleModel):
pulse_parameter_options (OrderedDict) : The pulse parameter options of the spectrometer pulse_parameter_options (OrderedDict) : The pulse parameter options of the spectrometer
""" """
SETTING_FILE_EXTENSION = "setduck"
settings: OrderedDict settings: OrderedDict
pulse_parameter_options: OrderedDict pulse_parameter_options: OrderedDict

View file

@ -8,6 +8,7 @@ from PyQt6.QtWidgets import (
QSizePolicy, QSizePolicy,
QSpacerItem, QSpacerItem,
QVBoxLayout, QVBoxLayout,
QPushButton,
) )
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
@ -91,3 +92,43 @@ class BaseSpectrometerView(ModuleView):
# Push all the settings to the top of the widget # Push all the settings to the top of the widget
self._ui_form.verticalLayout.addStretch(1) self._ui_form.verticalLayout.addStretch(1)
# Now we add a save and load button to the widget
self.button_layout = QHBoxLayout()
self.save_button = QPushButton("Save Settings")
self.save_button.setIcon(Logos.Save16x16())
#self.save_button.setIconSize(self.save_button.size())
self.save_button.clicked.connect(self.on_save_button_clicked)
self.button_layout.addWidget(self.save_button)
self.load_button = QPushButton("Load Settings")
self.load_button.setIcon(Logos.Load16x16())
#self.load_button.setIconSize(self.load_button.size())
self.load_button.clicked.connect(self.on_load_button_clicked)
self.button_layout.addWidget(self.load_button)
self._ui_form.verticalLayout.addLayout(self.button_layout)
def on_save_button_clicked(self):
"""This method is called when the save button is clicked."""
logger.debug("Save button clicked")
# Open a dialog to save the settings to a file
file_manager = self.FileManager(
extension=self.module.model.SETTING_FILE_EXTENSION, parent=self
)
path = file_manager.saveFileDialog()
if path:
self.module.controller.save_settings(path)
def on_load_button_clicked(self):
"""This method is called when the load button is clicked."""
logger.debug("Load button clicked")
# Open a dialog to load the settings from a file
file_manager = self.FileManager(
extension=self.module.model.SETTING_FILE_EXTENSION, parent=self
)
path = file_manager.loadFileDialog()
self.module.controller.load_settings(path)
if path:
self.module.controller.load_settings(path)

View file

@ -36,6 +36,7 @@ class Setting(QObject):
description (str): A description of the setting. description (str): A description of the setting.
default: The default value of the setting. default: The default value of the setting.
""" """
self.widget = None
super().__init__() super().__init__()
self.name = name self.name = name
self.description = description self.description = description
@ -87,9 +88,16 @@ class NumericalSetting(Setting):
It can additionally have a minimum and maximum value. It can additionally have a minimum and maximum value.
""" """
def __init__(self, name: str, description: str, default, min_value = None, max_value = None ) -> None:
def __init__(
self, name: str, description: str, default, min_value=None, max_value=None
) -> None:
"""Create a new numerical setting.""" """Create a new numerical setting."""
super().__init__(name, self.description_limit_info(description, min_value, max_value), default) super().__init__(
name,
self.description_limit_info(description, min_value, max_value),
default,
)
def description_limit_info(self, description: str, min_value, max_value) -> str: def description_limit_info(self, description: str, min_value, max_value) -> str:
"""Updates the description with the limits of the setting if there are any. """Updates the description with the limits of the setting if there are any.
@ -103,11 +111,11 @@ class NumericalSetting(Setting):
str: The description of the setting with the limits. str: The description of the setting with the limits.
""" """
if min_value is not None and max_value is not None: if min_value is not None and max_value is not None:
description += (f"\n (min: {min_value}, max: {max_value})") description += f"\n (min: {min_value}, max: {max_value})"
elif min_value is not None: elif min_value is not None:
description += (f"\n (min: {min_value})") description += f"\n (min: {min_value})"
elif max_value is not None: elif max_value is not None:
description += (f"\n (max: {max_value})") description += f"\n (max: {max_value})"
return description return description
@ -133,13 +141,19 @@ class FloatSetting(NumericalSetting):
description: str, description: str,
min_value: float = None, min_value: float = None,
max_value: float = None, max_value: float = None,
spin_box: tuple = (False, False) spin_box: tuple = (False, False),
) -> None: ) -> None:
"""Create a new float setting.""" """Create a new float setting."""
self.spin_box = spin_box
super().__init__(name, description, default, min_value, max_value) super().__init__(name, description, default, min_value, max_value)
if spin_box[0]: if spin_box[0]:
self.widget = DuckSpinBox(min_value=min_value, max_value=max_value, slider=spin_box[1], double_box=True) self.widget = DuckSpinBox(
min_value=min_value,
max_value=max_value,
slider=spin_box[1],
double_box=True,
)
self.widget.spin_box.setValue(default) self.widget.spin_box.setValue(default)
else: else:
self.widget = DuckFloatEdit(min_value=min_value, max_value=max_value) self.widget = DuckFloatEdit(min_value=min_value, max_value=max_value)
@ -169,6 +183,12 @@ class FloatSetting(NumericalSetting):
self._value = float(value) self._value = float(value)
self.settings_changed.emit() self.settings_changed.emit()
if self.widget:
if self.spin_box[0]:
self.widget.spin_box.setValue(self._value)
else:
self.widget.setText(str(self._value))
class IntSetting(NumericalSetting): class IntSetting(NumericalSetting):
"""A setting that is an Integer. """A setting that is an Integer.
@ -189,13 +209,15 @@ class IntSetting(NumericalSetting):
description: str, description: str,
min_value=None, min_value=None,
max_value=None, max_value=None,
spin_box: tuple = (False, False) spin_box: tuple = (False, False),
) -> None: ) -> None:
"""Create a new int setting.""" """Create a new int setting."""
self.spin_box = spin_box
super().__init__(name, description, default, min_value, max_value) super().__init__(name, description, default, min_value, max_value)
if self.spin_box[0]:
if spin_box[0]: self.widget = DuckSpinBox(
self.widget = DuckSpinBox(min_value=min_value, max_value=max_value, slider=spin_box[1]) min_value=min_value, max_value=max_value, slider=spin_box[1]
)
self.widget.spin_box.setValue(default) self.widget.spin_box.setValue(default)
else: else:
self.widget = DuckIntEdit(min_value=min_value, max_value=max_value) self.widget = DuckIntEdit(min_value=min_value, max_value=max_value)
@ -225,7 +247,11 @@ class IntSetting(NumericalSetting):
value = int(float(value)) value = int(float(value))
self._value = value self._value = value
self.settings_changed.emit() self.settings_changed.emit()
if self.widget:
if self.spin_box[0]:
self.widget.spin_box.setValue(value)
else:
self.widget.setText(str(value))
class BooleanSetting(Setting): class BooleanSetting(Setting):
@ -253,6 +279,8 @@ class BooleanSetting(Setting):
def value(self, value): def value(self, value):
try: try:
self._value = bool(value) self._value = bool(value)
if self.widget:
self.widget.setChecked(self._value)
self.settings_changed.emit() self.settings_changed.emit()
except ValueError: except ValueError:
raise ValueError("Value must be a bool") raise ValueError("Value must be a bool")
@ -307,6 +335,8 @@ class SelectionSetting(Setting):
try: try:
if value in self.options: if value in self.options:
self._value = value self._value = value
if self.widget:
self.widget.setCurrentText(value)
self.settings_changed.emit() self.settings_changed.emit()
else: else:
raise ValueError("Value must be one of the options") raise ValueError("Value must be one of the options")