Bootstrap

Qt之表格的理解和使用(QTableView和QTableWiget)

QTableView

  在通常情况下,一个应用需要和一批数据(比如数组、列表)进行交互,然后以表格的形式输出这些信息,
这时就要用到QTableView类了。在QtableView中可以使用自定义的数据模型来进行数据单元添加、单元格删除和设置表格标题等操作,
  一般常见的就是QStandardItemModel模型,通过setModel来绑定数据源。
  使用时需要包含#include 和#include

常用方法:
标题操作:
  setHorizontalHeaderLabels(…)//水平标题设置
  setVerticalHeaderLabels(…)//竖直标题设置
  headerData()//获取当前列的文字内容
插入数据:
  setItem()//指定设置的行和列,设置QStandardItem
  setData //insertRow创建modeIndex,指定QModelIndex,设置数据
  appendRow()//可以先将一行item的对象添加一个list中然后一次性插入
删除数据:
  clear() //可以一次将表格内容和表格标题都删掉
  removeRows() //参数1:起始行 参数2:行数 只是想将表格内容清空
  removeColumn() //删除某一列数据
属性设置:
  horizontalHeader()->setSectionResizeMode(…);
  //QHeaderView::Fixed:不可手动调整宽度;
  //QHeaderView::ResizeToContents:表格宽度随内容自动扩展;
  //QHeaderView::Stretch:表格宽度自动根据UI进行计算,不可手动调整宽度
  setShowGrid(false);//隐藏网格线
  setGridStyle(Qt::DotLine);//线的样式

可以自定义数据模型来显示数据:
  qtableview的自定义model对比QTableWidget,可以优化很多内存,特别是在数据量庞大的时候。
  QStandardItemModel可以满足一般的Table的结构,也可以满足树状的结构,
但是自带缺点是使用起来不够灵活,对数据的操作受限于自带的接口,所以需要自定义model。
  QStringListModel:存储简单的字符串列表
  QStandardItemModel:可以用于树结构的存储,提供了层次数据
  QFileSystemModel:本地系统的文件和目录信息
  QSqlQueryModel、QSqlTableModel、QSqlRelationalTableModel:存取数据库数据
  一般情况下满足需求了,不过有时候需要一些定制功能,或者是大量数据下对性能和开销比较注重,觉得自带的model无用功能太多效率比较低,这时候自定义model就比较适合了。
  使用自定义model 同时出于这两方面需要,既为了性能也为了特殊功能。 (***)
  展示大量级数据方法:1)多线程处理 2)动态局部加载
  重写一个model继承QAbstractTableModel,QAbstractTableModel继承自 QAbstractItemModel,用于为 QTableView 提供数据接口,必须实现以下五个接口:
  int rowCount(const QModelIndex& parent = QModelIndex()) const Q_DECL_OVERRIDE;
  int columnCount(const QModelIndex& parent = QModelIndex()) const Q_DECL_OVERRIDE;
  QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
  bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) Q_DECL_OVERRIDE;
  Qt::ItemFlags flags(const QModelIndex& index) const Q_DECL_OVERRIDE;

QTableWidget

  是QTableView的子类,使用的是标准的数据模型,其单元格数据是通过QTableWidgetItem对象来实现。

常用方法:
标题操作:
  setHorizontalHeaderLabels(…)//水平标题设置
  setVerticalHeaderLabels(…)//竖直标题设置
插入数据:
  setItem //通过insertRow插入行 setItem指定行列设置QTableWidgetItem
删除数据:
  clear() //可以一次将表格内容和表格标题都删掉
  removeRow //遍历所有行,进行删除
属性设置:
  setEditTriggers //设置编辑状态
  setColumnWidth //设置列宽
  setSectionResizeMode //设置伸缩状态
  setSelectionMode //设置连续选择状态

代码

widgettable.h:

#ifndef WIDGETTABLE_H
#define WIDGETTABLE_H

