是否可以通过 QML 从 PySide2 插槽(服务调用)获取对象列表?
Is it possible to get a list of objects from a PySide2 slot (service call) via QML?
我正在尝试将现有的 PySide2/QtWidgets 应用程序转换为 PySide2/QML。我正在尝试通过 QML MouseArea
单击从 Python 服务调用中获取自定义对象列表。
我目前有一个主脚本 (main.py
),它启动一个包含我的 QML(包含在 main.qml
中)的 QQuickView
。它还为我的模型(Role
,在 role.py
中定义)注册了一个自定义类型,并将我的服务实例 class(包含在 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 循环。
我只能在网上找到几个类似的问题,目前常见的解决方案(如 答案)是将值设置为 属性 class 的类型并将其指定为 QVariantList
类型。这是因为 PySide2 显然取消了它们的 QVariant
-like 类型,所以我无法正确指定插槽的结果类型。
但是,我不确定这个解决方案是否适合这种情况,因为我正在处理一个服务对象。在服务 class 上设置 属性 以保持 return 值感觉很脆弱。没有其他方法可以做到这一点吗?
既然你没有提供Roleclass,我会假设它是一个QObject,如果不是,那么你必须修改你的class,使它不被QML识别, 除了信号,qproperties 和 slot 在 QML 中被识别。
另一方面,仅当您想在注册的 QML 中创建对象时才需要 qmlRegisterType class,在您的情况下,我认为没有必要。
最后,如果您想将列表传递给 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
我正在尝试将现有的 PySide2/QtWidgets 应用程序转换为 PySide2/QML。我正在尝试通过 QML MouseArea
单击从 Python 服务调用中获取自定义对象列表。
我目前有一个主脚本 (main.py
),它启动一个包含我的 QML(包含在 main.qml
中)的 QQuickView
。它还为我的模型(Role
,在 role.py
中定义)注册了一个自定义类型,并将我的服务实例 class(包含在 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 循环。
我只能在网上找到几个类似的问题,目前常见的解决方案(如 QVariantList
类型。这是因为 PySide2 显然取消了它们的 QVariant
-like 类型,所以我无法正确指定插槽的结果类型。
但是,我不确定这个解决方案是否适合这种情况,因为我正在处理一个服务对象。在服务 class 上设置 属性 以保持 return 值感觉很脆弱。没有其他方法可以做到这一点吗?
既然你没有提供Roleclass,我会假设它是一个QObject,如果不是,那么你必须修改你的class,使它不被QML识别, 除了信号,qproperties 和 slot 在 QML 中被识别。
另一方面,仅当您想在注册的 QML 中创建对象时才需要 qmlRegisterType class,在您的情况下,我认为没有必要。
最后,如果您想将列表传递给 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