Qt QWidget 简约美观的加载动画 第四季

news2025/1/11 7:07:25

😊 第四季来啦 😊
效果如下:
在这里插入图片描述
只有三个文件,可以直接编译运行的

//main.cpp
#include "LoadingAnimWidget.h"
#include <QApplication>
#include <QVBoxLayout>
#include <QGridLayout>
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QWidget w;
    w.setWindowTitle("加载动画 第四季");
    QGridLayout * mainLayout = new QGridLayout;
    auto* anim1= new JellyInBox;
    mainLayout->addWidget(anim1,0,0);

    auto* anim2 = new HorizontalDNA;
    mainLayout->addWidget(anim2,0,1);

    auto * anim3 = new  LoopedRingWithText;
    mainLayout->addWidget(anim3,1,0);

    auto* anim4 = new Radar;
    mainLayout->addWidget(anim4,1,1);

    w.setLayout(mainLayout);
    w.show();
    anim1->start();
    anim2->start();
    anim3->start();
    anim4->start();
    return a.exec();
}

//LoadingAnimWidget.h
#ifndef LOADINGANIMWIDGET_H
#define LOADINGANIMWIDGET_H
#include <QPropertyAnimation>
#include <QWidget>
class LoadingAnimBase:public QWidget
{
    Q_OBJECT
    Q_PROPERTY(qreal angle READ angle WRITE setAngle)
public:
    LoadingAnimBase(QWidget* parent=nullptr);
    virtual ~LoadingAnimBase();

    qreal angle()const;
    void setAngle(qreal an);
public slots:
    virtual void exec();
    virtual void start();
    virtual void stop();
protected:
    QPropertyAnimation mAnim;
    qreal mAngle;
};

class JellyInBox:public LoadingAnimBase{//一个会伸缩的果冻在一个小盒子里面,一开始拉伸,然后移动,然后收缩
public:
    explicit JellyInBox(QWidget* parent = nullptr);
protected:
    void paintEvent(QPaintEvent *event);
};
class HorizontalDNA:public LoadingAnimBase{//两根水平方向的小球链,螺旋滚动,像DNA双分子链
public:
    explicit HorizontalDNA(QWidget* parent = nullptr);
protected:
    void paintEvent(QPaintEvent*);
private:
    void getPosYAlphaScale(const int amplitude,const qreal & offset,qreal & y,qreal & alpha,qreal & scale);
    //根据原始偏移获取当前时间点下的y轴坐标,透明度,和缩放比例
};
class LoopedRingWithText:public LoadingAnimBase{//外面有三圈圆环转动,中间有一个高亮的灯光从左向右移动照亮字符串的一部分
public:
    explicit LoopedRingWithText(QWidget* parent = nullptr);
protected:
    void paintEvent(QPaintEvent*);
};
class Radar:public LoadingAnimBase{//像一个雷达一样扫描,等待耗时操作的结束
public:
    explicit Radar(QWidget* parent = nullptr);
protected:
    void paintEvent(QPaintEvent*);
};

#endif // LOADINGANIMWIDGET_H

