如何在Qt中绘制一个带有动画的弧形进度条?

如何在Qt中绘制一个弧形的进度条

在图形用户界面开发中,进度指示控件(Progress Widget)是非常常见且实用的组件。CCArcProgressWidget 是一个继承自 QWidget 的自定义控件,用于绘制圆弧形进度条。当然,笔者看了眼公开的实现,基本上都非常完善了,笔者在这里添加了一个更好的动画。

在这里插入图片描述

类定义概览

CCArcProgressWidget 类定义在 CCArcProgressWidget.h 中,使用 Qt 元对象系统,通过 Q_OBJECT 宏启用信号与属性机制。该控件支持如下属性绑定:

  • value:当前进度值
  • maxValue:最大进度值
  • displayValue:动画过程中的显示值(与实际 value 异步)

这些属性可被 QML 或动画机制绑定,便于动态效果的呈现。(笔者这里使用的是Q_PROPERTY属性系统公开的,所以QML可用(笑))

静态常量定义(这部分是笔者认为编译的时候可以指定的)

类中定义了多个静态常量,用于控制组件的外观与行为:

  • DURATION:动画持续时间(单位:毫秒),默认为 500ms
  • ARC_WIDTH:圆弧的线宽,默认为 50 像素
  • DEFAULT_VALUE:默认初始值,为 0
  • DEFAULT_MAX:默认最大值,为 100
  • DEF_TEXT_COLOR:默认文本颜色
  • DEF_BKCOLOR:默认背景弧颜色(未完成部分)
  • DEF_ARC_COLOR:默认进度弧颜色(已完成部分)

这些常量使得控件具有清晰的默认状态,便于使用和维护。

属性访问与设置接口

该类提供了一系列 inline 内联函数和公开接口,用于读取与设置进度值及外观样式:

  • int value() const:获取当前进度值
  • void setValue(int val):设置当前进度值(含动画)
  • int maxValue() const:获取最大值
  • void setMaxValue(int max):设置最大值
  • QColor progressArcColor() const / void setProgressArcColor(const QColor&):读取与设置进度弧颜色
  • QColor progressBackgroundColor() const / void setProgressBackgroundColor(const QColor&):读取与设置背景弧颜色
  • QColor progressTextColor() const / void setProgressTextColor(const QColor&):读取与设置文本颜色

所有设置函数内部均会判断是否真正发生变化,避免无谓的刷新,若发生更改则调用 update() 触发重绘。

信号机制

该控件定义了三个信号:

  • valueChanged(int):当用户设置新进度值时发出
  • maxValueChanged(int):当最大值被重新设置时发出
  • displayValueChanged(int):当动画中显示的值发生变化时发出

这些信号便于其他模块(如界面展示、数据记录)实时响应进度的变化。

绘制函数与动画支持

该类重载了 paintEvent 事件处理函数,实现核心绘制逻辑。绘制内容包括三部分:

  • 背景弧:通过 drawBackgroundArc() 绘制未完成部分
  • 进度弧:通过 drawProgressArc() 根据当前动画角度绘制完成部分
  • 中心文本:通过 drawText() 绘制当前数值或状态文字

同时,setupAnimation() 函数用于构建 QPropertyAnimation 动画,使 valuedisplayValue 之间具备平滑过渡效果。动画期间实际值不变,仅 displayValue 动态变化,从而提升用户体验。

私有成员变量

类中使用了如下私有成员保存状态:

  • progress_value:当前进度值
  • progress_display_value:当前显示值(用于动画)
  • progress_max_value:最大进度值
  • progress_minAngleprogress_startAngle:控制弧线的起始与方向(默认从顶部顺时针)
  • progress_arc_colorprogress_backgroundColorprogress_textColor:颜色配置
  • QPropertyAnimation* animation:动画对象指针

这些成员变量共同构成了进度显示的完整状态。

使用示例(简要)

CCArcProgressWidget* widget = new CCArcProgressWidget(this);
widget->setValue(70);
widget->setMaxValue(100);
widget->setProgressArcColor(Qt::blue);
widget->setProgressBackgroundColor(Qt::lightGray);
widget->setProgressTextColor(Qt::black);

