Qt纯代码绘制一个等待提示Ui控件

news2024/12/26 23:10:37

         等待样式控件是我们在做UI时出场率还挺高的控件之一,通常情况下有如下的几种实现方式:1、自定义绘图,然后重写paintEvent函数,在paintEvent中绘制等待图标,通过QTimer更新绘制达到转圈圈的效果。2、 获取一张gif的资源图,然后使用QMovie 在一个QLabel 控件上加载显示gif的waiting等待动态。

        本示例采用自定义绘图,然后使用Qt动画,达到转圈圈的效果,给大家一个好看的样式示例。你可以根据需要进行修改和扩展,实现你想要的程序启动等待提示栏效果。

想看利用QMovie实现的请移步:https://blog.csdn.net/u012959478/article/details/140441021

一、简述

         Qt纯代码绘制一个等待提示Ui控件。自定义绘图,然后重写paintEvent函数,在paintEvent中绘制等待图标,通过QTimer更新绘制达到转圈圈的效果。

二、 设计思路   
  1. 创建一个QWidget派生的自定义控件,用于显示等待提示。
  2. 在自定义控件中,绘制一个圆形的等待图标。
  3. 通过重写paintEvent函数,在paintEvent中绘制等待图标。
  4. 利用定时器QTimer并实现一个槽函数来更新图标。
  5. 在槽函数中调用update函数,触发重绘事件,更新控件的显示。
三、效果 

四、核心代码  
1、头文件
#pragma once

#include <QWidget>
#include <QTimer>
#include <QColor>

class WaitingSpinnerWidget : public QWidget {
    Q_OBJECT
public:
    WaitingSpinnerWidget(QWidget *parent = 0,
                         bool centerOnParent = true,
                         bool disableParentWhenSpinning = true);

    WaitingSpinnerWidget(Qt::WindowModality modality,
                         QWidget *parent = 0,
                         bool centerOnParent = true,
                         bool disableParentWhenSpinning = true);

public slots:
    void start();
    void stop();

public:
    void setColor(QColor color);
    void setRoundness(qreal roundness);//圆角
    void setMinimumTrailOpacity(qreal minimumTrailOpacity);//最小轨迹不透明度
    void setTrailFadePercentage(qreal trail);//轨迹褪色百分比
    void setRevolutionsPerSecond(qreal revolutionsPerSecond);//每秒钟转数
    void setNumberOfLines(int lines);//线条数量
    void setLineLength(int length);//线条长度
    void setLineWidth(int width);//线条宽度
    void setInnerRadius(int radius);//内圆半径

    QColor color();
    qreal roundness();
    qreal minimumTrailOpacity();
    qreal trailFadePercentage();
    qreal revolutionsPersSecond();
    int numberOfLines();
    int lineLength();
    int lineWidth();
    int innerRadius();

    bool isSpinning() const;

private slots:
    void rotate();

protected:
    void paintEvent(QPaintEvent *paintEvent);

private:
    static int lineCountDistanceFromPrimary(int current, int primary,
                                            int totalNrOfLines);
    static QColor currentLineColor(int distance, int totalNrOfLines,
                                   qreal trailFadePerc, qreal minOpacity,
                                   QColor color);

    void initialize();
    void updateSize();
    void updateTimer();
    void updatePosition();

private:
    QColor  _color;
    qreal   _roundness; // 0..100
    qreal   _minimumTrailOpacity;
    qreal   _trailFadePercentage;
    qreal   _revolutionsPerSecond;
    int     _numberOfLines;
    int     _lineLength;
    int     _lineWidth;
    int     _innerRadius;

private:
    WaitingSpinnerWidget(const WaitingSpinnerWidget&);
    WaitingSpinnerWidget& operator=(const WaitingSpinnerWidget&);

    QTimer *_timer;
    bool    _centerOnParent;
    bool    _disableParentWhenSpinning;
    int     _currentCounter;
    bool    _isSpinning;
};
2、实现代码
#include "waitingspinnerwidget.h"

#include <cmath>
#include <algorithm>
#include <QPainter>
#include <QTimer>

WaitingSpinnerWidget::WaitingSpinnerWidget(QWidget *parent,
                                           bool centerOnParent,
                                           bool disableParentWhenSpinning)
    : QWidget(parent),
      _centerOnParent(centerOnParent),
      _disableParentWhenSpinning(disableParentWhenSpinning) {
    initialize();
}