//LoadingAnimWidget.cpp
#include "LoadingAnimWidget.h"
#include <QDebug>
#include <QPaintEvent>
#include <QPainter>
#include <QtMath>
LoadingAnimBase::LoadingAnimBase(QWidget* parent):QWidget(parent){
    mAnim.setPropertyName("angle");
    mAnim.setTargetObject(this);
    mAnim.setDuration(2000);
    mAnim.setLoopCount(-1);//run forever
    mAnim.setEasingCurve(QEasingCurve::Linear);
    setFixedSize(200,200);
    mAngle = 0;
}
LoadingAnimBase::~LoadingAnimBase(){}
void LoadingAnimBase::exec(){
    if(mAnim.state() == QAbstractAnimation::Stopped){
        start();
    }
    else{
        stop();
    }
}
void LoadingAnimBase::start(){
    mAnim.setStartValue(0);
    mAnim.setEndValue(360);
    mAnim.start();
}
void LoadingAnimBase::stop(){
    mAnim.stop();
}
qreal LoadingAnimBase::angle()const{ return mAngle;}
void LoadingAnimBase::setAngle(qreal an){
    mAngle = an;
    update();
}
JellyInBox::JellyInBox(QWidget* parent):LoadingAnimBase (parent){}
void JellyInBox::paintEvent(QPaintEvent *event){
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setBrush(Qt::NoBrush);

    int x = this->width();
    const int y = this->height();

    //先画一个盒子
    QPen pen("black");
    pen.setWidth(3);
    painter.setPen(pen);
    painter.drawRoundedRect(QRectF(1,0.4375*y,x-2,y/8),y/16.0,y/16.0);

    //画果冻
    pen.setCapStyle(Qt::RoundCap);
    painter.setBrush(Qt::NoBrush);

    const int gap = 4;//果冻和盒子之间的间距
    const int boxGap = 1;//盒子的外间距
    const qreal jh = y/8 - gap*2;//果冻高度
    pen.setWidthF(jh);
    painter.setPen(pen);

    painter.translate(boxGap + gap + jh/2,y/2);
    x -= (boxGap + gap) * 2 + jh;//可以移动的x范围是总宽度 - 两边的两个间距 - 笔头厚度
    const qreal maxw = 0.4*x;//最大的果冻宽度
    int jx = 0;
    qreal jw = 0;

    if(mAngle < 90){
        jw = maxw * mAngle / 90;
    }
    else if(mAngle < 270){
        jw = maxw;
        jx = (x - maxw) * (mAngle - 90) / 180;
    }
    else{
        jx = x - maxw + (mAngle - 270) / 90 * maxw;
        jw = x - jx;
    }
    painter.drawLine(jx,0,jx+jw,0);
}
HorizontalDNA::HorizontalDNA(QWidget* parent):LoadingAnimBase (parent){
    mAnim.setDuration(5000);
}
void HorizontalDNA::getPosYAlphaScale(const int amplitude,const qreal & offset,qreal & y,qreal & alpha,qreal & scale){
    static const qreal a = 2*3.1415926 / 360;
    //qreal x = a*mAngle + offset;//随着时间的流逝,x变大,y踩着着右边的值,看上去就是右边的小球带动左侧的小球移动
    //如果要实现左边的小球带动右侧的小球,要这样
    auto x = -a * mAngle + offset;
    y = qSin(x);
    auto tmp = qCos(x); //随着时间的流逝,x变小,当小球向上移动的时候,透明度变小
    tmp += 1;
    tmp /= 2;
    alpha = 1- tmp;
    scale = alpha / 2 + 0.5;//不要太小了,最小是0.5
    y *= amplitude;//amplitude就是振幅 sin(x)的振幅是1
}
void HorizontalDNA::paintEvent(QPaintEvent*){
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setPen(Qt::NoPen);

    const int x = width();
    const qreal radius = x / 20.0;//小球的半径
    const qreal y = height();
    const qreal unitx = x / 8.0;//小球的间距
    painter.translate(unitx,y/2);

    static const qreal gap = M_PI / 6;
    static const qreal offsetList[7] = {gap*0,gap*1,gap*2,gap*3,gap*4,gap*5,gap*6};
    QColor ballColor("black");
    for(int i = 0;i < 7;++i){
        qreal bally ,alpha,scale;
        getPosYAlphaScale(y/6,offsetList[i],bally,alpha,scale);
        ballColor.setAlphaF(alpha);
        painter.setBrush(QBrush(ballColor));
        painter.drawEllipse(QPointF(unitx*i,bally),radius*scale,radius*scale);
    }
    static const qreal offsetList2[7] = {gap*6,gap*7,gap*8,gap*9,gap*10,gap*11,0};
    for(int i = 0;i < 7;++i){
        qreal bally ,alpha,scale;
        getPosYAlphaScale(y/6,offsetList2[i],bally,alpha,scale);
        ballColor.setAlphaF(alpha);
        painter.setBrush(QBrush(ballColor));
        painter.drawEllipse(QPointF(unitx*i,bally),radius*scale,radius*scale);
    }
}
LoopedRingWithText:: LoopedRingWithText(QWidget* parent):LoadingAnimBase(parent){
    mAnim.setDuration(6000);
}
void  LoopedRingWithText::paintEvent(QPaintEvent*){
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    const int x = width();
    const int y = height();
    //step0:定义一些颜色变量,也可以改成成员变量
    static const QColor bright("turquoise");//最亮的圆环颜色
    static const QColor medium = QColor(bright.red(),bright.green(),bright.blue(),200);
    static const QColor dim = QColor(bright.red(),bright.green(),bright.blue(),160);

    //step1:画一个深色的背景
    painter.setPen(Qt::NoPen);
    painter.setBrush(QBrush(QColor("dimgray")));
    painter.drawRoundedRect(this->rect(),x/12,x/12);
    //step2:画最外面的两个圆环
    painter.translate(x/2,y/2);
    QPen pen(dim);
    pen.setWidth(4);
    pen.setCapStyle(Qt::RoundCap);
    painter.setPen(pen);
    const auto rect1 = QRectF(-0.45*x,-0.45*y,0.9*x,0.9*y);

    static const qreal start = 30;
    static const qreal span = 120;
    auto ang = mAngle;
    painter.rotate(ang);
    painter.drawArc(rect1,start * 16,span * 16);
    painter.drawArc(rect1,(start+180) *16,span * 16);
    painter.resetTransform();

    //step3:画中间的两个圆环
    pen.setColor(medium);
    painter.setPen(pen);
    painter.translate(x/2,y/2);
    const auto rect2 = QRectF(-0.375*x,-0.375*y,0.75*x,0.75*y);
    if(ang > 180) ang -= 180;//周期改成180,比外面的转的快一倍
    painter.rotate(-2*ang);
    painter.drawArc(rect2,start * 16,span * 16);
    painter.drawArc(rect2,(start + 180)*16,span*16);
    painter.resetTransform();

    //step4:画内部的4个圆环
    pen.setColor(bright);
    painter.setPen(pen);
    painter.translate(x/2,y/2);
    const auto rect3 = QRectF(-0.3*x,-0.3*y,0.6*x,0.6*y);
    ang = mAngle;
    static const qreal t3 = 120;//周期改成120,比中间的更快
    while(ang > t3) ang -= t3;
    painter.rotate( ang * 90 / t3);//四个圆环转过90度的时候也就是长短交替一次,要把这个转过的角度乘以调整系数:90/120
    qreal start0 = 15 + (90-15) * (ang / t3);//开始的时候上方圆环起点在15度,变化到90度时弧长为0
    qreal span0 = 180 - 2*start0;
    if(span0 > 0){
        //第一组
        painter.drawArc(rect3,start0*16 , span0*16);
        painter.drawArc(rect3,(start0+180)*16,span0*16);
    }
    start0 = -75 * (ang / t3);//开始的时候,右侧圆环起点是0,弧长为0,它的最大弧长时刻,起点在-75度
    span0 = -start0 * 2;
    if(span0 > 0){
        //第二组
        painter.drawArc(rect3,start0*16,span0*16);
        painter.drawArc(rect3,(start0+180)*16,span0*16);
    }
    painter.resetTransform();

    QFont font("Microsoft YaHei",12,4);
    painter.setFont(font);
    QFontMetrics fm(font);
    static const QString text("Loading...");
    qreal textw = fm.horizontalAdvance(text);
    qreal texth = fm.height();
    QPainterPath pp;
    pp.addText(QPointF( (x - textw)/2,0.5*y + texth/4),font,text);
    painter.setClipPath(pp);
    painter.setPen(Qt::NoPen);
    painter.setBrush(QBrush(dim));
    //step5: 先画一个淡色的文本
    painter.drawRect(rect());
    //step6: 最后一步啦,画一个圆灯光
    painter.translate(0,0.5*y);
    const qreal unit = x / 360.0;
    QRadialGradient grad(QPointF(mAngle*unit,0),texth);
    grad.setColorAt(0,bright);
    grad.setColorAt(1,dim);
    painter.setBrush(grad);
    painter.drawEllipse(QPointF(mAngle * unit,0),texth,texth);
}
Radar::Radar(QWidget* parent):LoadingAnimBase(parent){}
void Radar::paintEvent(QPaintEvent*){
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    //step1: 画一个淡色背景,很多简约的界面都需要透明背景,这一步可以删掉
    painter.setPen(Qt::NoPen);
    painter.setBrush(QBrush("deepskyblue"));
    const int x = width();
    const int y = height();
    painter.drawRoundedRect(rect(),x/12,x/12);
    //step2: 画一个雷达
    painter.translate(x/2,y/2);
    QConicalGradient gra(QPointF(0,0),45);
    static const QColor color0("white");
    static const QColor color1(color0.red(),color0.green(),color0.blue(),100);
    gra.setColorAt(0,color0);
    gra.setColorAt(0.2,color1);
    gra.setColorAt(1,"transparent");
    painter.setBrush(gra);
    painter.rotate(mAngle);
    const auto rect = QRectF(-x/4,-y/4,x/2,y/2);
    painter.drawPie(rect,45*16,90*16);

}

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

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

