【Qt】Qt多线程开发—多线程应用示例分析(Mandelbrot)

Qt-多线程应用示例分析(Mandelbrot)

一、写在前面

​ 本文章讨论内容是:关于Qt的多线程应用,创建线程的方法是:子类化QThread创建线程,重载run()函数实现多线程。

​ (注)本文所有代码出自官方示例《Mandelbrot Example》

​ 《Mandelbrot Example》示例包含两个类:

1、RenderThread是一个QThread子类,用于呈现Mandelbrot集合。
2、MandelbrotWidget是一个QWidget子类,用于在屏幕上显示Mandelbrot集合,并绑定鼠标缩放和滚动事件处理功能。

二、RendThread类的定义
#include <QMutex>
#include <QSize>
#include <QThread>
#include <QWaitCondition>

QT_BEGIN_NAMESPACE
class QImage;
QT_END_NAMESPACE

//! [0]
class RenderThread : public QThread
{
    Q_OBJECT

public:
    RenderThread(QObject *parent = nullptr);
    ~RenderThread();

    void render(double centerX, double centerY, double scaleFactor, QSize resultSize);

signals:
    void renderedImage(const QImage &image, double scaleFactor);

protected:
    void run() override;

private:
    uint rgbFromWaveLength(double wave);

    QMutex mutex;
    QWaitCondition condition;
    double centerX;
    double centerY;
    double scaleFactor;
    QSize resultSize;
    bool restart;
    bool abort;

    enum { ColormapSize = 512 };
    uint colormap[ColormapSize];
};

​ 1、该类继承了QThread,因此它能够在单独的线程中运行。除了构造函数和析构函数外,render()是唯一的公共函数。每当线程渲染一个图像时,都会发出renderedImage()信号。renderedImage()信号将与MandelbrotWidget类的updatePixmap槽函数相连接,用于更新QPixmap。以便于MandelbrotWidget类的paintEvent()绘图事件函数对经过RenderThread线程渲染后的Pixmap绘制。如下代码片段:

connect(&thread, &RenderThread::renderedImage,this, &MandelbrotWidget::updatePixmap);

2、在QThread子类中重新实现受保护的run()函数,在线程启动时自动调用它。

​ 3、在私有部分中,有一个QMutex、一个QWaitCondition和一些其他的数据成员。互斥锁保护另一个数据成员。

(2-1)run()函数实现
void RenderThread::run()
{
  forever {
        mutex.lock();
        QSize resultSize = this->resultSize;
        double scaleFactor = this->scaleFactor;
        double centerX = this->centerX;
        double centerY = this->centerY;
        mutex.unlock();
 
        int halfWidth = resultSize.width() / 2;
 
        int halfHeight = resultSize.height() / 2;
        QImage image(resultSize, QImage::Format_RGB32);

        const int NumPasses = 8;
        int pass = 0;
        while (pass < NumPasses) {
            const int MaxIterations = (1 << (2 * pass + 6)) + 32;
            const int Limit = 4;
            bool allBlack = true;

             for (int y = -halfHeight; y < halfHeight; ++y) {
                 if (restart)
                     break;
                 if (abort)
                     return;
 
                 uint *scanLine =
                         reinterpret_cast<uint *>(image.scanLine(y + halfHeight));
                 double ay = centerY + (y * scaleFactor);
 
                for (int x = -halfWidth; x < halfWidth; ++x) {
                     double ax = centerX + (x * scaleFactor);
                     double a1 = ax;
                     double b1 = ay;
                     int numIterations = 0;
 
                     do {
                         ++numIterations;
                         double a2 = (a1 * a1) - (b1 * b1) + ax;
                         double b2 = (2 * a1 * b1) + ay;
                         if ((a2 * a2) + (b2 * b2) > Limit)
                             break;
 
                         ++numIterations;
                         a1 = (a2 * a2) - (b2 * b2) + ax;
                         b1 = (2 * a2 * b2) + ay;
                         if ((a1 * a1) + (b1 * b1) > Limit)
                             break;
                     } while (numIterations < MaxIterations);
 
                     if (numIterations < MaxIterations) {
                         *scanLine++ = colormap[numIterations % ColormapSize];
                         allBlack = false;
                     } else {
                         *scanLine++ = qRgb(0, 0, 0);
                     }
                 }
             }//for END
       /* 数据计算完后成 处理 */
             if (allBlack && pass == 0) {
                 pass = 4;
             } else {
                 if (!restart)
                     emit renderedImage(image, scaleFactor);  /* 发出renderImage()信号 */
                 ++pass;
             }
         }//While END
 
         mutex.lock();
 
         if (!restart)
             condition.wait(&mutex);
         restart = false;
         mutex.unlock();
     }
}
(2-2)render()函数的实现
void RenderThread::render(double centerX, double centerY, double scaleFactor,
                          QSize resultSize)
{
    QMutexLocker locker(&mutex);

    this->centerX = centerX;
    this->centerY = centerY;
    this->scaleFactor = scaleFactor;
    this->resultSize = resultSize;

    if (!isRunning()) {
        start(LowPriority);
    } else {
        restart = true;
        condition.wakeOne();
    }
}
三、MandelbrotWidget类的定义
class MandelbrotWidget : public QWidget
{
    Q_OBJECT

public:
    MandelbrotWidget(QWidget *parent = nullptr);

protected:
    void paintEvent(QPaintEvent *event) override;
    void resizeEvent(QResizeEvent *event) override;
    void keyPressEvent(QKeyEvent *event) override;
#if QT_CONFIG(wheelevent)
    void wheelEvent(QWheelEvent *event) override;
#endif
    void mousePressEvent(QMouseEvent *event) override;
    void mouseMoveEvent(QMouseEvent *event) override;
    void mouseReleaseEvent(QMouseEvent *event) override;

private slots:
    void updatePixmap(const QImage &image, double scaleFactor);
    void zoom(double zoomFactor);

private:
    void scroll(int deltaX, int deltaY);

    RenderThread thread;
    QPixmap pixmap;
    QPoint pixmapOffset;
    QPoint lastDragPos;
    double centerX;
    double centerY;
    double pixmapScale;
    double curScale;
};
四、总结

​ 1、RendThread类与MandelbrotWidget类通过信号和槽机制进行关联:用于更新MandelbrotWidget类中所使用的Pixmap数据,然后在paintEvent()绘图事件函数中进行绘制。

​ 2、MandelbrotWidget类通过调用RendThread类的render(double centerX, double centerY, double scaleFactor, QSize resultSize)成员函数向RendThread类中传递数据并启动RendThread类线程进行计算。

​ 3、Qt中Widgets部件(例如QPushButtonQLabel等)不能在其他线程中创建,只能在GUI线程中创建。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值