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.s‌​ortChanged), 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

这仍然不理想,主要原因有两个:

  1. 每次按第一列排序时,都会执行两种排序,一种是强制静态方向,另一种是children。可能是更大数据集的问题,而且不够优雅。

  2. 第一列的排序指示器将卡住显示静态排序方向,而不是 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_())