Here is a more generic version of the QSortFilterProxyModel with a multicolumn filter implementation.
This design allows you to indicate whether the model multifilters when creating an SortFilterProxyModel object. The reason for this is so that you can add other custom behavior without having to create a separate QSortFilterProxyModel subclass.
In other words, if you create other custom behaviors by overriding QSortFilterProxyModel functions in this manner, you can pick and choose which custom sort/filter behaviors you want, and which standard sort/filter behaviors you want, for a given object.
Obviously, if you don't need or want that kind of flexibility with your subclass, you can make it your own with a few small adjustments.
Header:
class SortFilterProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
explicit SortFilterProxyModel(bool multiFilterModel, QObject *parent = nullptr);
void setMultiFilterRegularExpression(const int &column, const QString &pattern);
void clearMultiFilter();
protected:
virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
private:
QMap<int, QRegularExpression> m_multiFilterMap;
bool m_multiFilterModel = false;
};
Implementation:
#include "sortfilterproxymodel.h"
//The constructor takes one additional argument (multiFilterModel) that
//will dictate filtering behavior. If multiFilterModel is false the
//setMultiFilterRegularExpression and clearMultifilter will do nothing.
SortFilterProxyModel::SortFilterProxyModel(bool multiFilterModel, QObject *parent) : QSortFilterProxyModel(parent)
{
m_multiFilterModel = multiFilterModel;
}
//This loads the QMap with the column numbers and their corresponding filters.
//This member function that should be called from your main to filter model.
void SortFilterProxyModel::setMultiFilterRegularExpression(const int &column, const QString &pattern)
{
if(!m_multiFilterModel) //notifying that this does nothing and returning
{
qDebug() << "Object is not a multiFilterModel!";
return;
}
QRegularExpression filter;
filter.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
filter.setPattern(pattern);
m_multiFilterMap.insert(column, filter);
invalidateFilter(); //this causes filterAcceptsRow to run
}
//This will effectively unfilter the model by making the pattern for all
//existing regular expressions in the QMap to an empty string, and then invalidating the filter.
//This member function should be called from main to clear filter.
void SortFilterProxyModel::clearMultiFilter()
{
if(!m_multiFilterModel) //notifying that this does nothing and returning
{
qDebug() << "Object is not a multiFilterModel!";
return;
}
QMap<int, QRegularExpression>::const_iterator i = m_multiFilterMap.constBegin();
while(i != m_multiFilterMap.constEnd())
{
QRegularExpression blankExpression("");
m_multiFilterMap.insert(i.key(), blankExpression);
i++;
}
invalidateFilter(); //this causes filterAcceptsRow to run
}
//This checks to see if the model should be multifiltered, else it will
//work like the standard QSortFilterProxyModel.
bool SortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
if(m_multiFilterModel)
{
QMap<int, QRegularExpression>::const_iterator i = m_multiFilterMap.constBegin();
while(i != m_multiFilterMap.constEnd())
{
QModelIndex index = sourceModel()->index(source_row, i.key(), source_parent);
QString indexValue = sourceModel()->data(index).toString();
if(!indexValue.contains(i.value()))
{
return false; //if a value doesn't match returns false
}
i++;
}
return true; //if all column values match returns true
}
//This is standard QSortFilterProxyModel behavoir. It only runs if object is not multiFilterModel
QModelIndex index = sourceModel()->index(source_row, filterKeyColumn(), source_parent);
QString indexValue = sourceModel()->data(index).toString();
return indexValue.contains(filterRegularExpression());
}