Объявление QAbstractListModel как свойства в Pyside2 - PullRequest
0 голосов
/ 14 февраля 2019

Я использую Pyside2 с QML и стараюсь хорошо организовать мой код.Я хочу выставить подкласс MyModel из QAbstractListModel из Python в QML для использования в ListView.Код работает отлично, если я объявляю экземпляр MyModel непосредственно внутри движка:

...
engine = QQmlApplicationEngine()
myModel = MyModel(some_dict)
engine.rootContext().setContextProperty("myModel ", myModel)
...

, который я могу затем использовать так:

ListView {
    model: myModel
    delegate: Row {
        Text { text: name }
        Text { text: type }
    }
}

Однако, когда я пытаюсь определить этоэлемент как Property класса, чтобы держать вещи в чистоте и не регистрировать модели повсеместно, я не могу заставить его работать.Я не могу восстановить хорошую отладочную информацию из qml, что также не помогает.

Я попытался объявить следующее

class ModelProvider(QObject):
    modelChanged = Signal()
    _entries: List[Dict[str, Any]]

    def __init__(self, entries, parent=None):
        QObject.__init__(self, parent)
        self._entries = entries

    def _model(self):
        return MyModel(self._entries)

    myModel = Property(list, _model, notify=modelChanged)
    myQVariantModel = Property('QVariantList', _model, notify=modelChanged)

...
modelProvider = ModelProvider(some_dict)
engine.rootContext().setContextProperty("modelProvider", modelProvider )

, а затем использовать его в qml

ListView {
    model: modelProvider.myModel
    // or model: modelProvider.myQVariantModel 
    delegate: Row {
        Text { text: name }
        Text { text: type }
    }
}

Результатом является пустой экран.

Я обнаружил там , что одной из возможных причин может быть то, что QAbstractListModel - это QObject, что делает его не подлежащим копированию, ив С ++ вместо этого они предлагают передать указатель на него.Но я подумал, что это будет автоматически в Python.

Что я делаю не так в этом случае?И, если возможно, как я могу узнать, почему ListView ничего не рендерит (может быть, отладочный вывод)?Разве вообще невозможно организовать мой код таким образом?


Для контекста я пытаюсь следовать шаблону Bloc , который мне очень нравится использовать с dart и flutter, в котором имеется один (или более) центральный класс Bloc, который предоставляет модель и методы для работы с этой моделью для представления.

1 Ответ

0 голосов
/ 15 февраля 2019

Вы должны указать, что Свойство является QObject, а не QVariantList или списком.С другой стороны, я не думаю, что вы меняете модель, поэтому вы должны использовать постоянное свойство и без сигналов.Кроме того, вы не верите в функцию Модель, поскольку каждый раз, когда вы вызываете _модель, создается новый объект.

main.py

import os
import sys
from functools import partial
from PySide2 import QtCore, QtGui, QtQml

class MyModel(QtCore.QAbstractListModel):
    NameRole = QtCore.Qt.UserRole + 1000
    TypeRole = QtCore.Qt.UserRole + 1001

    def __init__(self, entries, parent=None):
        super(MyModel, self).__init__(parent)
        self._entries = entries

    def rowCount(self, parent=QtCore.QModelIndex()):
        if parent.isValid(): return 0
        return len(self._entries)

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if 0 <= index.row() < self.rowCount() and index.isValid():
            item = self._entries[index.row()]
            if role == MyModel.NameRole:
                return item["name"]
            elif role == MyModel.TypeRole:
                return item["type"]

    def roleNames(self):
        roles = dict()
        roles[MyModel.NameRole] = b"name"
        roles[MyModel.TypeRole] = b"type"
        return roles

    def appendRow(self, n, t):
        self.beginInsertRows(QtCore.QModelIndex(), self.rowCount(), self.rowCount())
        self._entries.append(dict(name=n, type=t))
        self.endInsertRows()

class ModelProvider(QtCore.QObject):
    def __init__(self, entries, parent=None):
        super(ModelProvider, self).__init__(parent)
        self._model = MyModel(entries)

    @QtCore.Property(QtCore.QObject, constant=True)
    def model(self):
        return self._model

def test(model):
    n = "name{}".format(model.rowCount())
    t = "type{}".format(model.rowCount())
    model.appendRow(n, t)

def main():
    app = QtGui.QGuiApplication(sys.argv)
    entries = [
        {"name": "name0", "type": "type0"},
        {"name": "name1", "type": "type1"},
        {"name": "name2", "type": "type2"},
        {"name": "name3", "type": "type3"},
        {"name": "name4", "type": "type4"},
    ]
    provider = ModelProvider(entries)
    engine = QtQml.QQmlApplicationEngine()
    engine.rootContext().setContextProperty("provider", provider)
    directory = os.path.dirname(os.path.abspath(__file__))
    engine.load(QtCore.QUrl.fromLocalFile(os.path.join(directory, 'main.qml')))
    if not engine.rootObjects():
        return -1
    timer = QtCore.QTimer(interval=500)
    timer.timeout.connect(partial(test, provider.model))
    timer.start()
    return app.exec_()

if __name__ == '__main__':
    sys.exit(main())

main.qml

import QtQuick 2.11
import QtQuick.Window 2.2
import QtQuick.Controls 2.2

ApplicationWindow {    
    visible: true
    width: 640
    height: 480
    ListView {
        model: provider.model
        anchors.fill: parent
        delegate: Row {
            Text { text: name }
            Text { text: type }
        }
    }
}
...