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 ...utility.config import AppConfig 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: self.devices_widgets[key].setText(value) else: raise KeyError(f"Unknown device '{key}'") class ScriptSelection(QGroupBox): script_changed = 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 value") 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) # 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.script_changed.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) self.file_path = None def on_radio_button_toggled(self): if self.radio_script_file.isChecked(): self.btn_load_script.setEnabled(True) self.w_constant_value.setEnabled(False) 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) self.file_path = file_path self.w_script_file.setText(self.file_path) # signal the change self.script_changed.emit() def get_script(self): if self.radio_script_file.isChecked(): return self.file_path else: return int(self.w_constant_value.value()) 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 = ScriptSelection() self.l_vbox.addWidget(self.w_led_script) # key-value stuff in a form self.l_form = QFormLayout() self.l_vbox.addLayout(self.l_form) self.ws_form = {} w_box_interval = QDoubleSpinBox(self) w_box_interval.setDecimals(2) w_box_interval.setMinimum(0.01) w_box_interval.setSingleStep(0.1) self._add_form_field("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._add_form_field("max_measurements", "Max Measurements", 0, w_box_max_measurements, "Number of measurements to take. Set to 0 for infinite measurements") self._add_form_field("stop_on_script_end", "Stop on Script End", False, QCheckBox(self), "Stop measurement when LED script ends") self._add_form_field("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._add_form_field("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) self._add_form_field("auto_add_metadata", "Auto-add Metadata", True, QCheckBox(self), "Automatically add measurement metadata to the data file.\nThis includes: device names, measurement mode, measurement interval, start and stop times, led script") def _add_form_field(self, key: str, label: str, default_value, widget: QWidget, tooltip: str = None): if tooltip: widget.setToolTip(tooltip) value = AppConfig.MEAS_CFG.get_or(key, default_value) # set the value depending on the type of the widget if isinstance(widget, QSpinBox) or isinstance(widget, QDoubleSpinBox): widget.setValue(value) widget.valueChanged.connect(lambda value: self.value_updated(key, value)) elif isinstance(widget, QCheckBox): widget.setChecked(value) widget.stateChanged.connect(lambda value: self.value_updated(key, value)) else: raise ValueError(f"Unknown widget type: {type(widget)}") self.l_form.addRow(QLabel(label), widget) self.ws_form[key] = widget def value_updated(self, key, value): AppConfig.MEAS_CFG.set(key, value) def set_value(self, key, value): if key in self.ws_form: # set depending on widget type if isinstance(self.ws_form[key], QSpinBox) or isinstance(self.ws_form[key], QDoubleSpinBox): self.ws_form[key].setValue(value) elif isinstance(self.ws_form[key], QCheckBox): self.ws_form[key].setChecked(value) else: raise ValueError(f"Unknown widget type: {type(self.ws_form[key])}") 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.ws_form: # get depending on widget type if isinstance(self.ws_form[key], QSpinBox) or isinstance(self.ws_form[key], QDoubleSpinBox): return self.ws_form[key].value() elif isinstance(self.ws_form[key], QCheckBox): return self.ws_form[key].isChecked() else: raise ValueError(f"Unknown widget type: {type(self.ws_form[key])}") elif key == "led_script": return self.w_led_script.get_script() else: raise ValueError(f"Unknown key: {key}")