Qt多级表头(QTableView表头合并)

本文采用重写QHeaderView的方法,不是联合两个QTableView的方法。

QHeaderView类为item views提供标题行或标题列。
QHeaderView显示item views中使用的标题,例如QTableView和QTreeView类。它取代了Qt3先前用于相同目的的QHeader类,但使用Qt的model/view体系结构与item view类保持一致。
QHeaderView类是model/view类之一,是Qt model/view框架的一部分。
头部使用QAbstractItemModel::headerData()函数从模型中获取每个部分的数据。您可以使用QAbstractItemModel::setHeaderData()来设置数据。
每个头文件都有一个orientation()和若干节,由count()函数给出。A section指的是标题的一部分——根据方向,可以是行,也可以是列。
section可以使用moveSection()和resizeSection()来移动和调整大小;它们也可以通过hideSection()和showSection()隐藏和显示。
头文件的每个部分都由其section()指定的节ID描述,并且可以位于头文件中的特定visualIndex()中。section可以使用setSortIndicator()设置排序指示符;这表示关联项视图中的项是否按照该部分给出的顺序排序。
对于水平标头,该部分相当于模型中的一列,而对于垂直标头,该部分相当于模型中的一行。


实例1:

FSItemDelegate.h

#pragma once

/*
自定义委托类
*/
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define FS_WHITE_BRUSH QBrush((QColor(255, 255, 255)))
#define FS_RED_BRUSH QBrush((QColor(255, 0, 0)))
#define FS_GREEN_BRUSH QBrush((QColor(0, 255, 0)))

enum class DELEGATE_TYPE
{
    READONLY,     //只读委托
    EDIDTEXT,     //可修改文本框委托
    COMBOBOX,     //组合框
    PASSWDLINE,   //密码修改框

};
//  只读委托
class ReadOnlyDelegate : public QItemDelegate
{
    Q_OBJECT

public:
    ReadOnlyDelegate(QObject *parent = 0) : QItemDelegate(parent) { }

    QWidget *createEditor(QWidget*parent, const QStyleOptionViewItem &option,
        const QModelIndex &index) const
    {
        return nullptr;
    }
};

MultistageHeader.h

#pragma once

#include 
#include "ui_MultistageHeader.h"
#include "RbTableHeaderView.h"
#include "FSItemDelegate.h"
#include 
#include 
#include 
#include "string"
#include 
class MultistageHeader : public QWidget
{
    Q_OBJECT

public:
    MultistageHeader(QWidget *parent = Q_NULLPTR);

private:
    Ui::MultistageHeaderClass ui;
    RbTableHeaderView* m_pHeader;
    QStandardItemModel* m_pDataModel;
    QList m_pVecItems;
    QTableView * m_pView;
    QHBoxLayout* m_pLayout = nullptr;
    QHBoxLayout* m_Hlayout;

};

MultistageHeader.cpp

#include "MultistageHeader.h"

MultistageHeader::MultistageHeader(QWidget *parent)
    : QWidget(parent)
{
    ui.setupUi(this);

    m_pView = new QTableView(this);
    m_pView->setContextMenuPolicy(Qt::CustomContextMenu);
    connect(m_pView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(tableContextMenuRequested(QPoint)));
    m_pHeader = new RbTableHeaderView(Qt::Horizontal, 3, 8, m_pView);
    QAbstractItemModel* pHeaderModel = m_pHeader->model();

    m_pHeader->setSpan(0, 0, 1, 4);
    m_pHeader->setSpan(0, 4, 1, 4);
    m_pHeader->setSpan(1, 0, 1, 2);
    m_pHeader->setSpan(1, 2, 1, 2);
    m_pHeader->setSpan(1, 4, 1, 2);
    m_pHeader->setSpan(1, 6, 1, 2);
    for (int i = 0; i < 8; i++)
    {
        m_pHeader->setSpan(2, i, 1, 1);
    }

    //一级
    pHeaderModel->setData(pHeaderModel->index(0, 0), QString(u8"横向尺寸"), Qt::DisplayRole);
    pHeaderModel->setData(pHeaderModel->index(0, 4), QString(u8"纵向尺寸"), Qt::DisplayRole);
    //二级
    pHeaderModel->setData(pHeaderModel->index(1, 0), QStringLiteral("极耳宽度"), Qt::DisplayRole);
    pHeaderModel->setData(pHeaderModel->index(1, 2), QStringLiteral("极耳高度"), Qt::DisplayRole);
    pHeaderModel->setData(pHeaderModel->index(1, 4), QStringLiteral("极片宽度"), Qt::DisplayRole);
    pHeaderModel->setData(pHeaderModel->index(1, 6), QStringLiteral("极耳间距"), Qt::DisplayRole);
    //三级
    pHeaderModel->setData(pHeaderModel->index(2, 0), QStringLiteral("CCD测量值"), Qt::DisplayRole);
    pHeaderModel->setData(pHeaderModel->index(2, 1), QStringLiteral("真值"), Qt::DisplayRole);
    pHeaderModel->setData(pHeaderModel->index(2, 2), QStringLiteral("CCD测量值"), Qt::DisplayRole);
    pHeaderModel->setData(pHeaderModel->index(2, 3), QStringLiteral("真值"), Qt::DisplayRole);
    pHeaderModel->setData(pHeaderModel->index(2, 4), QStringLiteral("CCD测量值"), Qt::DisplayRole);
    pHeaderModel->setData(pHeaderModel->index(2, 5), QStringLiteral("真值"), Qt::DisplayRole);
    pHeaderModel->setData(pHeaderModel->index(2, 6), QStringLiteral("CCD测量值"), Qt::DisplayRole);
    pHeaderModel->setData(pHeaderModel->index(2, 7), QStringLiteral("真值"), Qt::DisplayRole);


    m_pHeader->setMinimumHeight(90);
    m_pHeader->setRowHeight(0, 30);
    m_pHeader->setRowHeight(1, 30);
    m_pHeader->setRowHeight(2, 30);
    //m_pHeader->setMinimumHeight(60);
    int a = m_pHeader->height();
    m_pHeader->setSectionsClickable(false);
    m_pHeader->setCellBackgroundColor(pHeaderModel->index(0, 0), 0xcfcfcf);
    m_pHeader->setCellBackgroundColor(pHeaderModel->index(0, 4), 0xcfcfcf);
    m_pHeader->setCellBackgroundColor(pHeaderModel->index(1, 0), 0xcfcfcf);
    m_pHeader->setCellBackgroundColor(pHeaderModel->index(1, 2), 0xcfcfcf);
    m_pHeader->setCellBackgroundColor(pHeaderModel->index(1, 4), 0xcfcfcf);
    m_pHeader->setCellBackgroundColor(pHeaderModel->index(1, 6), 0xcfcfcf);
    for (int i = 0; i < 8; i++)
    {
        m_pHeader->setCellBackgroundColor(pHeaderModel->index(2, i), 0xcfcfcf);
    }

    int rowCount = 10;
    m_pDataModel = new QStandardItemModel;
    for (int i = 0; i < rowCount; i++)
    {
        QList items;
        for (int j = 0; j < 8; j++)
        {
            items.append(new QStandardItem);
            m_pVecItems.append(items[j]);
        }

        m_pDataModel->appendRow(items);
    }

    m_pView->setModel(m_pDataModel);
    m_pView->setHorizontalHeader(m_pHeader);

    ReadOnlyDelegate *readonlydelegate = new ReadOnlyDelegate();
    m_pView->setItemDelegateForColumn(0, readonlydelegate);
    m_pView->setItemDelegateForColumn(2, readonlydelegate);
    m_pView->setItemDelegateForColumn(4, readonlydelegate);
    m_pView->setItemDelegateForColumn(6, readonlydelegate);

    if (!m_pLayout)
    {
        m_pLayout = new QHBoxLayout(this);
        this->setLayout(m_pLayout);
    }

    m_pLayout->addWidget(m_pView);
}

RbTableHeaderView.h

#ifndef RBTABLEHEADERVIEW_H_
#define RBTABLEHEADERVIEW_H_
#include 
#include 
#include 
#include 
#include 

enum eRbHeaderRole
{
    COLUMN_SPAN_ROLE = Qt::UserRole + 1,
    ROW_SPAN_ROLE,
    COLUMN_SIZE_ROLE,
    ROW_SIZE_ROLE,
};

