QTreeWidget - 从排序中排除顶级项目
QTreeWidget - excluding top level items from sort
我有一个 2 级 QTreeWidget。顶层只有一列,仅用于对第二层进行分组,第二层包含实际的 multi-column 数据。
当我通过单击 header(或任何其他方式,真的)进行排序时,我只想对第二级进行排序,因为 top-level 项目在别处设置了固定顺序在应用程序中,应该不会受到影响。
如何实现?
您可以像这样使用 QTreeWidgetItem(s) 的 sortChildren
方法:
sort_column = 1
# Iterate top-level elements
for index in range(tree.topLevelItemCount()):
# Obtain the actual top-level item
top_level_item = tree.topLevelItem(index)
# Sort
top_level_item.sortChildren(sort_column)
请注意,如果您向某个项目添加新的子项,则需要再次对其进行排序(仅更改的项目)
编辑
Thanks, that's a step in the right direction, but the question now is how to override the default sort behavior. I'm doing self.treeWidget.header().sortIndicatorChanged.connect(self.sortChanged), but since that's a signal it just triggers after the widget does its regular sort, so in the end it has no effect - the top level items get sorted before the function is called
除非你去定制model/view,你可以尝试两种方法(我没有测试过):
1.覆盖 QTreeWidget
的 "sorting" 方法
尝试用一些空操作替换 sortItems()
方法,但小部件可能有其他内部调用的私有方法。
2。覆盖根项 sortChildren()
排序可能是通过调用(隐藏的)根项目 sortChildren() 实现的,这将对顶级项目进行排序并调用这些项目的 sortChildren()。您也许能够弄清楚如何使用 rootIndex()
/setRootIndex()
get/set 此项,然后覆盖排序方法(例如 root.sortChildren = lambda c, o: None
)。
我根据 问题的答案找到了解决方案。捕获 sectionClicked 信号并检查单击的列是否是第一个。如果是,则强制静态排序方向,然后手动排序children。还需要一个变量来手动跟踪该列的排序方向。相关代码:
self.lastSortDir = 1
self.treeWidget.header().sectionClicked.connect(self.headerSectionClicked)
def headerSectionClicked(self, colId):
flip = {0:1, 1:0}
if colId == 0:
self.treeWidget.header().setSortIndicator(0,1)
sortDir = flip[self.lastSortDir]
for gId in self.groupWidgets:
self.groupWidgets[gId].sortChildren(0, sortDir)
self.lastSortDir = sortDir
这仍然不理想,主要原因有两个:
每次按第一列排序时,都会执行两种排序,一种是强制静态方向,另一种是children。可能是更大数据集的问题,而且不够优雅。
第一列的排序指示器将卡住显示静态排序方向,而不是 children 排序所依据的实际方向。主要只是一个外观问题,但它仍然触发我。
不理想,但考虑到我尝试过的所有其他方法根本不起作用,我暂时接受它。我已经在上面浪费了足够多的时间,不完美但能工作总比完美但不能工作要好。
最简单的解决方案是创建 QTreeWidgetItem
的子类并重新实现其 __lt__
方法,使其始终 returns False
。 Qt 使用稳定的排序算法,所以这意味着项目将始终保持其原始顺序。此子类应仅用于顶级项目。
这是一个似乎符合您的规范的工作演示:
import sys
from random import randint
from PyQt4 import QtCore, QtGui
class TreeWidgetItem(QtGui.QTreeWidgetItem):
def __lt__(self, other):
return False
class Window(QtGui.QTreeWidget):
def __init__(self):
super(Window, self).__init__()
self.setHeaderLabels('Name X Y'.split())
for text in 'One Two Three Four'.split():
parent = TreeWidgetItem([text])
for text in 'Red Blue Green Yellow'.split():
child = QtGui.QTreeWidgetItem([
text, str(randint(0, 9)), str(randint(0, 9))])
parent.addChild(child)
self.addTopLevelItem(parent)
self.expandAll()
self.setSortingEnabled(True)
self.sortByColumn(0, QtCore.Qt.AscendingOrder)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(800, 100, 320, 400)
window.show()
sys.exit(app.exec_())
我有一个 2 级 QTreeWidget。顶层只有一列,仅用于对第二层进行分组,第二层包含实际的 multi-column 数据。
当我通过单击 header(或任何其他方式,真的)进行排序时,我只想对第二级进行排序,因为 top-level 项目在别处设置了固定顺序在应用程序中,应该不会受到影响。
如何实现?
您可以像这样使用 QTreeWidgetItem(s) 的 sortChildren
方法:
sort_column = 1
# Iterate top-level elements
for index in range(tree.topLevelItemCount()):
# Obtain the actual top-level item
top_level_item = tree.topLevelItem(index)
# Sort
top_level_item.sortChildren(sort_column)
请注意,如果您向某个项目添加新的子项,则需要再次对其进行排序(仅更改的项目)
编辑
Thanks, that's a step in the right direction, but the question now is how to override the default sort behavior. I'm doing self.treeWidget.header().sortIndicatorChanged.connect(self.sortChanged), but since that's a signal it just triggers after the widget does its regular sort, so in the end it has no effect - the top level items get sorted before the function is called
除非你去定制model/view,你可以尝试两种方法(我没有测试过):
1.覆盖 QTreeWidget
尝试用一些空操作替换 sortItems()
方法,但小部件可能有其他内部调用的私有方法。
2。覆盖根项 sortChildren()
排序可能是通过调用(隐藏的)根项目 sortChildren() 实现的,这将对顶级项目进行排序并调用这些项目的 sortChildren()。您也许能够弄清楚如何使用 rootIndex()
/setRootIndex()
get/set 此项,然后覆盖排序方法(例如 root.sortChildren = lambda c, o: None
)。
我根据
self.lastSortDir = 1
self.treeWidget.header().sectionClicked.connect(self.headerSectionClicked)
def headerSectionClicked(self, colId):
flip = {0:1, 1:0}
if colId == 0:
self.treeWidget.header().setSortIndicator(0,1)
sortDir = flip[self.lastSortDir]
for gId in self.groupWidgets:
self.groupWidgets[gId].sortChildren(0, sortDir)
self.lastSortDir = sortDir
这仍然不理想,主要原因有两个:
每次按第一列排序时,都会执行两种排序,一种是强制静态方向,另一种是children。可能是更大数据集的问题,而且不够优雅。
第一列的排序指示器将卡住显示静态排序方向,而不是 children 排序所依据的实际方向。主要只是一个外观问题,但它仍然触发我。
不理想,但考虑到我尝试过的所有其他方法根本不起作用,我暂时接受它。我已经在上面浪费了足够多的时间,不完美但能工作总比完美但不能工作要好。
最简单的解决方案是创建 QTreeWidgetItem
的子类并重新实现其 __lt__
方法,使其始终 returns False
。 Qt 使用稳定的排序算法,所以这意味着项目将始终保持其原始顺序。此子类应仅用于顶级项目。
这是一个似乎符合您的规范的工作演示:
import sys
from random import randint
from PyQt4 import QtCore, QtGui
class TreeWidgetItem(QtGui.QTreeWidgetItem):
def __lt__(self, other):
return False
class Window(QtGui.QTreeWidget):
def __init__(self):
super(Window, self).__init__()
self.setHeaderLabels('Name X Y'.split())
for text in 'One Two Three Four'.split():
parent = TreeWidgetItem([text])
for text in 'Red Blue Green Yellow'.split():
child = QtGui.QTreeWidgetItem([
text, str(randint(0, 9)), str(randint(0, 9))])
parent.addChild(child)
self.addTopLevelItem(parent)
self.expandAll()
self.setSortingEnabled(True)
self.sortByColumn(0, QtCore.Qt.AscendingOrder)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(800, 100, 320, 400)
window.show()
sys.exit(app.exec_())