关于实现QT进度条的显示值动态连续增加的方法

  • Post author:
  • Post category:其他





前言

因为项目中经常要用到进度条,以用于提示用户当前进度信息。但是之前百度并没有找到自己想要的动态显示效果的案例。后来索性自己尝试写了一个,效果还可以,现将普通进度条的显示效果,与本例中的动态显示进度条做一个直观的GIF对比。



普通进度条显示

在这里插入图片描述



自定义的动态进度条显示

在这里插入图片描述




一、主要实现方式概述

  1. 继承QProgressBar类,其中定义两个定时器。一个用来保证进度条一直在变化增加,一个用来控制进度条“顺滑”增加。
  2. 该自定义类主要由两个外部接口函数控制,void mStart(int times, int msec = 300)和void mUpdate()(void mUpdate()最好通过外部信号来触发,主要是考虑到多线程场景)



二、直接上代码

复制粘贴进工程,即可直接运行。UI界面通过代码创建。



main.cpp

#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}



mainwindow.h

#define MAINWINDOW_H

#include <QMainWindow>
#include <QPushButton>
#include <QBoxLayout>
#include "myprogressbar.h"

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

    //延时函数
    void Delay_MSec(unsigned int msec);

private:
    Ui::MainWindow *ui;

    QHBoxLayout *qHBoxLayout;   //水平窗口布局
    QVBoxLayout *qVBoxLayout;   //垂直窗口布局
    QPushButton *btn_run;       //按钮
    MyProgressBar *mPB;         //自定义进度条

    //初始化UI界面
    void initUI();

signals:
    //主窗口发送用于更新进度条的信号
    void signal_updateProgressBar();
};
#endif // MAINWINDOW_H



mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"


MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    //初始化UI界面
    initUI();
    //按钮点击事件
    connect(btn_run, &QPushButton::clicked, this,[=](){
        //mInit(3)表示主窗口会发送三次signal_updateProgressBar信号
        //注意,一定注意,使用MyProgressBar自定义进度条,mInit(n)中的数字n要等于信号signal_updateProgressBar发送的次数,切记。
        //并且mInit(n)函数一定要在signal_updateProgressBar信号发送之前调用。
        //为什么采用信号的方式而不采用直接调用MyProgressBar中mUpdate函数的方式,
        //主要是因为在多线程的情况下,signal_updateProgressBar可在其他线程中发出。信号槽是线程安全的
        mPB->mStart(3);
        //此处的Delay_MSec可成其他执行函数
        Delay_MSec(1000);
        emit signal_updateProgressBar();
        //此处的Delay_MSec可成其他执行函数
        Delay_MSec(2000);
        emit signal_updateProgressBar();
        //此处的Delay_MSec可成其他执行函数
        Delay_MSec(1000);
        emit signal_updateProgressBar();
    });
}

void MainWindow::Delay_MSec(unsigned int msec)
{
   QEventLoop loop;                                     //定义一个新的事件循环
   QTimer::singleShot(msec, &loop, &QEventLoop::quit);  //创建单次定时器,槽函数为事件循环的退出函数
   loop.exec();                                         //事件循环开始执行,程序会卡在这里,直到定时时间到,本循环被退出
}

void MainWindow::initUI()
{
    qHBoxLayout = new QHBoxLayout(this);
    btn_run = new QPushButton(QStringLiteral("运行"),this);
    qHBoxLayout->addStretch();
    qHBoxLayout->addWidget(btn_run);
    qHBoxLayout->addStretch();
    qVBoxLayout = new QVBoxLayout(this);
    mPB = new MyProgressBar(this);
    qVBoxLayout->addWidget(mPB);
    qVBoxLayout->addLayout(qHBoxLayout);
    //centralwidget为qt自动创建,此处直接调用
    ui->centralwidget->setLayout(qVBoxLayout);
    //此信号槽一定要写,用于接收主窗口发送的更新进度条的信号
    //如果是多线程中使用,信号发送对象就要写成子线程中的对象
    connect(this, &MainWindow::signal_updateProgressBar, mPB,&MyProgressBar::mUpdate);
}

MainWindow::~MainWindow()
{
    delete ui;
}



myprogressbar.h

#ifndef MYPROGRESSBAR_H
#define MYPROGRESSBAR_H

#include <QProgressBar>
#include <QTimer>

