PyQt5 QThread中调用pyserial出现ClearCommError句柄无效错误求助
问题根因
你遇到的串口句柄无效报错,核心原因是Windows系统下串口句柄具有线程亲和性:你在主线程中初始化并打开了全局serialPort对象,后续在子线程中直接调用该对象的in_waiting、read()等方法属于跨线程操作串口资源,系统会直接拒绝访问抛出错误。单线程场景下所有串口操作都在同一个线程执行,因此运行正常。
修复方案
核心原则是所有串口相关操作(初始化、打开、读、写、关闭)全部放在同一个线程中执行,主线程和串口线程之间仅通过Qt信号槽传递指令和数据,禁止跨线程直接操作串口对象。
修改点如下:
- 移除全局串口对象,将所有串口逻辑封装到Worker线程类内部
- 新增Qt信号实现主线程向串口线程传递发送指令、配置串口参数的请求
- 新增线程退出标志,避免程序退出时子线程死锁
- 按钮点击事件通过信号触发串口线程的发送逻辑,避免跨线程写串口
修改后的完整代码
from PyQt5 import uic from PyQt5.QtWidgets import QApplication,QWidget,QMainWindow from PyQt5.QtCore import QCoreApplication, QObject, pyqtSignal, pyqtSlot, QThread import serial from GUI import Ui_MainGUIWindow import sys class Worker(QObject): finished = pyqtSignal() dataReady = pyqtSignal(str) # 新增接收发送指令的槽函数触发信号 send_cmd = pyqtSignal(str) def __init__(self, port, baud_rate): super().__init__() self.port = port self.baud_rate = baud_rate self.serial_port = None self.running_flag = True # 线程退出标志 @pyqtSlot() def run(self): # 串口初始化、打开全部在子线程执行 self.serial_port = serial.Serial( port = self.port, baudrate=self.baud_rate, bytesize=8, timeout=None, stopbits=serial.STOPBITS_ONE ) if not self.serial_port.isOpen(): self.serial_port.open() # 绑定发送指令的信号槽 self.send_cmd.connect(self.write_serial) # 读串口循环 while self.running_flag: if self.serial_port.in_waiting > 0: serial_string = self.serial_port.read() self.dataReady.emit(serial_string.decode('Ascii')) # 线程退出前关闭串口 self.serial_port.close() self.finished.emit() @pyqtSlot(str) def write_serial(self, cmd): if self.serial_port and self.serial_port.isOpen(): self.serial_port.write(f"{cmd}\r".encode()) def stop(self): self.running_flag = False class GUI(QWidget): def __init__(self): super().__init__() # 初始化Worker时传入串口参数,不要在主线程初始化串口 self.obj = Worker(port="COM20", baud_rate=9600) self.thread = QThread() self.obj.dataReady.connect(self.onDataReady) self.obj.moveToThread(self.thread) self.obj.finished.connect(self.thread.quit) self.thread.started.connect(self.obj.run) self.thread.finished.connect(app.exit) self.thread.start() self.initUI() def initUI(self): self.window = QMainWindow() self.form = Ui_MainGUIWindow() self.form.setupUi(self.window) # 按钮点击时发信号给Worker线程执行发送操作 self.form.FVButton.clicked.connect(lambda: self.obj.send_cmd.emit("fv")) self.window.show() @pyqtSlot(str) def onDataReady(self, serial_string): self.form.plainTextOutput.insertPlainText(serial_string) self.form.plainTextOutput.ensureCursorVisible() def closeEvent(self, event): # 窗口关闭时先停止Worker线程 self.obj.stop() self.thread.wait() event.accept() if __name__ == '__main__': app = QApplication(sys.argv) form = GUI() app.exec_() sys.exit()
Spyder内核崩溃问题解决
该问题是Spyder内置IPython内核的兼容性限制,内核对PyQt事件循环的管理存在缺陷,多次运行PyQt程序后残留的事件循环实例会导致内核崩溃,和你的代码逻辑无关。解决方案如下:
- 开发调试阶段每次运行代码前手动重启Spyder内核
- 正式运行时直接通过Anaconda命令提示符执行脚本,你当前已经验证该方式可正常运行
内容的提问来源于stack exchange,提问作者Nirmala
相关产品推荐
相关产品推荐

