我们在
QSortFilterProxyModel
上安装了一个 QTableView
和两个(或更多)QLineEdit
用于过滤视图(基于这些 QLineEdit
的文本)
在我们看来,我们有一个槽,它告诉我们行编辑的字符串和我们想要的当前列。像这样的东西:
void onTextChange(int index, QString ntext) {
filter.setFilterKeyColumn(index);
filter.setFilterRegExp(QRegExp(ntext, Qt::CaseInsensitive));
}
第一列有名字,第二列有生日年份。
现在我们在第 2 列中输入年份(例如 1985 年)。到目前为止,过滤还可以,但是当我们切换到第一行编辑并输入名称(例如约翰)时,之前基于年份的过滤将重置。
我们如何改变我们的自定义行为
QSortFilterProxyModel
?
(实际上,当我们更改过滤器键列时,过滤器模型必须过滤现有视图而不是重置它)
基于@Mike的回答: 如果您使用
QMap<int, QRegExp>
与未知的列数交互将会对您有所帮助
基于@Hayt 的回答和评论。由于您希望在模型上有两个单独的过滤器,因此您可以有两个链接的
QSortFilterProxyModel
(一个根据名称进行过滤,另一个根据年份进行过滤,使用第一个过滤模型作为源模型) .
这是一个完整的示例,说明如何为一个表设置两个单独的过滤器:
#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();
}
您可以对
QSortFilterProxyModel
进行子类化,使其采用两个单独的过滤器(一个用于名称,另一个用于年份),并覆盖 filterAcceptsRow
以仅当两个过滤器都满足时才返回 true
。
Qt 文档的 自定义排序/过滤器模型示例 显示了一个子类
QSortFilterProxyModel
,除了用于搜索的主字符串过滤器之外,它还可以采用日期过滤器。
这是一个完整的示例,说明如何创建子类
QSortFilterProxyModel
为一个表应用两个单独的过滤器:
#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"
如果您想使用“and”过滤器连接 2 个输入,您只需将它们分层即可。
这样的东西应该有效。
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;
}
}
我知道这是一个旧线程,但这里是带有多列过滤器实现的 QSortFilterProxyModel 的更通用版本。我看到有人的评论(Mike)回避了这样的解决方案,但我没有看到任何代码示例。
此设计允许您在创建 SortFilterProxyModel 对象时指示模型是否为多重过滤器。这样做的原因是您可以添加其他自定义行为,而无需创建单独的 QSortFilterProxyModel 子类。换句话说,如果您通过以这种方式重写 QSortFilterProxyModel 函数来创建其他自定义行为,您可以针对给定对象选择您想要的自定义排序/过滤行为,以及您想要的标准排序/过滤行为。显然,如果您不需要或不希望子类具有这种灵活性,您可以通过一些小的调整使其成为您自己的子类。
标题:
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;
signals:
};
实施:
#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());
}
这里有一个MixedFilterModel继承QSortFilterProxyModel
https://gist.github.com/mindon/a2a0432c9ede07b627f62af94ac95a96