Qt之QFutureWatcher

简介: 简述QFuture 表示异步计算的结果,QFutureWatcher 则允许使用信号和槽监视 QFuture,也就是说,QFutureWatcher 是为 QFuture 而生的。简述详细描述基本使用更多参考详细描述QFutureWatcher 提供了有关 QFuture 的信息和通知,使用 setFuture() 函数开始监视一个特

简述

QFuture 表示异步计算的结果,QFutureWatcher 则允许使用信号和槽监视 QFuture,也就是说,QFutureWatcher 是为 QFuture 而生的。

详细描述

QFutureWatcher 提供了有关 QFuture 的信息和通知,使用 setFuture() 函数开始监视一个特定的 QFuture,函数 future() 则返回由 setFuture() 设置的 future。

为了方便,QFuture 的很多函数可以直接通过 QFutureWatcher 来访问,例如:progressValue()、progressMinimum()、progressMaximum()、progressText()、isStarted()、isFinished()、isRunning()、isCanceled()、isPaused()、waitForFinished()、result() 和 resultAt()。而 cancel()、setPaused()、pause()、resume() 和 togglePaused() 是 QFutureWatcher 中的槽函数。

状态更改由 started()、finished()、cancelled()、paused()、resumed()、resultReadyAt() 和 resultsReadyAt() 信号提供,进度信息由 progressRangeChanged()、progressValueChanged() 和progressTextChanged() 信号提供。

由函数 setPendingResultsLimit() 提供节流控制。当挂起的 resultReadyAt() 或 resultsReadyAt() 信号数量超过限制时,由 future 表示的计算将被自动节流。一旦挂起的信号数量下降到限制以下时,计算将恢复。

示例,开始计算并当完成时获取槽回调:

// 实例化对象,并连接到 finished() 信号。
MyClass myObject;
QFutureWatcher<int> watcher;
connect(&watcher, SIGNAL(finished()), &myObject, SLOT(handleFinished()));

// 开始计算
QFuture<int> future = QtConcurrent::run(...);
watcher.setFuture(future);

基本使用

来看一个图像加载和缩放的示例。选择多个图片,进行异步计算(将所有图片进行缩放),加载过程中可以显示进度,以便我们实时了解进展。每当一个图片处理完成,就会显示在窗体中。

这里仅为了演示效果,加载了 8 张 图片。

这里写图片描述

具体的源码如下所示:

ImagesView.h:

#ifndef IMAGES_VIEW_H
#define IMAGES_VIEW_H

#include <QFutureWatcher>
#include <QWidget>

class QLabel;
class QPushButton;
class QVBoxLayout;
class QGridLayout;

class ImagesView : public QWidget
{
    Q_OBJECT

public:
    explicit ImagesView(QWidget *parent = 0);
    ~ImagesView();

private slots:
    void open();  // 打开目录,加载图片
    void showImage(int index);  // 显示图片
    void finished();  // 更新按钮状态

private:
    QPushButton *m_pOpenButton;
    QPushButton *m_pCancelButton;
    QPushButton *m_pPauseButton;
    QVBoxLayout *m_pMainLayout;
    QGridLayout *m_pImagesLayout;
    QList<QLabel *> labels;
    QFutureWatcher<QImage> *m_pWatcher;
};

#endif // IMAGES_VIEW_H

下面是实现部分,c_nImageSize 表示的是图片被缩放的大小(宽度:100 px,高度:100px),函数 scale() 则是对图片缩放的具体实现。

ImagesView.cpp

#include <QLabel>
#include <QPushButton>
#include <QProgressBar>
#include <QFileDialog>
#include <QtConcurrent/QtConcurrentMap>
#include <QStandardPaths>
#include <QHBoxLayout>
#include <qmath.h>
#include "ImagesView.h"

const int c_nImageSize = 100;