#include <QWidget>
#include "tableviewheader.h"
#include <QStandardItemModel>
#include <QMutex>
#include "tablemodel.h"

namespace Ui {
class WidgetTable;
}

typedef  struct TEST_TABLE_DATA_T
{
    int nNo;
    QString sName;
    int nSex;
    int nAge;
    QString sID;
}TEST_TABLE_DATA;

typedef QHash<QString, TEST_TABLE_DATA> QHashTestTable;
typedef QHash<QString, TEST_TABLE_DATA>::iterator QHashTestTableIterator;



class WidgetTable : public QWidget
{
    Q_OBJECT

    enum COLUMN_NUM_E
    {
        COLUMN_CHECKBOX = 0,
        COLUMN_NO,
        COLUMN_NAME,
        COLUMN_SEX,
        COLUMN_AGE,
        COLUMN_ID
    };

public:
    explicit WidgetTable(QWidget *parent = nullptr);
    ~WidgetTable();

    void initTableView();

    void initTableWidget();

    void initCusModelTableView();

    //测试用
    void initTestHash();

    void setColumnWidth(); // 更新列宽
    void resizeEvent(QResizeEvent *); //拉伸

public slots:
    void on_tableViewClick(QModelIndex nIndex);
    void on_tableViewDbClick(QModelIndex nIndex);
    void on_tableViewMenu(const QPoint &pos);
    void on_tableViewSort(int nColumn);
    void on_tableViewSelectAllCheckBoxClick(Qt::CheckState state);


protected:
    void changeEvent(QEvent* event);

private slots:
    void on_Btn_Update_clicked();

    void on_Btn_Get_clicked();
    void on_Btn_Clear_clicked();

    void on_Btn_Update2_clicked();


    void on_Btn_SetCurrentIndex_clicked();

    void on_Btn_SetColor_clicked();

private:
    void retranslateUi();

private:
    Ui::WidgetTable *ui;

private:
    //tableview
    QStandardItemModel *m_pTableModel;
    TableViewHeader *m_pTableHeader;
    QMutex m_lock;
    int m_nCheckedCount;//选中个数
    QMenu *m_actMenu;
    QAction *m_actDevRefresh;

    QHashTestTable m_testTableHash;
    TableModel *m_pModelEx;
};

#endif // WIDGETTABLE_H

widgettable.cpp

#include "widgettable.h"
#include "ui_widgettable.h"
#include <QLinkedList>
#include <QStringList>
#include "commondef.h"
#include <QMenu>

WidgetTable::WidgetTable(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::WidgetTable)
{
    ui->setupUi(this);
    
    initTableView();
    initTestHash();

    initCusModelTableView();

    initTableWidget();
}

WidgetTable::~WidgetTable()
{
    SAFE_FREE(m_actDevRefresh);
    SAFE_FREE(m_actMenu);

    delete ui;
}

/*************************
 * QTableView-初始化
 * ***********************/
