zoukankan      html  css  js  c++  java
  • Qt QFutureWatcher

    简述

    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 表示的计算将被自动节流。一旦挂起的信号数量下降到限制以下时,计算将恢复。

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

    1 // 实例化对象,并连接到 finished() 信号。
    2 MyClass myObject;
    3 QFutureWatcher<int> watcher;
    4 connect(&watcher, SIGNAL(finished()), &myObject, SLOT(handleFinished()));
    5  
    6 // 开始计算
    7 QFuture<int> future = QtConcurrent::run(...);
    8 watcher.setFuture(future);

    基本使用

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

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

    这里写图片描述

    具体的源码如下所示:

    ImagesView.h:

     1 #ifndef IMAGES_VIEW_H
     2 #define IMAGES_VIEW_H
     3  
     4 #include <QFutureWatcher>
     5 #include <QWidget>
     6  
     7 class QLabel;
     8 class QPushButton;
     9 class QVBoxLayout;
    10 class QGridLayout;
    11  
    12 class ImagesView : public QWidget
    13 {
    14     Q_OBJECT
    15  
    16 public:
    17     explicit ImagesView(QWidget *parent = 0);
    18     ~ImagesView();
    19  
    20 private slots:
    21     void open();  // 打开目录,加载图片
    22     void showImage(int index);  // 显示图片
    23     void finished();  // 更新按钮状态
    24  
    25 private:
    26     QPushButton *m_pOpenButton;
    27     QPushButton *m_pCancelButton;
    28     QPushButton *m_pPauseButton;
    29     QVBoxLayout *m_pMainLayout;
    30     QGridLayout *m_pImagesLayout;
    31     QList<QLabel *> labels;
    32     QFutureWatcher<QImage> *m_pWatcher;
    33 };
    34  
    35 #endif // IMAGES_VIEW_H

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

    ImagesView.cpp

      1 #include <QLabel>
      2 #include <QPushButton>
      3 #include <QProgressBar>
      4 #include <QFileDialog>
      5 #include <QtConcurrent/QtConcurrentMap>
      6 #include <QStandardPaths>
      7 #include <QHBoxLayout>
      8 #include <qmath.h>
      9 #include "ImagesView.h"
     10  
     11 const int c_nImageSize = 100;
     12  
     13 // 缩放图片
     14 QImage scale(const QString &imageFileName)
     15 {
     16     QImage image(imageFileName);
     17     return image.scaled(QSize(c_nImageSize, c_nImageSize), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
     18 }
     19  
     20 ImagesView::ImagesView(QWidget *parent)
     21     : QWidget(parent)
     22 {
     23     setWindowIcon(QIcon(":/Images/logo"));
     24     setWindowTitle(QStringLiteral("Qt之QFutureWatcher"));
     25     resize(800, 600);
     26  
     27     // 初始化控件
     28     m_pWatcher = new QFutureWatcher<QImage>(this);
     29     m_pOpenButton = new QPushButton(QStringLiteral("打开图片"));
     30     m_pCancelButton = new QPushButton(QStringLiteral("取消"));
     31     m_pPauseButton = new QPushButton(QStringLiteral("暂停/恢复"));
     32     QProgressBar *pProgressBar = new QProgressBar(this);
     33  
     34     m_pCancelButton->setEnabled(false);
     35     m_pPauseButton->setEnabled(false);
     36  
     37     // 布局
     38     QHBoxLayout *pButtonLayout = new QHBoxLayout();
     39     pButtonLayout->addWidget(m_pOpenButton);
     40     pButtonLayout->addWidget(m_pCancelButton);
     41     pButtonLayout->addWidget(m_pPauseButton);
     42     pButtonLayout->addStretch();
     43     pButtonLayout->setSpacing(10);
     44     pButtonLayout->setMargin(0);
     45  
     46     m_pImagesLayout = new QGridLayout();
     47  
     48     m_pMainLayout = new QVBoxLayout();
     49     m_pMainLayout->addLayout(pButtonLayout);
     50     m_pMainLayout->addWidget(pProgressBar);
     51     m_pMainLayout->addLayout(m_pImagesLayout);
     52     m_pMainLayout->addStretch();
     53     m_pMainLayout->setSpacing(10);
     54     m_pMainLayout->setContentsMargins(10, 10, 10, 10);
     55     setLayout(m_pMainLayout);
     56  
     57     // 连接信号槽 - 加载、显示进度、打开、取消等操作
     58     connect(m_pWatcher, SIGNAL(resultReadyAt(int)), SLOT(showImage(int)));
     59     connect(m_pWatcher, SIGNAL(progressRangeChanged(int,int)), pProgressBar, SLOT(setRange(int,int)));
     60     connect(m_pWatcher, SIGNAL(progressValueChanged(int)), pProgressBar, SLOT(setValue(int)));
     61     connect(m_pWatcher, SIGNAL(finished()), SLOT(finished()));
     62     connect(m_pOpenButton, SIGNAL(clicked()), SLOT(open()));
     63     connect(m_pCancelButton, SIGNAL(clicked()), m_pWatcher, SLOT(cancel()));
     64     connect(m_pPauseButton, SIGNAL(clicked()), m_pWatcher, SLOT(togglePaused()));
     65 }
     66  
     67 ImagesView::~ImagesView()
     68 {
     69     m_pWatcher->cancel();
     70     m_pWatcher->waitForFinished();
     71 }
     72  
     73 // 打开目录,加载图片
     74 void ImagesView::open()
     75 {
     76     // 如果已经加载图片,取消并进行等待
     77     if (m_pWatcher->isRunning()) {
     78         m_pWatcher->cancel();
     79         m_pWatcher->waitForFinished();
     80     }
     81  
     82     // 显示一个文件打开对话框
     83     QStringList files = QFileDialog::getOpenFileNames(this,
     84                                                       QStringLiteral("选择图片"),
     85                                                       QStandardPaths::writableLocation(QStandardPaths::PicturesLocation),
     86                                                       "*.jpg *.png");
     87  
     88     if (files.count() == 0)
     89         return;
     90  
     91     // 做一个简单的布局
     92     qDeleteAll(labels);
     93     labels.clear();
     94  
     95     int dim = qSqrt(qreal(files.count())) + 1;
     96     for (int i = 0; i < dim; ++i) {
     97         for (int j = 0; j < dim; ++j) {
     98             QLabel *pLabel = new QLabel(this);
     99             pLabel->setFixedSize(c_nImageSize, c_nImageSize);
    100             m_pImagesLayout->addWidget(pLabel, i, j);
    101             labels.append(pLabel);
    102         }
    103     }
    104  
    105     // 使用 mapped 来为 files 运行线程安全的 scale 函数
    106     m_pWatcher->setFuture(QtConcurrent::mapped(files, scale));
    107  
    108     m_pOpenButton->setEnabled(false);
    109     m_pCancelButton->setEnabled(true);
    110     m_pPauseButton->setEnabled(true);
    111 }
    112  
    113 // 显示图片
    114 void ImagesView::showImage(int index)
    115 {
    116     labels[index]->setPixmap(QPixmap::fromImage(m_pWatcher->resultAt(index)));
    117 }
    118  
    119 // 更新按钮状态
    120 void ImagesView::finished()
    121 {
    122     m_pOpenButton->setEnabled(true);
    123     m_pCancelButton->setEnabled(false);
    124     m_pPauseButton->setEnabled(false);
    125 }

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

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

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

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

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

     1 #include <QApplication>
     2 #include "ImagesView.h"
     3  
     4 int main(int argc, char *argv[])
     5 {
     6     QApplication app(argc,argv);
     7  
     8     ImagesView view;
     9     view.show();
    10  
    11     return app.exec();
    12 }

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

  • 相关阅读:
    3-4: 一元多项式的乘法与加法运算
    设计模式一装饰者模式
    设计模式一组合模式
    设计模式一命令模式
    设计模式一建造者模式
    设计模式一桥接模式
    设计模式一适配器模式
    设计模式一抽象工厂模式
    排序算法一二分排序
    排序算法一希尔排序
  • 原文地址:https://www.cnblogs.com/ybqjymy/p/14632036.html
Copyright © 2011-2022 走看看