Qt 基于QAbstractTableModel自定义TableModel

前言

平时用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的表头格式,实现排序查找等功能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值