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
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_())
【讨论】:
以上是关于PySide2/QML 填充和动画 Gridview 模型/委托的主要内容,如果未能解决你的问题,请参考以下文章
PySide2 + QML:QApplication:通过了无效的样式覆盖,忽略它