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进行重绘。

那么我们就要另外想办法了,经过分析,刚开始提出了两种方案:

解决方案
  描述 优点 缺点
方案一
  • 隐藏表头
  • 前两行当做表头
  • 内容行从第三行开始
  • 对表格设置itemDelegate,对前两行的表头进行重绘
  • 一个QTableWidget,实现起来方便一些。
  • 当出现滚动条,表头会随着着内容表格个移动,不符合大众习惯。
  • 改变了内容表格的整个原有序列,所有的行数都需要比原来大2,对所有的API进行重写工作难不高,复杂度比较高。
方案二
  • 使用一个QTableWidget命名为m_frozonTableWgt作为表头。
  • 使用另外一个QTableWidget作为内容显示的表格。
  • m_frozonTableWgt隐藏表头、隐藏滚动条、只显示2行的内容表格、显示到内容表格上方、只占据内容表的表头高度、设置ItemDelegate进行重绘。
  • 内容表格,显示表头,高度设置成m_frozonTableWgt前两行的高度。
  • 最终效果更好,体验更好。
  • 需要对2个QTableWidget进行操作,比较麻烦。
  • 需要对表头的QTableWidget进行锁死(固定)。
  • 3.需要对2个QtableWidget进行联动设置

总结一下就是:

  • 第一种方案比较简单,但是最终体验效果不太好。
  • 第二种方案实现起来比较复杂,但是最终体验效果比较好。

本着成就客户与自我成长的态度,最终选择了第二种解决方案。

我们首先要做的就是创建一个继承于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);//第一行设置高度42pxm_frozenTableWgt->setRowHeight(1, 42);//第二行设置高度42pxfor (int row = 2; row < m_frozenTableWgt->rowCount(); ++row)//隐藏2行后的行m_frozenTableWgt->setRowHidden(row, true);//===================设置header内容=================////合并单元格m_frozenTableWgt->setSpan(0, 0, 2, 1);//老师IDm_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 && current.row() > 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 的样式重绘,就是第二个要解决的问题了。

这个问题,我们要新建一个继承于QStyledItemDelegate的代理类,我们叫ItemDelegate。并且重写paint方法,在paint方法里绘制m_frozenTableWgt;

m_frozenTableWgt->setItemDelegate(new ItemDelegate(0));//设置绘画代理(主要在代理中画出来header)
class ItemDelegate : public QStyledItemDelegate
{Q_OBJECT
public:ItemDelegate(int type, QObject *parent=0);void paint(QPainter *painter,const QStyleOptionViewItem &option, const QModelIndex &index) const;private:};

在paint方法中,根据每个单元格的背景不同进行绘制背景

    int rowIndex = index.row();//行号int colIndex = index.column();//列号if (rowIndex == 0 || rowIndex == 1)//前两行作为header{//背景QColor color;if (rowIndex == 0 && (colIndex == 0 || //老师IDcolIndex == 1 || //老师姓名colIndex == 2 || //课程类型colIndex == 9))  //操作{color.setRgb(231, 238, 251);}else if ((rowIndex == 0 && colIndex == 3) || //8月20日(rowIndex == 1 && (colIndex == 3 || //续报率colIndex == 4 || //新学员续报率colIndex == 5 || //续报增长人数colIndex == 6))) //续报增长率{color.setRgb(214, 228, 253);}else if ((rowIndex == 0 && colIndex == 7) || //8月19日(rowIndex == 1 && (colIndex == 7 || //续报率colIndex == 8))) //新学员续报率{color.setRgb(203, 221, 255);}//绘制背景painter->setPen(color);painter->setBrush(QBrush(color));painter->drawRect(option.rect);

根据每个单元格要求绘画是否需要右侧的渐变的分隔线。

        //右侧spacerif ((rowIndex == 0 && (colIndex == 0 || colIndex == 1) )) {int startX = option.rect.right();int startY = option.rect.y() + (option.rect.height() - 40) / 2;int endX = startX;int endY = startY + 40;QLinearGradient linearGradient(startX, startY, endX, endY);linearGradient.setColorAt(0, QColor(164, 188, 240, 0));linearGradient.setColorAt(0.5, QColor(164, 188, 240, 255));linearGradient.setColorAt(1, QColor(164, 188, 240, 0));painter->setBrush(linearGradient);painter->drawRect(option.rect.right()- 2, startY, 2, 40);}else if (rowIndex == 1 && (colIndex == 3 ||colIndex == 4 ||colIndex == 5 ||colIndex == 7 )) {int startX = option.rect.right();int startY = option.rect.y() + (option.rect.height() - 28) / 2;int endX = startX;int endY = startY + 28;QLinearGradient linearGradient(startX, startY, endX, endY);linearGradient.setColorAt(0, QColor(164, 188, 240, 0));linearGradient.setColorAt(0.5, QColor(164, 188, 240, 255));linearGradient.setColorAt(1, QColor(164, 188, 240, 0));painter->setBrush(linearGradient);painter->drawRect(option.rect.right()- 2, startY, 2, 28);        }

最后将每个单元格的字体画出来

        //字体painter->setPen(QColor(51, 51, 51));QTextOption op;op.setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);QFont font;font.setFamily("Microsoft YaHei");font.setPixelSize(14);font.setBold(true);painter->setFont(font);painter->drawText(option.rect, index.data(Qt::DisplayRole).toString(), op);

这样就解决了header里面的难题。

=================================分割线========================================

demo下载地址:https://download.csdn.net/download/xiezhongyuan07/10694125  (没有积分的小伙伴评论上写邮箱,我发给你们)

