将 QML 中的 QSortFilterProxyModel 与 PyQt5 结合使用
Use a QSortFilterProxyModel from QML with PyQt5
我尝试在 PyQt5 中结合 QML 视图和 QSortFilterProxyModel。不幸的是,我无法让它以任何方式工作。
我现在的主要问题似乎是从 QML 传回项目。但即使这是可能的,它似乎也不起作用,如果我直接在 python.
中设置模型,我会得到 TypeError: QSortFilterProxyModel.setSourceModel(QAbstractItemModel): argument 1 has unexpected type 'PyCapsule'
目前我有:
class SortFilterProxyModel(QSortFilterProxyModel):
@pyqtProperty(QQmlListReference)
def source (self):
return self._source
@source.setter
def source (self, source):
setSourceModel(source)
self._source = source
class MyItem(QObject):
nameChanged = pyqtSignal()
def __init__(self, name, parent=None):
QObject.__init__(self, parent)
self._name = name
@pyqtProperty('QString', notify=nameChanged)
def name(self):
return self._name
class MyModel(QObject):
itemsChanged = pyqtSignal()
def __init__(self, parent=None):
QObject.__init__(self, parent)
self._items = [MyItem('one'), MyItem('two'), MyItem('three')]
@pyqtProperty(QQmlListProperty, notify=itemsChanged)
def items(self):
print('Query for items')
return QQmlListProperty(MyItem, self, self._items)
@pyqtSlot()
def new_item(self):
print('Append new item')
self._items.append(MyItem('new'))
self.itemsChanged.emit()
和
import QtQuick 2.2
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.2
import QtQuick.Dialogs 1.2
import MyModel 1.0
import MyItem 1.0
import SortFilterProxyModel 1.0
ApplicationWindow {
function getCurrentIndex(list, element) {
console.log('getCurrentIndex')
if (list && element) {
for (var i = 0; i < list.length; i++) {
if (list[i].name === element.name) {
console.log('Found item at pos: ' + i)
return i
}
}
}
return -1
}
id: mainWindow
width: 800; height: 600
color: "gray"
MyModel {
id: mymodel
}
SortFilterProxyModel {
id: proxyModel
source: mymodel.items
}
TableView {
anchors.fill: parent
//model: mymodel.items
model: proxyModel
TableViewColumn {
role: "name"
title: "Name"
}
}
}
完整资源在这里:
https://github.com/sturmf/python_samples/tree/master/pyqt_qsortfilterproxymodel
让它工作的唯一方法是按照 Frank 的建议切换到 QAbstractListModel。这是代码的重要部分:
class MyItem(QObject):
nameChanged = pyqtSignal()
def __init__(self, name, parent=None):
QObject.__init__(self, parent)
self._name = name
@pyqtProperty('QString', notify=nameChanged)
def name(self):
return self._name
class MyModel(QAbstractListModel):
NameRole = Qt.UserRole + 1
_roles = {NameRole: "name"}
def __init__(self, parent=None):
print("constructing")
super().__init__(parent)
self._items = [MyItem('one'), MyItem('two'), MyItem('three')]
self._column_count = 1
def roleNames(self):
print("roleNames")
return self._roles
def rowCount(self, parent=QModelIndex()):
print("rowCount", len(self._items))
return len(self._items)
def data(self, index, role=Qt.DisplayRole):
print("in data")
try:
item = self._items[index.row()]
except IndexError:
return QVariant()
if role == self.NameRole:
return item.name
return QVariant()
和一个我可以从 QML 使用的 SortFilterProxyModel
class SortFilterProxyModel(QSortFilterProxyModel):
def __init__(self, parent):
super().__init__(parent)
@pyqtProperty(QAbstractItemModel)
def source (self):
return self._source
@source.setter
def source (self, source):
self.setSourceModel(source)
self._source = source
def roleKey(self, role):
roles = self.roleNames()
for key, value in roles.items():
if value == role:
return key
return -1
@pyqtSlot(str, int)
def sort(self, role, order):
self.setSortRole(self.roleKey(role));
super().sort(0, order);
现在我可以在 QML 中执行以下操作
MyModel {
id: mymodel
}
SortFilterProxyModel {
id: proxyModel
source: mymodel
}
TableView {
id: tableView
anchors.fill: parent
model: proxyModel
sortIndicatorVisible: true
onSortIndicatorOrderChanged: model.sort(getColumn(sortIndicatorColumn).role, sortIndicatorOrder)
onSortIndicatorColumnChanged: model.sort(getColumn(sortIndicatorColumn).role, sortIndicatorOrder)
TableViewColumn {
title: "Name"
role: "name"
}
}
我现在还实现了这里描述的另一种方式:http://blog.qt.io/blog/2014/04/16/qt-weekly-6-sorting-and-filtering-a-tableview/
SortFilterProxyModel 中的实现有点长,但 QML 源代码变得更好了。这个版本还包括一个过滤器实现
这也使它更长。
class MyItem(QObject):
nameChanged = pyqtSignal()
def __init__(self, name, parent=None):
QObject.__init__(self, parent)
self._name = name
@pyqtProperty('QString', notify=nameChanged)
def name(self):
return self._name
class MyModel(QAbstractListModel):
NameRole = Qt.UserRole + 1
_roles = {NameRole: "name"}
def __init__(self, parent=None):
super().__init__(parent)
self._items = [MyItem('one'), MyItem('two'), MyItem('three')]
self._column_count = 1
def roleNames(self):
return self._roles
def rowCount(self, parent=QModelIndex()):
return len(self._items)
def data(self, index, role=Qt.DisplayRole):
try:
item = self._items[index.row()]
except IndexError:
return QVariant()
if role == self.NameRole:
return item.name
return QVariant()
和一个我可以从 QML 使用的 SortFilterProxyModel
class SortFilterProxyModel(QSortFilterProxyModel):
class FilterSyntax:
RegExp, Wildcard, FixedString = range(3)
Q_ENUMS(FilterSyntax)
def __init__(self, parent):
super().__init__(parent)
@pyqtProperty(QAbstractItemModel)
def source(self):
return super().sourceModel()
@source.setter
def source(self, source):
self.setSourceModel(source)
@pyqtProperty(int)
def sortOrder(self):
return self._order
@sortOrder.setter
def sortOrder(self, order):
self._order = order
super().sort(0, order)
@pyqtProperty(QByteArray)
def sortRole(self):
return self._roleNames().get(super().sortRole())
@sortRole.setter
def sortRole(self, role):
super().setSortRole(self._roleKey(role))
@pyqtProperty(QByteArray)
def filterRole(self):
return self._roleNames().get(super().filterRole())
@filterRole.setter
def filterRole(self, role):
super().setFilterRole(self._roleKey(role))
@pyqtProperty(str)
def filterString(self):
return super().filterRegExp().pattern()
@filterString.setter
def filterString(self, filter):
super().setFilterRegExp(QRegExp(filter, super().filterCaseSensitivity(), self.filterSyntax))
@pyqtProperty(int)
def filterSyntax(self):
return super().filterRegExp().patternSyntax()
@filterSyntax.setter
def filterSyntax(self, syntax):
super().setFilterRegExp(QRegExp(self.filterString, super().filterCaseSensitivity(), syntax))
def filterAcceptsRow(self, sourceRow, sourceParent):
rx = super().filterRegExp()
if not rx or rx.isEmpty():
return True
model = super().sourceModel()
sourceIndex = model.index(sourceRow, 0, sourceParent)
# skip invalid indexes
if not sourceIndex.isValid():
return True
# If no filterRole is set, iterate through all keys
if not self.filterRole or self.filterRole == "":
roles = self._roleNames()
for key, value in roles.items():
data = model.data(sourceIndex, key)
if rx.indexIn(data) != -1:
return True
return False
# Here we have a filterRole set so only search in that
data = model.data(sourceIndex, self._roleKey(self.filterRole))
return rx.indexIn(data) != -1
def _roleKey(self, role):
roles = self.roleNames()
for key, value in roles.items():
if value == role:
return key
return -1
def _roleNames(self):
source = super().sourceModel()
if source:
return source.roleNames()
return {}
现在我可以在 QML 中执行以下操作
MyModel {
id: mymodel
}
SortFilterProxyModel {
id: proxyModel
source: mymodel
sortOrder: tableView.sortIndicatorOrder
sortCaseSensitivity: Qt.CaseInsensitive
sortRole: tableView.getColumn(tableView.sortIndicatorColumn).role
filterString: "*" + searchBox.text + "*"
filterSyntax: SortFilterProxyModel.Wildcard
filterCaseSensitivity: Qt.CaseInsensitive
filterRole: tableView.getColumn(tableView.sortIndicatorColumn).role
}
TableView {
id: tableView
anchors.fill: parent
model: proxyModel
sortIndicatorVisible: true
TableViewColumn {
role: "name"
title: "Name"
}
}
我尝试在 PyQt5 中结合 QML 视图和 QSortFilterProxyModel。不幸的是,我无法让它以任何方式工作。 我现在的主要问题似乎是从 QML 传回项目。但即使这是可能的,它似乎也不起作用,如果我直接在 python.
中设置模型,我会得到TypeError: QSortFilterProxyModel.setSourceModel(QAbstractItemModel): argument 1 has unexpected type 'PyCapsule'
目前我有:
class SortFilterProxyModel(QSortFilterProxyModel):
@pyqtProperty(QQmlListReference)
def source (self):
return self._source
@source.setter
def source (self, source):
setSourceModel(source)
self._source = source
class MyItem(QObject):
nameChanged = pyqtSignal()
def __init__(self, name, parent=None):
QObject.__init__(self, parent)
self._name = name
@pyqtProperty('QString', notify=nameChanged)
def name(self):
return self._name
class MyModel(QObject):
itemsChanged = pyqtSignal()
def __init__(self, parent=None):
QObject.__init__(self, parent)
self._items = [MyItem('one'), MyItem('two'), MyItem('three')]
@pyqtProperty(QQmlListProperty, notify=itemsChanged)
def items(self):
print('Query for items')
return QQmlListProperty(MyItem, self, self._items)
@pyqtSlot()
def new_item(self):
print('Append new item')
self._items.append(MyItem('new'))
self.itemsChanged.emit()
和
import QtQuick 2.2
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.2
import QtQuick.Dialogs 1.2
import MyModel 1.0
import MyItem 1.0
import SortFilterProxyModel 1.0
ApplicationWindow {
function getCurrentIndex(list, element) {
console.log('getCurrentIndex')
if (list && element) {
for (var i = 0; i < list.length; i++) {
if (list[i].name === element.name) {
console.log('Found item at pos: ' + i)
return i
}
}
}
return -1
}
id: mainWindow
width: 800; height: 600
color: "gray"
MyModel {
id: mymodel
}
SortFilterProxyModel {
id: proxyModel
source: mymodel.items
}
TableView {
anchors.fill: parent
//model: mymodel.items
model: proxyModel
TableViewColumn {
role: "name"
title: "Name"
}
}
}
完整资源在这里:
https://github.com/sturmf/python_samples/tree/master/pyqt_qsortfilterproxymodel
让它工作的唯一方法是按照 Frank 的建议切换到 QAbstractListModel。这是代码的重要部分:
class MyItem(QObject):
nameChanged = pyqtSignal()
def __init__(self, name, parent=None):
QObject.__init__(self, parent)
self._name = name
@pyqtProperty('QString', notify=nameChanged)
def name(self):
return self._name
class MyModel(QAbstractListModel):
NameRole = Qt.UserRole + 1
_roles = {NameRole: "name"}
def __init__(self, parent=None):
print("constructing")
super().__init__(parent)
self._items = [MyItem('one'), MyItem('two'), MyItem('three')]
self._column_count = 1
def roleNames(self):
print("roleNames")
return self._roles
def rowCount(self, parent=QModelIndex()):
print("rowCount", len(self._items))
return len(self._items)
def data(self, index, role=Qt.DisplayRole):
print("in data")
try:
item = self._items[index.row()]
except IndexError:
return QVariant()
if role == self.NameRole:
return item.name
return QVariant()
和一个我可以从 QML 使用的 SortFilterProxyModel
class SortFilterProxyModel(QSortFilterProxyModel):
def __init__(self, parent):
super().__init__(parent)
@pyqtProperty(QAbstractItemModel)
def source (self):
return self._source
@source.setter
def source (self, source):
self.setSourceModel(source)
self._source = source
def roleKey(self, role):
roles = self.roleNames()
for key, value in roles.items():
if value == role:
return key
return -1
@pyqtSlot(str, int)
def sort(self, role, order):
self.setSortRole(self.roleKey(role));
super().sort(0, order);
现在我可以在 QML 中执行以下操作
MyModel {
id: mymodel
}
SortFilterProxyModel {
id: proxyModel
source: mymodel
}
TableView {
id: tableView
anchors.fill: parent
model: proxyModel
sortIndicatorVisible: true
onSortIndicatorOrderChanged: model.sort(getColumn(sortIndicatorColumn).role, sortIndicatorOrder)
onSortIndicatorColumnChanged: model.sort(getColumn(sortIndicatorColumn).role, sortIndicatorOrder)
TableViewColumn {
title: "Name"
role: "name"
}
}
我现在还实现了这里描述的另一种方式:http://blog.qt.io/blog/2014/04/16/qt-weekly-6-sorting-and-filtering-a-tableview/
SortFilterProxyModel 中的实现有点长,但 QML 源代码变得更好了。这个版本还包括一个过滤器实现 这也使它更长。
class MyItem(QObject):
nameChanged = pyqtSignal()
def __init__(self, name, parent=None):
QObject.__init__(self, parent)
self._name = name
@pyqtProperty('QString', notify=nameChanged)
def name(self):
return self._name
class MyModel(QAbstractListModel):
NameRole = Qt.UserRole + 1
_roles = {NameRole: "name"}
def __init__(self, parent=None):
super().__init__(parent)
self._items = [MyItem('one'), MyItem('two'), MyItem('three')]
self._column_count = 1
def roleNames(self):
return self._roles
def rowCount(self, parent=QModelIndex()):
return len(self._items)
def data(self, index, role=Qt.DisplayRole):
try:
item = self._items[index.row()]
except IndexError:
return QVariant()
if role == self.NameRole:
return item.name
return QVariant()
和一个我可以从 QML 使用的 SortFilterProxyModel
class SortFilterProxyModel(QSortFilterProxyModel):
class FilterSyntax:
RegExp, Wildcard, FixedString = range(3)
Q_ENUMS(FilterSyntax)
def __init__(self, parent):
super().__init__(parent)
@pyqtProperty(QAbstractItemModel)
def source(self):
return super().sourceModel()
@source.setter
def source(self, source):
self.setSourceModel(source)
@pyqtProperty(int)
def sortOrder(self):
return self._order
@sortOrder.setter
def sortOrder(self, order):
self._order = order
super().sort(0, order)
@pyqtProperty(QByteArray)
def sortRole(self):
return self._roleNames().get(super().sortRole())
@sortRole.setter
def sortRole(self, role):
super().setSortRole(self._roleKey(role))
@pyqtProperty(QByteArray)
def filterRole(self):
return self._roleNames().get(super().filterRole())
@filterRole.setter
def filterRole(self, role):
super().setFilterRole(self._roleKey(role))
@pyqtProperty(str)
def filterString(self):
return super().filterRegExp().pattern()
@filterString.setter
def filterString(self, filter):
super().setFilterRegExp(QRegExp(filter, super().filterCaseSensitivity(), self.filterSyntax))
@pyqtProperty(int)
def filterSyntax(self):
return super().filterRegExp().patternSyntax()
@filterSyntax.setter
def filterSyntax(self, syntax):
super().setFilterRegExp(QRegExp(self.filterString, super().filterCaseSensitivity(), syntax))
def filterAcceptsRow(self, sourceRow, sourceParent):
rx = super().filterRegExp()
if not rx or rx.isEmpty():
return True
model = super().sourceModel()
sourceIndex = model.index(sourceRow, 0, sourceParent)
# skip invalid indexes
if not sourceIndex.isValid():
return True
# If no filterRole is set, iterate through all keys
if not self.filterRole or self.filterRole == "":
roles = self._roleNames()
for key, value in roles.items():
data = model.data(sourceIndex, key)
if rx.indexIn(data) != -1:
return True
return False
# Here we have a filterRole set so only search in that
data = model.data(sourceIndex, self._roleKey(self.filterRole))
return rx.indexIn(data) != -1
def _roleKey(self, role):
roles = self.roleNames()
for key, value in roles.items():
if value == role:
return key
return -1
def _roleNames(self):
source = super().sourceModel()
if source:
return source.roleNames()
return {}
现在我可以在 QML 中执行以下操作
MyModel {
id: mymodel
}
SortFilterProxyModel {
id: proxyModel
source: mymodel
sortOrder: tableView.sortIndicatorOrder
sortCaseSensitivity: Qt.CaseInsensitive
sortRole: tableView.getColumn(tableView.sortIndicatorColumn).role
filterString: "*" + searchBox.text + "*"
filterSyntax: SortFilterProxyModel.Wildcard
filterCaseSensitivity: Qt.CaseInsensitive
filterRole: tableView.getColumn(tableView.sortIndicatorColumn).role
}
TableView {
id: tableView
anchors.fill: parent
model: proxyModel
sortIndicatorVisible: true
TableViewColumn {
role: "name"
title: "Name"
}
}