如何按多列对 QTreeWidget 或 QTableWidget 进行排序(以及如何将这些列排序为数值)?
How do I sort sort a QTreeWidget or QTableWidget by multiple columns (and how do I sort these columns as numerical values)?
假设我有一个包含三列的 QTreeWidget。其中两个具有字符串值,第三个具有整数值,所有这些都可能在每一列中出现不止一次。
- 用户名 (str)
- 产品 (str)
- 数量(整数)
然后我希望能够按 username 或 product 对这些项目进行排序,并让共享这些值的行按数量.
排序
附带说明一下,我还需要能够将假设 quantity
的值排序为数值。
假设我在前面的示例中按数量排序了三行,并且这些行具有这些值:
- 1
- 2
- 10
然后我希望这些行以相同的顺序排序,而不是如果它们被排序为字符串值:
- 1
- 10
- 2
如何使用 PyQt5 实现这种组合?
前言
我不喜欢冗长的答案,甚至不喜欢冗长的难以阅读的代码,但这些 none 我想出的解决方案越少前段时间自己在寻找这个问题的答案时。
简单
第一段代码基本上是我最终使用的非常简化的解决方案。它更高效,更重要的是,更易于阅读和理解。
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))
扩展
我也有需要...
- 将某些列排序为整数 and/or
decimal.Decimal
输入 objects.
- 混合升序和降序(即注意每列的
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):
r"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):
r"""
Update private attributes to reflect the current sort indicator.
(Connected to self.header().sortIndicatorChanged)
:param col_n: Sort indicator indicates column with this index to be
the currently sorted column.
:type col_n: int
:param order: New sort order indication. Qt enum, 1 or 0.
:type order: Qt.SortOrder
"""
# 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
# priority.
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):
r"""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):
r"""Compare order between this and another MultisortTreeWidgetItem like
instance.
:param other: Object to compare against.
:type other: MultisortTreeWidgetItem.
: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 = []
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
class的静态方法SORT_COL_KEY
和SORT_COL_KEYS
class属性也允许[=返回的值以外的其他值self.text(N)
被使用,例如 self.data()
.
返回的列表
以下示例将第一列的行中的文本按整数排序,并按 self.data()
返回的列表中相应的 object 对第三列的行进行排序。所有其他列按 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 str defaults to 0
}
创建 MultisortTreeWidget
object 并将其添加到布局,然后创建 UsageExampleItems
并将它们添加到 MultisortTreeWidget
。
此解决方案“记住”以前使用的列和排序顺序。因此,如果您想要根据第一列中的值对 UsageExampleItems
小部件中的项目进行排序,并且让共享一个值的行彼此之间按照第二列进行排序,那么您首先要单击 header 第二列的项目,然后继续单击第一列的 header 项目。
假设我有一个包含三列的 QTreeWidget。其中两个具有字符串值,第三个具有整数值,所有这些都可能在每一列中出现不止一次。
- 用户名 (str)
- 产品 (str)
- 数量(整数)
然后我希望能够按 username 或 product 对这些项目进行排序,并让共享这些值的行按数量.
排序附带说明一下,我还需要能够将假设 quantity
的值排序为数值。
假设我在前面的示例中按数量排序了三行,并且这些行具有这些值:
- 1
- 2
- 10
然后我希望这些行以相同的顺序排序,而不是如果它们被排序为字符串值:
- 1
- 10
- 2
如何使用 PyQt5 实现这种组合?
前言
我不喜欢冗长的答案,甚至不喜欢冗长的难以阅读的代码,但这些 none 我想出的解决方案越少前段时间自己在寻找这个问题的答案时。
简单
第一段代码基本上是我最终使用的非常简化的解决方案。它更高效,更重要的是,更易于阅读和理解。
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))
扩展
我也有需要...
- 将某些列排序为整数 and/or
decimal.Decimal
输入 objects. - 混合升序和降序(即注意每列的
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):
r"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):
r"""
Update private attributes to reflect the current sort indicator.
(Connected to self.header().sortIndicatorChanged)
:param col_n: Sort indicator indicates column with this index to be
the currently sorted column.
:type col_n: int
:param order: New sort order indication. Qt enum, 1 or 0.
:type order: Qt.SortOrder
"""
# 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
# priority.
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):
r"""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):
r"""Compare order between this and another MultisortTreeWidgetItem like
instance.
:param other: Object to compare against.
:type other: MultisortTreeWidgetItem.
: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 = []
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
class的静态方法SORT_COL_KEY
和SORT_COL_KEYS
class属性也允许[=返回的值以外的其他值self.text(N)
被使用,例如 self.data()
.
以下示例将第一列的行中的文本按整数排序,并按 self.data()
返回的列表中相应的 object 对第三列的行进行排序。所有其他列按 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 str defaults to 0
}
创建 MultisortTreeWidget
object 并将其添加到布局,然后创建 UsageExampleItems
并将它们添加到 MultisortTreeWidget
。
此解决方案“记住”以前使用的列和排序顺序。因此,如果您想要根据第一列中的值对 UsageExampleItems
小部件中的项目进行排序,并且让共享一个值的行彼此之间按照第二列进行排序,那么您首先要单击 header 第二列的项目,然后继续单击第一列的 header 项目。