基于Python的Qt开发之Pyside6 QtSerialPort库的使用

一、简介

QtSerialPort 是 PySide6 用于实现串口异步通信的核心模块,专门用于与串口设备(如单片机、传感器、蓝牙模块、串口打印机等)进行数据交互。

二、QtSerialPort库的核心内容

QtSerialPort 库的核心功能由两个关键类提供,核心操作类QSerialPort辅助信息类QSerialPortInfo, 所有串口操作均围绕这两个类展开:

1、核心操作类:QSerialPort

QSerialPort是实现串口通信的核心类,负责串口的配置、打开 / 关闭、数据读写、错误处理等核心操作,它的核心特性包括:

  • 支持串口参数配置(波特率、数据位、校验位、停止位、流控)
  • 提供异步读写接口(基于信号与槽,无阻塞)
  • 支持串口状态检测和错误反馈
  • 支持数据缓冲区操作

QSerialPort 常用属性(串口参数)

串口通信的参数必须与外设保持一致,否则无法正常通信,常用参数如下:

参数类型示例说明
波特率QSerialPort.Baud9600QSerialPort.Baud115200串口通信速率,最常用 9600、115200
数据位QSerialPort.Data8QSerialPort.Data7每帧数据的位数,最常用 8 位
校验位QSerialPort.NoParityQSerialPort.EvenParity奇偶校验位,最常用无校验(NoParity)
停止位QSerialPort.OneStopQSerialPort.TwoStop数据帧结束标志,最常用 1 位
流控QSerialPort.NoFlowControlQSerialPort.HardwareControl流量控制,最常用无流控

QSerialPort 常用信号

