更改多列过滤的 QSortFilterProxyModel 行为

Change QSortFilterProxyModel behaviour for multiple column filtering

我们在 QTableView 和两个(或更多)QLineEdit 上安装了一个 QSortFilterProxyModel 用于过滤视图(基于这些 QLineEdit 的文本)

在我们看来,我们有一个插槽告诉我们行编辑的字符串和我们想要的当前列。像这样:

void onTextChange(int index, QString ntext) {
    filter.setFilterKeyColumn(index);
    filter.setFilterRegExp(QRegExp(ntext, Qt::CaseInsensitive));
}

第一列是名字,第二列是生日。

现在我们在第 2 列输入年份(例如 1985)。到目前为止,过滤还可以,但是当我们切换到第一行编辑并输入名称(例如 john)时,之前基于年份的过滤将重置。

我们如何为我们的自定义 QSortFilterProxyModel 更改此行为?

(实际上,当我们更改过滤器键列时,过滤器模型必须过滤现有视图而不是重置它)

更新...

基于@Mike 的回答: 如果您使用 QMap<int, QRegExp> 与未知列计数交互,将对您有所帮助

如果您想将 2 个输入与 "and" 过滤器连接,您可以简单地将它们分层。

像这样的东西应该有用。

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;    
    }
}

基于@Hayt 的回答和评论。因为你想在你的模型上有两个单独的过滤器,你可以有两个链接 QSortFilterProxyModel(一个根据名称进行过滤,另一个使用第一个过滤模型作为源根据年份进行过滤模型)。

这是一个完整的示例,说明如何为一个过滤器设置两个单独的过滤器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();
}

您可以子类化 QSortFilterProxyModel,使其采用两个单独的过滤器(一个用于名称,另一个用于年份),并将 filterAcceptsRow 覆盖为 return true 仅当两个过滤器都满足时。

Qt 文档的 Custom Sort/Filter Model Example 显示了一个子类 QSortFilterProxyModel,除了用于搜索的主要字符串过滤器之外,它还可以对日期进行过滤器。

这是一个完整的示例,说明如何使子类 QSortFilterProxyModel 为一个 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"