Skip to content

Commit b5f6a85

Browse files
committed
Add a way to filter only rows or columns in QSortFilterProxyModel
If we want to filter away a column without changing the filtering for the rows calling invalidateFilter() is wasteful because it will call filterAcceptsRow() for all rows even though that is not needed. This commit add two functions, invalidateRowsFilter() and invalidateColumnsFilter() that work the same way as invalidateFilter() except that they will invoke respectively only filterAcceptsRow() and filterAcceptsColumn(). Change-Id: Ib4351cf08c229bd97bbbfee6da92397dca579a84 Reviewed-by: David Faure <david.faure@kdab.com>
1 parent 963c47a commit b5f6a85

File tree

4 files changed

+157
-16
lines changed

4 files changed

+157
-16
lines changed

src/corelib/itemmodels/qsortfilterproxymodel.cpp

Lines changed: 73 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,12 @@ class QSortFilterProxyModelPrivate : public QAbstractProxyModelPrivate
274274
Q_DECLARE_PUBLIC(QSortFilterProxyModel)
275275

276276
public:
277+
enum class Direction {
278+
Rows = 1,
279+
Columns = 2,
280+
All = Rows | Columns
281+
};
282+
277283
struct Mapping {
278284
QVector<int> source_rows;
279285
QVector<int> source_columns;
@@ -413,7 +419,7 @@ class QSortFilterProxyModelPrivate : public QAbstractProxyModelPrivate
413419
void update_persistent_indexes(const QModelIndexPairList &source_indexes);
414420

415421
void filter_about_to_be_changed(const QModelIndex &source_parent = QModelIndex());
416-
void filter_changed(const QModelIndex &source_parent = QModelIndex());
422+
void filter_changed(Direction dir, const QModelIndex &source_parent = QModelIndex());
417423
QSet<int> handle_filter_changed(
418424
QVector<int> &source_to_proxy, QVector<int> &proxy_to_source,
419425
const QModelIndex &source_parent, Qt::Orientation orient);
@@ -431,6 +437,11 @@ class QSortFilterProxyModelPrivate : public QAbstractProxyModelPrivate
431437

432438
typedef QHash<QModelIndex, QSortFilterProxyModelPrivate::Mapping *> IndexMap;
433439

440+
static bool operator&(QSortFilterProxyModelPrivate::Direction a, QSortFilterProxyModelPrivate::Direction b)
441+
{
442+
return int(a) & int(b);
443+
}
444+
434445
void QSortFilterProxyModelPrivate::_q_sourceModelDestroyed()
435446
{
436447
QAbstractProxyModelPrivate::_q_sourceModelDestroyed();
@@ -1269,14 +1280,14 @@ void QSortFilterProxyModelPrivate::filter_about_to_be_changed(const QModelIndex
12691280
Updates the proxy model (adds/removes rows) based on the
12701281
new filter.
12711282
*/
1272-
void QSortFilterProxyModelPrivate::filter_changed(const QModelIndex &source_parent)
1283+
void QSortFilterProxyModelPrivate::filter_changed(Direction dir, const QModelIndex &source_parent)
12731284
{
12741285
IndexMap::const_iterator it = source_index_mapping.constFind(source_parent);
12751286
if (it == source_index_mapping.constEnd())
12761287
return;
12771288
Mapping *m = it.value();
1278-
QSet<int> rows_removed = handle_filter_changed(m->proxy_rows, m->source_rows, source_parent, Qt::Vertical);
1279-
QSet<int> columns_removed = handle_filter_changed(m->proxy_columns, m->source_columns, source_parent, Qt::Horizontal);
1289+
const QSet<int> rows_removed = (dir & Direction::Rows) ? handle_filter_changed(m->proxy_rows, m->source_rows, source_parent, Qt::Vertical) : QSet<int>();
1290+
const QSet<int> columns_removed = (dir & Direction::Columns) ? handle_filter_changed(m->proxy_columns, m->source_columns, source_parent, Qt::Horizontal) : QSet<int>();
12801291

12811292
// We need to iterate over a copy of m->mapped_children because otherwise it may be changed by other code, invalidating
12821293
// the iterator it2.
@@ -1290,7 +1301,7 @@ void QSortFilterProxyModelPrivate::filter_changed(const QModelIndex &source_pare
12901301
indexesToRemove.push_back(i);
12911302
remove_from_mapping(source_child_index);
12921303
} else {
1293-
filter_changed(source_child_index);
1304+
filter_changed(dir, source_child_index);
12941305
}
12951306
}
12961307
QVector<int>::const_iterator removeIt = indexesToRemove.constEnd();
@@ -2605,7 +2616,7 @@ void QSortFilterProxyModel::setFilterRegExp(const QRegExp &regExp)
26052616
Q_D(QSortFilterProxyModel);
26062617
d->filter_about_to_be_changed();
26072618
d->filter_data.setRegExp(regExp);
2608-
d->filter_changed();
2619+
d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows);
26092620
}
26102621

26112622
#if QT_CONFIG(regularexpression)
@@ -2634,7 +2645,7 @@ void QSortFilterProxyModel::setFilterRegularExpression(const QRegularExpression
26342645
Q_D(QSortFilterProxyModel);
26352646
d->filter_about_to_be_changed();
26362647
d->filter_data.setRegularExpression(regularExpression);
2637-
d->filter_changed();
2648+
d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows);
26382649
}
26392650
#endif
26402651

