如何在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
繁體地址,請注明出處:http://hk.pswp.cn/news/907555.shtml
英文地址,請注明出處:http://en.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;它描述了合約的函數、事件和…