You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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_())

关键修改说明

  1. 子线程处理串口操作

    • 创建SerialWorker继承QThread,把所有串口相关逻辑放到run方法中,避免阻塞主线程的UI事件循环,保证界面始终响应。
    • 通过_is_running标志安全控制循环的启动与停止,避免强制终止线程导致的资源泄漏。
  2. 按钮改为开关模式

    • 点击按钮可以切换采集状态:启动/停止,无需多次点击。
    • 线程结束时会自动关闭串口,避免资源占用。
  3. 自动填充空输入框

    • fill_empty_line_edit函数会优先填充第一个空的QLineEdit,当两个输入框都有内容时,默认循环覆盖(把第二个的内容移到第一个,新数据填入第二个),你可以根据需求修改这个逻辑(比如清空所有、忽略新数据等)。
  4. 状态反馈

    • 新增status_label显示串口连接状态、错误信息,方便调试和用户查看。

原代码中return self.custom_attribute.emit(data)的问题在于,return会直接退出while循环,导致只能获取一次数据,现在去掉return,每次采集到数据都发射信号,实现持续更新。

内容的提问来源于stack exchange,提问作者Sator

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 22:22:36