QFuture的使用:多线程与进度条

介绍

QFuture 类可以用来获取异步计算的结果(类似 std::future ),一般配合 Qt Concurrent 模块和 QFutureWatcher 类工作。在 Qt Creator 中搜索 concurrent 可以看到一些相关示例。官方示例中, QFuture 一般和 QFutureWatcher 配合,因为 QFuture 不是 QObject 子类,没有信号槽。但是 QFuture 相关的接口会触发 QFutureCallOutEvent 事件,QFutureWatcher 接收该事件后发送对应信号。

相关类的对应基本介绍:

  • QFuture:表示异步计算的结果
  • QFutureWatcher:QFuture本身不带信号槽,可使用QFutureWatcher进行监控
  • QFutureInterface:该类没有提供文档,出现于Qt源码。QFuture与QFutureInterface的关系类似std::future与std::promise。QFutureInterface可用于生成QFuture,在Qt类实现中,QFuture持有一个QFutureInterface成员。

从目前的文档和示例来看,QFuture 主要是由 Qt Concurrent 的相关接口生成,一般操作如下:

//concurrent生成future
QFuture future = QtConcurrent::run([this]{ /**/ });

//future设置给watcher进行监控
QFutureWatcher theWatcher;
theWatcher.setFuture(future);

//future对应的操作结束,触发watcher信号
connect(&theWatcher,&QFutureWatcher::finished,this,[this]{ /**/ });

本文的需求是实现一个展示多线程处理进度的进度条,但 QFuture 只有获取状态值的接口,而没有设置状态值的接口,这些都被封装在了 QFutureInterface 中。要实现需求,需要创建一个 QFutureInterface 对象,并用他来生成一个相关联的 QFuture 对象,再设置给 QFutureWatcher 进行监测,在线程中我们操作 QFutureInterface 实例就能触发 QFutureWatcher 对应的信号。

功能实现

实现效果(GIF):

QFuture的使用:多线程与进度条_第1张图片

完整代码链接(GitHub):https://github.com/gongjianbo/MyTestCode/tree/master/Qt/TestQt_20200625_QFuture

主要实现代码(组件是在 ui 上拖的):

(其实这里遇到一个问题,就是调用 cancel 来取消的话,会异常结束或者卡死,所以暂时用的 pause 来结束,线程中判断是否 pause。还要注意一个问题是,pause 或者 cancel 后,后续的设置是不会触发信号的,所以我在线程中结束了pause。)

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include 
//QFuture类表示异步计算的结果
#include 
//QFuture本身不带信号槽,可使用QFutureWatcher进行监控
#include 
//QFutureInterface没有提供文档,出现于Qt源码
//QFuture与QFutureInterface的关系类似std::future与std::promise
//QFutureInterface可用于生成QFuture
//在Qt类实现中,QFuture持有一个QFutureInterface成员
#include 

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private:
    Ui::MainWindow *ui;
    //QFuture本身不带信号槽,可使用QFutureWatcher进行监控
    QFutureWatcher myWatcher;
};
#endif // MAINWINDOW_H

 

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

#include 

#include 
#include 

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    connect(ui->btnStart,&QPushButton::clicked,[this](){
        qDebug()<<"点击 start"< interface;
        interface.reportStarted();
        interface.setProgressRange(0,100);
        myWatcher.setFuture(interface.future());
        //QFutureInterface是允许拷贝的,里面有一个d指针
        std::thread thread([this,interface]() mutable {
            qDebug()<<"线程开始"<btnCancel,&QPushButton::clicked,[this](){
        qDebug()<<"点击 cancel"<::finished,[=]{
        qDebug()<<"任务已完成"<label->setText("任务完成");
        }else{
            ui->label->setText("任务失败");
        }
    });
    connect(&myWatcher,&QFutureWatcher::paused,[=]{
        qDebug()<<"任务已取消";
    });

    //进度条相关
    ui->progressBar->setRange(0,100);
    ui->progressBar->setValue(0);
    connect(&myWatcher,&QFutureWatcher::progressValueChanged,[=](int value){
        qDebug()<<"Value"<progressBar->setValue(value);
    });
    connect(&myWatcher,&QFutureWatcher::progressTextChanged,[=](const QString text){
        qDebug()<<"Text"<label->setText(text);
    });
}

MainWindow::~MainWindow()
{
    myWatcher.pause();
    myWatcher.waitForFinished();
    delete ui;
}

参考

文档:https://doc.qt.io/qt-5/qfuture.html

博客:https://zhuanlan.zhihu.com/p/81566592?from_voters_page=true

你可能感兴趣的:(Qt,略知一二,QFuture,QFutureWatcher)