如何按多列对QTreeWidget或QTableWidget进行排序(以及如何按数值对这些列进行排序?

问题描述 投票:0回答:1

假设我有一个QTreeWidget,它的两列字符串值可能会出现多次,而第三列的整数值分别是usernameproductquantity

然后,我希望能够通过用户名 产品对这些项目进行排序,并使共享此值的行通过quantity进行排序。我还希望能够将quantity的值作为数字值进行排序。这意味着值1、2和10将按该顺序排序,而不是像按字符串值排序时那样的1、10和2。如何在PyQt5中实现此功能?我在大约一年前寻找解决方案的地方偶然发现了这个问题的第一部分,但没有得到答案。我一直无法找到该问题,但此后已经为其写了答案,因此我打算对自己的问题发布答案。

由于我不确定我在哪里找到了该问题的一部分,如果已经在stackoverflow上发布了此问题,请随时将其标记为重复。

python pyqt qtablewidget qtreewidget
1个回答
0
投票

前言

我不是长答案的忠实拥护者,甚至不愿意长篇幅的难以阅读的代码迷,但是这些仍然是我想出的解决方案。

简单

第一段代码基本上是我最后使用的非常简化的解决方案。它更有效,更重要的是,它更易于阅读和理解。

from PyQt5.QtWidgets import QTreeWidget, QTreeWidgetItem

class SimpleMultisortTreeWidget(QTreeWidget):
    def __init__(self, *a, **k):
        super().__init__(*a, **k)
        self._csort_order = []
        self.header().sortIndicatorChanged.connect(self._sortIndicatorChanged)

    def _sortIndicatorChanged(self, n, order):
        try:
            self._csort_order.remove(n)
        except ValueError:
            pass
        self._csort_order.insert(0, n)
        self.sortByColumn(n, order)


class SimpleMultisortTreeWidgetItem(QTreeWidgetItem):

    def __lt__(self, other):
        corder = self.treeWidget()._csort_order
        return list(map(self .text, corder)) < \
               list(map(other.text, corder))

解决方案

我还需要将一些列排序为整数和小数。十进制类型对象以及各个列的Qt.SortOrder都很重要,因此下面的示例是我最后使用的示例。

这段代码不是很有效,但是可以完成工作。

from PyQt5.QtWidgets import QTreeWidget, QTreeWidgetItem


class MultisortTreeWidget(QTreeWidget):
    u"""
    QTreeWidget inheriting object, to be populated by MultisortTreeWidgetItems, that
    allows sorting of multiple columns with different Qt.SortOrder values.
    """
    def __init__(self, *arg, **kw):
        u"Pass on all positional and key word arguments to super().__init__"
        super().__init__(*arg, **kw)
        self._csort_corder = []
        self._csort_sorder = []
        self.header().sortIndicatorChanged.connect(self._sortIndicatorChanged)

    def _sortIndicatorChanged(self, col_n, order):
        """
        Update private attributes to reflect the current sort indicator.

        Connected to self.header().sortIndicatorChanged.

        :param col_n: int
            Sort indicator indicates column with this index to be the currently
            sorted column.
        :param order: Qt.SortOrder
            Sort indicator indicates this sort order. Qt enum, 1 or 0.
        """
        # The new and current column number may, or may not, already be in the
        # list of columns that is used as a reference for their individual prio-
        # rity.
        try:
            i = self._csort_corder.index(col_n)
        except ValueError:
            pass
        else:
            del self._csort_corder[i]
            del self._csort_sorder[i]
        # Force current column to have highest priority when sorting.
        self._csort_corder.insert(0, col_n)
        self._csort_sorder.insert(0, order)
        self._csort = list(zip(self._csort_corder,self._csort_sorder))
        # Resort items using the modified attributes.
        self.sortByColumn(col_n, order)


class MultisortTreeWidgetItem(QTreeWidgetItem):
    u"""
    QTreeWidgetÍtem inheriting objects that, when added to a MultisortTreeWidget,
    keeps the order of multiple columns at once. Also allows for column specific
    type sensitive sorting when class attributes SORT_COL_KEYS is set.
    """

    @staticmethod
    def SORT_COL_KEY(ins, c):
        return ins.text(c)

    SORT_COL_KEYS = []

    def __lt__(self, other):
        """
        Compare order between this and another MultisortTreeWidgetItem like
        instance.

        :param other: MultisortTreeWidgetItem.
            Object to compare against.
        :returns: bool
        """
        # Fall back on the default functionality if the parenting QTreeWidget is
        # not a subclass of MultiSortTreeWidget or the SortIndicator has not been
        # changed.
        try:
            csort = self.treeWidget()._csort
        except AttributeError:
            return super(MultisortTreeWidgetItem, self).__lt__(other)
        # Instead of comparing values directly, place them in two lists and
        # extend those lists with values from columns with known sort order.
        order = csort[0][1]
        left  = []
        right = []
        # Reverse sort order for columns not sorted according to the
        # current sort order indicator.
        for c,o in csort:
            try:
                key = self.SORT_COL_KEYS[c]
            except (KeyError, IndexError):
                key = self.SORT_COL_KEY
            #  Reverse sort order for columns not sorted according to the
            # current sort order indicator.
            if o == order:
                left .append(key(self ,c))
                right.append(key(other,c))
            else:
                left .append(key(other,c))
                right.append(key(self ,c))
        return left < right

用法

上述MultisortTreeWidgetItem类的SORT_COL_KEY和SORT_COL_KEYS类属性还允许使用self.text(N)返回的值以外的其他值,例如self.data()返回的列表。

下面的示例将第一列的行中的文本按整数排序,并按self.data()返回的列表中的相应对象对第三列的行进行排序。所有其他列均按item.text()值排序,并按字符串排序。

class UsageExampleItem(MultisortTreeWidgetItem):

    SORT_COL_KEYS = {
        0: lambda item, col: int(item.text(col)),
        2: lambda item, col: item.data()[col],  
        5: lambda item, col: int(item.text(col) or 0) # Empty strings defaults to 0.
    }

创建一个MultisortTreeWidget对象并将其添加到布局,然后创建UsageExampleItems并将它们添加到MultiSortTreeWidget。

此解决方案“记住”以前使用的列和排序顺序。因此,要主要按第一列进行排序,并让具有相同值的行按秒列进行排序,只需单击第二列的标题项,然后单击第一列的标题项。

© www.soinside.com 2019 - 2024. All rights reserved.