相关文章

【其他】简易代码项目记录

1. KeypointDetection 1.1. CharPointDetection 识别字符中的俩个关键点。 1.2. Facial-keypoints-detection 用于检测人脸的68个关键点示例。 1.3. Hourglass-facekeypoints 使用基于论文Hourglass 的模型实现人体关键点检测。 1.4. Realtime-Action-Recognition containing:…

AI 绘画:人工智能绘画之美

人工智能&#xff08;AI&#xff09;是当今科技领域的热门话题&#xff0c;它不仅可以帮助我们解决各种复杂的问题&#xff0c;还可以创造出令人惊叹的艺术作品。AI 绘画是一种利用 AI 技术生成图像的方法&#xff0c;它可以模仿不同的风格、主题和技巧&#xff0c;甚至可以创造…

2023年5个最好的向量数据库

向量数据库全景图 图5:向量数据库全景图 在人工智能&#xff08;AI&#xff09;领域&#xff0c;庞大的数据量需要高效处理和加工。随着我们深入研究更先进的AI应用&#xff0c;如图像识别、语音搜索或推荐引擎&#xff0c;数据的性质变得更加复杂。这就是向量数据库发挥作用的…

编码后的字符串lua

-- 长字符串 local long_string "你好你好你好你好你好你好你好你好" local encoded_string "" for i 1, #long_string do local char_code string.byte (long_string, i) encoded_string encoded_string .. char_code .. "," end encoded_…

