feat(graph): add graph tab for whole-run data
Provides a static line chart plotting multi-sensor temperature values over time when a session file is loaded.
This commit is contained in:
@@ -11,7 +11,11 @@ from PySide6.QtCore import Property, QObject, Qt, QTimer, Signal, Slot
|
||||
from pygui.backend.cluster_average import average_clusters, group_sensors_by_radius
|
||||
from pygui.backend.data.csv_recorder import CsvRecorder
|
||||
from pygui.backend.models.frame import Frame
|
||||
from pygui.backend.models.frame_player import FramePlayer, frames_from_wafer_data
|
||||
from pygui.backend.models.frame_player import (
|
||||
FramePlayer,
|
||||
all_sensor_series,
|
||||
frames_from_wafer_data,
|
||||
)
|
||||
from pygui.backend.models.frame_stats import compute_edge_center
|
||||
from pygui.backend.models.sensor_editor import SensorEditor
|
||||
from pygui.backend.models.session_model import SessionModel, SessionUpdate
|
||||
@@ -207,6 +211,18 @@ class SessionController(QObject):
|
||||
@Property(str, notify=loadedFileChanged)
|
||||
def loadedFile(self) -> str: return self._loaded_file
|
||||
|
||||
@Property(str, notify=loadedFileChanged)
|
||||
def graphSensorNames(self) -> str:
|
||||
"""Comma-joined sensor names for the whole-run Graph tab."""
|
||||
names, _ = all_sensor_series(list(self._player.frames))
|
||||
return ",".join(names)
|
||||
|
||||
@Property(str, notify=loadedFileChanged)
|
||||
def graphSeriesJson(self) -> str:
|
||||
"""JSON per-sensor value series (one list per sensor) for the Graph tab."""
|
||||
_, series = all_sensor_series(list(self._player.frames))
|
||||
return json.dumps(series)
|
||||
|
||||
@Property("QVariantList", notify=frameUpdated) # type: ignore[arg-type]
|
||||
def overriddenSensors(self) -> list[int]:
|
||||
"""Indices of sensors that currently have a replacement or offset."""
|
||||
|
||||
@@ -8,6 +8,19 @@ def frames_from_wafer_data(data: ZWaferData | None, records) -> list[Frame]:
|
||||
"""Build Frames from parsed DataRecords (records = list[DataRecord].)"""
|
||||
return [Frame(seq=i, time=r.time, values=list(r.values)) for i, r in enumerate(records)]
|
||||
|
||||
|
||||
def all_sensor_series(frames: list[Frame]) -> tuple[list[str], list[list[float]]]:
|
||||
"""Per-sensor value series across every frame, for the whole-run Graph tab.
|
||||
|
||||
Sensor names are 1-based ("Sensor 1", ...), matching the C# original.
|
||||
"""
|
||||
if not frames:
|
||||
return [], []
|
||||
n_sensors = len(frames[0].values)
|
||||
names = [f"Sensor {i + 1}" for i in range(n_sensors)]
|
||||
series = [[frame.values[i] for frame in frames] for i in range(n_sensors)]
|
||||
return names, series
|
||||
|
||||
class FramePlayer:
|
||||
def __init__(self) -> None:
|
||||
self._frames: list[Frame] =[]
|
||||
@@ -22,6 +35,10 @@ class FramePlayer:
|
||||
def total(self) -> int:
|
||||
return len(self._frames)
|
||||
|
||||
@property
|
||||
def frames(self) -> tuple[Frame, ...]:
|
||||
return tuple(self._frames)
|
||||
|
||||
@property
|
||||
def index(self) -> int:
|
||||
return self._i
|
||||
|
||||
Reference in New Issue
Block a user