class RbTableHeaderItem
{
public:
    RbTableHeaderItem(RbTableHeaderItem* parent = nullptr);
    RbTableHeaderItem(int arow, int acolumn, RbTableHeaderItem* parent = 0);
    virtual ~RbTableHeaderItem();

    // interface
    RbTableHeaderItem* insertChild(int row, int col);
    const RbTableHeaderItem* child(int row, int col) const;
    RbTableHeaderItem* child(int row, int col);
    void setData(const QVariant& data, int role);
    QVariant data(int role = Qt::UserRole + 1) const;
    inline int column() const { return column_prop; }
    inline int row() const { return row_prop; }
    RbTableHeaderItem* parent() { return parent_item; }
    void setText(const QString& text);
    void clear();

private:
    // properties
    int row_prop;                                              //当前表格在表头中所在行
    int column_prop;                                           //当前表格在表头中所在行
    // inherent features
    RbTableHeaderItem* parent_item;
    QHash, RbTableHeaderItem*> child_items;
    QHash role_datas;                           //
};


class RbTableHeaderModel : public QAbstractTableModel
{
    Q_OBJECT
public:
    RbTableHeaderModel(int rows, int cols, QObject* parent = 0);
    virtual ~RbTableHeaderModel();

public:
    // override
    virtual QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const Q_DECL_OVERRIDE;
    virtual int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE { Q_UNUSED(parent); return row_count_prop; }
    virtual int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE { Q_UNUSED(parent); return column_count_prop; }
    virtual QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE;
    virtual bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole) Q_DECL_OVERRIDE;
    virtual Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE;

private:
    // properties
    int row_count_prop;                    //表头行总数
    int column_count_prop;                 //表头列总数
    // inherent features
    RbTableHeaderItem* root_item;          //表头视图模型的根节点
};


class RbTableHeaderView : public QHeaderView
{
    Q_OBJECT
public:
    RbTableHeaderView(Qt::Orientation orientation, int rows, int columns, QWidget* parent = 0);
    virtual ~RbTableHeaderView();

    void setRowHeight(int row, int rowHeight);
    void setColumnWidth(int col, int colWidth);
    void setSpan(int row, int column, int rowSpanCount, int columnSpanCount);
    void setCellBackgroundColor(const QModelIndex& index, const QColor&);
    void setCellForegroundColor(const QModelIndex& index, const QColor&);


protected:
    // override
    virtual void mousePressEvent(QMouseEvent* event) Q_DECL_OVERRIDE;
    virtual QModelIndex indexAt(const QPoint&);
    virtual void paintSection(QPainter* painter, const QRect& rect, int logicalIndex) const Q_DECL_OVERRIDE;
    virtual QSize sectionSizeFromContents(int logicalIndex) const Q_DECL_OVERRIDE;

    // inherent features
    QModelIndex columnSpanIndex(const QModelIndex& currentIndex) const;
    QModelIndex rowSpanIndex(const QModelIndex& currentIndex) const;
    int columnSpanSize(int row, int from, int spanCount) const;
    int rowSpanSize(int column, int from, int spanCount) const;
    int getSectionRange(QModelIndex& index, int* beginSection, int* endSection) const;

protected slots:
    void onSectionResized(int logicalIdx, int oldSize, int newSize);

signals:
    void sectionPressed(int from, int to);
};

#endif /* RBTABLEHEADERVIEW_H_ */

RbTableHeaderView.cpp

#include "RbTableHeaderView.h"
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

RbTableHeaderItem::RbTableHeaderItem(RbTableHeaderItem* parent):
   row_prop(0),column_prop(0),parent_item(parent)
{
}

RbTableHeaderItem::RbTableHeaderItem(int arow, int acolumn, RbTableHeaderItem* parent):
    row_prop(arow),column_prop(acolumn),parent_item(parent)
{
}

RbTableHeaderItem::~RbTableHeaderItem()
{
    qDebug() << "~RbTableHeaderItem()" << endl;
    clear();
}

RbTableHeaderItem* RbTableHeaderItem::insertChild(int row, int col)
{
    RbTableHeaderItem* newChild = new RbTableHeaderItem(row, col, this);
    child_items.insert(QPair(row, col), newChild);
    return newChild;
}

const RbTableHeaderItem* RbTableHeaderItem::child(int row, int col) const
{
    QHash, RbTableHeaderItem*>::const_iterator itr = child_items.find(QPair(row, col));
    if (itr != child_items.end()) return itr.value();
    return nullptr;
}

RbTableHeaderItem* RbTableHeaderItem::child(int row, int col)
{
    QHash, RbTableHeaderItem*>::iterator itr = child_items.find(QPair(row, col));
    if (itr != child_items.end()) return itr.value();
    return nullptr;
}

void RbTableHeaderItem::setText(const QString& text)
{
    role_datas.insert(Qt::DisplayRole, text);
}

void RbTableHeaderItem::clear()
{
    QList items = child_items.values();
    foreach(RbTableHeaderItem* item, items)
    {
        if (item) delete item;
        item = nullptr;
    }
    child_items.clear();
}

QVariant RbTableHeaderItem::data(int role) const
{
    QHash::const_iterator itr = role_datas.find(role);
    if (itr != role_datas.end()) return itr.value();
    return QVariant();
}

void RbTableHeaderItem::setData(const QVariant& data, int role)
{
    role_datas.insert(role, data);
}

RbTableHeaderModel::RbTableHeaderModel(int rows, int cols, QObject* parent) :
QAbstractTableModel(parent),row_count_prop(rows),column_count_prop(cols),root_item(new RbTableHeaderItem())
{
}

RbTableHeaderModel::~RbTableHeaderModel()
{
    qDebug() << "~RbTableHeaderModel()" << endl;
    root_item->clear();
    delete root_item;
    root_item = nullptr;
}

QModelIndex RbTableHeaderModel::index(int row, int column, const QModelIndex & parent) const
{
    if (!hasIndex(row, column, parent)) return QModelIndex();

    RbTableHeaderItem* parentItem;
    if (!parent.isValid()) parentItem = root_item; // parent item is always the root_item on table model
    else parentItem = static_cast(parent.internalPointer()); // no effect

    RbTableHeaderItem* childItem = parentItem->child(row, column);
    if (!childItem) childItem = parentItem->insertChild(row, column);
    return createIndex(row, column, childItem);

    return QModelIndex();
}

QVariant RbTableHeaderModel::data(const QModelIndex& index, int role) const
{
    if (!index.isValid())
        return QVariant();

    if (index.row() >= row_count_prop || index.row() < 0 || index.column() >= column_count_prop || index.column() < 0)
        return QVariant();

    RbTableHeaderItem* item = static_cast(index.internalPointer());

    return item->data(role);
}

bool RbTableHeaderModel::setData(const QModelIndex & index, const QVariant & value, int role)
{
    if (index.isValid())
    {
        RbTableHeaderItem* item = static_cast(index.internalPointer());
        if (role == COLUMN_SPAN_ROLE)
        {
            int col = index.column();
            int span = value.toInt();
            if (span > 0) // span size should be more than 1, else nothing to do
            {
                if (col + span - 1 >= column_count_prop) // span size should be less than whole columns,
                    span = column_count_prop - col;
                item->setData(span, COLUMN_SPAN_ROLE);
            }
        }
        else if (role == ROW_SPAN_ROLE)
        {
            int row = index.row();
            int span = value.toInt();
            if (span > 0) // span size should be more than 1, else nothing to do
            {
                if (row + span - 1 >= row_count_prop)
                    span = row_count_prop - row;
                item->setData(span, ROW_SPAN_ROLE);
            }
        }
        else
            item->setData(value, role);

        return true;
    }
    return false;
}

Qt::ItemFlags RbTableHeaderModel::flags(const QModelIndex &index) const
{
    if (!index.isValid())
        return Qt::ItemIsEnabled;
    return QAbstractTableModel::flags(index) | Qt::ItemIsEditable;
}