由于 PySide6 采用信号与槽机制,QSerialPort 的异步操作通过以下核心信号通知上层逻辑:

  • readyRead():当串口接收缓冲区有新数据到达时触发(读取数据的核心信号
  • errorOccurred(error):当串口发生错误时触发(如端口不存在、权限不足)
  • bytesWritten(bytes):当数据成功写入串口发送缓冲区时触发
QSerialPort 常用方法
  • setPortName(portName):设置串口名(如 "COM3"(Windows)、"/dev/ttyUSB0"(Linux))
  • open(openMode):打开串口,打开模式常用 QSerialPort.ReadWrite(读写模式)
  • close():关闭串口,释放串口资源
  • write(data):向串口写入数据(需传入字节类型 bytes,而非字符串)
  • readAll():读取接收缓冲区中的所有可用数据(返回 QByteArray,可转换为 Python 字节 / 字符串)
  • read(maxSize):读取指定长度的字节数据
  • isOpen():判断串口是否已成功打开
  • clear():清空接收 / 发送缓冲区

2. 辅助信息类:QSerialPortInfo

QSerialPortInfo 是辅助类,用于枚举系统中可用的串口设备,获取串口的详细信息,无需打开串口即可使用,核心作用是帮助用户选择有效的串口。

QSerialPortInfo 常用方法
  • availablePorts():静态方法,返回系统中所有可用串口的 QSerialPortInfo 列表
  • portName():获取串口名(如 "COM3"、"/dev/ttyUSB0")
  • description():获取串口设备描述(如 "USB-SERIAL CH340")
  • manufacturer():获取串口设备制造商信息
  • isValid():判断当前 QSerialPortInfo 对应的串口是否有效

三、QtSerialPort 基本使用流程

使用 QtSerialPort 实现串口通信的核心流程分为 5 步,全程基于信号与槽实现异步无阻塞操作:

  1. 导入核心类
  2. 枚举可用串口(可选,方便用户选择)
  3. 配置串口参数并打开串口
  4. 绑定信号与槽(处理数据接收、错误反馈)
  5. 发送数据 / 关闭串口

四、完整可运行示例

下面实现一个简单的串口通信工具,支持枚举串口、打开 / 关闭串口、发送数据、接收数据并显示,可直接运行测试(需安装 PySide6 和对应串口驱动)。

from PySide6.QtWidgets import ( QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QComboBox, QLineEdit, QTextEdit ) from PySide6.QtCore import Qt from PySide6.QtSerialPort import QSerialPort, QSerialPortInfo import sys class SerialPortTool(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("QtSerialPort 串口通信示例") self.setFixedSize(600, 400) # 1. 初始化串口对象(核心) self.serial_port = QSerialPort() # 2. 构建界面 self._init_ui() # 3. 绑定信号与槽 self._bind_signals() # 4. 初始化时枚举可用串口 self.refresh_serial_ports() def _init_ui(self): """构建界面""" central_widget = QWidget() self.setCentralWidget(central_widget) main_layout = QVBoxLayout(central_widget) # 串口选择区域 port_layout = QHBoxLayout() self.port_combo = QComboBox() self.refresh_btn = QPushButton("刷新串口") self.open_btn = QPushButton("打开串口") self.close_btn = QPushButton("关闭串口") self.close_btn.setEnabled(False) port_layout.addWidget(self.port_combo) port_layout.addWidget(self.refresh_btn) port_layout.addWidget(self.open_btn) port_layout.addWidget(self.close_btn) # 数据发送区域 send_layout = QHBoxLayout() self.send_input = QLineEdit() self.send_input.setPlaceholderText("输入要发送的数据,按回车或点击发送按钮") self.send_btn = QPushButton("发送数据") send_layout.addWidget(self.send_input) send_layout.addWidget(self.send_btn) # 数据接收区域 self.recv_text = QTextEdit() self.recv_text.setPlaceholderText("接收的数据将显示在这里...") self.recv_text.setReadOnly(True) # 添加到主布局 main_layout.addLayout(port_layout) main_layout.addLayout(send_layout) main_layout.addWidget(self.recv_text) def _bind_signals(self): """绑定信号与槽""" # 界面按钮信号 self.refresh_btn.clicked.connect(self.refresh_serial_ports) self.open_btn.clicked.connect(self.open_serial_port) self.close_btn.clicked.connect(self.close_serial_port) self.send_btn.clicked.connect(self.send_data) self.send_input.returnPressed.connect(self.send_data) # 串口核心信号 self.serial_port.readyRead.connect(self.read_serial_data) # 接收数据 self.serial_port.errorOccurred.connect(self.handle_serial_error) # 错误处理 def refresh_serial_ports(self): """枚举并刷新可用串口""" self.port_combo.clear() # 获取所有可用串口 available_ports = QSerialPortInfo.availablePorts() for port_info in available_ports: # 显示格式:串口名 - 设备描述 port_text = f"{port_info.portName()} - {port_info.description()}" self.port_combo.addItem(port_text, port_info.portName()) def open_serial_port(self): """配置并打开串口""" if self.port_combo.count() == 0: self._append_recv_text("错误:无可用串口!") return # 获取选中的串口名 selected_port = self.port_combo.currentData() if not selected_port: self._append_recv_text("错误:无效的串口!") return # 1. 配置串口参数 self.serial_port.setPortName(selected_port) self.serial_port.setBaudRate(QSerialPort.Baud9600) # 波特率 9600 self.serial_port.setDataBits(QSerialPort.Data8) # 数据位 8 self.serial_port.setParity(QSerialPort.NoParity) # 无校验 self.serial_port.setStopBits(QSerialPort.OneStop) # 停止位 1 self.serial_port.setFlowControl(QSerialPort.NoFlowControl) # 无流控 # 2. 打开串口(读写模式) if self.serial_port.open(QSerialPort.ReadWrite): self._append_recv_text(f"成功:打开串口 {selected_port}") self.open_btn.setEnabled(False) self.close_btn.setEnabled(True) self.port_combo.setEnabled(False) self.refresh_btn.setEnabled(False) else: self._append_recv_text(f"错误:打开串口 {selected_port} 失败(权限不足/端口被占用)") def close_serial_port(self): """关闭串口""" if self.serial_port.isOpen(): self.serial_port.close() self._append_recv_text(f"信息:串口 {self.serial_port.portName()} 已关闭") self.open_btn.setEnabled(True) self.close_btn.setEnabled(False) self.port_combo.setEnabled(True) self.refresh_btn.setEnabled(True) def send_data(self): """向串口发送数据""" if not self.serial_manager.serial_port.isOpen(): self._append_recv_text("错误:串口未打开!") return # 获取输入框数据并转换为字节类型(必须传入 bytes,字符串需 encode) input_text = self.send_input.text().strip() if not input_text: return # 判断是否为十六进制格式(去除空格后检查是否全为十六进制字符) clean_text = input_text.replace(' ', '').replace('\t', '').replace('\n', '') # 如果是十六进制字符串,则转换为字节数组 if all(c in '0123456789ABCDEFabcdef' for c in clean_text) and len(clean_text) % 2 == 0: try: # 将十六进制字符串转换为字节数组 send_bytes = bytes.fromhex(clean_text) except ValueError: self._append_recv_text("错误:十六进制数据格式不正确!") return else: # 按普通字符串处理 send_bytes = input_text.encode("utf-8") # 写入串口(encode 转换为 UTF-8 字节流,外设需对应编码) written_bytes = self.serial_manager.serial_port.write(send_bytes) if written_bytes > 0: # 显示发送的十六进制数据 send_hex = send_bytes.hex().upper() self._append_recv_text(f"发送:{send_hex}(字节数:{written_bytes})") # self.send_input.clear() def read_serial_data(self): """读取串口接收的数据(响应 readyRead 信号)""" if not self.serial_port.isOpen(): return # 读取所有可用数据(QByteArray -> bytes -> str) recv_data = self.serial_port.readAll() # 转换为 Python 字节类型,再解码为字符串(与发送端编码一致) recv_str = bytes(recv_data).decode("utf-8", errors="ignore") if recv_str: self._append_recv_text(f"接收:{recv_str}") def handle_serial_error(self, error): """处理串口错误(响应 errorOccurred 信号)""" # 在 PySide6 中,使用 QSerialPort.SerialPortError 枚举值 if error == QSerialPort.SerialPortError.NoError: error_msg = "信息:无错误" elif error == QSerialPort.SerialPortError.DeviceNotFoundError: error_msg = "错误:串口端口不存在" elif error == QSerialPort.SerialPortError.PermissionError: error_msg = "错误:串口权限不足" elif error == QSerialPort.SerialPortError.OpenError: error_msg = "错误:无法打开串口(可能已被其他程序占用)" elif error == QSerialPort.SerialPortError.NotOpenError: error_msg = "错误:串口未打开" elif error == QSerialPort.SerialPortError.ResourceError: error_msg = "错误:串口资源被占用或断开连接" elif error == QSerialPort.SerialPortError.UnsupportedOperationError: error_msg = "错误:不支持的操作" elif error == QSerialPort.SerialPortError.TimeoutError: error_msg = "错误:操作超时" elif error == QSerialPort.SerialPortError.UnknownError: error_msg = "错误:未知串口错误" else: error_msg = f"错误:未知串口错误(代码:{error})" self._append_recv_text(error_msg) # 错误发生时自动关闭串口 self.close_serial_port() def _append_recv_text(self, text): """向接收区域追加文本并自动滚动""" self.recv_text.append(text) # 自动滚动到最下方 scroll_bar = self.recv_text.verticalScrollBar() scroll_bar.setValue(scroll_bar.maximum()) def closeEvent(self, event): """窗口关闭时自动关闭串口""" if self.serial_port.isOpen(): self.serial_port.close() event.accept() if __name__ == "__main__": app = QApplication(sys.argv) window = SerialPortTool() window.show() sys.exit(app.exec())

五、运行与使用说明

  1. 运行效果:启动程序后,会自动枚举系统中的可用串口,显示在下拉框中。
  2. 操作步骤
    • 选择需要连接的串口(如 "COM3 - USB-SERIAL CH340")
    • 点击「打开串口」(成功后按钮状态切换)
    • 在输入框中输入数据,点击「发送数据」或按回车发送
    • 外设返回数据后,会自动显示在下方接收区域
    • 结束通信后,点击「关闭串口」释放资源

六、关键注意事项

  1. 数据格式QSerialPort.write() 仅接受字节类型(bytes),字符串必须通过 encode() 转换;读取数据时,readAll() 返回 QByteArray,需先转换为 bytesdecode() 为字符串。
  2. 串口参数一致性:波特率、数据位等参数必须与外设完全一致,否则会出现乱码或无法通信。
  3. 异步无阻塞:QtSerialPort 采用异步通信,readyRead() 信号仅在有新数据到达时触发,无需轮询(轮询会导致阻塞,影响界面响应)。
  4. 资源释放:串口使用完毕后必须调用 close() 关闭,否则会导致端口被占用,下次无法打开。
  5. 编码一致性:收发数据的编码格式(如 UTF-8、GBK)必须与外设保持一致,否则会出现乱码。

七、总结

  1. QtSerialPort 库的核心是 QSerialPort(串口操作)和 QSerialPortInfo(串口枚举)。
  2. 串口通信的核心流程是「配置参数→打开串口→绑定信号槽→读写数据→关闭串口」。
  3. 异步通信依赖 readyRead() 信号接收数据,无需轮询,保证界面响应流畅。
  4. 数据格式转换(字符串 ↔ 字节)和串口参数一致性是串口通信成功的关键。

Read more

OpenClaw 安装 + 接入飞书机器人完整教程

OpenClaw 安装 + 接入飞书机器人完整教程 OpenClaw 曾用名:ClawdBot → MoltBot → OpenClaw(同一软件,勿混淆) 适用系统:Windows 10/11 最后更新:2026年3月 一、什么是 OpenClaw? OpenClaw 是一款 2026 年爆火的开源个人 AI 助手,GitHub 星标已超过 10 万颗。 与普通 AI 聊天机器人的核心区别: * 真正的执行能力:不只回答问题,能实际操作你的电脑 * 24/7 全天候待命:睡觉时也能主动完成任务 * 完全开源免费:数据完全掌控在自己手中 * 支持国内平台:飞书、钉钉等均已支持接入 二、安装前准备:安装 Node.js 建议提前手动安装

By Ne0inhk
SQL Server(2022)安装及使用

SQL Server(2022)安装及使用

一、安装SQL Server 1.到微软官网下载SQL Server Developer版本,现在的最新版本是SQL Server 2022 Developer。 SQL Server 下载 | Microsoft 2.下载完成之后,在文件夹中找到刚才下载的文件,双击打开,打开之后的界面如下图所示。 3.我们选择自定义安装,之后再选择要安装的位置,再点击安装,如下图所示 4.点了安装之后会出现如下图所示的界面,我们需要等待它下载安装包,此过程等待的时间可能较长 5.安装包下载并提取完成之后,会出现下图所示的界面 6.依次点击安装、全新SQL Server独立安装或向现有安装添加功能,如下图所示 7.出现如下图所示的界面,不用管密钥,点击下一步 8.再点击我接受许可条款,点击下一步,之后新出现的窗口会让你选择是否检查更新,大家可以根据自己的需要选择,默认是不检查更新,再点击下一步,这时会显示正在检查更新,如下图所示,

By Ne0inhk
SpringAOP详解(二)

SpringAOP详解(二)

一、代理模式的核心概念 1. 定义 为目标对象提供 “代理类”,让调用方不直接访问目标对象,而是通过代理类间接访问,从而在代理类中实现功能增强(比如日志、权限校验)。 2. 核心角色(以 “房屋租赁” 为例) 角色对应示例作用说明SubjectHouseSubject接口定义目标对象和代理类的共同行为(比如 “租房”)RealSubjectRealHouseSubject(房东)目标对象(被代理的实际业务执行者)ProxyHouseProxy(中介)代理类,包装目标对象,在调用目标方法前后添加增强逻辑 静态代理的实现步骤(以房屋租赁为例) 1. 定义共同接口(Subject) 2. 实现目标对象(RealSubject) 3. 实现代理类(Proxy) 4. 使用代理 静态代理的核心特点 * 提前创建:代理类的.class文件在程序运行前就已存在(比如HouseProxy是提前写好的); * 功能增强:不修改目标对象代码,通过代理类实现 “附加逻辑”

By Ne0inhk
【MySQL基础】(1):MySQL的安装

【MySQL基础】(1):MySQL的安装

✅ 适用人群:刚接触 Linux 和数据库的新手 ✅ 目标:快速装好 MySQL,用 root 用户练习 SQL,无需复杂权限配置 ✅ 系统要求:Ubuntu 20.04 / 22.04 / 24.04 LTS(阿里云、腾讯云、AWS EC2 等均可) 🔧 第一步:登录你的云服务器 1. 使用 SSH 工具(如 Xshell、FinalShell、或 macOS/Linux 的终端)连接到你的 Ubuntu 服务器。 2. 先确认你是普通用户(不是 root),但拥有 sudo 权限(大多数云服务器默认如此)

By Ne0inhk