mirror of
https://github.com/goldenfishs/MRobot.git
synced 2025-07-27 16:59:01 +08:00
167 lines
6.0 KiB
Python
167 lines
6.0 KiB
Python
import serial
|
|
import serial.tools.list_ports
|
|
from PyQt5.QtCore import Qt, QThread, pyqtSignal
|
|
from PyQt5.QtGui import QTextCursor
|
|
from PyQt5.QtWidgets import QVBoxLayout, QHBoxLayout, QSizePolicy
|
|
from PyQt5.QtWidgets import QWidget
|
|
from qfluentwidgets import (
|
|
FluentIcon, PushButton, ComboBox, TextEdit, LineEdit, CheckBox,
|
|
SubtitleLabel, BodyLabel, HorizontalSeparator
|
|
)
|
|
|
|
class SerialReadThread(QThread):
|
|
data_received = pyqtSignal(str)
|
|
|
|
def __init__(self, ser):
|
|
super().__init__()
|
|
self.ser = ser
|
|
self._running = True
|
|
|
|
def run(self):
|
|
while self._running:
|
|
if self.ser and self.ser.is_open and self.ser.in_waiting:
|
|
try:
|
|
data = self.ser.readline().decode(errors='ignore')
|
|
self.data_received.emit(data)
|
|
except Exception:
|
|
pass
|
|
|
|
def stop(self):
|
|
self._running = False
|
|
self.wait()
|
|
|
|
class SerialTerminalInterface(QWidget):
|
|
def __init__(self, parent=None):
|
|
super().__init__(parent=parent)
|
|
self.setObjectName("serialTerminalInterface")
|
|
main_layout = QVBoxLayout(self)
|
|
main_layout.setSpacing(12)
|
|
|
|
# 顶部:串口设置区
|
|
top_hbox = QHBoxLayout()
|
|
top_hbox.addWidget(BodyLabel("串口:"))
|
|
self.port_combo = ComboBox()
|
|
self.refresh_ports()
|
|
top_hbox.addWidget(self.port_combo)
|
|
top_hbox.addWidget(BodyLabel("波特率:"))
|
|
self.baud_combo = ComboBox()
|
|
self.baud_combo.addItems(['9600', '115200', '57600', '38400', '19200', '4800'])
|
|
top_hbox.addWidget(self.baud_combo)
|
|
self.connect_btn = PushButton("连接")
|
|
self.connect_btn.clicked.connect(self.toggle_connection)
|
|
top_hbox.addWidget(self.connect_btn)
|
|
self.refresh_btn = PushButton(FluentIcon.SYNC, "刷新")
|
|
self.refresh_btn.clicked.connect(self.refresh_ports)
|
|
top_hbox.addWidget(self.refresh_btn)
|
|
top_hbox.addStretch()
|
|
main_layout.addLayout(top_hbox)
|
|
|
|
main_layout.addWidget(HorizontalSeparator())
|
|
|
|
# 中部:左侧预设命令,右侧显示区
|
|
center_hbox = QHBoxLayout()
|
|
# 左侧:预设命令竖排
|
|
preset_vbox = QVBoxLayout()
|
|
preset_vbox.addWidget(SubtitleLabel("快捷指令"))
|
|
preset_vbox.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
self.preset_commands = [
|
|
("线程监视器", "htop"),
|
|
("陀螺仪校准", "cali_gyro"),
|
|
("性能监视", "htop"),
|
|
("重启", "reset"),
|
|
("显示所有设备", "ls /dev"),
|
|
("查询id", "id"),
|
|
]
|
|
for label, cmd in self.preset_commands:
|
|
btn = PushButton(label)
|
|
btn.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
|
|
btn.clicked.connect(lambda _, c=cmd: self.send_preset_command(c))
|
|
preset_vbox.addWidget(btn)
|
|
preset_vbox.addStretch()
|
|
main_layout.addLayout(center_hbox, stretch=1)
|
|
|
|
# 右侧:串口数据显示区
|
|
self.text_edit = TextEdit()
|
|
self.text_edit.setReadOnly(True)
|
|
self.text_edit.setMinimumWidth(400)
|
|
center_hbox.addWidget(self.text_edit, 3)
|
|
center_hbox.addLayout(preset_vbox, 1)
|
|
|
|
main_layout.addWidget(HorizontalSeparator())
|
|
|
|
# 底部:输入区
|
|
bottom_hbox = QHBoxLayout()
|
|
self.input_line = LineEdit()
|
|
self.input_line.setPlaceholderText("输入内容,回车发送")
|
|
self.input_line.returnPressed.connect(self.send_data)
|
|
bottom_hbox.addWidget(self.input_line, 4)
|
|
send_btn = PushButton("发送")
|
|
send_btn.clicked.connect(self.send_data)
|
|
bottom_hbox.addWidget(send_btn, 1)
|
|
self.auto_enter_checkbox = CheckBox("自动回车 ")
|
|
self.auto_enter_checkbox.setChecked(True)
|
|
bottom_hbox.addWidget(self.auto_enter_checkbox)
|
|
bottom_hbox.addStretch()
|
|
main_layout.addLayout(bottom_hbox)
|
|
|
|
self.ser = None
|
|
self.read_thread = None
|
|
|
|
def send_preset_command(self, cmd):
|
|
self.input_line.setText(cmd)
|
|
self.send_data()
|
|
|
|
def refresh_ports(self):
|
|
self.port_combo.clear()
|
|
ports = serial.tools.list_ports.comports()
|
|
for port in ports:
|
|
self.port_combo.addItem(port.device)
|
|
|
|
def toggle_connection(self):
|
|
if self.ser and self.ser.is_open:
|
|
self.disconnect_serial()
|
|
else:
|
|
self.connect_serial()
|
|
|
|
def connect_serial(self):
|
|
port = self.port_combo.currentText()
|
|
baud = int(self.baud_combo.currentText())
|
|
try:
|
|
self.ser = serial.Serial(port, baud, timeout=0.1)
|
|
self.connect_btn.setText("断开")
|
|
self.text_edit.append(f"已连接到 {port} @ {baud}")
|
|
self.read_thread = SerialReadThread(self.ser)
|
|
self.read_thread.data_received.connect(self.display_data)
|
|
self.read_thread.start()
|
|
except Exception as e:
|
|
self.text_edit.append(f"连接失败: {e}")
|
|
|
|
def disconnect_serial(self):
|
|
if self.read_thread:
|
|
self.read_thread.stop()
|
|
self.read_thread = None
|
|
if self.ser:
|
|
self.ser.close()
|
|
self.ser = None
|
|
self.connect_btn.setText("连接")
|
|
self.text_edit.append("已断开连接")
|
|
|
|
def display_data(self, data):
|
|
self.text_edit.moveCursor(QTextCursor.End)
|
|
self.text_edit.insertPlainText(data)
|
|
self.text_edit.moveCursor(QTextCursor.End)
|
|
|
|
def send_data(self):
|
|
if self.ser and self.ser.is_open:
|
|
text = self.input_line.text()
|
|
try:
|
|
if not text:
|
|
self.ser.write('\n'.encode())
|
|
else:
|
|
for char in text:
|
|
self.ser.write(char.encode())
|
|
if self.auto_enter_checkbox.isChecked():
|
|
self.ser.write('\n'.encode())
|
|
except Exception as e:
|
|
self.text_edit.append(f"发送失败: {e}")
|
|
self.input_line.clear() |