PySide2/QML 填充和动画化 Gridview model/delegate

PySide2/QML populate and animate Gridview model/delegate

我是 QML 的新手,正在寻求以下方面的帮助

这里是测试代码

qmlHoverView.py

from PySide2 import QtCore, QtQuick, QtGui, QtWidgets, QtQml
import os
import sys

class inventoryModel(QtCore.QAbstractListModel):
    def __init__(self, entries, parent=None):
        super(inventoryModel, self).__init__(parent)
        self.titleRole = QtCore.Qt.UserRole + 1000
        self.thumbnailRole = QtCore.Qt.UserRole + 1001
        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 == self.titleRole:
                return item["title"]
            elif role == self.thumbnailRole:
                return item["thumbnail"]

    def roleNames(self):
        roles = dict()
        roles[self.titleRole] = b"title"
        roles[self.thumbnailRole] = b"thumbnail"
        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 Foo(QtCore.QObject):
    def __init__(self):
        QtCore.QObject.__init__(self)


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    entries = [
        {"title": "Zero", "thumbnail": "file:///E:/Tech/QML/projects/Test_005/zero.png"},
        {"title": "One", "thumbnail": "file:///E:/Tech/QML/projects/Test_005/one.png"},
        {"title": "Two", "thumbnail": "file:///E:/Tech/QML/projects/Test_005/two.png"},
        {"title": "Three", "thumbnail": "file:///E:/Tech/QML/projects/Test_005/three.png"},
        {"title": "Four", "thumbnail": "file:///E:/Tech/QML/projects/Test_005/four.png"},
        {"title": "Five", "thumbnail": "file:///E:/Tech/QML/projects/Test_005/five.png"},
        {"title": "Six", "thumbnail": "file:///E:/Tech/QML/projects/Test_005/six.png"},
        {"title": "Seven", "thumbnail": "file:///E:/Tech/QML/projects/Test_005/seven.png"},
        {"title": "Eight", "thumbnail": "file:///E:/Tech/QML/projects/Test_005/eight.png"},
        {"title": "Nine", "thumbnail": "file:///E:/Tech/QML/projects/Test_005/nine.png"},
    ]
    assetModel = inventoryModel(entries)
    foo = Foo()
    engine = QtQml.QQmlApplicationEngine()
    engine.rootContext().setContextProperty("foo", foo)
    engine.rootContext().setContextProperty("assetModel", assetModel)
    engine.load(QtCore.QUrl.fromLocalFile('E:/Tech/QML/projects/Test_005/main.qml'))
    if not engine.rootObjects():
        sys.exit(-1)
    engine.quit.connect(app.quit)
    sys.exit(app.exec_())

main.qml

import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQuick.Layouts 1.13

ApplicationWindow {
    id: mainWindowId
    visible: true
    width: 1280
    height: 720
    title: qsTr("Image Hover Effect")

    Rectangle {
        width: parent.width
        height: parent.height

        ColumnLayout {
            width: parent.width
            height: parent.height
            spacing: 0

            TextField{
                id: filterTextFieldId
                Layout.fillWidth: true
                Layout.preferredHeight: 40
                font {
                    family: "SF Pro Display"
                    pixelSize: 22
                }

                color: "dodgerblue"
            }

            Rectangle {
                Layout.fillWidth: true
                Layout.fillHeight: true
                color: "gold"

                GridView {
                    id: thumbViewId
                    width: parent.width
                    height: parent.height
                    anchors.fill: parent
                    anchors.margins: 25
                    cellWidth: 260
                    cellHeight: 260
                    model: assetModel
                    delegate: ThumbDelegate {}
                    focus: true
                }
            }
        }
    }

    Connections {
        target: foo
    }
}

ThumbDelegate.qml

import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQuick.Layouts 1.13

