PyQt 和 QML - 如何创建自定义数据模型

发布于 2024-10-05 04:01:42 字数 65 浏览 8 评论 0原文

我应该如何在 PyQt 中创建 QAbstractListModel 并将其与 QML ListView 一起使用?

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

软糯酥胸 2024-10-12 04:01:42

如果其他人正在寻找答案,我制作了一个小应用程序,它使用 QAbstractListModel 的子类连接到参与者数据库,并根据 @fgungor 发布的示例在 QGridView 中显示缩略图。(PyQt5)

ma​​in.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()

ma​​in.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
                }


        }
    }
}

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
                }


        }
    }
}
丿*梦醉红颜 2024-10-12 04:01:42

您需要设置它的角色名称才能在 QML 中使用它;

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

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

勿忘初心 2024-10-12 04:01:42

还没有使用过 PyQT,但您可以在这里找到一个最小的工作示例:http://doc. qt.nokia.com/stable/qdeclarativemodels.html

如果您检查包含 class Animal {...} 的示例,您会发现必须为不同字段定义角色你想要供应。并且至少必须定义 data() 函数,返回给定索引的相应字段值。此外,您可能还需要自己的自定义方法来插入和删除。希望这有帮助...

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文