QTableView效率优化2 - 自定义Model秒级加载百万行数据

本文介绍了如何在数据量较大时优化QStandardItemModel,通过自定义QAbstractTableModel子类提高效率。在十万行乃至百万行数据场景下,通过设置数据结构和重写关键函数,实现了秒级加载速度。示例代码展示了如何创建和使用这个自定义模型。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在前文中,对于QStandardItemModel的效率进行了部分优化,https://blog.csdn.net/qq_37996632/article/details/123427832?spm=1001.2014.3001.5501,几万行以内的数据,QStandardItemModel还是非常好用的,直接有Qt写好的大量现成接口。也正是因为QStandardItemModel和QStandardItem包含的东西过多,不够简洁,所以在十万行级别的大量数据时QStandardItemModel的效率实在是差强人意,往往一个表格要几分钟才能加载出来。

所以在表格为几十万行甚至上百万行时,正确的做法是根据自己的数据类型,在此基础上自定义model,只保留自己需要的那几个功能和结构就好(另一个折中方法是分页,比如将大量数据拆成十分之一,当前页面只显示那十分之一的数据)。

一般在QTableView中,重写model继承QAbstractTableModel,需要重新实现的虚函数有:

  • flags() - 单元格的可操作性标志位,如可编辑,可选中
  • headerData()  - 表头的数据
  • data() - model中每个单元格的数据
  • rowCount() - 获取行数
  • columnCount() - 获取列数

下面是一个自定义Model的例子,数据方面采用了一个QVariantMap,每一个map里的值对应着一行表格数据。经过验证,10w行数据可以1秒内加载出来,100w行数据3秒加载出来。

nari_tableitemmodel.h

#ifndef NARI_TABLEITEMMODEL_H
#define NARI_TABLEITEMMODEL_H

#include <QAbstractTableModel>
#include <QObject>
#include <QStringList>
#include <QVector>


class NARI_TableItemModel : public QAbstractTableModel
{
    Q_OBJECT
public:
    explicit NARI_TableItemModel(QObject *parent = NULL);
    ~NARI_TableItemModel();
    
    // 自定义的函数,为整个model设置数据
    void SetData(const QVariantMap &map);

    virtual Qt::ItemFlags flags(const QModelIndex &index) const;
    virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;

    virtual int rowCount(const QModelIndex &parent) const;
    virtual int columnCount(const QModelIndex &parent) const;

    virtual QVariant data(const QModelIndex &index, int role) const;

private:
    QStringList m_hor_hedlbls;                      // headerlabels
    QStringList m_vec_hedlbls;                      // oids - map.keys()
    QVariantMap m_table_map;
};

#endif // NARI_TABLEITEMMODEL_H

nari_tableitemmodel.cpp

#include "nari_tableitemmodel.h"

#include <QDebug>
#include <QColor>
#include <QIcon>


NARI_TableItemModel::NARI_TableItemModel(QObject *parent)
    : QAbstractTableModel(parent)
{
}

NARI_TableItemModel::~NARI_TableItemModel()
{
}

void NARI_TableItemModel::SetData(const QVariantMap &map)
{
    beginResetModel();
    m_hor_hedlbls = map["headerlabel"].toStringList();
    m_table_map = map;
    m_vec_hedlbls = map.keys();
    endResetModel();
}

Qt::ItemFlags NARI_TableItemModel::flags(const QModelIndex &index) const
{
     Qt::ItemFlags flags = QAbstractItemModel::flags(index);
     flags |= Qt::ItemIsEditable;
     return flags;
}

QVariant NARI_TableItemModel::data(const QModelIndex &_index, int role) const
{
    if(role == Qt::DisplayRole || role == Qt::EditRole)
    {
        QVariantMap *_map = (QVariantMap*)(m_table_map[m_vec_hedlbls[_index.row()]].data());
        return (*_map)[m_hor_hedlbls[_index.column()]];
    }
    else if(role == Qt::TextAlignmentRole)  return Qt::AlignCenter; // 文字居中

    return QVariant();
}

QVariant NARI_TableItemModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (orientation == Qt::Horizontal)
    {
        if (role == Qt::DisplayRole)    return m_hor_hedlbls.at(section);
        else    return QVariant();
    }

    return QAbstractTableModel::headerData(section, orientation, role); // 垂直表头的序号
}

int NARI_TableItemModel::rowCount(const QModelIndex &parent) const
{
    if (parent.isValid())   return 0;
    else    return m_table_map.size() - 1;
}

int NARI_TableItemModel::columnCount(const QModelIndex &parent) const
{
    if (parent.isValid())   return 0;
    else    return m_hor_hedlbls.size();
}

mainwindow.cpp

NARI_TableItemModel *table_model = new NARI_TableItemModel();
ui->tableView->setModel(table_model);

QVariantMap data_map;
data_map["headerlabel"] = QStringList() << "ID" << tr("jiange") << tr("device") << tr("device type") << tr("factory name") << tr("voltage type") << tr("wf identifier") << tr("wf tuichu");

