QRowTable表格控件(二)-红涨绿跌
一、开心一刻
一天,五娃和六娃去跟蛇精决斗,决斗前有这样一段对话。
五娃:“妖精!今天我俩就要消灭你!今天就是你的死期!”
蛇精:“呵呵呵,真是可笑。你们自己个儿都是从树上长出来的,凭什么叫我妖精?!”
五娃:“你也说了,我们是从树上长出来的,是葫芦变的,自然不是妖精。”
蛇精:“你们不是妖精,难道还是神仙了,再难不成你把自己当人了?”
五娃和六娃异口同声道:“哈哈哈哈哈哈!你说对了,我就是人,是植!物!人!”
二、概述
最近工作比较忙,不过还是抽时间把这个表格组件继续完善下去。
Qt的自带的表格控件非常强大,支持各种方便操作,上一篇文章QRowTable表格控件-支持hover整行、checked整行、指定列排序等介绍了一个简单的demo,主要是做一个股票表格控件,而他天然的就是支持hover和checked行特性,对Qt比较熟悉的同学可能都知道Qt其实有两个接口,可以设置交互行为为选择行。
接口就是这两个货了。
setSelectionBehavior(QAbstractItemView::SelectRows);
setSelectionMode(QTableView::SingleSelection);//不能多选
既然Qt已经有接口了,为什么我们还要自己写这个控件呢!
尝试过用Qt的接口设置相关行为的同学我相信最后都会发现是什么原因,这里我直接把原因放出来。
- 首先我们的需求是每一行的文字颜色是不一样的,Qt表格默认的行为是:表格cell如果被选中,前景色和背景色则是从高亮role中拿到的色值,以下代码是一个示例代码,其中的QPalette::HighlightedText和QPalette::Highlight就是存储单元格被选中时,绘制的颜色。
view_option.palette.setColor(QPalette::HighlightedText, index.data(Qt::ForegroundRole).value<QColor>());
view_option.palette.setColor(QPalette::Highlight, index.data(Qt::BackgroundRole).value<QColor>());
- 最重要的时候我们自己还是实现了一堆的小需求,下一小节我们来一个个分析
三、效果展示
以下是红涨绿跌效果图,实现功能一样。
UI展现形式不一样,但是都实现了红涨绿跌
- 背景色不同
- 排序效果不同
纯白版

腹黑版

