QTQTableViewQTableWidget复杂表头(多⾏表头)、(冻
结、固定特定的⾏)
demo下载地址在最后
================================分割线======================================
对于所有前端开发⼈员会留意到,我们在开发过程中对于表格使⽤频率还是挺⾼的,使⽤QT框架开发时候我们使⽤QTableView或者QTableWidget创建表格。
其中表格分为 表格头与表格体:
对于简单地表格,我们可以设置表头来满⾜我们的要求(当然也可以隐藏表头),不过对于定制化的表头,我们能做的不是特别多。特别是对于复杂的表头,使⽤⾃带的表头,⽆论怎么设置都不太可能达到需求。例如我最近接到的⼀个项⽬,需求是:
我们分析⼀下这个表格有什么特点:
1.表头不是简单的⼀⾏,⽽是两⾏。
2.表头有单元格的合并。
3.部分表头中间有使⽤渐变的分隔线且分割线不是上下充满表格的。
如果能解决上⾯三个问题,我们基本都可以把这个表格做出来了。这个表头明显是⼀个⽐较复杂的表头。对于只对QT提供的API或者CSS上⾯三个问题,没有⼀个能够解决的。
这时候可能会有⽼师提出解决办法:给header 设置itemDelegate,⾃⼰在itemDelegate中重写paintEvent,⾃⼰画表头。 因为我们都知道,⾃⼰画单元格,要更灵活,满⾜更多需求。但是我们平时都是对单元格进⾏重绘,并不是对header的单元格进⾏重绘。于是就去搜索QT的帮助⽂档,惊喜的发现居然有设置itemdelegate的API,⼼⾥觉得有戏于是创建 ItemDelegate类,对header进⾏设置如下
tableWidget->horizontalHorizon()->setItemDelegate(new ItemDelegate());
可是结果却是令⼈失望的,没有⼀点效果。于是反⾝去查QT 关于这部分的介绍,终于到了原因:
结果就显⽽易见了,对于headerView,并不能使⽤ItemDelegate进⾏重绘。那么我们就要另外想办法了,经过分析,刚开始提出了两种⽅案:
第⼀种⽅案⽐较简单,但是最终体验效果不太好。css设置表格滚动条
第⼆种⽅案实现起来⽐较复杂,但是最终体验效果⽐较好。本着成就客户与⾃我成长的态度,最终选择了第⼆种解决⽅案。
我们⾸先要做的就是创建⼀个继承于QTableWidget的⼀个类,命名为TDMSummaryTableWgt。
class TDMSummaryTableWgt : public QTableWidget
然后需要在TDMSummaryTableWgt类中,声明另外⼀个⽤于header的QTableWidget,命名为 m_frozenTableWgt;
private:
QTableWidget *m_frozenTableWgt;// 使⽤TableWidget 作为header ,并冻结
这个m_frozenTableWgt,就是作为表头,并且固定位置,不随着滚动条移动位置。这个时候我们只需要解决两个问题,就可以搞定表头了:⼀.表头位置锁定(固定、锁死)。⼆.重绘表头。
对于第⼀个问题,表头位置的固定。我们应该从哪些⽅⾯考虑来解决?
1.从界⾯初始化开始,我们应当让表头m_frozenTableWgt具备: 不显⽰表头,不显⽰滚动条、设置rowcount为2⾏并隐藏2⾏后所有的元素、设置窗⼝层次在TDMSummaryTableWgt之前、对单元格进⾏合并等要素。
这⾥要特别注意的是,m_frozenTableWgt与TDMSummaryTableWgt设置的列数应该完全⼀致,每⼀列的尺⼨与伸展⽅案也应该完全⼀致。
void TDMSummaryTableWgt::initFrozenFrame()
{
m_frozenTableWgt = new QTableWidget(this);
m_frozenTableWgt->horizontalHeader()->setVisible(false);//表头不可见
m_frozenTableWgt->verticalHeader()->setVisible(false);//表头不可见
m_frozenTableWgt->setShowGrid(false);//⽹格线不可见
m_frozenTableWgt->setEditTriggers(QAbstractItemView::NoEditTriggers);//设置单元格不可编辑
m_frozenTableWgt->horizontalHeader()->setStretchLastSection(true);//最后⼀个单元格扩展
m_frozenTableWgt->setFocusPolicy(Qt::NoFocus);//解决选中虚框问题
m_frozenTableWgt->setFrameShape(QFrame::NoFrame);//去除边框尴尬
m_frozenTableWgt->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);//隐藏滚动条
m_frozenTableWgt->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);//
m_frozenTableWgt->setHorizontalScrollMode(ScrollPerPixel);
m_frozenTableWgt->setItemDelegate(new ItemDelegate(0));//设置绘画代理(主要在代理中画出来header)    viewport()->stackUnder(m_frozenTableWgt);//设置窗⼝层次
m_frozenTableWgt->setColumnCount(10);//header10列
m_frozenTableWgt->setRowCount(2);//header2⾏
m_frozenTableWgt->setRowHeight(0, 42);//第⼀⾏设置⾼度42px
m_frozenTableWgt->setRowHeight(1, 42);//第⼆⾏设置⾼度42px
for (int row = 2; row < m_frozenTableWgt->rowCount(); ++row)//隐藏2⾏后的⾏
m_frozenTableWgt->setRowHidden(row, true);
//===================设置header内容=================//
//合并单元格
m_frozenTableWgt->setSpan(0, 0, 2, 1);//⽼师ID
m_frozenTableWgt->setSpan(0, 1, 2, 1);//⽼师姓名
m_frozenTableWgt->setSpan(0, 2, 2, 1);//⽼师姓名
m_frozenTableWgt->setSpan(0, 3, 1, 4);//最新⽇期(8⽉20)
m_frozenTableWgt->setSpan(0, 7, 1, 2);//前⼀⽇(8⽉19)
m_frozenTableWgt->setSpan(0, 9, 2, 1);//操作
m_frozenTableWgt->setItem(0, 0, new QTableWidgetItem("⽼师ID"));
m_frozenTableWgt->setItem(0, 1, new QTableWidgetItem("⽼师姓名"));
m_frozenTableWgt->setItem(0, 2, new QTableWidgetItem("⽼师姓名"));
m_frozenTableWgt->setItem(0, 3, new QTableWidgetItem("8⽉20⽇"));
m_frozenTableWgt->setItem(0, 7, new QTableWidgetItem("8⽉19⽇"));
m_frozenTableWgt->setItem(0, 9, new QTableWidgetItem("操作"));
m_frozenTableWgt->setItem(1, 3, new QTableWidgetItem("续报率"));
m_frozenTableWgt->setItem(1, 4, new QTableWidgetItem("新学员续报率"));
m_frozenTableWgt->setItem(1, 5, new QTableWidgetItem("续报增长⼈数"));
m_frozenTableWgt->setItem(1, 6, new QTableWidgetItem("续报增长率"));
m_frozenTableWgt->setItem(1, 7, new QTableWidgetItem("续报增长率"));
m_frozenTableWgt->setItem(1, 8, new QTableWidgetItem("新学员续报率"));
//连接信号槽。⽤于滚动条联动
connect(m_frozenTableWgt->verticalScrollBar(), &QAbstractSlider::valueChanged,
verticalScrollBar(), &QAbstractSlider::setValue);
connect(verticalScrollBar(), &QAbstractSlider::valueChanged,
m_frozenTableWgt->verticalScrollBar(), &QAbstractSlider::setValue);
updateFrozenTableGeometry();//更新位置
m_frozenTableWgt->show();
}
2.除了上⾯的考虑之外,我们就需要考虑m_frozenTableWgt与TDMSummaryTableWgt之间的联动问题了,主要包括表格的尺⼨变化、滚动条移动、界⾯平移等问题。
我们⾸先要写⼀个⽅法,来确定m_frozenTableWgt与TDMSummaryTableWgt位置。
void TDMSummaryTableWgt::updateFrozenTableGeometry()
{
m_frozenTableWgt->setGeometry(frameWidth(),
frameWidth(),
viewport()->width(),
horizontalHeader()->height());
}
我们需要重写3个上⾯提到问题解决⽅案的函数,在每个⽅法⾥都要重新执⾏updateFrozenTableGeometry();
protected:
/**
* @brief resizeEvent              重载虚函数 resize事件,同时更新m_frozenTableWgt的位置
* @param event
*/
virtual void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE;
/**
* @brief moveCursor                重载虚函数⿏标移动事件
* @param cursorAction
* @param modifiers
* @return
*/
virtual QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) Q_DECL_OVERRIDE;
/**
* @brief scrollTo                  TableWidget移动事件
* @param index
* @param hint
*/
void scrollTo (const QModelIndex & index, ScrollHint hint = EnsureVisible) Q_DECL_OVERRIDE;
对上⾯这三个虚函数,我们需要特别注意的重点是moveCursor⽅法。这个⽅法⾥我们应该重点关注⿏标向上移动的情景:只有当⿏标向上移动,并且TDMSummaryTableWgt还未显⽰到第⼀⾏,并且可视区域的顶点应该⼩于m_frozenTableWgt的第⼀⾏,才允许继续向上移动:
QModelIndex TDMSummaryTableWgt::moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
{
QModelIndex current = QTableView::moveCursor(cursorAction, modifiers);
if (cursorAction == MoveUp && w() > 0
&& visualRect(current).topLeft().y() < m_frozenTableWgt->rowHeight(1) ){
const int newValue = verticalScrollBar()->value() + visualRect(current).topLeft().y()
- m_frozenTableWgt->rowHeight(0) - m_frozenTableWgt->rowHeight(1);
verticalScrollBar()->setValue(newValue);
}
return current;
}
做完上⾯这⼏部,基本解决了第⼀个问题,就是将m_frozenTableWgt的固定⾏(冻结)的功能。
要完成m_frozenTableWgtde 的样式重绘,就是第⼆个要解决的问题了。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。