问题
I'm newbie to QML and looking for help on below points
How to filter QAbstractListModel data (Title) in Gridview model via PySide2 based on TextField input (as Regex ).
How to animate delegate of Gridview on mouse hover (as showen in below image.)
Here is the test code
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'
}
}
}
}
Output of above code
回答1:
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.
In your case 3 elements are required:
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.
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.
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.
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
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
}
}
Update:
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_())
来源:https://stackoverflow.com/questions/58197429/pyside2-qml-populate-and-animate-gridview-model-delegate