使用 QAbstractListModel 从 python 访问 QML 中的列表元素

Posted

技术标签:

【中文标题】使用 QAbstractListModel 从 python 访问 QML 中的列表元素【英文标题】:Access list element in QML from python using the QAbstractListModel 【发布时间】:2019-11-24 23:03:54 【问题描述】:

我是 Qt 的初学者,我正在通过以下方式创建应用程序:

    使用 QML 进行视图设计, 使用python主要用于Controller和Model部分。

因此,QML 需要与 python 对象进行交互。

我的问题:我通过以下(简化)代码在 python 中创建了一个QAbstractListModel

class MyList(QAbstractListModel):

    _myCol1 = Qt.UserRole + 1
    _myCol2 = Qt.UserRole + 2


    def __init__(self, parent=None):
        super().__init__(parent)
        self.myData= [
            
                'id': '01',
                'name': 'test1',
            ,
            
                'id': '02',
                'name': 'test2',
            
        ]

    def data(self, index, role=Qt.DisplayRole):
        row = index.row()
        if role == MyList._myCol1:
            return self.myData[row]['id']
        if role == MyList._myCol2:
            return self.myData[row]['name']

    def rowCount(self, parent=QModelIndex()):
        return len(self.myData)

    def roleNames(self):
        return 
            MyList._myCol1: b'id',
            MyList._myCol2: b'name'
        

    def get(self, index):
        # How to implement this?

以上代码工作正常,并通过QQmlApplicationEnginerootContext().setContextProperty(...) 将列表从python 公开到QML(我使用how to insert/edit QAbstractListModel in python and qml updates automatically? 的答案和Qt for Python 文档作为方向)。

如果使用 QML ListModel,我可以使用文档 https://doc.qt.io/qt-5/qml-qtqml-models-listmodel.html 中描述的 object get(index) 函数。然而:

    如何从 QML 访问实例化的 MyList 中的特定元素,如果它是原生 QML ListModel,我将使用 get(index) 方法访问该元素? 如何实现get(index)方法?

我仍在寻找并期待一个涉及 python 和 QML 的解决方案。 感谢您的帮助!

【问题讨论】:

【参考方案1】:

只有某些类型的变量可以导出到 QML,其中包括 str、int、float、list,但在字典的情况下,它必须导出为 QVariant。

另一方面,如果你想从 QML 访问一个方法,那么如果你使用 PyQt5 或 PySide2,则必须分别使用 @pyqtSlot 或 @Slot 装饰器,指示在这种情况下为 int 和结果参数的输出类型。

ma​​in.py

from PySide2 import QtCore, QtGui, QtQml


class MyList(QtCore.QAbstractListModel):
    col1 = QtCore.Qt.UserRole + 1
    col2 = QtCore.Qt.UserRole + 2

    def __init__(self, parent=None):
        super().__init__(parent)
        self.myData = ["id": "01", "name": "test1",, "id": "02", "name": "test2",]

    def data(self, index, role=QtCore.Qt.DisplayRole):
        row = index.row()
        if index.isValid() and 0 <= row < self.rowCount():
            if role == MyList.col1:
                return self.myData[row]["id"]
            if role == MyList.col2:
                return self.myData[row]["name"]

    def rowCount(self, parent=QtCore.QModelIndex()):
        return len(self.myData)

    def roleNames(self):
        return MyList.col1: b"id", MyList.col2: b"name"

    @QtCore.Slot(int, result='QVariant')
    def get(self, row):
        if 0 <= row < self.rowCount():
            return self.myData[row]


if __name__ == "__main__":
    import os
    import sys

    app = QtGui.QGuiApplication(sys.argv)

    current_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)))
    qml_file = os.path.join(current_dir, "main.qml")

    model = MyList()

    engine = QtQml.QQmlApplicationEngine()
    engine.rootContext().setContextProperty("listmodel", model)
    engine.load(QtCore.QUrl.fromLocalFile(qml_file))

    sys.exit(app.exec_())

ma​​in.qml

import QtQuick 2.13
import QtQuick.Controls 2.13

ApplicationWindow
    id: root
    visible: true
    width: 640
    height: 480
    ListView
        id: view
        anchors.fill: parent
        model: listmodel
        delegate: Text
            text: model.id + " " + model.name
        
    
    Component.onCompleted: 
        var obj = listmodel.get(0)
        console.log(obj["id"])
        console.log(obj["name"])
    

输出:

qml: 01
qml: test1

加:

只有一些基本类型被直接接受,而 dict 不是这种情况,在这些情况下,您可以使用 QVariant 和 QVariantList(用于列表或元组),但在 PySide2 中没有 QVariant,因此您可以在C++ 通过将它们作为字符串传递:“QVariant”。 docs 中表示的内容:

QVariant

由于 QVariant 被移除,任何期待它的函数 可以接收任何 Python 对象(None 是无效的 QVariant)。相同 返回某些东西时规则是有效的:返回的 QVariant 将是 转换为其原始的 Python 对象类型。

当方法需要 QVariant::Type 时,程序员可以使用 字符串(类型名称)或类型本身。

(强调我的)

【讨论】:

感谢您的快速回答!正如您在使用 PySide2 时指出的那样,我必须使用 Qt.Core.Slot 作为装饰器。但我的问题是 PySide2 中没有 QVariant 类型。在link“PySide 仅支持 PyQt 的“API 2”(PSEP 101)”下,我发现应该使用原生 python 类型。但是,如果我将result=dict 用于get 函数装饰器,我将无法像您那样访问该角色。使用您的代码 get 将在 qml 中返回 QVariant(PySide::PyObjectWrapper, ) 作为 obj 好的。我发现,我必须按如下方式实现get 装饰器的result 关键字(至少对于PySide2):result='QVariant'(在这里找到:link)但我不知道为什么要这样做.我会很感激一些解释。顺便说一句,官方“Qt for Python”文档中的任何地方都有记录吗?直到现在,我发现它们并不是很有用。 @BeCurious 当您意识到答案取决于绑定时:PyQt5 或 PySide2 所以对于下一个问题,您必须清楚地指出以避免混淆。查看我的更新。

以上是关于使用 QAbstractListModel 从 python 访问 QML 中的列表元素的主要内容,如果未能解决你的问题,请参考以下文章

从 QAbstractListModel 继承的列表模型,列表项属性不会从 QML 更新

从 QAbstractListModel 中删除行

什么是最适合 QAbstractListModel 和 QListView 的 Qt 容器

为 QML ListView 实现 QAbstractListModel 子类?

向 QAbstractListModel 子类添加新行时,QML 视图不会更新

基类 'QAbstractListModel' 具有私有复制构造函数