void WidgetTable::initTableView()
{
    //自定义表头操作
    m_pTableHeader = new TableViewHeader(Qt::Horizontal, this);//表头
    m_pTableHeader->setStretchLastSection(true);//表头最后一列填充
    ui->tableView->setHorizontalHeader(m_pTableHeader);//设置表头

    //创建model(目前使用的是标准数据模型,可以自定义数据模型),绑定model
    m_pTableModel = new QStandardItemModel(this);
    ui->tableView->setModel(m_pTableModel);//设置model

    //右键菜单创建
    m_actMenu = new QMenu(ui->tableView);
    m_actMenu->setStyleSheet("border:1px solid #4d738b;");
    m_actDevRefresh = new QAction("Refresh", ui->tableView);
    connect(m_actDevRefresh, &QAction::triggered, [this]()
    {
        MY_DEBUG << "Refresh";
    });
    m_actMenu->addAction(m_actDevRefresh);


    //创建表头信息
    QStringList headerList;
    headerList << "" << tr("No.") << tr("Name") << tr("Sex") << tr("Age") << tr("ID");
    m_pTableModel->setHorizontalHeaderLabels(headerList);

    //单击
    connect(ui->tableView, &QTableView::clicked,
            this, &WidgetTable::on_tableViewClick);
    //双击
    connect(ui->tableView, &QTableView::doubleClicked,
            this, &WidgetTable::on_tableViewDbClick);

    //右键菜单设置
    ui->tableView->setContextMenuPolicy(Qt::CustomContextMenu);
    connect(ui->tableView, &QTableView::customContextMenuRequested,
            this, &WidgetTable::on_tableViewMenu);

    //标题头排序
    connect(ui->tableView->horizontalHeader(), &QHeaderView::sectionClicked,
            this, &WidgetTable::on_tableViewSort);
    //全选
    connect(m_pTableHeader, &TableViewHeader::stateChanged,
            this, &WidgetTable::on_tableViewSelectAllCheckBoxClick);

    //设置列宽
    setColumnWidth();

}

/*************************
 * QTableView-设置列宽
 * ***********************/
void WidgetTable::setColumnWidth()
{
    ui->tableView->setColumnWidth(COLUMN_NO, 100);
    ui->tableView->setColumnWidth(COLUMN_NAME, 200);
    ui->tableView->setColumnWidth(COLUMN_SEX, 100);
    ui->tableView->setColumnWidth(COLUMN_AGE, 100);
    //ui->tableView->setColumnWidth(COLUMN_ID, qRound(0.3*width));
}

void WidgetTable::resizeEvent(QResizeEvent *)
{
    setColumnWidth();
}

/*************************
 * QTableView-单击事件
 * ***********************/
void WidgetTable::on_tableViewClick(QModelIndex nIndex)
{
    QMutexLocker guard(&m_lock);

    int nRow = nIndex.row();
    int nCol = nIndex.column();

    Qt::CheckState oldState = m_pTableModel->item(nRow, COLUMN_CHECKBOX)->checkState();
    Qt::CheckState newState;

    if(oldState == Qt::Unchecked)
    {
        newState = Qt::Checked;
        m_nCheckedCount++;
    }
    else
    {
        newState = Qt::Unchecked;
        m_nCheckedCount--;
    }
    m_pTableModel->item(nRow, COLUMN_CHECKBOX)->setCheckState(newState);

    if(newState == Qt::Unchecked || m_nCheckedCount == m_pTableModel->rowCount())
    {
        m_pTableHeader->onStateChanged(newState);//改变标题栏的全选状态
    }
}

/*************************
 * QTableView-双击事件
 * ***********************/
void WidgetTable::on_tableViewDbClick(QModelIndex nIndex)
{
    QMutexLocker guard(&m_lock);

    int row = nIndex.row();

    MY_DEBUG << "Db Click row:" << row;
}

/*************************
 * QTableView-右键菜单显示
 * ***********************/
void WidgetTable::on_tableViewMenu(const QPoint &pos)
{
    //m_actMenu->exec(mapToGlobal(pos));//绝对位置
    m_actMenu->exec(cursor().pos());//相对位置
}

/*************************
 * QTableView-排序
 * ***********************/
void WidgetTable::on_tableViewSort(int nColumn)
{
    QMutexLocker guard(&m_lock);

    //点击表头实现排序
    ui->tableView->horizontalHeader()->setSortIndicatorShown(true);
    ui->tableView->sortByColumn(nColumn);
}

/*************************
 * QTableView-响应表头全选操作
 * ***********************/
void WidgetTable::on_tableViewSelectAllCheckBoxClick(Qt::CheckState state)
{
    QMutexLocker guard(&m_lock);
    int nRowCount = m_pTableModel->rowCount();//当前行数

    for(int i = 0; i < nRowCount; i++)
    {
        m_pTableModel->item(i, COLUMN_CHECKBOX)->setCheckState(state);
    }
}

