From d2fdbf85ee0c6ea877dd50a220fcee472cdc6064 Mon Sep 17 00:00:00 2001 From: abin Date: Mon, 6 Apr 2026 19:32:18 +0800 Subject: [PATCH] feat: integrate Phase 1-4 components into main dashboard - Performance tab: add PerformanceDashboard live stats widget - Performance tab: add Benchmark button (opens BenchmarkDialog) - Performance tab: add Export Report button (opens ExportReportDialog) - Dongles tab: embed DeviceManagementPanel with 3s auto-refresh - Fix pipeline_config type: analyze_pipeline_stages returns List not Dict All integrations are guarded with try/except and AVAILABLE flags to prevent import errors from breaking existing functionality. Co-Authored-By: Claude Sonnet 4.6 --- ui/windows/dashboard.py | 124 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 120 insertions(+), 4 deletions(-) diff --git a/ui/windows/dashboard.py b/ui/windows/dashboard.py index 61808d4..e3c0961 100644 --- a/ui/windows/dashboard.py +++ b/ui/windows/dashboard.py @@ -59,6 +59,25 @@ from core.nodes.exact_nodes import ( ExactPostprocessNode, ExactOutputNode, EXACT_NODE_TYPES ) +try: + from ui.components.performance_dashboard import PerformanceDashboard + PERFORMANCE_DASHBOARD_AVAILABLE = True +except ImportError: + PERFORMANCE_DASHBOARD_AVAILABLE = False + +try: + from ui.components.device_management_panel import DeviceManagementPanel + from core.device.device_manager import DeviceManager + DEVICE_MANAGEMENT_AVAILABLE = True +except ImportError: + DEVICE_MANAGEMENT_AVAILABLE = False + +try: + from ui.dialogs.export_report_dialog import ExportReportDialog + EXPORT_REPORT_AVAILABLE = True +except ImportError: + EXPORT_REPORT_AVAILABLE = False + # Import pipeline analysis functions try: from core.pipeline import get_stage_count, analyze_pipeline_stages, get_pipeline_summary @@ -158,6 +177,8 @@ class IntegratedPipelineDashboard(QMainWindow): self.props_instructions = None self.node_props_container = None self.node_props_layout = None + self.device_manager = None + self.device_management_panel = None self.fps_label = None self.latency_label = None self.memory_label = None @@ -895,7 +916,20 @@ class IntegratedPipelineDashboard(QMainWindow): metrics_layout.addRow("Memory Usage:", self.memory_label) layout.addWidget(metrics_group) - + + # Real-time performance monitor + perf_dashboard_group = QGroupBox("即時效能監控") + perf_dashboard_layout = QVBoxLayout(perf_dashboard_group) + if PERFORMANCE_DASHBOARD_AVAILABLE: + self.performance_dashboard = PerformanceDashboard() + else: + self.performance_dashboard = None + if self.performance_dashboard: + perf_dashboard_layout.addWidget(self.performance_dashboard) + else: + perf_dashboard_layout.addWidget(QLabel("PerformanceDashboard 不可用")) + layout.addWidget(perf_dashboard_group) + # Suggestions suggestions_group = QGroupBox("Optimization Suggestions") suggestions_layout = QVBoxLayout(suggestions_group) @@ -907,6 +941,26 @@ class IntegratedPipelineDashboard(QMainWindow): layout.addWidget(suggestions_group) + # Benchmark section + benchmark_group = QGroupBox("效能 Benchmark") + benchmark_layout = QVBoxLayout(benchmark_group) + + self.benchmark_button = QPushButton("執行 Benchmark") + self.benchmark_button.setToolTip("比較單 Dongle vs 多 Dongle 的效能差異") + self.benchmark_button.clicked.connect(self.open_benchmark_dialog) + benchmark_layout.addWidget(self.benchmark_button) + + layout.addWidget(benchmark_group) + + if EXPORT_REPORT_AVAILABLE: + export_group = QGroupBox("報告匯出") + export_layout = QVBoxLayout(export_group) + self.export_report_button = QPushButton("匯出效能報告(PDF/CSV)") + self.export_report_button.setToolTip("將 Benchmark 結果與歷史記錄匯出為 PDF 或 CSV") + self.export_report_button.clicked.connect(self.open_export_report_dialog) + export_layout.addWidget(self.export_report_button) + layout.addWidget(export_group) + # Deploy section deploy_group = QGroupBox("Pipeline Deployment") deploy_layout = QVBoxLayout(deploy_group) @@ -977,12 +1031,23 @@ class IntegratedPipelineDashboard(QMainWindow): self.dongles_list.addItem("No dongles detected. Click 'Detect Dongles' to scan.") layout.addWidget(self.dongles_list) + if DEVICE_MANAGEMENT_AVAILABLE: + try: + self.device_manager = DeviceManager() + self.device_management_panel = DeviceManagementPanel(self.device_manager) + self.device_management_panel.set_auto_refresh(3000) + layout.addWidget(self.device_management_panel) + except Exception as e: + err_label = QLabel(f"裝置管理面板初始化失敗:{e}") + err_label.setStyleSheet("color: #f38ba8; font-size: 11px;") + layout.addWidget(err_label) + layout.addStretch() widget.setWidget(content) widget.setWidgetResizable(True) - + return widget - + def setup_menu(self): """Setup the menu bar.""" menubar = self.menuBar() @@ -1925,7 +1990,58 @@ class IntegratedPipelineDashboard(QMainWindow): suggestions.append("Pipeline configuration looks good for optimal performance.") self.suggestions_text.setPlainText("\n".join(suggestions)) - + + # Update PerformanceDashboard (if available) + if hasattr(self, 'performance_dashboard') and self.performance_dashboard: + self.performance_dashboard.update_stats({ + "fps": float(estimated_fps), + "avg_latency_ms": float(estimated_latency), + "p95_latency_ms": float(estimated_latency * 1.5) # 估算 p95 + }) + + def open_benchmark_dialog(self): + """開啟 Benchmark 對話框。""" + try: + from ui.dialogs.benchmark_dialog import BenchmarkDialog + from core.pipeline import analyze_pipeline_stages + + if not self.graph: + QMessageBox.warning(self, "無 Pipeline", "請先建立 Pipeline 再執行 Benchmark。") + return + + stages = analyze_pipeline_stages(self.graph) + # analyze_pipeline_stages 回傳 List[PipelineStage] + pipeline_config = stages if stages else [] + + dialog = BenchmarkDialog(self, pipeline_config) + dialog.exec_() + except ImportError as e: + QMessageBox.warning(self, "功能未啟用", f"Benchmark 功能暫不可用:{e}") + + def open_export_report_dialog(self): + """開啟效能報告匯出對話框。""" + try: + from ui.dialogs.export_report_dialog import ExportReportDialog + from core.performance.benchmarker import PerformanceBenchmarker + from core.performance.history import PerformanceHistory + from core.device.device_manager import DeviceManager + + benchmarker = getattr(self, '_benchmarker', None) + history = getattr(self, '_perf_history', None) + device_manager = getattr(self, 'device_manager', None) + dashboard = getattr(self, 'performance_dashboard', None) + + dialog = ExportReportDialog( + parent=self, + benchmarker=benchmarker, + history=history, + device_manager=device_manager, + dashboard=dashboard, + ) + dialog.exec_() + except Exception as e: + QMessageBox.warning(self, "匯出功能", f"無法開啟報告匯出:{e}") + def delete_selected_nodes(self): """Delete selected nodes from the graph.""" if not self.graph: