如何使用带有 Qabstractitemmodel 的 QSortFilterProxyModel 隐藏第一列
Posted
技术标签:
【中文标题】如何使用带有 Qabstractitemmodel 的 QSortFilterProxyModel 隐藏第一列【英文标题】:How to hide the first column using a QSortFilterProxyModel with a Qabstractitemmodel 【发布时间】:2021-07-18 02:14:04 【问题描述】:在小部件我的实现中,model
源模型(从 QAbstractItemModel
子类化),proxy_model
(从 QSortFilterProxyModel 子类化)代理,tree
(QTreeView) 代表模型的树。
我想隐藏第一列。
我尝试使用tree.hideColumn(0)
,树显示为平面。
filterAcceptsColumn
子类化为仅对第二列返回True,则不会显示任何行。
我相信这是因为父/子关系锚定在索引的第一列上,当代理询问第 1 列的给定索引的行数时,模型返回 0(这是如果我理解得很好,模型实现)。
如果我将rowCount
设置为在模型中为列索引> 0 返回非0 值,我可以看到树和行,但是模型没有通过QAbstractItemModelTester
测试并出现以下错误:
qt.modeltest: FAIL! childIndex != childIndex1 () returned FALSE
我很清楚在树模型中,子索引必须附加到单个父索引(第一列)。 但是,如果源模型的父子关系在第一列被过滤的情况下未被代理保留,我应该如何隐藏代理模型中的第一列?我觉得这是代理的“错误”,或者我错过了什么!
有谁知道过滤/隐藏树视图中第一列而不丢失父/子信息并仍然验证 qmodel 实现的正确方法?
谢谢!
【问题讨论】:
嗯,这有点棘手。问题在于,正如您已经发现的那样,树模型(至少在 Qt 中)只考虑每个父索引的第一列的子项。如果您考虑这些子项,问题会变得更加复杂:模型是否应该为它们隐藏第一列?正确的实现可能会变得非常困难(并且可能有问题),所以我建议尝试“破解”你的方式:因为我假设你只是为了显示目的才需要它,如果你只是返回每个***项目的column+1数据? 感谢您的回答!我不确定你提出的 hack 会有什么不同..? 这个想法不是隐藏第一列,而是在请求的列旁边显示该列的数据,并最终隐藏最后一列。这样就保持了父子关系(由于模型结构始终相同,索引始终引用原始列),唯一的区别是显示的内容移动了一列。 如果我做得好,那么你的树视图中就会出现一个空列,对吧? 嗯,理论上是的,但是你可以隐藏 that 最后一列,而不用担心结构。请注意,我没有测试过它,这只是一个突然出现在我脑海中的想法,但对我来说似乎很有意义。 【参考方案1】:正确和正确的实现至少需要代理为 second 列的父级创建索引,需要正确实现 index()
、parent()
、mapToSource()
和 @987654324 @。对于可能非常棘手的树模型。
如果源模型不太复杂并且其所有功能都已正确实现,则可能的解决方法是仅覆盖代理的data()
(和headerData
)并始终返回下一列的兄弟。
以下测试是使用简单的 QStandardItemModel 完成的,但我认为使用 QAbstractItemModel 应该没有什么不同,只要正确实现即可。
from PyQt5 import QtCore, QtGui, QtWidgets
class ColumnSwapProxy(QtCore.QSortFilterProxyModel):
def data(self, index, role=QtCore.Qt.DisplayRole):
return super().data(index.sibling(index.row(), index.column() + 1), role)
def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
if orientation == QtCore.Qt.Horizontal:
section += 1
return super().headerData(section, orientation, role)
class Test(QtWidgets.QWidget):
def __init__(self):
super().__init__()
layout = QtWidgets.QVBoxLayout(self)
self.combo = QtWidgets.QComboBox()
layout.addWidget(self.combo)
self.tree = QtWidgets.QTreeView()
layout.addWidget(self.tree)
self.model = QtGui.QStandardItemModel()
self.createTree(self.model.invisibleRootItem())
self.tree.setModel(self.model)
self.model.setHorizontalHeaderLabels(
['Root', 'Fake root'] + ['Col '.format(c) for c in range(2, 6)])
self.tree.header().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
self.proxy = ColumnSwapProxy()
self.proxy.setSourceModel(self.model)
self.combo.addItem('Base model', self.model)
self.combo.addItem('First column hidden', self.proxy)
self.combo.currentIndexChanged.connect(self.setModel)
def setModel(self):
model = self.combo.currentData()
self.tree.setModel(model)
lastColumn = self.model.columnCount() - 1
self.tree.header().setSectionHidden(lastColumn, model == self.proxy)
def createTree(self, parent, level=0):
for r in range(10):
first = QtGui.QStandardItem('Root (level )'.format(level + 1, r + 1))
if level < 2 and not r & 3:
self.createTree(first, level + 1)
row = [first]
for c in range(5):
row.append(QtGui.QStandardItem(
'Column (level )'.format(c + 2, level + 1)))
parent.appendRow(row)
import sys
app = QtWidgets.QApplication(sys.argv)
w = Test()
w.show()
sys.exit(app.exec_())
【讨论】:
以上是关于如何使用带有 Qabstractitemmodel 的 QSortFilterProxyModel 隐藏第一列的主要内容,如果未能解决你的问题,请参考以下文章
如何在 QAbstractItemModel 和 QTreeView 类中找到子项?
如何在 QAbstractItemModel 中为 QTreeView 创建人工节点
QAbstractItemModel data() 永远不会被调用
以编程方式检查QAbstractItemModel / QTreeView中的项目