光学3D表面轮廓仪微纳米三维形貌一键测量

光学3D表面轮廓仪(白光干涉仪)利用白光干涉原理&#xff0c;以0.1nm分辨率精准捕捉物体的表面细节&#xff0c;实现三维显微成像测量&#xff0c;被广泛应用于材料学领域的研究和应用。 了解工作原理与技术 材料学领域中的光学3D表面轮廓仪&#xff0c;也被称为白光干涉仪&am…

一周学会Django5 Python Web开发-Django5路由重定向

锋哥原创的Python Web开发 Django5视频教程&#xff1a; 2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~共计25条视频&#xff0c;包括&#xff1a;2024版 Django5 Python we…

基于深度学习的人脸表情识别系统(含UI界面、yolov8、Python代码、数据集)

项目介绍 项目中所用到的算法模型和数据集等信息如下&#xff1a; 算法模型&#xff1a;     yolov8 yolov8主要包含以下几种创新&#xff1a;         1. 可以任意更换主干结构&#xff0c;支持几百种网络主干。 数据集&#xff1a;     fer2013数据集&#xff0…

免费享受企业级安全:雷池社区版WAF,高效专业的Web安全的方案

网站安全成为了每个企业及个人不可忽视的重要议题。 随着网络攻击手段日益狡猾和复杂&#xff0c;选择一个强大的安全防护平台变得尤为关键。 推荐的雷池社区版——一个为网站提供全面安全防护解决方案的平台&#xff0c;它不仅具备高效的安全防护能力&#xff0c;还让网站安…

【Python从入门到进阶】49、当当网Scrapy项目实战(二)

接上篇《48、当当网Scrapy项目实战&#xff08;一&#xff09;》 上一篇我们正式开启了一个Scrapy爬虫项目的实战&#xff0c;对当当网进行剖析和抓取。本篇我们继续编写该当当网的项目&#xff0c;讲解刚刚编写的Spider与item之间的关系&#xff0c;以及如何使用item&#xff…

