[Qt]QListView 重绘实例之一:背景重绘
0 环境
- Windows 11
- Qt 5.15.2 MinGW x64
1 系列文章
简介:本系列文章,是以纯代码方式实现 Qt 控件的重构,尽量不使用 Qss 方式。
《[Qt]QListView 重绘实例之一:背景重绘》
《[Qt]QListView 重绘实例之二:列表项覆盖的问题处理》
《[Qt]QListView 重绘实例之三:滚动条覆盖的问题处理》
《[Qt]QListView 重绘实例之四:效果一讲解》
《[Qt]QListView 重绘实例之五:效果二讲解》
2 开始
自定义 Qt 控件,无外乎两个主要目的:
- 实现更漂亮的样式;
- 实现更强大的/更合适的功能;
要实现以上两个主要目的,基本上都需要对 Qt 原生控件进行一定的重绘,以适应需求。
本节中,主要讲解 QListView 的背景绘制。

(之所以单独写一文,是因为自己动手实现时才发现:虽然最后的实现代码并不多,但要弄懂这些,还是要花费很多精力的。)
→ 解决方案直达 ←
3 paintEvent 重绘与问题
通常,重构一个新控件,基本上都是直接重写 void paintEvent(QPaintEvent *event) 方法。
void PListView::paintEvent(QPaintEvent *event)
{Q_UNUSED(event)QPainter painter(this); // Errorpainter.setRenderHint(QPainter::Antialiasing);painter.setPen(QPen(Qt::red));painter.setBrush(QBrush(Qt::white));painter.drawRoundedRect(rect(), 5, 5);
}
3.1 问题 1 —— 绘图对象
通常,进行重绘时,新建 QPainter 对象都是以父控件为对象,意即在父控件中进行绘制。
但是,如果这样直接对 QListView 进行重绘,是会出错的:
QWidget::paintEngine: Should no longer be called
QPainter::begin: Paint device returned engine == 0, type: 1
QPainter::setRenderHint: Painter must be active to set rendering hints
QPainter::setPen: Painter not active
QPainter::setBrush: Painter not active
(猜测)原因大致应该是:QListView 是由多个子控件组成,实际负责显示内容的只是其中的一个子控件,所以绘制对象需要具体指定到负责显示的对象。
QListView 继承树如下:

而一个默认 QListView 对象包含的子控件如下:
(QWidget(0x1eb4600, name = "qt_scrollarea_viewport"),
QStyledItemDelegate(0x1eb1840),
QItemSelectionModel(0x1eb1ba0),
QWidget(0x1eb1010, name = "qt_scrollarea_hcontainer"),
QWidget(0x1eb1150, name = "qt_scrollarea_vcontainer"))
其中,实际显示内容的对象就是 “qt_scrollarea_viewport”,也就是 QListView 的视口(viewport)。这样做的主要原因,是要实现对 QListView 内容的滚动显示(显示部分内容)。
所以,对于 QListView 重绘,必须要针对视口 viewport()。
void PListView::paintEvent(QPaintEvent *event)
{Q_UNUSED(event)QPainter painter(viewport());painter.setRenderHint(QPainter::Antialiasing);painter.setPen(QPen(Qt::red));painter.setBrush(QBrush(Qt::white));painter.drawRoundedRect(rect(), 5, 5);
}
效果如下图示:

3.2 问题 2 —— 外边线框
从上图看,这次倒是绘制出背景框。但首先注意到的问题是 QListView 默认外连线框,非常显眼。因此,首要目的是要去掉这个外连线框。
上文实现代码中的重绘过程,仅做了两件事:
- 绘制了一个圆角矩形;
- 阻止了
QListView的其它默认绘制;
因此 ,基本可以肯定,外连线框并不是由 paintEvent() 绘制过程中引起的。看来原因得到 QListView 里层查找。
原因查找的具体过程略过不述,QListView 的外边线框其实就是其父类 QFrame 的边框(可以理解为一个底层,其它内容都绘制在这个底层之上,毕竟 QListView 是 UI 控件)。
只需要对 QListView 进行如下设置,改变一下 QFrame 样式即可去掉外边线框:
PListVeiw::PListView(QWidget *parent) : QListView(parent)
{setFrameStyle(QFrame::NoFrame);
}
效果如下:

强制隐藏/关闭垂直滚动条,效果如下:

3.3 问题 3 —— 绘制区域
从上图可知,绘制的背景效果基本出来了。但是,也被垂直滚动条挡住了一部分。
再回来看一看绘图代码,其中有一行如下:
painter.drawRoundedRect(rect(), 5, 5);
此时,指定的绘图区域为 rect(),即针对控件的整个显示区域。而我们指定的绘图对象是 QListView 的视口,原则上为了保证一致性,在什么上绘图,就应该在该对象的区域内进行绘制。所以,修改以上那行的代码:
painter.drawRoundedRect(viewport()->rect(), 5, 5);
效果如下:

这种效果,也还可以。一些样式也确实是将滚动条置于控件之外的。
本文不针对此样式进行讲解,主要考虑滚动条内含在列表内的样式。
滚动条的问题,先按下不提,具体详见本系列后文说明。参考《[Qt]QListView 重绘实例之三:滚动条覆盖的问题处理》。
3.4 问题 4 —— 滚动时残留
先前为了重点显示 QListView 的背景绘制效果,所以没有绘制 QListView 的内容。
现在,加上内容的绘制代码:
void PListView::paintEvent(QPaintEvent *event)
{QPainter painter(viewport());painter.setRenderHint(QPainter::Antialiasing);painter.setPen(QPen(Qt::red));painter.setBrush(QBrush(Qt::white));painter.drawRoundedRect(rect(), 5, 5); // 理解为视口占据整个控件区域QListView::paintEvent(event);
}
说明:绘制顺序是有要求的。应该先绘制背景,然后绘制列表内容(即前景)。
效果图如下:

但是,如果我们使用鼠标滚轮滚动或拖动滚动条,滚动 QListView 的内容,却出现了如下效果:

这显然不是想要的效果。
具体原因未深究,暂时未知,猜测应该是底层代码的原因。因为,上文中的重绘代码其实很简单,并未做多余的动作。
但这种残留效果,显然不可接受。
因此,至少到目前,这种方式绘制 QListView 的背景是不可行的。
(考虑到添加委托会对列表项进行绘制,可能会影响到这个残留问题。尝试过添加委托,但这个残留问题依然存在。)
4 解决方案
从上文得知,采用 paintEvent() 对 QListView 背景进行绘制的方案不可行。
另,考虑到后来的 Qt 版本对于 Qss 的性能问题,本系列也不考虑 Qss 方案。
于是,已知可行的方案只剩使用 QProxyStyle 代理样式定制了。
(之前也没有实际使用过代理样式,通过学习/练习/测试得出了合适的效果。)
关于 QProxyStyle 的具体内容,查找资料的过程中有发现,有不少介绍的好博文,请酌情参考(文末参考资料有链接),本文不另述。
4.1 定义背景绘制样式
/* .h */
class PListViewStyle : public QProxyStyle
{
public:PListViewStyle();void drawControl(QStyle::ControlElement element,const QStyleOption *option,QPainter *painter,const QWidget *widget = nullptr) const override;
};/* .cpp */
PListViewStyle::PListViewStyle()
{
}
void PListViewStyle::drawControl(QStyle::ControlElement element,const QStyleOption *option,QPainter *painter,const QWidget *widget) const
{switch(element){case QStyle::CE_ShapedFrame:{const QStyleOptionFrame *opt = qstyleoption_cast<const QStyleOptionFrame *>(option);if(nullptr == opt) { return; }painter->save();painter->setRenderHint(QPainter::Antialiasing);painter->setPen(QPen(Qt::red));painter->setBrush(QBrush(Qt::white));painter->drawRoundedRect(opt->rect, 5, 5);painter->restore();return;}default:break;}QProxyStyle::drawControl(element, option, painter, widget);
}
4.2 使用代理样式
PListVeiw::PListView(QWidget *parent) : QListView(parent)
{// setFrameStyle(QFrame::NoFrame); // Must delete or comment itsetStyle(new PListViewStyle);
}
注意:
- 需要删除重写函数
void paintEvent(QPaintEvent *event),否则可能覆盖效果。 - 需要删除对
QFrame的样式设置,不能再设置为QFrame::NoFrame。因为代理样式实际是对QFrame进行绘制的,如果设置了QFrame::NoFrame,则绘制的样式根本就不会显示。
效果如下:

至少看上去,基本达到了预期的效果。
但是,
但是,
但是,总有但是,哈哈。
将背景的圆角矩形圆角半径加大一下,再来看看效果图:

从上图可以看出有几个问题:
- 列表项在背景的上层,即背景绘制先于列表项。而列表项也是有背景的(以及高亮/选中背景),可以理解为列表项就是一个个小矩形(默认没有圆角)。由上可以看出,视口的最上/最下一行,都有矩形直角覆盖了背景(圆角矩形),因此破坏了背景的效果;
- 同理,滚动条也在背景上层,滚动条也是一个直角矩形,矩形直角覆盖了背景,因此也破坏了背景的效果;
其中:
- 对于列表项产生的覆盖问题,可以通过使用委托,控制列表项背景(默认背景/高亮背景/选中背景)的绘制,使绘制视口最上/最下一行时,绘制合适的圆角效果。
- 对于滚动条的问题,就复杂得多,具体详见本系列后文内容。参考《[Qt]QListView 重绘实例之三:滚动条覆盖的问题处理》。
5 参考资料
- 《C++ GUI Qt 4编程(第二版)》,第 19 章,19.2 子类化 QStyle
- QStyle类用法总结(一)
- 绘制自定义QSlider
相关文章:
[Qt]QListView 重绘实例之一:背景重绘
0 环境 Windows 11Qt 5.15.2 MinGW x64 1 系列文章 简介:本系列文章,是以纯代码方式实现 Qt 控件的重构,尽量不使用 Qss 方式。 《[Qt]QListView 重绘实例之一:背景重绘》 《[Qt]QListView 重绘实例之二:列表项覆…...
国庆周《Linux学习第二课》
Linux开篇指南针环境安装(第一课)-CSDN博客 Linux详细的环境安装介绍在上面 第一 环境准备过程 安装过程...
6年前的麒麟980依旧可以再战
麒麟980,使用6年后的今天,我对它进行跑分测试。 在bench旗下的VRMark跑分中,麒麟980荣获5023分,同款跑分APP,要知道同一时期的高通骁龙855只有4937分, 打游戏,以和平精英为例,帧率3…...
JS计算任意多边形的面积
计算任意多边形的面积需要使用一些几何数学公式。具体的计算方法取决于多边形的形状和提供的顶点坐标。下面是一个通用的 JavaScript 函数,用于计算任意多边形的面积,假设你提供多边形的顶点坐标数组: function calculatePolygonArea(vertic…...
ios xcode15 navigationController?.navigationBar.isHidden = false无效
xcode 15 用 navigationController?.setNavigationBarHidden(true, animated: false)隐藏navigationBar后,再调用 navigationController?.navigationBar.isHidden false无效 解决 用 navigationController?.navigationBar.isHidden true隐藏navigationBar...
Python二级 每周练习题20
练习一: 日期计算器 设计一款日期计算程序,能否实现下面的功能: (1)要求用户分别输入年、月、日(分三次输入); (2)程序自动会根据输入的年月日计算出这一天是这一年的第几天; (3)输出格式为:这…...
深度学习-一个简单的深度学习推导
文章目录 前言1.sigmod函数2.sigmoid求导3.损失函数loss4.神经网络1.神经网络结构2.公式表示-正向传播3.梯度计算1.Loss 函数2.梯度1.反向传播第2-3层2.反向传播第1-2层 3.python代码4.MNIST 数据集 前言 本章主要推导一个简单的两层神经网络。 其中公式入口【入口】 1.sigmod…...
ES写入数据报错:retrying failed action with response code: 429
报错: 使用logstash导入分片数量为9的index发生错误,[logstash.outputs.elasticsearch] retrying failed action with response code: 429 ({"type">"es_rejected_execution_exception", "reason">"rejected execution …...
Redis给Lua脚本的调用
Redis给Lua脚本的调用 Redis为Lua提供了一组内置函数,这些函数可用于执行与Redis数据存储和操作相关的任务。这些内置函数可以在Lua脚本中使用,以便在Redis中执行各种操作。以下是一些常用的Redis Lua内置函数: 主要知道call就好了 redis.ca…...
Spring工具类--ReflectUtils的使用
原文网址:Spring工具类系列--ReflectUtils的使用_IT利刃出鞘的博客-CSDN博客 简介 本文介绍Spring的ReflectUtils的使用。 ReflectUtils工具类的作用:便利地进行反射操作。 Spring还有一个工具类:ReflectionUtils,它们在功能上…...
联盟 | 彩漩 X HelpLook,AI技术赋能企业效率提升
近日,AI 驱动的 PPT 协作分享平台「 彩漩 」与 AI 知识库搭建工具「 HelpLook」,携手为用户工作流注入更多智能和创造力,全面拥抱 AIGC 时代带来的机遇,致力于提供前沿的智能解决方案。 彩 漩 彩漩是一个以 AI 技术为基础、贯彻 …...
MATLAB m文件格式化
记录一个网上查到的目前感觉挺好用的格式化方法。 原链接: https://cloud.tencent.com/developer/article/2058259 压缩包: 链接:https://pan.baidu.com/s/1ZpQ9qGLY7sjcvxzjMPAitw?pwd6666 提取码:6666 下载压缩包…...
分拆菜鸟将使阿里巴巴股票迎来新一轮上涨?
来源:猛兽财经 作者:猛兽财经 总结: (1)阿里巴巴(BABA)最近公布的季度财报显示,该公司有能力实现快速盈利。 (2)据报道,阿里巴巴正计划分拆菜鸟集团,并将在香…...
Excel 技巧记录-那些复杂的公式和函数
目标表格的关键字在行和列里,匹配源表格的行和列对应的关键字 **具体需求为:**表A叫Total_202308.xlsx,sheet叫摊销前分析,表B叫data.xlsx,sheet叫总部费用,表A的数据里,A列是科目名称,第9行是…...
vue里使用elementui的级联选择器el-cascader进行懒加载的怎么实现数据回显?
需要实现的懒加载回显效果 比如:后端返回数据 广东省/广州市/天河区 :440000000000/440100000000/440106000000,需要我们自动展开到天河区的下一级,效果如下 代码实现 我的实现思路就是拿到 440000000000/440100000000/44010600…...
Qt raise()问题
项目场景: 需要将一个弹窗提升至最前面,那么弹出时直接使用raise()即可。 问题描述: 使用QDialog::raise()时,偶发界面阻塞卡死现象。 原因分析: QDialog::raise()函数是置于顶部的作用,但是如果使用不当…...
26591-2011 粮油机械 糙米精选机
声明 本文是学习GB-T 26591-2011 粮油机械 糙米精选机. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本标准规定了糙米精选机的有关术语和定义、工作原理、型号及基本参数、技术要求、试验方法、检 验规则、标志、包装、运输和储存要求。 …...
JavaScript位运算的妙用
位运算的妙用: 奇偶数, 色值换算,换值, 编码等 位运算的基础知识: 操作数是32位整数自动转化为整数在二进制下进行运算 一.按位与& 判断奇偶数: 奇数: num & 1 1偶数: num & 1 0 基本知识: 用法:操作数1 & 操作数2规则:有 0 则为…...
This dependency was not found: vxe-table/lib/vxe-table in ./src/main.js
描述 使用时 安装 npm install xe-utils vxe-table 引入 import Vue from vue import xe-utils import VXETable from vxe-table import vxe-table/lib/style.css vxe-table是一个基于 vue 的 PC 端表格组件, 支持增删改查、虚拟滚动、懒加载、快捷菜单、数据校验…...
网工内推 | H3C售前工程师,上市公司,13薪,有带薪年假、年终奖
01 长虹佳华 招聘岗位:高级售前工程师(H3C) 职责描述: 1. 负责公司签约代理的网络安全产品在区域的项目售前技术支持工作,包括项目售前交流、方案编写、招投标、产品测试等相关支持工作; 2. 与厂商产品部门…...
VB.net复制Ntag213卡写入UID
本示例使用的发卡器:https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...
《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...
初探Service服务发现机制
1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能:服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源…...
CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)
漏洞概览 漏洞名称:Apache Flink REST API 任意文件读取漏洞CVE编号:CVE-2020-17519CVSS评分:7.5影响版本:Apache Flink 1.11.0、1.11.1、1.11.2修复版本:≥ 1.11.3 或 ≥ 1.12.0漏洞类型:路径遍历&#x…...
面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...
基于SpringBoot在线拍卖系统的设计和实现
摘 要 随着社会的发展,社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统,主要的模块包括管理员;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...
【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...