Component {
    Rectangle {
        width: 256
        height: 256
        color: 'green'

        Image {
            id: thumbImageId
            source: thumbnail
            asynchronous: true
        }

        Rectangle {
            width: parent.width
            height: 50
            anchors.bottom: parent.bottom
            color: 'grey'

            Label {
                anchors.verticalCenter: parent.verticalCenter
                anchors.left: parent.left
                anchors.leftMargin: 10
                text: title
                font.family: 'SF Pro Display'
                font.pixelSize: 22
                color: 'white'
            }
        }
    }
}

以上代码的输出

你问了不同的问题,这次我会回答所有问题,但下一次你必须按照 SO 指南中的指示为每个问题创建一个 post。


在您的情况下,需要 3 个元素:

  • 将图像加载到GridView中:建议实现一个模型,在这种情况下,基于具有自定义角色的QStandardItemModel实现它,并且与代表建立联系。

  • Filter:为此,您可以使用 DelegateModel 或 QSortFilterProxyModel,在这种情况下使用第二个选项,因为它通过角色和通过实现过滤一个正则表达式。

  • 悬停动画: 首先是检测鼠标何时进入或离开项目,为此使用一个 MouseArea 来触发进入和退出信号。然后我们使用 Behavior 来设置 "y" 属性 变化时的动画。然后只需要在信号被触发时设置各自的最终值。我删除了 "anchors.bottom: parent.bottom" 因为锚点不允许修改 属性.

另一方面,如果您为委托创建 qml,则不必使用组件,因为它本身就是一个组件,另一方面,您必须启用 "clip" 属性使物品的涂装不在自己的区域之外

综合以上,解决方案是:

├── images
│   └── android.png
├── main.py
└── qml
    ├── main.qml
    └── ThumbDelegate.qml

main.py

import os
import sys

from PySide2 import QtCore, QtGui, QtWidgets, QtQml


class CustomModel(QtGui.QStandardItemModel):
    TitleRole = QtCore.Qt.UserRole + 1000
    UrlRole = QtCore.Qt.UserRole + 1001

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setItemRoleNames(
            {CustomModel.TitleRole: b"title", CustomModel.UrlRole: b"thumbnail"}
        )

    @QtCore.Slot(str, QtCore.QUrl)
    def addItem(self, title, url):
        it = QtGui.QStandardItem()
        it.setData(title, CustomModel.TitleRole)
        it.setData(url, CustomModel.UrlRole)
        self.appendRow(it)


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    current_dir = os.path.dirname(os.path.realpath(__file__))

    model = CustomModel()

    # add items
    for (
        text
    ) in "amputate arena accept architecture astonishing advertise abortion apple absolute advice".split():
        title = text
        image_path = os.path.join(current_dir, "images", "android.png")
        model.addItem(title, QtCore.QUrl.fromLocalFile(image_path))

    proxy_filter = QtCore.QSortFilterProxyModel()
    proxy_filter.setSourceModel(model)
    proxy_filter.setFilterRole(CustomModel.TitleRole)

    engine = QtQml.QQmlApplicationEngine()

    engine.rootContext().setContextProperty("proxy_filter", proxy_filter)
    filename = os.path.join(current_dir, "qml", "main.qml")
    engine.load(QtCore.QUrl.fromLocalFile(filename))

    if not engine.rootObjects():
        sys.exit(-1)
    engine.quit.connect(app.quit)
    sys.exit(app.exec_())

main.qml

import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQuick.Layouts 1.13

ApplicationWindow {
    id: mainWindowId
    visible: true
    width: 1280
    height: 720
    title: qsTr("Image Hover Effect")

    Rectangle {
        anchors.fill: parent

        ColumnLayout {
            anchors.fill: parent
            spacing: 0
            TextField{
                id: filterTextFieldId
                Layout.fillWidth: true
                Layout.preferredHeight: 40
                font {
                    family: "SF Pro Display"
                    pixelSize: 22
                }
                color: "dodgerblue"
                onTextChanged: proxy_filter.setFilterRegExp(text)
            }

            Rectangle {
                Layout.fillWidth: true
                Layout.fillHeight: true
                color: "gold"
                GridView {
                    clip: true
                    id: thumbViewId
                    anchors.fill: parent
                    anchors.margins: 25

                    cellWidth: 260
                    cellHeight: 260

                    model: proxy_filter
                    delegate: ThumbDelegate {
                        source: model.thumbnail
                        title: model.title
                    }
                    focus: true
                }
            }
        }
    }
}

