Достъп до елемент от списък в QML от python с помощта на QAbstractListModel

Аз съм начинаещ в Qt и създавам приложение от:

  1. Използвайки QML за дизайн на View,
  2. Използване на python главно за частта за контролер и модел.

Следователно QML трябва да взаимодейства с обектите на Python.

Моят проблем: Създадох QAbstractListModel в python чрез следния (опростен) код:

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?

Кодът по-горе работи добре и излагането на списъка от python на QML чрез QQmlApplicationEngine и rootContext().setContextProperty(...) работи (използвах отговора от как да вмъквам/редактирам QAbstractListModel в актуализации на python и qml автоматично? и документите на Qt за Python като ориентация).

Ако използвам QML ListModel, мога да използвам функцията object get(index), както е описано в документите https://doc.qt.io/qt-5/qml-qtqml-models-listmodel.html. Въпреки това:

  1. Как мога да осъществя достъп до конкретен елемент в инстанциирания MyList от QML, тъй като бих направил този елемент с метода get(index), ако щеше да бъде естествен QML ListModel?
  2. Как да внедрим метода get(index)?

Все още търся и очаквам решение, отнасящо се до python и QML. Благодаря за вашата помощ!


person BeCurious    schedule 24.11.2019    source източник


Отговори (1)


Само някои типове променливи могат да се експортират в QML, сред тях са str, int, float, list, но в случай на речник трябва да се експортира като QVariant.

От друга страна, ако искате да получите достъп до метод от QML, тогава трябва да използвате декоратора @pyqtSlot или @Slot, ако използвате съответно PyQt5 или PySide2, като посочите типа входни данни, които в този случай са 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". Какво е посочено в документи:

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.

Когато метод очаква QVariant::Type, програмистът може да използва низ (име на типа) или самия тип.

(подчертаването е мое)

person eyllanesc    schedule 25.11.2019
comment
Благодаря ти за бързия отговор! Както посочихте, когато използвам PySide2, трябва да използвам Qt.Core.Slot като декоратор. Но проблемът ми е, че в PySide2 няма тип QVariant. В връзка под PySide поддържа само API 2 на PyQt (PSEP 101) Открих, че трябва да се използват собствени типове на Python . Но ако използвам result=dict за декоратора на функцията get, нямам достъп до ролята, както направихте вие. С вашия код get ще върне QVariant(PySide::PyObjectWrapper, ) като obj в qml. - person BeCurious; 25.11.2019
comment
Добре. Разбрах, че трябва да внедря ключовата дума result на декоратора get както следва (поне за PySide2): result='QVariant' (намерих го тук: връзка) Но не съм сигурен защо да го правя. Ще се радвам на малко обяснение. И между другото, това документирано ли е някъде в официалните документи на Qt за Python? Досега не ги намирах за много полезни. - person BeCurious; 25.11.2019
comment
@BeCurious Както разбирате, отговорът зависи от обвързването: PyQt5 или PySide2, така че за следващия въпрос трябва да посочите ясно, за да избегнете объркване. Вижте моята актуализация. - person eyllanesc; 25.11.2019