@@ -2657,7 +2668,7 @@ void QSortFilterProxyModel::setFilterKeyColumn(int column)
26572668
Q_D(QSortFilterProxyModel);
26582669
d->filter_about_to_be_changed();
26592670
d->filter_column = column;
2660-
d->filter_changed();
2671+
d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows);
26612672
}
26622673

26632674
/*!
@@ -2683,7 +2694,7 @@ void QSortFilterProxyModel::setFilterCaseSensitivity(Qt::CaseSensitivity cs)
26832694
return;
26842695
d->filter_about_to_be_changed();
26852696
d->filter_data.setCaseSensitivity(cs);
2686-
d->filter_changed();
2697+
d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows);
26872698
emit filterCaseSensitivityChanged(cs);
26882699
}
26892700

@@ -2754,7 +2765,7 @@ void QSortFilterProxyModel::setFilterRegExp(const QString &pattern)
27542765
QRegExp rx(pattern);
27552766
rx.setCaseSensitivity(d->filter_data.caseSensitivity());
27562767
d->filter_data.setRegExp(rx);
2757-
d->filter_changed();
2768+
d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows);
27582769
}
27592770

27602771
#if QT_CONFIG(regularexpression)
@@ -2775,7 +2786,7 @@ void QSortFilterProxyModel::setFilterRegularExpression(const QString &pattern)
27752786
d->filter_about_to_be_changed();
27762787
QRegularExpression rx(pattern);
27772788
d->filter_data.setRegularExpression(rx);
2778-
d->filter_changed();
2789+
d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows);
27792790
}
27802791
#endif
27812792

@@ -2791,7 +2802,7 @@ void QSortFilterProxyModel::setFilterWildcard(const QString &pattern)
27912802
d->filter_about_to_be_changed();
27922803
QRegExp rx(pattern, d->filter_data.caseSensitivity(), QRegExp::Wildcard);
27932804
d->filter_data.setRegExp(rx);
2794-
d->filter_changed();
2805+
d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows);
27952806
}
27962807

27972808
/*!
@@ -2806,7 +2817,7 @@ void QSortFilterProxyModel::setFilterFixedString(const QString &pattern)
28062817
d->filter_about_to_be_changed();
28072818
QRegExp rx(pattern, d->filter_data.caseSensitivity(), QRegExp::FixedString);
28082819
d->filter_data.setRegExp(rx);
2809-
d->filter_changed();
2820+
d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows);
28102821
}
28112822

28122823
/*!
@@ -2886,7 +2897,7 @@ void QSortFilterProxyModel::setFilterRole(int role)
28862897
return;
28872898
d->filter_about_to_be_changed();
28882899
d->filter_role = role;
2889-
d->filter_changed();
2900+
d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows);
28902901
emit filterRoleChanged(role);
28912902
}
28922903

@@ -2913,7 +2924,7 @@ void QSortFilterProxyModel::setRecursiveFilteringEnabled(bool recursive)
29132924
return;
29142925
d->filter_about_to_be_changed();
29152926
d->filter_recursive = recursive;
2916-
d->filter_changed();
2927+
d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows);
29172928
emit recursiveFilteringEnabledChanged(recursive);
29182929
}
29192930

@@ -2964,11 +2975,57 @@ void QSortFilterProxyModel::filterChanged()
29642975
(e.g. filterAcceptsRow()), and your filter parameters have changed.
29652976
29662977
\sa invalidate()
2978+
\sa invalidateColumnsFilter()
2979+
\sa invalidateRowsFilter()
29672980
*/
29682981
void QSortFilterProxyModel::invalidateFilter()
29692982
{
29702983
Q_D(QSortFilterProxyModel);
2971-
d->filter_changed();
2984+
d->filter_changed(QSortFilterProxyModelPrivate::Direction::All);
2985+
}
2986+
2987+
/*!
2988+
\since 6.0
2989+
2990+
Invalidates the current filtering for the columns.
2991+
2992+
This function should be called if you are implementing custom filtering
2993+
(by filterAcceptsColumn()), and your filter parameters have changed.
2994+
This differs from invalidateFilter() in that it will not invoke
2995+
filterAcceptsRow(), but only filterAcceptsColumn(). You can use this
2996+
instead of invalidateFilter() if you want to hide or show a column where
2997+
the rows don't change.
2998+
2999+
\sa invalidate()
3000+
\sa invalidateFilter()
3001+
\sa invalidateRowsFilter()
3002+
*/
3003+
void QSortFilterProxyModel::invalidateColumnsFilter()
3004+
{
3005+
Q_D(QSortFilterProxyModel);
3006+
d->filter_changed(QSortFilterProxyModelPrivate::Direction::Columns);
3007+
}
3008+
3009+
/*!
3010+
\since 6.0
3011+
3012+
Invalidates the current filtering for the rows.
3013+
3014+
This function should be called if you are implementing custom filtering
3015+
(by filterAcceptsRow()), and your filter parameters have changed.
3016+
This differs from invalidateFilter() in that it will not invoke
3017+
filterAcceptsColumn(), but only filterAcceptsRow(). You can use this
3018+
instead of invalidateFilter() if you want to hide or show a row where
3019+
the columns don't change.
3020+
3021+
\sa invalidate()
3022+
\sa invalidateFilter()
3023+
\sa invalidateColumnsFilter()
3024+
*/
3025+
void QSortFilterProxyModel::invalidateRowsFilter()
3026+
{
3027+
Q_D(QSortFilterProxyModel);
3028+
d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows);
29723029
}
29733030

