filterAcceptsRow from QSortFilterProxyModel is not called when item state of source model changed
-
Hi,
I have custom model
H5Model
inherited fromQAbstractItemModel
.
Some items from this custom model is checkable.
I display this model inQTreeView_0
but when user check item in this custom model I need to display that item in custom proxy modelH5ProxyModel
inherited fromQSortFilterProxyModel
and this custom proxy model I display inQTreeView_1
.H5ProxyModel.h
#ifndef H5PROXYMODEL_H #define H5PROXYMODEL_H #include <QSortFilterProxyModel> class H5ProxyModel : public QSortFilterProxyModel { Q_OBJECT public: H5ProxyModel(QObject *parent = nullptr); protected: virtual bool filterAcceptsRow( int source_row, const QModelIndex &source_parent) const override; }; #endif // H5PROXYMODEL_H
H5ProxyModel.cpp
#include "h5proxymodel.h" #include <h5item.h> H5ProxyModel::H5ProxyModel(QObject *parent) { } bool H5ProxyModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent) const{ QModelIndex index = sourceModel()->index(source_row, 0, source_parent); if (!index.isValid()) return false; H5Item *item = static_cast<H5Item*>(index.internalPointer()); if (item->isRoot()) return true; return item->isChecked(); }
But when I change the checkstate of item from source model it doesn't invoke
bool H5ProxyModel::filterAcceptsRow()
.
Source model works like this when checkstate is changed:bool H5Model::setData(const QModelIndex &index, const QVariant &value, int role){ if (!index.isValid()) return false; H5Item *item = getItem(index); if (role == Qt::EditRole){ bool result = item->setData(value.toString()); emit dataChanged(index, index, {Qt::EditRole}); return result; } else if (role == Qt::DisplayRole){ bool result = item->setData(value.toString()); emit dataChanged(index, index, {Qt::DisplayRole}); return result; } else if (role == Qt::CheckStateRole){ item->setChecked(value.toBool()); emit dataChanged(index, index, {Qt::CheckStateRole}); emit itemStateChanged(item, value.toBool()); return true; } return false; }
-
Your testcase is wrong - you can't simply cast the internal pointer to a QStandardItem. You need
auto sm = static_cast<QStandardItemModel*>(sourceModel());
QStandardItem* item = sm->itemFromIndex(index);Then it works as expected. The only thing is that the children are only visible when the parent was checked once but this is an implementation problem in your filterAcceptsRow() function.
btw: setDynamicSortFilter() is not needed as I saw now
-
@gde23
invalidate()
is a member ofQSortFilterProxyModel
In my case in first view I display my custom model where I change the checkstate. But when I do this then it doesn't invokefilterAcceptsRow()
from my proxy model.In the picture above:
- in the left view my custom model where I change the checkstate
- in the right view my proxy model where should be displayed checked item from left view
So I don't understand how to use
invalidate()
-
Did you also enable dynamic filtering?
-
@Christian-Ehrlicher I just Enabled it but that didn't help... When I change checkstate in left view it doesnt invoke
filterAcceptsRow()
-
Please provide a fully minimal and compilable example so we can see what's going wrong.
-
@Christian-Ehrlicher I just created example based on
QStandardItemModel()
. It just beacause my real model depends on external library. But anyway this example could help me to understand how to "connect" checked items with proxy model
Here is the zipped project:
https://yadi.sk/d/Kft2dNpTEuTEeQ -
Your testcase is wrong - you can't simply cast the internal pointer to a QStandardItem. You need
auto sm = static_cast<QStandardItemModel*>(sourceModel());
QStandardItem* item = sm->itemFromIndex(index);Then it works as expected. The only thing is that the children are only visible when the parent was checked once but this is an implementation problem in your filterAcceptsRow() function.
btw: setDynamicSortFilter() is not needed as I saw now
-
@Christian-Ehrlicher thank you!
Now I'm trying to overcome the thing that children are not displayed if their parent is not checked -
@Please_Help_me_D You have to adjust your filterAcceptRow function to check for the children if one is checked.
-
@Christian-Ehrlicher
filterAcceptsRow
doesn't get called when I check the child from unchecked parent. And when I launch the app all children are UnChecked.
Or I misunderstand something?
On the picture I checked a child butfilterAcceptsRow
was not called
-
I just understood that
filterAcceptsRow
get invoked only if parent ofindex
is displayed in proxy model. Oherwise proxy model thinks that it is not worth to insert row in proxy model.
I can't understand what method ofQSortFilterProxyModel
I should rewrite to make the proxy to add each checked items regardless of its parent is shown or not.
Does anybody know? -
@Please_Help_me_D said in filterAcceptsRow from QSortFilterProxyModel is not called when item state of source model changed:
Does anybody know?
You can emit a dataChanged() signal for the parents to trigger a recheck.
-
I just understood that filterAcceptsRow get invoked only if parent of index is displayed in proxy model
I truly do not know if the following has any relevance to your situation. But it only takes 5 seconds to try, and you can throw away if it makes no difference.
Qt introduced https://doc.qt.io/qt-5/qsortfilterproxymodel.html#recursiveFilteringEnabled-prop. The default is false. Try setting it to true and see whether that causes it to evaluate
filterAcceptsRow()
against whatever node you are saying it does not currently look at? -
@Christian-Ehrlicher I'm thinking about this but I'm trying to avoid sending items that were not changed via dataChanged() signal.
@JonB Yes I've already tried it but this doesn't solve my task. It slightly changes the behaviour of proxy model but not in a way I expect it to be -
I decided to make the following:
I'm going to make all the items (except the root) checkable. When I change checkstate of a item toQt::Checked
then this item also changes checkState of its parent toQt::PartiallyChecked
.
So this chain should work but I have not finished it yet