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