代码之家  ›  专栏  ›  技术社区  ›  Clifton Roberts

是否可以通过QML从Pyside2插槽(服务调用)中获取对象列表?

  •  0
  • Clifton Roberts  · 技术社区  · 6 年前

    我正在尝试将现有的pyside2/qtwidgets应用程序转换为pyside2/qml。我试图通过qml从python服务调用中获取自定义对象的列表 MouseArea 点击。

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

    我的qml显示正确,我可以单击 穆塞雷亚 但不是回去 List[Role] ,我好像得到了 QVariant 元组。更具体地说, QVariant(PySide::PyObjectWrapper, )

    相关文件:
    模拟角色服务.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())
    

    主.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_())
    

    主.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显然 变量 -与类型类似,因此无法正确指定插槽的结果类型。

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

    0 回复  |  直到 6 年前
        1
  •  0
  •   eyllanesc Yonghwan Shin    6 年前

    由于您还没有提供role类,所以我假设它是一个qobject,如果不是,那么您必须修改您的类,以便qml无法识别它,此外qml中只识别信号、qproperty和slot。

    另一方面,qmlregistertype只有在您想要在注册类的qml中创建对象时才是必需的,在您的情况下,我认为没有必要。

    最后,如果要将列表传递给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