kzsnyk
kzsnyk

Reputation: 2211

QSortFilterProxyModel and dynamicSortFilter property

I am using a QSortFilterProxyModel and the dynamicSortFilter property does not work.

// main.cpp
QSortFilterProxyModel carsProxyModel;
carsProxyModel.setSourceModel(&carsModel);
carsProxyModel.setDynamicSortFilter(false); // whatever its true or false, it does not change the behavior

On the QML side, i have 2 ListView, one for the source model and one for the proxy and they display the list of cars in the same way.

When the source model is reversed :

// carmodel.cpp
void CarModel::reverseModel()
{
    beginResetModel();
    std::reverse(m_carItems.begin(), m_carItems.end());
    endResetModel();
}

Both views shows the reversed list of cars and it is what i expect when the dynamicSortFilter is true but not when its false (https://doc.qt.io/qt-5/qsortfilterproxymodel.html#dynamicSortFilter-prop), actually both values always leads to a change in the proxy view.

Maybe i am not using the right property but i cannot see something else so far.

When the source model is reversed - i don't want to see the change in the proxy view, only in the source view. Is this possible ?

Hope this question is clear, i have tried to extract the most important parts because the code base is too long to be posted here.

Thank you.

EDIT :

A small example :

if the proxy view is sorted by "car brand" (a QString), then reversing the source model will reverse the order of cars with same brand but the sort or filtering is preserved generally (for cars of different brands)

Upvotes: 4

Views: 2227

Answers (2)

king_nak
king_nak

Reputation: 11513

As @ypnos mentioned, dynamic sorting only affects when the proxy's sort is called. If it is true, it will be automatically called whenever the source model changes. It does not affect how the objects are sorted; this is up to your implementation

However, I read in you question that you have 2 views on 1 list, that need to be sorted differently. I suggest you use 2 distinct QProxyFilterModels, one for each view, and don't handle sorting in the base model at all.

The "reverse" model could simply invert the order of the original list by mapping the items in reverse order:

QModelIndex ReverseProxyModel::mapFromSource(const QModelIndex &sourceIndex) const {
    if (reversed) {
        return createIndex(sourceModel()->rowCount() - sourceIndex.row(), sourceIndex.column());
    } else {
        return createIndex(sourceModel()->rowCount(), sourceIndex.column());
    }
}

// Similar for mapToSource

Upvotes: 3

ypnos
ypnos

Reputation: 52317

So the issue at hands here is that you would like to control the total order of elements, not just how they are ordered according to DisplayRole. You need to make the proxy model aware of your total order which includes multiple attributes of the items.

As is explained in the documentation:

Custom sorting behavior is achieved by subclassing QSortFilterProxyModel and reimplementing lessThan(), which is used to compare items.

The dynamic sorting property is not doing what you believe it is doing. Actually it is a trigger to sort() on the proxy model whenever the source model changes. But the default sorter for QStrings will not change the order of items that look the same to it (it is a so-called stable sorter). Even if it were not a stable sorter, you would get basically random shuffling of the items at best after each source model change.

Note that you cannot prevent changes in the source order to be propagated through the proxy directly; you need to suppress them by providing a sorter that completely controls the order. So lessThan() would take into consideration car brand, make, color, etc. Alternatively, you could store the initial index of each item and use that as a secondary key in sorting after the brand. So basically:

if (a.brand == b.brand)
    return a.index < b.index;
return a.brand < b.brand;

Upvotes: 3

Related Questions