WaitingSpinnerWidget::WaitingSpinnerWidget(Qt::WindowModality modality,
                                           QWidget *parent,
                                           bool centerOnParent,
                                           bool disableParentWhenSpinning)
    : QWidget(parent, Qt::Dialog | Qt::FramelessWindowHint),
      _centerOnParent(centerOnParent),
      _disableParentWhenSpinning(disableParentWhenSpinning){
    initialize();

    // We need to set the window modality AFTER we've hidden the
    // widget for the first time since changing this property while
    // the widget is visible has no effect.
    setWindowModality(modality);
    setAttribute(Qt::WA_TranslucentBackground);
}

void WaitingSpinnerWidget::initialize() {
    _color = Qt::black;
    _roundness = 100.0;
    _minimumTrailOpacity = 3.14159265358979323846;
    _trailFadePercentage = 80.0;
    _revolutionsPerSecond = 1.57079632679489661923;
    _numberOfLines = 20;
    _lineLength = 10;
    _lineWidth = 2;
    _innerRadius = 10;
    _currentCounter = 0;
    _isSpinning = false;

    _timer = new QTimer(this);
    connect(_timer, SIGNAL(timeout()), this, SLOT(rotate()));
    updateSize();
    updateTimer();
    hide();
}

void WaitingSpinnerWidget::paintEvent(QPaintEvent *) {
    updatePosition();
    QPainter painter(this);
    painter.fillRect(this->rect(), Qt::transparent);
    painter.setRenderHint(QPainter::Antialiasing, true);

    if (_currentCounter >= _numberOfLines) {
        _currentCounter = 0;
    }

    painter.setPen(Qt::NoPen);
    for (int i = 0; i < _numberOfLines; ++i) {
        painter.save();
        painter.translate(_innerRadius + _lineLength,
                          _innerRadius + _lineLength);
        qreal rotateAngle =
                static_cast<qreal>(360 * i) / static_cast<qreal>(_numberOfLines);
        painter.rotate(rotateAngle);
        painter.translate(_innerRadius, 0);
        int distance =
                lineCountDistanceFromPrimary(i, _currentCounter, _numberOfLines);
        QColor color =
                currentLineColor(distance, _numberOfLines, _trailFadePercentage,
                                 _minimumTrailOpacity, _color);
        painter.setBrush(color);
        // TODO improve the way rounded rect is painted
        painter.drawRoundedRect(
                    QRect(0, -_lineWidth / 2, _lineLength, _lineWidth), _roundness,
                    _roundness, Qt::RelativeSize);
        painter.restore();
    }
}

void WaitingSpinnerWidget::start() {
    updatePosition();
    _isSpinning = true;
    show();

    if(parentWidget() && _disableParentWhenSpinning) {
        parentWidget()->setEnabled(false);
    }

    if (!_timer->isActive()) {
        _timer->start();
        _currentCounter = 0;
    }
}

void WaitingSpinnerWidget::stop() {
    _isSpinning = false;
    hide();

    if(parentWidget() && _disableParentWhenSpinning) {
        parentWidget()->setEnabled(true);
    }

    if (_timer->isActive()) {
        _timer->stop();
        _currentCounter = 0;
    }
}

void WaitingSpinnerWidget::setNumberOfLines(int lines) {
    _numberOfLines = lines;
    _currentCounter = 0;
    updateTimer();
}

void WaitingSpinnerWidget::setLineLength(int length) {
    _lineLength = length;
    updateSize();
}

void WaitingSpinnerWidget::setLineWidth(int width) {
    _lineWidth = width;
    updateSize();
}

void WaitingSpinnerWidget::setInnerRadius(int radius) {
    _innerRadius = radius;
    updateSize();
}

QColor WaitingSpinnerWidget::color() {
    return _color;
}

qreal WaitingSpinnerWidget::roundness() {
    return _roundness;
}

qreal WaitingSpinnerWidget::minimumTrailOpacity() {
    return _minimumTrailOpacity;
}

qreal WaitingSpinnerWidget::trailFadePercentage() {
    return _trailFadePercentage;
}

qreal WaitingSpinnerWidget::revolutionsPersSecond() {
    return _revolutionsPerSecond;
}

