假设我有一个QTreeWidget,它的两列字符串值可能会出现多次,而第三列的整数值分别是 username , product 和数量。
然后,我希望能够按用户名 或 产品对这些项目进行排序,并使具有此值的行进行排序通过 quantity 。
我还希望能够将 quantity 的值作为数字值进行排序。这意味着值1、2和10应该按该顺序排序,而不是像按字符串值进行排序那样的1、10和2。
如何在PyQt5中实现这一点?
答案 0 :(得分:1)
我不是很喜欢长答案的人,甚至也不愿喜欢那些难于阅读的长篇文章,但这仍然是我想出的解决方案。
第一部分代码基本上是我最后使用的非常简化的解决方案。它更有效,更重要的是,它更易于阅读和理解。
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))
我还需要将一些列作为整数和decimal.Decimal
类型的对象以及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):
u"""
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):
u"""
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 = []
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
例如,上述SORT_COL_KEY
类的静态方法SORT_COL_KEYS
和MultisortTreeWidgetItem
类属性还允许使用除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中。
此解决方案“记住”以前使用的列和排序顺序。因此,要主要按第一列进行排序,并让具有相同值的行按秒列进行排序,只需单击第二列的标题项,然后单击第一列的标题项即可。