RbTableHeaderView::RbTableHeaderView(Qt::Orientation orientation, int rows, int columns, QWidget* parent) :
    QHeaderView(orientation, parent)
{
    QSize baseSectionSize;
    if (orientation == Qt::Horizontal)
    {
        baseSectionSize.setWidth(defaultSectionSize());
        baseSectionSize.setHeight(20);
    }
    else
    {
        baseSectionSize.setWidth(50);
        baseSectionSize.setHeight(defaultSectionSize());
    }

    // create header model
    RbTableHeaderModel* headerModel = new RbTableHeaderModel(rows, columns);

    // set default size of item
    for (int row = 0; row < rows; ++row)
        for (int col = 0; col < columns; ++col)
            headerModel->setData(headerModel->index(row, col), baseSectionSize, Qt::SizeHintRole);

    setModel(headerModel);

    connect(this, SIGNAL(sectionResized(int, int, int)), this, SLOT(onSectionResized(int, int, int)));
}

RbTableHeaderView::~RbTableHeaderView()
{
    qDebug() << "~RbTableHeaderView()" << endl;
    RbTableHeaderModel* md = qobject_cast(model());
    if (md) delete md;
    setModel(nullptr);
}

void RbTableHeaderView::setRowHeight(int row, int rowHeight)
{
    RbTableHeaderModel* md = qobject_cast(model());
    const int cols = md->columnCount();
    for (int col = 0; col < cols; ++col)
    {
        QSize sz = md->index(row, col).data(Qt::SizeHintRole).toSize();
        sz.setHeight(rowHeight);
        md->setData(md->index(row, col), sz, Qt::SizeHintRole);
    }
    if (orientation() == Qt::Vertical)
        resizeSection(row, rowHeight);
}

void RbTableHeaderView::setColumnWidth(int col, int colWidth)
{
    RbTableHeaderModel* md = qobject_cast(model());
    const int rows = md->rowCount();
    for (int row = 0; row < rows; ++row)
    {
        QSize sz = md->index(row, col).data(Qt::SizeHintRole).toSize();
        sz.setWidth(colWidth);
        md->setData(md->index(row, col), sz, Qt::SizeHintRole);
    }
    if (orientation() == Qt::Horizontal)
        resizeSection(col, colWidth);
}


void RbTableHeaderView::setSpan(int row, int column, int rowSpanCount, int columnSpanCount)
{
    RbTableHeaderModel* md = qobject_cast(model());
    QModelIndex idx = md->index(row, column);
    if (rowSpanCount > 0)
        md->setData(idx, rowSpanCount, ROW_SPAN_ROLE);
    if (columnSpanCount)
        md->setData(idx, columnSpanCount, COLUMN_SPAN_ROLE);
}

void RbTableHeaderView::setCellBackgroundColor(const QModelIndex& index, const QColor& color)
{
    RbTableHeaderModel* md = qobject_cast(model());
    md->setData(index, color, Qt::BackgroundRole);
}

void RbTableHeaderView::setCellForegroundColor(const QModelIndex& index, const QColor& color)
{
    RbTableHeaderModel* md = qobject_cast(model());
    md->setData(index, color, Qt::ForegroundRole);
}


void RbTableHeaderView::mousePressEvent(QMouseEvent* event)
{
    QHeaderView::mousePressEvent(event);
    QPoint pos = event->pos();
    QModelIndex index = indexAt(pos);
    const int OTN = orientation();
    if (index.isValid())
    {
        int beginSection = -1;
        int endSection = -1;
        int numbers = 0;
        numbers = getSectionRange(index, &beginSection, &endSection);
        if (numbers > 0)
        {
            emit sectionPressed(beginSection, endSection);
            return;
        }
        else
        {
            const RbTableHeaderModel* tblModel = qobject_cast(this->model());
            const int LEVEL_CNT = (OTN == Qt::Horizontal) ? tblModel->rowCount() : tblModel->columnCount();
            int logicalIdx = (OTN == Qt::Horizontal) ? index.column() : index.row();
            int curLevel = (OTN == Qt::Horizontal) ? index.row() : index.column();
            for (int i = 0; i < LEVEL_CNT; ++i)
            {
                QModelIndex cellIndex = (OTN == Qt::Horizontal) ? tblModel->index(i, logicalIdx) : tblModel->index(logicalIdx, i);
                numbers = getSectionRange(cellIndex, &beginSection, &endSection);
                if (numbers > 0)
                {
                    if (beginSection <= logicalIdx && logicalIdx <= endSection)
                    {
                        int beginLevel = (OTN == Qt::Horizontal) ? cellIndex.row() : cellIndex.column();
                        QVariant levelSpanCnt = cellIndex.data((OTN == Qt::Horizontal) ? ROW_SPAN_ROLE : COLUMN_SPAN_ROLE);
                        if (!levelSpanCnt.isValid())
                            continue;
                        int endLevel = beginLevel + levelSpanCnt.toInt() - 1;
                        if (beginLevel <= curLevel && curLevel <= endLevel)
                        {
                            emit sectionPressed(beginSection, endSection);
                            break;
                        }
                    }
                }
            }
        }
    }
}

QModelIndex RbTableHeaderView::indexAt(const QPoint& pos)
{
    const RbTableHeaderModel* tblModel = qobject_cast(this->model());
    const int OTN = orientation();
    const int ROWS = tblModel->rowCount();
    const int COLS = tblModel->columnCount();
    int logicalIdx = logicalIndexAt(pos);

    if (OTN == Qt::Horizontal)
    {
        int dY = 0;
        for (int row = 0; row < ROWS; ++row)
        {
            QModelIndex cellIndex = tblModel->index(row, logicalIdx);
            dY += cellIndex.data(Qt::SizeHintRole).toSize().height();
            if (pos.y() <= dY) return cellIndex;
        }
    }
    else
    {
        int dX = 0;
        for (int col = 0; col < COLS; ++col)
        {
            QModelIndex cellIndex = tblModel->index(logicalIdx, col);
            dX += cellIndex.data(Qt::SizeHintRole).toSize().width();
            if (pos.x() <= dX) return cellIndex;
        }
    }

    return QModelIndex();
}