int WaitingSpinnerWidget::numberOfLines() {
    return _numberOfLines;
}

int WaitingSpinnerWidget::lineLength() {
    return _lineLength;
}

int WaitingSpinnerWidget::lineWidth() {
    return _lineWidth;
}

int WaitingSpinnerWidget::innerRadius() {
    return _innerRadius;
}

bool WaitingSpinnerWidget::isSpinning() const {
    return _isSpinning;
}

void WaitingSpinnerWidget::setRoundness(qreal roundness) {
    _roundness = std::max(0.0, std::min(100.0, roundness));
}

void WaitingSpinnerWidget::setColor(QColor color) {
    _color = color;
}

void WaitingSpinnerWidget::setRevolutionsPerSecond(qreal revolutionsPerSecond) {
    _revolutionsPerSecond = revolutionsPerSecond;
    updateTimer();
}

void WaitingSpinnerWidget::setTrailFadePercentage(qreal trail) {
    _trailFadePercentage = trail;
}

void WaitingSpinnerWidget::setMinimumTrailOpacity(qreal minimumTrailOpacity) {
    _minimumTrailOpacity = minimumTrailOpacity;
}

void WaitingSpinnerWidget::rotate() {
    ++_currentCounter;
    if (_currentCounter >= _numberOfLines) {
        _currentCounter = 0;
    }
    update();
}

void WaitingSpinnerWidget::updateSize() {
    int size = (_innerRadius + _lineLength) * 2;
    setFixedSize(size, size);
}

void WaitingSpinnerWidget::updateTimer() {
    _timer->setInterval(1000 / (_numberOfLines * _revolutionsPerSecond));
}

void WaitingSpinnerWidget::updatePosition() {
    if (parentWidget() && _centerOnParent) {
        move(parentWidget()->width() / 2 - width() / 2,
             parentWidget()->height() / 2 - height() / 2);
    }
}

int WaitingSpinnerWidget::lineCountDistanceFromPrimary(int current, int primary,
                                                       int totalNrOfLines) {
    int distance = primary - current;
    if (distance < 0) {
        distance += totalNrOfLines;
    }
    return distance;
}

QColor WaitingSpinnerWidget::currentLineColor(int countDistance, int totalNrOfLines,
                                              qreal trailFadePerc, qreal minOpacity,
                                              QColor color) {
    if (countDistance == 0) {
        return color;
    }
    const qreal minAlphaF = minOpacity / 100.0;
    int distanceThreshold =
            static_cast<int>(ceil((totalNrOfLines - 1) * trailFadePerc / 100.0));
    if (countDistance > distanceThreshold) {
        color.setAlphaF(minAlphaF);
    } else {
        qreal alphaDiff = color.alphaF() - minAlphaF;
        qreal gradient = alphaDiff / static_cast<qreal>(distanceThreshold + 1);
        qreal resultAlpha = color.alphaF() - gradient * countDistance;

        // If alpha is out of bounds, clip it.
        resultAlpha = std::min(1.0, std::max(0.0, resultAlpha));
        color.setAlphaF(resultAlpha);
    }
    return color;
}

        以上是等待提示UI控件的实现代码,大家可以根据不同的应用场景和用户需求进行扩展。    

        需要注意的是,等待提示UI控件应该在适当的时机显示,并且要避免过长时间的等待,以免影响用户体验。同时,等待提示UI控件的样式应该简洁明了,清晰展示当前操作的状态,以便用户能够理解和接受。

五、使用示例

以下是一个简单的示例代码,演示了如何在Qt中使用此控件:

#include <QCoreApplication>
#include <QApplication>
#include <waitingspinnerwidget.h>
#include <QFrame>
#include <QHBoxLayout>

