QT的TableView实现多级表头
最近项目需要支持多级表头,QT本身不支持。可重写QHeaderView表头实现。
demo如下:
FSItemDelegate.h
#pragma once/*
自定义委托类
*/
#include <QItemDelegate>
#include <QSpinBox>
#include <QDoubleSpinBox>
#include <QComboBox>
#include <QStandardItemModel>
#include <QLineEdit>
#include <QDebug>#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_OBJECTpublic:ReadOnlyDelegate(QObject *parent = 0) : QItemDelegate(parent) { }QWidget *createEditor(QWidget*parent, const QStyleOptionViewItem &option,const QModelIndex &index) const{return nullptr;}
};
MultistageHeader.h
#pragma once#include <QtWidgets/QMainWindow>
#include "ui_MultistageHeader.h"
#include "RbTableHeaderView.h"
#include "FSItemDelegate.h"
#include <QTableView>
#include <QHBoxLayout>
#include <QStandardItemModel>
#include "string"
#include <QString>
class MultistageHeader : public QWidget
{Q_OBJECTpublic:MultistageHeader(QWidget *parent = Q_NULLPTR);private:Ui::MultistageHeaderClass ui;RbTableHeaderView* m_pHeader;QStandardItemModel* m_pDataModel;QList<QStandardItem*> m_pVecItems;QTableView * m_pView;QHBoxLayout* m_pLayout = nullptr;QHBoxLayout* m_Hlayout;};
RbTableHeaderView.h
#ifndef RBTABLEHEADERVIEW_H_
#define RBTABLEHEADERVIEW_H_
#include <QHeaderView>
#include <QAbstractTableModel>
#include <QModelIndex>
#include <QHash>
#include <QPair>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();// interfaceRbTableHeaderItem* 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:// propertiesint row_prop; //当前表格在表头中所在行int column_prop; //当前表格在表头中所在行// inherent featuresRbTableHeaderItem* parent_item;QHash<QPair<int, int>, RbTableHeaderItem*> child_items;QHash<int, QVariant> role_datas; //
};class RbTableHeaderModel : public QAbstractTableModel
{Q_OBJECT
public:RbTableHeaderModel(int rows, int cols, QObject* parent = 0);virtual ~RbTableHeaderModel();public:// overridevirtual 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:// propertiesint row_count_prop; //表头行总数int column_count_prop; //表头列总数// inherent featuresRbTableHeaderItem* 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:// overridevirtual 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 featuresQModelIndex 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_ */
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<QStandardItem*> 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.cpp
#include "RbTableHeaderView.h"
#include <QPainter>
#include <QStandardItem>
#include <QMouseEvent>
#include <QVariant>
#include <QBrush>
#include <qdrawutil.h>
#include <QList>
#include <QDebug>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<int, int>(row, col), newChild);return newChild;
}const RbTableHeaderItem* RbTableHeaderItem::child(int row, int col) const
{QHash<QPair<int, int>, RbTableHeaderItem*>::const_iterator itr = child_items.find(QPair<int, int>(row, col));if (itr != child_items.end()) return itr.value();return nullptr;
}RbTableHeaderItem* RbTableHeaderItem::child(int row, int col)
{QHash<QPair<int, int>, RbTableHeaderItem*>::iterator itr = child_items.find(QPair<int, int>(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<RbTableHeaderItem*> items = child_items.values();foreach(RbTableHeaderItem* item, items){if (item) delete item;item = nullptr;}child_items.clear();
}QVariant RbTableHeaderItem::data(int role) const
{QHash<int, QVariant>::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 modelelse parentItem = static_cast<RbTableHeaderItem*>(parent.internalPointer()); // no effectRbTableHeaderItem* 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<RbTableHeaderItem*>(index.internalPointer());return item->data(role);
}bool RbTableHeaderModel::setData(const QModelIndex & index, const QVariant & value, int role)
{if (index.isValid()){RbTableHeaderItem* item = static_cast<RbTableHeaderItem*>(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);}}elseitem->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 modelRbTableHeaderModel* headerModel = new RbTableHeaderModel(rows, columns);// set default size of itemfor (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<RbTableHeaderModel*>(model());if (md) delete md;setModel(nullptr);
}void RbTableHeaderView::setRowHeight(int row, int rowHeight)
{RbTableHeaderModel* md = qobject_cast<RbTableHeaderModel*>(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<RbTableHeaderModel*>(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<RbTableHeaderModel*>(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<RbTableHeaderModel*>(model());md->setData(index, color, Qt::BackgroundRole);
}void RbTableHeaderView::setCellForegroundColor(const QModelIndex& index, const QColor& color)
{RbTableHeaderModel* md = qobject_cast<RbTableHeaderModel*>(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<RbTableHeaderModel*>(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<RbTableHeaderModel*>(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<RbTableHeaderModel*>(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 cellif (OTN == Qt::Horizontal)sectionRect.setTop(rowSpanSize(logicalIdx, 0, i)); // distance from 0 to i-1 rowselsesectionRect.setLeft(columnSpanSize(logicalIdx, 0, i));sectionRect.setSize(cellSize);// check up span column or rowQModelIndex 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 spanQVariant 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 spanQVariant 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 styleQStyleOptionHeader 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 cellQVariant bg = cellIndex.data(Qt::BackgroundRole);QVariant fg = cellIndex.data(Qt::ForegroundRole);if (bg.canConvert(QVariant::Brush)){sectionStyle.palette.setBrush(QPalette::Button, qvariant_cast<QBrush>(bg));sectionStyle.palette.setBrush(QPalette::Window, qvariant_cast<QBrush>(bg));}if (fg.canConvert(QVariant::Brush)){sectionStyle.palette.setBrush(QPalette::ButtonText, qvariant_cast<QBrush>(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<const RbTableHeaderModel*>(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<RbTableHeaderModel*>(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<RbTableHeaderModel*>(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<RbTableHeaderModel*>(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<RbTableHeaderModel*>(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 indexQModelIndex 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 spanQVariant 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 spanQVariant 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<RbTableHeaderModel*>(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 cellif (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));elsesectionRect.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 <QtWidgets/QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);MultistageHeader w;w.show();return a.exec();
}