QT的TableView实现多级表头

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_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 <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_OBJECT

public:
	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();

	// 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<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:
	// 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_ */

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 model
	else parentItem = static_cast<RbTableHeaderItem*>(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<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);
			}
		}
		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<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 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(&sectionStyle);
		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<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, &sectionStyle.palette.brush(QPalette::Button));
		style()->drawControl(QStyle::CE_HeaderLabel, &sectionStyle, 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 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<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 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 <QtWidgets/QApplication>

int main(int argc, char *argv[])
{
	QApplication a(argc, argv);
	MultistageHeader w;
	w.show();
	return a.exec();
}
  • 9
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
Qt TableView是一个功能非常强大的控件,它可以用来展示各种表格数据。而在一些情况下,用户需要对TableView设置两行表头。 两行表头的作用是在表格中展示不同层次的信息,第一行表头通常是展示较为宏观的信息,而第二行表头则是展示更加详细的信息。在实际的项目开发过程中,我们可以通过设置TableView的属性来达到这个目的。 首先,我们需要创建一个QHeaderView对象来作为表格的代理HeaderView,之后在TableView中进行设置: QTableView *tableView = new QTableView(); QHeaderView *headerView1 = new QHeaderView(Qt::Horizontal, tableView); QHeaderView *headerView2 = new QHeaderView(Qt::Horizontal, tableView); tableView->setHorizontalHeader(headerView1); tableView->setVerticalHeader(headerView2); 然后,我们需要将两个表头的信息分别设置到headerView1和headerView2中: headerView1->setSectionsClickable(true); // 首行可点击 tableView->setHorizontalHeader(headerView1); headerView2->setSectionsClickable(false); // 次行不可点击 tableView->setVerticalHeader(headerView2); tableView->setSpan(0, 0, 1, 3); // 合并单元格 最后,我们还需要将表格数据填充到TableView中,这样我们就可以在两行表头下方展示数据了。 两行表头是一个非常实用的TableView功能,它可以让用户更加清晰地了解表格中的数据信息和层次。通过以上的代码实现,我们可以轻松地将TableView的两行表头设置好,进一步提升表格数据的可读性和美观性。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值