PySide2/QML 填充和动画 Gridview 模型/委托 [英] PySide2/QML populate and animate Gridview model/delegate

查看:65
本文介绍了PySide2/QML 填充和动画 Gridview 模型/委托的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm newbie to QML and looking for help on below points

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

  • How to filter QAbstractListModel data (Title) in Gridview model via PySide2 based on TextField input (as Regex ).

如何在鼠标悬停时为 Gridview 的代理设置动画(如下图所示.)

How to animate delegate of Gridview on mouse hover (as showen in below image.)

这是测试代码

qmlHoverView.py

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

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 中所示指南.

You have asked different questions that for this occasion I will answer all but for a next occasion you will have to create a post for each question as indicated in the SO guides.

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

In your case 3 elements are required:

  • 将图像加载到 GridView 中:建议实现一个模型,在这种情况下,基于具有自定义角色的 QStandardItemModel 实现它,并与委托建立连接.

  • Load the image into the GridView: It is advisable to implement a model, in this case, implement it based on a QStandardItemModel with custom roles, and make connections with the delegate.

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

Filter: For this you can use a DelegateModel, or a QSortFilterProxyModel, in this case use the second option since it implements the filtering by role and through a regex.

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

Hover Animation: The first thing is to detect when the mouse enters or exits the item, and for this a MouseArea is used that triggers the enter and exit signals. Then we use a Behavior to set the animation when the "y" property changes. And then it is only necessary to set the respective final values when the signals are triggered. I have removed "anchors.bottom: parent.bottom" since an anchor does not allow modifying the property.

另一方面,如果您为委托创建一个 qml,则没有必要使用 Component,因为它本身就是一个组件,另一方面,您必须启用clip"属性,以便项目的绘制不在自己的区域之外.

On the other hand if you create a qml for a delegate it is not necessary to use Component since in itself it is a component, on the other hand you must enable the "clip" property so that the painting of the items is not outside its own area.

综合以上,解决办法是:

Considering the above, the solution is:

├── 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代码必须与我在上一部分回答中提出的相同.

Seeing that you have updated your question, it is only necessary to modify the python code, the qml code must be the same as the one I proposed in the previous part of my answer.

*.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_())

这篇关于PySide2/QML 填充和动画 Gridview 模型/委托的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