以上代码将在界面中创建一个蓝色的圆形进度条,表示当前进度为 70%。

一些实现的细节说明

​ 下面的部分是属性设置的接口,没什么有趣的。

#include "CCArcProgressWidget.h"
#include <QPropertyAnimation>CCArcProgressWidget::CCArcProgressWidget(QWidget* parent): QWidget { parent } {setupAnimation();
}void CCArcProgressWidget::setupAnimation() {animation = new QPropertyAnimation(this, "displayValue");animation->setDuration(DURATION);animation->setEasingCurve(QEasingCurve::OutCubic);
}void CCArcProgressWidget::setValue(int val) {val = qBound(0, val, progress_max_value);if (val == progress_value) // avoid duplicate animationsreturn;progress_value = val;animation->stop();animation->setStartValue(progress_display_value);animation->setEndValue(progress_value);animation->start();
}

​ 下面说下我们的绘制,这里是每一次触发重绘的时候我们的设备实际上进行的绘制。

void CCArcProgressWidget::paintEvent(QPaintEvent* event [[maybe_unused]]) {QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing, true);QRectF baseRect = rect();double side = qMin(baseRect.width(), baseRect.height());// 到这里,是为了获取绘制成正方形而不是椭圆形(不然太难看了)QRectF squareRect((baseRect.width() - side) / 2.0,(baseRect.height() - side) / 2.0,side, side);int margin = ARC_WIDTH + 5;QRectF arcRect = squareRect.adjusted(margin, margin, -margin, -margin);double radius = qMin(arcRect.width(), arcRect.height()) / 2;QPointF center = arcRect.center();double angle = 360.0 * progress_display_value / progress_max_value;angle = qMax<double>(progress_minAngle, -angle);drawBackgroundArc(painter, arcRect);drawProgressArc(painter, arcRect, angle);drawText(painter, center, radius);
}
  1. paintEvent 事件首先确定绘制区域 arcRect,再根据当前 displayValue 计算对应的角度 angle。之后,依次调用:
  • drawBackgroundArc:用圆弧绘制背景轨迹。
  • drawProgressArc:绘制当前进度的圆弧,同时在圆弧末端绘制小圆点,增强视觉效果。
  • drawText:居中绘制当前进度的百分比文本。
