导言


在前面的示例中,交互主要由 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 QQmlApplicationEngine

class 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 += 1
        self.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 += 1
        self.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: label
        anchors.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框架的约定规则,具体原因:

  1. 自动连接机制:QML引擎会自动识别以on开头且后面跟着信号名的函数,并将其作为该信号的处理器
  2. 命名转换规则:
    • Python信号:messageChanged
    • QML处理函数:onMessageChanged
    • 规则:on + 信号名(首字母大写)
  3. 框架内部实现:Qt的元对象系统依赖这种命名约定来建立信号和槽之间的连接
    这就像是一个"合约",Python端发出messageChanged信号,QML端必须用onMessageChanged来接收,这样Qt框架才知道它们是对应的。

更多推荐