// 缩放图片
QImage scale(const QString &imageFileName)
{
    QImage image(imageFileName);
    return image.scaled(QSize(c_nImageSize, c_nImageSize), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
}

ImagesView::ImagesView(QWidget *parent)
    : QWidget(parent)
{
    setWindowIcon(QIcon(":/Images/logo"));
    setWindowTitle(QStringLiteral("Qt之QFutureWatcher"));
    resize(800, 600);

    // 初始化控件
    m_pWatcher = new QFutureWatcher<QImage>(this);
    m_pOpenButton = new QPushButton(QStringLiteral("打开图片"));
    m_pCancelButton = new QPushButton(QStringLiteral("取消"));
    m_pPauseButton = new QPushButton(QStringLiteral("暂停/恢复"));
    QProgressBar *pProgressBar = new QProgressBar(this);

    m_pCancelButton->setEnabled(false);
    m_pPauseButton->setEnabled(false);

    // 布局
    QHBoxLayout *pButtonLayout = new QHBoxLayout();
    pButtonLayout->addWidget(m_pOpenButton);
    pButtonLayout->addWidget(m_pCancelButton);
    pButtonLayout->addWidget(m_pPauseButton);
    pButtonLayout->addStretch();
    pButtonLayout->setSpacing(10);
    pButtonLayout->setMargin(0);

    m_pImagesLayout = new QGridLayout();

    m_pMainLayout = new QVBoxLayout();
    m_pMainLayout->addLayout(pButtonLayout);
    m_pMainLayout->addWidget(pProgressBar);
    m_pMainLayout->addLayout(m_pImagesLayout);
    m_pMainLayout->addStretch();
    m_pMainLayout->setSpacing(10);
    m_pMainLayout->setContentsMargins(10, 10, 10, 10);
    setLayout(m_pMainLayout);

    // 连接信号槽 - 加载、显示进度、打开、取消等操作
    connect(m_pWatcher, SIGNAL(resultReadyAt(int)), SLOT(showImage(int)));
    connect(m_pWatcher, SIGNAL(progressRangeChanged(int,int)), pProgressBar, SLOT(setRange(int,int)));
    connect(m_pWatcher, SIGNAL(progressValueChanged(int)), pProgressBar, SLOT(setValue(int)));
    connect(m_pWatcher, SIGNAL(finished()), SLOT(finished()));
    connect(m_pOpenButton, SIGNAL(clicked()), SLOT(open()));
    connect(m_pCancelButton, SIGNAL(clicked()), m_pWatcher, SLOT(cancel()));
    connect(m_pPauseButton, SIGNAL(clicked()), m_pWatcher, SLOT(togglePaused()));
}

ImagesView::~ImagesView()
{
    m_pWatcher->cancel();
    m_pWatcher->waitForFinished();
}

// 打开目录,加载图片
void ImagesView::open()
{
    // 如果已经加载图片,取消并进行等待
    if (m_pWatcher->isRunning()) {
        m_pWatcher->cancel();
        m_pWatcher->waitForFinished();
    }

    // 显示一个文件打开对话框
    QStringList files = QFileDialog::getOpenFileNames(this,
                                                      QStringLiteral("选择图片"),
                                                      QStandardPaths::writableLocation(QStandardPaths::PicturesLocation),
                                                      "*.jpg *.png");

    if (files.count() == 0)
        return;

    // 做一个简单的布局
    qDeleteAll(labels);
    labels.clear();

    int dim = qSqrt(qreal(files.count())) + 1;
    for (int i = 0; i < dim; ++i) {
        for (int j = 0; j < dim; ++j) {
            QLabel *pLabel = new QLabel(this);
            pLabel->setFixedSize(c_nImageSize, c_nImageSize);
            m_pImagesLayout->addWidget(pLabel, i, j);
            labels.append(pLabel);
        }
    }

    // 使用 mapped 来为 files 运行线程安全的 scale 函数
    m_pWatcher->setFuture(QtConcurrent::mapped(files, scale));

    m_pOpenButton->setEnabled(false);
    m_pCancelButton->setEnabled(true);
    m_pPauseButton->setEnabled(true);
}

// 显示图片
void ImagesView::showImage(int index)
{
    labels[index]->setPixmap(QPixmap::fromImage(m_pWatcher->resultAt(index)));
}

// 更新按钮状态
void ImagesView::finished()
{
    m_pOpenButton->setEnabled(true);
    m_pCancelButton->setEnabled(false);
    m_pPauseButton->setEnabled(false);
}

构造函数中,需要注意的是槽函数,其中 resultReadyAt() 表示 index 对应位置的处理结果已准备就绪,所以连接该信号至槽函数 showImage(),可以显示处理完的图片。

为了显示处理进度,我们构造了一个进度条,当 QFutureWatcher 的 progressRangeChanged() 的信号发射时,进度条的范围会发生改变,而 progressValueChanged() 信号发射时,会更新进度条的值。

如果加载的图片较多时,可以通过点击“取消”按钮,这时会调用 QFutureWatcher 的 cancel() 槽函数来取消计算。“暂停/恢复”则调用 togglePaused() 槽函数,用于切换异步计算的暂停状态,换句话说,如果计算当前已暂停,调用此函数将进行恢复;如果计算正在运行,则会暂停。

当点击“打开”按钮时,会调用槽函数 open(),默认打开图片目录,以便进行图片的选择。然后根据图片创建对应数量的标签 QLabel,用于显示后期缩放的图片。创建完成后,使用 mapped() 进行并行计算,并添加至 QFutureWatcher 中,让其使用信号和槽监视 QFuture。

接下来,就可以直接使用了。

#include <QApplication>
#include "ImagesView.h"

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

    ImagesView view;
    view.show();

    return app.exec();
}

