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();
}

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

更多参考

目录
相关文章
|
5月前
|
安全 C++
Qt moveToThread
Qt moveToThread
56 0
|
存储 Cloud Native 前端开发
Qt QScrollArea使用
Qt QScrollArea使用
26 QT - 事件过滤器
26 QT - 事件过滤器
62 0
|
存储 安全 Windows
Qt之QEvent
简述 QEvent 类是所有事件类的基类,事件对象包含事件参数。 Qt 的主事件循环(QCoreApplication::exec())从事件队列中获取本地窗口系统事件,将它们转化为 QEvents,然后将转换后的事件发送给 QObjects。 一般来说,事件来自底层窗口系统(spontaneous() 返回 true),但也可以使用 QCoreApplication:
1840 0
|
存储
Qt之QUrlQuery
简述 QUrlQuery 类提供了一种方法来操纵 URL 查询中的 key-value 对。 简述 详细描述 编码 处理空格和加号 全解码 非标准分隔符 使用 QUrlQuery 分隔符 查询 删除 是否为空 详细描述 QUrlQuery 用来解析 URL 中的查询字符串,像下面这样: 上述的查询字符串在 URL 中 被用来传输
2894 0
Qt之QScrollArea
简述 QScrollArea提供了一个滚动视图到另一个部件。 滚动区域用于显示一个画面中的子部件的内容。如果部件超过画面的大小,视图可以提供滚动条,这样就都可以看到部件的整个区域。 简述 基本使用 对齐方式 调整部件大小 手动调整 自动调整 获取与移除部件 获取 移除 基本使用 子部件必须使用setWidget()指定,例如: QLab
3264 0