PyQt5:如何持续运行函数并将设备数据填入多个QLineEdit
如何让PyQt5串口数据采集持续运行并自动填充空QLineEdit?
首先,你的代码存在几个关键问题导致无法持续获取数据且会阻塞UI:
- 串口操作在主线程执行,
while循环会卡住整个界面,导致无响应 getdevice_data里的return语句会在第一次获取数据后直接退出循环,无法持续采集- 每次点击按钮都会重新打开串口,容易引发串口占用冲突
- 没有处理多个QLineEdit的自动填充逻辑
下面是修改后的完整解决方案,用子线程处理串口操作避免UI阻塞,同时实现持续采集并自动填充空的输入框:
完整修改代码
import sys from PyQt5 import QtWidgets as qtw from PyQt5 import QtCore as qtc from PyQt5 import QtGui as qtg import serial import time class SerialWorker(qtc.QThread): """负责在子线程中处理串口数据采集的工作类""" data_received = qtc.pyqtSignal(str) # 传递采集到的字符串数据 error_occurred = qtc.pyqtSignal(str) # 传递错误/状态信息 def __init__(self, port='COM6', baudrate=2400): super().__init__() self.port = port self.baudrate = baudrate self._is_running = False self.serial_port = None def run(self): """线程启动后执行的逻辑""" self._is_running = True try: # 仅在启动时打开一次串口 self.serial_port = serial.Serial( port=self.port, baudrate=self.baudrate, bytesize=7, parity=serial.PARITY_NONE, timeout=None, stopbits=1 ) if self.serial_port.is_open: self.error_occurred.emit("串口已连接,开始采集数据...") # 持续采集直到停止信号触发 while self._is_running and self.serial_port.is_open: # 读取串口数据 data = self.serial_port.read() time.sleep(1) remaining_data = self.serial_port.inWaiting() data += self.serial_port.read(remaining_data) # 解码并发射数据 try: data_str = data.decode("utf-8", "strict") self.data_received.emit(data_str) except UnicodeDecodeError: self.error_occurred.emit("数据解码失败,跳过当前帧") except serial.serialutil.SerialException: self.error_occurred.emit("无法打开串口,请检查端口或连接") finally: # 线程结束时确保串口关闭 if self.serial_port and self.serial_port.is_open: self.serial_port.close() self._is_running = False def stop(self): """停止数据采集""" self._is_running = False class CustmClass(qtw.QWidget): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # 界面布局 self.resize(300, 250) self.toggle_button = qtw.QPushButton("启动数据采集") self.status_label = qtw.QLabel("等待启动...") self.device_input1 = qtw.QLineEdit(placeholderText="等待数据填充") self.device_input2 = qtw.QLineEdit(placeholderText="等待数据填充") vbox_layout = qtw.QVBoxLayout() vbox_layout.addWidget(self.toggle_button) vbox_layout.addWidget(self.status_label) vbox_layout.addWidget(self.device_input1) vbox_layout.addWidget(self.device_input2) self.setLayout(vbox_layout) self.show() # 初始化串口工作线程 self.serial_worker = SerialWorker() # 连接信号与槽函数 self.serial_worker.data_received.connect(self.fill_empty_line_edit) self.serial_worker.error_occurred.connect(self.status_label.setText) self.toggle_button.clicked.connect(self.toggle_collection) def toggle_collection(self): """切换数据采集的启动/停止状态""" if not self.serial_worker.isRunning(): self.serial_worker.start() self.toggle_button.setText("停止数据采集") else: self.serial_worker.stop() self.serial_worker.wait() # 等待线程安全结束 self.toggle_button.setText("启动数据采集") self.status_label.setText("数据采集已停止") def fill_empty_line_edit(self, data): """自动填充第一个空的QLineEdit,若都满则循环覆盖""" if not self.device_input1.text(): self.device_input1.setText(data) elif not self.device_input2.text(): self.device_input2.setText(data) else: # 此处可根据需求修改:比如清空第一个、滚动显示等 self.device_input1.setText(self.device_input2.text()) self.device_input2.setText(data) if __name__ == '__main__': app = qtw.QApplication(sys.argv) w = CustmClass() sys.exit(app.exec_())
关键修改说明
子线程处理串口操作
- 创建
SerialWorker继承QThread,把所有串口相关逻辑放到run方法中,避免阻塞主线程的UI事件循环,保证界面始终响应。 - 通过
_is_running标志安全控制循环的启动与停止,避免强制终止线程导致的资源泄漏。
- 创建
按钮改为开关模式
- 点击按钮可以切换采集状态:启动/停止,无需多次点击。
- 线程结束时会自动关闭串口,避免资源占用。
自动填充空输入框
fill_empty_line_edit函数会优先填充第一个空的QLineEdit,当两个输入框都有内容时,默认循环覆盖(把第二个的内容移到第一个,新数据填入第二个),你可以根据需求修改这个逻辑(比如清空所有、忽略新数据等)。
状态反馈
- 新增
status_label显示串口连接状态、错误信息,方便调试和用户查看。
- 新增
原代码中return self.custom_attribute.emit(data)的问题在于,return会直接退出while循环,导致只能获取一次数据,现在去掉return,每次采集到数据都发射信号,实现持续更新。
内容的提问来源于stack exchange,提问作者Sator
相关产品推荐
相关产品推荐

