【问题标题】:Speed up QSortFilterProxyModel filtering when dealing with nearly large data sets在处理近乎大型的数据集时加快 QSortFilterProxyModel 过滤
【发布时间】:2016-10-14 18:23:17
【问题描述】:

之前,我向a question 询问了多列过滤,我们需要表示适合多个过滤模式的行。

现在在处理大表(big 我的意思是大约 200,000 行和 4 列)时,如果我们有一个那么大的表(通常这对于过滤模式的前 2 个字符来说是最差的),过滤会变慢。

那么您对此有何建议?

注意:我有自己的高性能源数据模型(而不是 QStandardItemModel),基于 this 示例,女巫在大约 1 秒内为我提供该行数的视图

编辑 1

从此改变我的方法:

bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const {
if (/* filtering is enable*/) {
    bool _res = sourceModel()->data(sourceModel()->index(source_row, 0, source_parent)).toString().contains( /*RegExp for column 0*/);
    for (int col = 0; col < columnCount(); col++) {
        _res &= sourceModel()->data(sourceModel()->index(source_row, col + 1, source_parent)).toString().contains(/*RegExp for column col + 1*/);
    }
    return _res;
}
return true;

}

对此:

bool DataFilter::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const {
    if (_enable) {
        return (sourceModel()->index(source_row, 0, source_parent.child(source_row, 0)).data().toString().contains( /*string for column 0*/ ))
            && sourceModel()->index(source_row, 1, source_parent.child(source_row, 1)).data().toString().contains(/*string for column 1*/))
            && sourceModel()->index(source_row, 2, source_parent.child(source_row, 2)).data().toString().contains(/*string for column 2*/))
            && sourceModel()->index(source_row, 3, source_parent.child(source_row, 3)).data().toString().contains(/*string for column 3*/));
    }
    return true;
}

看起来很完美。现在过滤效果就像一个魅力,没有延迟

【问题讨论】:

  • 1.描述您的模型性能。 2.不要使用正则表达式进行过滤,编写自己的方法。 3. 如果你真的需要大数据支持 - 将内存中的 SQLite 作为模型和过滤的来源。
  • 您可以放弃 QSortFilterProxyModel 并用您自己的专用实现替换它;因为您知道您的应用的特殊需求,您可能能够将您的实现设计为比 QSortFilterProxyModel 更高效,后者必须是通用的,并且不能对其交互的代码的行为做出任何假设。
  • @JeremyFriesner。所以你说我们应该忘记QSortFilterProxyModel。如果我们只需要过滤,在源数据模型(源自QAbstractTableModel)中实现过滤是个好主意吗?如果没有,我会听你的建议。
  • @DmitrySazonov。如果我是对的,您确认:使用字符串代替 RegExp 并在自己的方法(设置器)中设置过滤器字符串?实际上我从 MSSQL 获取数据到我的模型,正如我所说,它对于 200,000 行非常快,当我们使用过滤时,数据(该视图使用它)在内存中
  • 当使用&amp;&amp; 时,尝试帮助它快速失败——把最不可能的情况放在第一位。这将减少必须评估连词的后续分支的次数。

标签: c++ qt qt5 qabstractitemmodel qsortfilterproxymodel


【解决方案1】:

如果项目数量非常多,无法一次性加载,您可以尝试仅在视图中需要时才批量添加项目。这可以通过覆盖canFetchMore()fetchMore() 来完成。看看Fetch More Example。请注意,这是 QSqlQueryModel 在内部从数据库中加载大型模型的方式,请参阅 here

以下是使用这种方法实现模型的方法:

#include <QApplication>
#include <QtWidgets>

class MyTableModel : public QAbstractTableModel{
public:
    explicit MyTableModel(int rowCount, QObject* parent=nullptr)
        :QAbstractTableModel(parent),currentRowCount(0),wholeRowCount(rowCount){}
    ~MyTableModel(){}

    int rowCount(const QModelIndex &parent) const override{
        if(parent.isValid()) return 0;
        return currentRowCount;
    }
    int columnCount(const QModelIndex &parent) const override{
        if(parent.isValid()) return 0;
        return 2;
    }

