如果过滤器严格变窄,请避免重复调用QSortFilterProxyModel :: filterAcceptsRow() [英] Avoid redundant calls to QSortFilterProxyModel::filterAcceptsRow() if the filter has become strictly narrower

查看:203
本文介绍了如果过滤器严格变窄,请避免重复调用QSortFilterProxyModel :: filterAcceptsRow()的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有什么方法可以使QSortFilterProxyModel中的过滤器无效,但是要表明过滤器已经缩小了范围,因此仅在当前可见的行上调用filterAcceptsRow()?

Is there any way invalidate the filter in a QSortFilterProxyModel, but to indicate that the filter has been narrowed down so filterAcceptsRow() should be called only on the currently visible rows?

目前,Qt并没有这样做.当我调用QSortFilterProxyModel::invalidateFilter()并将过滤器从"abcd"更改为"abcde"时,将创建一个全新的映射,并在所有源行上调用filterAcceptsRow(),即使很明显隐藏了源行也是如此.远将保持隐藏.

Currently Qt doesn't do that. When I call QSortFilterProxyModel::invalidateFilter(), and my filter is changed from "abcd" to "abcde", an entirely new mapping is created, and filterAcceptsRow() is called on all source rows, even though it's obvious that source rows that were hidden so far will remain hidden.

这是来自Qt的QSortFilterProxyModelPrivate::create_mapping()中的代码,该代码调用了我覆盖的filterAcceptsRow(),它创建了一个全新的Mapping并遍历所有源行:

This is the code from Qt's sources in QSortFilterProxyModelPrivate::create_mapping() which calls my overridden filterAcceptsRow(), and it creates an entirely new Mapping and iterates over all the source rows:

Mapping *m = new Mapping;

int source_rows = model->rowCount(source_parent);
m->source_rows.reserve(source_rows);
for (int i = 0; i < source_rows; ++i) {
    if (q->filterAcceptsRow(i, source_parent))
        m->source_rows.append(i);
}

我想要的是仅对映射中的可见行进行迭代,并仅对它们进行调用.如果已经隐藏了行,则不应在其上调用filterAcceptsRow(),因为我们已经知道该行将为此返回false(过滤器变得更加严格,没有被松开).

