implementing sorting with QSortFilterProxyModel
-
Hi all -
I've implemented a QSortFilterProxyModel (on a QAbstractListModel) for the purpose of filtering, which it does just fine. Now, I want to implement a sort of the filtered data. It's not clear to me from the docs how I cause this to happen. The docs state:
Custom sorting behavior is achieved by subclassing QSortFilterProxyModel and reimplementing lessThan(), which is used to compare items.
I've done this, but my lessThan() is never called.What do I need to do in order to "activate" sorting? From the examples, it doesn't appear that I should actually invoke the sort() method directly.
EDIT:
Here's the signatures of my reimplmented functions:
protected: bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
Thanks...
-
@mzimmers If your model is to be consumed in QML, it most likely has a single column and multiple roles if it's meant to be consumed by a ListView (multiple columns shouldn't be a thing unless you are implementing a spreadsheet, change my mind).
call
setDynamicSortFilter(true)
andsort(0)
once at the beginning and you should be good to go. YourlessThan
function will be called when needed. Or use my project on GitHub like mentioned above. It hasn't been updated for a while but it is still relevant. Take a look at the available forks too, there might be improvements there. -
@mzimmers said in implementing sorting with QSortFilterProxyModel:
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
If you want to sort a container you have to provide a compare operator. For QSortFilterProxyModel you have to provide the comparision through the lessThan() function
-
@Christian-Ehrlicher right -- my lessThan() function is currently empty (save for a qDebug() statement) but it should still be called, no?
-
@mzimmers said in implementing sorting with QSortFilterProxyModel:
but it should still be called, no?
It will be called as soon as you sort your view.
-
@Christian-Ehrlicher ahh...and, since (according to the docs) QAbstractItemModel::sort() does nothing by default, I guess I do have to implement my own sort algorithm?
-
@mzimmers said in implementing sorting with QSortFilterProxyModel:
I guess I do have to implement my own sort algorithm?
As already written you have to reimplement QSFPM::lessThan() if you want a custom sorting of your data.
-
@Christian-Ehrlicher I realize that. What I'm trying to understand is whether I need to re-implement sort() as well.
From the docs:
Sorts the model by column in the given order.
The base class implementation does nothing.The Qt-supplied examples don't seem to directly invoke a sort() function, and the docs say that it's called behind the scenes. So, I'm still not sure what all I need to supply in order to effect a sort. I do have my lessThan() function defined and I'll flesh it out once I figure out how it gets invoked.
-
@mzimmers said in implementing sorting with QSortFilterProxyModel:
What I'm trying to understand is whether I need to re-implement sort() as well.
If you don't want to use a QSortFilterProxyModel then yes.
-
@Christian-Ehrlicher we seem to be talking past each other...either that, or I'm especially dense this morning.
As stated in my first post, I do have a QSortFilterProxyModel set onto a QAbstractListModel. Here's the complete header:
class EquipmentValveProxy : public QSortFilterProxyModel { Q_OBJECT public: explicit EquipmentValveProxy(QObject *parent = nullptr); protected: bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; // void sort(int column, Qt::SortOrder = Qt::AscendingOrder) const; bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; };
When I try to use QSortFilterProxyModel::sort(), I get this error:
equipmentValveProxy.cpp:22:5: 'this' argument to member function 'sort' has type 'const EquipmentValveProxy', but function is not marked const qsortfilterproxymodel.h:157:10: 'sort' declared here
Am I supposed to implement my own sort() as a wrapper to QSortFilterProxyModel::sort()?
Thanks...
-
@mzimmers said in implementing sorting with QSortFilterProxyModel:
Am I supposed to implement my own sort() as a wrapper to QSortFilterProxyModel::sort()?
Why?
Again: reimplement lessThan() and then sort your view. Nothing more. -
I have re-implemented lessThan(). It's currently a stub, but I'd expect it would still get called, if I can figure out how to invoke a sort.
But my attempts to use QSortFilterProxyModel::sort() produce a the above compile-time error.
-
@mzimmers said in implementing sorting with QSortFilterProxyModel:
, if I can figure out how to invoke a sort.
already told you: It will be called as soon as you sort your view.
-
@Christian-Ehrlicher ahhhhhh...thank you for the emphasis; now I understand what you're saying. But...I don't know how to do that.
My view is a QML ListView. How does one effect a sort on that?
-
I don't use QML but with QList/Table/TreeView the sorting is done by clicking on the header item.
You can call QSFPM::sort() from c++ but don't know how/if it is possible directly in the ListView
-
@mzimmers
I would just start by GooglingQML QSortFilterProxyModel
. https://stackoverflow.com/questions/45563393/sort-the-data-of-a-qml-listview-by-using-a-qsortfilterproxymodel might be just what you need to understand from QML (though it might be too detailed), I don't know. Note that it talks about implementing thelessThan()
. That is all you have to do for aQSortFilterProxyModel
. Its own code will call that repeatedly in order to present its source model in sorted order to the outside world. -
@mzimmers Here is an exemple.Hope it helps
SortOnSecondModel.hclass SortOnSecondModel : public QSortFilterProxyModel
{
public:
SortOnSecondModel( QObject *parent = 0 );
protected:
bool lessThan( const QModelIndex &left, const QModelIndex &right ) const;
};SortOnSecondModel.cpp
bool SortOnSecondModel::lessThan( const QModelIndex &left,
const QModelIndex &right ) const
{
QString leftString = sourceModel()->data( left ).toString();
QString rightString = sourceModel()->data( right ).toString();
if( !leftString.isEmpty() )
leftString = leftString.mid( 1 );
if( !rightString.isEmpty() )
rightString = rightstring.mid( 1 );
return leftString < rightString;
} -
@Ronel_qtmaster thanks for the example. I haven't gotten around to implementing my lessThan() function, because I'm still trying to figure out how/where to invoke the sort() call. This is what I'm trying:
EquipmentValveProxy::EquipmentValveProxy(EquipmentModel *equipmentModel, QObject *parent) : QSortFilterProxyModel{parent}, m_equipmentModel(equipmentModel) { QObject::connect(m_equipmentModel, &EquipmentModel::modelUpdated, this, &EquipmentValveProxy::updateSorting); } void EquipmentValveProxy::updateSorting() { invalidate(); sort(0); // what should I use for a column? }
This is successful in that it gets me into my lessThan() function, currently stubbed as:
bool EquipmentValveProxy::lessThan(const QModelIndex &left, const QModelIndex &right) const { bool rc = false; QVariant leftData = sourceModel()->data(left); QVariant rightData = sourceModel()->data(right); return rc; }
Unfortunately, I'm still doing something wrong, because both leftData and rightData are invalid after assignments. How does this look to you?
Thanks...
-
@mzimmers said in implementing sorting with QSortFilterProxyModel:
To invoque the sort , you just need to call QTableView::setSortingEnabled( true );
In your EquipmentValveProxy class, you don't need any connect in the constructor, you only need to reimplement lessThan function.
Now you need to pass the model containing your data to the sorter class.
EquipmentValveProxy ::setSourceModel( &model );Then set the sorter class as model to your QTableView
QTableView::setModel(&your_sorter_class);And finally enable sorting using QTableView::setSortingEnabled( true );
-
@Ronel_qtmaster said in implementing sorting with QSortFilterProxyModel:
To invoque the sort , you just need to call QTableView::setSortingEnabled( true );
But...it's a QML ListView, not a C++ TableView.
-
@mzimmers Okay.Then you should consider porting your model to qml https://doc.qt.io/qt-6/qtquick-modelviewsdata-cppmodels.html