PySide2/QML 填充和动画 Gridview 模型/委托

Posted

技术标签:

【中文标题】PySide2/QML 填充和动画 Gridview 模型/委托【英文标题】:PySide2/QML populate and animate Gridview model/delegate 【发布时间】:2019-10-02 08:00:17 【问题描述】:

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

如何根据 TextField 输入(作为 Regex )通过 PySide2 过滤 Gridview 模型中的 QAbstractListModel 数据(标题)。

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

这是测试代码

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'
            
        
    

以上代码的输出

【问题讨论】:

如果您更新了您的问题并且有用户之前回答过,那么建议您在每个答案下方写一条评论,表明您已更新您的问题,因为您的版本不会生成通知. 好的,现在我使用回滚选项重置原始问题 没有必要,因为我目前的回答也涵盖了您更新的代码 好的,现在我也添加了更新的代码。 【参考方案1】:

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


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

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

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

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

另一方面,如果您为委托创建 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_())

【讨论】:

以上是关于PySide2/QML 填充和动画 Gridview 模型/委托的主要内容,如果未能解决你的问题,请参考以下文章

PySide2 + QML:QApplication:通过了无效的样式覆盖,忽略它

将 QML 信号连接到 PySide2 插槽

是否可以通过 QML 从 PySide2 插槽(服务调用)获取对象列表?

添加数据库后填充listview

如何为 CAShapeLayer 路径和填充颜色设置动画

使用填充为 svg 设置动画