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部件(例如QPushButton
、QLabel
等)不能在其他线程中创建,只能在GUI线程中创建。