四、任务需求
看过效果图之后,有没有发现我们这里的表格控件和Qt自带的控件有什么区别呢?其实这里我们要达到gif展示的那种效果,还是使用了很多实现技巧。
控件都包含哪些功能呢?
- 指定列排序
指定列排序,这个版本的代码经过了优化,比QRowTable表格控件-支持hover整行、checked整行、指定列排序等这篇文章中将的版本要更优雅一些。
- 排序图标
纯白版使用的是Qt排序图标
腹黑版排序图标使我们自己去绘制的,并且绘制水平表头时文本有特殊处理
- 列内容对其方式
看到这里的同学不放自己也可以先思考下,看这3种需求的实现方式,有了大致思路后在继续往下看。
这样带着思路看,理解起来应该更加的容易。
五、指定列排序
还记得上一篇文章QRowTable表格控件-支持hover整行、checked整行、指定列排序等中怎么禁用指定列排序吗?忘记的同学可以到这篇文章中去熟悉下,我记着应该是发现排序列不允许被排序时,直接禁用排序功能。
本篇文章我们使用了一种更加优雅的方式来阻止指定列排序。
首先我们重写了表头组件,并且重写了mouseReleaseEvent这个函数,因为这个函数中才触发了排序,因此这个函数中足以过滤掉不让排序的列。
class QRowHeader : public QHeaderView
{
Q_OBJECT
public:
QRowHeader(Qt::Orientation orientation, QWidget * parent = nullptr);
public:
//设置是否支持排序
void SetSortEnable(int logicalIndex, bool enable);
signals:
void MouseMove();
protected:
virtual void mouseMoveEvent(QMouseEvent * event) override;
virtual void mouseReleaseEvent(QMouseEvent *e) override;
virtual void paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const override;
private:
QMap<int, bool> m_Indicator;
};
然后代码是这样处理的,代码量不大,就是判断鼠标点击的位置如果是禁止排序的列,我们直接把事件循环中断啦!!!
是不是很坏,哈哈哈
void QRowHeader::mouseReleaseEvent(QMouseEvent * event)
{
int column = logicalIndexAt(event->pos().x());
if (m_Indicator.contains(column) && m_Indicator[column] == false)
{
return;
}
QHeaderView::mouseReleaseEvent(event);
}
六、排序
上边讲完了怎么去阻止排序,重写了QHeaderView。
virtual void paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const override;
里边有paintSection这样的函数,他就是绘制表头一个单元格所产生的回调。
在这个函数里边首先调用父窗口绘制表头,然后如果发现自己是排序列时,顺道去绘制一个排序图标.
代码很容易理解,绘制朝上还是朝下的图标取决于我们当前的排序方式
void QRowHeader::paintSection(QPainter * painter, const QRect & rect, int logicalIndex) const
{
...
//绘制三角形
if (sortIndicatorSection() == logicalIndex)
{
QRect indicator = rect;
indicator.setLeft(indicator.right() - 6);
indicator.setHeight(10);
indicator.moveTop((rect.height() - indicator.height()) / 2);
indicator.moveLeft(indicator.left() - 10);//距离左边界10像素
if (sortIndicatorOrder() == Qt::AscendingOrder)
{
painter->drawPixmap(indicator, QPixmap(":/QRowTable/down_arrow.png.png"));
}
else if (sortIndicatorOrder() == Qt::DescendingOrder)
{
painter->drawPixmap(indicator, QPixmap(":/QRowTable/up_arrow.png.png"));
}
}
}
绘制列头中的项时,如果当前列是排序列,那么这里就需要绘制排序图标。如果刚好这一列的文字是右对齐呢,正常情况下我们的图标还有文字可能是没有问题的。
如果该列很窄,那么排序图标和列cell中的文字可能会重叠
既然有概率重叠,这里我们就需要处理这个异常,当出现上述这种情况时,我们需要改变原有的文字绘制区域,不让他在图标的绘制区域绘制文本。
还是重写paintSection方法,这个是绘制列头一个单元格的方法。
看如下代码,首先我们填充了这个cell,为什么呢?不着急回答这个问题,接着往下看,我们把原有的rect向左便宜了16个像素,然后绘制列头cell。
void QRowHeader::paintSection(QPainter * painter, const QRect & rect, int logicalIndex) const
{
painter->fillRect(rect.adjusted(-1, 0, -1, -1), QColor("#212121"));
painter->save();
QRect r = rect;
if (sortIndicatorSection() == logicalIndex)
{
r.adjust(0, 0, -16, 0);
}
QHeaderView::paintSection(painter, r, logicalIndex);
painter->restore();
painter->setPen(QColor("#2B2B2B"));
painter->drawRect(rect.adjusted(-1, 0, -1, -1));
...
这样会有什么问题?仔细想想,文字绘制没问题,其实有问题的是背景色绘制会发现错误
因此我们在函数开头先把列头cell背景色进行了填充,这个背景色其实就是列头cell的背景色
setStyleSheet("QTableView{background:#333333;}"
"QHeaderView{background:#212121;color:gray;}"
"QHeaderView:section{padding:6px;border:0;background:#212121;color:gray;}");
上边是这个组件的qss样式,表头cell的背景色其实就是#2212121,因此填充区域我们也使用了这个色值,最终达到我们预期的效果。
这里插一句:绘制时一定要注意绘制的顺序,否则我们自定义的绘制可能会和Qt自己的绘制有冲突。这里尽量考虑清楚,免得产生冲突。
当列表头被点击时,QRowHeader对象会发出列cell被点击信号sectionClicked,这个信号也是从鼠标弹起函数mouseReleaseEvent中触发。
connect(m_pHHeader, &QRowHeader::sectionClicked, m_pFilter, &QFilterModel::setFilterKeyColumn);
connect(m_pHHeader, &QRowHeader::sectionClicked, this, [this](int column){
if (column == 0 || column == 1 || column == 2)
{
GetFilterModel()->SetCompareType(QFilterModel::CT_INT);
}
else
{
GetFilterModel()->SetCompareType(QFilterModel::CT_STRING);
}
});
sectionClicked信号触发后,这里干了两件事:设置当前的排序列和当前的排序方式。
SetCompareType
接口用于设置当前排序方式。
目前这个组件支持3中排序方式,数字、百分比和字符串
bool QFilterModel::lessThan(const QModelIndex & source_left, const QModelIndex & source_right) const
{
if (m_eType == CT_INT)
{
double l = source_left.data().toDouble();
double r = source_right.data().toDouble();
return l < r;
}
else if (m_eType == CT_PERCENT)
{
double l = source_left.data(Qt::UserRole + 2).toString().remove("%").toDouble();
double r = source_right.data(Qt::UserRole + 2).toString().remove("%").toDouble();
return l < r;
}
else
{
QString l = source_left.data().toString();
QString r = source_right.data().toString();
return l < r;
}
}
这里需要说明一下,百分比为什么要单独拿出来分一类,设计之初,百分比和数字是放在一类中去比较的,但是后来发现百分比没有办法存储在double中,因此添加了百分比分类。这里也不强制非要添加一个新类,其他能实现比较需求的办法均可。
七、列对其方式
Qt的文字对其方式有如下这么多,然后我们这个组件支持比较主流的集中排序方式,分别是:水平居左、水平居中、水平居右
水平居右时,如果当前列时排序列,我们文字绘制的位置区域不能包含排序图标的大小,否则文字可能和图标重叠
enum AlignmentFlag {
AlignLeft = 0x0001,
AlignLeading = AlignLeft,
AlignRight = 0x0002,
AlignTrailing = AlignRight,
AlignHCenter = 0x0004,
AlignJustify = 0x0008,
AlignAbsolute = 0x0010,
AlignHorizontal_Mask = AlignLeft | AlignRight | AlignHCenter | AlignJustify | AlignAbsolute,
AlignTop = 0x0020,
AlignBottom = 0x0040,
AlignVCenter = 0x0080,
AlignBaseline = 0x0100,
// Note that 0x100 will clash with Qt::TextSingleLine = 0x100 due to what the comment above
// this enum declaration states. However, since Qt::AlignBaseline is only used by layouts,
// it doesn't make sense to pass Qt::AlignBaseline to QPainter::drawText(), so there
// shouldn't really be any ambiguity between the two overlapping enum values.
AlignVertical_Mask = AlignTop | AlignBottom | AlignVCenter | AlignBaseline,
AlignCenter = AlignVCenter | AlignHCenter
};
控件之外,我们通过SetAlignment接口设置了该列的排序方式,排序方式对列的内容和列头都起作用。也就是说列头和列内容排序方式是一致的。
model->SetAlignment(0, Qt::AlignRight | Qt::AlignVCenter);
model->SetAlignment(1, Qt::AlignRight | Qt::AlignVCenter);
model->SetAlignment(2, Qt::AlignRight | Qt::AlignVCenter);
model->SetAlignment(3, Qt::AlignLeft | Qt::AlignVCenter);
model->SetAlignment(4, Qt::AlignLeft | Qt::AlignVCenter);
model->SetAlignment(5, Qt::AlignLeft | Qt::AlignVCenter);
列头绘制时,会通过headerData接口拿文字对其方式,然后这里只需要返回之前使用SetAlignment接口设置的对其方式即可。
QVariant QRowModel::headerData(int section, Qt::Orientation orientation, int role /*= Qt::DisplayRole*/) const
{
if (Qt::TextAlignmentRole == role)
{
//Q::AlignLeft | Qt::AlignVCenter
auto iter = m_AlignmentList.find(section);
if (iter != m_AlignmentList.end())
{
return iter->second;
}
return Qt::AlignCenter;
//return m_AlignmentList.at(section);
}
return QStandardItemModel::headerData(section, orientation, role);
}
补充
重新换了一种方式实现第五节的mouseReleaseEvent函数。
之前的方法在开启了列拖拽之后会出现问题
void QRowHeader::mouseReleaseEvent(QMouseEvent * event)
{
int column = logicalIndexAt(event->pos().x());
if (m_Indicator.contains(column) && m_Indicator[column] == false)
{
setSectionsClickable(false);
QHeaderView::mouseReleaseEvent(event);
setSectionsClickable(true);
}
else
{
QHeaderView::mouseReleaseEvent(event);
}
}
八、相关文章
![]() |
![]() |
很重要--转载声明
本站文章无特别说明,皆为原创,版权所有,转载时请用链接的方式,给出原文出处。同时写上原作者:朝十晚八 or Twowords
如要转载,请原文转载,如在转载时修改本文,请事先告知,谢绝在转载时通过修改本文达到有利于转载者的目的。
QRowTable表格控件(二)-红涨绿跌的更多相关文章
- QRowTable表格控件(三)-效率优化之-合理使用QStandardItem
目录 一.开心一刻 二.概述 三.效果展示 四.QStandardItem 1.QStandardItem是什么鬼 2.性能分析 3.QStandardItem使用上的坑 五.相关文章 原文链接:QR ...
- QRowTable表格控件(四)-效率优化之-优化数据源
目录 一.开心一刻 二.问题分析 三.重写数据源 1.自己存储数据 2.重写data接口 四.比较 五.相关文章 原文链接:QRowTable表格控件(四)-效率优化之-优化数据源 一.开心一刻 一程 ...
- QRowTable表格控件(五)-重写表头排序、支持第三次单击恢复默认排序
目录 一.原生表格 二.效果展示 三.实现方式 1.排序列定制 2.排序交互修改 四.相关文章 原文链接:QRowTable表格控件(五)-重写表头排序.支持第三次单击恢复默认排序 一.原生表格 开发 ...
- QRowTable表格控件-支持hover整行、checked整行、指定列排序等
目录 一.开心一刻 二.嘴一嘴 三.效果展示 四.浅谈实现 五.自定义数据源 1.data函数 2.flags函数 六.自定义视图 1.目的 2.问题分析 七.测试 八.相关文章 原文链接:QRowT ...
- QTableView表格控件区域选择-自绘选择区域
目录 一.开心一刻 二.概述 三.效果展示 四.实现思路 1.绘制区域 2.绘制边框 3.绘制 五.相关文章 原文链接:QTableView表格控件区域选择-自绘选择区域 一.开心一刻 陪完客户回到家 ...
- 深入浅出ExtJS 第三章 表格控件
3.1 表格的特性简介 >.Ext中的表格功能:包括排序/缓存/拖动/隐藏某一列/自动显示行号/列汇总/单元格编辑等实用功能; >.表格由类Ext.grid.GridPanel定义,继承自 ...
- Ext入门学习系列(五)表格控件(3)
上节学习了Ext中如何绑定服务器端传递的数据.分别用asp.net和asp.net MVC.PHP.XML为例.本节主要介绍绑定之后的分页功能. 一.Ext的表格控件如何绑定? 分页是Ext自带的一个 ...
- Ext入门学习系列(五)表格控件(2)
上节学习了Ext中表格控件,从创建,到定义数据源.绑定显示,大体明白了一个基本的表格控件是怎么实现的.而我们用表格控件多用于从各种数据源接收数据并显示出来,并不是写死的.本章我们就不同数据源的不同实现 ...
- Ext入门学习系列(五)表格控件(1)
上节学习了Ext面板控件,为后面的各个控件学习奠定基础,在此基础上本章将学习网络开发最期待的功能——表格控件. 我们都知道网络编程语言中,除了.net其他的基本没有提供网格控件,而最近的asp.net ...
随机推荐
- Windows 10 UWP 部署
原文 http://youthlin.com/20151105.html 我们知道VS连接手机可以直接部署到手机里,但平板貌似无法这样干,平板与电脑连接没有丝毫反应……那么想看VS里写的uwp应 ...
- EF日志记录,包括记录Linq生成的Sql
<interceptors> <interceptor type="System.Data.Entity.Infrastructure.Interception.Datab ...
- Qt5 结构及模块组成?
作为一个Qt的粉丝,对将于明年发布的Qt5充满了期待.可是想想Qt5将发生的巨大变化,心底又有点不安.Qt5到底会变成什么样呢? 看看近期Qt5的一些大动作: 从 QtCore中移除 QSetting ...
- 海康威视频监控设备Web查看系统(三):Web篇
声明:本系列文章只提供交流与学习使用.文章中所有涉及到海康威视设备的SDK均可在海康威视官方网站下载得到.文章中所有除官方SDK以为的代码均可随意使用,任何涉及到海康威视公司利益的非正常使用由使用者自 ...
- Md2All:好用的markdown文件转换工具,文章迁移微信公众号的利器
目录 简介 使用体验 极速上手 更多功能 总结 简介 markdown以简单的语法和强大的功能,征服了无数技术创作者,几乎主流的技术博客网站都开始支持markdown语言撰写博客.但是微信公众号的文章 ...
- [转]Android的taskAffinity
Activity的归属,也就是Activity应该在哪个Task中,Activity与Task的吸附关系.我们知道,一般情况下在同一个应用中,启动的Activity都在同一个Task中,它们在该Tas ...
- 《HelloGitHub》第 39 期
兴趣是最好的老师,HelloGitHub 就是帮你找到兴趣! 简介 分享 GitHub 上有趣.入门级的开源项目. 这是一个面向编程新手.热爱编程.对开源社区感兴趣 人群的月刊,月刊的内容包括:各种编 ...
- Spring Boot的学习之路(02):和你一起阅读Spring Boot官网
官网是我们学习的第一手资料,我们不能忽视它.却往往因为是英文版的,我们选择了逃避它,打开了又关闭. 我们平常开发学习中,很少去官网上看.也许学完以后,我们连官网长什么样子,都不是很清楚.所以,我们在开 ...
- Spring Boot2(二):使用Spring Boot2集成Mybatis缓存机制
前言 学习SpringBoot集成Mybatis的第二章,了解到Mybatis自带的缓存机制,在部署的时候踩过了一些坑.在此记录和分享一下Mybatis的缓存作用. 本文章的源码再文章末尾 什么是查询 ...
- SpringBoot从入门到精通二(SpringBoot整合myBatis的两种方式)
前言 通过上一章的学习,我们已经对SpringBoot有简单的入门,接下来我们深入学习一下SpringBoot,我们知道任何一个网站的数据大多数都是动态的,也就是说数据是从数据库提取出来的,而非静态数 ...