ThumbDelegate.qml

import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQuick.Layouts 1.13


Rectangle {
    id: root
    width: 256
    height: 256
    color: 'green'
    clip: true

    property alias source: thumbImageId.source
    property alias title: label.text

    Image {
        id: thumbImageId
        asynchronous: true
        anchors.fill: parent
    }

    Rectangle {
        id: base
        width: parent.width
        height: 50
        color: 'grey'
        y: root.height
        Behavior on y { NumberAnimation {duration: 500} }
        Label {
            id: label
            anchors.verticalCenter: parent.verticalCenter
            anchors.left: parent.left
            anchors.leftMargin: 10
            font.family: 'SF Pro Display'
            font.pointSize: 22
            color: 'white'
        }
    }


    MouseArea{
        anchors.fill: parent
        hoverEnabled: true
        onEntered: base.y = root.height - base.height
        onExited: base.y = root.height
    }
}

更新:

看到你更新了你的问题,只需要修改python代码,qml代码必须和我上一部分回答中提出的一样。

*.py

import os
import sys

from PySide2 import QtCore, QtGui, QtWidgets, QtQml


class InventoryModel(QtCore.QAbstractListModel):
    TitleRole = QtCore.Qt.UserRole + 1000
    ThumbnailRole = QtCore.Qt.UserRole + 1001

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

    def rowCount(self, parent=QtCore.QModelIndex()):
        return 0 if parent.isValid() else 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 == InventoryModel.TitleRole:
                return item["title"]
            elif role == InventoryModel.ThumbnailRole:
                return item["thumbnail"]

    def roleNames(self):
        roles = dict()
        roles[InventoryModel.TitleRole] = b"title"
        roles[InventoryModel.ThumbnailRole] = b"thumbnail"
        return roles

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


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)

    current_dir = os.path.dirname(os.path.realpath(__file__))

    entries = [
        {"title": "Zero", "thumbnail": "file:///E:/Tech/QML/projects/Test_005/zero.png"},
        {"title": "One", "thumbnail": "file:///E:/Tech/QML/projects/Test_005/one.png"},
        {"title": "Two", "thumbnail": "file:///E:/Tech/QML/projects/Test_005/two.png"},
        {"title": "Three", "thumbnail": "file:///E:/Tech/QML/projects/Test_005/three.png"},
        {"title": "Four", "thumbnail": "file:///E:/Tech/QML/projects/Test_005/four.png"},
        {"title": "Five", "thumbnail": "file:///E:/Tech/QML/projects/Test_005/five.png"},
        {"title": "Six", "thumbnail": "file:///E:/Tech/QML/projects/Test_005/six.png"},
        {"title": "Seven", "thumbnail": "file:///E:/Tech/QML/projects/Test_005/seven.png"},
        {"title": "Eight", "thumbnail": "file:///E:/Tech/QML/projects/Test_005/eight.png"},
        {"title": "Nine", "thumbnail": "file:///E:/Tech/QML/projects/Test_005/nine.png"},
    ]

    assetModel = InventoryModel(entries)
    engine = QtQml.QQmlApplicationEngine()

    proxy_filter = QtCore.QSortFilterProxyModel()
    proxy_filter.setSourceModel(assetModel)
    proxy_filter.setFilterRole(InventoryModel.TitleRole)

    engine.rootContext().setContextProperty("proxy_filter", proxy_filter)
    engine.load(QtCore.QUrl.fromLocalFile('E:/Tech/QML/projects/Test_005/main.qml'))

    if not engine.rootObjects():
        sys.exit(-1)

    engine.quit.connect(app.quit)
    sys.exit(app.exec_())