    QVariant data(const QModelIndex &index, int role) const override{
        Q_ASSERT(index.row()<currentRowCount);
        QVariant val;
        if(role== Qt::DisplayRole || role== Qt::EditRole){
            switch(index.column()){
            case 0:
                val= QString("#%1").arg(index.row()+1, 8, 10, QChar('0'));
                break;
            case 1:
                val= rows[index.row()];
                break;
            }
        }
        return val;
    }

    bool canFetchMore(const QModelIndex &parent) const override{
        if(parent.isValid()) return false;
        return (currentRowCount < wholeRowCount);
    }

    void fetchMore(const QModelIndex& /* index */) override{
        int toFetch= qMin(52, wholeRowCount-currentRowCount);
        char ch = 'A';
        beginInsertRows(QModelIndex(), currentRowCount, currentRowCount+toFetch-1);
        for(int i=0; i<toFetch; i++){
            rows+= QString(QChar(ch));
            if(ch == 'Z') ch = 'A';
            else ch++;
        }
        currentRowCount+= toFetch;
        endInsertRows();
    }

private:
    int currentRowCount;
    int wholeRowCount;
    QStringList rows;
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QWidget w;
    QVBoxLayout layout(&w);
    QLineEdit filterLineEdit;
    QTableView tableView;
    layout.addWidget(&filterLineEdit);
    layout.addWidget(&tableView);

    MyTableModel model(200000);
    QSortFilterProxyModel proxyModel;
    proxyModel.setSourceModel(&model);
    proxyModel.setFilterKeyColumn(-1);
    tableView.setModel(&proxyModel);

    QObject::connect(&filterLineEdit, &QLineEdit::textChanged, [&](){
        proxyModel.setFilterFixedString(filterLineEdit.text());
    });

    w.show();

    return a.exec();
}

如果您确定真正的瓶颈是过滤,您可能还希望避免使用@DmitrySazonov 所指出的正则表达式,子类QSortFilterProxyModel,覆盖filterAcceptsRow() 并在那里提供您的算法,而不是使用一般的QRegExp基于过滤器。

要考虑的另一件事是避免在过滤器变窄时检查已过滤的行,请查看this question

【讨论】:

  • 感谢您的回答。但我很确定我的模型运行良好,我用快速的内部结构实现了它,我可以在一秒钟内加载 200k 行(在 release 配置中) .在这段时间内,我显示了一个不需要分页的进度条。我现在的问题是使用QSortFilterProxyModel进行实时过滤。您能否解释一下algorithm instead of QRegExp-based filter不仅仅是比较字符串?
  • 如何在这段时间内显示进度条?如果您的模型正在主线程中加载,则在控制权返回到事件循环(加载完成时)之前无法更新进度条。我认为你做错了什么。
  • @IMAN4K ,我的意思是为filterAcceptsRow() 提供一个不依赖于正则表达式的自定义实现(相反,由于您的特定类型的过滤,它会更好地工作)。但是,我真的认为 Qt 中的正则表达式优化得非常好,除非您确定您的过滤具有与普通字符串/正则表达式匹配不同的非常特殊的需求,否则您不应该这样做。
  • @Mike.使用线程:我将在工作对象中运行我的查询并填充我的结构,结束后我将发出成功信号(在此过程中,视图被隐藏并显示自定义进度条)当成功信号发出时,我会将我的结构提供给模型,然后调用view::show()。这一切都在大约 1 秒内发生,而不会阻塞主循环。
  • @IMAN4K ,在您链接的 github 存储库中,我找不到线程的任何用途,这就是我问我的问题的原因。那么,您是否正在寻找一种在另一个线程中进行过滤的方法?如果是这样,您可能想专门提出一个新问题,并提供您尝试过的内容。 . .
猜你喜欢
  • 2016-10-16
  • 2013-12-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-02
  • 2016-09-11
  • 1970-01-01
相关资源
最近更新 更多