void RbTableHeaderView::paintSection(QPainter* painter, const QRect& rect, int logicalIdx) const
{
    const RbTableHeaderModel* tblModel = qobject_cast(this->model());               //表头视图模型
    const int OTN = orientation();                                                                       //获取表头方向
    const int LEVEL_CNT = (OTN == Qt::Horizontal) ? tblModel->rowCount() : tblModel->columnCount();      //获取列数或者行数
    for (int i = 0; i < LEVEL_CNT; ++i)
    {
        QModelIndex cellIndex = (OTN == Qt::Horizontal) ? tblModel->index(i, logicalIdx) : tblModel->index(logicalIdx, i);
        QSize cellSize = cellIndex.data(Qt::SizeHintRole).toSize();
        QRect sectionRect(rect);

        // set position of the cell
        if (OTN == Qt::Horizontal)
            sectionRect.setTop(rowSpanSize(logicalIdx, 0, i)); // distance from 0 to i-1 rows
        else
            sectionRect.setLeft(columnSpanSize(logicalIdx, 0, i));

        sectionRect.setSize(cellSize);

        // check up span column or row
        QModelIndex colSpanIdx = columnSpanIndex(cellIndex);
        QModelIndex rowSpanIdx = rowSpanIndex(cellIndex);
        if (colSpanIdx.isValid())
        {
            int colSpanFrom = colSpanIdx.column();
            int colSpanCnt = colSpanIdx.data(COLUMN_SPAN_ROLE).toInt();
            int colSpanTo = colSpanFrom + colSpanCnt - 1;
            int colSpan = columnSpanSize(cellIndex.row(), colSpanFrom, colSpanCnt);
            if (OTN == Qt::Horizontal)
                sectionRect.setLeft(sectionViewportPosition(colSpanFrom));
            else
            {
                sectionRect.setLeft(columnSpanSize(logicalIdx, 0, colSpanFrom));
                i = colSpanTo;
            }

            sectionRect.setWidth(colSpan);

            // check up  if the column span index has row span
            QVariant subRowSpanData = colSpanIdx.data(ROW_SPAN_ROLE);
            if (subRowSpanData.isValid())
            {
                int subRowSpanFrom = colSpanIdx.row();
                int subRowSpanCnt = subRowSpanData.toInt();
                int subRowSpanTo = subRowSpanFrom + subRowSpanCnt - 1;
                int subRowSpan = rowSpanSize(colSpanFrom, subRowSpanFrom, subRowSpanCnt);
                if (OTN == Qt::Vertical)
                    sectionRect.setTop(sectionViewportPosition(subRowSpanFrom));
                else
                {
                    sectionRect.setTop(rowSpanSize(colSpanFrom, 0, subRowSpanFrom));
                    i = subRowSpanTo;
                }
                sectionRect.setHeight(subRowSpan);
            }
            cellIndex = colSpanIdx;
        }
        if (rowSpanIdx.isValid())
        {
            int rowSpanFrom = rowSpanIdx.row();
            int rowSpanCnt = rowSpanIdx.data(ROW_SPAN_ROLE).toInt();
            int rowSpanTo = rowSpanFrom + rowSpanCnt - 1;
            int rowSpan = rowSpanSize(cellIndex.column(), rowSpanFrom, rowSpanCnt);
            if (OTN == Qt::Vertical)
                sectionRect.setTop(sectionViewportPosition(rowSpanFrom));
            else
            {
                sectionRect.setTop(rowSpanSize(logicalIdx, 0, rowSpanFrom));
                i = rowSpanTo;
            }
            sectionRect.setHeight(rowSpan);

            // check up if the row span index has column span
            QVariant subColSpanData = rowSpanIdx.data(COLUMN_SPAN_ROLE);
            if (subColSpanData.isValid())
            {
                int subColSpanFrom = rowSpanIdx.column();
                int subColSpanCnt = subColSpanData.toInt();
                int subColSpanTo = subColSpanFrom + subColSpanCnt - 1;
                int subColSpan = columnSpanSize(rowSpanFrom, subColSpanFrom, subColSpanCnt);
                if (OTN == Qt::Horizontal)
                    sectionRect.setLeft(sectionViewportPosition(subColSpanFrom));
                else
                {
                    sectionRect.setLeft(columnSpanSize(rowSpanFrom, 0, subColSpanFrom));
                    i = subColSpanTo;
                }
                sectionRect.setWidth(subColSpan);
            }
            cellIndex = rowSpanIdx;
        }

        // draw section with style
        QStyleOptionHeader sectionStyle;
        initStyleOption(§ionStyle);
        sectionStyle.textAlignment = Qt::AlignCenter;
        sectionStyle.iconAlignment = Qt::AlignVCenter;
        sectionStyle.section = logicalIdx;
        sectionStyle.text = cellIndex.data(Qt::DisplayRole).toString();
        sectionStyle.rect = sectionRect;

        // file background or foreground color of the cell
        QVariant bg = cellIndex.data(Qt::BackgroundRole);
        QVariant fg = cellIndex.data(Qt::ForegroundRole);

        if (bg.canConvert(QVariant::Brush))
        {
            sectionStyle.palette.setBrush(QPalette::Button, qvariant_cast(bg));
            sectionStyle.palette.setBrush(QPalette::Window, qvariant_cast(bg));
        }
        if (fg.canConvert(QVariant::Brush))
        {
            sectionStyle.palette.setBrush(QPalette::ButtonText, qvariant_cast(fg));
        }

        painter->save();
        qDrawShadePanel(painter, sectionStyle.rect, sectionStyle.palette, false, 1, §ionStyle.palette.brush(QPalette::Button));
        style()->drawControl(QStyle::CE_HeaderLabel, §ionStyle, painter);
        painter->restore();
    }
}


QSize RbTableHeaderView::sectionSizeFromContents(int logicalIndex) const
{
    const RbTableHeaderModel* tblModel = qobject_cast(this->model());
    const int OTN = orientation();
    const int LEVEL_CNT = (OTN == Qt::Horizontal) ? tblModel->rowCount() : tblModel->columnCount();

    QSize siz = QHeaderView::sectionSizeFromContents(logicalIndex);
    for (int i = 0; i < LEVEL_CNT; ++i)
    {
        QModelIndex cellIndex = (OTN == Qt::Horizontal) ? tblModel->index(i, logicalIndex) : tblModel->index(logicalIndex, i);
        QModelIndex colSpanIdx = columnSpanIndex(cellIndex);
        QModelIndex rowSpanIdx = rowSpanIndex(cellIndex);
        siz = cellIndex.data(Qt::SizeHintRole).toSize();

        if (colSpanIdx.isValid())
        {
            int colSpanFrom = colSpanIdx.column();
            int colSpanCnt = colSpanIdx.data(COLUMN_SPAN_ROLE).toInt();
            int colSpanTo = colSpanFrom + colSpanCnt - 1;
            siz.setWidth(columnSpanSize(colSpanIdx.row(), colSpanFrom, colSpanCnt));
            if (OTN == Qt::Vertical) i = colSpanTo;
        }
        if (rowSpanIdx.isValid())
        {
            int rowSpanFrom = rowSpanIdx.row();
            int rowSpanCnt = rowSpanIdx.data(ROW_SPAN_ROLE).toInt();
            int rowSpanTo = rowSpanFrom + rowSpanCnt - 1;
            siz.setHeight(rowSpanSize(rowSpanIdx.column(), rowSpanFrom, rowSpanCnt));
            if (OTN == Qt::Horizontal) i = rowSpanTo;
        }
    }
    return siz;
}

QModelIndex RbTableHeaderView::columnSpanIndex(const QModelIndex& currentIdx) const
{
    const RbTableHeaderModel* tblModel = qobject_cast(model());
    const int curRow = currentIdx.row();
    const int curCol = currentIdx.column();
    int i = curCol;
    while (i >= 0)
    {
        QModelIndex spanIndex = tblModel->index(curRow, i);
        QVariant span = spanIndex.data(COLUMN_SPAN_ROLE);
        if (span.isValid() && spanIndex.column() + span.toInt() - 1 >= curCol)
            return spanIndex;
        i--;
    }
    return QModelIndex();
}

QModelIndex RbTableHeaderView::rowSpanIndex(const QModelIndex& currentIdx) const
{
    const RbTableHeaderModel* tblModel = qobject_cast(model());
    const int curRow = currentIdx.row();
    const int curCol = currentIdx.column();
    int i = curRow;
    while (i >= 0)
    {
        QModelIndex spanIndex = tblModel->index(i, curCol);
        QVariant span = spanIndex.data(ROW_SPAN_ROLE);
        if (span.isValid() && spanIndex.row() + span.toInt() - 1 >= curRow)
            return spanIndex;
        i--;
    }
    return QModelIndex();
}

int RbTableHeaderView::columnSpanSize(int row, int from, int spanCount) const
{
    const RbTableHeaderModel* tblModel = qobject_cast(model());
    int span = 0;
    for (int i = from; i < from + spanCount; ++i)
    {
        QSize cellSize = tblModel->index(row, i).data(Qt::SizeHintRole).toSize();
        span += cellSize.width();
    }
    return span;
}


int RbTableHeaderView::rowSpanSize(int column, int from, int spanCount) const
{
    const RbTableHeaderModel* tblModel = qobject_cast(model());
    int span = 0;
    for (int i = from; i < from + spanCount; ++i)
    {
        QSize cellSize = tblModel->index(i, column).data(Qt::SizeHintRole).toSize();
        span += cellSize.height();
    }
    return span;
}

/**
 * @return section numbers
 */
