Qt 和 Python - QIdentityProxyModel 嵌套在 QSortFilterProxyModel 顶部时无法获得正确的列数
Qt and Python - QIdentityProxyModel does not get the right column count when nested on top of a QSortFilterProxyModel
我有三个模型,一个 QAbstractItemModel“sourceModel”,一个用于过滤的 QSortFilterProxyModel“proxyModel”,一个用于修改列和显示数据的 QIdentityProxyModel“dataModel”。
当我只对 QAbstractItemModel -> QIdentityProxyModel 进行分层时,一切正常。
我的源模型只给定了一个默认列,我可以在我的 QIdentifyProxyModel 上添加多个新列,并且我的视图显示所有正确的数据。这让我可以在具有不同数据的多个视图上重用相同的模型,太棒了。
但是,如果我对 QAbstractItemModel -> QSortFilterProxyModel -> QIdentityProxyModel 进行分层,那么我的视图只会在第一列中显示数据,并且选择模型会中断。
如果我在我的源模型中定义了相等或更多数量的列,那么所有 QIdentityProxyModel 列在我的视图中都会正确运行,并显示与我在我的源模型中定义的不同的数据。
当代理被嵌套时,QIdentityProxyModel class 仍然可以使用数据访问源项,但是传递给 QIdentityProxyModel 的数据函数的索引只查询源模型中定义的数量列。
知道怎么做吗?非常感谢任何帮助!
from PySide2 import QtCore, QtWidgets, QtGui
class Item(dict):
def __init__(self, data=None):
super(Item, self).__init__()
self._children = list()
self._parent = None
if data:
self.update(data)
def childCount(self):
return len(self._children)
def child(self, row):
if row >= len(self._children):
return
return self._children[row]
def children(self):
return self._children
def parent(self):
return self._parent
def row(self):
if self._parent is not None:
siblings = self.parent().children()
return siblings.index(self)
def add_child(self, child):
child._parent = self
self._children.append(child)
class TreeModel(QtCore.QAbstractItemModel):
Columns = list()
ItemRole = QtCore.Qt.UserRole + 1
def __init__(self, parent=None):
super(TreeModel, self).__init__(parent)
self._root_item = Item()
def rowCount(self, parent):
if parent.isValid():
item = parent.internalPointer()
else:
item = self._root_item
return item.childCount()
def columnCount(self, parent):
return len(self.Columns)
def setColumns(self, keys):
assert isinstance(keys, (list, tuple))
self.Columns = keys
def data(self, index, role):
if not index.isValid():
return None
if role == QtCore.Qt.DisplayRole or role == QtCore.Qt.EditRole:
item = index.internalPointer()
column = index.column()
key = self.Columns[column]
return item.get(key, None)
if role == self.ItemRole:
return index.internalPointer()
def headerData(self, section, orientation, role):
if role == QtCore.Qt.DisplayRole:
if section < len(self.Columns):
return self.Columns[section]
super(TreeModel, self).headerData(section, orientation, role)
def parent(self, index):
item = index.internalPointer()
parent_item = item.parent()
if parent_item == self._root_item or not parent_item:
return QtCore.QModelIndex()
return self.createIndex(parent_item.row(), 0, parent_item)
def index(self, row, column, parent):
if not parent.isValid():
parent_item = self._root_item
else:
parent_item = parent.internalPointer()
child_item = parent_item.child(row)
if child_item:
return self.createIndex(row, column, child_item)
else:
return QtCore.QModelIndex()
def add_child(self, item, parent=None):
if parent is None:
parent = self._root_item
parent.add_child(item)
def column_name(self, column):
if column < len(self.Columns):
return self.Columns[column]
def clear(self):
self.beginResetModel()
self._root_item = Item()
self.endResetModel()
class CustomSortFilterProxyModel(QtCore.QSortFilterProxyModel):
def __init__(self, parent=None):
super(CustomSortFilterProxyModel, self).__init__(parent)
def filterAcceptsRow(self, row, parent):
model = self.sourceModel()
index = model.index(row, self.filterKeyColumn(), parent)
item = index.internalPointer()
if item.get('name'):
return True
else:
return False
class IdentityProxyModel(QtCore.QIdentityProxyModel):
def __init__(self, *args, **kwargs):
super(IdentityProxyModel, self).__init__(*args, **kwargs)
self.Columns = []
self._root_item = Item()
def setColumns(self, keys):
assert isinstance(keys, (list, tuple))
self.Columns = keys
#
def columnCount(self, parent):
# return 3
return len(self.Columns)
def column_name(self, column):
if column < len(self.Columns):
return self.Columns[column]
def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
if role == QtCore.Qt.DisplayRole:
if section < len(self.Columns):
return self.Columns[section]
super(IdentityProxyModel, self).headerData(section, orientation, role)
def data(self, index, role):
if not index.isValid():
return
if role == QtCore.Qt.DisplayRole or role == QtCore.Qt.EditRole:
item = self.mapToSource(index).data(TreeModel.ItemRole)
column = index.column()
key = self.Columns[column]
return item.get(key, None)
return super(IdentityProxyModel, self).data(index, role)
if __name__ == '__main__':
import sys
sourceModel = TreeModel()
sourceModel.setColumns(['name'])
sourceModel.add_child(Item({'name': 'itemA', 'number': '1', 'info': 'A'}))
sourceModel.add_child(Item({'name': 'itemB', 'number': '2', 'info': 'B'}))
proxyModel = CustomSortFilterProxyModel()
proxyModel.setSourceModel(sourceModel)
dataModel = IdentityProxyModel()
dataModel.setSourceModel(proxyModel)
dataModel.setColumns(['name', 'info'])
app = QtWidgets.QApplication(sys.argv)
view = QtWidgets.QTreeView()
view.setModel(dataModel)
view.show()
sys.exit(app.exec_())
树模型很棘手。 很多.
无论何时你必须处理它们,你都必须真正理解递归。对于 Qt 模型(在某种程度上很复杂),这变得更加困难,并且常常是导致严重头痛和熬夜的原因。
我将以下代码基于 ,这是非常基本的,没有考虑子列数量不一致的可能性。
概念是基于树结构的代理模型需要跟踪层次结构(通常基于父索引内部指针或 id),并且当“幽灵”列出现时必须考虑这一点已创建。
以上所有总是 非常重要。项目视图需要所有实现才能正确更新其内容并允许有效的用户交互。
class IdentityProxyModel(QtCore.QIdentityProxyModel):
def __init__(self, *args, **kwargs):
super(IdentityProxyModel, self).__init__(*args, **kwargs)
self.Columns = []
self._parents = {}
self._root_item = Item()
# ...
def _isInvalid(self, column):
# we assume that the model always has the same column count
return column > self.sourceModel().columnCount() - 1
def mapToSource(self, index):
if self._isInvalid(index.column()):
index = index.sibling(index.row(), 0)
return super().mapToSource(index)
def index(self, row, column, parent=QtCore.QModelIndex()):
if self._isInvalid(column):
index = self.createIndex(row, column, parent.internalId())
self._parents[index] = parent
return index
return super().index(row, column, parent)
def parent(self, index):
if self._isInvalid(index.column()):
return self._parents[index]
return super().parent(index)
def flags(self, index):
if self._isInvalid(index.column()):
return self.flags(index.sibling(index.row(), 0))
return super().flags(index)
def sibling(self, row, column, idx):
if self._isInvalid(column):
return self.index(row, column, idx.parent())
elif self._isInvalid(idx.column()):
idx = self.index(idx.row(), 0, idx.parent())
return super().sibling(row, column, idx)
注意:覆盖应始终使用其基本实现的签名。例如,rowCount()
和 columnCount()
必须 接受无效的关键字父参数,not 用于“快速在模型的顶层调用”。通常的做法是使用基本的 QModelIndex 实例,因为它基本上是一个不可变的对象(但 None
也被普遍接受):
def rowCount(self, parent=QtCore.QModelIndex()):
# ...
def columnCount(self, parent=QtCore.QModelIndex()):
# ...
我有三个模型,一个 QAbstractItemModel“sourceModel”,一个用于过滤的 QSortFilterProxyModel“proxyModel”,一个用于修改列和显示数据的 QIdentityProxyModel“dataModel”。
当我只对 QAbstractItemModel -> QIdentityProxyModel 进行分层时,一切正常。 我的源模型只给定了一个默认列,我可以在我的 QIdentifyProxyModel 上添加多个新列,并且我的视图显示所有正确的数据。这让我可以在具有不同数据的多个视图上重用相同的模型,太棒了。
但是,如果我对 QAbstractItemModel -> QSortFilterProxyModel -> QIdentityProxyModel 进行分层,那么我的视图只会在第一列中显示数据,并且选择模型会中断。 如果我在我的源模型中定义了相等或更多数量的列,那么所有 QIdentityProxyModel 列在我的视图中都会正确运行,并显示与我在我的源模型中定义的不同的数据。
当代理被嵌套时,QIdentityProxyModel class 仍然可以使用数据访问源项,但是传递给 QIdentityProxyModel 的数据函数的索引只查询源模型中定义的数量列。
知道怎么做吗?非常感谢任何帮助!
from PySide2 import QtCore, QtWidgets, QtGui
class Item(dict):
def __init__(self, data=None):
super(Item, self).__init__()
self._children = list()
self._parent = None
if data:
self.update(data)
def childCount(self):
return len(self._children)
def child(self, row):
if row >= len(self._children):
return
return self._children[row]
def children(self):
return self._children
def parent(self):
return self._parent
def row(self):
if self._parent is not None:
siblings = self.parent().children()
return siblings.index(self)
def add_child(self, child):
child._parent = self
self._children.append(child)
class TreeModel(QtCore.QAbstractItemModel):
Columns = list()
ItemRole = QtCore.Qt.UserRole + 1
def __init__(self, parent=None):
super(TreeModel, self).__init__(parent)
self._root_item = Item()
def rowCount(self, parent):
if parent.isValid():
item = parent.internalPointer()
else:
item = self._root_item
return item.childCount()
def columnCount(self, parent):
return len(self.Columns)
def setColumns(self, keys):
assert isinstance(keys, (list, tuple))
self.Columns = keys
def data(self, index, role):
if not index.isValid():
return None
if role == QtCore.Qt.DisplayRole or role == QtCore.Qt.EditRole:
item = index.internalPointer()
column = index.column()
key = self.Columns[column]
return item.get(key, None)
if role == self.ItemRole:
return index.internalPointer()
def headerData(self, section, orientation, role):
if role == QtCore.Qt.DisplayRole:
if section < len(self.Columns):
return self.Columns[section]
super(TreeModel, self).headerData(section, orientation, role)
def parent(self, index):
item = index.internalPointer()
parent_item = item.parent()
if parent_item == self._root_item or not parent_item:
return QtCore.QModelIndex()
return self.createIndex(parent_item.row(), 0, parent_item)
def index(self, row, column, parent):
if not parent.isValid():
parent_item = self._root_item
else:
parent_item = parent.internalPointer()
child_item = parent_item.child(row)
if child_item:
return self.createIndex(row, column, child_item)
else:
return QtCore.QModelIndex()
def add_child(self, item, parent=None):
if parent is None:
parent = self._root_item
parent.add_child(item)
def column_name(self, column):
if column < len(self.Columns):
return self.Columns[column]
def clear(self):
self.beginResetModel()
self._root_item = Item()
self.endResetModel()
class CustomSortFilterProxyModel(QtCore.QSortFilterProxyModel):
def __init__(self, parent=None):
super(CustomSortFilterProxyModel, self).__init__(parent)
def filterAcceptsRow(self, row, parent):
model = self.sourceModel()
index = model.index(row, self.filterKeyColumn(), parent)
item = index.internalPointer()
if item.get('name'):
return True
else:
return False
class IdentityProxyModel(QtCore.QIdentityProxyModel):
def __init__(self, *args, **kwargs):
super(IdentityProxyModel, self).__init__(*args, **kwargs)
self.Columns = []
self._root_item = Item()
def setColumns(self, keys):
assert isinstance(keys, (list, tuple))
self.Columns = keys
#
def columnCount(self, parent):
# return 3
return len(self.Columns)
def column_name(self, column):
if column < len(self.Columns):
return self.Columns[column]
def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
if role == QtCore.Qt.DisplayRole:
if section < len(self.Columns):
return self.Columns[section]
super(IdentityProxyModel, self).headerData(section, orientation, role)
def data(self, index, role):
if not index.isValid():
return
if role == QtCore.Qt.DisplayRole or role == QtCore.Qt.EditRole:
item = self.mapToSource(index).data(TreeModel.ItemRole)
column = index.column()
key = self.Columns[column]
return item.get(key, None)
return super(IdentityProxyModel, self).data(index, role)
if __name__ == '__main__':
import sys
sourceModel = TreeModel()
sourceModel.setColumns(['name'])
sourceModel.add_child(Item({'name': 'itemA', 'number': '1', 'info': 'A'}))
sourceModel.add_child(Item({'name': 'itemB', 'number': '2', 'info': 'B'}))
proxyModel = CustomSortFilterProxyModel()
proxyModel.setSourceModel(sourceModel)
dataModel = IdentityProxyModel()
dataModel.setSourceModel(proxyModel)
dataModel.setColumns(['name', 'info'])
app = QtWidgets.QApplication(sys.argv)
view = QtWidgets.QTreeView()
view.setModel(dataModel)
view.show()
sys.exit(app.exec_())
树模型很棘手。 很多.
无论何时你必须处理它们,你都必须真正理解递归。对于 Qt 模型(在某种程度上很复杂),这变得更加困难,并且常常是导致严重头痛和熬夜的原因。
我将以下代码基于
概念是基于树结构的代理模型需要跟踪层次结构(通常基于父索引内部指针或 id),并且当“幽灵”列出现时必须考虑这一点已创建。
以上所有总是 非常重要。项目视图需要所有实现才能正确更新其内容并允许有效的用户交互。
class IdentityProxyModel(QtCore.QIdentityProxyModel):
def __init__(self, *args, **kwargs):
super(IdentityProxyModel, self).__init__(*args, **kwargs)
self.Columns = []
self._parents = {}
self._root_item = Item()
# ...
def _isInvalid(self, column):
# we assume that the model always has the same column count
return column > self.sourceModel().columnCount() - 1
def mapToSource(self, index):
if self._isInvalid(index.column()):
index = index.sibling(index.row(), 0)
return super().mapToSource(index)
def index(self, row, column, parent=QtCore.QModelIndex()):
if self._isInvalid(column):
index = self.createIndex(row, column, parent.internalId())
self._parents[index] = parent
return index
return super().index(row, column, parent)
def parent(self, index):
if self._isInvalid(index.column()):
return self._parents[index]
return super().parent(index)
def flags(self, index):
if self._isInvalid(index.column()):
return self.flags(index.sibling(index.row(), 0))
return super().flags(index)
def sibling(self, row, column, idx):
if self._isInvalid(column):
return self.index(row, column, idx.parent())
elif self._isInvalid(idx.column()):
idx = self.index(idx.row(), 0, idx.parent())
return super().sibling(row, column, idx)
注意:覆盖应始终使用其基本实现的签名。例如,rowCount()
和 columnCount()
必须 接受无效的关键字父参数,not 用于“快速在模型的顶层调用”。通常的做法是使用基本的 QModelIndex 实例,因为它基本上是一个不可变的对象(但 None
也被普遍接受):
def rowCount(self, parent=QtCore.QModelIndex()):
# ...
def columnCount(self, parent=QtCore.QModelIndex()):
# ...