29743031
/*!

src/corelib/itemmodels/qsortfilterproxymodel.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ public Q_SLOTS:
143143
QT_DEPRECATED_X("Use QSortFilterProxyModel::invalidateFilter") void filterChanged();
144144
#endif
145145
void invalidateFilter();
146+
void invalidateRowsFilter();
147+
void invalidateColumnsFilter();
146148

147149
public:
148150
using QObject::parent;

tests/auto/corelib/itemmodels/qsortfilterproxymodel_common/tst_qsortfilterproxymodel.cpp

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4940,4 +4940,84 @@ void tst_QSortFilterProxyModel::filterAndInsertRow()
49404940
}
49414941
}
49424942

4943+
void tst_QSortFilterProxyModel::invalidateColumnsOrRowsFilter()
4944+
{
4945+
class FilterProxy : public QSortFilterProxyModel
4946+
{
4947+
public:
4948+
FilterProxy()
4949+
{}
4950+
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override
4951+
{
4952+
rowFiltered++;
4953+
4954+
if (sourceModel()->data(sourceModel()->index(source_row, 0, source_parent)).toString() == QLatin1String("A1"))
4955+
return !rejectA1;
4956+
return true;
4957+
}
4958+
bool filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const override
4959+
{
4960+
Q_UNUSED(source_column)
4961+
Q_UNUSED(source_parent)
4962+
4963+
columnFiltered++;
4964+
return true;
4965+
}
4966+
4967+
mutable int rowFiltered = 0;
4968+
mutable int columnFiltered = 0;
4969+
bool rejectA1 = false;
4970+
4971+
using QSortFilterProxyModel::invalidateFilter;
4972+
using QSortFilterProxyModel::invalidateRowsFilter;
4973+
using QSortFilterProxyModel::invalidateColumnsFilter;
4974+
};
4975+
QStandardItemModel model(10, 4);
4976+
for (int i = 0; i < model.rowCount(); ++i) {
4977+
for (int j = 0; j < model.columnCount(); ++j) {
4978+
model.setItem(i, j, new QStandardItem(QString('A' + j) + QString::number(i + 1)));
4979+
model.item(i, 0)->appendColumn({ new QStandardItem(QString("child col %0").arg(j)) });
4980+
}
4981+
}
4982+
FilterProxy proxy;
4983+
proxy.setSourceModel(&model);
4984+
4985+
QTreeView view;
4986+
view.setModel(&proxy);
4987+
view.expandAll();
4988+
4989+
QCOMPARE(proxy.rowFiltered, 20); //10 parents + 10 children
4990+
QCOMPARE(proxy.columnFiltered, 44); // 4 parents + 4 * 10 children
4991+
4992+
proxy.rowFiltered = proxy.columnFiltered = 0;
4993+
proxy.invalidateFilter();
4994+
4995+
QCOMPARE(proxy.rowFiltered, 20);
4996+
QCOMPARE(proxy.columnFiltered, 44);
4997+
4998+
proxy.rowFiltered = proxy.columnFiltered = 0;
4999+
proxy.invalidateRowsFilter();
5000+
5001+
QCOMPARE(proxy.rowFiltered, 20);
5002+
QCOMPARE(proxy.columnFiltered, 0);
5003+
5004+
proxy.rowFiltered = proxy.columnFiltered = 0;
5005+
proxy.invalidateColumnsFilter();
5006+
5007+
QCOMPARE(proxy.rowFiltered, 0);
5008+
QCOMPARE(proxy.columnFiltered, 44);
5009+
5010+
QCOMPARE(proxy.rowCount(), 10);
5011+
proxy.rejectA1 = true;
5012+
proxy.rowFiltered = proxy.columnFiltered = 0;
5013+
proxy.invalidateRowsFilter();
5014+
QCOMPARE(proxy.rowCount(), 9);
5015+
QCOMPARE(proxy.rowFiltered, 19); // it will not check the child row of A1
5016+
5017+
proxy.rowFiltered = proxy.columnFiltered = 0;
5018+
proxy.setRecursiveFilteringEnabled(true); // this triggers invalidateRowsFilter()
5019+
QCOMPARE(proxy.rowCount(), 10);
5020+
QCOMPARE(proxy.rowFiltered, 20);
5021+
}
5022+
49435023
#include "tst_qsortfilterproxymodel.moc"

tests/auto/corelib/itemmodels/qsortfilterproxymodel_common/tst_qsortfilterproxymodel.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ private slots:
159159
void removeIntervals_data();
160160
void removeIntervals();
161161

162+
void invalidateColumnsOrRowsFilter();
163+
162164
protected:
163165
void buildHierarchy(const QStringList &data, QAbstractItemModel *model);
164166
void checkHierarchy(const QStringList &data, const QAbstractItemModel *model);

0 commit comments

Comments
 (0)