forked from chiyu1468/AirTrapMine
feat(GUI): 新增模擬驗證滑動開關(ToggleSwitch)
在清除軌跡按鈕左邊加入滑動開關,可控制執行任務時是否開啟 matplotlib 驗證視窗。 預設開啟,OFF 時跳過驗證。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+81
-2
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user