Fix and refactor led script updates. They now work from the file and the LedScriptViewer

This commit is contained in:
CPD 2025-03-17 13:24:53 +01:00
parent 605310dbe5
commit bfaae16ac4
3 changed files with 60 additions and 34 deletions

View File

@ -112,10 +112,10 @@ class MainWindow(QMainWindow):
self.w_right_tab.addTab(self.w_plot, "Plot")
# LED SCRIPT
self.w_led_script = LedScriptViewer(LedScript(0))
self.w_led_script.dataChanged.connect(self._led_script_updated)
self.w_lefttab.addTab(self.w_led_script, "LED Script")
self.w_measurement_settings.w_led_script.script_changed.connect(self.led_script_load)
self.w_led_script_viewer = LedScriptViewer(LedScript(0))
self.w_led_script_viewer.scriptUpdated.connect(self._led_script_updated)
self.w_lefttab.addTab(self.w_led_script_viewer, "LED Script")
self.w_measurement_settings.w_led_script_load.loadScript.connect(self.led_script_load)
self.verbose = True
@ -256,16 +256,14 @@ class MainWindow(QMainWindow):
if self.leddev is None:
raise ValueError("No led control device selected")
name = self.w_measurement_settings.get_value("name")
script = self.w_measurement_settings.get_value("led_script")
led_script = LedScript(script=script)
flush_after = self.w_measurement_settings.get_value("flush_after")
use_buffer = self.w_measurement_settings.get_value("use_buffer")
# check if device supports buffer mode
if use_buffer and not callable(getattr(self.vmdev, 'buffer_measure', None)):
# show warning dialog
ret = QMessageBox.warning(self, "Buffer mode not supported", "The selected voltage measurement device does not support the buffer measurement mode.\nClick Ok to continue without buffer measurement mode.", buttons=QMessageBox.StandardButton.Ok|QMessageBox.StandardButton.Cancel, defaultButton=QMessageBox.StandardButton.Ok)
ret = QMessageBox.warning(self, "Buffer mode not supported", "The selected voltage measurement device does not support the buffer measurement mode.\nClick OK to continue without buffer measurement mode.", buttons=QMessageBox.StandardButton.Ok|QMessageBox.StandardButton.Cancel, defaultButton=QMessageBox.StandardButton.Ok)
if ret == QMessageBox.StandardButton.Ok:
log.warning("Device does not support buffer mode, disabling")
use_buffer = False
@ -290,6 +288,8 @@ class MainWindow(QMainWindow):
self.topbar.disable_button("meas_save")
self.topbar.disable_button("meas_load")
self.topbar.enable_button("meas_stop")
self.w_measurement_settings.setEnabled(False)
self.w_metadata.setEnabled(False)
self.w_plot.clear_data()
# have the led script member be the only auto-updating script,
@ -326,7 +326,7 @@ class MainWindow(QMainWindow):
# the time left estimation might be a little to short, since the actual measurement is started a few lines of
# code later
self.led_script.start()
self.w_led_script.update_time_predictions()
self.w_led_script_viewer.update_time_predictions()
def measure_stop(self):
log.info("Stopping measurement")
@ -339,7 +339,7 @@ class MainWindow(QMainWindow):
self.set_status("Saving data...")
self.data_collector.save_csv_in_dir()
self.proc_measure = None
# dont update w_led_script, keep displaying the last values
# don't update w_led_script, keep displaying the last values
self.led_script.reset()
self.topbar.enable_button("meas_start")
self.topbar.enable_button("connect_vmdev")
@ -347,10 +347,12 @@ class MainWindow(QMainWindow):
self.topbar.enable_button("meas_save")
self.topbar.enable_button("meas_load")
self.topbar.disable_button("meas_stop")
self.w_measurement_settings.setEnabled(True)
self.w_metadata.setEnabled(True)
self.set_status("Ready")
def measure_update(self):
self.w_led_script.update_time(time.time())
self.w_led_script_viewer.update_time(time.time())
if self.proc_measure.is_alive():
while not self.data_queue.empty():
# print(data_queue.qsize(), "\n\n")
@ -402,6 +404,14 @@ class MainWindow(QMainWindow):
self.set_status(f"Aborted saving data, no file selected")
def measurement_load(self, filepath):
"""
Load measurement data from the filepath. Only updates the plot.
Parameters
----------
filepath
The path to the file or directory to load the data from.
File may be csv or pickle containing (data, metadata)
"""
log.info(f"Loading measurement data from '{filepath}'")
if self.measurement_is_running():
QMessageBox.critical(self, "Measurement running", "Can not load data while measurement is running")
@ -421,6 +431,9 @@ class MainWindow(QMainWindow):
self.w_plot.set_data(data[:,1], data[:,2], data[:,3])
def measurement_load_dialog(self):
"""
Open a file dialog to load measurement data. This only updates the plot
"""
if self.measurement_is_running():
QMessageBox.critical(self, "Measurement running", "Can not load data while measurement is running")
return
@ -436,6 +449,9 @@ class MainWindow(QMainWindow):
self.measurement_load(file_path)
def led_script_load(self):
"""
Load a new led script from the value in the measurement settings widget
"""
script = self.w_measurement_settings.get_value("led_script")
script_type = self.w_measurement_settings.get_value("led_script_type")
auto_update = AppConfig.MAIN_CFG.get_or("led_script_watch_file", False)
@ -454,7 +470,7 @@ class MainWindow(QMainWindow):
self.led_script_watcher.fileChanged.connect(self._led_script_update_from_file)
else:
self.led_script_watcher = None
self.w_led_script.set_script(self.led_script)
self.w_led_script_viewer.set_script(self.led_script)
self._led_script_updated()
def _led_script_update_from_file(self):
@ -468,9 +484,8 @@ class MainWindow(QMainWindow):
"""
Send the new led script to the measurement thread and update the gui widget.
"""
print("Update script")
# update gui
self.w_led_script.update_time_predictions()
self.w_led_script_viewer.update_time_predictions()
# update the measurement led script
if self.measurement_is_running():
self.command_queue.put(("led_script", self.led_script.copy()))

