Files
scuc-qt-course/docs/teaching/examples/mandelbrot_renderer/mandelbrot_strip_task.h
T
张宗平 6773f9b29e Day4 教学示例:Mandelbrot 分形渲染器(QPainter 绘图 + 多线程 + 事件处理综合演示)
三段式对比直击 Day4 slides「主线程耗时=冻结」痛点:主线程渲染冻结 / 后台单线程不卡但慢 / 后台多线程不卡且快。
- QThreadPool + QRunnable 分水平条带并行,排队信号槽回传,epoch 版本号防过期覆盖
- wheelEvent 缩放(鼠标为锚)+ mouseEvent 拖拽平移 + paintEvent 画 QImage
- 子目录自带独立 CMakeLists.txt,可单独构建/共享(双模式约定同 lineedit_eye_toggle)
- 配套 README(概念/走读/双构建/5 踩坑点/练习)+ 设计规格
- 已验证:独立工程 offscreen 运行 exit=0;主工程构建 exit=0(w64devkit g++16 + mingw73 Qt5.14.2)

注:主工程 examples/CMakeLists.txt 的接入(mandelbrot + lineedit_eye_toggle + qdialog + themes 原子接入)与顶层 CMakeLists 本地调试改动未一并提交,待整体确认后单独提交。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 09:01:29 +08:00

76 lines
3.3 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ============================================================================
// mandelbrot_strip_task —— Day4 Mandelbrot 渲染器的「分块计算 + 调色板」单元
// 职责:
// * MandelViewport : 视口参数(复平面中心 / 像素跨度 / 尺寸 / 最大迭代)
// * MandelPalette : 迭代次数→RGB 的预计算调色板(提升趣味,与知识点无关)
// * renderStrip() : 纯计算函数,渲染视口内 [y0,y1) 行像素。多线程 task 与
// 「主线程卡死」演示路径共用它——保证两路径算的是同一份分形,
// 差异只在「是否阻塞事件循环 / 是否并行」。
// * MandelbrotStripTask : 一条水平带的 QRunnable 任务。多继承 QObject+QRunnable
// 以获得信号能力(QRunnable 本身无信号——这是初学者高频困惑,
// 见 README 踩坑点 2)。
// 设计要点:task 持 palette/viewport 的【值副本】,自包含、不引用主线程对象,
// 避免主对象先析构导致的悬垂访问(见 README 踩坑点 4)。
// ============================================================================
#ifndef MANDELBROT_STRIP_TASK_H
#define MANDELBROT_STRIP_TASK_H
#include <QImage>
#include <QObject>
#include <QRunnable>
#include <QRgb>
#include <QVector>
// 视口参数:复平面映射。scale = 每像素对应的复平面跨度。
struct MandelViewport {
double centerX = -0.5; // 经典 Mandelbrot 主分形居中
double centerY = 0.0;
double scale = 3.0 / 600.0;
int width = 0; // show 后由 widget 尺寸填入
int height = 0;
int maxIter = 256;
};
// 调色板:迭代次数 → RGB。集合内部(迭代未逃逸)映射为黑。
class MandelPalette {
public:
explicit MandelPalette(int maxIter = 256);
QRgb color(int i) const { return table_.at(i); }
private:
QVector<QRgb> table_;
};
// 纯计算:渲染视口内 [y0, y1) 行像素,返回 (width × (y1-y0)) 的 RGB32 QImage。
QImage renderStrip(const MandelViewport& vp, const MandelPalette& pal, int y0, int y1);
// 一条水平带的多线程任务。多继承 QObject + QRunnable 以拥有信号。
// setAutoDelete(true)(默认):run() 结束后由线程池回收;不设 QObject parent
// 避免与 QThreadPool 的自动删除发生双重所有权——见 README 踩坑点 2。
class MandelbrotStripTask : public QObject, public QRunnable {
Q_OBJECT
public:
MandelbrotStripTask(quint64 epoch, int stripIndex,
const MandelViewport& vp, const MandelPalette& pal,
int y0, int y1)
: QObject(nullptr), epoch_(epoch), stripIndex_(stripIndex),
vp_(vp), pal_(pal), y0_(y0), y1_(y1) { setAutoDelete(true); }
void run() override {
// 算完一条带 → 通过信号把结果「排队」送回主线程(Qt 自动跨线程调度)。
emit stripReady(epoch_, stripIndex_, renderStrip(vp_, pal_, y0_, y1_));
}
signals:
void stripReady(quint64 epoch, int stripIndex, QImage strip);
private:
quint64 epoch_;
int stripIndex_;
MandelViewport vp_;
MandelPalette pal_; // 值副本:任务自包含
int y0_;
int y1_;
};
#endif // MANDELBROT_STRIP_TASK_H