news 2026/3/6 9:46:01

PySide6从0开始学习的笔记(二十二) 几种封装信号传递内容的方法

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
PySide6从0开始学习的笔记(二十二) 几种封装信号传递内容的方法

Qt 框架的信号,它支持的类型范围为:

类别支持的类型示例
基础 Python 类型int, float, str, bool, list, dict, tupleSignal(int, str, list)
Qt 专用类型QWidget, QPoint, QColor, QSize 等Signal(QWidget, QPoint)
自定义类型自定义 Python 类(需注册)Signal(MyCustomClass)
无类型 / 任意类型objectSignal(object)

在实际工程中,经常会用到几个不同类型的变量组成的“复合”信号,比如视觉项目中,相机线程会将相机采集到的一幅图像的像素数据和采集的时间戳组合起来发给AI线程,AI线程对图像进行识别后再把标注了目标框的像素数据与识别结果以及时间戳组合起来发给UI线程以及后台保存线程,那么在定义信号的时候,当然可以使用列出信号元素的所有类型参数的方法,比如:

signal = Signal(str, str, float, QPixmap) # 时间戳,识别内容,置信度,显示像素

但是这种方法在使用的过程中并不直观,如上例中的2个str,它分别代表的含义在调用时并不明示。如果将信号传递的内容打包定义成一个类去创建、编辑和传递,在工程使用和移植代码的时候就很方便。

下面就列出几种定义信号传递的内容的常用方法:

一、将传递的内容定义为Qt的QObject基类

  • QObject:这是 Qt 中所有支持信号槽、属性系统、对象树管理的类的基类。
  • 意味着该类可以使用 Qt 的核心特性(如属性通知、父子对象管理等),而非普通 Python 类。
import sys from PySide6.QtCore import QObject, Signal from PySide6.QtGui import QPixmap from PySide6.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout # 定义一个类,用来封装猫咪信息并作为信号发射 class CatSignal(QObject): def __init__(self, name: str, age: int, pixmap: QPixmap, parent=None): super().__init__(parent) self.name = name self.age = age self.pixmap = pixmap # 可选:添加数据验证,避免非法值 if not isinstance(name, str) or len(name) == 0: raise ValueError("猫咪名称必须是非空字符串") if not isinstance(age, int) or age < 0: raise ValueError("猫咪年龄必须是非负整数") if not isinstance(pixmap, QPixmap) or pixmap.isNull(): raise ValueError("图片必须是有效的 QPixmap") class MyWidget(QWidget): image_signal = Signal(CatSignal) def __init__(self): super().__init__() self.setWindowTitle("猫咪信息") layout = QVBoxLayout(self) self.label1 = QLabel(self) self.image_signal.connect(self.on_image_signal) self.label1.setScaledContents(True) self.label2 = QLabel(self) layout.addWidget(self.label1) layout.addWidget(self.label2) def on_image_signal(self, cat): self.label1.setPixmap(cat.pixmap) self.label2.setText(f"猫咪名字:{cat.name} , 猫咪年龄:{cat.age}") if __name__ == '__main__': app = QApplication(sys.argv) form = MyWidget() form.show() cat1 = CatSignal("三毛", 2, QPixmap("cat1.jpg")) # 创建信号 form.image_signal.emit(cat1) # 发射信号 sys.exit(app.exec())

二、将传递的内容定义为QGraphicsObject

如果在视图和场景体系中应用:

import sys from PySide6.QtCore import QObject, Signal, QRectF, Qt from PySide6.QtGui import QPixmap, QPainter from PySide6.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout, QGraphicsObject, QGraphicsView, \ QGraphicsScene, QGraphicsPixmapItem class CatSignal(QGraphicsObject): def __init__(self, name: str, age: int, pixmap: QPixmap, parent=None): super().__init__(parent) self.name = name self.age = age self.pixmap = pixmap class MyWidget(QWidget): image_signal = Signal(CatSignal) def __init__(self): super().__init__() self.setWindowTitle("猫咪信息") self.layout = QVBoxLayout(self) self.view = QGraphicsView(self) self.scene = QGraphicsScene(self) self.view.setScene(self.scene) self.layout.addWidget(self.view) self.label = QLabel(self) self.label.setScaledContents(True) self.layout.addWidget(self.label) self.image_signal.connect(self.on_image_signal) def on_image_signal(self, cat): self.scene.clear() item_graphics = QGraphicsPixmapItem(cat.pixmap) self.scene.addItem(item_graphics) self.label.setText(f"猫咪名字:{cat.name} , 猫咪年龄:{cat.age}") self.layout.update() if __name__ == '__main__': app = QApplication(sys.argv) form = MyWidget() form.show() cat1 = CatSignal("MiMi", 2, QPixmap("cat1.jpg")) form.image_signal.emit(cat1) # 发射信号 sys.exit(app.exec())

