一、问题复现
当点击按钮时,函数会处理一个比较耗时的工作,同时我需要根据耗时的进度,刷新进度条的进度。这种情况下,我的基本思路如下:
利用定时器,每隔一段时间,刷新进度条(模拟事务处理进度),当事务处理完毕后,关闭定时器
QTimer *timer = new QTimer(this);
//利用定时器,定时更新进度条(模拟事务处理进度)
connect(timer, &QTimer::timeout, [this](){
int newVal = ui->progressBar->value();
if (newVal < 95) {
ui->progressBar->setValue(newVal + 5);
} else {
ui->progressBar->setValue(99);
}
});
//启动定时器,每500秒发送一次 timeout 信号
timer->start(500);
//模拟耗时操作
sleep();
//耗时操作完毕,进度条设置为100
ui->progressBar->setValue(100);
timer->stop();
但是按照上面的思路,并没有成功,当处理耗时业务时,由于处于主线程,界面已经处于假死状态,无法更新进度条进度。只有事务处理完毕后,才会开始更新,所以见到的界面就是:进度条一直是0,然后过一段时间突然100%,无法模拟显示事务处理进度。
二、解决办法
思路很明确,只要将处理耗时业务的代码在子线程中运行,这样就不会影响主线程刷新进度条。这里就是采用 QFutureWatcher
和 QFuture
的场景。解决办法如下:
Dialog.h
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private slots:
void on_pushButton_clicked();
private:
Ui::Dialog *ui;
QTimer *timer;
QFutureWatcher<bool> *watcher;
private slots:
void handleFinished();
};
// DIALOG_H
Dialog.cpp
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
//初始化 QFuture 监视器
watcher = new QFutureWatcher<bool>();
//绑定监视器结束信号和处理槽
connect(watcher, SIGNAL(finished()), this, SLOT(handleFinished()));
//初始化定时器
timer = new QTimer(this);
//根据定时器,定时刷新进度条
connect(timer, &QTimer::timeout, [this](){
int newVal = ui->progressBar->value();
if (newVal < 95) {
ui->progressBar->setValue(newVal + 5);
} else {
ui->progressBar->setValue(99);
}
});
}
Dialog::~Dialog()
{
delete ui;
}
//模拟处理耗时业务
bool sleep() {
QTime currentTime = QTime::currentTime();
//主线程休眠5秒中
while (currentTime.elapsed() < 5000) {
}
return true;
}
void Dialog::on_pushButton_clicked()
{
//启动定时器,每500ms,发送一次 timeout 信号
timer->start(500);
//使用 QFuture 开启子线程处理耗时业务
QFuture<bool> future = QtConcurrent::run(sleep);
//监视器监视 QFuture
watcher->setFuture(future);
}
//耗时业务处理完毕后的处理逻辑
void Dialog::handleFinished()
{
ui->progressBar->setValue(100);
timer->stop();
}