#!/usr/bin/env python3 from PyQt6.QtWidgets import QTableWidget, QTableWidgetItem, QHeaderView, QLabel from PyQt6.QtCore import Qt class OverviewTable(QTableWidget): """總覽表格,顯示所有無人機的狀態資訊""" # 默認的資訊類型和映射 # 「任務狀態」放最上面:閉環跑點時一眼看每台的 WP 進度 + 通訊(送出)狀態。 # 此列不來自遙測 QLabel,由 MissionExecutor 訊號經 update_table(field='mission') 餵入。 DEFAULT_INFO_TYPES = ["任務狀態", "模式", "ARM", "電壓", "經度", "緯度", "FixType", "衛星數", "EPH", "EPV", "高度", "XY位置", "XY速度", "地速", "航向", "空速", "油門", "海拔高度", "爬升率", "Roll", "Pitch", "Yaw", "丟包", "延遲"] DEFAULT_INFO_TYPE_MAP = { "mission": 0, "mode": 1, "armed": 2, "battery": 3, "longitude": 4, "latitude": 5, "fix_type": 6, "satellites_visible": 7, "eph": 8, "epv": 9, "altitude": 10, "local": 11, "velocity": 12, "groundspeed": 13, "heading": 14, "airspeed": 15, "throttle": 16, "hud_alt": 17, "climb": 18, "roll": 19, "pitch": 20, "yaw": 21, "loss_rate": 22, "ping": 23 } def __init__(self, info_types=None, info_type_map=None, parent=None): super().__init__(parent) # 使用提供的或默認的資訊類型 self.info_types = info_types if info_types is not None else self.DEFAULT_INFO_TYPES self.info_type_map = info_type_map if info_type_map is not None else self.DEFAULT_INFO_TYPE_MAP self.drones = {} # 存儲無人機面板的引用 # 任務狀態不是遙測,不在 panel QLabel 內;獨立保存,避免 refresh_all 用遙測把它蓋掉 self.mission_status = {} # drone_id -> 任務狀態文字 # 初始化表格 self.setColumnCount(1) self.setRowCount(len(self.info_types)) self.setHorizontalHeaderLabels(["資訊"]) header = self.horizontalHeader() header.setSectionResizeMode(0, QHeaderView.ResizeMode.ResizeToContents) self.verticalHeader().setVisible(False) # 設置第一列的資訊類型 for i, txt in enumerate(self.info_types): item = QTableWidgetItem(txt) item.setFlags(Qt.ItemFlag.ItemIsEnabled) item.setTextAlignment(Qt.AlignmentFlag.AlignCenter) self.setItem(i, 0, item) def set_drones(self, drones): """設置無人機面板字典的引用""" self.drones = drones def update_table(self, drone_id=None, field=None, value=None): """更新總覽表格 Args: drone_id: 無人機 ID field: 欄位名稱 (如 'mode', 'altitude' 等) value: 要更新的值 """ # 更新特定儲存格 if drone_id and field and value: if drone_id not in self.drones: return # 任務狀態另存一份,讓 refresh_all(用遙測重繪)不會把它清掉 if field == 'mission': self.mission_status[drone_id] = value col = 1 + list(self.drones.keys()).index(drone_id) row = self.info_type_map.get(field, -1) if row == -1: return # 無效的欄位 item = self.item(row, col) if not item: item = QTableWidgetItem() self.setItem(row, col, item) item.setText(value) item.setTextAlignment(Qt.AlignmentFlag.AlignCenter) # 如果沒有指定更新,刷新整個表格 if drone_id is None: self.refresh_all() def refresh_all(self): """刷新整個表格""" cols = 1 + len(self.drones) self.setColumnCount(cols) headers = ["資訊"] + list(self.drones.keys()) self.setHorizontalHeaderLabels(headers) for col, did in enumerate(self.drones, start=1): panel = self.drones[did] for field, row in self.info_type_map.items(): if field == 'mission': # 任務狀態非遙測,取自獨立保存,避免被 "--" 蓋掉 val = self.mission_status.get(did, "--") else: lbl = panel.findChild(QLabel, f"{did}_{field}") val = lbl.text() if lbl else "--" val_item = QTableWidgetItem(val) val_item.setTextAlignment(Qt.AlignmentFlag.AlignCenter) self.setItem(row, col, val_item) def add_drone_column(self, drone_id): """當新增無人機時,添加一列""" if drone_id in self.drones: self.refresh_all() def remove_drone_column(self, drone_id): """當移除無人機時,刷新表格""" if drone_id in self.drones: self.refresh_all()