int main(int argc,char* argv[])
{
    QApplication a(argc,argv);


    WaitingSpinnerWidget* spinner = new WaitingSpinnerWidget;
    /// 设置waiting组件的样式
    spinner->setRoundness(50.0);
    spinner->setMinimumTrailOpacity(15.0);
    spinner->setTrailFadePercentage(70.0);
    spinner->setNumberOfLines(16);
    spinner->setLineLength(15);
    spinner->setLineWidth(5);
    spinner->setInnerRadius(30);
    spinner->setRevolutionsPerSecond(1);
    spinner->setColor(QColor(81, 4, 71));

    spinner->start(); // gets the show on the road!


    QFrame* f = new QFrame;
    QHBoxLayout* hlayout = new QHBoxLayout;
    hlayout->addWidget(spinner);
    f->setLayout(hlayout);
    f->show();

    return a.exec();
}

        总结一下,笔者分享纯代码实现等待提示UI控件的一种设计方法和流程,在此操作的基础上我们可以发挥想象开发出更多更有意思的控件,源码我放在此处以下地址。如有错误也请各位看官手下留情,欢迎评论区批评指正。

        谢谢你的关注和阅读,希望我的回答能帮到你。如果还有其他问题,欢迎随时向我提问。祝你一切顺利!

六、源代码下载

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

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

相关文章

GD32 MCU上电跌落导致启动异常如何解决

大家是否碰到过MCU上电过程中存在电源波动或者电压跌落导致MCU启动异常的问题&#xff1f;本视频将会为大家讲解可能的原因以及解决方法&#xff1a; GD32 MCU上下电复位波形如下图所示&#xff0c;上电过程中如果存在吃电的模块&#xff0c;比如wifi模块/4G模块/开启某块电路…

【Python实战因果推断】37_双重差分8

目录 Diff-in-Diff with Covariates Diff-in-Diff with Covariates 您需要学习的 DID 的另一个变量是如何在模型中包含干预前协变量。这在您怀疑平行趋势不成立&#xff0c;但条件平行趋势成立的情况下非常有用&#xff1a; 考虑这种情况&#xff1a;您拥有与之前相同的营销数…

Java面试题--JVM大厂篇之Serial GC在JVM中有哪些优点和局限性

目录 引言: 正文&#xff1a; 一、Serial GC概述 二、Serial GC的优点 三、Serial GC的局限性 结束语: 引言: 在Java虚拟机&#xff08;JVM&#xff09;中&#xff0c;垃圾收集器&#xff08;Garbage Collector, GC&#xff09;是关键组件之一&#xff0c;负责自动管理内…

深度学习落地实战:手势识别

前言 大家好&#xff0c;我是机长 本专栏将持续收集整理市场上深度学习的相关项目&#xff0c;旨在为准备从事深度学习工作或相关科研活动的伙伴&#xff0c;储备、提升更多的实际开发经验&#xff0c;每个项目实例都可作为实际开发项目写入简历&#xff0c;且都附带完整的代…

部署运维之二:虚拟化

摘要&#xff1a; 在21世纪初的曙光中&#xff0c;虚拟化技术悄然萌芽&#xff0c;标志着计算领域的一次革命性飞跃。这一时期&#xff0c;通过引入虚拟化技术&#xff0c;业界实现了在单一物理服务器之上并行运行多个虚拟机的壮举&#xff0c;每个虚拟机均构筑起一个隔离而独…

【计算机网络】学习指南及导论

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️计算机网络】 文章目录 前言我们为什么要学计算机网络&#xff1f;计算机网络概述计算机网络的分类按交换技术分类按使用者分类按传输介质分类按覆盖网络分类按覆盖网络分类 局域网的连接方式有线连接…

从零实现大模型-BERT微调

The Annotated Transformer注释加量版&#xff1a;复现Transformer&#xff0c;训练翻译模型 The Annotated GPT2注释加量版&#xff1a;GPT2预训练 The Annotated BERT注释加量版&#xff1a;BERT预训练 从零实现大模型-GPT2指令微调&#xff1a;GPT2指令微调 按照顺序&am…

海外媒体发稿:葡萄牙-实现高效媒体软文发稿计划-大舍传媒

一、葡萄牙媒体环境概述 葡萄牙&#xff0c;位于欧洲大陆西南端的国家&#xff0c;拥有丰富的文化和历史。在这个国家&#xff0c;媒体行业也有着相当大的影响力。葡萄牙的媒体环境多元化&#xff0c;包括电视、广播、报纸、杂志和互联网等各个领域。 二、葡萄牙媒体发稿的重…

Win10+Docker配置TensorRT环境

1.Docker下载和安装 Docker下载:Install Docker Desktop on Windows Docker安装: 勾选直接下一步就行,安装完成后需要电脑重启。 重启后,选择Accept—>Continue without signing in—>skip survey. 可以进入下面页面,并且左下角是绿色的,显示e…

