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>
This commit is contained in:
张宗平
2026-07-09 09:01:29 +08:00
parent 05a685c952
commit 6773f9b29e
8 changed files with 706 additions and 0 deletions
@@ -0,0 +1,59 @@
// ============================================================================
// mandelbrot_widget —— Day4 Mandelbrot 渲染器的交互与渲染调度主体(QWidget)
// 职责:
// * paintEvent : 把累积 QImage 一次性画到窗口(QPainter 绘图知识点)
// * wheelEvent/mouse* : 滚轮缩放(以鼠标为锚)、左键拖拽平移(事件处理知识点)
// * scheduleRender() : 切水平条带丢 QThreadPool 并行算(多线程知识点)
// * onStripReady() : 收条带、拼图、版本号防过期、耗时显示
// * onRenderOnMainThread() : 「主线程渲染」按钮——同步算整帧,演示事件循环被
// 占用即界面冻结(直击 Day4 slides「主线程耗时=冻结」痛点)
// * onToggleThreadMode() : 复用同一套分块逻辑,切线程池大小,对比单/多线程加速比
// ============================================================================
#ifndef MANDELBROT_WIDGET_H
#define MANDELBROT_WIDGET_H
#include <QElapsedTimer>
#include <QImage>
#include <QPoint>
#include <QWidget>
#include "mandelbrot_strip_task.h" // MandelViewport / MandelPalette
class QLabel;
class MandelbrotWidget : public QWidget {
Q_OBJECT
public:
explicit MandelbrotWidget(QWidget* parent = nullptr);
protected:
void paintEvent(QPaintEvent*) override;
void wheelEvent(QWheelEvent*) override;
void mousePressEvent(QMouseEvent*) override;
void mouseMoveEvent(QMouseEvent*) override;
void mouseReleaseEvent(QMouseEvent*) override;
void resizeEvent(QResizeEvent*) override;
private slots:
void onStripReady(quint64 epoch, int stripIndex, QImage strip);
void onRenderOnMainThread(); // 卡死演示
void onToggleThreadMode(); // 单/多线程切换
private:
void scheduleRender(); // ++epoch + 分块丢池
MandelViewport vp_;
MandelPalette pal_;
QImage pixmap_; // 累积画布(离屏 QImagepaintEvent 外对它构造 QPainter 合法)
quint64 epoch_ = 0; // 渲染版本号:新视口令旧结果作废丢弃(防过期覆盖)
int stripsTotal_ = 0;
int stripsDone_ = 0;
QElapsedTimer timer_;
bool multiThread_ = true;
int threadCount_ = 0;
bool dragging_ = false;
QPoint lastMouse_;
QLabel* label_ = nullptr;
};
#endif // MANDELBROT_WIDGET_H