Update GUI 2.4.1

ken910606 1 month ago
parent d2aef78a4b
commit 003f8988c5

@ -147,7 +147,7 @@ class ToggleSwitch(QWidget):
class ControlStationUI(QMainWindow):
planning_finished = pyqtSignal(object)
VERSION = '2.4.0'
VERSION = '2.4.1'
FONT_SCALE_MIN = 70
FONT_SCALE_MAX = 180
FONT_SCALE_DEFAULT = 100
@ -194,6 +194,11 @@ class ControlStationUI(QMainWindow):
self.panel_map_timer = QTimer()
self.panel_map_timer.timeout.connect(self._update_panel_and_map)
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 更新)
self.msg_queue_timer = QTimer()
@ -202,7 +207,9 @@ class ControlStationUI(QMainWindow):
# 快取消息數據,以便在沒有新消息時使用上一次的值
self._message_cache = {}
self._attitude_cache = {}
self._overview_cache = {}
self._map_dirty_drones = set()
self.message_history = []
self.max_message_history = 500
@ -230,10 +237,10 @@ class ControlStationUI(QMainWindow):
# 初始化地圖
self.drone_map = DroneMap()
# 地圖更新獨立降頻5Hz避免 WebEngine / JS map 拖慢 panel 更新
# 地圖更新獨立節流10Hz避免 WebEngine / JS map 拖慢 panel 更新
self.map_timer = QTimer()
self.map_timer.timeout.connect(self._update_map_only)
self.map_timer.start(200)
self.map_timer.start(100)
# Overview table 獨立批次更新5Hz避免每筆資料都重繪 Qt table
self.overview_timer = QTimer()
@ -1603,6 +1610,8 @@ class ControlStationUI(QMainWindow):
self._message_cache[drone_id] = {}
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:
start_time = time.time()
pending_messages = self._message_cache
self._message_cache = {}
# ✅ 步驟 2: 遍歷快取中最新的資料來更新 UI
for drone_id in list(self._message_cache.keys()):
# ✅ 步驟 2: 只處理本批新資料,避免每個 tick 重跑舊資料造成週期性卡頓
for drone_id, cached_data in pending_messages.items():
if drone_id not in self.drones:
continue
panel = self.drones[drone_id]
cached_data = self._message_cache[drone_id]
# 處理所有快取的消息類型
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, 'pitch', f"{pitch:.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':
gps_data = data
lat, lon = gps_data.get('lat', 0), gps_data.get('lon', 0)
self.drone_positions[drone_id] = (lat, lon)
self._map_dirty_drones.add(drone_id)
alt = gps_data.get('alt', 0)
if not hasattr(self.monitor, 'drone_gps'):
self.monitor.drone_gps = {}
@ -2360,6 +2366,7 @@ class ControlStationUI(QMainWindow):
hud_data = data
heading = hud_data.get('heading', 0)
self.drone_headings[drone_id] = heading
self._map_dirty_drones.add(drone_id)
groundspeed = hud_data.get('groundspeed', 0)
airspeed = hud_data.get('airspeed', 0)
throttle = hud_data.get('throttle', 0)
@ -2387,11 +2394,43 @@ class ControlStationUI(QMainWindow):
def _update_map_only(self):
"""獨立降頻更新地圖,避免地圖 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)
lat, lon = pos
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):

@ -340,6 +340,7 @@ class DroneMap:
var markers = {};
var idLabels = {};
var trajectories = {};
const maxTrajectoryPoints = 300;
var trajectoryLines = {};
var focusedId = null;
var initialized = false;
@ -702,11 +703,12 @@ class DroneMap:
const point = [lat, lon];
trajectories[id].push(point);
if (trajectories[id].length > 1000) {
if (trajectories[id].length > maxTrajectoryPoints) {
trajectories[id].shift();
trajectoryLines[id].setLatLngs(trajectories[id]);
} else {
trajectoryLines[id].addLatLng(point);
}
trajectoryLines[id].setLatLngs([...trajectories[id]]);
}
function focusOn(id) {
@ -878,7 +880,7 @@ class DroneMap:
# 設置地圖更新計時器
self.map_update_timer = QTimer()
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):
"""地圖加載完成回調"""

Loading…
Cancel
Save