int RbTableHeaderView::getSectionRange(QModelIndex& index, int* beginSection, int* endSection) const
{
    // check up section range from the index
    QModelIndex colSpanIdx = columnSpanIndex(index);
    QModelIndex rowSpanIdx = rowSpanIndex(index);

    if (colSpanIdx.isValid())
    {
        int colSpanFrom = colSpanIdx.column();
        int colSpanCnt = colSpanIdx.data(COLUMN_SPAN_ROLE).toInt();
        int colSpanTo = colSpanFrom + colSpanCnt - 1;
        if (orientation() == Qt::Horizontal)
        {
            *beginSection = colSpanFrom;
            *endSection = colSpanTo;
            index = colSpanIdx;
            return colSpanCnt;
        }
        else
        {
            // check up  if the column span index has row span
            QVariant subRowSpanData = colSpanIdx.data(ROW_SPAN_ROLE);
            if (subRowSpanData.isValid())
            {
                int subRowSpanFrom = colSpanIdx.row();
                int subRowSpanCnt = subRowSpanData.toInt();
                int subRowSpanTo = subRowSpanFrom + subRowSpanCnt - 1;
                *beginSection = subRowSpanFrom;
                *endSection = subRowSpanTo;
                index = colSpanIdx;
                return subRowSpanCnt;
            }
        }
    }

    if (rowSpanIdx.isValid())
    {
        int rowSpanFrom = rowSpanIdx.row();
        int rowSpanCnt = rowSpanIdx.data(ROW_SPAN_ROLE).toInt();
        int rowSpanTo = rowSpanFrom + rowSpanCnt - 1;
        if (orientation() == Qt::Vertical)
        {
            *beginSection = rowSpanFrom;
            *endSection = rowSpanTo;
            index = rowSpanIdx;
            return rowSpanCnt;
        }
        else
        {
            // check up if the row span index has column span
            QVariant subColSpanData = rowSpanIdx.data(COLUMN_SPAN_ROLE);
            if (subColSpanData.isValid())
            {
                int subColSpanFrom = rowSpanIdx.column();
                int subColSpanCnt = subColSpanData.toInt();
                int subColSpanTo = subColSpanFrom + subColSpanCnt - 1;
                *beginSection = subColSpanFrom;
                *endSection = subColSpanTo;
                index = rowSpanIdx;
                return subColSpanCnt;
            }
        }
    }

    return 0;
}


void RbTableHeaderView::onSectionResized(int logicalIndex, int oldSize, int newSize)
{
    RbTableHeaderModel* tblModel = qobject_cast(this->model());
    const int OTN = orientation();
    const int LEVEL_CNT = (OTN == Qt::Horizontal) ? tblModel->rowCount() : tblModel->columnCount();
    int pos = sectionViewportPosition(logicalIndex);
    int xx = (OTN == Qt::Horizontal) ? pos : 0;
    int yy = (OTN == Qt::Horizontal) ? 0 : pos;
    QRect sectionRect(xx, yy, 0, 0);
    for (int i = 0; i < LEVEL_CNT; ++i)
    {
        QModelIndex cellIndex = (OTN == Qt::Horizontal) ? tblModel->index(i, logicalIndex) : tblModel->index(logicalIndex, i);
        QSize cellSize = cellIndex.data(Qt::SizeHintRole).toSize();
        // set position of cell
        if (OTN == Qt::Horizontal)
        {
            sectionRect.setTop(rowSpanSize(logicalIndex, 0, i));
            cellSize.setWidth(newSize);
        }
        else
        {
            sectionRect.setLeft(columnSpanSize(logicalIndex, 0, i));
            cellSize.setHeight(newSize);
        }
        tblModel->setData(cellIndex, cellSize, Qt::SizeHintRole);

        QModelIndex colSpanIdx = columnSpanIndex(cellIndex);
        QModelIndex rowSpanIdx = rowSpanIndex(cellIndex);

        if (colSpanIdx.isValid())
        {
            int colSpanFrom = colSpanIdx.column();
            if (OTN == Qt::Horizontal)
                sectionRect.setLeft(sectionViewportPosition(colSpanFrom));
            else
            {
                sectionRect.setLeft(columnSpanSize(logicalIndex, 0, colSpanFrom));
            }

        }
        if (rowSpanIdx.isValid())
        {
            int rowSpanFrom = rowSpanIdx.row();
            if (OTN == Qt::Vertical)
                sectionRect.setTop(sectionViewportPosition(rowSpanFrom));
            else
                sectionRect.setTop(rowSpanSize(logicalIndex, 0, rowSpanFrom));
        }
        QRect rToUpdate(sectionRect);
        rToUpdate.setWidth(viewport()->width() - sectionRect.left());
        rToUpdate.setHeight(viewport()->height() - sectionRect.top());
        viewport()->update(rToUpdate.normalized());
    }
}

main.cpp

#include "MultistageHeader.h"
#include 

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    MultistageHeader w;
    w.show();

    return a.exec();
}

实例2:

MultiLevelHeaderView.h

#pragma once

#include 
#include 

enum MyItemDataRole
{
    COLUMN_SPAN_ROLE = Qt::UserRole + 1,
    ROW_SPAN_ROLE,
};

class MultiLevelHeaderView : public QHeaderView
{
    Q_OBJECT
public:
    MultiLevelHeaderView(Qt::Orientation orientation, int rows, int columns, QWidget* parent = 0);
    virtual ~MultiLevelHeaderView();

    void setRowHeight(int row, int rowHeight);
    void setColumnWidth(int col, int colWidth);
    void setCellData(int row, int column, int role, const QVariant& value);
    // the below methods is just shortcut for setCellData
    void setCellSpan(int row, int column, int rowSpanCount, int columnSpanCount);
    void setCellBackgroundColor(int row, int column, const QColor&);
    void setCellForegroundColor(int row, int column, const QColor&);
    void setCellText(int row, int column, const QString& text);

    QModelIndex columnSpanIndex(const QModelIndex& currentIndex) const;
    QModelIndex rowSpanIndex(const QModelIndex& currentIndex) const;
protected:
    // override
    virtual void mousePressEvent(QMouseEvent* event) override;
    virtual QModelIndex indexAt(const QPoint&);
    virtual void paintSection(QPainter* painter, const QRect& rect, int logicalIndex) const override;
    virtual QSize sectionSizeFromContents(int logicalIndex) const override;

    // inherent features
    int columnSpanSize(int row, int from, int spanCount) const;
    int rowSpanSize(int column, int from, int spanCount) const;
    bool getRootCell(int row, int column, int& rootCellRow, int& rootCellColumn) const;
    // (row, column) must be root cell of merged cells
    QRect getCellRect(int row, int column) const;
    int getSectionRange(QModelIndex& index, int* beginSection, int* endSection) const;

protected slots:
    void onSectionResized(int logicalIdx, int oldSize, int newSize);

signals:
    void sectionPressed(int from, int to);
};

MultiLevelHeaderView.cpp

#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include "MultiLevelHeaderView.h"

struct Cell
{
    Cell(int r, int c):row(r), column(c){}
    int row = 0;
    int column = 0;
    bool operator<(const Cell& oth) const
    {
        if (row < oth.row)
            return true;
        if (row == oth.row)
            return column < oth.column;
        return false;
    }
};

class MultiLevelHeaderModel : public QAbstractTableModel
{
public:
    MultiLevelHeaderModel(Qt::Orientation orientation, int rows, int cols, QObject* parent = 0);
    virtual ~MultiLevelHeaderModel();

public:
    // override
    virtual QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const override;
    virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    virtual int columnCount(const QModelIndex &parent = QModelIndex()) const override;
    virtual QVariant data(const QModelIndex &index, int role) const override;
    virtual bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole) override;
    virtual Qt::ItemFlags flags(const QModelIndex &index) const override;
    void setRowHeight(int row, int size);
    int getRowHeight(int row) const;
    void setColumnWidth(int col, int size);
    int getColumnWidth(int col) const;
    void setRootCell(int row, int column, int rootRow, int rootColumn);
    bool getRootCell(int row, int column, int & rootCellRow, int & rootCellColumn) const;
private:
    Qt::Orientation m_orientation;
    int m_rowCount;
    int m_columnCount;
    std::vector m_rowSizes;
    std::vector m_columnSizes;
    QMap> m_data;
    QMap m_rootCellMap;
};

MultiLevelHeaderModel::MultiLevelHeaderModel(Qt::Orientation orientation, int rows, int cols, QObject* parent) :
    QAbstractTableModel(parent), m_orientation(orientation), m_rowCount(rows), m_columnCount(cols)
{
    m_rowSizes.resize(rows, 0);
    m_columnSizes.resize(cols, 0);
}

MultiLevelHeaderModel::~MultiLevelHeaderModel()
{
}

QModelIndex MultiLevelHeaderModel::index(int row, int column, const QModelIndex & parent) const
{
    if (!hasIndex(row, column, parent))
        return QModelIndex();

    return createIndex(row, column, nullptr);
}

int MultiLevelHeaderModel::rowCount(const QModelIndex &parent) const
{
    return m_rowCount;
}
int MultiLevelHeaderModel::columnCount(const QModelIndex &parent) const
{
    return m_columnCount;
}