没有积分支持的小伙伴,也可以访问、下载github代码,欢迎加星,会持续更新:https://github.com/xiezhongyuan/ComplexMeterTable

=================================分割线========================================

QT QTableView QTableWidget 复杂表头(多行表头) 、(冻结、固定特定的行)相关推荐

  1. qtabwidget设置表头_Qt GUI图形图像开发之QT表格控件QTableView,QTableWidget复杂表头(多行表头) 及冻结、固定特定的行的详细方法与实例...

    我们在开发过程中对于表格使用频率还是挺高的,使用QT框架开发时候我们使用QTableView或者QTableWidget创建表格. 其中表格分为 表格头与表格体: 对于简单地表格,我们可以设置表头来满 ...

  2. tablewidget 多行表头_Qt GUI图形图像开发之QT表格控件QTableView,QTableWidget复杂表头(多行表头) 及冻结、固定特定的行的详细方法与实例...

    我们在开发过程中对于表格使用频率还是挺高的,使用QT框架开发时候我们使用QTableView或者QTableWidget创建表格. 其中表格分为 表格头与表格体: 对于简单地表格,我们可以设置表头来满 ...

  3. 如何设置按组分页同时每页的行数为固定的行数

    报表在设置的过程中,通常会遇到报表需要按组分页,这个可以通过设置行后分页来实现,也会遇到需要报表每页的行数固定,如要求报表每页只显示5行,这个可以通过设置按行分页来实现,但如何将两个需求结合起来呢,既 ...

  4. PYQT之表格控件QTableWidget复杂表头(多行表头) 及冻结行的简单方法

    PYQT之表格控件QTableWidget复杂表头(多行表头) 及冻结行的简单方法 表格需要用到复杂表头,查了好久觉得方法不是过于麻烦就是不符合表格要求,经过多方综合,总结超简单方法如下: 1.上下布 ...

  5. Qt实现表格控件-支持多级列表头、多级行表头、单元格合并、字体设置等

    文章目录 炫酷进度条 提示框 小时钟 高仿excel表格 多级表头表格 多级表头树控件 多维度折线图 表格控件-蚂蚁线 日历控件 饼图 窗体靠边自动隐藏 下拉框内容定制 模仿QQ上传头像 菜单定制 属 ...

  6. qt qtableview 刷新列表_qt qtablewidget 刷新

    QTableWidget单击.双击表头进行排序_互联网_IT/计算机_专业资料 暂无评价|0人阅读|0次下载|举报文档 QTableWidget单击.双击表头进行排序_互联网_IT/计算机_专业... ...

  7. QT中的QTableView+QTableWidget

    该类是显示表格数据的,像Excel一样, 参考:https://blog.csdn.net/qq769651718/article/details/79357938 QTableWidget控件参考: ...

  8. 多行表头_多行表头数据汇总你怎么操作?手动复制粘贴?OUT!用VBA1分钟完成

    前景提要(文末提供源码下载) 发现小伙伴们的数据结果真的好复杂,不昨天才分享过有多行表头的数据如何汇总合并,今天就有小伙伴反馈,他的数据虽然是有多行表头的,但是又有一些数据没有多行表头,那么在进行批量 ...

  9. GridView多行表头合并

    多行表头合并, 网上很多实例, 这里写的很详细, 力求让每个人都能看懂. 实现原理:GridView在ASP.NET中最终转为HMTL的表格显示表头. 在GridView创建行表头行时: e.Row. ...

最新文章

  1. 2021-10-27 我与地坛
  2. go 实现 kafka 消息发送、接收
  3. CIR,CBS,EBS,PIR,PBS傻傻分不清楚?看这里!—-揭秘令牌桶
  4. 算法竞赛入门经典_6数据结构基础
  5. ML.NET机器学习、API容器化与Azure DevOps实践(一):简介
  6. linux中fcntl()、lockf、flock的区别
  7. 动手学无人驾驶(5):多传感器数据融合
  8. java mvel_MVEL实现java直接根据公式计算结果
  9. php object 对象不存在。增加对象_PHP 闭包那点事儿
  10. 转基因粮食的毒性,首先是因为农药
  11. json输出count如何计算_基于 Kafka + Flink + Redis 的电商大屏实时计算案例
  12. 读河南干旱帖有感而发的一天(20191006)
  13. 2023年CISSP考点及预约时间参考
  14. qcqa是什么职位_QA,QE是什么职位
  15. 机器人涂装工程师岗位英文缩写_公司职位及英文缩写大全
  16. 精通CSS-添加样式
  17. 基于 xterm + websocket + vue 实现网页版终端 terminal
  18. MyBatis-Plus Cause: java.lang.IllegalArgumentException: argument type mismatch解决
  19. 分数除法计算机应用题练习,分数除法应用题 六年级的,分数除法应用题练习...
  20. 毕业设计 记账系统 毕设 管理系统 SpringBoot Vue Java

热门文章

  1. webots导入solidworks模型
  2. 解决Windows服务无法访问网络映射盘的问题
  3. centos安装duf
  4. vue怎么设置封面_vue设置视频封面教程 vue如何修改标题
  5. 医学会议直播平台有哪些
  6. 企业邮箱搬家及扩容流程
  7. wordpress程序搬家
  8. java在线考试系统源码网上考试系统源码在线考试系统(考试管理系统)考试系统
  9. Cadence常用器件中英文对照表
  10. 【数据集汇总(附下载链接)】再也不愁没数据练习机器学习算法啦!