导言


在这里插入图片描述
这张图描述了 QML 前端 和 Python 后端 的交互关系,用于说明 信号 (signal) 和 槽 (slot) 的连接方式。实际上是展示了一个 QML → Python 单向调用流程。

两个 QML 按钮组件,每个按钮都有一个 clicked 信号。

  • 按钮1 触发clicked信号,发送字符串"button1 clicked"给Python的backed.print_button1()
  • 按钮2 触发clicked信号,发送字符串"button2 clicked"给Python的backed.print_button2()

效果如下所示:
在这里插入图片描述
工程代码:

  • github: https://github.com/q164129345/myPyside6_QML/tree/main/basic03_button_slot_with_para
  • gitee: https://gitee.com/wallace89/myPyside6_QML/tree/main/basic03_button_slot_with_para

一、main.py


import sys
from PySide6.QtCore import QObject, Slot
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine

class Backend(QObject):
    @Slot(str)
    def print_button1(self, name):
        print(f"{name}")
        
    @Slot(str)
    def print_button2(self, name):
        print(f"{name}")

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):
    @Slot(str)
    def print_button1(self, name):
        print(f"{name}")
        
    @Slot(str)
    def print_button2(self, name):
        print(f"{name}")
  • 继承 QObject:让这个类能够作为 Qt 的对象被 QML 识别和调用。
  • @Slot(str)
    • 告诉 Qt 这是一个槽函数,QML 可以直接调用它。
    • (str) 指定参数类型为字符串。这样 QML 在调用时必须传入字符串。

二、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("Hello PySide6 + QML")  // 窗口标题,qsTr用于国际化

    // Row布局:水平排列子元素
    Row {
        anchors.centerIn: parent  // 将Row布局锚定到父元素(窗口)的中心
        spacing: 20              // 子元素之间的间距为20像素
        
        // 第一个按钮
        Button {
            id: btn1             // 给按钮设置唯一标识符,便于引用
            text: "button1"      // 按钮显示的文本
            onClicked: {         // 点击事件处理器
                // 调用Python后端的方法,传递参数
                backend.print_button1("button1 clicked")
                // 修改按钮文本为"clicked"
                btn1.text = "clicked"
            }
        }

        // 第二个按钮
        Button {
            id: btn2             // 给按钮设置唯一标识符
            text: "button2"      // 按钮显示的文本
            onClicked: {         // 点击事件处理器
                // 调用Python后端的方法,传递参数
                backend.print_button2("button2 clicked")
                // 修改按钮文本为"clicked"
                btn2.text = "clicked"
            }
        }
    }
}

关键点说明

Row {
	anchors.centerIn: parent  // 将Row布局锚定到父元素(窗口)的中心
	spacing: 20              // 子元素之间的间距为20像素
	
	// 第一个按钮
	Button {
		id: btn1             // 给按钮设置唯一标识符,便于引用
		text: "button1"      // 按钮显示的文本
		onClicked: {         // 点击事件处理器
			// 调用Python后端的方法,传递参数
			backend.print_button1("button1 clicked")
			// 修改按钮文本为"clicked"
			btn1.text = "clicked"
		}
	}

	// 第二个按钮
	Button {
		id: btn2             // 给按钮设置唯一标识符
		text: "button2"      // 按钮显示的文本
		onClicked: {         // 点击事件处理器
			// 调用Python后端的方法,传递参数
			backend.print_button2("button2 clicked")
			// 修改按钮文本为"clicked"
			btn2.text = "clicked"
		}
	}
}
  • Row:水平布局容器,两个按钮并排。
  • Button 1:显示文字 “button1”。点击时调用 backend.print_button1(“button1 clicked”) → Python 输出。
  • Button 2:同理,调用 backend.print_button2(“button2 clicked”)。

三、细节补充


3.1、@Slot()可以传递哪些类型的变量?

Python 类型声明 对应 QML 类型 示例
int int @Slot(int)
float real @Slot(float)
bool bool @Slot(bool)
str string @Slot(str)
list var (数组) @Slot(list)
dict var (对象) @Slot(dict)
QObject Item/对象引用 @Slot(QObject)
示例代码
  1. 接收整数
@Slot(int)
def set_number(self, n):
    print(f"number={n}")

QML 调用:

onClicked: backend.set_number(42)
  1. 接收浮点数
@Slot(float)
def set_ratio(self, r):
    print(f"ratio={r}")

QML 调用:

onClicked: backend.set_ratio(3.14)
  1. 接收布尔值
@Slot(bool)
def toggle(self, state):
    print("ON" if state else "OFF")

QML 调用:

onClicked: backend.toggle(true)
  1. 接收数组 / 字典
@Slot(list)
def print_list(self, arr):
    print(arr)

@Slot(dict)
def print_dict(self, obj):
    print(obj)

QML 调用:

onClicked: backend.print_list([1,2,3])
onClicked: backend.print_dict({"name":"Alice", "age":18})
  1. 指定返回值
@Slot(int, int, result=int)
def add(self, a, b):
    return a + b

QML 调用:

onClicked: console.log( backend.add(5, 7) )

总结

  • 参数:支持 int, float, bool, str, list, dict, QObject 等。
  • 返回值:用 result=类型 声明。
  • QML 中:int/real/bool/string/var 对应 Python 的基本类型。

3.2、@Slot()装饰器的必要性

场景分析

class Backend(QObject):
    # 情况1: 没有 @Slot()
    # Qt 内部:找到 Python 对象 -> 查找方法 -> 类型转换 -> 调用
    # 每次调用都要做类型推断
    def print_button1(self, name):
        print(f"{name}")
        
    # 情况2: 有 @Slot() 
    # Qt 内部:已知参数类型 -> 直接调用
    # 性能提升 20-50%
    @Slot(str)
    def print_button1(self, name):
	    print(f"{name}")
  • 情况1与情况2都能正常工作。但是,情况2的性能提升20~50%。

官方建议
来自Qt for Python文档:
Best Practice: Always use @Slot() decorator for methods that will be:

  • Called from QML
  • Connected to signals
  • Exposed to Qt’s meta-object system
    While it may work without @Slot(), using it ensures:
  • Better performance
  • Type safety
  • Proper integration with Qt’s meta-object system

总的来说,宁可多加 @Slot(),也不要少加!这是零成本的性能和安全提升。

更多推荐