jjzjj

c++ - 如果过滤器严格变窄,避免对 QSortFilterProxyModel::filterAcceptsRow() 的冗余调用

coder 2023-11-14 原文

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

目前 Qt 不这样做。当我调用 QSortFilterProxyModel::invalidateFilter() 时,我的过滤器从“abcd”更改为“abcde”,创建了一个全新的映射,并且 filterAcceptsRow() 是调用所有源行,即使很明显到目前为止隐藏的源行将保持隐藏状态。

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

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()。如果某行已经被隐藏,则不应对其调用 filterAcceptsRow(),因为我们已经知道它会为其返回 false(过滤器变得更加严格,它并没有松动)。

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

另一方面,如果我将过滤器从 abcd 更改为 abce,那么应该在所有源行上调用过滤器,因为它已变得严格更窄。

最佳答案

我写了一个QIdentityProxyModel存储链式 QSortFilterProxyModel 列表的子类。它提供了一个类似于 QSortFilterProxyModel 的接口(interface),并接受一个 narrowedDown bool 参数,该参数指示过滤器是否正在缩小范围。这样:

  • 当过滤器缩小范围时,一个新的QSortFilterProxyModel 被添加到链中,并且QIdentityProxyModel 切换到代理链末端的新过滤器。
  • 否则,它删除链中的所有过滤器,构造一个新链,其中一个过滤器对应于当前过滤条件。之后,QIdentityProxyModel 切换到代理链中的新过滤器。

这是一个程序,将类与使用普通 QSortFilterProxyModel 子类进行比较:

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

注意事项:

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

关于c++ - 如果过滤器严格变窄,避免对 QSortFilterProxyModel::filterAcceptsRow() 的冗余调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39414607/

有关c++ - 如果过滤器严格变窄,避免对 QSortFilterProxyModel::filterAcceptsRow() 的冗余调用的更多相关文章

  1. ruby-on-rails - 如果为空或不验证数值,则使属性默认为 0 - 2

    我希望我的UserPrice模型的属性在它们为空或不验证数值时默认为0。这些属性是tax_rate、shipping_cost和price。classCreateUserPrices8,:scale=>2t.decimal:tax_rate,:precision=>8,:scale=>2t.decimal:shipping_cost,:precision=>8,:scale=>2endendend起初,我将所有3列的:default=>0放在表格中,但我不想要这样,因为它已经填充了字段,我想使用占位符。这是我的UserPrice模型:classUserPrice回答before_val

  2. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  3. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

  4. ruby - 如果指定键的值在数组中相同,如何合并哈希 - 2

    我有一个这样的哈希数组:[{:foo=>2,:date=>Sat,01Sep2014},{:foo2=>2,:date=>Sat,02Sep2014},{:foo3=>3,:date=>Sat,01Sep2014},{:foo4=>4,:date=>Sat,03Sep2014},{:foo5=>5,:date=>Sat,02Sep2014}]如果:date相同,我想合并哈希值。我对上面数组的期望是:[{:foo=>2,:foo3=>3,:date=>Sat,01Sep2014},{:foo2=>2,:foo5=>5:date=>Sat,02Sep2014},{:foo4=>4,:dat

  5. ruby-on-rails - 如果我将 ruby​​ 版本 2.5.1 与 rails 版本 2.3.18 一起使用会怎样? - 2

    如果我使用ruby​​版本2.5.1和Rails版本2.3.18会怎样?我有基于rails2.3.18和ruby​​1.9.2p320构建的rails应用程序,我只想升级ruby的版本,而不是rails,这可能吗?我必须面对哪些挑战? 最佳答案 GitHub维护apublicfork它有针对旧Rails版本的分支,有各种变化,它们一直在运行。有一段时间,他们在较新的Ruby版本上运行较旧的Rails版本,而不是最初支持的版本,因此您可能会发现一些关于需要向后移植的有用提示。不过,他们现在已经有几年没有使用2.3了,所以充其量只能让更

  6. ruby-on-rails - 事件管理员日期过滤器日期格式自定义 - 2

    是否有简单的方法来更改默认ISO格式(yyyy-mm-dd)的ActiveAdmin日期过滤器显示格式? 最佳答案 您可以像这样为日期选择器提供额外的选项,而不是覆盖js:=f.input:my_date,as::datepicker,datepicker_options:{dateFormat:"mm/dd/yy"} 关于ruby-on-rails-事件管理员日期过滤器日期格式自定义,我们在StackOverflow上找到一个类似的问题: https://s

  7. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  8. ruby-on-rails - 在 Controller 中干净地处理多个过滤器(参数) - 2

    我有一个名为Post的类,我需要能够适应以下场景:如果用户选择了一个类别,则只显示该类别的帖子如果用户选择了一种类型,则只显示该类型的帖子如果用户选择了一个类别和类型,则只显示该类别中该类型的帖子如果用户没有选择任何内容,则显示所有帖子我想知道我的Controller是否不可避免地会因大量条件语句而显得粗糙...这是我解决此问题的错误方法-有谁知道我如何才能做到这一点?classPostsController 最佳答案 您最好遵循“胖模型,瘦Controller”的惯例,这意味着您应该将这种逻辑放在模型本身中。Post类应该能够报告

  9. ruby - 如何计算 Liquid 中的变量 +1 - 2

    我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我

  10. ruby-on-rails - 如何处理 Grape 中特定操作的过滤器之前? - 2

    我正在我的Rails项目中安装Grape以构建RESTfulAPI。现在一些端点的操作需要身份验证,而另一些则不需要身份验证。例如,我有users端点,看起来像这样:moduleBackendmoduleV1classUsers现在如您所见,除了password/forget之外的所有操作都需要用户登录/验证。创建一个新的端点也没有意义,比如passwords并且只是删除password/forget从逻辑上讲,这个端点应该与用户资源。问题是Grapebefore过滤器没有像except,only这样的选项,我可以在其中说对某些操作应用过滤器。您通常如何干净利落地处理这种情况?

随机推荐