是否可以通过 QML 从 PySide2 插槽(服务调用)获取对象列表? [英] Is it possible to get a list of objects from a PySide2 slot (service call) via QML?

查看:90
本文介绍了是否可以通过 QML 从 PySide2 插槽(服务调用)获取对象列表?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试将现有的 PySide2/QtWidgets 应用程序转换为 PySide2/QML.我正在尝试通过 QML MouseArea 单击从 Python 服务调用中获取自定义对象列表.

I am trying to convert an existing PySide2/QtWidgets application to PySide2/QML. I am trying to get a list of custom objects from a Python service call via a QML MouseArea click.

我目前有一个主脚本 (main.py),它启动包含我的 QML(包含在 main.qml 中)的 QQuickView.它还为我的模型注册一个自定义类型(Role,在 role.py 中定义)并公开我的服务类的实例(包含在 mock_role_service.py) 到视图的根上下文.

I currently have a main script (main.py) that launches a QQuickView containing my QML (contained in main.qml). It also registers a custom type for my model (Role, defined in role.py) and exposes an instance of my service class (contained in mock_role_service.py) to the view's root context.

我的 QML 显示正确,我可以点击我的 MouseArea,但是我没有返回 List[Role],我似乎得到了一个 QVariant代码>元组.更具体地说,QVariant(PySide::PyObjectWrapper, ).

My QML displays correctly and I can click my MouseArea, but instead of getting back List[Role], I seem to be getting a QVariant tuple. More specifically, 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 循环.

The result of the first console.log() call is QVariant(PySide::PyObjectWrapper, ) and the for loop is never entered.

我只能在网上找到几个类似的问题,以及到目前为止的通用解决方案(例如在 this 答案)是将值设置为类的属性并将其指定为 QVariantList 类型.这是因为 PySide2 显然取消了它们的 QVariant 类类型,所以我无法正确指定插槽的结果类型.

I've only been able to find a couple of similar problems online, and the common solution to them so far (like in this answer) is to set the value to a property of the class and specify it to be of type QVariantList. This is because PySide2 apparently did away with their QVariant-like types, so I can't specify the slot's result type correctly.

但是,我不确定这个解决方案是否适合这种情况,因为我正在处理一个服务对象.在服务类上设置属性以保存返回值感觉很脆弱.没有其他方法可以做到这一点吗?

However, I'm not sure this solution is appropriate for this situation because I am dealing with a service object. Setting a property on a service class to hold a return value feels brittle. Is there no other way of accomplishing this?

推荐答案

既然你没有提供 Role 类,我就假设它是一个 QObject,如果不是,那么你必须修改你的类,使其成为QML 不识别,QML 只识别信号、qproperties 和槽位.

Since you have not provided the Role class, I will assume that it is a QObject, if it is not, then you must modify your class so that it is not recognized by QML, besides only the signals, qproperties and slot are recognized in QML.

另一方面,qmlRegisterType 仅当您想在注册类的 QML 中创建对象时才需要,在您的情况下,我认为没有必要.

On the other hand qmlRegisterType is only necessary if you want to create objects in QML of the registered class, in your case I do not see it necessary.

最后,如果要将列表传递给 QML,则必须使用签名 'QVariantList'(在 PyQt 中,如果列表有效).

Finally if you want to pass a list to QML you must use the signature 'QVariantList'(in PyQt if list is valid).

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

这篇关于是否可以通过 QML 从 PySide2 插槽(服务调用)获取对象列表?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