Pyside6 + QML - 信号与槽04 - Python 主动发射信号驱动 QML UI
导言
在前面的示例中,交互主要由 QML 调用 Python 函数完成,Python 端处于被动响应。本篇将演示如何由 Python 主动发射信号并驱动 QML UI 更新,实现从“被动调用”到“主动推送”的双向交互。
这张图描述了 Python 端通过信号(Signal)把字符串传递给 QML。QML 接收到信号后,更新界面上的标签文字,从而实现 Python 主动驱动 QML UI。
效果如下所示:
工程代码:
- github: https://github.com/q164129345/myPyside6_QML/tree/main/basic05_signal
- gitee: https://gitee.com/wallace89/myPyside6_QML/tree/main/basic05_signal
一、main.py
# python3.10.11 - PySide6==6.9
import sys
from PySide6.QtCore import QObject, Slot, Signal, QTimer
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngineclass Backend(QObject):# 定义信号messageChanged = Signal(str)def __init__(self):super().__init__() # 初始化父类self.counter = 0# 创建QTimer定时器,每秒触发一次(不要使用python自带的定时器,会卡UI)self.timer = QTimer(self)self.timer.timeout.connect(self.updateMessage)self.timer.start(1000) # 1000毫秒def updateMessage(self):# 每1秒发送一次信号self.counter += 1self.messageChanged.emit(f"Hello from Python! {self.counter}") # 发送信号if __name__ == "__main__":# 创建应用程序和引擎app = QGuiApplication(sys.argv)engine = QQmlApplicationEngine()# qml与python交互backend = Backend() # 实例化python后端对象engine.rootContext().setContextProperty("backend", backend) # 注册到QML环境(名叫 “backend”)# 加载QML文件engine.addImportPath(sys.path[0]) # 当前项目路径engine.loadFromModule("Example", "Main") # 模块(Example) + QML文件名(Main.qml)if not engine.rootObjects():sys.exit(-1)sys.exit(app.exec())
关键点说明
class Backend(QObject):# 定义信号messageChanged = Signal(str)def __init__(self):super().__init__() # 初始化父类self.counter = 0# 创建QTimer定时器,每秒触发一次self.timer = QTimer(self)self.timer.timeout.connect(self.updateMessage)self.timer.start(1000) # 1000毫秒def updateMessage(self):# 每1秒发送一次信号self.counter += 1self.messageChanged.emit(f"Hello from Python! {self.counter}") # 发送信号
messageChanged = Signal(str)
:创建一个信号,名为messageChanged,传递消息的类型是字符串。super().__init__()
:因为显式重写了__init__()
构造函数,记得调用super().__init__()
初始化父类QObject。另外,如果没有重写__init__()
的话,Python会自动帮我们调用super().__init__()
。所以,之前的例子都不需要调用super().__init__()
。self.messageChanged.emit()
:是将字符串“信号“发送给QML。emit()
方法用于触发/发送之前定义的messageChanged
信号,这是Qt信号-槽机制中的"发送端"。
二、Main.qml
// 导入QML基础模块,包含基本的QML元素
import QtQuick 2.15
// 导入Qt控件模块,包含Button等界面控件
import QtQuick.Controls 2.15// 应用程序主窗口
ApplicationWindow {visible: true // 窗口可见width: 400 // 窗口宽度为400像素height: 300 // 窗口高度为300像素title: qsTr("Python主动更新 -> QML显示") // 窗口标题,qsTr用于国际化Label {id: labelanchors.centerIn: parent // 标签居中显示text: "等待Python消息..." // 标签初始文本内容}// 绑定Python信号Connections {target: backend // 连接到名为backend的Python对象function onMessageChanged(msg) {label.text = msg // 更新标签文本为传递的新文本}}
}
关键点说明
// 绑定Python信号
Connections {target: backend // 连接到名为backend的Python对象function onMessageChanged(msg) {label.text = msg // 更新标签文本为传递的新文本}
}
Connections
:是QML中的一个特殊元素,用于建立信号-槽连接,它允许QML对象监听其他对象的信号。target
:指定要监听的对象(比如Backend对象)。function onMessageChanged(msg)
:onMessageChanged
是信号处理器的命名约定(不能随意更改),当Python端的messageChanged
信号发出时,这个函数会自动被调用。
为什么不能用其他名字?
这是Qt/QML框架的约定规则,具体原因:
- 自动连接机制:QML引擎会自动识别以on开头且后面跟着信号名的函数,并将其作为该信号的处理器
- 命名转换规则:
- Python信号:messageChanged
- QML处理函数:onMessageChanged
- 规则:on + 信号名(首字母大写)
- 框架内部实现:Qt的元对象系统依赖这种命名约定来建立信号和槽之间的连接
这就像是一个"合约",Python端发出messageChanged信号,QML端必须用onMessageChanged来接收,这样Qt框架才知道它们是对应的。