我们在开发过程中对于表格使用频率还是挺高的,使用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进行锁死(固定)。

需要对2个QtableWidget进行联动设置

总结一下就是:

第一种方案比较简单,但是最终体验效果不太好。

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

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

我们首先要做的就是创建一个继承于QTableWidget的一个类,命名为TDMSummaryTableWgt。

class TDMSummaryTableWgt : public QTableWidget

然后需要在TDMSummaryTableWgt类中,声明另外一个用于header的QTableWidget,命名为 m_frozenTableWgt;

private:

QTableWidget *m_frozenTableWgt;// 使用TableWidget 作为header,并冻结

这个m_frozenTableWgt,就是作为表头,并且固定位置,不随着滚动条移动位置。

这个时候我们只需要解决两个问题,就可以搞定表头了:

1.表头位置锁定(固定、锁死)。

2.重绘表头。

对于第一个问题,表头位置的固定。我们应该从哪些方面考虑来解决?

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 && 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 || //老师ID

colIndex == 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);

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

//右侧spacer

if ((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里面的难题。

本文主要讲解了QT表格控件QTableView,QTableWidget复杂表头(多行表头) 及冻结、固定特定的行的详细方法与实例,更多关于Qt GUI图形图像开发知识请查看下面的相关链接

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

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

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

  2. Qt GUI图形图像开发之QT表格控件QTableView详细使用方法与实例

    QT表格控件QTableView简介 ​ 表格视图控件QTableView,需要和QStandardItemModel, 配套使用,这套框架是基于MVC设计模式设计的,M(Model)是QStanda ...

  3. python表格控件_python GUI库图形界面开发之PyQt5表格控件QTableView详细使用方法与实例...

    PyQt5表格控件QTableView简介 在通常情况下,一个应用需要和一批数据进行交互,然后以表格的形式输出这些信息,这时就需要用到QTableView类了,在QTableView中可以使用自定义的 ...

  4. python gui 显示表格_python GUI库图形界面开发之PyQt5表格控件QTableView详细使用方法与实例...

    PyQt5表格控件QTableView简介 在通常情况下,一个应用需要和一批数据进行交互,然后以表格的形式输出这些信息,这时就需要用到QTableView类了,在QTableView中可以使用自定义的 ...

  5. qpython3可视图形界面_python GUI库图形界面开发之PyQt5窗口控件QWidget详细使用方法...

    QWidget基本介绍 基础窗口控件QWidget类是所有用户界面对象的基类,所有的窗口或者控件都直接或者间接的继承自QWidget类. 窗口坐标系统 PyQt使用统一的坐标系统来定位窗口控件的位置和 ...

  6. python menu实例_python GUI库图形界面开发之PyQt5菜单栏控件QMenuBar的详细使用方法与实例...

    PyQt5菜单栏控件QMenuBar介绍 QMenuBar在QMainWindow对象的标题栏下方,水平的QMenuBar被保留显示QMenu对象 QMenuBar类提供了一个可以包含一个或多个QAc ...

  7. pythongui项目实例_python GUI库图形界面开发之PyQt5状态栏控件QStatusBar详细使用方法实例...

    PyQt5状态栏控件QStatusBar简介 MainWindow对象在底部保留有一个水平条,作为状态栏(QstatusBar),用于显示永久或临时的状态信息 QStatusBar类中的常用方法 方法 ...

  8. python选项卡控件_python GUI库图形界面开发之PyQt5选项卡控件QTabWidget详细使用方法与...

    PyQt5选项卡控件QTabWidget简介 QTabWidget控件提供了一个选项卡和一个页面区域,默认显示第一个选项卡的页面,通过单击各选项卡可以查看对应的界面,如果在一个窗口中显示的输入字段很多 ...

  9. pythongui界面实例带注释_python GUI库图形界面开发之PyQt5状态栏控件QStatusBar详细使用方法实例...

    PyQt5状态栏控件QStatusBar简介 MainWindow对象在底部保留有一个水平条,作为状态栏(QstatusBar),用于显示永久或临时的状态信息 QStatusBar类中的常用方法 方法 ...

最新文章

  1. 计算机程序水仙花数,水仙花数
  2. 是起点,而非终点——评《程序员的思维修炼》
  3. 独家 | 在Python编程面试前需要学会的10个算法(附代码)
  4. 52 个深度学习目标检测模型汇总,论文、源码一应俱全!(附链接)
  5. 无参数实例化Configuration对象以及addResource无法加载core-site.xml中的内容
  6. Docker无法连接到docker守护程序
  7. aliez歌词_Aliez歌词
  8. C# word 转 pdf
  9. POJ 1458 Common Subsequence
  10. 关于发那科机器人弧焊指令故障处理
  11. 蒙特卡洛—赌博模型笔记
  12. 50份Word简历模板,全都免费领取,送给最需要的你
  13. MATLAB 数据拟合 (使用 polyfit 多项式曲线拟合、polyval)
  14. ubuntu磁盘空间清理
  15. 论文解读:Prediction of Protein–Protein Interaction Sites Using Convolutional Neural Network
  16. UED、UCD、UE、UI、交互设计这 5 个名词有哪些区别?
  17. 波士顿科学完成从霸菱亚洲收购科医人外科业务;刘孟元博士加盟克睿基因 | 医药健闻...
  18. 几种for循环用法详解。
  19. 先验分布/后验分布/似然估计/贝叶斯公式
  20. excel不显示0_Excel中把0显示为空白的三种解决方法

热门文章

  1. 【CXY】JAVA基础 之 Collections
  2. 搜索凑单页大促显示延迟方案设计
  3. Pulp Fiction (低俗小说)
  4. 【STM32】TCL2543CN 12位11通道ADC芯片stm驱动程序,使用32自带SPI实现
  5. 炒汇常识--股市与汇市的区别
  6. 自动填充空白单元格_使用自动填充插入或删除单元格
  7. css的`class`选择器选择前缀.
  8. 悟空crm配置mysql_超简单的悟空crm安装教程!看完不信你不会
  9. 如何在linux CentOS中安装cPanel和WHM
  10. matlab光学几何光学模拟,matlab在几何光学仿真 实验中的应用.doc