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

Access list element in QML from python using the QAbstractListModel

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

  1. 使用 QML 进行视图设计,
  2. 使用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(我使用了 和 Qt 的答案用于 Python 文档作为方向)。

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

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

我仍在搜索并期待参考 python 和 QML 的解决方案。 感谢您的帮助!

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

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

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

main.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

As QVariant was removed, any function expecting it can receive any Python object (None is an invalid QVariant). The same rule is valid when returning something: the returned QVariant will be converted to the its original Python object type.

When a method expects a QVariant::Type the programmer can use a string (the type name) or the type itself.

(强调我的)