QVariant MultiLevelHeaderModel::data(const QModelIndex& index, int role) const
{
    if (!index.isValid())
        return QVariant();

    if (index.row() >= m_rowCount || index.row() < 0 || index.column() >= m_columnCount || index.column() < 0)
        return QVariant();

    auto it = m_data.find(Cell(index.row(), index.column()));
    if (it != m_data.end())
    {
        auto it2 = it.value().find(role);
        if (it2 != it.value().end())
            return it2.value();
    }

    // default value
    if (role == Qt::BackgroundRole)
        return QColor(0xcfcfcf);

    if (role == Qt::SizeHintRole)
    {
        return QSize(m_columnSizes[index.column()], m_rowSizes[index.row()]);
    }
    return QVariant();
}

bool MultiLevelHeaderModel::setData(const QModelIndex & index, const QVariant & value, int role)
{
    if (index.isValid())
    {
        QVariant value2;
        if (role == COLUMN_SPAN_ROLE)
        {
            int col = index.column();
            int span = value.toInt();
            if (span > 0) // span size should be more than 1, else nothing to do
            {
                if (col + span - 1 >= m_columnCount) // span size should be less than whole columns,
                    span = m_columnCount - col;
                value2 = span;
            }
        }
        else if (role == ROW_SPAN_ROLE)
        {
            int row = index.row();
            int span = value.toInt();
            if (span > 0) // span size should be more than 1, else nothing to do
            {
                if (row + span - 1 >= m_rowCount)
                    span = m_rowCount - row;
                value2 = span;
            }
        }
        else
        {
            value2 = value;
        }
        if (value.isValid())
        {
            if (role == Qt::SizeHintRole)
            {
                m_columnSizes[index.column()] = value.toSize().width();
                m_rowSizes[index.row()] = value.toSize().height();
            }
            else
            {
                auto& map = m_data[Cell(index.row(), index.column())];
                map.insert(role, value2);
            }
        }

        return true;
    }
    return false;
}

Qt::ItemFlags MultiLevelHeaderModel::flags(const QModelIndex &index) const
{
    return  Qt::NoItemFlags | QAbstractTableModel::flags(index);
}

void MultiLevelHeaderModel::setRowHeight(int row, int size)
{
    if (row >= 0 && row < m_rowCount)
    {
        m_rowSizes[row] = size;
        emit dataChanged(index(row, 0), index(row, m_columnCount - 1), QVector() << Qt::SizeHintRole);
    }
}

int MultiLevelHeaderModel::getRowHeight(int row) const
{
    if (row >= 0 && row < m_rowCount)
        return m_rowSizes[row];
    return 0;
}

void MultiLevelHeaderModel::setColumnWidth(int col, int size)
{
    if (col >= 0 && col < m_columnCount)
    {
        m_columnSizes[col] = size;
        emit dataChanged(index(0, col), index(m_rowCount - 1, col),  QVector() << Qt::SizeHintRole);
    }
}

int MultiLevelHeaderModel::getColumnWidth(int col) const
{
    if (col >= 0 && col < m_columnCount)
        return m_columnSizes[col];
    return 0;
}

void MultiLevelHeaderModel::setRootCell(int row, int column, int rootRow, int rootColumn)
{
    Cell c{ row, column };
    auto it = m_rootCellMap.find(c);
    Q_ASSERT(it == m_rootCellMap.end());
    Cell rc{ rootRow, rootColumn };
    m_rootCellMap.insert(c, rc);
}

bool MultiLevelHeaderModel::getRootCell(int row, int column, int & rootCellRow, int & rootCellColumn) const
{
    Cell c{ row, column };
    auto it = m_rootCellMap.find(c);
    if(it == m_rootCellMap.end())
        return false;
    rootCellRow = it.value().row;
    rootCellColumn = it.value().column;
    return true;
}


MultiLevelHeaderView::MultiLevelHeaderView(Qt::Orientation orientation, int rows, int columns, QWidget* parent) :
    QHeaderView(orientation, parent)
{
    // create header model
    MultiLevelHeaderModel* m = new MultiLevelHeaderModel(orientation, rows, columns);

    // set default size of item
    if (orientation == Qt::Horizontal)
    {
        for (int row = 0; row < rows; ++row)
            m->setRowHeight(row, 20);
        for (int col = 0; col < columns; ++col)
            m->setColumnWidth(col, defaultSectionSize());
    }
    else
    {
        for (int row = 0; row < rows; ++row)
            m->setRowHeight(row, defaultSectionSize());
        for (int col = 0; col < columns; ++col)
            m->setColumnWidth(col, defaultSectionSize());
    }

    setModel(m);

    connect(this, SIGNAL(sectionResized(int, int, int)), this, SLOT(onSectionResized(int, int, int)));
}

MultiLevelHeaderView::~MultiLevelHeaderView()
{
    MultiLevelHeaderModel* m = static_cast(model());
    if (m)
        delete m;
    setModel(nullptr);
}

void MultiLevelHeaderView::setRowHeight(int row, int rowHeight)
{
    MultiLevelHeaderModel* m = static_cast(model());

    m->setRowHeight(row, rowHeight);
    if (orientation() == Qt::Vertical)
        resizeSection(row, rowHeight);
}

void MultiLevelHeaderView::setColumnWidth(int col, int colWidth)
{
    MultiLevelHeaderModel* m = static_cast(model());
    m->setColumnWidth(col, colWidth);
    if (orientation() == Qt::Horizontal)
        resizeSection(col, colWidth);
}

void MultiLevelHeaderView::setCellData(int row, int column, int role, const QVariant & value)
{
    MultiLevelHeaderModel* m = static_cast(model());
    m->setData(m->index(row, column), value, role);
}

void MultiLevelHeaderView::setCellSpan(int row, int column, int rowSpanCount, int columnSpanCount)
{
    Q_ASSERT(rowSpanCount > 0);
    Q_ASSERT(columnSpanCount > 0);
    MultiLevelHeaderModel* m = static_cast(model());
    QModelIndex idx = m->index(row, column);
    m->setData(idx, rowSpanCount, ROW_SPAN_ROLE);
    m->setData(idx, columnSpanCount, COLUMN_SPAN_ROLE);
    int lastRow = row + rowSpanCount;
    int lastCol = column + columnSpanCount;
    for (int i = row; i < lastRow; ++i)
    {
        for (int j = column; j < lastCol; ++j)
        {
            m->setRootCell(i, j, row, column);
        }
    }
}

void MultiLevelHeaderView::setCellBackgroundColor(int row, int column, const QColor& color)
{
    MultiLevelHeaderModel* m = static_cast(model());
    m->setData(m->index(row, column), color, Qt::BackgroundRole);
}

void MultiLevelHeaderView::setCellForegroundColor(int row, int column, const QColor& color)
{
    MultiLevelHeaderModel* m = static_cast(model());
    m->setData(m->index(row, column), color, Qt::ForegroundRole);
}

void MultiLevelHeaderView::setCellText(int row, int column, const QString & text)
{
    MultiLevelHeaderModel* m = static_cast(model());
    m->setData(m->index(row, column), text, Qt::DisplayRole);
}


void MultiLevelHeaderView::mousePressEvent(QMouseEvent* event)
{
    QHeaderView::mousePressEvent(event);
    QPoint pos = event->pos();
    QModelIndex index = indexAt(pos);
    const int orient = orientation();
    if (index.isValid())
    {
        int beginSection = -1;
        int endSection = -1;
        int numbers = 0;
        numbers = getSectionRange(index, &beginSection, &endSection);
        if (numbers > 0)
        {
            emit sectionPressed(beginSection, endSection);
            return;
        }
        else
        {
            const MultiLevelHeaderModel* m = static_cast(this->model());
            const int levelCount = (orient == Qt::Horizontal) ? m->rowCount() : m->columnCount();
            int logicalIdx = (orient == Qt::Horizontal) ? index.column() : index.row();
            int curLevel = (orient == Qt::Horizontal) ? index.row() : index.column();
            for (int i = 0; i < levelCount; ++i)
            {
                QModelIndex cellIndex = (orient == Qt::Horizontal) ? m->index(i, logicalIdx) : m->index(logicalIdx, i);
                numbers = getSectionRange(cellIndex, &beginSection, &endSection);
                if (numbers > 0)
                {
                    if (beginSection <= logicalIdx && logicalIdx <= endSection)
                    {
                        int beginLevel = (orient == Qt::Horizontal) ? cellIndex.row() : cellIndex.column();
                        QVariant levelSpanCnt = cellIndex.data((orient == Qt::Horizontal) ? ROW_SPAN_ROLE : COLUMN_SPAN_ROLE);
                        if (!levelSpanCnt.isValid())
                            continue;
                        int endLevel = beginLevel + levelSpanCnt.toInt() - 1;
                        if (beginLevel <= curLevel && curLevel <= endLevel)
                        {
                            emit sectionPressed(beginSection, endSection);
                            break;
                        }
                    }
                }
            }
        }
    }
}

