前言 在 PySide2 中,Python和QML之间的交互主要使用信号与槽机制。
QML调用Python 以下是一个简单的步骤说明:
方法的类 创建一个 QObject 子类
首先,你需要创建一个继承自 QObject 的 Python 类,并在类中定义你希望在 QML 中调用的方法。
使用 @Slot 装饰器来标记这些方法,以便它们可以在 QML 中被调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from PySide2.QtCore import QObject, Slotfrom utils.z_setting import save_config,get_configfrom utils.z_twain_utils import select_devicefrom typing import Dict , List , Literal , Optional , Tuple class QmlCommon (QObject ): @Slot(str , str , result=None ) def saveConfig (self, key, value ): save_config(key,value) @Slot(str , result=str ) def getConfig (self, key )->str : return get_config(key) @Slot(result=list ) def getScannerList (self )-> list : scanner_list = select_device() return scanner_list
注意下面的写法是不行的
1 2 3 4 5 @Slot(result=List [str ] ) def getScannerList (self )-> List [str ]: scanner_list = select_device() print (scanner_list) return scanner_list
这是因为在 PyQt 或 PySide 中,槽函数的返回类型声明需要使用 Python 的内置类型(如 list),而不是类型注解(如 List[str])。List[str] 是 typing 模块中的类型,而 PyQt 或 PySide 的槽机制不直接支持 typing 模块的类型声明。
改成这样也是可以的
1 2 3 4 5 @Slot(result=list ) def getScannerList (self )-> List [str ]: scanner_list = select_device() print (scanner_list) return scanner_list
暴露实例 将 Python 对象暴露给 QML
接下来,你需要将这个 Python 对象暴露给 QML。
你可以通过 QQuickView 或 QQmlApplicationEngine 来加载 QML 文件,并将 Python 对象设置为 QML 上下文中的属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from PySide2.QtGui import QGuiApplicationfrom PySide2.QtQml import QQmlApplicationEngineapp = QGuiApplication([]) engine = QQmlApplicationEngine() qml_object = QmlCommon() engine.rootContext().setContextProperty("qmlObject" , qml_object) engine.load("main.qml" ) if not engine.rootObjects(): exit(-1 ) exit(app.exec_())
注意
一定要在加载页面前设置属性,否则在Component.onCompleted生命周期中获取不到。
例如
1 2 3 4 5 6 7 Component.onCompleted: { let saveFolderPath = qmlObject.getConfig("saveFolderPath" ) if (saveFolderPath) { saveFolder.text = saveFolderPath; } }
QML中调用 在 QML 中调用 Python 方法
在 QML 文件中,你可以通过 qmlObject 来访问 myMethod 方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import QtQuick 2.12 import QtQuick.Controls 2.12 ApplicationWindow { visible: true width: 640 height: 480 Button { text: "Call Python Method" onClicked: { qmlObject.saveConfig("name" , "zhangsan" ) let result = qmlObject.getConfig("name" ) console.info("result:" +result) } } }
总结
创建一个继承自 QObject 的 Python 类,并使用 @Slot 装饰器标记你希望在 QML 中调用的方法。
将这个 Python 对象通过 setContextProperty 方法暴露给 QML。
在 QML 中通过对象名调用 Python 方法。
这样,你就可以在 QML 中调用 Python 方法了。
Python调用QML方法 准确来说这个不是Python直接调用QML的方法,而是通过QML监听Python的信号来使用同样的目的。
数据的类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from PySide2.QtCore import Property, QObject, Signal, Slotclass QmlCommon (QObject ): scanNumChanged = Signal(int ) def __init__ (self ): super ().__init__() self ._scanNum = 0 @Property(int ) def scanNum (self ): return self ._scanNum @Slot(str ) def updateScanNum (self, newData ): if self ._scanNum != newData: self ._scanNum = newData self .scanNumChanged.emit(newData)
也就是定义信号和发送信号
1 2 3 4 scanNumChanged = Signal(int ) self .scanNumChanged.emit(123 )
其中
@Property(int)是为了在QML中能取值
@Slot(str)是为了在QML中能调用方法
多个参数的信号
1 showToast = Signal(str , int )
注意:
信号在QML中用 on+信号名(首字母大写)进行监听。
暴露实例 1 2 3 4 5 6 7 qml_object = QmlCommon() engine.rootContext().setContextProperty("qmlObject" , qml_object) qml_object.updateScanNum(10 )
QML中监听 1 2 3 4 5 6 7 8 Connections { function onScanNumChanged(num) { console.log("onScanNumChanged" ); scanNumComp.text = num.toString(); } target: qmlObject }
监听到但是无法更新 如果我们监听到事件了,在事件中更新UI,但是不生效,是因为接受信号时,UI还没渲染完毕。
我们可以这样处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from PySide2.QtCore import QUrl, QTimerengine = QQmlApplicationEngine() engine.rootContext().setContextProperty("qmlObject" , qml_object) def showUpadteDialog (msg ): qml_object.showUpdateDialogAction(msg) def on_object_created (obj, url ): if obj is None : print (f"QML 文件加载失败: {url} " ) else : print (f"QML 文件加载完成: {url} " ) QTimer.singleShot(200 , lambda : showUpadteDialog("要更新啦" )) engine.objectCreated.connect(on_object_created)
注意
QML的创建完毕事件中,UI也没有渲染完毕,这里使用的延迟发送信号。
当然我们再调用接口后更新的时候UI基本都渲染完毕了。
QML绑定Python中的属性 使用Q_PROPERTY
定义数据类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from PySide2.QtCore import Property, QObject, Signalclass DataObject (QObject ): def __init__ (self ): super ().__init__() self ._customData = 0 customDataChanged = Signal(int ) @Property(int , notify=customDataChanged ) def customData (self ): return self ._customData @customData.setter def customData (self, value ): self ._customData = value self .customDataChanged.emit(value)
这里属性和信号绑定,一旦收到变化信号,绑定这个属性的就会自动更新。
跟上面的区别在于,不用监听事件自己处理了。
暴露实例 1 2 3 4 5 6 7 dataObj = DataObject() engine.rootContext().setContextProperty("dataObj" , dataObj) dataObj.customData = 123
QML中使用 1 2 3 4 5 6 7 8 9 10 11 12 Button { text: "Change Data" anchors.centerIn: parent onClicked: { dataObj.customData = 42 } } Text { text: "Data value: " + dataObj.customData anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter }