""" ui/components/performance_dashboard.py PerformanceDashboard — 顯示即時 FPS 與延遲數值的 QWidget。 使用 pyqtgraph 繪製折線圖(如可用),否則降級為純 QLabel 顯示數值, 避免 import error 導致應用崩潰。 """ from typing import Any, Dict, Optional from PyQt5.QtCore import pyqtSignal from PyQt5.QtWidgets import QHBoxLayout, QLabel, QVBoxLayout, QWidget try: import pyqtgraph as pg # type: ignore _PYQTGRAPH_AVAILABLE = True except ImportError: _PYQTGRAPH_AVAILABLE = False # TODO: Phase 2 - 當 pyqtgraph 可用時,改用折線圖顯示歷史 FPS/Latency class PerformanceDashboard(QWidget): """即時效能儀錶板元件。 顯示當前 FPS、平均延遲與 p95 延遲。 接受 update_stats(stats) 推送的數據並更新 QLabel 顯示值。 """ update_requested = pyqtSignal(dict) def __init__(self, parent: Optional[QWidget] = None) -> None: super().__init__(parent) # 內部狀態 self.current_fps: float = 0.0 self.current_avg_latency_ms: float = 0.0 self.current_p95_latency_ms: float = 0.0 self.display_window_seconds: int = 60 # UI 元件(動態值 label,前綴由靜態 label 負責) self.fps_label = QLabel("0.0") self.avg_latency_label = QLabel("0.0") self.p95_latency_label = QLabel("0.0") self._setup_ui() def _setup_ui(self) -> None: layout = QVBoxLayout() fps_row = QHBoxLayout() fps_row.addWidget(QLabel("FPS:")) fps_row.addWidget(self.fps_label) avg_row = QHBoxLayout() avg_row.addWidget(QLabel("Avg Latency:")) avg_row.addWidget(self.avg_latency_label) p95_row = QHBoxLayout() p95_row.addWidget(QLabel("P95 Latency:")) p95_row.addWidget(self.p95_latency_label) layout.addLayout(fps_row) layout.addLayout(avg_row) layout.addLayout(p95_row) self.setLayout(layout) def update_stats(self, stats: Dict[str, Any]) -> None: """接收效能數據並更新顯示。 Args: stats: 包含 "fps"、"avg_latency_ms"、"p95_latency_ms" 的字典。 """ self.current_fps = float(stats.get("fps", 0.0)) self.current_avg_latency_ms = float(stats.get("avg_latency_ms", 0.0)) self.current_p95_latency_ms = float(stats.get("p95_latency_ms", 0.0)) self.fps_label.setText(f"{self.current_fps:.1f} FPS") self.avg_latency_label.setText(f"{self.current_avg_latency_ms:.1f} ms") self.p95_latency_label.setText(f"{self.current_p95_latency_ms:.1f} ms") def reset(self) -> None: """清空所有顯示值,回到初始狀態(0)。""" self.current_fps = 0.0 self.current_avg_latency_ms = 0.0 self.current_p95_latency_ms = 0.0 self.fps_label.setText("0.0 FPS") self.avg_latency_label.setText("0.0 ms") self.p95_latency_label.setText("0.0 ms") def set_display_window(self, seconds: int = 60) -> None: """設定圖表顯示的時間視窗(秒)。 Args: seconds: 要顯示的歷史時間範圍,預設 60 秒。 """ self.display_window_seconds = seconds