这样,我们就完成了一个图片缩放加载缩放的功能。

更多参考

目录
相关文章
|
6月前
|
前端开发 编译器 开发工具
Qt
Qt
336 0
|
存储 Cloud Native 前端开发
Qt QScrollArea使用
Qt QScrollArea使用
|
存储
Qt之QLCDNumber
Qt之QLCDNumber
371 0
|
算法
Qt之QTimeLine
简述 QTimeLine 类提供了用于控制动画的时间轴,通常用于通过定期调用一个槽函数来动画一个 GUI 控件。 相信了解动画的人对帧应该不陌生,可以把一个动画想象成由很多张静态画面组成,而每一个画面就是一帧图像。每隔一定时间间隔就显示一帧图像,当该间隔较短时,人眼就感觉不出来了,觉得看到的是连续的影像。 简述 详细说明 状态 方向 曲线形状 详细
2270 0
|
Java C++
Qt之QFuture
简述 QFuture 类代表一个异步计算的结果。 要启动一个计算,使用 Qt之Concurrent框架 中的 APIs 之一。 QFuture 允许线程与一个或多个结果同步,这些结果将在稍后的时间点准备就绪,该结果可以是具有默认构造函数和拷贝构造函数的任何类型。如果一个结果在调用 result()、resultAt() 或 results() 函数时不可用,QFutur
2042 0
|
索引
Qt之QToolBox
简述 QToolBox类提供了一个列(选项卡式的)部件条目。 QToolBox可以在一个tab列上显示另外一个,并且当前的item显示在当前的tab下面。每个tab都在tab列中有一个索引位置。tab的item是一个QWidget 。 简述 详细描述 使用 效果 源码 详细描述 每个item都有一个itemText()、一个可选的itemI
2520 0
Qt之QDateTimeEdit
简述 QDateTime类提供了一个部件,用于编辑日期和时间。 QDateTimeEdit允许用户编辑日期,通过使用键盘或箭头键来增加和减少日期和时间值。箭头键可以在QDateTimeEdit内进行部分移动,日期和时间的格式按照setDisplayFormat()设置的显示。 简述 基本使用 效果 源码 日期时间格式 效果 源码 日期时间范围 效果
2714 0
|
缓存 Unix Windows
Qt之QLocalSocket
简述 QLocalSocket类提供了一个本地socket。 在Windows中,这是一个命名管道;在Unix中,这是一个本地网域socket。 如果发生错误,socketError()会返回错误的类型,errorString()则返回人类可读的错误描述。 虽然QLocalSocket是一个事件循环使用而设计,它也可以不被如此使用。这种情况下,必须使用 waitF
2670 0