QModelIndex MultiLevelHeaderView::indexAt(const QPoint& pos)
{
    const MultiLevelHeaderModel* m = static_cast(this->model());
    const int orient = orientation();
    const int ROWS = m->rowCount();
    const int COLS = m->columnCount();
    int logicalIdx = logicalIndexAt(pos);

    if (orient == Qt::Horizontal)
    {
        int dY = 0;
        for (int row = 0; row < ROWS; ++row)
        {
            dY += m->getRowHeight(row);
            if (pos.y() <= dY)
            {
                QModelIndex cellIndex = m->index(row, logicalIdx);
                return cellIndex;
            }
        }
    }
    else
    {
        int dX = 0;
        for (int col = 0; col < COLS; ++col)
        {
            dX += m->getColumnWidth(col);
            if (pos.x() <= dX)
            {
                QModelIndex cellIndex = m->index(logicalIdx, col);
                return cellIndex;
            }
        }
    }

    return QModelIndex();
}

std::set getCellsToBeDrawn(const MultiLevelHeaderView* view, const MultiLevelHeaderModel* m, int orient, int levelCount, int logicalIdx)
{
    std::set cellsToBeDrawn;
    for (int i = 0; i < levelCount; ++i)
    {
        int row = i, column = logicalIdx;
        if (orient == Qt::Vertical)
            std::swap(row, column);
        int rootRow, rootCol;
        if (m->getRootCell(row, column, rootRow, rootCol))
            cellsToBeDrawn.insert({ rootRow, rootCol });
    }
    return cellsToBeDrawn;
}

void MultiLevelHeaderView::paintSection(QPainter* painter, const QRect& rect, int logicalIdx) const
{
    const MultiLevelHeaderModel* m = static_cast(this->model());
    const int orient = orientation();
    const int levelCount = (orient == Qt::Horizontal) ? m->rowCount() : m->columnCount();
    std::set cellsToBeDrawn = getCellsToBeDrawn(this, m, orient, levelCount, logicalIdx);

    for (const auto& cell: cellsToBeDrawn)
    {
        QRect sectionRect = getCellRect(cell.row, cell.column);

        // draw section with style
        QStyleOptionHeader sectionStyle;
        initStyleOption(§ionStyle);
        sectionStyle.textAlignment = Qt::AlignCenter;
        sectionStyle.iconAlignment = Qt::AlignVCenter;
        //sectionStyle.section = logicalIdx;
        QModelIndex cellIndex = m->index(cell.row, cell.column);
        sectionStyle.text = cellIndex.data(Qt::DisplayRole).toString();
        sectionStyle.rect = sectionRect;

        // file background or foreground color of the cell
        QVariant bg = cellIndex.data(Qt::BackgroundRole);
        QVariant fg = cellIndex.data(Qt::ForegroundRole);

        if (bg.canConvert(QVariant::Brush))
        {
            sectionStyle.palette.setBrush(QPalette::Button, qvariant_cast(bg));
            sectionStyle.palette.setBrush(QPalette::Window, qvariant_cast(bg));
        }
        if (fg.canConvert(QVariant::Brush))
        {
            sectionStyle.palette.setBrush(QPalette::ButtonText, qvariant_cast(fg));
        }

        painter->save();
        qDrawShadePanel(painter, sectionStyle.rect, sectionStyle.palette, false, 1, §ionStyle.palette.brush(QPalette::Button));
        style()->drawControl(QStyle::CE_HeaderLabel, §ionStyle, painter);
        painter->restore();
    }
}

QSize MultiLevelHeaderView::sectionSizeFromContents(int logicalIdx) const
{
    const MultiLevelHeaderModel* m = static_cast(this->model());
    const int orient = orientation();
    const int levelCount = (orient == Qt::Horizontal) ? m->rowCount() : m->columnCount();

    int w = 0, h = 0;
    if (orient == Qt::Horizontal)
    {
        w = m->getColumnWidth(logicalIdx);
        for (int i = 0; i < levelCount; ++i)
        {
            h += m->getRowHeight(i);
        }
    }
    else
    {
        for (int i = 0; i < levelCount; ++i)
        {
            w += m->getColumnWidth(i);
        }
        h = m->getRowHeight(logicalIdx);
    }
    return QSize(w, h);
}

QModelIndex MultiLevelHeaderView::columnSpanIndex(const QModelIndex& currentIdx) const
{
    const MultiLevelHeaderModel* m = static_cast(model());
    const int curRow = currentIdx.row();
    const int curCol = currentIdx.column();
    int i = curCol;
    while (i >= 0)
    {
        QModelIndex spanIndex = m->index(curRow, i);
        QVariant span = spanIndex.data(COLUMN_SPAN_ROLE);
        if (span.isValid() && spanIndex.column() + span.toInt() - 1 >= curCol)
            return spanIndex;
        i--;
    }
    return QModelIndex();
}

QModelIndex MultiLevelHeaderView::rowSpanIndex(const QModelIndex& currentIdx) const
{
    const MultiLevelHeaderModel* m = static_cast(model());
    const int curRow = currentIdx.row();
    const int curCol = currentIdx.column();
    int i = curRow;
    while (i >= 0)
    {
        QModelIndex spanIndex = m->index(i, curCol);
        QVariant span = spanIndex.data(ROW_SPAN_ROLE);
        if (span.isValid() && spanIndex.row() + span.toInt() - 1 >= curRow)
            return spanIndex;
        i--;
    }
    return QModelIndex();
}

int MultiLevelHeaderView::columnSpanSize(int row, int from, int spanCount) const
{
    const MultiLevelHeaderModel* m = static_cast(model());
    int span = 0;
    for (int i = from; i < from + spanCount; ++i)
    {
        int colWidth = m->getColumnWidth(i);
        span += colWidth;
    }
    return span;
}


int MultiLevelHeaderView::rowSpanSize(int column, int from, int spanCount) const
{
    const MultiLevelHeaderModel* m = static_cast(model());
    int span = 0;
    for (int i = from; i < from + spanCount; ++i)
    {
        int rowHeight = m->getRowHeight(i);
        span += rowHeight;
    }
    return span;
}

bool MultiLevelHeaderView::getRootCell(int row, int column, int & rootCellRow, int & rootCellColumn) const
{
    const MultiLevelHeaderModel* m = static_cast(this->model());
    return m->getRootCell(row, column, rootCellRow, rootCellColumn);
}

QRect MultiLevelHeaderView::getCellRect(int row, int column) const
{
    const MultiLevelHeaderModel* m = static_cast(this->model());
    const int orient = orientation();
    QModelIndex cellIndex = m->index(row, column);
    auto colSpanVar = cellIndex.data(COLUMN_SPAN_ROLE);
    auto rowSpanVar = cellIndex.data(ROW_SPAN_ROLE);
    Q_ASSERT(colSpanVar.isValid() && rowSpanVar.isValid());
    int colSpan = colSpanVar.toInt();
    int rowSpan = rowSpanVar.toInt();
    int w = 0, h = 0, l = 0, t = 0;
    w = columnSpanSize(row, column, colSpan);
    h = rowSpanSize(column, row, rowSpan);
    if (orient == Qt::Horizontal)
    {
        l = sectionViewportPosition(column);
        for (int i = 0; i < row; ++i)
            t += m->getRowHeight(i);
    }
    else
    {
        for (int i = 0; i < column; ++i)
            l += m->getColumnWidth(i);
        t = sectionViewportPosition(row);
    }

    QRect rect(l, t, w, h);
    return rect;
}

