cpdctrl-gui/cpdctrl_gui/ui/widgets/settings/measurement_settings.py
2025-04-03 19:03:08 +02:00

213 lines
8.8 KiB
Python

import os.path
from PyQt6.QtCore import pyqtSignal
from PyQt6.QtWidgets import QWidget, QRadioButton, QVBoxLayout, QHBoxLayout, QPushButton, QSpinBox, QFileDialog, QLabel
from PyQt6.QtWidgets import QFormLayout, QDoubleSpinBox, QCheckBox, QLineEdit, QGroupBox
from os import path
from cpdctrl.led_script import LedScript, InvalidScript
from cpdctrl_gui.utility.config import AppConfig
from .base import SettingsForm
class DeviceSelection(QGroupBox):
def __init__(self, parent=None):
super().__init__(parent=parent, title="Devices")
self.layout = QFormLayout()
self.setLayout(self.layout)
self.devices_widgets = {}
self._add_device("voltage_measurement", "Voltage Measurement", "N.C.")
self._add_device("led_controller", "Led Controller", "N.C.")
self._add_device("led", "Led Lamp", "Unknown")
# self.layout.addStretch(1)
def _add_device(self, key, label, name):
self.devices_widgets[key] = QLabel(name)
self.layout.addRow(QLabel(label), self.devices_widgets[key])
def set_value(self, key, value):
key = key.replace("device_", "")
if key in self.devices_widgets:
if value is None:
text = "N.C."
elif type(value) != str:
text = str(value)
else:
text = value
self.devices_widgets[key].setText(text)
else:
raise KeyError(f"Unknown device '{key}'")
class ScriptSelection(QGroupBox):
loadScript = pyqtSignal()
def __init__(self, parent=None):
super().__init__(parent=parent, title="LED Script")
self.layout = QVBoxLayout()
# Radio buttons
self.radio_script_file = QRadioButton("Script file")
self.radio_constant_value = QRadioButton("Constant brightness (%)")
self.radio_script_file.toggled.connect(self.on_radio_button_toggled)
self.radio_constant_value.toggled.connect(self.on_radio_button_toggled)
# Load from file button
self.btn_load_script = QPushButton("Load from file")
self.btn_load_script.clicked.connect(self.load_file)
self.w_script_file = QLineEdit()
self.w_script_file.setEnabled(False)
last_file = AppConfig.MAIN_CFG.get_or("tmp_last_script_file", "")
# only use last file if exists and is valid
if os.path.isfile(last_file):
try:
LedScript.parse_script(last_file)
except InvalidScript:
last_file = ""
self.w_script_file.setText(last_file)
self.file_path = last_file
# QSpinBox for constant value
self.w_constant_value = QSpinBox()
self.w_constant_value.setRange(0, 100)
# signal when changed
self.w_constant_value.valueChanged.connect(lambda: self.loadScript.emit())
# Layouts
l_constant_value = QVBoxLayout()
l_constant_value.addWidget(self.radio_constant_value)
l_constant_value.addWidget(self.w_constant_value)
l_file = QVBoxLayout()
l_file.addWidget(self.radio_script_file)
l_file.addWidget(self.btn_load_script)
l_file.addWidget(self.w_script_file)
# Add layouts to main layout
self.layout.addLayout(l_constant_value)
self.layout.addLayout(l_file)
self.setLayout(self.layout)
# Initial state
self.radio_constant_value.setChecked(True)
self.on_radio_button_toggled()
self.layout.addStretch(1)
def on_radio_button_toggled(self):
if self.radio_script_file.isChecked():
self.btn_load_script.setEnabled(True)
self.w_constant_value.setEnabled(False)
# if a file is set, load it when the button is toggled
if self.file_path:
self.loadScript.emit()
else:
self.btn_load_script.setEnabled(False)
self.w_constant_value.setEnabled(True)
def load_file(self):
# options = QFileDialog.Options()
last_dir = AppConfig.MAIN_CFG.get_or("tmp_last_script_dir", "")
file_path, _ = QFileDialog.getOpenFileName(self, "Open Script File", last_dir, "All Files (*);;Text files (*.led)")
if file_path:
dir_name = path.dirname(file_path)
AppConfig.MAIN_CFG.set("tmp_last_script_dir", dir_name)
AppConfig.MAIN_CFG.set("tmp_last_script_file", file_path)
self.file_path = file_path
self.w_script_file.setText(self.file_path)
# signal the change
self.loadScript.emit()
def get_script(self):
if self.radio_script_file.isChecked():
return self.file_path
else:
return int(self.w_constant_value.value())
def get_script_type(self):
if self.radio_script_file.isChecked():
return "file"
else:
return "constant"
class MeasurementSettings(QWidget):
"""
Widget allowing the user to set the measurement settings.
It loads the values from AppConfig.MEAS_CFG and automatically
updates the values in AppConfig.MEAS_CFG when the user changes them.
"""
def __init__(self, parent=None):
super().__init__(parent)
self.l_vbox = QVBoxLayout()
# self.label = QLabel("Measurement Settings")
# self.layout.addWidget(self.label)
self.setLayout(self.l_vbox)
# devices
self.w_device_selection = DeviceSelection()
self.l_vbox.addWidget(self.w_device_selection)
# - script
self.w_led_script_load = ScriptSelection()
self.l_vbox.addWidget(self.w_led_script_load)
# key-value stuff in a form
self.w_form = SettingsForm(AppConfig.MEAS_CFG)
self.l_vbox.addWidget(self.w_form)
self.w_form.add_form_row("name", "Name", "", QLineEdit(self), "Name of the measurement")
w_box_interval = QDoubleSpinBox(self)
w_box_interval.setDecimals(2)
w_box_interval.setMinimum(0.01)
w_box_interval.setSingleStep(0.1)
self.w_form.add_form_row("interval", "Interval [s]", 1.0, w_box_interval, "Amount of seconds to wait between voltage measurements and LED device updates")
w_box_max_measurements = QSpinBox(self)
w_box_max_measurements.setMaximum(2147483647) # max int32
w_box_max_measurements.setMinimum(0) # 0 for infinite measurements
self.w_form.add_form_row("max_measurements", "Max Measurement Points", 0, w_box_max_measurements, "Number of measurements to take. Set to 0 for infinite measurements")
w_stop_script_end = QCheckBox(self)
self.w_form.add_form_row("stop_on_script_end", "Stop on Script End", False, w_stop_script_end, "Stop measurement when LED script ends")
self.w_poweroff_script_end = QCheckBox(self)
# do after adding to form, since the actual value will have been loaded
w_stop_script_end.stateChanged.connect(self._update_poweroff_checkbox)
w_box_max_measurements.valueChanged.connect(self._update_poweroff_checkbox)
self._update_poweroff_checkbox()
self.w_form.add_form_row("power_off_script_end", "Power Off on Script End", False, self.w_poweroff_script_end, "Turn off the USB power switch when the measurement ends")
self.w_form.add_form_row("use_buffer", "Use Buffer", False, QCheckBox(self), "If available, use the voltage device buffer for more accurate measurement timings.\nLeads to a lower accuracy of LED update timings, up to 1*interval")
w_box_flush_after = QSpinBox(self)
w_box_flush_after.setMaximum(2147483647) # max int32
w_box_flush_after.setSingleStep(500)
self.w_form.add_form_row("flush_after", "Flush-Data Interval", 0, w_box_flush_after, "Number of measurements to take before writing the data to an intermediate file")
self.l_vbox.addStretch(1)
def _update_poweroff_checkbox(self):
"""
Enable the power-off-on-end checkbox only if the measurement
will stopped automatically
"""
max_meas = self.w_form.get_value("max_measurements")
stop_script_end = self.w_form.get_value("stop_on_script_end")
if max_meas > 0 or stop_script_end == True:
self.w_poweroff_script_end.setEnabled(True)
else:
self.w_poweroff_script_end.setEnabled(False)
def set_value(self, key, value):
if key in self.w_form:
self.w_form.set_value(key, value)
elif key.startswith("device_"):
self.w_device_selection.set_value(key, value)
else:
raise ValueError(f"Unknown key: {key}")
def get_value(self, key):
if key in self.w_form:
return self.w_form.get_value(key)
elif key == "led_script":
return self.w_led_script_load.get_script()
elif key == "led_script_type":
return self.w_led_script_load.get_script_type()
else:
raise ValueError(f"Unknown key: {key}")