class MyProgressBar : public QProgressBar
{
    Q_OBJECT
public:
    explicit MyProgressBar(QWidget *parent = nullptr);
    //初始化进度条,times表示主窗口会更新几次进度条
    void mStart(int times, int msec = 300);
    //设置当前progressBar的值并格式化显示
    void mSetValue(int PBValue);

public slots:
    //接收外部信号,用于更新进度条显示
    void mUpdate();

private:
    //此vector专门用来控制进度条的显示进度,对应于每次进度条的中间节点
    QVector<int> pbStatusValue;
    //index_pbvalue用来控制pbStatusValue的值
    int index_pbvalue;
    //慢的定时器
    QTimer* mySlowTimer;
    //快的定时器
    QTimer* myFastTimer;
    //初始化pbStatusValue
    void initVector(int times);
};

#endif // MYPROGRESSBAR_H



myprogressbar.cpp

#include "myprogressbar.h"

MyProgressBar::MyProgressBar(QWidget *parent)
    : QProgressBar{parent}
{
    //进度条初始化
    this->setOrientation(Qt::Horizontal);                   //水平方向
    this->setMinimum(0);                                    //最小值
    this->setMaximum(100);                                  //最大值
    this->setAlignment(Qt::AlignRight | Qt::AlignVCenter);  //对齐方式
    this->mSetValue(0);                                     //设置初始值

    //定时器创建
    mySlowTimer = new QTimer(this);
    myFastTimer = new QTimer(this);

    //一号定时器,让进度条慢慢的增加
    connect(mySlowTimer,&QTimer::timeout,this,[=](){
        int currentValue = this->value();
        //获取pbStatusValue下一个节点的值
        int nextStatusValue = pbStatusValue[index_pbvalue + 1];
        //控制mySlowTimer增加后的进度条的值不能大于等于当前节点值
        if(currentValue <= (nextStatusValue - 2))
        {
            currentValue++;
            this->mSetValue(currentValue);
        }
    });
    //二号定时器,一旦有信号发过来要更改进度条的值时,进度条快速的增加
    connect(myFastTimer,&QTimer::timeout,this,[=](){
        int currentValue = this->value();
        //获取pbStatusValue当前节点的值
        int destValue = pbStatusValue[index_pbvalue];
        //当进度条的当前值小于pbStatusValue当前节点的值时,快速增加进度条的当前值
        if(currentValue < destValue)
        {
            currentValue++;
            this->mSetValue(currentValue);
        }
        else
        {
            //当进度条的当前值等于pbStatusValue当前节点的值时,停止增加进度条的当前值
            myFastTimer->stop();
        }
    });
}

void MyProgressBar::initVector(int times)
{
    //相当于进度条的第一个值为0
    pbStatusValue.push_back(0);
    //当需要通过外部多次更新当前进度条的值时,在pbStatusValue中插入times-1个中间节点
    //中间节点的间隔通过均分得到
    if(times > 1)
    {
        for(int i = 0; i < (times - 1); ++i)
        {
            pbStatusValue.push_back(pbStatusValue[i] + 100/times);
        }
    }
    //相当于进度条的最后一个值为100
    pbStatusValue.push_back(100);
}

void MyProgressBar::mStart(int times, int msec)
{
    pbStatusValue.clear();
    index_pbvalue = 0;
    mSetValue(0);
    this->initVector(times);
    mySlowTimer->start(msec);
}

void MyProgressBar::mUpdate()
{
    //当主窗口的更新信号发送的次数大于pbStatusValue的最大值时,直接返回
    if(index_pbvalue >= pbStatusValue.size())
        return;
    //每次主窗口发送signal_updateProgressBar信号,都将index_pbvalue加1
    index_pbvalue++;
    //启动myFastTimer定时器,快速增加进度条的当前值
    myFastTimer->start(5);
    //当达到进度条的最大更新值时,将mySlowTimer定时器停止
    if(pbStatusValue[index_pbvalue] == pbStatusValue.back())
    {
        mySlowTimer->stop();
    }
}

void MyProgressBar::mSetValue(int PBValue)
{
    //刷新当前进度显示。结果显示含有小数,并保留小数位1位
    this->setValue(PBValue);
    double dProgress = (this->value() - this->minimum()) * 100.0
                    / (this->maximum() - this->minimum());
    this->setFormat(QString::fromLocal8Bit("%1%").arg(QString::number(dProgress, 'f', 1)));
}



番外

  1. Python把视频转为GIF:

    https://zhuanlan.zhihu.com/p/163575055
  2. 纯代码创建UI及注意事项:

    https://blog.csdn.net/weixin_44954996/article/details/125448991?spm=1001.2014.3001.5502



版权声明:本文为weixin_44954996原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。