/**
 * @return section numbers
 */
int MultiLevelHeaderView::getSectionRange(QModelIndex& index, int* beginSection, int* endSection) const
{
    int row = index.row(), column = index.column();
    int rootRow, rootCol;
    bool success = getRootCell(row, column, rootRow, rootCol);
    if (!success)
        return 0;
    const MultiLevelHeaderModel* m = static_cast(model());
    QModelIndex rootIndex = m->index(rootRow, rootCol);
    if (orientation() == Qt::Horizontal)
    {
        int colSpanCnt = rootIndex.data(COLUMN_SPAN_ROLE).toInt();
        *beginSection = rootIndex.column();;
        *endSection = *beginSection + colSpanCnt - 1;
        index = rootIndex;
        return colSpanCnt;
    }
    else
    {
        int rowSpanCnt = rootIndex.data(ROW_SPAN_ROLE).toInt();
        *beginSection = rootIndex.row();;
        *endSection = *beginSection + rowSpanCnt - 1;
        index = rootIndex;
        return rowSpanCnt;
    }
}

void MultiLevelHeaderView::onSectionResized(int logicalIndex, int oldSize, int newSize)
{
    MultiLevelHeaderModel* m = static_cast(this->model());
    const int orient = orientation();
    const int levelCount = (orient == Qt::Horizontal) ? m->rowCount() : m->columnCount();
    if (orient == Qt::Horizontal)
    {
        m->setColumnWidth(logicalIndex, newSize);
    }
    else
    {
        m->setRowHeight(logicalIndex, newSize);
    }

    std::set cellsToBeDrawn = getCellsToBeDrawn(this, m, orient, levelCount, logicalIndex);
    for (const auto& cell : cellsToBeDrawn)
    {
        QRect sectionRect = getCellRect(cell.row, cell.column);
        if (orient == Qt::Horizontal)
        {
            sectionRect.setWidth(viewport()->width() - sectionRect.left());
        }
        else
        {
            sectionRect.setHeight(viewport()->height() - sectionRect.top());
        }
        viewport()->update(sectionRect);
    }
}

main.cpp

#include 
#include 
#include 
#include "MultiLevelHeaderView.h"

int main(int argc, char *argv[])
{
        QApplication a(argc, argv);
        QTableView tableView;
        tableView.setMinimumSize(800, 600);
        auto pView = &tableView;
        pView->setContextMenuPolicy(Qt::CustomContextMenu);
        bool horizontal = false;
        if (horizontal)
        {
                auto pHeader = new MultiLevelHeaderView(Qt::Horizontal, 3, 8, pView);
                pHeader->setCellSpan(0, 0, 1, 4);
                pHeader->setCellSpan(0, 4, 1, 4);
                pHeader->setCellSpan(1, 0, 1, 2);
                pHeader->setCellSpan(1, 2, 1, 2);
                pHeader->setCellSpan(1, 4, 1, 2);
                pHeader->setCellSpan(1, 6, 1, 2);
                for (int i = 0; i < 8; i++)
                {
                        pHeader->setCellSpan(2, i, 1, 1);
                }

                //一级
                pHeader->setCellText(0, 0, QString(u8"横向尺寸"));
                pHeader->setCellText(0, 4, QString(u8"纵向尺寸"));
                //二级
                pHeader->setCellText(1, 0, QStringLiteral("极耳宽度"));
                pHeader->setCellText(1, 2, QStringLiteral("极耳高度"));
                pHeader->setCellText(1, 4, QStringLiteral("极片宽度"));
                pHeader->setCellText(1, 6, QStringLiteral("极耳间距"));
                //三级
                pHeader->setCellText(2, 0, QStringLiteral("CCD测量值"));
                pHeader->setCellText(2, 1, QStringLiteral("真值"));
                pHeader->setCellText(2, 2, QStringLiteral("CCD测量值"));
                pHeader->setCellText(2, 3, QStringLiteral("真值"));
                pHeader->setCellText(2, 4, QStringLiteral("CCD测量值"));
                pHeader->setCellText(2, 5, QStringLiteral("真值"));
                pHeader->setCellText(2, 6, QStringLiteral("CCD测量值"));
                pHeader->setCellText(2, 7, QStringLiteral("真值"));


                pHeader->setMinimumHeight(90);
                for (int i = 0; i < 3; ++i)
                        pHeader->setRowHeight(i, 30);
                //m_pHeader->setMinimumHeight(60);
                pHeader->setSectionsClickable(false);
                pHeader->setCellBackgroundColor(0, 0, 0xffcfcf);
                pHeader->setCellBackgroundColor(0, 4, 0xcfcfcf);
                pHeader->setCellBackgroundColor(1, 0, 0xcfcfcf);
                pHeader->setCellBackgroundColor(1, 2, 0xcfcfcf);
                pHeader->setCellBackgroundColor(1, 4, 0xcfcfcf);
                pHeader->setCellBackgroundColor(1, 6, 0xcfcfcf);
                for (int i = 0; i < 8; i++)
                {
                        pHeader->setCellBackgroundColor(2, i, 0xcfcfcf);
                }

                int rowCount = 10;
                auto m_pDataModel = new QStandardItemModel;
                for (int i = 0; i < rowCount; i++)
                {
                        QList items;
                        for (int j = 0; j < 8; j++)
                        {
                                items.append(new QStandardItem);
                        }

                        m_pDataModel->appendRow(items);
                }

                pView->setModel(m_pDataModel);
                pView->setHorizontalHeader(pHeader);
        }
        else
        {
                auto pHeader = new MultiLevelHeaderView(Qt::Vertical, 8, 3, pView);
                pHeader->setCellSpan(0, 0, 4, 1);
                pHeader->setCellSpan(4, 0, 4, 1);
                pHeader->setCellSpan(0, 1, 2, 1);
                pHeader->setCellSpan(2, 1, 2, 1);
                pHeader->setCellSpan(4, 1, 2, 1);
                pHeader->setCellSpan(6, 1, 2, 1);
                for (int i = 0; i < 8; i++)
                {
                        pHeader->setCellSpan(i, 2, 1, 1);
                }

                //一级
                pHeader->setCellText(0, 0, QString(u8"横\n向\n尺\n寸"));
                pHeader->setCellText(4, 0, QString(u8"纵\n向\n尺\n寸"));
                //二级
                pHeader->setCellText(0, 1, QStringLiteral("极耳宽度"));
                pHeader->setCellText(2, 1, QStringLiteral("极耳高度"));
                pHeader->setCellText(4, 1, QStringLiteral("极片宽度"));
                pHeader->setCellText(6, 1, QStringLiteral("极耳间距"));
                //三级
                pHeader->setCellText(0, 2, QStringLiteral("CCD测量值"));
                pHeader->setCellText(1, 2, QStringLiteral("真值"));
                pHeader->setCellText(2, 2, QStringLiteral("CCD测量值"));
                pHeader->setCellText(3, 2, QStringLiteral("真值"));
                pHeader->setCellText(4, 2, QStringLiteral("CCD测量值"));
                pHeader->setCellText(5, 2, QStringLiteral("真值"));
                pHeader->setCellText(6, 2, QStringLiteral("CCD测量值"));
                pHeader->setCellText(7, 2, QStringLiteral("真值"));


                pHeader->setMinimumHeight(90);
                pHeader->setMinimumWidth(30);
                for (int i = 0; i < 3; ++i)
                        pHeader->setColumnWidth(i, 30);
                for (int i = 0; i < 8; ++i)
                        pHeader->setRowHeight(i, 30);
                //m_pHeader->setMinimumHeight(60);
                pHeader->setSectionsClickable(false);

                int rowCount = 8;
                auto m_pDataModel = new QStandardItemModel;
                for (int i = 0; i < rowCount; i++)
                {
                        QList items;
                        for (int j = 0; j < 6; j++)
                        {
                                items.append(new QStandardItem);
                        }

                        m_pDataModel->appendRow(items);
                }

                pView->setModel(m_pDataModel);
                pView->setVerticalHeader(pHeader);
        }
        pView->show();
        return a.exec();
}

你可能感兴趣的:(Qt,qt,开发语言)