What I want is to iterate only the visible rows in mapping and call filterAcceptsRow() only on them. If a row is already hidden filterAcceptsRow() should not be called on it, because we already know that it would return false for it (the filter has become more stringent, it hasn't been loosened).

由于我已经覆盖了filterAcceptsRow(),因此Qt无法知道过滤器的性质,但是当我调用QSortFilterProxyModel::invalidateFilter()时,我掌握了有关过滤器是否严格变窄的信息,因此可以将该信息传递给Qt是否可以接受它.

Since I have overriden filterAcceptsRow(), Qt can't know the nature of the filter, but when I call QSortFilterProxyModel::invalidateFilter(), I have the information about whether the filter has become strictly narrower, so I could pass that information to Qt if it has a way of accepting it.

另一方面,如果我已将过滤器从abcd更改为abce,则应在所有源行上调用该过滤器,因为它已变得越来越狭窄.

On the other hand, if I've changed the filter from abcd to abce, then the filter should be called on all source rows, since it has become strictly narrower.

推荐答案

我写了 c12> 子类,用于存储链式QSortFilterProxyModel的列表.它提供类似于QSortFilterProxyModel的接口,并接受narrowedDown布尔参数,该参数指示是否缩小过滤器范围.这样:

I wrote a QIdentityProxyModel subclass that stores a list of chained QSortFilterProxyModel. It provides an interface similar to QSortFilterProxyModel and accepts a narrowedDown boolean parameter that indicates if the filter is being narrowed down. So that:

  • 缩小过滤器的范围时,会将新的QSortFilterProxyModel附加到链上,然后QIdentityProxyModel切换为在链的末尾代理新过滤器.
  • 否则,它将删除链中的所有过滤器,并使用一个与当前过滤条件相对应的过滤器来构建新链.之后,QIdentityProxyModel切换为代理链中的新过滤器.
  • When the filter is being narrowed down, a new QSortFilterProxyModel is appended to the chain, and the QIdentityProxyModel switches to proxy the new filter at the end of the chain.
  • Otherwise, It deletes all the filters in the chain, constructs a new chain with one filter that corresponds to the current filtering criteria. After that, the QIdentityProxyModel switches to proxy the new filter in the chain.

这是一个将类与使用常规QSortFilterProxyModel子类进行比较的程序:

Here is a program that compares the class to using a normal QSortFilterProxyModel subclass:

#include <QtWidgets>

class FilterProxyModel : public QSortFilterProxyModel{
public:
    explicit FilterProxyModel(QObject* parent= nullptr):QSortFilterProxyModel(parent){}
    ~FilterProxyModel(){}

    //you can override filterAcceptsRow here if you want
};

//the class stores a list of chained FilterProxyModel and proxies the filter model

class NarrowableFilterProxyModel : public QIdentityProxyModel{
    Q_OBJECT
    //filtering properties of QSortFilterProxyModel
    Q_PROPERTY(QRegExp filterRegExp READ filterRegExp WRITE setFilterRegExp)
    Q_PROPERTY(int filterKeyColumn READ filterKeyColumn WRITE setFilterKeyColumn)
    Q_PROPERTY(Qt::CaseSensitivity filterCaseSensitivity READ filterCaseSensitivity WRITE setFilterCaseSensitivity)
    Q_PROPERTY(int filterRole READ filterRole WRITE setFilterRole)
public:
    explicit NarrowableFilterProxyModel(QObject* parent= nullptr):QIdentityProxyModel(parent), m_filterKeyColumn(0),
        m_filterCaseSensitivity(Qt::CaseSensitive), m_filterRole(Qt::DisplayRole), m_source(nullptr){
    }

    void setSourceModel(QAbstractItemModel* sourceModel){
        m_source= sourceModel;
        QIdentityProxyModel::setSourceModel(sourceModel);
        for(FilterProxyModel* proxyNode : m_filterProxyChain) delete proxyNode;
        m_filterProxyChain.clear();
        applyCurrentFilter();
    }

    QRegExp filterRegExp()const{return m_filterRegExp;}
    int filterKeyColumn()const{return m_filterKeyColumn;}
    Qt::CaseSensitivity filterCaseSensitivity()const{return m_filterCaseSensitivity;}
    int filterRole()const{return m_filterRole;}

    void setFilterKeyColumn(int filterKeyColumn, bool narrowedDown= false){
        m_filterKeyColumn= filterKeyColumn;
        applyCurrentFilter(narrowedDown);
    }
    void setFilterCaseSensitivity(Qt::CaseSensitivity filterCaseSensitivity, bool narrowedDown= false){
        m_filterCaseSensitivity= filterCaseSensitivity;
        applyCurrentFilter(narrowedDown);
    }
    void setFilterRole(int filterRole, bool narrowedDown= false){
        m_filterRole= filterRole;
        applyCurrentFilter(narrowedDown);
    }
    void setFilterRegExp(const QRegExp& filterRegExp, bool narrowedDown= false){
        m_filterRegExp= filterRegExp;
        applyCurrentFilter(narrowedDown);
    }
    void setFilterRegExp(const QString& filterRegExp, bool narrowedDown= false){
        m_filterRegExp.setPatternSyntax(QRegExp::RegExp);
        m_filterRegExp.setPattern(filterRegExp);
        applyCurrentFilter(narrowedDown);
    }
    void setFilterWildcard(const QString &pattern, bool narrowedDown= false){
        m_filterRegExp.setPatternSyntax(QRegExp::Wildcard);
        m_filterRegExp.setPattern(pattern);
        applyCurrentFilter(narrowedDown);
    }
    void setFilterFixedString(const QString &pattern, bool narrowedDown= false){
        m_filterRegExp.setPatternSyntax(QRegExp::FixedString);
        m_filterRegExp.setPattern(pattern);
        applyCurrentFilter(narrowedDown);
    }

private:
    void applyCurrentFilter(bool narrowDown= false){
        if(!m_source) return;
        if(narrowDown){ //if the filter is being narrowed down
            //instantiate a new filter proxy model and add it to the end of the chain
            QAbstractItemModel* proxyNodeSource= m_filterProxyChain.empty()?
                        m_source : m_filterProxyChain.last();
            FilterProxyModel* proxyNode= newProxyNode();
            proxyNode->setSourceModel(proxyNodeSource);
            QIdentityProxyModel::setSourceModel(proxyNode);
            m_filterProxyChain.append(proxyNode);
        } else { //otherwise
            //delete all filters from the current chain
            //and construct a new chain with the new filter in it
            FilterProxyModel* proxyNode= newProxyNode();
            proxyNode->setSourceModel(m_source);
            QIdentityProxyModel::setSourceModel(proxyNode);
            for(FilterProxyModel* node : m_filterProxyChain) delete node;
            m_filterProxyChain.clear();
            m_filterProxyChain.append(proxyNode);
        }
    }
    FilterProxyModel* newProxyNode(){
        //return a new child FilterModel with the current properties
        FilterProxyModel* proxyNode= new FilterProxyModel(this);
        proxyNode->setFilterRegExp(filterRegExp());
        proxyNode->setFilterKeyColumn(filterKeyColumn());
        proxyNode->setFilterCaseSensitivity(filterCaseSensitivity());
        proxyNode->setFilterRole(filterRole());
        return proxyNode;
    }
    //filtering parameters for QSortFilterProxyModel
    QRegExp m_filterRegExp;
    int m_filterKeyColumn;
    Qt::CaseSensitivity m_filterCaseSensitivity;
    int m_filterRole;

    QAbstractItemModel* m_source;
    QList<FilterProxyModel*> m_filterProxyChain;
};

//Demo program that uses the class

//used to fill the table with dummy data
std::string nextString(std::string str){
    int length= str.length();
    for(int i=length-1; i>=0; i--){
        if(str[i] < 'z'){
            str[i]++; return str;
        } else str[i]= 'a';
    }
    return std::string();
}

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    //set up GUI
    QWidget w;
    QGridLayout layout(&w);
    QLineEdit lineEditFilter;
    lineEditFilter.setPlaceholderText("filter");
    QLabel titleTable1("NarrowableFilterProxyModel:");
    QTableView tableView1;
    QLabel labelTable1;
    QLabel titleTable2("FilterProxyModel:");
    QTableView tableView2;
    QLabel labelTable2;
    layout.addWidget(&lineEditFilter,0,0,1,2);
    layout.addWidget(&titleTable1,1,0);
    layout.addWidget(&tableView1,2,0);
    layout.addWidget(&labelTable1,3,0);
    layout.addWidget(&titleTable2,1,1);
    layout.addWidget(&tableView2,2,1);
    layout.addWidget(&labelTable2,3,1);

    //set up models
    QStandardItemModel sourceModel;
    NarrowableFilterProxyModel filterModel1;;
    tableView1.setModel(&filterModel1);

    FilterProxyModel filterModel2;
    tableView2.setModel(&filterModel2);

    QObject::connect(&lineEditFilter, &QLineEdit::textChanged, [&](QString newFilter){
        QTime stopWatch;
        newFilter.prepend("^"); //match from the beginning of the name
        bool narrowedDown= newFilter.startsWith(filterModel1.filterRegExp().pattern());
        stopWatch.start();
        filterModel1.setFilterRegExp(newFilter, narrowedDown);
        labelTable1.setText(QString("took: %1 msecs").arg(stopWatch.elapsed()));
        stopWatch.start();
        filterModel2.setFilterRegExp(newFilter);
        labelTable2.setText(QString("took: %1 msecs").arg(stopWatch.elapsed()));
    });

    //fill model with strings from "aaa" to "zzz" (17576 rows)
    std::string str("aaa");
    while(!str.empty()){
        QList<QStandardItem*> row;
        row.append(new QStandardItem(QString::fromStdString(str)));
        sourceModel.appendRow(row);
        str= nextString(str);
    }
    filterModel1.setSourceModel(&sourceModel);
    filterModel2.setSourceModel(&sourceModel);

    w.show();
    return a.exec();
}