/*************************
 * QTableView-更新数据
 * ***********************/
void WidgetTable::on_Btn_Update_clicked()
{
    QMutexLocker guard(&m_lock);

    //清空数据
    if(m_pTableModel->rowCount() > 0)
    {
        m_pTableModel->removeRows(0, m_pTableModel->rowCount());
    }

    QHashTestTableIterator i = m_testTableHash.begin();
    int row = 0;
    for(; i != m_testTableHash.end(); ++i)
    {
        TEST_TABLE_DATA data = i.value();

        //可以通过insertRow和setData增加数据
        m_pTableModel->insertRow(row, QModelIndex());
        m_pTableModel->setData(m_pTableModel->index(row, COLUMN_CHECKBOX), "");
        m_pTableModel->setData(m_pTableModel->index(row, COLUMN_NO), row+1);
        m_pTableModel->setData(m_pTableModel->index(row, COLUMN_NAME), data.sName);
        m_pTableModel->setData(m_pTableModel->index(row, COLUMN_SEX), data.nSex);
        m_pTableModel->setData(m_pTableModel->index(row, COLUMN_AGE), data.nAge);
        m_pTableModel->setData(m_pTableModel->index(row, COLUMN_ID), data.sID);

        //也可以通过setItem增加数据
        //m_pTableModel->setItem(row, COLUMN_CHECKBOX, new QStandardItem(""));

        // 设置全部列居中显示
        for(int j = COLUMN_NO; j <= COLUMN_ID; j++)
        {
            m_pTableModel->item(row, j)->setTextAlignment(Qt::AlignCenter);
        }
        row++;
    }
}

/*************************
 * QTableWidget-初始化
 * ***********************/
void WidgetTable::initTableWidget()
{
    //设置禁止修改
    ui->tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
    //双击或获取焦点后单击,进入编辑状态
    //ui->tableWidget->setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::SelectedClicked);

    //设置列数和列宽
    ui->tableWidget->setColumnCount(5);
    ui->tableWidget->setColumnWidth(0, 100);
    ui->tableWidget->setColumnWidth(1, 100);
    ui->tableWidget->setColumnWidth(2, 200);
    ui->tableWidget->setColumnWidth(3, 100);

    //设置列伸缩
    ui->tableWidget->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed);
    ui->tableWidget->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Fixed);
    ui->tableWidget->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Fixed);
    ui->tableWidget->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Fixed);
    ui->tableWidget->horizontalHeader()->setSectionResizeMode(4, QHeaderView::Stretch);

    //设置最后一栏自适应长度
    //ui->tableWidget->horizontalHeader()->setStretchLastSection(true);

    // 表格宽度随内容自动扩展
    //ui->tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);

    //设置连续选择状态
    ui->tableWidget->setSelectionMode(QAbstractItemView::ContiguousSelection);
    //设置为可以选中多个目标,按ctrl键
    //ui->tableWidget->setSelectionMode(QAbstractItemView::ExtendedSelection);

    //开启交替行背景色,在设置style为交替颜色时必须开启
    //ui->tableWidget->setAlternatingRowColors(true);

    //不显示边框
    ui->tableWidget->setGridStyle(Qt::NoPen);
    connect(ui->tableWidget,SIGNAL(itemChanged(QTableWidgetItem*)),
            ui->tableWidget,SLOT(resizeRowsToContents()) );//自动换行

}

/*************************
 * QTableWidget-插入数据
 * ***********************/
