更改多列筛选的QSortFilterProxyModel行为

时间:2016-09-14 11:06:50

标签: qt qt5 qtableview qsortfilterproxymodel

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

在我们的视图中,我们有一个插槽,告诉我们所需的lineedits字符串和当前列。像这样:

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

在第一列,我们在第二列中有名字,我们有一年的生日。

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

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

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

更新...

根据@ Mike的回答: 如果您使用QMap<int, QRegExp>与未知列数进行交互,则可以帮助您

3 个答案:

答案 0 :(得分:3)

您可以对QSortFilterProxyModel进行子类化,使其需要两个单独的过滤器(一个用于名称,另一个用于年份),并覆盖filterAcceptsRow仅在两个过滤器时返回true很满意。

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

以下是关于如何使子类QSortFilterProxyModel为一个表应用两个单独的过滤器的完整工作示例:

Example Screenshot

#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"

答案 1 :(得分:2)

基于@ Hayt的回答和评论。由于您希望模型上有两个单独的过滤器,您可以有两个链式QSortFilterProxyModel(一个基于名称进行过滤,另一个基于使用第一个过滤模型作为源的年份进行过滤模型)。

以下是关于如何为一个表创建两个单独过滤器的完整工作示例:

Example Screenshot

#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();
}

答案 2 :(得分:1)

如果要将2个输入连接到&#34;和&#34;过滤你可以简单地分层。

这样的事情应该有效。

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