503 lines
18 KiB
Python
503 lines
18 KiB
Python
|
|
import time
|
|||
|
|
import threading
|
|||
|
|
from typing import Optional, List
|
|||
|
|
import numpy as np
|
|||
|
|
|
|||
|
|
import grpc
|
|||
|
|
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
|
|||
|
|
QHBoxLayout, QLabel, QPushButton, QComboBox,
|
|||
|
|
QSpinBox, QGroupBox, QGridLayout, QLineEdit, QMessageBox)
|
|||
|
|
from PyQt5.QtCore import QTimer, Qt
|
|||
|
|
from PyQt5.QtGui import QFont
|
|||
|
|
import pyqtgraph as pg
|
|||
|
|
|
|||
|
|
from clients._path_setup import ensure_paths
|
|||
|
|
|
|||
|
|
ensure_paths()
|
|||
|
|
|
|||
|
|
from cmvr.api import dexhand_service_pb2_grpc
|
|||
|
|
from cmvr.api import dexhand_command_pb2
|
|||
|
|
from google.protobuf.timestamp_pb2 import Timestamp
|
|||
|
|
|
|||
|
|
|
|||
|
|
class DexHandSensorClient:
|
|||
|
|
"""灵巧手传感器数据客户端"""
|
|||
|
|
|
|||
|
|
def __init__(self, ip="192.168.0.222", port=50052, device_id="dexhand_001", timeout=2, retries=1):
|
|||
|
|
self.ip = ip
|
|||
|
|
self.port = port
|
|||
|
|
self.address = f"{ip}:{port}"
|
|||
|
|
self.device_id = device_id
|
|||
|
|
self.timeout = timeout
|
|||
|
|
self.retries = retries
|
|||
|
|
self.channel = None
|
|||
|
|
self.stub = None
|
|||
|
|
self._connect_with_retry()
|
|||
|
|
|
|||
|
|
def _connect_with_retry(self):
|
|||
|
|
"""连接gRPC服务器"""
|
|||
|
|
attempt = 0
|
|||
|
|
while attempt <= self.retries:
|
|||
|
|
print(f"Connecting to DexHand gRPC server at {self.address}... (Attempt {attempt + 1})")
|
|||
|
|
try:
|
|||
|
|
self.channel = grpc.insecure_channel(self.address)
|
|||
|
|
grpc.channel_ready_future(self.channel).result(timeout=self.timeout)
|
|||
|
|
self.stub = dexhand_service_pb2_grpc.DexHandServiceStub(self.channel)
|
|||
|
|
print(f"Successfully connected to DexHand gRPC server: {self.address}")
|
|||
|
|
print(f"Device ID: {self.device_id}")
|
|||
|
|
return True
|
|||
|
|
except grpc.FutureTimeoutError:
|
|||
|
|
attempt += 1
|
|||
|
|
print(f"Connection timed out, attempt {attempt} failed")
|
|||
|
|
if attempt > self.retries:
|
|||
|
|
return False
|
|||
|
|
print(f"Waiting {self.timeout}s before retrying...")
|
|||
|
|
time.sleep(self.timeout)
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"Connection error: {e}")
|
|||
|
|
return False
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
def get_sensor_data(self) -> Optional[dexhand_command_pb2.GetSensorDataCommand.Feedback]:
|
|||
|
|
"""获取传感器数据"""
|
|||
|
|
if not self.stub:
|
|||
|
|
print("No gRPC connection available")
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
# 创建请求
|
|||
|
|
timestamp = Timestamp()
|
|||
|
|
timestamp.GetCurrentTime()
|
|||
|
|
|
|||
|
|
request = dexhand_command_pb2.GetSensorDataCommand.Request()
|
|||
|
|
request.header.device_id = self.device_id
|
|||
|
|
request.header.timestamp.CopyFrom(timestamp)
|
|||
|
|
|
|||
|
|
# 发送请求
|
|||
|
|
feedback = self.stub.GetSensorData(request)
|
|||
|
|
return feedback
|
|||
|
|
|
|||
|
|
except grpc.RpcError as e:
|
|||
|
|
print(f"gRPC error: {e.code()} - {e.details()}")
|
|||
|
|
return None
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"Error getting sensor data: {e}")
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
def close(self):
|
|||
|
|
"""关闭连接"""
|
|||
|
|
if self.channel:
|
|||
|
|
self.channel.close()
|
|||
|
|
print(f"Closed connection to DexHand gRPC server {self.address}")
|
|||
|
|
|
|||
|
|
def is_connected(self):
|
|||
|
|
"""检查是否已连接"""
|
|||
|
|
return self.channel is not None and self.stub is not None
|
|||
|
|
|
|||
|
|
|
|||
|
|
class SensorDataPlotWidget(QWidget):
|
|||
|
|
"""传感器数据绘制窗口"""
|
|||
|
|
|
|||
|
|
def __init__(self, parent=None):
|
|||
|
|
super().__init__(parent)
|
|||
|
|
self.client = None
|
|||
|
|
self.update_timer = QTimer()
|
|||
|
|
self.data_buffer = {
|
|||
|
|
'sum': [], # sum数据缓冲区
|
|||
|
|
'peak': [] # peak数据缓冲区
|
|||
|
|
}
|
|||
|
|
self.time_buffer = [] # 时间戳缓冲区
|
|||
|
|
self.max_points = 100 # 最大显示点数
|
|||
|
|
self.current_finger = dexhand_command_pb2.SensorData.INDEX # 默认食指
|
|||
|
|
self.current_part = dexhand_command_pb2.SensorData.TIP # 默认指端
|
|||
|
|
|
|||
|
|
self.setup_ui()
|
|||
|
|
self.setup_plots()
|
|||
|
|
|
|||
|
|
def setup_ui(self):
|
|||
|
|
"""设置UI"""
|
|||
|
|
layout = QVBoxLayout()
|
|||
|
|
self.setLayout(layout)
|
|||
|
|
|
|||
|
|
# 标题
|
|||
|
|
title = QLabel("灵巧手传感器数据监控 - 食指指端")
|
|||
|
|
title_font = QFont()
|
|||
|
|
title_font.setPointSize(14)
|
|||
|
|
title_font.setBold(True)
|
|||
|
|
title.setFont(title_font)
|
|||
|
|
title.setAlignment(Qt.AlignCenter)
|
|||
|
|
layout.addWidget(title)
|
|||
|
|
|
|||
|
|
# 控制面板
|
|||
|
|
control_group = QGroupBox("连接配置")
|
|||
|
|
control_layout = QGridLayout()
|
|||
|
|
control_group.setLayout(control_layout)
|
|||
|
|
|
|||
|
|
# IP地址
|
|||
|
|
control_layout.addWidget(QLabel("服务器IP:"), 0, 0)
|
|||
|
|
self.ip_edit = QLineEdit("192.168.0.222")
|
|||
|
|
control_layout.addWidget(self.ip_edit, 0, 1)
|
|||
|
|
|
|||
|
|
# 端口
|
|||
|
|
control_layout.addWidget(QLabel("端口:"), 1, 0)
|
|||
|
|
self.port_edit = QLineEdit("50052")
|
|||
|
|
control_layout.addWidget(self.port_edit, 1, 1)
|
|||
|
|
|
|||
|
|
# 设备ID
|
|||
|
|
control_layout.addWidget(QLabel("设备ID:"), 2, 0)
|
|||
|
|
self.device_id_edit = QLineEdit("dexhand_001")
|
|||
|
|
control_layout.addWidget(self.device_id_edit, 2, 1)
|
|||
|
|
|
|||
|
|
# 完整地址显示
|
|||
|
|
control_layout.addWidget(QLabel("完整地址:"), 3, 0)
|
|||
|
|
self.address_label = QLabel("192.168.0.222:50052")
|
|||
|
|
self.address_label.setStyleSheet("font-weight: bold;")
|
|||
|
|
control_layout.addWidget(self.address_label, 3, 1)
|
|||
|
|
|
|||
|
|
# 连接按钮
|
|||
|
|
self.connect_btn = QPushButton("连接")
|
|||
|
|
self.connect_btn.clicked.connect(self.toggle_connection)
|
|||
|
|
self.connect_btn.setMinimumHeight(40)
|
|||
|
|
self.connect_btn.setStyleSheet("font-weight: bold;")
|
|||
|
|
control_layout.addWidget(self.connect_btn, 4, 0, 1, 2)
|
|||
|
|
|
|||
|
|
# 连接状态
|
|||
|
|
self.status_label = QLabel("未连接")
|
|||
|
|
self.status_label.setAlignment(Qt.AlignCenter)
|
|||
|
|
self.status_label.setStyleSheet("color: red; font-weight: bold; padding: 5px;")
|
|||
|
|
control_layout.addWidget(self.status_label, 5, 0, 1, 2)
|
|||
|
|
|
|||
|
|
layout.addWidget(control_group)
|
|||
|
|
|
|||
|
|
# 数据配置面板
|
|||
|
|
data_config_group = QGroupBox("数据配置")
|
|||
|
|
data_config_layout = QGridLayout()
|
|||
|
|
data_config_group.setLayout(data_config_layout)
|
|||
|
|
|
|||
|
|
# 更新频率
|
|||
|
|
data_config_layout.addWidget(QLabel("更新频率(ms):"), 0, 0)
|
|||
|
|
self.freq_spin = QSpinBox()
|
|||
|
|
self.freq_spin.setRange(10, 1000)
|
|||
|
|
self.freq_spin.setValue(50)
|
|||
|
|
self.freq_spin.valueChanged.connect(self.set_update_interval)
|
|||
|
|
data_config_layout.addWidget(self.freq_spin, 0, 1)
|
|||
|
|
|
|||
|
|
# 显示点数
|
|||
|
|
data_config_layout.addWidget(QLabel("显示点数:"), 1, 0)
|
|||
|
|
self.points_spin = QSpinBox()
|
|||
|
|
self.points_spin.setRange(10, 500)
|
|||
|
|
self.points_spin.setValue(100)
|
|||
|
|
self.points_spin.valueChanged.connect(self.set_max_points)
|
|||
|
|
data_config_layout.addWidget(self.points_spin, 1, 1)
|
|||
|
|
|
|||
|
|
# 清空数据按钮
|
|||
|
|
self.clear_btn = QPushButton("清空数据")
|
|||
|
|
self.clear_btn.clicked.connect(self.clear_data)
|
|||
|
|
self.clear_btn.setEnabled(False)
|
|||
|
|
data_config_layout.addWidget(self.clear_btn, 2, 0)
|
|||
|
|
|
|||
|
|
# 测试连接按钮
|
|||
|
|
self.test_btn = QPushButton("测试连接")
|
|||
|
|
self.test_btn.clicked.connect(self.test_connection)
|
|||
|
|
data_config_layout.addWidget(self.test_btn, 2, 1)
|
|||
|
|
|
|||
|
|
layout.addWidget(data_config_group)
|
|||
|
|
|
|||
|
|
# 创建绘图区域
|
|||
|
|
plots_widget = QWidget()
|
|||
|
|
plots_layout = QVBoxLayout()
|
|||
|
|
plots_widget.setLayout(plots_layout)
|
|||
|
|
|
|||
|
|
# SUM曲线图
|
|||
|
|
self.sum_plot_widget = pg.PlotWidget(title="食指指端传感器数据 - SUM值")
|
|||
|
|
self.sum_plot_widget.setLabel('left', 'SUM值')
|
|||
|
|
self.sum_plot_widget.setLabel('bottom', '时间 (秒)')
|
|||
|
|
self.sum_plot_widget.showGrid(x=True, y=True, alpha=0.3)
|
|||
|
|
self.sum_plot_widget.addLegend()
|
|||
|
|
self.sum_curve = self.sum_plot_widget.plot(pen='y', name='SUM值', width=2)
|
|||
|
|
plots_layout.addWidget(self.sum_plot_widget)
|
|||
|
|
|
|||
|
|
# PEAK曲线图
|
|||
|
|
self.peak_plot_widget = pg.PlotWidget(title="食指指端传感器数据 - PEAK值")
|
|||
|
|
self.peak_plot_widget.setLabel('left', 'PEAK值')
|
|||
|
|
self.peak_plot_widget.setLabel('bottom', '时间 (秒)')
|
|||
|
|
self.peak_plot_widget.showGrid(x=True, y=True, alpha=0.3)
|
|||
|
|
self.peak_plot_widget.addLegend()
|
|||
|
|
self.peak_curve = self.peak_plot_widget.plot(pen='r', name='PEAK值', width=2)
|
|||
|
|
plots_layout.addWidget(self.peak_plot_widget)
|
|||
|
|
|
|||
|
|
layout.addWidget(plots_widget)
|
|||
|
|
|
|||
|
|
# 实时数据展示
|
|||
|
|
data_group = QGroupBox("当前数据")
|
|||
|
|
data_layout = QHBoxLayout()
|
|||
|
|
data_group.setLayout(data_layout)
|
|||
|
|
|
|||
|
|
self.sum_label = QLabel("SUM: --")
|
|||
|
|
self.sum_label.setStyleSheet("font-weight: bold; color: orange; font-size: 12pt;")
|
|||
|
|
data_layout.addWidget(self.sum_label)
|
|||
|
|
|
|||
|
|
self.peak_label = QLabel("PEAK: --")
|
|||
|
|
self.peak_label.setStyleSheet("font-weight: bold; color: red; font-size: 12pt;")
|
|||
|
|
data_layout.addWidget(self.peak_label)
|
|||
|
|
|
|||
|
|
self.timestamp_label = QLabel("时间: --")
|
|||
|
|
self.timestamp_label.setStyleSheet("font-size: 10pt;")
|
|||
|
|
data_layout.addWidget(self.timestamp_label)
|
|||
|
|
|
|||
|
|
layout.addWidget(data_group)
|
|||
|
|
|
|||
|
|
# 连接输入变化信号
|
|||
|
|
self.ip_edit.textChanged.connect(self.update_address_label)
|
|||
|
|
self.port_edit.textChanged.connect(self.update_address_label)
|
|||
|
|
|
|||
|
|
def update_address_label(self):
|
|||
|
|
"""更新地址标签"""
|
|||
|
|
ip = self.ip_edit.text().strip()
|
|||
|
|
port = self.port_edit.text().strip()
|
|||
|
|
if ip and port:
|
|||
|
|
self.address_label.setText(f"{ip}:{port}")
|
|||
|
|
else:
|
|||
|
|
self.address_label.setText("--")
|
|||
|
|
|
|||
|
|
def setup_plots(self):
|
|||
|
|
"""初始化绘图"""
|
|||
|
|
self.sum_curve.setData([], [])
|
|||
|
|
self.peak_curve.setData([], [])
|
|||
|
|
|
|||
|
|
def test_connection(self):
|
|||
|
|
"""测试连接"""
|
|||
|
|
ip = self.ip_edit.text().strip()
|
|||
|
|
port = self.port_edit.text().strip()
|
|||
|
|
device_id = self.device_id_edit.text().strip()
|
|||
|
|
|
|||
|
|
if not ip or not port:
|
|||
|
|
QMessageBox.warning(self, "警告", "请输入IP地址和端口")
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
port = int(port)
|
|||
|
|
except ValueError:
|
|||
|
|
QMessageBox.warning(self, "警告", "端口必须是数字")
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
# 创建临时客户端测试连接
|
|||
|
|
test_client = DexHandSensorClient(ip, port, device_id, timeout=2, retries=0)
|
|||
|
|
if test_client.is_connected():
|
|||
|
|
QMessageBox.information(self, "成功", "连接测试成功!")
|
|||
|
|
test_client.close()
|
|||
|
|
else:
|
|||
|
|
QMessageBox.critical(self, "失败", "连接测试失败,请检查服务器地址和网络")
|
|||
|
|
|
|||
|
|
def set_client(self, client: DexHandSensorClient):
|
|||
|
|
"""设置客户端"""
|
|||
|
|
self.client = client
|
|||
|
|
self.status_label.setText(f"已连接 (设备: {client.device_id})")
|
|||
|
|
self.status_label.setStyleSheet("color: green; font-weight: bold; padding: 5px;")
|
|||
|
|
self.connect_btn.setText("断开连接")
|
|||
|
|
self.clear_btn.setEnabled(True)
|
|||
|
|
self.ip_edit.setEnabled(False)
|
|||
|
|
self.port_edit.setEnabled(False)
|
|||
|
|
self.device_id_edit.setEnabled(False)
|
|||
|
|
self.test_btn.setEnabled(False)
|
|||
|
|
|
|||
|
|
def clear_client(self):
|
|||
|
|
"""清除客户端"""
|
|||
|
|
self.client = None
|
|||
|
|
self.status_label.setText("未连接")
|
|||
|
|
self.status_label.setStyleSheet("color: red; font-weight: bold; padding: 5px;")
|
|||
|
|
self.connect_btn.setText("连接")
|
|||
|
|
self.clear_btn.setEnabled(False)
|
|||
|
|
self.ip_edit.setEnabled(True)
|
|||
|
|
self.port_edit.setEnabled(True)
|
|||
|
|
self.device_id_edit.setEnabled(True)
|
|||
|
|
self.test_btn.setEnabled(True)
|
|||
|
|
|
|||
|
|
def toggle_connection(self):
|
|||
|
|
"""切换连接状态"""
|
|||
|
|
if self.client is None or not self.client.is_connected():
|
|||
|
|
# 尝试连接
|
|||
|
|
ip = self.ip_edit.text().strip()
|
|||
|
|
port = self.port_edit.text().strip()
|
|||
|
|
device_id = self.device_id_edit.text().strip()
|
|||
|
|
|
|||
|
|
if not ip or not port:
|
|||
|
|
QMessageBox.warning(self, "警告", "请输入IP地址和端口")
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
port = int(port)
|
|||
|
|
except ValueError:
|
|||
|
|
QMessageBox.warning(self, "警告", "端口必须是数字")
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
# 显示连接中状态
|
|||
|
|
self.status_label.setText("连接中...")
|
|||
|
|
self.status_label.setStyleSheet("color: orange; font-weight: bold; padding: 5px;")
|
|||
|
|
QApplication.processEvents()
|
|||
|
|
|
|||
|
|
# 创建客户端
|
|||
|
|
try:
|
|||
|
|
client = DexHandSensorClient(ip, port, device_id)
|
|||
|
|
if client.is_connected():
|
|||
|
|
self.set_client(client)
|
|||
|
|
self.start_updating()
|
|||
|
|
else:
|
|||
|
|
self.clear_client()
|
|||
|
|
QMessageBox.critical(self, "错误", f"无法连接到服务器 {ip}:{port}")
|
|||
|
|
except Exception as e:
|
|||
|
|
self.clear_client()
|
|||
|
|
QMessageBox.critical(self, "错误", f"连接失败: {str(e)}")
|
|||
|
|
else:
|
|||
|
|
# 断开连接
|
|||
|
|
self.stop_updating()
|
|||
|
|
self.client.close()
|
|||
|
|
self.clear_client()
|
|||
|
|
self.clear_data()
|
|||
|
|
|
|||
|
|
def start_updating(self):
|
|||
|
|
"""开始更新数据"""
|
|||
|
|
self.update_timer.timeout.connect(self.update_data)
|
|||
|
|
self.update_timer.start(self.freq_spin.value())
|
|||
|
|
|
|||
|
|
def stop_updating(self):
|
|||
|
|
"""停止更新数据"""
|
|||
|
|
self.update_timer.stop()
|
|||
|
|
try:
|
|||
|
|
self.update_timer.timeout.disconnect()
|
|||
|
|
except:
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
def set_update_interval(self, ms: int):
|
|||
|
|
"""设置更新间隔"""
|
|||
|
|
if self.update_timer.isActive():
|
|||
|
|
self.update_timer.setInterval(ms)
|
|||
|
|
|
|||
|
|
def set_max_points(self, points: int):
|
|||
|
|
"""设置最大显示点数"""
|
|||
|
|
self.max_points = points
|
|||
|
|
# 如果当前数据超过新限制,截断数据
|
|||
|
|
if len(self.time_buffer) > self.max_points:
|
|||
|
|
self.time_buffer = self.time_buffer[-self.max_points:]
|
|||
|
|
self.data_buffer['sum'] = self.data_buffer['sum'][-self.max_points:]
|
|||
|
|
self.data_buffer['peak'] = self.data_buffer['peak'][-self.max_points:]
|
|||
|
|
|
|||
|
|
def clear_data(self):
|
|||
|
|
"""清空数据"""
|
|||
|
|
self.data_buffer['sum'].clear()
|
|||
|
|
self.data_buffer['peak'].clear()
|
|||
|
|
self.time_buffer.clear()
|
|||
|
|
self.sum_curve.setData([], [])
|
|||
|
|
self.peak_curve.setData([], [])
|
|||
|
|
self.sum_label.setText("SUM: --")
|
|||
|
|
self.peak_label.setText("PEAK: --")
|
|||
|
|
|
|||
|
|
def extract_finger_data(self, feedback):
|
|||
|
|
"""提取食指指端的传感器数据"""
|
|||
|
|
if not feedback or not feedback.sensor:
|
|||
|
|
return None, None
|
|||
|
|
|
|||
|
|
sum_values = []
|
|||
|
|
peak_values = []
|
|||
|
|
|
|||
|
|
for sensor in feedback.sensor:
|
|||
|
|
# 检查是否为食指指端
|
|||
|
|
if (sensor.finger_type == self.current_finger and
|
|||
|
|
sensor.part_type == self.current_part):
|
|||
|
|
|
|||
|
|
# 处理传感器数据
|
|||
|
|
if sensor.data:
|
|||
|
|
for row in sensor.data:
|
|||
|
|
if row.values:
|
|||
|
|
row_array = np.array(row.values)
|
|||
|
|
sum_values.append(np.sum(row_array))
|
|||
|
|
peak_values.append(np.max(row_array))
|
|||
|
|
|
|||
|
|
if sum_values and peak_values:
|
|||
|
|
return np.mean(sum_values), np.mean(peak_values)
|
|||
|
|
return None, None
|
|||
|
|
|
|||
|
|
def update_data(self):
|
|||
|
|
"""更新数据"""
|
|||
|
|
if not self.client or not self.client.is_connected():
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
feedback = self.client.get_sensor_data()
|
|||
|
|
if not feedback:
|
|||
|
|
# 如果获取数据失败,可能连接已断开
|
|||
|
|
if self.client and not self.client.is_connected():
|
|||
|
|
self.toggle_connection() # 自动断开
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
# 提取食指指端数据
|
|||
|
|
sum_val, peak_val = self.extract_finger_data(feedback)
|
|||
|
|
|
|||
|
|
if sum_val is not None and peak_val is not None:
|
|||
|
|
# 更新时间戳
|
|||
|
|
current_time = time.time()
|
|||
|
|
self.time_buffer.append(current_time)
|
|||
|
|
self.data_buffer['sum'].append(sum_val)
|
|||
|
|
self.data_buffer['peak'].append(peak_val)
|
|||
|
|
|
|||
|
|
# 限制缓冲区大小
|
|||
|
|
if len(self.time_buffer) > self.max_points:
|
|||
|
|
self.time_buffer.pop(0)
|
|||
|
|
self.data_buffer['sum'].pop(0)
|
|||
|
|
self.data_buffer['peak'].pop(0)
|
|||
|
|
|
|||
|
|
# 更新曲线
|
|||
|
|
if len(self.time_buffer) > 1:
|
|||
|
|
# 归一化时间显示(相对于第一个点)
|
|||
|
|
t0 = self.time_buffer[0]
|
|||
|
|
plot_time = [t - t0 for t in self.time_buffer]
|
|||
|
|
|
|||
|
|
self.sum_curve.setData(plot_time, self.data_buffer['sum'])
|
|||
|
|
self.peak_curve.setData(plot_time, self.data_buffer['peak'])
|
|||
|
|
|
|||
|
|
# 自动调整Y轴范围
|
|||
|
|
self.sum_plot_widget.autoRange()
|
|||
|
|
self.peak_plot_widget.autoRange()
|
|||
|
|
|
|||
|
|
# 更新标签
|
|||
|
|
self.sum_label.setText(f"SUM: {sum_val:.2f}")
|
|||
|
|
self.peak_label.setText(f"PEAK: {peak_val:.2f}")
|
|||
|
|
self.timestamp_label.setText(f"时间: {time.strftime('%H:%M:%S')}")
|
|||
|
|
|
|||
|
|
|
|||
|
|
class SensorDataWindow(QMainWindow):
|
|||
|
|
"""传感器数据监控主窗口"""
|
|||
|
|
|
|||
|
|
def __init__(self):
|
|||
|
|
super().__init__()
|
|||
|
|
self.setWindowTitle("灵巧手传感器数据监控 - 可配置版")
|
|||
|
|
self.setGeometry(100, 100, 900, 1000)
|
|||
|
|
|
|||
|
|
# 设置中央部件
|
|||
|
|
central_widget = SensorDataPlotWidget()
|
|||
|
|
self.setCentralWidget(central_widget)
|
|||
|
|
|
|||
|
|
def closeEvent(self, event):
|
|||
|
|
"""关闭窗口时清理资源"""
|
|||
|
|
central_widget = self.centralWidget()
|
|||
|
|
if central_widget:
|
|||
|
|
central_widget.stop_updating()
|
|||
|
|
if central_widget.client:
|
|||
|
|
central_widget.client.close()
|
|||
|
|
event.accept()
|
|||
|
|
|
|||
|
|
|
|||
|
|
def main():
|
|||
|
|
"""主函数"""
|
|||
|
|
import sys
|
|||
|
|
|
|||
|
|
app = QApplication(sys.argv)
|
|||
|
|
app.setStyle('Fusion') # 使用Fusion风格,看起来更现代
|
|||
|
|
|
|||
|
|
window = SensorDataWindow()
|
|||
|
|
window.show()
|
|||
|
|
|
|||
|
|
sys.exit(app.exec_())
|
|||
|
|
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
main()
|