前言
今天被这个python类注册给qml调用给搞晕了,本来写对了代码,但是死活不生效,废了好长时间才找到原因,就是因为某个原因,导致报如下错误:
QWindowsContext: OleInitialize() failed: "COM error 0xffffffff80010106 RPC_E_CHANGED_MODE (Unknown error 0x080010106)"
不说这个了,说下正题,如何让pyside中的python类对象在qml中调用呢? 就像之前调用C++一样灵活,答案毋庸置疑肯定是可以的,现在网上很多都不知道怎么用,其实方法是和C++如何被Qml调用一样,首先python必须是继续于QObject ,这点是必须的,其次就是调用QQmlApplicationEngine 方法来注册上下文,如下代码:
engine.rootContext().setContextProperty("qml_c_object", xxxxx)
demo示例
这个过程已经说了,其实最重要的还是代码,下面我贴出两种方式的代码,记住,无论哪种方式,qml想要使用python类,只能通过属性,信号,槽,这三种方式,下面我说的就是如何通过属性来识别:
第一种实现方式
第一种方式是直接通过@Property 实现调用
main.py 文件内容如下:
import sys
from PySide2.QtCore import QObject, Signal, Property, QUrl
from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine
class Backend(QObject):
textChanged = Signal(str)
def __init__(self, parent=None):
QObject.__init__(self, parent)
self.m_text = ""
@Property(str, notify=textChanged)
def text(self):
return self.m_text
@text.setter
def setText(self, text):
if self.m_text == text:
return
self.m_text = text
self.textChanged.emit(self.m_text)
if __name__ == '__main__':
app = QGuiApplication(sys.argv)
backend = Backend()
backend.textChanged.connect(lambda text: print(text))
engine = QQmlApplicationEngine()
engine.rootContext().setContextProperty("backend", backend)
engine.load(QUrl.fromLocalFile('main.qml'))
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec_())
main.qml 文件如下:
import QtQuick 2.10
import QtQuick.Controls 2.1
import QtQuick.Window 2.2
ApplicationWindow {
title: qsTr("Test")
width: 640
height: 480
visible: true
Column{
TextField{
id: tf
text: "Hello"
}
Button {
text: qsTr("Click Me")
onClicked: backend.text = tf.text
}
}
}
第二种实现方式
第二种方式,直接调用Property 宏方式显示注明方法名称,和在C++ 使用Q_PROPERTY() 一样,不过python这边需要接收一个返回值,然后qml调用识别的是返回值名称,下面看代码实现,就一目了然了!
main.py 内容如下:
from typing import List
from PySide2.QtCore import Property, QObject, QUrl, Signal, Slot
from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import qmlRegisterType
from PySide2.QtQuick import QQuickView
class Role(QObject):
idChanged = Signal()
nameChanged = Signal()
def __init__(self, id_, name, parent=None):
super().__init__(parent)
self._id = id_
self._name = name
def get_id(self) -> int:
return self._id
def set_id(self, id_) -> None:
if self._id != id_:
self._id = id_
self.idChanged.emit()
def get_name(self) -> str:
return self._name
def set_name(self, name) -> None:
if self._name != name:
self._name = name
self.nameChanged.emit()
id_ = Property(int, fget=get_id, fset=set_id, notify=idChanged)
name = Property(str, fget=get_name, fset=set_name, notify=nameChanged)
class MockRoleService(QObject):
def __init__(self, parent=None):
super().__init__(parent)
self.records = {
1: Role(id_=1, name="Admin", parent=self),
2: Role(id_=2, name="User", parent=self),
}
@Slot(result="QVariantList")
def find_all(self) -> List[Role]:
return list(self.records.values())
if __name__ == "__main__":
import os
import sys
app = QGuiApplication(sys.argv)
view = QQuickView()
current_dir = os.path.dirname(os.path.realpath(__file__))
url = QUrl.fromLocalFile(os.path.join(current_dir, "Views/main.qml"))
view.setSource(url)
view.setResizeMode(QQuickView.SizeRootObjectToView)
role_service = MockRoleService()
view.rootContext().setContextProperty("roleService", role_service)
if view.status() == QQuickView.Error:
sys.exit(-1)
view.show()
sys.exit(app.exec_())
main.qml 文件如下:
import QtQuick 2.12
Item{
width: 640
height: 480
MouseArea {
anchors.fill: parent
onClicked: {
var roles = roleService.find_all()
console.log(roles)
for (var i in roles) {
var role = roles[i]
console.log(role.id_, role.name);
}
}
}
}
输出如下:
qml: [Role(0x55b5385589b0),Role(0x55b538558d40)]
qml: 1 Admin
qml: 2 User
总结
可以看到无论第一种还是第二种方式都是可以注册,第二种采用了QQuickView 方式加载,也是能识别注册的,基本python注册qml上下文使用,就是这两个demo诠释了!看不懂的可以留言评论,看到必回~
|