Update GUI 2.4.1

ken910606 1 month ago
parent d2aef78a4b
commit 003f8988c5

@ -147,7 +147,7 @@ class ToggleSwitch(QWidget):
class ControlStationUI(QMainWindow): class ControlStationUI(QMainWindow):
planning_finished = pyqtSignal(object) planning_finished = pyqtSignal(object)
VERSION = '2.4.0' VERSION = '2.4.1'
FONT_SCALE_MIN = 70 FONT_SCALE_MIN = 70
FONT_SCALE_MAX = 180 FONT_SCALE_MAX = 180
FONT_SCALE_DEFAULT = 100 FONT_SCALE_DEFAULT = 100
@ -195,6 +195,11 @@ class ControlStationUI(QMainWindow):
self.panel_map_timer.timeout.connect(self._update_panel_and_map) self.panel_map_timer.timeout.connect(self._update_panel_and_map)
self.panel_map_timer.start(100) # 10Hz self.panel_map_timer.start(100) # 10Hz
# Attitude 顯示器獨立高頻更新30Hz避免被 panel/table/map 節奏拖住
self.attitude_timer = QTimer()
self.attitude_timer.timeout.connect(self._update_attitude_only)
self.attitude_timer.start(33)
# 消息隊列處理定時器(來自線程的 GUI 更新) # 消息隊列處理定時器(來自線程的 GUI 更新)
self.msg_queue_timer = QTimer() self.msg_queue_timer = QTimer()
self.msg_queue_timer.timeout.connect(self._process_message_queue) self.msg_queue_timer.timeout.connect(self._process_message_queue)
@ -202,7 +207,9 @@ class ControlStationUI(QMainWindow):
# 快取消息數據,以便在沒有新消息時使用上一次的值 # 快取消息數據,以便在沒有新消息時使用上一次的值
self._message_cache = {} self._message_cache = {}
self._attitude_cache = {}
self._overview_cache = {} self._overview_cache = {}
self._map_dirty_drones = set()
self.message_history = [] self.message_history = []
self.max_message_history = 500 self.max_message_history = 500
@ -230,10 +237,10 @@ class ControlStationUI(QMainWindow):
# 初始化地圖 # 初始化地圖
self.drone_map = DroneMap() self.drone_map = DroneMap()
# 地圖更新獨立降頻5Hz避免 WebEngine / JS map 拖慢 panel 更新 # 地圖更新獨立節流10Hz避免 WebEngine / JS map 拖慢 panel 更新
self.map_timer = QTimer() self.map_timer = QTimer()
self.map_timer.timeout.connect(self._update_map_only) self.map_timer.timeout.connect(self._update_map_only)
self.map_timer.start(200) self.map_timer.start(100)
# Overview table 獨立批次更新5Hz避免每筆資料都重繪 Qt table # Overview table 獨立批次更新5Hz避免每筆資料都重繪 Qt table
self.overview_timer = QTimer() self.overview_timer = QTimer()
@ -1603,6 +1610,8 @@ class ControlStationUI(QMainWindow):
self._message_cache[drone_id] = {} self._message_cache[drone_id] = {}
self._message_cache[drone_id][msg_type] = data self._message_cache[drone_id][msg_type] = data
if msg_type == 'attitude':
self._attitude_cache[drone_id] = data
# ================================================================================ # ================================================================================
@ -2242,14 +2251,15 @@ class ControlStationUI(QMainWindow):
try: try:
start_time = time.time() start_time = time.time()
pending_messages = self._message_cache
self._message_cache = {}
# ✅ 步驟 2: 遍歷快取中最新的資料來更新 UI # ✅ 步驟 2: 只處理本批新資料,避免每個 tick 重跑舊資料造成週期性卡頓
for drone_id in list(self._message_cache.keys()): for drone_id, cached_data in pending_messages.items():
if drone_id not in self.drones: if drone_id not in self.drones:
continue continue
panel = self.drones[drone_id] panel = self.drones[drone_id]
cached_data = self._message_cache[drone_id]
# 處理所有快取的消息類型 # 處理所有快取的消息類型
for msg_type, data in cached_data.items(): for msg_type, data in cached_data.items():
@ -2312,16 +2322,12 @@ class ControlStationUI(QMainWindow):
self.queue_overview_update(drone_id, 'roll', f"{roll:.1f}°") self.queue_overview_update(drone_id, 'roll', f"{roll:.1f}°")
self.queue_overview_update(drone_id, 'pitch', f"{pitch:.1f}°") self.queue_overview_update(drone_id, 'pitch', f"{pitch:.1f}°")
self.queue_overview_update(drone_id, 'yaw', f"{yaw:.1f}°") self.queue_overview_update(drone_id, 'yaw', f"{yaw:.1f}°")
panel._last_roll = roll
panel._last_pitch = pitch
if hasattr(panel, 'update_attitude'):
heading = self.drone_headings.get(drone_id, yaw)
panel.update_attitude(heading, roll, pitch)
elif msg_type == 'gps': elif msg_type == 'gps':
gps_data = data gps_data = data
lat, lon = gps_data.get('lat', 0), gps_data.get('lon', 0) lat, lon = gps_data.get('lat', 0), gps_data.get('lon', 0)
self.drone_positions[drone_id] = (lat, lon) self.drone_positions[drone_id] = (lat, lon)
self._map_dirty_drones.add(drone_id)
alt = gps_data.get('alt', 0) alt = gps_data.get('alt', 0)
if not hasattr(self.monitor, 'drone_gps'): if not hasattr(self.monitor, 'drone_gps'):
self.monitor.drone_gps = {} self.monitor.drone_gps = {}
@ -2360,6 +2366,7 @@ class ControlStationUI(QMainWindow):
hud_data = data hud_data = data
heading = hud_data.get('heading', 0) heading = hud_data.get('heading', 0)
self.drone_headings[drone_id] = heading self.drone_headings[drone_id] = heading
self._map_dirty_drones.add(drone_id)
groundspeed = hud_data.get('groundspeed', 0) groundspeed = hud_data.get('groundspeed', 0)
airspeed = hud_data.get('airspeed', 0) airspeed = hud_data.get('airspeed', 0)
throttle = hud_data.get('throttle', 0) throttle = hud_data.get('throttle', 0)
@ -2387,11 +2394,43 @@ class ControlStationUI(QMainWindow):
def _update_map_only(self): def _update_map_only(self):
"""獨立降頻更新地圖,避免地圖 JS 呼叫拖住 panel / table。""" """獨立降頻更新地圖,避免地圖 JS 呼叫拖住 panel / table。"""
for drone_id, pos in list(self.drone_positions.items()): dirty_drones = getattr(self, '_map_dirty_drones', set())
if not dirty_drones:
return
pending_drones = list(dirty_drones)
self._map_dirty_drones = set()
for drone_id in pending_drones:
pos = self.drone_positions.get(drone_id)
if not pos:
continue
heading = self.drone_headings.get(drone_id, 0) heading = self.drone_headings.get(drone_id, 0)
lat, lon = pos lat, lon = pos
self.drone_map.update_drone_position(drone_id, lat, lon, heading) self.drone_map.update_drone_position(drone_id, lat, lon, heading)
def _update_attitude_only(self):
"""高頻更新 drone panel 的 attitude 顯示器。"""
if not getattr(self, '_attitude_cache', None):
return
latest_attitudes = self._attitude_cache
self._attitude_cache = {}
for drone_id, data in latest_attitudes.items():
panel = self.drones.get(drone_id)
if not panel or not hasattr(panel, 'update_attitude'):
continue
roll = data.get('roll', 0)
pitch = data.get('pitch', 0)
yaw = data.get('yaw', 0)
heading = self.drone_headings.get(drone_id, yaw)
panel._last_roll = roll
panel._last_pitch = pitch
panel.update_attitude(heading, roll, pitch)
def _process_message_queue(self): def _process_message_queue(self):

