前言
在 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
| from PySide2.QtCore import QObject, Slot
from utils.z_setting import save_config,get_config from utils.z_twain_utils import select_device from 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 QGuiApplication from PySide2.QtQml import QQmlApplicationEngine
app = 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, Slot
class 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, QTimer
engine = 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, Signal
class 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 }
|