We have a QSortFilterProxyModel
installed on a QTableView
and two (or more) QLineEdit
for filtering the view (based on the text of the
Based on @Hayt's answer and comment. Since you want to have two separate filters on your model, You can have two chained QSortFilterProxyModel
(one does the filtering based on the name, and the other does the filtering based on the year using the first filtering model as the source model).
Here is a fully working example on how to have two separate filters for one table:
#include <QApplication>
#include <QtWidgets>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//set up GUI
QWidget w;
QVBoxLayout layout(&w);
QHBoxLayout hLayout;
QLineEdit lineEditName;
QLineEdit lineEditYear;
lineEditName.setPlaceholderText("name filter");
lineEditYear.setPlaceholderText("year filter");
lineEditYear.setValidator(new QRegExpValidator(QRegExp("[0-9]*")));
lineEditYear.setMaxLength(4);
hLayout.addWidget(&lineEditName);
hLayout.addWidget(&lineEditYear);
QTableView tableView;
layout.addLayout(&hLayout);
layout.addWidget(&tableView);
//set up models
QStandardItemModel sourceModel;
QSortFilterProxyModel yearFilterModel;
yearFilterModel.setSourceModel(&sourceModel);
QSortFilterProxyModel nameFilterModel;
//nameFilterModel uses yearFilterModel as source
nameFilterModel.setSourceModel(&yearFilterModel);
//tableView displayes the last model in the chain nameFilterModel
tableView.setModel(&nameFilterModel);
nameFilterModel.setFilterKeyColumn(0);
yearFilterModel.setFilterKeyColumn(1);
nameFilterModel.setFilterCaseSensitivity(Qt::CaseInsensitive);
yearFilterModel.setFilterCaseSensitivity(Qt::CaseInsensitive);
QObject::connect(&lineEditName, &QLineEdit::textChanged, &nameFilterModel,
static_cast<void (QSortFilterProxyModel::*)(const QString&)>
(&QSortFilterProxyModel::setFilterRegExp));
QObject::connect(&lineEditYear, &QLineEdit::textChanged, &yearFilterModel,
static_cast<void (QSortFilterProxyModel::*)(const QString&)>
(&QSortFilterProxyModel::setFilterRegExp));
//fill with dummy data
QVector<QString> names{"Danny", "Christine", "Lars",
"Roberto", "Maria"};
for(int i=0; i<100; i++){
QList<QStandardItem*> row;
row.append(new QStandardItem(names[i%names.size()]));
row.append(new QStandardItem(QString::number((i%9)+1980)));
sourceModel.appendRow(row);
}
w.show();
return a.exec();
}
You can subclass QSortFilterProxyModel
, to make it take two separate filters (one for the name and the other for the year), and override filterAcceptsRow
to return true
only when both filters are satisfied.
The Qt documentation's Custom Sort/Filter Model Example shows a subclassed QSortFilterProxyModel
that can take filters for dates in addition to the main string filter used for searching.
Here is a fully working example on how to make a subclassed QSortFilterProxyModel
apply two separate filters for one table:
#include <QApplication>
#include <QtWidgets>
class NameYearFilterProxyModel : public QSortFilterProxyModel{
Q_OBJECT
public:
explicit NameYearFilterProxyModel(QObject* parent= nullptr):
QSortFilterProxyModel(parent){
//general parameters for the custom model
nameRegExp.setCaseSensitivity(Qt::CaseInsensitive);
yearRegExp.setCaseSensitivity(Qt::CaseInsensitive);
yearRegExp.setPatternSyntax(QRegExp::RegExp);
nameRegExp.setPatternSyntax(QRegExp::RegExp);
}
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override{
QModelIndex nameIndex= sourceModel()->index(sourceRow, 0, sourceParent);
QModelIndex yearIndex= sourceModel()->index(sourceRow, 1, sourceParent);
QString name= sourceModel()->data(nameIndex).toString();
QString year= sourceModel()->data(yearIndex).toString();
return (name.contains(nameRegExp) && year.contains(yearRegExp));
}
public slots:
void setNameFilter(const QString& regExp){
nameRegExp.setPattern(regExp);
invalidateFilter();
}
void setYearFilter(const QString& regExp){
yearRegExp.setPattern(regExp);
invalidateFilter();
}
private:
QRegExp nameRegExp;
QRegExp yearRegExp;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//set up GUI
QWidget w;
QVBoxLayout layout(&w);
QHBoxLayout hLayout;
QLineEdit lineEditName;
QLineEdit lineEditYear;
lineEditName.setPlaceholderText("name filter");
lineEditYear.setPlaceholderText("year filter");
lineEditYear.setValidator(new QRegExpValidator(QRegExp("[0-9]*")));
lineEditYear.setMaxLength(4);
hLayout.addWidget(&lineEditName);
hLayout.addWidget(&lineEditYear);
QTableView tableView;
layout.addLayout(&hLayout);
layout.addWidget(&tableView);
//set up models
QStandardItemModel sourceModel;
NameYearFilterProxyModel filterModel;;
filterModel.setSourceModel(&sourceModel);
tableView.setModel(&filterModel);
QObject::connect(&lineEditName, &QLineEdit::textChanged,
&filterModel, &NameYearFilterProxyModel::setNameFilter);
QObject::connect(&lineEditYear, &QLineEdit::textChanged,
&filterModel, &NameYearFilterProxyModel::setYearFilter);
//fill with dummy data
QVector<QString> names{"Danny", "Christine", "Lars",
"Roberto", "Maria"};
for(int i=0; i<100; i++){
QList<QStandardItem*> row;
row.append(new QStandardItem(names[i%names.size()]));
row.append(new QStandardItem(QString::number((i%9)+1980)));
sourceModel.appendRow(row);
}
w.show();
return a.exec();
}
#include "main.moc"
If you want to connect the 2 inputs with a "and" filter you can simply layer them.
Something like this should work.
QSortFilterProxyModel namefilter;
nameFilter.setFilterKeyColumn(nameColum);
QSortFilterProxyModel yearFilter;
yearFilter.setFilterKeyColumn(yearColumn);
yearFilter.setSourceModel(model);
nameFilter.setSourceModel(&yearFilter);
view.setSource(&nameFilter);
//....
void onTextChange(int index, QString ntext)
{
switch(index)
{
case yearColumn:
yearFilter.setFilterRegExp(QRegExp(ntext, Qt::CaseInsensitive));
break;
case nameColum:
namefilter.setFilterRegExp(QRegExp(ntext, Qt::CaseInsensitive));
break;
}
}