#include "main.moc"

注意:

  • 该类仅在缩小过滤器时才提供某种优化,因为在链末尾新建的过滤器不需要搜索所有源模型的行.
  • li>
  • 该类取决于用户,以判断过滤器是否正在缩小.就是说,当用户为参数narrowedDown传递true时,假定该过滤器是当前过滤器的一种特殊情况(即使实际上并非如此).否则,它的行为与普通的QSortFilterProxyModel完全相同,并且可能会有一些额外的开销(由于清理旧的过滤器链而导致).
  • 当不缩小过滤器范围时,可以进一步优化该类,以便在当前过滤器链中查找与当前过滤器类似的过滤器并立即切换到该过滤器(而不是删除整个链并开始一个新的).当用户在结尾的过滤器QLineEdit上删除某些字符时(例如,当过滤器从"abcd"变回"abc"时,这特别有用),因为您应该已经在链中使用"abc"了一个过滤器).但是目前,这还没有实现,因为我希望答案尽可能小且清晰.
  • Notes:

    • The class provides some kind of optimization only when the filter is being narrowed down, since the newly constructed filter at the end of the chain does not need to search through all source model's rows.
    • The class depends on the user to tell if the filter is being narrowed down. That is when the user passes true for the argument narrowedDown, the filter is assumed to be a special case of the current filter (even if it is not really so). Otherwise, it behaves exactly the same as the normal QSortFilterProxyModel and possibly with some additional overhead (resulted from cleaning up the old filter chain).
    • The class can be further optimized when the filter is not being narrowed down, so that it looks in the current filter chain for a filter that is similar to the current filter and switch to it immediately (instead of deleting the whole chain and starting a new one). This can be particularly useful when the user is deleting some characters at the end filter QLineEdit (ie. when the filter changes back from "abcd" to "abc", since you should already have a filter in the chain with "abc"). But currently, this is not implemented as I want the answer to be as minimal and clear as possible.
    • 这篇关于如果过滤器严格变窄,请避免重复调用QSortFilterProxyModel :: filterAcceptsRow()的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