feat(GUI): 新增模擬驗證滑動開關(ToggleSwitch)

在清除軌跡按鈕左邊加入滑動開關,可控制執行任務時是否開啟 matplotlib 驗證視窗。
預設開啟,OFF 時跳過驗證。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
wenchun 1 week ago
parent 1426a618f4
commit 730c3e2420

@ -5,8 +5,8 @@ from PyQt6.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayout
QSizePolicy, QTabWidget, QTableWidget, QTableWidgetItem,
QHeaderView, QPushButton, QCheckBox, QLineEdit,
QComboBox, QDialog, QPlainTextEdit, QSlider)
from PyQt6.QtCore import Qt, QTimer, QObject, pyqtSignal, QEvent
from PyQt6.QtGui import QColor, QFont
from PyQt6.QtCore import Qt, QTimer, QObject, pyqtSignal, QEvent, QPropertyAnimation, pyqtProperty, QEasingCurve
from PyQt6.QtGui import QColor, QFont, QPainter, QPen
import sys
import asyncio
import json
@ -72,6 +72,76 @@ class StreamRedirector(QObject):
self.text_written.emit(line)
self._buffer = ""
class ToggleSwitch(QWidget):
"""自訂滑動開關 — ON 綠色 / OFF 紅色"""
toggled = pyqtSignal(bool)
def __init__(self, checked=True, parent=None):
super().__init__(parent)
self._checked = checked
self._knob_x = 1.0 if checked else 0.0
self.setFixedSize(44, 24)
self.setCursor(Qt.CursorShape.PointingHandCursor)
self._animation = QPropertyAnimation(self, b"knob_position", self)
self._animation.setDuration(150)
self._animation.setEasingCurve(QEasingCurve.Type.InOutCubic)
def _get_knob_position(self):
return self._knob_x
def _set_knob_position(self, val):
self._knob_x = val
self.update()
knob_position = pyqtProperty(float, _get_knob_position, _set_knob_position)
def isChecked(self):
return self._checked
def setChecked(self, checked):
if self._checked != checked:
self._checked = checked
self._animate(checked)
self.toggled.emit(checked)
def mousePressEvent(self, event):
self.setChecked(not self._checked)
def _animate(self, checked):
self._animation.stop()
self._animation.setStartValue(self._knob_x)
self._animation.setEndValue(1.0 if checked else 0.0)
self._animation.start()
def paintEvent(self, event):
p = QPainter(self)
p.setRenderHint(QPainter.RenderHint.Antialiasing)
w, h = self.width(), self.height()
r = h / 2
# 背景軌道:依 knob_x 插值 灰→綠
red = QColor(0x88, 0x88, 0x88)
green = QColor(0x4C, 0xAF, 0x50)
t = self._knob_x
bg = QColor(
int(red.red() + (green.red() - red.red()) * t),
int(red.green() + (green.green() - red.green()) * t),
int(red.blue() + (green.blue() - red.blue()) * t),
)
p.setPen(Qt.PenStyle.NoPen)
p.setBrush(bg)
p.drawRoundedRect(0, 0, w, h, r, r)
# 滑塊圓形
knob_d = h - 4
knob_range = w - knob_d - 4
knob_cx = 2 + self._knob_x * knob_range + knob_d / 2
p.setBrush(QColor(255, 255, 255))
p.drawEllipse(int(knob_cx - knob_d / 2), 2, int(knob_d), int(knob_d))
p.end()
class ControlStationUI(QMainWindow):
VERSION = '2.2.0'
FONT_SCALE_MIN = 70
@ -276,6 +346,12 @@ class ControlStationUI(QMainWindow):
group_header.addLayout(title_layout)
group_header.addStretch()
verify_label = QLabel("模擬驗證")
verify_label.setStyleSheet("color: #CCC; font-size: 12px;")
self.verify_toggle = ToggleSwitch(checked=True)
group_header.addWidget(verify_label)
group_header.addWidget(self.verify_toggle)
clear_traj_btn = QPushButton("清除軌跡")
clear_traj_btn.setStyleSheet("""
QPushButton { background-color: #EF5350; color: white; border: none;
@ -1935,6 +2011,9 @@ class ControlStationUI(QMainWindow):
selected_drones, waypoints_per_drone, origin,
target_gps=None, rect_corners=None, route_waypoints=None):
"""存 JSON + 啟動 matplotlib 視覺化驗證 (獨立 process)"""
if not self.verify_toggle.isChecked():
_log("INFO", "模擬驗證已關閉,跳過驗證視窗")
return
import os
data = {
'mission_type': mission_type,

Loading…
Cancel
Save