feat(tabs): update all tab views, dialogs, and session controller
This commit is contained in:
@@ -37,10 +37,12 @@ class SessionController(QObject):
|
||||
loadedFileChanged = Signal()
|
||||
clusterAveragingEnabledChanged = Signal()
|
||||
settingsChanged = Signal() # emitted when session state changes that should be persisted
|
||||
liveStatsChanged = Signal() # emitted when receivedCount or errorCount change
|
||||
# trend: per-frame avg for live graph
|
||||
trendData = Signal(str) # JSON array of floats
|
||||
# private: marshal a worker-thread frame onto the main thread
|
||||
_liveFrame = Signal(object) # Frame
|
||||
_liveError = Signal() # worker-thread error ping
|
||||
|
||||
def __init__(self, parent: QObject | None = None,
|
||||
settings: dict | None = None) -> None:
|
||||
@@ -72,6 +74,7 @@ class SessionController(QObject):
|
||||
self._dirty = False
|
||||
|
||||
self._liveFrame.connect(self._on_live_frame, Qt.ConnectionType.QueuedConnection)
|
||||
self._liveError.connect(self._on_live_error, Qt.ConnectionType.QueuedConnection)
|
||||
|
||||
self._sensor_editor = SensorEditor()
|
||||
self._last_raw_frame: Frame | None = None
|
||||
@@ -79,6 +82,8 @@ class SessionController(QObject):
|
||||
self._cluster_averaging_enabled = False
|
||||
self._active_clusters: list[list[int]] = []
|
||||
self._stats_tracker = ReplayStatsTracker()
|
||||
self._received_count: int = 0
|
||||
self._error_count: int = 0
|
||||
|
||||
# Restore persisted state if available
|
||||
self._load_settings(settings or {})
|
||||
@@ -105,6 +110,9 @@ class SessionController(QObject):
|
||||
@Property(int, notify=frameUpdated)
|
||||
def frameTotal(self) -> int: return self._player.total
|
||||
|
||||
@Property(bool, notify=stateChanged)
|
||||
def playing(self) -> bool: return self._play_timer.isActive()
|
||||
|
||||
@Property(str, notify=stateChanged)
|
||||
def state(self) -> str:
|
||||
if self._last:
|
||||
@@ -210,6 +218,14 @@ class SessionController(QObject):
|
||||
self.clusterAveragingEnabledChanged.emit()
|
||||
self._reprocess_current()
|
||||
|
||||
@Property(int, notify=liveStatsChanged)
|
||||
def receivedCount(self) -> int:
|
||||
return self._received_count
|
||||
|
||||
@Property(int, notify=liveStatsChanged)
|
||||
def errorCount(self) -> int:
|
||||
return self._error_count
|
||||
|
||||
# ---- mode + thresholds ----
|
||||
@Slot(str)
|
||||
@slot_error_boundary
|
||||
@@ -292,11 +308,13 @@ class SessionController(QObject):
|
||||
@slot_error_boundary
|
||||
def play(self) -> None:
|
||||
self._play_timer.start(self._next_interval_ms())
|
||||
self.stateChanged.emit()
|
||||
|
||||
@Slot()
|
||||
@slot_error_boundary
|
||||
def pause(self) -> None:
|
||||
self._play_timer.stop()
|
||||
self.stateChanged.emit()
|
||||
|
||||
@Slot()
|
||||
@slot_error_boundary
|
||||
@@ -304,6 +322,7 @@ class SessionController(QObject):
|
||||
self._play_timer.stop()
|
||||
self._player.seek(0)
|
||||
self._emit_current()
|
||||
self.stateChanged.emit()
|
||||
|
||||
@Slot(int)
|
||||
@slot_error_boundary
|
||||
@@ -311,6 +330,7 @@ class SessionController(QObject):
|
||||
self._play_timer.stop()
|
||||
self._player.step(delta)
|
||||
self._emit_current()
|
||||
self.stateChanged.emit()
|
||||
|
||||
@Slot(int)
|
||||
@slot_error_boundary
|
||||
@@ -335,6 +355,7 @@ class SessionController(QObject):
|
||||
def _advance(self) -> None:
|
||||
if self._player.at_end:
|
||||
self._play_timer.stop()
|
||||
self.stateChanged.emit()
|
||||
return
|
||||
self._player.step(1)
|
||||
self._emit_current()
|
||||
@@ -419,6 +440,9 @@ class SessionController(QObject):
|
||||
self._stats_tracker.reset()
|
||||
self._last = None
|
||||
self._last_raw_frame = None
|
||||
self._received_count = 0
|
||||
self._error_count = 0
|
||||
self.liveStatsChanged.emit()
|
||||
|
||||
transport = SerialPort.open_port(port, timeout=1)
|
||||
# Send 'D2' command padded to 512 bytes to start the stream
|
||||
@@ -427,6 +451,7 @@ class SessionController(QObject):
|
||||
|
||||
def on_error(exc: Exception):
|
||||
log.error("Live stream error: %s", exc)
|
||||
self._liveError.emit()
|
||||
|
||||
self._reader = StreamReader(
|
||||
transport, parse_binary_frame,
|
||||
@@ -444,6 +469,9 @@ class SessionController(QObject):
|
||||
def stopStream(self) -> None:
|
||||
self._repaint_timer.stop()
|
||||
self._trend_timer.stop()
|
||||
self._received_count = 0
|
||||
self._error_count = 0
|
||||
self.liveStatsChanged.emit()
|
||||
if self._reader:
|
||||
transport = self._reader._transport
|
||||
self._reader.stop()
|
||||
@@ -465,6 +493,8 @@ class SessionController(QObject):
|
||||
@slot_error_boundary
|
||||
def _on_live_frame(self, frame: Frame) -> None:
|
||||
import json
|
||||
self._received_count += 1
|
||||
self.liveStatsChanged.emit()
|
||||
self._stats_tracker.track(frame.seq, frame.values)
|
||||
# Main thread (queued). Record raw, process edited; mark dirty for repaint.
|
||||
self._last_raw_frame = frame
|
||||
@@ -498,6 +528,11 @@ class SessionController(QObject):
|
||||
self.frameUpdated.emit()
|
||||
self.stateChanged.emit()
|
||||
|
||||
def _on_live_error(self) -> None:
|
||||
"""Main-thread callback for worker-thread stream errors."""
|
||||
self._error_count += 1
|
||||
self.liveStatsChanged.emit()
|
||||
|
||||
# ---- recording ----
|
||||
@Slot(str, str)
|
||||
@slot_error_boundary
|
||||
|
||||
Reference in New Issue
Block a user