[PyQt] QSortFilterProxyModel::sort() not sorting on column types

J Barchan jnbarchan at gmail.com
Tue Feb 5 09:06:32 GMT 2019


On Fri, 1 Feb 2019 at 20:50, Maurizio Berti <maurizio.berti at gmail.com>
wrote:

> To correctly support sorting for non string values you need to provide
> your own method.
> Also, for numeric values ensure that the data is stored as a number and
> not as string. For QStandardItemModel you can achieve that by creating
> QStandardItems without any data value, and then set the numeric data using
> QStandardItem.setData(value, QtCore.Qt.DisplayValue); remember that the
> role must be specified, as the setData method of QStandardItem uses the
> UserRole + 1 role by default. This will solve the sorting for your float
> column.
>
> To sort by date, it's better to set the QDateTime value as a custom role
> (>=UserRole) and store the object as it is, while you can choose to set the
> displayed string once you create the QStandardItems or by using an item
> delegate, if you need a visual representation.
> After that just implement the lessThan() method of QSortFilterProxyModel,
> which allows to customize how the items are sorted whenever the sort()
> method is called.
>
> Supposing that you're using the second column for the QDateTime items and
> you're using a role DateRole = QtCore.Qt.UserRole + 1:
>
> class SortModel(QtCore.QSortFilterProxyModel):
>     def lessThan(self, left, right):
>         if left.column() == right.column() and left.column() == 1:
>             return left.data(DateRole) < right.data(DateRole)
>         return QtCore.QSortFilterProxyModel.lessThan(self, left, right)
>
> class MyWidget(QtWidgets.QWidget):
>     def __init__(self, *args, **kwargs):
>         QtWidgets.QWidget.__init__(self, *args, **kwargs)
>         [...]
>         self.sourceModel = QtGui.QStandardItemModel()
>         self.sortModel = SortModel()
>         self.someTable.setModel(self.sortModel)
>         self.sortModel.setSourceModel(self.sourceModel)
>
>     def appendFields(self):
>         floatItem = QtGui.QStandardItem()
>         floatItem.setData(self.getSomeFloatNumber(), QtCore.Qt.DisplayRole)
>         someDate = QtCore.QDateTime.currentDateTime()
>         item = QtGui.QStandardItem(someDate.toString())
>         item.setData(someDate, DateRole)
>         self.sourceModel.appendRow([floatItem, dateItem])
>
> I've tested it on an old 5.7.1, but from what you are experiencing it is
> not a bug, but the expected behavior, since you didn't correctly implement
> the sorting method and/or didn't provide a correct way for Qt to interpret
> data for its default sorting rules.
>
> Regards,
> Maurizio
>
>
>
> Il giorno ven 1 feb 2019 alle ore 10:45 J Barchan <jnbarchan at gmail.com>
> ha scritto:
>
>> I use Qt/PyQt 5.7, the standard distribution supplied for Ubuntu 18.04.
>>
>> I have a QSortFilterProxyModel wrapped around a QStandardItemModel as
>> its source model.  I try to use QSortFilterProxyModel::sort() to sort by
>> column values.
>>
>> I have a column of Python type datetime.date.  When I call the sort(),
>> there is no "warning" but *nothing happens*.  The sort does not
>> rearrange the items at all.  They are initially "randomly" ordered in rows,
>> and the rows simply retain their current order.  It does *not* even
>> reorder by string value.  It is as though it simply decides they are
>> "unorderable".
>>
>> When I sort instead by a Python str column they *do* get reordered.
>> FWIW, I have another column of Python type decimal.Decimal and sorting
>> by that too does nothing, even though I think that should be convertible to
>> float/double.  However, that may complicate matters so let's stick to
>> the datetime.date case, I am just mentioning it in case it's relevant.
>>
>> It took me a long time to realise where the problem lies.  I have a
>> workaround: I *explicitly* change all my values in the model from
>> datetime.date to QDate(value) and now it does sort.
>>
>> But my understanding/experience from other PyQt methods is that it does
>> this kind of Python type -> QVariant conversion for you behind the
>> scenes itself.  I shouldn't have to change my types or write special code.
>>
>> Is this a bug?  Is this in my PyQt 5.7 only?
>>
>> --
>> Kindest,
>> Jonathan
>> _______________________________________________
>> PyQt mailing list    PyQt at riverbankcomputing.com
>> https://www.riverbankcomputing.com/mailman/listinfo/pyqt
>>
>
>
> --
> È difficile avere una convinzione precisa quando si parla delle ragioni
> del cuore. - "Sostiene Pereira", Antonio Tabucchi
> http://www.jidesk.net
>

Thank you for replying, Maurizio.  It has taken me a few days to get back
to this.

While I have no argument with your detailed reply (I'm quite sure you know
much than I do), I do not understand *why* any of this necessary, and would
appreciate any clarification.

To correctly support sorting for non string values you need to provide your
> own method.
>

If I did not make it clear I would remind you of one of my findings.  I
start with Python datetime.date variables.  I populate the model via
setData(index,
dateValue) (no role specified, so EditRole).  At this point no sorting
happens.  However, all I have to do is change that to setData(index,
QDate(dateValue)) and it *does* then work, so no "need to provide your own
method".

Why is that?  From http://doc.qt.io/qt-5/qsortfilterproxymodel.html#lessThan
I am told that the sorting deals in QVariant types, and QMetaType::QDate is
among those it handles.  Now, you know much better than I, but everywhere
else I use PyQt it seems to convert between Python types and necessary
QVariant types invisibly and all is well, but not here?  Why not?

I'm also a little lost about QStandardItem vs QStandardItemModel, if that's
significant.  My code creates the (source) model as a QStandardItemModel.
That claims to use http://doc.qt.io/qt-5/qstandarditemmodel.html#setData,whose
default role is EditRole.  Nowhere do I create a QStandardItem explicitly,
whose http://doc.qt.io/qt-5/qstandarditem.html#setData uses default
role is UserRole
+ 1.  So now I'm not sure which I am using, though I would think only
QStandardItemModel?  Is this of relevance to my issue?

Thank you for any explanation.

-- 
Kindest,
Jonathan
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://www.riverbankcomputing.com/pipermail/pyqt/attachments/20190205/5c04af84/attachment-0001.html>


More information about the PyQt mailing list