View File

@ -1,12 +1,14 @@
from PyQt6.QtWidgets import QWidget, QVBoxLayout, QLabel, QScrollArea, QTableView, QFormLayout
from PyQt6.QtCore import QAbstractTableModel, QModelIndex, Qt
from PyQt6.QtCore import QAbstractTableModel, QModelIndex, Qt, pyqtSignal
import numpy as np
from cpdctrl.led_script import LedScript
import time
import datetime
import logging
log = logging.getLogger(__name__)
import numpy as np
from cpdctrl.led_script import LedScript
timedelta = [("d", 24*3600), ("h", 3600), ("m", 60), ("s", 1)]
def duration_to_string(duration: float) -> str:
@ -35,6 +37,12 @@ def duration_to_string(duration: float) -> str:
return s.strip()
class TimeLeft(QWidget):
"""
Widget that shows:
- Time passed
- Time left
- End time and date
"""
def __init__(self, parent=None):
super().__init__(parent)
self.t_end = None
@ -88,6 +96,7 @@ class LedScriptTableModel(QAbstractTableModel):
It only allows editing values that are in the future
"""
scriptUpdated = pyqtSignal()
def __init__(self, led_script: LedScript, parent=None):
super().__init__(parent)
self.led_script: LedScript = led_script
@ -117,8 +126,16 @@ class LedScriptTableModel(QAbstractTableModel):
def setData(self, index: QModelIndex, value, role: int):
if role == Qt.ItemDataRole.EditRole:
self.led_script.script[self.indices[index.column()]][index.row()] = value
self.dataChanged.emit(index, index, role)
newscript = self.led_script.script.copy()
newscript[self.indices[index.column()]][index.row()] = value
try:
log.info(f"Updating script from {self.led_script.script} to {newscript}")
self.led_script.update_from_script(newscript)
except ValueError as e:
raise e
# return False
self.dataChanged.emit(index, index) # this is for updating the view
self.scriptUpdated.emit() # this for notifying the main window
return True
return False
@ -149,6 +166,7 @@ class LedScriptTableModel(QAbstractTableModel):
class LedScriptViewer(QWidget):
scriptUpdated = pyqtSignal()
def __init__(self, led_script: LedScript, parent=None):
super().__init__(parent)
self.led_script = None
@ -173,14 +191,10 @@ class LedScriptViewer(QWidget):
self.w_time_left = TimeLeft(self)
self.l_vbox.addWidget(self.w_time_left)
self.l_vbox.addStretch(1)
# TODO: this should emit when the script is changed
# TODO: apparently not working, also cant use on_update_callback since the file watcher watchdog thing
# TODO: when updating, the update notice comes twice so I suspect the model copies the other one?
# is not in a QThread, thus the callback cant interact with qt objects and signals
self.model.dataChanged.connect(self.update_time_predictions)
def set_script(self, led_script: LedScript):
self.model = LedScriptTableModel(led_script, self)
self.model.scriptUpdated.connect(self.scriptUpdated)
self.w_table.setModel(self.model)
self.w_table.show()
@ -199,8 +213,6 @@ class LedScriptViewer(QWidget):
t_now
Current time since epoch.
"""
if self.model.led_script.has_updated():
self.update_time_predictions()
self.model.update_time(t_now)
self.w_time_left.update_time(t_now)
self.w_table.update()

View File

@ -34,7 +34,7 @@ class DeviceSelection(QGroupBox):
class ScriptSelection(QGroupBox):
script_changed = pyqtSignal()
loadScript = pyqtSignal()
def __init__(self, parent=None):
super().__init__(parent=parent, title="LED Script")
self.layout = QVBoxLayout()
@ -55,7 +55,7 @@ class ScriptSelection(QGroupBox):
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())
self.w_constant_value.valueChanged.connect(lambda: self.loadScript.emit())
# Layouts
l_constant_value = QVBoxLayout()
@ -98,7 +98,7 @@ class ScriptSelection(QGroupBox):
self.file_path = file_path
self.w_script_file.setText(self.file_path)
# signal the change
self.script_changed.emit()
self.loadScript.emit()
def get_script(self):
@ -130,8 +130,8 @@ class MeasurementSettings(QWidget):
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)
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)
@ -155,7 +155,6 @@ class MeasurementSettings(QWidget):
self.l_vbox.addStretch(1)
def set_value(self, key, value):
if key in self.w_form:
self.w_form.set_value(key, value)
@ -168,8 +167,8 @@ class MeasurementSettings(QWidget):
if key in self.w_form:
return self.w_form.get_value(key)
elif key == "led_script":
return self.w_led_script.get_script()
return self.w_led_script_load.get_script()
elif key == "led_script_type":
return self.w_led_script.get_script_type()
return self.w_led_script_load.get_script_type()
else:
raise ValueError(f"Unknown key: {key}")
raise ValueError(f"Unknown key: {key}")