void WidgetTable::on_Btn_Get_clicked()
{
    //设置表头
    QStringList headList;
    headList<<"NO."<<"Name"<<"Sex"<<"Age"<<"ID";
    ui->tableWidget->setColumnCount(headList.size());
    ui->tableWidget->setHorizontalHeaderLabels(headList); //设置水平表头为list

    //各列数据
    QStringList nameList;
    nameList << "AAA" << "BBB" << "CCC";
    QStringList sexList;
    sexList << "man" << "woman" << "woman";
    QStringList ageList;
    ageList << "32" << "30" << "3";
    QStringList idList;
    idList << "A11111" << "B11111" << "C111111";

    int num = ageList.size();
    for(int i = 0; i < num; i ++)
    {
        int Col = 0;
        //通过insertRow 和 setItem 插入数据
        int RowCont = ui->tableWidget->rowCount();
        ui->tableWidget->insertRow(RowCont);
        ui->tableWidget->setItem(RowCont, Col++, new QTableWidgetItem(QString::number(RowCont+1)));
        ui->tableWidget->setItem(RowCont, Col++, new QTableWidgetItem(nameList.at(i)));
        ui->tableWidget->setItem(RowCont, Col++, new QTableWidgetItem(sexList.at(i)));
        ui->tableWidget->setItem(RowCont, Col++, new QTableWidgetItem(ageList.at(i)));
        ui->tableWidget->setItem(RowCont, Col++, new QTableWidgetItem(idList.at(i)));

        //ui->tableWidget->setCellWidget()//可以放入组件例如QComboBox

        //设置各列数据居中显示
        for(int j = 0; j < Col; j++)
        {
            ui->tableWidget->item(RowCont, j)->setTextAlignment(Qt::AlignCenter);
        }
    }
    ui->tableWidget->scrollToBottom();
}


/*************************
 * QTableWidget-清除数据
 * ***********************/
void WidgetTable::on_Btn_Clear_clicked()
{
    QMutex mutex;
    QMutexLocker locker(&mutex);

    int RowCont = ui->tableWidget->rowCount();
    for(int i = RowCont; i >= 0; i--)
    {
        ui->tableWidget->removeRow(i);
    }
}



/*************************
 * QTableView-自定义model
 * ***********************/
void WidgetTable::initCusModelTableView()
{
    m_pModelEx = new TableModel();
    ui->tableView_2->setModel(m_pModelEx);

    /*
    int nID = 0;
    //保存和恢复model选中项,因为在resetModel后会失效

    connect(m_pModelEx, &TableModel::modelAboutToBeReset, this, [=]{
      QModelIndex selectedIndex = ui->tableView_2->currentIndex();
      selectedIndex.data(Qt::UserRole).toInt();
    },Qt::DirectConnection);
    connect(m_pModelEx, &TableModel::modelReset, this, [=]{
        //不用担心无效的index,接口内部会处理
        //ui->tableView_2->setCurrentIndex(selectedIndex);
    },Qt::DirectConnection);
    */
}




///

/*************************
 * 数据初始化
 * ***********************/
void WidgetTable::initTestHash()
{
    TEST_TABLE_DATA data[5];
    data[0] = {1, "AAA", 1, 31, "A1111111"};
    data[1] = {2, "BBB", 0, 33, "B1111111"};
    data[2] = {3, "CCC", 1, 29, "C1111111"};
    data[3] = {4, "DDD", 0, 26, "D1111111"};
    data[4] = {5, "EEE", 1, 32, "E1111111"};

    for(int i = 0; i < 5; i++)
    {
        m_testTableHash.insert(data[i].sID, data[i]);
    }
}

void WidgetTable::on_Btn_Update2_clicked()
{
    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 < 10000; i++)
    {
        QVariantMap map;
        map["ID"] = i + 1;
        map["jiange"] = i + 2;
        map["device"] = i + 3;
        map["device type"] = i + 4;
        map["factory name"] = i + 5;
        map["voltage type"] = i + 6;
        map["wf identifier"] = i + 7;
        map["wf tuichu"] = i + 8;
        data_map[QString::number(i) + "id"] = map;
    }
    m_pModelEx->SetData(data_map);
}


