我正在尝试将现有的PySide2 / QtWidgets应用程序转换为PySide2 / QML。我试图通过QML MouseArea
点击来从Python服务调用中获取自定义对象的列表。
我目前有一个主脚本(main.py
),该脚本启动一个包含我的QML(包含在QQuickView
中)的main.qml
。它还为我的模型(在Role
中定义的role.py
注册了一个自定义类型,并向视图的根上下文公开了我的服务类的一个实例(包含在mock_role_service.py
中)。
我的QML正确显示,我可以单击我的MouseArea
,但是似乎没有得到List[Role]
元组,而不是返回QVariant
。更具体地说,QVariant(PySide::PyObjectWrapper, )
。
相关文件:
mock_role_service.py
:
class MockRoleService(QObject):
def __init__(self):
super().__init__()
self.next_id = 5
self.records = {
1: Role(id_=1,
name='Admin'),
2: Role(id_=2,
name='User')
}
@Slot(result=list)
def find_all(self) -> List[Role]:
return list(self.records.values())
main.py
:
...
app = QGuiApplication(sys.argv)
qmlRegisterType(Role, 'Models', 1, 0, 'Role')
view = QQuickView()
url = QUrl('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
:
...
MouseArea {
onClicked: {
console.log(roleService.find_all())
for (role in roleService.find_all()) {
console.log(role.get_id())
}
}
}
...
第一次console.log()
调用的结果是QVariant(PySide::PyObjectWrapper, )
,并且永远不会进入for循环。
我只能在线找到几个类似的问题,到目前为止,它们的常见解决方案(例如在this答案中)是将值设置为类的属性并指定它的类型为QVariantList
。这是因为PySide2显然取消了类似QVariant
的类型,所以我无法正确指定广告位的结果类型。
但是,我不确定此解决方案是否适合这种情况,因为我正在处理服务对象。在服务类上设置属性以保存返回值的感觉很脆弱。没有其他方法可以做到这一点吗?
答案 0 :(得分:1)
由于您没有提供Role类,所以我将假定它是QObject,如果不是,那么您必须修改您的类,以便QML无法识别它,除了仅信号,qproperties和slot是在QML中得到认可。
另一方面,仅当您要在已注册类的QML中创建对象时,才需要qmlRegisterType,在您的情况下,我认为没有必要。
最后,如果您要将列表传递给QML,则必须使用签名'QVariantList'
(如果列表有效,则在PyQt中使用)。
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)
# qmlRegisterType(Role, "Models", 1, 0, "Role")
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_())
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