前端开发之盒子模型

目录 盒子分类 display属性 盒子内部结构特征 padding填充区 border边框区 margin外边距 盒子width和height边界 盒子分类 块级盒子&#xff08;又叫块级元素、块级标签&#xff09; 特征&#xff1a;独占一行&#xff0c;对宽度高度支持 如&#xff1a;p div ul li h1…

Vue3项目基于Axios封装request请求

在 Vue 3 的项目开发中&#xff0c;使用 Axios 进行 HTTP 请求是非常常见的作法&#xff0c;为了更方便开发者更高效的进行代码编写和项目的维护&#xff0c;可以通过再次封装 Axios 来实现。 在本文中&#xff0c;博主将详细指导你如何在自己的 Vue 3 项目中使用 Axios 二次封…

【Java开发实训】day04——可变参数和递归练习

目录 一、可变参数 1.1定义 1.2注意 1.3示例 二、递归 2.1定义 2.2注意 2.3示例 2.4练习 &#x1f308;嗨&#xff01;我是Filotimo__&#x1f308;。很高兴与大家相识&#xff0c;希望我的博客能对你有所帮助。 &#x1f4a1;本文由Filotimo__✍️原创&#xff0c;首发于CSDN&…

CSS3实现提示工具的渐入渐出效果及CSS3动画简介

上一篇文章用CSS3实现了一个提示工具&#xff0c;本文介绍如何利用CSS3实现提示工具以渐入的方式呈现&#xff0c;以渐出的方式消失。 CSS3主要可以通过两个样式来实现动画效果&#xff1a;animation和transition。 其中&#xff0c;animation需要自己定义一组关键帧从而实现…

css实现前端水印

单处水印 代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Watermark Example</title>&l…

昇思25天学习打卡营第24天|应用实践之Pix2Pix实现图像转换

基本介绍 今日要实践的模型是Pix2Pix模型&#xff0c;用于图像转换。使用官方的指定数据集&#xff0c;该数据集是已经经过处理的外墙&#xff08;facades&#xff09;数据&#xff0c;可以直接使用mindspore.dataset的方法读取。由于Pix2Pix模型是基于cGAN&#xff08;条件生成…

SQL常用数据过滤---IN操作符

在SQL中&#xff0c;IN操作符常用于过滤数据&#xff0c;允许在WHERE子句中指定多个可能的值。如果列中的值匹配IN操作符后面括号中的任何一个值&#xff0c;那么该行就会被选中。 以下是使用IN操作符的基本语法&#xff1a; SELECT column1, column2, ... FROM table_name WH…

MQ四兄弟:如何实现延时消息

RabbitMQ延时消息 RabbitMQ 本身并没有直接支持延时消息的功能&#xff0c;但是可以通过使用 RabbitMQ 插件或构建消息死信队列&#xff08;Dead Letter Exchange, DLX&#xff09;的方式来实现延时消息。以下是两种实现延时消息的方法&#xff1a; 1、死信队列 (Dead-Letter …

kubernetes集群部署elasticsearch集群,包含无认证和有认证模式

1、背景&#xff1a; 因公司业务需要&#xff0c;需要在测试、生产kubernetes集群中部署elasticsearch集群&#xff0c;因不同环境要求&#xff0c;需要部署不同模式的elasticsearch集群&#xff0c; 1、测试环境因安全性要求不高&#xff0c;是部署一套默认配置&#xff1b; 2…

【LeetCode】十七、并查集

文章目录 1、并查集Union Find2、并查集find的优化&#xff1a;路径压缩 Quick find3、并查集union的优化&#xff1a;权重标记 1、并查集Union Find 并查集&#xff0c;一种树形的数据结构&#xff0c;处理不相交的两个集合的合并与查询问题。 【参考&#xff1a;&#x1f4…

Linux·基本指令(下)

1. mv 指令 (move) 语法&#xff1a;mv[选项] 源文件或目录 目标文件或目录 功能&#xff1a;将源文件或目录剪贴到一个新位置&#xff0c;或给源文件或目录改名但不会改变其内容 常用选项&#xff1a; -f &#xff1a;force 强制&#xff0c;如果目标文件已经存在&#xff0c;…