void WidgetTable::on_Btn_SetCurrentIndex_clicked()
{
    MY_DEBUG << "on_Btn_SetCurrentIndex_clicked";
    QVector< QPair<int, int> > indexs;
    for(int i = 0; i < 2000; i++)
    {
        indexs.append(qMakePair<int ,int>(i, 2));
    }
    m_pModelEx->setHignlightIndex(indexs);

}

void WidgetTable::on_Btn_SetColor_clicked()
{
    MY_DEBUG << "on_Btn_SetColor_clicked";

    m_pModelEx->setItemText(1, 1, "test");
    m_pModelEx->SetItemData(1, 1, "aoligei");
}


void WidgetTable::changeEvent(QEvent *event)
{
    switch(event->type())
    {
        case QEvent::LanguageChange:
            retranslateUi();
            break;
        default:
            QWidget::changeEvent(event);
    }
}

void WidgetTable::retranslateUi()
{
    ui->label->setText(tr("Use Table View:"));
    ui->label_2->setText(tr("Use Table Widget:"));
    ui->label_3->setText(tr("Use Table View(Custom Model)"));
    ui->Btn_Get->setText(tr("Get"));
    ui->Btn_Clear->setText(tr("Clear"));
    ui->Btn_Update->setText(tr("Update"));
    ui->Btn_Update2->setText(tr("Update"));
}


tableviewheader.h

#ifndef TABLEVIEWHEADER_H
#define TABLEVIEWHEADER_H
#include <QHeaderView>
#include <QEvent>
#include <QPainter>
#include <QCheckBox>
#include <QMouseEvent>
#include <qnamespace.h>
#include <QDebug>

#define CHECK_BOX_COLUMN 0

class TableViewHeader :public QHeaderView
{
    Q_OBJECT
public:
    TableViewHeader(Qt::Orientation orientation,QWidget *parent = nullptr);

public:
    void onStateChanged(int state);
    bool isChecked();

signals:
    void stateChanged(Qt::CheckState);

protected:
    void paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const;
    void mousePressEvent(QMouseEvent *event);
    void mouseReleaseEvent(QMouseEvent *event);
    bool event(QEvent *event);

private:
    bool m_bPressed;
    bool m_bChecked;
    bool m_bTristate;
    bool m_bNoChange;
    bool m_bMoving;
};

#endif // TABLEVIEWHEADER_H


tableviewheader.cpp

#include "tableviewheader.h"


TableViewHeader::TableViewHeader(Qt::Orientation orientation, QWidget *parent)
    : QHeaderView(orientation, parent),
      m_bPressed(false),
      m_bChecked(false),
      m_bTristate(false),
      m_bNoChange(false),
      m_bMoving(false)
{
    setHighlightSections(false);
    setMouseTracking(true);
    // 响应鼠标
    setSectionsClickable(true);
}

// 槽函数,用于更新复选框状态
void TableViewHeader::onStateChanged(int state)
{
    if (state == Qt::PartiallyChecked)
    {
        m_bTristate = true;
        m_bNoChange = true;
    }
    else
    {
        m_bNoChange = false;
    }

    m_bChecked = (state != Qt::Unchecked);
    update();
}

bool TableViewHeader::isChecked()
{
    return m_bChecked;
}

// 绘制复选框
void TableViewHeader::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const
{
    painter->save();
    QHeaderView::paintSection(painter, rect, logicalIndex);
    painter->restore();

    if (logicalIndex == CHECK_BOX_COLUMN)
    {
        QStyleOptionButton option;
        option.initFrom(this);

        if (m_bChecked)
            option.state |= QStyle::State_Sunken;

        if (m_bTristate && m_bNoChange)
            option.state |= QStyle::State_NoChange;
        else
            option.state |= m_bChecked ? QStyle::State_On : QStyle::State_Off;
        if (testAttribute(Qt::WA_Hover) && underMouse())
        {
            if (m_bMoving)
                option.state |= QStyle::State_MouseOver;
            else
                option.state &= ~QStyle::State_MouseOver;
        }

        QCheckBox checkBox;
        option.iconSize = QSize(14, 14);
        option.rect = QRect(3,7,14,14);
        style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &option, painter, &checkBox);
    }
}