Stable Diffusion 3内测申请~~~快冲鸭~~~~~

Stability AI 将其更改为 Stable Diffusion 3。VentureBeat 报道称&#xff0c;Stability AI 的下一代旗舰 AI 图像生成模型将使用类似于 OpenAI 的 Sora 的扩散变压器框架。其当前模型仅依赖于扩散架构。虽然尚未发布&#xff0c;但您可以在等候名单中注册。 以下内容机翻&am…

matlab悬臂梁有限元分析

1、内容简介 略 47-可以交流、咨询、答疑 2、内容说明 略 建模说明 设计一个长方体的悬臂梁&#xff0c;长宽高分别为100m、10m和15m&#xff0c;材料特性为杨氏模量2e5&#xff0c;泊松比0.3&#xff0c; Matlab有限元分析&#xff08;截图&#xff09; 上图为悬臂梁的扰度…

2023年Q4 Coremail管理员社区季刊发布

2023年Q4季刊新鲜出炉&#xff0c;本期内容涵盖了Coremail管理员社区Q4征稿活动、社区热门内容以及直播活动的总结。 本文为2023年Q4 Coremail管理员社区季刊精彩内容的节选&#xff0c;完整内容请上Coremail管理员社区进行查看。 1、Coremail管理员社区季刊介绍 2023 年4月&a…

CUDA编程 - 用向量化访存优化 - Cuda elementwise - Add(逐点相加)- 学习记录

Cuda elementwise - Add 一、简介1.1、ElementWise Add1.2、 float4 - 向量化访存 二、实践2.1、如何使用向量化访存2.1、简单的逐点相加核函数2.2、ElementWise Add float4&#xff08;向量化访存&#xff09;2.3、完整代码 一、简介 1.1、ElementWise Add Element-wise 操作…

table展示子集踩坑

##elemenui中table通过row中是否有children进行判断是否展示子集&#xff0c;通过设置tree-prop的属性进行设置&#xff0c;子级的children的名字可以根据自己的子级名字进行替换&#xff0c;当然同样可以对数据处理成含有chilren的子级list。 问题&#xff1a; 1.如果是根据后…

wpf 简单实验 数据更新 列表更新

1.概要 1.1 需求 一个列表提供添加修改删除的功能&#xff0c;添加和修改的内容都来自一个输入框 1.2 要点 DisplayMemberPath"Zhi"列表.ItemsSource datalist;(列表.SelectedItem ! null)(列表.SelectedItem as A).Zhi 内容.Text;datalist.Remove((列表.Selec…

YUV Format

title: 音视频同步 date: 2024-02-26 21:01:24 comments: true #是否可评论 toc: true #是否显示文章目录 categories: #分类 - 多媒体技术 tags: #标签 - 多媒体技术 summary: YUV Format YUV Format 简介 国际视频组织定义了多种yuv数据格式。文章中每一个像素都只有8bits…

大佬推荐的网络安全学习路线(从基础到高级)

说起网络安全&#xff0c;你可能会担心它是一个过时的行业。有人说&#xff0c;网络安全快卷死了&#xff0c;你既要攻又要防&#xff0c;并且随着技术的发展&#xff0c;你还要不断地学习&#xff0c;不在卷中生&#xff0c;就在卷中死。 实际上&#xff0c;随着数字化进程的…

Nest.js权限管理系统开发(八)jwt登录

安装相关依赖 虽然仅使用nestjs/jwt就能实现身份验证的功能&#xff0c;但是使用passport能在更高层次上提供更多便利。Passport 拥有丰富的 strategies 生态系统&#xff0c;实现了各种身份验证机制。虽然概念简单&#xff0c;但你可以选择的 Passport 策略集非常丰富且种类繁…

【性能测试】企业性能测试-并发用户数估算(详细)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 并发用户数&#…

05 Flink 的 WordCount

前言 本文对应于 spark 系列的 Spark 的 WordCount 这里主要是 从宏观上面来看一下 flink 这边的几个角色, 以及其调度的整个流程 一个宏观 大局上的任务的处理, 执行 基于 一个本地的 flink 集群 测试用例 /*** com.hx.test.Test01WordCount** author Jerry.X.He* ver…