PySide2/QML 填充和动画化 Gridview model/delegate
PySide2/QML populate and animate Gridview model/delegate
我是 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'
}
}
}
}
以上代码的输出
你问了不同的问题,这次我会回答所有问题,但下一次你必须按照 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_())
我是 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'
}
}
}
}
以上代码的输出
你问了不同的问题,这次我会回答所有问题,但下一次你必须按照 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_())