// 鼠标按下表头
void TableViewHeader::mousePressEvent(QMouseEvent *event)
{
    int nColumn = logicalIndexAt(event->pos());
    if ((event->buttons() & Qt::LeftButton) && (nColumn == CHECK_BOX_COLUMN))
    {
        m_bPressed = true;
    }
    else
    {
        QHeaderView::mousePressEvent(event);
    }
}

// 鼠标从表头释放,发送信号,更新model数据
void TableViewHeader::mouseReleaseEvent(QMouseEvent *event)
{
    if (m_bPressed)
    {
        if (m_bTristate && m_bNoChange)
        {
            m_bChecked = true;
            m_bNoChange = false;
        }
        else
        {
            m_bChecked = !m_bChecked;
        }

        update();

        Qt::CheckState state = m_bChecked ? Qt::Checked : Qt::Unchecked;

        emit stateChanged(state);
    }
    else
    {
        QHeaderView::mouseReleaseEvent(event);
    }

    m_bPressed = false;
}

// 鼠标滑过、离开,更新复选框状态
bool TableViewHeader::event(QEvent *event)
{
    if (event->type() == QEvent::Enter || event->type() == QEvent::Leave)
    {
        QMouseEvent *pEvent = static_cast<QMouseEvent *>(event);
        int nColumn = logicalIndexAt(pEvent->x());
        if (nColumn == CHECK_BOX_COLUMN)
        {
            m_bMoving = (event->type() == QEvent::Enter);

            update();
            return true;
        }
    }

    return QHeaderView::event(event);
}


tablemodel.h

#ifndef TableModel_H
#define TableModel_H
#include<QAbstractTableModel>
#include <QObject>
#include<QVector>
#include<QMap>
#include<QStringList>


//自定义QTableView model
class TableModel : public QAbstractTableModel
{
    Q_OBJECT
public:
    explicit TableModel(QObject *parent=nullptr);

    void SetData(const QVariantMap &map);
    void setHignlightIndex(QVector< QPair<int, int> > vec_index);

    //itemFlag相关的设置是针对项的,与View中的拖放、编辑、选择等属性的设置是独立的,一个项能否进行拖放、编辑、选择等
    virtual Qt::ItemFlags flags(const QModelIndex &index) const;
    QVariant headerData(int section, Qt::Orientation orientation, int role) const;

    //行列数
    virtual int rowCount(const QModelIndex &parent=QModelIndex()) const;
    virtual int columnCount(const QModelIndex &parent) const;

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

    QString itemText(int row, int column) const;
    QString itemText(const QModelIndex &index) const;
    void setItemText(const QModelIndex &index, const QString &str);
    void setItemText(int row, int column, const QString &str);

    QVariant ItemData(const QModelIndex &index) const;
    QVariant ItemData(int row, int column) const;
    void SetItemData(const QModelIndex &index, const QVariant &data);
    void SetItemData(int row, int column, const QVariant &data);

    void clear();

signals:
    void itemChanged(const QModelIndex &index, const QVariant &value);


private:
    QStringList m_pHeaders;

    QStringList m_hor_hedlbls;                      // 橫向标题
    QStringList m_vec_hedlbls;                      // 纵向标题
    QVariantMap m_table_map, m_data_map;
    QVector<QPair<int, int> > m_highlight_indexs;   // 背景高亮的indexs

};

#endif // TableModel_H


tablemodel.cpp

#include "tablemodel.h"

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

void TableModel::SetData(const QVariantMap &map)
{
    //重置model数据之前调用beginResetModel,此时会触发modelAboutToBeReset信号
    beginResetModel();

    m_hor_hedlbls = map["headerlabel"].toStringList();
    m_table_map = map;
    m_vec_hedlbls = map.keys();

    //数据设置结束后调用endResetModel,此时会触发modelReset信号
    endResetModel();

    //如果表的行列数是固定的,只是数据变更了,我们可以用 dataChanged 信号来请求刷新
    //emit dataChanged(index(0,0),index(RowMax-1,ColMax-1),QVector<int>());
}

