diff --git a/cpdctrl_gui/ui/widgets/settings/app_settings.py b/cpdctrl_gui/ui/widgets/settings/app_settings.py index 3aca189..24b6db5 100644 --- a/cpdctrl_gui/ui/widgets/settings/app_settings.py +++ b/cpdctrl_gui/ui/widgets/settings/app_settings.py @@ -9,32 +9,47 @@ class AppSettings(QWidget): self.setLayout(QVBoxLayout()) self.w_form = SettingsForm(AppConfig.MAIN_CFG) self.layout().addWidget(self.w_form) + self.w_form.add_group("devices", "Devices") + self.w_form.add_group("interface", "Interface") + self.w_form.add_group("led_script", "LED Script") + self.w_form.add_group("power_switch", "Power Switch") w_cache_dir = FileSelection(filemode=QFileDialog.FileMode.Directory) self.w_form.add_form_row("dir_cache", "Cache Directory", "~/.cache/cpdctrl", w_cache_dir, "Directory to store temporary data") - self.w_form.add_form_row("led_device_auto_reconnect", "Autoconnect to last LED Controller", True, QCheckBox(), "Automatically connect to the last used LED Controller") - self.w_form.add_form_row("voltage_measurement_device_auto_reconnect", "Autoconnect to last Voltage Measurement Device", True, QCheckBox(), "Automatically connect to the last used Voltage Measurement Device") - w_plot_n = QSpinBox() - w_plot_n.setMinimum(1000) - w_plot_n.setMaximum(200000) - w_plot_n.setSingleStep(1000) - self.w_form.add_form_row("plot_max_data_points", "Max. Datapoints in the Plot", 20000, w_plot_n, "Maximum number of datapoints in the live plot.\nThis value is limited to ensure performance is not degraded in long measurements") - w_plot_dt = QSpinBox() - w_plot_dt.setMinimum(10) - w_plot_dt.setMaximum(200000) - w_plot_dt.setSingleStep(100) - self.w_form.add_form_row("plot_update_interval_ms", "Plot Update Interval (ms)", 200, w_plot_dt, "Number of milliseconds to wait before updating the live plot") - self.w_form.add_form_row("led_script_watch_file", "Watch Led Script File", False, QCheckBox(), "Watch the LED script file for changes and reload it automatically") - self.w_form.add_form_row("led_script_save_copy", "Copy Led Script to Cache Dir", True, QCheckBox(), "Save the final version of the led script in the cache directory") - self.w_form.add_form_row("metadata_auto_update", "Auto-Update Metadata", True, QCheckBox(), "Allow metadata updates while the program is running") - self.w_form.add_form_row("metadata_auto_add", "Auto-Add Metadata", True, QCheckBox(), "Automatically add measurement metadata to the data file.\nThis includes: device names, measurement mode, measurement interval, start and stop times, led script") - w_usb_switch_exe = FileSelection(filemode=QFileDialog.FileMode.ExistingFile) - self.w_form.add_form_row("power_switch_exe", "Power Switch Executable", "", w_usb_switch_exe, "Path to the USBSwitchCmd executable for the Cleware USB switch\nRequires a relaunch to take effect") + # devices + self.w_form.add_form_row("led_device_auto_reconnect", "Autoconnect to last LED Controller", True, QCheckBox(), "Automatically connect to the last used LED Controller", group="devices") + self.w_form.add_form_row("voltage_measurement_device_auto_reconnect", "Autoconnect to last Voltage Measurement Device", True, QCheckBox(), "Automatically connect to the last used Voltage Measurement Device", group="devices") w_idle_dt = QSpinBox() w_idle_dt.setMinimum(10) w_idle_dt.setMaximum(200000) w_idle_dt.setSingleStep(10) - self.w_form.add_form_row("idle_update_interval_s", "Device Connection Check Interval (s)", 30, w_plot_dt, "How often to check whether the devices are still connected.\nApplies only when not in a measurement.") - self.w_form.add_form_row("error_show_dialog", "Show Dialog on Error", True, QCheckBox(), "Whether to show a dialog window when uncaught errors occur\nLeave this on.") - self.w_form.add_form_row("error_stack_trace", "Stacktrace in errors", False, QCheckBox(), "Whether to show the call stack in unexpected error messages") + self.w_form.add_form_row("idle_update_interval_s", "Device Connection Check Interval (s)", 30, w_idle_dt, "How often to check whether the devices are still connected.\nApplies only when not in a measurement.", group="devices") + + # interface + w_plot_n = QSpinBox() + w_plot_n.setMinimum(1000) + w_plot_n.setMaximum(200000) + w_plot_n.setSingleStep(1000) + self.w_form.add_form_row("plot_max_data_points", "Max. Datapoints in the Plot", 20000, w_plot_n, "Maximum number of datapoints in the live plot.\nThis value is limited to ensure performance is not degraded in long measurements", group="interface") + w_plot_dt = QSpinBox() + w_plot_dt.setMinimum(10) + w_plot_dt.setMaximum(200000) + w_plot_dt.setSingleStep(100) + self.w_form.add_form_row("plot_update_interval_ms", "Plot Update Interval (ms)", 200, w_plot_dt, "Number of milliseconds to wait before updating the live plot", group="interface") + self.w_form.add_form_row("error_show_dialog", "Show Dialog on Error", True, QCheckBox(), "Whether to show a dialog window when uncaught errors occur\nLeave this on.", group="interface") + self.w_form.add_form_row("error_stack_trace", "Stacktrace in errors", False, QCheckBox(), "Whether to show the call stack in unexpected error messages", group="interface") + + # led_script + self.w_form.add_form_row("led_script_watch_file", "Watch Led Script File", False, QCheckBox(), "Watch the LED script file for changes and reload it automatically", group="led_script") + self.w_form.add_form_row("led_script_save_copy", "Copy Led Script to Cache Dir", True, QCheckBox(), "Save the final version of the led script in the cache directory", group="led_script") + + self.w_form.add_form_row("metadata_auto_update", "Auto-Update Metadata", True, QCheckBox(), "Allow metadata updates while the program is running") + self.w_form.add_form_row("metadata_auto_add", "Auto-Add Metadata", True, QCheckBox(), "Automatically add measurement metadata to the data file.\nThis includes: device names, measurement mode, measurement interval, start and stop times, led script") + + # power_switch + w_usb_switch_exe = FileSelection(filemode=QFileDialog.FileMode.ExistingFile) + self.w_form.add_form_row("power_switch_exe", "Power Switch Executable", "", w_usb_switch_exe, "Path to the USBSwitchCmd executable for the Cleware USB switch\nRequires a relaunch to take effect", group="power_switch") + + self.w_form.update_alignment() + diff --git a/cpdctrl_gui/ui/widgets/settings/base.py b/cpdctrl_gui/ui/widgets/settings/base.py index 75ea6c2..7d7ca9b 100644 --- a/cpdctrl_gui/ui/widgets/settings/base.py +++ b/cpdctrl_gui/ui/widgets/settings/base.py @@ -1,6 +1,7 @@ from PyQt6.QtGui import QIcon -from PyQt6.QtWidgets import QWidget, QLabel, QFormLayout, QSpinBox, QDoubleSpinBox, QLineEdit, QHBoxLayout, QPushButton, QFileDialog, QCheckBox -from PyQt6.QtCore import pyqtSignal +from PyQt6.QtWidgets import QWidget, QLabel, QFormLayout, QSpinBox, QDoubleSpinBox, QLineEdit, QHBoxLayout, QPushButton, \ + QFileDialog, QCheckBox, QVBoxLayout, QGroupBox +from PyQt6.QtCore import pyqtSignal, Qt from cpdctrl.utility.config_file import ConfigFile @@ -47,14 +48,35 @@ class SettingsForm(QWidget): """ def __init__(self, config_file: ConfigFile, parent=None): super().__init__(parent) - self.setLayout(QFormLayout()) - self.ws_form = {} + self.setLayout(QVBoxLayout()) + self.ws_form: dict[str, QWidget] = {} + self.ls_form: dict[str, QLabel] = {} + self.ws_groups: dict[str, QFormLayout] = {} self.config_file = config_file + w_default = QWidget() + w_default.setLayout(QFormLayout()) + self.ws_groups["default"] = w_default.layout() + self.layout().addWidget(w_default) def __contains__(self, item): return item in self.ws_form - def add_form_row(self, key: str, label: str, default_value, widget: QWidget, tooltip: str = None): + def add_group(self, key, name): + """ + Add a group box + Parameters + ---------- + key + The key to be used with add_form_row + name + The name to display + """ + w_group = QGroupBox(parent=None, title=name) + w_group.setLayout(QFormLayout()) + self.layout().addWidget(w_group) + self.ws_groups[key] = w_group.layout() + + def add_form_row(self, key: str, label: str, default_value, widget: QWidget, tooltip: str = None, group:str="default"): """ Add a row to the form. Uses the value from the config file corresponding to or the default value. Parameters @@ -62,15 +84,21 @@ class SettingsForm(QWidget): key label: str Label for the form widget - default_value - widget + default_value: + The default value to use for the widget + widget: QWidget Widget to add to the form - tooltip + tooltip: str + Tooltip for the widget + group: str + Group to add the row to. Returns ------- """ + if not group in self.ws_groups: + raise ValueError(f"Can not add form row '{key}': Group '{group}' does not exist.") if tooltip: widget.setToolTip(tooltip) value = self.config_file.get_or(key, default_value) # set the value depending on the type of the widget @@ -88,8 +116,10 @@ class SettingsForm(QWidget): widget.textChanged.connect(lambda v: self.value_updated(key, v)) else: raise ValueError(f"Unknown widget type: {type(widget)}") - self.layout().addRow(QLabel(label), widget) + l_label = QLabel(label) + self.ws_groups[group].addRow(l_label, widget) self.ws_form[key] = widget + self.ls_form[key] = l_label def value_updated(self, key, value): @@ -152,3 +182,18 @@ class SettingsForm(QWidget): raise ValueError(f"Unknown widget type: {type(self.ws_form[key])}") else: raise ValueError(f"Unknown key: {key}") + + def update_alignment(self): + """ + Give all labels the min width of the widest label, + so that all forms are aligned even when in different group boxes + """ + max_width = 0 + for _, l in self.ls_form.items(): + w = l.minimumSizeHint().width() + if w > max_width: + max_width = w + for _, l in self.ls_form.items(): + l.setMinimumWidth(max_width) + +