python - PySide2/QML 填充 Gridview 模型/委托(delegate)并为其设置动画

标签 python qml pyside2

我是 QML 新手,正在寻求以下几点帮助

  • 如何基于 TextField 输入(如 Regex )通过 PySide2 过滤 Gridview 模型中的 QAbstractListModel 数据(标题)。

  • 如何在鼠标悬停时为 Gridview 的委托(delegate)设置动画(如下图所示。)

enter image description here

这是测试代码

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'
            }
        }
    }
}

以上代码的输出

enter image description here

最佳答案

您提出了不同的问题,这次我将回答所有问题,但下次您将必须按照 SO 指南中的指示为每个问题创建一个帖子。


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

  • 将图像加载到 GridView 中: 建议实现一个模型,在本例中,基于具有自定义角色的 QStandardItemModel 实现它,并与委托(delegate)建立连接。

  • 过滤器:为此,您可以使用 DelegateModel 或 QSortFilterProxyModel,在本例中使用第二个选项,因为它按角色并通过正则表达式实现过滤。

  • 悬停动画:第一件事是检测鼠标何时进入或退出项目,为此使用 MouseArea 来触发进入和退出信号。然后我们使用Behavior来设置“y”属性改变时的动画。然后只需要在信号被触发时设置相应的最终值即可。我已删除“anchors.bottom:parent.bottom”,因为 anchor 不允许修改该属性。

另一方面,如果您为委托(delegate)创建 qml,则无需使用 Component,因为它本身就是一个组件,另一方面,您必须启用“clip”属性,以便项目的绘制不在自己的区域之外。

考虑到上述情况,解决方案是:

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

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

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

关于python - PySide2/QML 填充 Gridview 模型/委托(delegate)并为其设置动画,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58197429/

相关文章:

python - 从模型更新 UI 组件

python - 如果 Edge TPU 编译器仅支持 3 个暗淡张量,如何实现 conv2d 层?

Python 在嵌套 for 循环后跳行

qt - 在 Component.onCompleted qml 中多次执行语句

qt - 你能设置QTreeWidget的动画速度吗?

python - 将两个 Python 脚本集成为一个?

python - 使用 mingw 捕获 distutils 的错误输出

python - Django Restful 中的 ListSerializer - 何时调用?

qml - Qt5中的Label和Text有什么区别?

c++ - Qt/QML qmlRegisterType 与 setContextProperty(区别)