Change QSortFilterProxyModel behaviour for multiple column filtering

风流意气都作罢 提交于 2019-12-05 01:22:55

问题


We have a QSortFilterProxyModel installed on a QTableView and two (or more) QLineEdit for filtering the view (based on the text of these QLineEdits)

In our view we have a slot that tells us the string of lineedits and the current column that we want. Something like this :

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

On the first column we have names in the second we have year of birthday.

Now we enter a year for column 2 (for example 1985). Until now filtering is ok but when we switch to the first lineedit and enter a name (for example john) the previous filtering based on year will reset.

How could we change this behaviour for our custom QSortFilterProxyModel ?

(Actually when we change the filter keycolumn the filtermodel must filter existing view not reset it)

Update...

Based on @Mike's answer : If you interacting with unknown column count using QMap<int, QRegExp> will help you


回答1:


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"



回答2:


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



回答3:


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


来源:https://stackoverflow.com/questions/39488901/change-qsortfilterproxymodel-behaviour-for-multiple-column-filtering

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!