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

在前文中,对于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的信号都是缺失的,在实际的使用过程中少了这些信号和函数是根本用不起来的,在下一篇文章中将会针对这些问题再做补充。

Qt是一个非常强大的GUI框架,对于百万数据展示来说,可以采用以下技巧: 1. 使用QTableView控件:QTableViewQt中用于展示表格数据的控件,它支持对大量数据的快速展示和滚动,可以通过设置model来实现数据加载。并且QTableView可以自动进分页显示,这样可以避免一次性加载大量数据导致程序崩溃的问题。 2. 使用QGraphicsView控件:QGraphicsView是Qt中用于展示图形数据的控件,它支持对大量数据的快速展示和滚动,可以通过设置scene来实现数据加载。与QTableView不同的是,QGraphicsView可以支持更加复杂的绘图操作,比如支持自定义的缩放和旋转等。 3. 数据分析和过滤:对于大量数据的展示,可以通过数据分析和过滤的方式来减少展示的数据量。比如可以根据用户的选择,只展示某个时间段内的数据,或者只展示某个地区的数据等。 4. 数据缓存:对于大量数据的展示,可以采用数据缓存的方式来提高程序的性能。比如可以将数据分成多个小块进加载,只有当用户需要查看某个小块的数据时才进加载,这样可以避免一次性加载大量数据导致程序崩溃的问题。 5. 多线程:对于大量数据的展示,可以采用多线程的方式来提高程序的性能。比如可以将数据加载和显示分别放在不同的线程中进,这样可以避免界面卡顿的问题。 综上所述,Qt提供了很多展示大量数据的技巧,可以根据实际情况选择合适的方法来进展示。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值