void CCArcProgressWidget::drawBackgroundArc(QPainter& painter, const QRectF& arcRect) {QPen pen(progress_backgroundColor, ARC_WIDTH);pen.setCapStyle(Qt::RoundCap);painter.setPen(pen);painter.drawArc(arcRect, progress_startAngle * 16, 360 * 16);
}void CCArcProgressWidget::drawProgressArc(QPainter& painter, const QRectF& arcRect, double angle) {if (angle == 0)return;QConicalGradient gradient(arcRect.center(), progress_startAngle);gradient.setColorAt(0, progress_arc_color.lighter(150));gradient.setColorAt(0.5, progress_arc_color);gradient.setColorAt(1, progress_arc_color.darker(150));QPen pen(QBrush(gradient), ARC_WIDTH);pen.setCapStyle(Qt::FlatCap);painter.setPen(pen);painter.drawArc(arcRect, progress_startAngle * 16, -angle * 16);double spanAngleRad = qDegreesToRadians(progress_startAngle - angle);double cx = arcRect.center().x();double cy = arcRect.center().y();double rx = arcRect.width() / 2;double ry = arcRect.height() / 2;double ex = cx + rx * qCos(spanAngleRad);double ey = cy - ry * qSin(spanAngleRad);QBrush brush(gradient);painter.setBrush(brush);painter.setPen(Qt::NoPen);painter.drawEllipse(QPointF(ex, ey), ARC_WIDTH / 2.0, ARC_WIDTH / 2.0);
}void CCArcProgressWidget::drawText(QPainter& painter, const QPointF& center, double radius) {painter.setFont(QFont("Arial", radius * 0.3, QFont::Bold));painter.setPen(progress_textColor);QString text = QString("%1%").arg(qRound(100.0 * progress_display_value / progress_max_value));QRectF textRect(center.x() - radius, center.y() - radius,radius * 2, radius * 2);painter.drawText(textRect, Qt::AlignCenter, text);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/news/907555.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在 Mac 下 VSCode 中的终端使用 option + b 或 f 的快捷键变成输入特殊字符的解决方案

前言 在终端里&#xff0c;我们可以使用 option b 和 option f 来在我们输入的命令中进行快速的前后调整光标&#xff0c;但是&#xff0c;在未设置的情况下&#xff0c;在 MacOS 中&#xff0c;会变成输入特殊字符。 普通键盘上是 alt b 和 alt f &#xff0c;只是叫法不…

Android bindservice绑定服务,并同步返回service对象的两个方法

先上一段代码&#xff1a; private IDeviceService deviceService null; private ServiceConnection connnull; private synchronized void bindyourservice() { Intent intent new Intent();intent.setPackage("servicepackagename");intent.setAction("…

Go语言之空接口与类型断言

Go 语言中&#xff0c;接口是一种强大的抽象机制。其中&#xff0c;空接口&#xff08;interface{}&#xff09;和类型断言为我们提供了处理任意类型与类型检查的能力。 一、空接口&#xff08;interface{}&#xff09; 空接口是 Go 中最特殊的接口&#xff1a;不包含任何方法…

三、OrcaSlicer预设显示

一、界面类 主框架使用的是wxWidgets库&#xff1b;3D模型的渲染区的控件&#xff0c;使用的是imgui库。 1、Plater 此类在OrcaSlicer\src\slic3r\GUI\Plater.hpp文件中定义 1.1 Plater::priv 此结构体是Plater的数据类&#xff0c;各种数据的对象和指针保存在此结构体中。如…

00 QEMU源码中文注释与架构讲解

QEMU源码中文注释与架构讲解 先占坑&#xff1a;等后续完善后再更新此文章 注释作者将狼才鲸创建日期2025-05-30更新日期NULL CSDN阅读地址&#xff1a;00 QEMU源码中文注释与架构讲解Gitee源码仓库地址&#xff1a;才鲸嵌入式/qemu 一、前言 参考网址 QEMU 源码目录简介qe…

一、Sqoop历史发展及原理

作者&#xff1a;IvanCodes 日期&#xff1a;2025年5月30日 专栏&#xff1a;Sqoop教程 在大数据时代&#xff0c;数据往往分散存储在各种不同类型的系统中。其中&#xff0c;传统的关系型数据库 (RDBMS) 如 MySQL, Oracle, PostgreSQL 等&#xff0c;仍然承载着大量的关键业务…

【Halcon】图像分割中的 regiongrowing 与dyn_threshold 动态阈值 算法详解对比

图像分割中的 regiongrowing 与动态阈值算法详解对比 在使用 HALCON 进行图像处理时&#xff0c;图像分割是最常见也最关键的操作之一。本文将深入讲解 regiongrowing 算子的原理与使用方法&#xff0c;并与另一常见方法——动态阈值 (dyn_threshold) 进行详细对比&#xff0c…

Docker部署项目无法访问,登录超时完整排查攻略

项目背景&#xff1a;迁移前后端应用&#xff0c;prod环境要求保留443端口&#xff0c;开发环境37800端口&#xff0c;后端容器端口为8000&#xff0c;前端为80&#xff0c;fastAPI对外端口为41000 生产环境部署在VM01,开发环境部署在VM03&#xff0c;在VM01配置nginx转发 [r…

充电便捷,新能源汽车移动充电服务如何预约充电

随着新能源汽车的普及&#xff0c;充电便捷性成为影响用户体验的关键因素之一。传统的固定充电桩受限于地理位置和数量&#xff0c;难以完全满足用户需求&#xff0c;而移动充电服务的出现&#xff0c;为车主提供了更加灵活的补能方式。通过手机APP、小程序或在线平台&#xff…

探索C++标准模板库(STL):从容器到底层奥秘-全面解析String类高效技巧(上篇)

前引&#xff1a;在现代软件开发中&#xff0c;字符串处理是几乎所有程序的核心需求之一。无论是文本解析、网络通信&#xff0c;还是用户交互&#xff0c;高效且安全的字符串操作能力直接决定了代码的质量与可维护性。而C标准模板库&#xff08;Standard Template Library, ST…

Python爬虫实战:抓取百度15天天气预报数据

&#x1f310; 编程基础第一期《9-30》–使用python中的第三方模块requests&#xff0c;和三个内置模块(re、json、pprint)&#xff0c;实现百度地图的近15天天气信息抓取 记得安装 pip install requests&#x1f4d1; 项目介绍 网络爬虫是Python最受欢迎的应用场景之一&…

HTML常见事件详解:从入门到实战应用

前言 在Web开发中&#xff0c;事件是用户与网页交互的核心机制。HTML事件让我们能够响应用户的各种操作&#xff0c;如点击、鼠标移动、键盘输入等。掌握HTML事件是前端开发的基础技能之一&#xff0c;本文将深入探讨HTML中的常见事件类型及其实际应用。 HTML事件概览总结 H…

模具制造业数字化转型:精密模塑,以数字之力铸就制造基石

模具被誉为 “工业之母”&#xff0c;是制造业的重要基石&#xff0c;其精度直接决定了工业产品的质量与性能。在工业制造向高精度、智能化发展的当下&#xff0c;《模具制造业数字化转型&#xff1a;精密模塑&#xff0c;以数字之力铸就制造基石》这一主题&#xff0c;精准点明…

深度解读漏洞扫描:原理、类型与应用实践

在网络安全领域&#xff0c;漏洞就像隐藏在系统中的定时炸弹&#xff0c;随时可能被攻击者利用&#xff0c;导致数据泄露、服务瘫痪等严重后果。而漏洞扫描作为发现这些潜在威胁的 “侦察兵”&#xff0c;是保障网络安全的重要防线。本文将全面介绍漏洞扫描的相关知识&#xff…

[HNCTF 2022 Week1]silly_zip

下载附件 解压发现需要密码 用010打开看看&#xff0c;发现是伪加密 改成00点击保存 解压后得到图片 感觉图片看着怪怪的&#xff0c;修改一下高度看看有没有其他线索 把47改成78 最后得到flag

Facebook 的隐私保护措施是否足够?技术观点

在数字时代&#xff0c;隐私保护成为了公众关注的焦点&#xff0c;尤其是对于拥有数十亿用户的社交媒体巨头 Facebook 来说&#xff0c;其隐私保护措施的有效性更是备受瞩目。本文将从技术角度探讨 Facebook 的隐私保护措施是否足够。 数据收集与使用 Facebook 收集用户数据的…

cocosCreator 1.8 升级到 2.4

现在负责的一个运营中的商业项目&#xff0c;使用的是 cocosCreator1.8&#xff0c;之前没有做好设计&#xff0c;所以东西都是直接加载在内存中的&#xff0c;到了现在性能问题逐渐暴露出来&#xff0c;讨论之后想进行引擎升级&#xff0c;升级到cocosCreator 2.4。 官方的升…

ubuntu 制作 ssl 证书

安装 openssl sudo apt install openssl 生成 SSL 证书 # 生成私钥 (Private Key) openssl genrsa -out private.key 2048 在当前目录生成 private.key # 生成证书签名请求 (CSR - Certificate Signing Request) openssl req -new -key private.key -out certificate.csr -…

【Java基础-环境搭建-创建项目】IntelliJ IDEA创建Java项目的详细步骤

在Java开发的世界里&#xff0c;选择一个强大的集成开发环境&#xff08;IDE&#xff09;是迈向高效编程的第一步。而IntelliJ IDEA无疑是Java开发者中最受欢迎的选择之一。它以其强大的功能、智能的代码辅助和简洁的用户界面&#xff0c;帮助无数开发者快速构建和部署Java项目…

WEB3——什么是ABI

怎么获得ABI&#xff1f; 在编译完合约后&#xff0c;可以在左边下面点击复制ABI ABI&#xff08;Application Binary Interface&#xff0c;应用二进制接口&#xff09;是用来让前端或服务端 JavaScript 代码与智能合约进行交互的桥梁&#xff0c;它描述了合约的函数、事件和…