三、将传递的内容定义为Python的object基类

  • 适用场景:最简单的场景,仅需封装属性,无额外需求。
  • 优势:轻量、无依赖,支持类型注解和数据验证。
  • 缺点:不具备Qt的Qobject的核心特性(如属性通知、父子对象管理等)。
class CatSignal(object): def __init__(self, name: str, age: int, pixmap: QPixmap): super().__init__() self.name = name self.age = age self.pixmap = pixmap # 可选:添加数据验证,避免非法值 if not isinstance(name, str) or len(name) == 0: raise ValueError("猫咪名称必须是非空字符串") if not isinstance(age, int) or age < 0: raise ValueError("猫咪年龄必须是非负整数") if not isinstance(pixmap, QPixmap) or pixmap.isNull(): raise ValueError("图片必须是有效的 QPixmap")

四、使用dataclasses.dataclass

  • 适用场景:需要自动生成__init____repr____eq__等方法,简化数据类代码。
  • 核心优势:减少重复代码,支持默认值、类型检查、冻结(不可修改)等特性。
from dataclasses import dataclass, field @dataclass(frozen=True) # frozen=True:实例不可修改(只读数据) class CatSignal: name: str # 必选属性 pixmap: QPixmap = field(compare=False) # 比较时忽略 pixmap(避免大对象比较) age: int = field(default=0) # 可选属性,默认值 0 # 可选:字段验证(通过 __post_init__ 实现) def __post_init__(self): if len(self.name) == 0: raise ValueError("名称不能为空") if self.pixmap.isNull(): raise ValueError("无效图片") if self.age < 0: raise ValueError("年龄不能为负")

五、使用typing.NamedTuple

  • 适用场景:需要轻量级、只读的数据容器,支持 tuple 特性(可迭代、哈希)。
  • 优势:比dataclass更轻量,天然支持解构(name, age = cat)。
import sys from dataclasses import dataclass, field from PySide6.QtCore import QObject, Signal from PySide6.QtGui import QPixmap from PySide6.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout # 定义一个类,用来封装猫咪信息并作为信号发射 from typing import NamedTuple class CatSignal(NamedTuple): name: str pixmap: QPixmap age: int = 0 class MyWidget(QWidget): image_signal = Signal(CatSignal) def __init__(self): super().__init__() self.setWindowTitle("猫咪信息") layout = QVBoxLayout(self) self.label1 = QLabel(self) self.image_signal.connect(self.on_image_signal) self.label1.setScaledContents(True) self.label2 = QLabel(self) layout.addWidget(self.label1) layout.addWidget(self.label2) def on_image_signal(self, cat): self.label1.setPixmap(cat.pixmap) self.label2.setText(f"猫咪名字:{cat.name} , 猫咪年龄:{cat.age}") if __name__ == '__main__': app = QApplication(sys.argv) form = MyWidget() form.show() # 使用(实例不可修改) cat1 = CatSignal("咪酱", QPixmap("cat1.jpg"), 3) print(cat1.name) name, pixmap, age = cat1 # 解构 print(name, age) form.image_signal.emit(cat1) # 发射信号 sys.exit(app.exec())

六、数据库储存场景

  • 若需将数据通过信号发射到数据库线程,存入数据库(如 SQLite、MySQL),可继承 ORM 框架的基类:
    • SQLAlchemy:declarative_base()
    • Django ORM:models.Model
  • 示例(SQLAlchemy)
from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, String, Integer, LargeBinary from PyQt5.QtGui import QPixmap import io Base = declarative_base() class CatDB(Base): __tablename__ = "cats" # 数据库表名 id = Column(Integer, primary_key=True, autoincrement=True) name = Column(String(50), nullable=False) # 名称(非空) age = Column(Integer, default=0) # 年龄 pixmap_data = Column(LargeBinary) # 图片二进制数据(QPixmap 无法直接存数据库) # 转换 QPixmap 为二进制(存库) def set_pixmap(self, pixmap: QPixmap): buffer = io.BytesIO() pixmap.save(buffer, "PNG") self.pixmap_data = buffer.getvalue() # 从二进制转换为 QPixmap(读库) def get_pixmap(self) -> QPixmap: buffer = io.BytesIO(self.pixmap_data) return QPixmap.fromImageReader(buffer)

