Pergunta

How should I create a QAbstractListModel in PyQt and use it with a QML ListView?

Foi útil?

Solução

You need to set it's role names to be able to use it in QML;

http://doc.qt.io/qt-4.8/qabstractitemmodel.html#setRoleNames

Outras dicas

Haven't used PyQT but you can find a minimal working sample here: http://doc.qt.nokia.com/stable/qdeclarativemodels.html

If you inspect the sample including the class Animal {...} you'll see that you have to define roles for the different fields you want to supply. And at a minimum you must define the data() function returning the corresponding field value for a given index. Also you'll need your own custom methods for inserting and removing possibly. Hope this helps...

If someone else is looking for an answer, I made a small app that connects to a database of actors with with a subclass of QAbstractListModel and displays the thumbnails in QGridView based on the example @fgungor posted.(PyQt5)

main.py:

import sys, models
from PyQt5.QtCore import QUrl
from PyQt5.QtWidgets import QApplication
from PyQt5.QtQuick import QQuickView

if __name__ == '__main__':

    # Prints QML errors
    def handleStatusChange(status):
        if status == QQuickView.Error:
            errors = appLabel.errors()
            if errors:
                print (errors[0].description())



    myApp = QApplication(sys.argv)
    appLabel = QQuickView()
    appLabel.statusChanged.connect(handleStatusChange)
    model = models.ActorModel(DB_PATH)
    ctxt = appLabel.rootContext()
    ctxt.setContextProperty('myModel', model)
    appLabel.setSource(QUrl('./qml/main/main.qml'))

    try:
        sys.exit(myApp.exec_())
    except:
        print("Exiting")

models.py:

import db
from PyQt5.QtCore import QAbstractListModel, Qt, pyqtSlot


class ActorModel(QAbstractListModel):
    NameRole = Qt.UserRole + 1
    ThumbRole = Qt.UserRole + 2

    _roles = {NameRole: b"name", ThumbRole: b"thumb"}

    def __init__(self, db_path):
        super(ActorModel, self).__init__()
        self._actors = []
        self._db = db.Database(db_path)


    def update(self, search_term):
        self.beginResetModel()
        self._actors = self._db.actor_search(search_term)
        self.endResetModel()

    # Reacts to onTextChanged event of searchBar (in QML code)
    @pyqtSlot(str)
    def search_input(self,search_input):
        if len(search_input) > 3:
            print (search_input)
            self.update(search_input)

    def rowCount(self, parent=None, *args, **kwargs):
        return len(self._actors)

    def data(self, QModelIndex, role=None):
        row = QModelIndex.row()
        if role == self.NameRole:
            return self._actors[row]["name"]

        if role == self.ThumbRole:
            return self._actors[row]["thumbnail"]

    def roleNames(self):
        return self._roles

db.py:

import sqlite3


class Database:

    def __init__(self, db_path):
        self.db_path = db_path
        self.sqlite_db = sqlite3.connect(self.db_path)
        self.sqlite_db.row_factory = sqlite3.Row
        self.cursor = self.sqlite_db.cursor()

    def actor_search(self, actor_name):
        self.cursor.execute('SELECT Actors.Id,Actors.Name,Actors.thumbnail AS thumbnail FROM Actors '
                                'WHERE Actors.Name LIKE \'%{}%\' ORDER BY Actors.Name'.format(actor_name))
        return self.cursor.fetchall()

main.qml:

import QtQuick 2.8
import QtQuick.Window 2.2
import QtQuick.Layouts 1.3
import QtQuick.Controls 2.1

Window {
    id: root
    visible: true
    title: 'Actor Exploer'
    width: 1280
    height: 720

    ColumnLayout {
        id: mainLayout
        anchors.fill: parent

        Row {
            Layout.fillWidth: true

            TextArea {
                id: searchBar
                placeholderText: "Input actor name"
                Layout.fillWidth: true
                width: 600
                onTextChanged: myModel.search_input(searchBar.text)                
            }


        }

        GridView {

            id: gridView
            keyNavigationWraps: true
            Layout.fillWidth: true
            Layout.fillHeight: true
            cellWidth: 220
            cellHeight: 320

            model: myModel // QML connection to python model
            delegate: Rectangle {
                id: thumb_frame
                height: 330
                width: 200
                Image {
                    id: actorThumb
                    asynchronous: true
                    source: "file:///" + thumb  // Access to the ThumbRole in ActorModel in our python code
                    smooth: true
                    sourceSize.width: 200
                    sourceSize.height: 300
                    height: 300
                    width: 200
                    anchors.left: thumb_frame.left
                    anchors.top: thumb_frame.top

                    onStatusChanged: {
                        if (actorThumb.status == Image.Error)
                            actorThumb.source = 'PLACEHOLDER_IMAGE_PATH'
                    }
                }

                Text {
                    anchors.top: actorThumb.bottom
                    anchors.horizontalCenter: actorThumb.horizontalCenter
                    text: name // Access to the NameRole in ActorModel in our python code
                }


        }
    }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top