QT圆形加载进度条
如果出现VIP浏览或者其它的限制,请联系我,我解除限制。
运行环境和编译工具如下:


效果图

LoadingWidgets.h
cpp
#ifndef LOADINGWIDGETS_H
#define LOADINGWIDGETS_H
#include <QWidget>
#include <QPainter>
#include <QTimer>
#include <QLabel>
#include <QVBoxLayout>
// --- 组件 1: 旋转的渐变圆环 ---
class ProgressRing : public QWidget {
Q_OBJECT
public:
explicit ProgressRing(QWidget *parent = nullptr);
protected:
void paintEvent(QPaintEvent *event) override;
private:
qreal m_angle; // 旋转角度
QTimer *m_timer;
};
// --- 组件 2: Loading 弹窗 ---
class LoadingBox : public QWidget {
Q_OBJECT
public:
explicit LoadingBox(QWidget *parent = nullptr);
// 专门的显示函数,自动计算居中位置
void showCentered();
protected:
void paintEvent(QPaintEvent *event) override;
private:
ProgressRing *m_ring;
QLabel *m_lblCenterText;
QLabel *m_lblSubTitle;
};
#endif // LOADINGWIDGETS_H
LoadingWidgets.cpp
cpp
#include "LoadingWidgets.h"
#include <QApplication>
#include <QScreen>
#include <QDebug>
// ================= ProgressRing 实现 =================
ProgressRing::ProgressRing(QWidget *parent) : QWidget(parent), m_angle(0) {
// 强制固定大小,防止被布局压缩看不见
setFixedSize(100, 100);
m_timer = new QTimer(this);
connect(m_timer, &QTimer::timeout, this, [=]() {
m_angle += 10; // 加快一点速度
if (m_angle >= 360) m_angle = 0;
update();
});
m_timer->start(30);
}
void ProgressRing::paintEvent(QPaintEvent *event) {
Q_UNUSED(event);
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
int width = this->width();
int height = this->height();
int side = qMin(width, height);
int radius = side / 2 - 10;
// 坐标系移到中心
p.translate(width / 2, height / 2);
p.rotate(m_angle); // 旋转坐标系
// 定义锥形渐变 (QConicalGradient)
// 中心点(0,0),起始角度 -90度(让颜色从顶部开始)
QConicalGradient gradient(0, 0, -90);
gradient.setColorAt(0, QColor(46, 204, 113)); // 头部:亮绿色
gradient.setColorAt(0.2, QColor(46, 204, 113));
gradient.setColorAt(0.5, QColor(64, 158, 255)); // 中段:蓝色
gradient.setColorAt(1, QColor(64, 158, 255, 0)); // 尾部:完全透明
QPen pen(gradient, 8); // 线宽8
pen.setCapStyle(Qt::RoundCap);
p.setPen(pen);
p.setBrush(Qt::NoBrush);
// 绘制整个圆,因为渐变本身包含了透明度,所以画满圆就有拖尾效果
p.drawEllipse(QPoint(0, 0), radius, radius);
}
// ================= LoadingBox 实现 =================
LoadingBox::LoadingBox(QWidget *parent) : QWidget(parent) {
setFixedSize(260, 220);
// FramelessWindowHint 去掉标题栏
// Dialog 保证它是独立的窗口层级(不会被父窗口裁剪)
setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint);
setAttribute(Qt::WA_TranslucentBackground);
setAttribute(Qt::WA_DeleteOnClose, false); // 关闭时不自动销毁,方便重复使用
QVBoxLayout *layout = new QVBoxLayout(this);
layout->setContentsMargins(0, 30, 0, 20);
layout->setSpacing(15);
// 1. 圆环区域
// 使用一个中间容器来居中圆环
QWidget *ringContainer = new QWidget(this);
ringContainer->setFixedSize(100, 100);
// 圆环
m_ring = new ProgressRing(ringContainer);
m_ring->move(0, 0); // 在容器内左上角对齐(也就是(0,0)到(100,100))
// 中间文字 "登录中"
m_lblCenterText = new QLabel("登录中", ringContainer);
m_lblCenterText->setGeometry(0, 0, 100, 100);
m_lblCenterText->setAlignment(Qt::AlignCenter);
m_lblCenterText->setStyleSheet("color: #409EFF; font-weight: bold; font-size: 14px;");
// 添加到主布局并居中
layout->addWidget(ringContainer, 0, Qt::AlignCenter);
// 2. 底部提示文字
m_lblSubTitle = new QLabel("正在验证用户信息...", this);
m_lblSubTitle->setAlignment(Qt::AlignCenter);
m_lblSubTitle->setStyleSheet("color: #666; font-size: 13px;");
layout->addWidget(m_lblSubTitle);
}
void LoadingBox::showCentered() {
QWidget *p = this->parentWidget();
if (p) {
// 核心修正:获取父窗口在【屏幕】上的中心点
QPoint parentCenter = p->mapToGlobal(p->rect().center());
// 计算 Loading 窗口左上角位置
int x = parentCenter.x() - this->width() / 2;
int y = parentCenter.y() - this->height() / 2;
this->move(x, y);
} else {
// 没有父窗口则居中于屏幕
QRect screenGeometry = QApplication::primaryScreen()->geometry();
int x = (screenGeometry.width() - this->width()) / 2;
int y = (screenGeometry.height() - this->height()) / 2;
this->move(x, y);
}
this->show();
}
void LoadingBox::paintEvent(QPaintEvent *event) {
Q_UNUSED(event);
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
// 绘制圆角背景
p.setBrush(QColor(255, 255, 255, 240)); // 白色,微透明
p.setPen(Qt::NoPen);
p.drawRoundedRect(this->rect(), 12, 12);
// 绘制边框阴影效果可以增加 QGraphicsDropShadowEffect,这里简单画个细边框
p.setBrush(Qt::NoBrush);
p.setPen(QPen(QColor(230, 230, 230), 1));
p.drawRoundedRect(this->rect().adjusted(0,0,-1,-1), 12, 12);
}
main.cpp
cpp
#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QVBoxLayout>
#include <QTimer>
#include "LoadingWidgets.h"
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
// 1. 主窗口
QWidget loginWindow;
loginWindow.setWindowTitle("系统登录");
loginWindow.resize(400, 300);
QVBoxLayout *layout = new QVBoxLayout(&loginWindow);
QPushButton *btnLogin = new QPushButton("点击登录", &loginWindow);
btnLogin->setFixedHeight(50);
layout->addStretch();
layout->addWidget(btnLogin);
layout->addStretch();
// 2. 创建 Loading 对象,指定 loginWindow 为父对象
LoadingBox *loading = new LoadingBox(&loginWindow);
// 3. 按钮点击事件
QObject::connect(btnLogin, &QPushButton::clicked, [&]() {
// A. 显示 Loading (会自动计算位置)
loading->showCentered();
// B. 【模拟业务逻辑】
// 警告:千万不要在这里写 Sleep(3000)!那会卡死动画。
// 实际开发中,这里应该是发送网络请求,或者启动一个线程。
// 这里我们用 QTimer::singleShot 模拟 "3秒后登录成功"
QTimer::singleShot(3000, [&]() {
// 3秒后执行的代码:
loading->hide(); // 隐藏 Loading
// 可以在这里跳转主界面...
});
});
loginWindow.show();
return a.exec();
}