void TableModel::setHignlightIndex(QVector<QPair<int, int> > vec_index)
{
    beginResetModel();
    m_highlight_indexs = vec_index;
    endResetModel();
}

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

int TableModel::rowCount(const QModelIndex &parent) const
{
    if(parent.isValid())
        return 0;
    else
        return m_table_map.size();

}

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

QVariant TableModel::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();
}

bool TableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if (index.isValid() && role == Qt::EditRole)
    {
        QVariantMap *_map = (QVariantMap*)(m_table_map[m_vec_hedlbls[index.row()]].data());
        if((*_map)[m_hor_hedlbls[index.column()]] != value)
        {
            (*_map)[m_hor_hedlbls[index.column()]] = value;
            emit dataChanged(index, index);
            emit itemChanged(index, value);

            return true;
        }
    }
    return false;
}

QString TableModel::itemText(int row, int column) const
{
    QVariantMap *_map = (QVariantMap*)(m_table_map[m_vec_hedlbls[row]].data());
    return (*_map)[m_hor_hedlbls[column]].toString();
}

QString TableModel::itemText(const QModelIndex &index) const
{
    QVariantMap *_map = (QVariantMap*)(m_table_map[m_vec_hedlbls[index.row()]].data());
    return (*_map)[m_hor_hedlbls[index.column()]].toString();
}

void TableModel::setItemText(const QModelIndex &index, const QString &str)
{
    QVariantMap *_map = (QVariantMap*)(m_table_map[m_vec_hedlbls[index.row()]].data());
    (*_map)[m_hor_hedlbls[index.column()]] = str;
}

void TableModel::setItemText(int row, int column, const QString &str)
{
    QVariantMap *_map = (QVariantMap*)(m_table_map[m_vec_hedlbls[row]].data());
    (*_map)[m_hor_hedlbls[column]] = str;
}

QVariant TableModel::ItemData(const QModelIndex &index) const
{
    QVariantMap *_map = (QVariantMap*)(m_data_map[m_vec_hedlbls[index.row()]].data());
    return (*_map)[m_hor_hedlbls[index.column()]];
}

QVariant TableModel::ItemData(int row, int column) const
{
    QVariantMap *_map = (QVariantMap*)(m_data_map[m_vec_hedlbls[row]].data());
    return (*_map)[m_hor_hedlbls[column]];
}

void TableModel::SetItemData(const QModelIndex &index, const QVariant &data)
{
    QString vec_lbl = m_vec_hedlbls[index.row()];
    if(!m_data_map.contains(vec_lbl))   m_data_map[vec_lbl] = QVariantMap();

    QVariantMap *_map = (QVariantMap*)(m_data_map[vec_lbl].data());
    (*_map)[m_hor_hedlbls[index.column()]] = data;

}

void TableModel::SetItemData(int row, int column, const QVariant &data)
{
    QString vec_lbl = m_vec_hedlbls[row];
    if(!m_data_map.contains(vec_lbl))   m_data_map[vec_lbl] = QVariantMap();

    QVariantMap *_map = (QVariantMap*)(m_data_map[vec_lbl].data());
    (*_map)[m_hor_hedlbls[column]] = data;
}

void TableModel::clear()
{
    beginResetModel();
    m_table_map.clear();
    m_data_map.clear();
    m_hor_hedlbls.clear();
    m_vec_hedlbls.clear();
    m_highlight_indexs.clear();
    endResetModel();
}

QVariant TableModel::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); // 垂直表头的序号
}


实现效果:

在这里插入图片描述

工程下载路径:

https://download.csdn.net/download/linyibin_123/86512679

;