前言
平时用Qt的QTableWidget,后面感觉数据多的时候就改用QTableView了,model用的Qt自带的QStandardItemModel,但是数据过多还是会卡顿,而且QStandardItemModel功能很多,里面很多自己用不到的信号和槽,所以后面基于QAbstractTableModel自定义了自己的模型TableModel。
实现效果
插入一百万条数据,大概需要5s左右吧,然后设置单双行背景不一样,点击表格项显示提示信息。注意这里添加数据的时候不是一条一条数据添加的,因为循环调用==beginResetModel()和endResetModel()==函数就会很卡。所以我是自定义了一个函数,可以一次新建多行数据。

需要实现的函数
想要自己的自定义Model能用起来,就必须实现基类QAbstractTableModel里的一些接口。如下:
// 返回行数
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
// 返回列数
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
// 根据模型索引返回当前的数据
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
// 设置表格是否可编辑
Qt::ItemFlags flags(const QModelIndex &index) const override;
// 设置value数据给index处的role角色
bool setData(const QModelIndex& index, const QVariant &value, int role=Qt::EditRole) override;
// 返回表头
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
rowCount接口返回的是行数,我是将数据存到QList里,然后返回这个QList的size();
columnCount接口返回的是列数,我是将水平表头保存到QStringList里,然后返回这个QStringList的size();
data接口比较重要,表格会一直调用该函数来获取当前数据以及一些其他信息;
flags接口影响表格是否可编辑,如果自定义表格需要双击表格可编辑数据的话,需要实现该接口和setData接口,后者用来处理编辑表格后数据怎么保存;
headerData返回水平方向或者垂直方向的表头。
以上所有接口都定义好之后自己的自定义TableModel基本就可以用了,后面的大部分自定义函数基本都是针对数据进行处理。
int MyTableModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid()) {
return 0;
} else {
return m_datas.size();
}
}
int MyTableModel::columnCount(const QModelIndex &parent) const
{
if (parent.isValid()) {
return 0;
} else {
return m_HorizontalHead.size();
}
}
QVariant MyTableModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid()) {
return QVariant();
}
if (role == Qt::DisplayRole || role == Qt::EditRole) {
return m_datas[index.row()][index.column()];//数据的呈现形式
}else if(role == Qt::BackgroundColorRole){
if(index.row() % 2 == 0)
return QColor(192,192,192);//单元格背景色
else
return QColor(159,252,252);//单元格背景色
}
else if (role == Qt::TextAlignmentRole) { //对其方式
return Qt::AlignCenter;
}
else if(role == Qt::ToolTipRole){
return m_datas[index.row()][index.column()];//数据的提示信息
}
return QVariant();
}
Qt::ItemFlags MyTableModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return Qt::NoItemFlags;
return Qt::ItemIsEnabled|Qt::ItemIsSelectable|Qt::ItemIsEditable;
}
bool MyTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (index.isValid() && role == Qt::EditRole)
{
m_datas[index.row()][index.column()] = value.value<QString>();
emit dataChanged(index, index, QVector<int>() << role); //发送信号触发刷新
return true;
}
return false;
}
QVariant MyTableModel::headerData(int section, Qt::Orientation orientation, int role) const {
if (role != Qt::DisplayRole) // 返回一个默认数据的表头
return QAbstractTableModel::headerData(section, orientation, role);
// 仅仅只是返回水平方向的表头
if (orientation == Qt::Orientation::Horizontal) {
if(m_HorizontalHead.size() >= m_datas.size())
return this->m_HorizontalHead[section];
}
return QAbstractTableModel::headerData(section, orientation, role);
}
对数据处理时注意事项
对自己的数据处理之前要调用beginResetModel(),对数据处理完之后要调用endResetModel();有时候界面比较卡顿主要是添加数据的时候可能是一条条添加,中间调用太多次这两个函数了。
MyTableModel头文件代码
#ifndef MYTABLEMODEL_H
#define MYTABLEMODEL_H
#include <QObject>
#include <QAbstractTableModel>
#include <QColor>
typedef QStringList TableRowData;
typedef QList<QStringList> TableRowDatas;
class MyTableModel : public QAbstractTableModel
{
Q_OBJECT
public:
explicit MyTableModel(QObject *parent = nullptr);
public:
// 返回行数
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
// 返回列数
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
// 根据模型索引返回当前的数据
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
// 设置表格是否可编辑
Qt::ItemFlags flags(const QModelIndex &index) const override;
// 设置value数据给index处的role角色
bool setData(const QModelIndex& index, const QVariant &value, int role=Qt::EditRole) override;
// 返回表头
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
///// 自定义函数
///< 设置水平方向表头
void setHorizontalHeader(QStringList header);
///< 设置垂直方向表头
void setVerticalHeader(QStringList header);
///< 设置最大行数
void setMaxRow(int size);
///< 增加行
bool appendRow(TableRowData rowData = TableRowData());
///< 增加多行
bool appendRowS(int count,TableRowDatas tableRowDatas = TableRowDatas());
///< 插入行
bool insertRow(int row,TableRowData rowData = TableRowData());
///< 插入多行
bool insertRowS(int row,int count,TableRowDatas tableRowDatas = TableRowDatas());
///< 删除行
bool removeRow(int row);
///< 行数据
QStringList rowTset(int row);
///< 单元格数据
QString itemText(int row,int column);
///< 删除所有单元格
void clear();
private:
// 如果字符串列比总列数小 则补空字符串
void CompleteList(QStringList& list);
// 检查数据 如果行数最大值超过所设置的值 则将较老的数据删除
void maxRowCheck();
private:
int m_rowNum; // 行大小
int m_maxRow; // 最大行
QStringList m_HorizontalHead; // 水平方向表头
QStringList m_VerticalHeader; // 水平方向表头
TableRowDatas m_datas; // 数据
};
#endif // MYTABLEMODEL_H
MyTableModel cpp文件代码
#include "mytablemodel.h"
#include <QtMath>
#include <QColor>
MyTableModel::MyTableModel(QObject *parent) : QAbstractTableModel(parent)
{
m_maxRow = 1000000;
}
int MyTableModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid()) {
return 0;
} else {
return m_datas.size();
}
}
int MyTableModel::columnCount(const QModelIndex &parent) const
{
if (parent.isValid()) {
return 0;
} else {
return m_HorizontalHead.size();
}
}
QVariant MyTableModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid()) {
return QVariant();
}
if (role == Qt::DisplayRole || role == Qt::EditRole) {
return m_datas[index.row()][index.column()];//数据的呈现形式
}else if(role == Qt::BackgroundColorRole){
if(index.row() % 2 == 0)
return QColor(192,192,192);//单元格背景色
else
return QColor(159,252,252);//单元格背景色
}
else if (role == Qt::TextAlignmentRole) { //对其方式
return Qt::AlignCenter;
}
else if(role == Qt::ToolTipRole){
return m_datas[index.row()][index.column()];//数据的提示信息
}
return QVariant();
}
Qt::ItemFlags MyTableModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return Qt::NoItemFlags;
return Qt::ItemIsEnabled|Qt::ItemIsSelectable|Qt::ItemIsEditable;
}
bool MyTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (index.isValid() && role == Qt::EditRole)
{
m_datas[index.row()][index.column()] = value.value<QString>();
emit dataChanged(index, index, QVector<int>() << role); //发送信号触发刷新
return true;
}
return false;
}
QVariant MyTableModel::headerData(int section, Qt::Orientation orientation, int role) const {
if (role != Qt::DisplayRole) // 返回一个默认数据的表头
return QAbstractTableModel::headerData(section, orientation, role);
// 仅仅只是返回水平方向的表头
if (orientation == Qt::Orientation::Horizontal) {
if(m_HorizontalHead.size() >= m_datas.size())
return this->m_HorizontalHead[section];
}
return QAbstractTableModel::headerData(section, orientation, role);
}
void MyTableModel::setHorizontalHeader(QStringList header)
{
m_HorizontalHead = header;
}
void MyTableModel::setVerticalHeader(QStringList header)
{
m_VerticalHeader = header;
}
void MyTableModel::setMaxRow(int size)
{
m_maxRow = size;
}
bool MyTableModel::appendRow(QStringList rowData)
{
CompleteList(rowData);
beginResetModel();
m_datas.append(rowData);
maxRowCheck();
endResetModel();
return true;
}
bool MyTableModel::appendRowS(int count,TableRowDatas tableRowDatas)
{
beginResetModel();
for(int i = 0; i < count;i++)
{
TableRowData rowData;
if(tableRowDatas.size() > i)
rowData = tableRowDatas[i];
CompleteList(rowData);
m_datas.append(rowData);
maxRowCheck();
}
endResetModel();
return true;
}
bool MyTableModel::insertRow(int row,TableRowData rowData)
{
CompleteList(rowData);
if(row < m_datas.size())
{
beginResetModel();
m_datas.insert(row,rowData);
maxRowCheck();
endResetModel();
return true;
}
return false;
}
bool MyTableModel::insertRowS(int row,int count,TableRowDatas tableRowDatas)
{
if(row < m_datas.size())
{
beginResetModel();
for(int i = 0; i < count;i++)
{
TableRowData rowData;
if(tableRowDatas.size() > i)
rowData = tableRowDatas[i];
CompleteList(rowData);
m_datas.insert(row+i,rowData);
maxRowCheck();
}
endResetModel();
return true;
}
return false;
}
bool MyTableModel::removeRow(int row)
{
if(row < m_datas.size()){
beginResetModel();
m_datas.removeAt(row);
endResetModel();
return true;
}
return false;
}
QStringList MyTableModel::rowTset(int row)
{
if(row < m_datas.size())
return m_datas[row];
return QStringList();
}
QString MyTableModel::itemText(int row,int column)
{
if(row < m_datas.size() && column < m_HorizontalHead.size())
return m_datas[row][column];
return "";
}
void MyTableModel::clear()
{
beginResetModel();
m_datas.clear();
endResetModel();
}
void MyTableModel::CompleteList(QStringList& list)
{
if(list.size() < m_HorizontalHead.size())
{
for(int i = list.size();i <= m_HorizontalHead.size();i++)
{
list.append("");
}
}
}
void MyTableModel::maxRowCheck()
{
if(m_maxRow > 0 && m_datas.size() > m_maxRow)
{
int curRowCount = m_datas.size();
for(int i = 0; i < curRowCount-m_maxRow;i++)
{
m_datas.removeFirst();
}
}
}
结束语
自定义model的增删改查写好了,接下来想试试QStyledItemDelegate代理,用它修改View的表头格式,实现排序查找等功能。
2325

被折叠的 条评论
为什么被折叠?



