文章目录
前言
因为项目中经常要用到进度条,以用于提示用户当前进度信息。但是之前百度并没有找到自己想要的动态显示效果的案例。后来索性自己尝试写了一个,效果还可以,现将普通进度条的显示效果,与本例中的动态显示进度条做一个直观的GIF对比。
普通进度条显示
自定义的动态进度条显示
一、主要实现方式概述
- 继承QProgressBar类,其中定义两个定时器。一个用来保证进度条一直在变化增加,一个用来控制进度条“顺滑”增加。
- 该自定义类主要由两个外部接口函数控制,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)));
}