diff --git a/src/unitdev01/unitdev01/gui.py b/src/unitdev01/unitdev01/gui.py index 623a0d2..d9e7a0f 100644 --- a/src/unitdev01/unitdev01/gui.py +++ b/src/unitdev01/unitdev01/gui.py @@ -3,11 +3,14 @@ import rclpy from rclpy.node import Node from PyQt6.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayout, QWidget, QLabel, QSplitter, QScrollArea, - QSizePolicy) -from PyQt6.QtCore import Qt, QTimer, QObject, pyqtSignal + QSizePolicy, QApplication) +from PyQt6.QtCore import Qt, QTimer, QObject, pyqtSignal, QUrl from PyQt6.QtGui import QColor +from PyQt6.QtWebEngineWidgets import QWebEngineView import math import re +import os +import sys from threading import Lock from geometry_msgs.msg import Point, Vector3 from sensor_msgs.msg import BatteryState, NavSatFix, Imu @@ -138,8 +141,8 @@ class DroneMonitor(Node): def gps_callback(self, drone_id, msg): self.signals.update_signal.emit('gps', drone_id, { 'lat': msg.latitude, - 'lon': msg.longitude, - 'alt': msg.altitude + 'lon': msg.longitude + ,'alt': msg.altitude }) def local_vel_callback(self, drone_id, msg): @@ -150,7 +153,9 @@ class DroneMonitor(Node): }) def altitude_callback(self, drone_id, msg): - self.signals.update_signal.emit('altitude', drone_id, msg.data) + self.signals.update_signal.emit('altitude', drone_id, { + 'altitude': msg.data + }) def local_pose_callback(self, drone_id, msg): self.signals.update_signal.emit('local_pose', drone_id, { @@ -212,26 +217,43 @@ class ControlStationUI(QMainWindow): left_layout.addWidget(scroll_area) # 右侧地图区域 - self.map_widget = QLabel() - self.map_widget.setAlignment(Qt.AlignmentFlag.AlignCenter) - self.map_widget.setStyleSheet(""" - QLabel { - background-color: #1A1A1A; - color: #AAAAAA; - font-size: 18px; - border: 2px solid #333; - border-radius: 10px; - padding: 20px; + self.map_view = QWebEngineView() + + inline_html = ''' + +
+ + + + + + + + + ''' + self.map_view.setHtml(inline_html) + self.map_view.loadFinished.connect(self.on_map_loaded) + main_splitter.addWidget(left_panel) - main_splitter.addWidget(self.map_widget) + main_splitter.addWidget(self.map_view) main_splitter.setSizes([400, 1000]) self.setCentralWidget(main_splitter) + def on_map_loaded(self, ok: bool): + if ok: + self.map_loaded = True + else: + print("⚠️ 地图页加载失败") + def create_drone_panel(self, drone_id): panel = QWidget() panel.setObjectName(f"panel_{drone_id}") @@ -280,11 +302,11 @@ class ControlStationUI(QMainWindow): # 数据字段(带标签) self.create_data_row(layout, "模式:", f"{drone_id}_mode", "--") - self.create_data_row(layout, "電壓:", f"{drone_id}_battery", "-- V") - self.create_data_row(layout, "GPS:", f"{drone_id}_gps", "等待定位...") - self.create_data_row(layout, "Local:", f"{drone_id}_local", "X: -- Y: -- Z: --") - self.create_data_row(layout, "速度:", f"{drone_id}_velocity", "VX: -- VY: -- VZ: --") - self.create_data_row(layout, "姿態:", f"{drone_id}_attitude", "Roll: -- Pitch: -- Yaw: --") + self.create_data_row(layout, "電壓:", f"{drone_id}_battery", "--") + self.create_data_row(layout, "GPS:", f"{drone_id}_gps", "--") + self.create_data_row(layout, "高度:", f"{drone_id}_altitude", "--") + self.create_data_row(layout, "Local:", f"{drone_id}_local", "--") + self.create_data_row(layout, "HUD:", f"{drone_id}_hud", "--") return panel @@ -317,7 +339,7 @@ class ControlStationUI(QMainWindow): return if msg_type == 'mode': - self.update_field(panel, drone_id, 'mode', f"模式: {data}", + self.update_field(panel, drone_id, 'mode', f"{data}", '#FF5555' if '返航' in data else '#55FF55') elif msg_type == 'battery': @@ -327,18 +349,26 @@ class ControlStationUI(QMainWindow): '#FF6464' if voltage < 12 else '#FFFFFF') elif msg_type == 'gps': - text = (f"緯度: {data['lat']:.6f}\n" - f"經度: {data['lon']:.6f}\n" - f"高度: {data['alt']:.1f} m") + text = (f"緯度: {data['lat']:.6f}°\n" + f"經度: {data['lon']:.6f}°") self.update_field(panel, drone_id, 'gps', text) - self.update_map_display(f"Drone {drone_id}\n{text}") + if self.on_map_loaded: + js = f"updateDrone({data['lat']}, {data['lon']:.6f}, {drone_id})" + self.map_view.page().runJavaScript(js) + + elif msg_type == 'altitude': + text = (f"{data['altitude']:.1f} m") + self.update_field(panel, drone_id, 'altitude', text) elif msg_type == 'local_pose': - text = (f"X: {data['x']:.1f} m\n" - f"Y: {data['y']:.1f} m\n" - f"Z: {data['z']:.1f} m") + text = (f"({data['x']:.1f}, {data['y']:.1f}, {data['z']:.1f})") self.update_field(panel, drone_id, 'local', text) + elif msg_type == 'hud': + text = (f"地速: {data['groundspeed']:.1f} m/s\n" + f"航向: {data['heading']:.1f}°") + self.update_field(panel, drone_id, 'hud', text) + ''' elif msg_type == 'velocity': text = (f"VX: {data['vx']:.1f} m/s\n" f"VY: {data['vy']:.1f} m/s\n" @@ -350,13 +380,7 @@ class ControlStationUI(QMainWindow): f"Pitch: {data['pitch']:.1f}°\n" f"Yaw: {data['yaw']:.1f}°") self.update_field(panel, drone_id, 'attitude', text) - - elif msg_type == 'hud': - text = (f"空速: {data['airspeed']:.1f} m/s\n" - f"地速: {data['groundspeed']:.1f} m/s\n" - f"航向: {data['heading']:.1f}°") - self.update_field(panel, drone_id, 'hud', text) - + ''' def update_field(self, panel, drone_id, field, text, color=None): if label := panel.findChild(QLabel, f"{drone_id}_{field}"): label.setText(text) @@ -369,10 +393,6 @@ class ControlStationUI(QMainWindow): self.info_layout.addWidget(panel) self.drones[drone_id] = panel - def update_map_display(self, text): - self.map_widget.setText(text) - self.map_widget.repaint() - def spin_ros(self): try: self.executor.spin_once(timeout_sec=0) @@ -386,7 +406,7 @@ class ControlStationUI(QMainWindow): event.accept() if __name__ == '__main__': - app = QApplication([]) + app = QApplication(sys.argv) station = ControlStationUI() station.show() - app.exec() \ No newline at end of file + app.exec(app.exec()) \ No newline at end of file