for(int i = 0; i < 100000; i++)
{
	QVariantMap map_1;
	map_1["ID"] = i + 1;
	map_1["jiange"] = i + 2;
	map_1["device"] = i + 3;
	map_1["device type"] = i + 4;
	map_1["factory name"] = i + 5;
	map_1["voltage type"] = i + 6;
	map_1["wf identifier"] = i + 7;
	map_1["wf tuichu"] = i + 8;
	data_map[QString::number(i) + "id"] = map_1;
}
table_model->SetData(data_map);

到此为止,10w行20w行数据确实是能秒级加载出来了,但是以上的代码只是实现了最简单的加载功能,对于QStandardItem中的setText(), setData(),setBackground(),甚至Itemchanged的信号都是缺失的,在实际的使用过程中少了这些信号和函数是根本用不起来的,在下一篇文章中将会针对这些问题再做补充。

### 回答1: 如果要在QTableView中显示百万条数据,要考虑以下两个方面: 1. 数据源 对于数据源,应该优先考虑使用数据库,因为数据库具有高效地存储和检索大量数据的能力。可以使用本地数据库,如SQLite,或者远程数据库,如MySQL或PostgreSQL。如果数据不是来自数据库,也可以使用QStandardItemModel等模型来组织数据。 2. 显示 QTableView会在启动时尝试将所有数据加载进内存并显示,如果数据量很大,这可能会导致内存不足而程序崩溃。这个问题可以通过分页和懒加载来解决。 分页:将数据分页加载,每次只加载一页数据到内存中,用户滚动QTableView时再加载下一页。这可以通过使用QAbstractItemModel的rowCount()方法来实现。 懒加载:仅在需要时加载数据,而不是把整个数据集都加载进内存。用QAbstractItemModel的data()方法获取数据并动态地从数据源中加载数据。 总之,QTableView显示大量数据时,需要考虑数据源和分页/懒加载。必要时,可以使用其他Qt组件来优化性能,例如QListView或QTreeView替代QTableView。 ### 回答2: 在使用QTableView显示百万数据时,我们需要考虑到以下几个方面: 1. 数据读取与处理:由于数据过多,一次性将所有数据读取到程序中会占用大量内存,造成程序运行缓慢甚至崩溃。因此,可以使用分页读取的方式,每次读取一部分数据,对数据进行排序后再显示在QTableView中,这样可以大大提高程序运行效率2. 模型的选择:在QTableView中,模型的选择对程序运行效率也有很大的影响。如果使用默认的QStandardItemModel模型,它会将数据全部存储在内存中,当数据量很大时会非常耗费内存。因此,可以选择使用QSqlQueryModel或QSqlTableModel等数据库模型来代替,因为它们可以直接从数据库中读取数据并显示。 3. 视图的优化:可以使用setOptimizationFlag()函数来进行优化。例如,设置QAbstractItemView::DontSavePainterState标志可以使得绘制视图时不会保存绘图状态,这样可以降低内存的占用;设置QAbstractItemView::Batched标志可以一次性绘制多行数据,提高绘制效率。 4. 使用线程:在数据量较大时,为了避免程序的阻塞,可以考虑使用线程来进行数据的读取和处理,使得界面可以同时进行其他操作,提高用户体验。 综上所述,当需要在QTableView中显示百万数据时,为了保证程序运行效率和用户体验,我们需要采用分页读取、更换模型、视图的优化以及使用线程等方法来进行优化。 ### 回答3: QTableView是一个基于模型-视图模式的控件,可以显示数据模型的任何部分,并随着数据变化自动更新。但如果数据量非常大,可以导致性能问题,因此,显示百万数据的QTableView需要进行优化。 一种优化方法是使用QSqlTableModel或QSqlQueryModel。这些模型允许在需要时从数据库加载数据,并且只在请求时将其添加到视图中。此外,可以使用分页技术将数据分成小批量加载。这种方法需要使用QSqlDatabase进行连接到数据库,所以需要同时在TableView和数据库中进行优化。 另一种优化方法是采用后台线程。可以使用QThread或QThreadPool构建后台线程,并将其用于数据库查询和加载数据。这样,可以在后台线程中加载数据,避免造成主线程的阻塞。在数据加载后,可以使用信号和槽机制来将数据展示到QTableView中,以此提高程序的响应速度。 另外,如果数据只读取一次,可以将数据预先加载到内存中,使用QStandardItemModel将数据直接设置到QTableView中。这种方法可以避免数据库连接、查询和加载数据带来的性能开销;但如果内存不足,会导致程序崩溃。 综上所述,QTableView显示百万数据需要进行性能优化,采用了QSqlTableModel或QSqlQueryModel、使用后台线程以及将数据预先加载到内存中等方法,可以在提高程序响应速度的同时保持程序的稳定性。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值