@ -340,6 +340,7 @@ class DroneMap:
var markers = {}; var markers = {};
var idLabels = {}; var idLabels = {};
var trajectories = {}; var trajectories = {};
const maxTrajectoryPoints = 300;
var trajectoryLines = {}; var trajectoryLines = {};
var focusedId = null; var focusedId = null;
var initialized = false; var initialized = false;
@ -702,11 +703,12 @@ class DroneMap:
const point = [lat, lon]; const point = [lat, lon];
trajectories[id].push(point); trajectories[id].push(point);
if (trajectories[id].length > 1000) { if (trajectories[id].length > maxTrajectoryPoints) {
trajectories[id].shift(); trajectories[id].shift();
trajectoryLines[id].setLatLngs(trajectories[id]);
} else {
trajectoryLines[id].addLatLng(point);
} }
trajectoryLines[id].setLatLngs([...trajectories[id]]);
} }
function focusOn(id) { function focusOn(id) {
@ -878,7 +880,7 @@ class DroneMap:
# 設置地圖更新計時器 # 設置地圖更新計時器
self.map_update_timer = QTimer() self.map_update_timer = QTimer()
self.map_update_timer.timeout.connect(self.update_map_positions) self.map_update_timer.timeout.connect(self.update_map_positions)
self.map_update_timer.start(200) # 每 200ms 更新一次 self.map_update_timer.start(100) # 每 100ms 更新一次10Hz
def _on_map_loaded(self, ok: bool): def _on_map_loaded(self, ok: bool):
"""地圖加載完成回調""" """地圖加載完成回調"""

Loading…
Cancel
Save