七、对比

需求场景推荐基类核心优势
Qt 图形场景(GraphicsScene)QGraphicsObject、QObject可以使用 Qt 的核心特性,QGraphicsObject支持绘图、碰撞检测、动画
仅封装数据(轻量)object无依赖,最简单
数据类(自动生成方法)dataclasses.dataclass简化代码,支持验证、默认值
只读数据容器typing.NamedTuple轻量,支持解构、哈希
数据库存储SQLAlchemyBase/DjangoModelORM 映射,直接操作数据库

总结

  1. 优先选纯 Python 基类:若信号传递的内容仅为数据载体,dataclass(灵活)或NamedTuple(只读)是最优选择,轻量且无 Qt 耦合;
  2. Qt 视图场景:继承QAbstractItemModel子类,享受 Qt 模型视图的自动同步特性;
  3. 图形 / 信号槽场景:继承QGraphicsObject或只保留QObject,利用 Qt 的图形 / 信号机制;
  4. 数据库 / 序列化场景:使用 ORM 或序列化库的基类,简化数据持久化。

根据实际需求(是否需要 UI 绑定、数据库存储、只读特性等)选择即可,大多数场景下dataclasses.dataclass是兼顾简洁性和功能性的首选。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/5 4:54:41

OpenWRT编译效率革命:云编译 vs 本地编译对比

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 生成一个OpenWRT云编译和本地编译的详细对比报告&#xff0c;包含&#xff1a;1. 相同配置下的编译时间对比&#xff1b;2. 硬件资源占用分析&#xff1b;3. 网络依赖程度评估&…

作者头像 李华
网站建设 2026/3/1 12:21:39

AI一键解密PHP SG11加密文件,解放开发者双手

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 开发一个PHP SG11解密工具&#xff0c;能够自动识别并解密经过SG11加密的PHP文件。要求&#xff1a;1. 支持上传加密的PHP文件 2. 使用AI分析加密模式 3. 输出解密后的可读源代码 …

作者头像 李华
网站建设 2026/3/5 12:22:13

C# await异步调用避免阻塞VibeVoice主线程

C# await异步调用避免阻塞VibeVoice主线程 在开发像 VibeVoice-WEB-UI 这样的语音合成系统时&#xff0c;一个常见的痛点浮出水面&#xff1a;用户点击“生成”后&#xff0c;界面瞬间卡死&#xff0c;鼠标无法拖动窗口&#xff0c;按钮点不动&#xff0c;甚至连进度条都停在原…

作者头像 李华
网站建设 2026/3/5 16:04:49

百度指数显示VibeVoice搜索热度持续上升

VibeVoice搜索热度持续攀升&#xff1a;对话级语音合成的技术突破与落地实践 在播客、有声书和虚拟访谈内容需求激增的今天&#xff0c;用户早已不满足于“机器朗读”式的生硬语音输出。他们期待的是自然流畅、角色分明、情感丰富的真实对话体验——就像两位老友坐在录音棚里侃…

作者头像 李华
网站建设 2026/3/1 4:46:11

IAR使用教程:高效使用快捷键提升效率

手不离键盘&#xff1a;用IAR快捷键打造嵌入式开发“丝滑”体验你有没有这样的经历&#xff1f;调试一个复杂的STM32固件时&#xff0c;刚在变量上点下断点&#xff0c;程序暂停&#xff0c;正想查看寄存器状态——结果鼠标一偏&#xff0c;误点了“继续运行”&#xff0c;目标…

作者头像 李华
网站建设 2026/3/6 6:36:23

DriverStore Explorer完全指南:轻松管理Windows驱动程序存储

DriverStore Explorer完全指南&#xff1a;轻松管理Windows驱动程序存储 【免费下载链接】DriverStoreExplorer Driver Store Explorer [RAPR] 项目地址: https://gitcode.com/gh_mirrors/dr/DriverStoreExplorer Windows驱动程序管理一直是许多用户头疼的问题&#xff…

作者头像 李华