1.01 双缓冲 (Double Buffering) 防闪烁
在 QWidget 的 paintEvent() 中,直接绘制到屏幕可能导致闪烁。
使用 双缓冲技术:先将所有内容绘制到一个离屏的 QPixmap 或 QImage 上,然后在 paintEvent 的最后,将这个完整的图像一次性绘制到设备上。
void CustomWidget::paintEvent(QPaintEvent *event) {
// 创建与控件大小相同的 pixmap 作为缓冲区
QPixmap buffer(size());
buffer.fill(Qt::transparent); // 或背景色
QPainter bufferPainter(&buffer);
// 在 bufferPainter 上进行所有复杂的绘制操作
drawComplexScene(&bufferPainter);
// 最后,将缓冲区内容一次性绘制到屏幕上
QPainter screenPainter(this);
screenPainter.drawPixmap(0, 0, buffer);
}
应用场景:任何包含复杂、频繁重绘的自定义控件。
1.02 QPainter::drawPixmapFragments() 批量绘制
当需要绘制大量相同或相似的小图像(如粒子系统、瓦片地图、图标列表)时,使用 QPainter::drawPixmapFragments() 可以显著提升性能。它允许你一次性提交多个绘制片段(位置、源矩形、变换等),减少函数调用开销。
关键点:适用于静态或半静态的批量绘制,避免在动画中每帧都构建 QPainter::PixmapFragment 数组。
应用场景:游戏引擎、数据可视化(大量标记点)、图像编辑器(图层预览)。
1.03 QGraphicsEffect 自定义视觉效果
继承 QGraphicsEffect 创建自定义的后处理效果,如模糊、阴影、发光、扭曲等。
QGraphicsEffect 会在目标绘制完成后,将其内容复制到一个 QPixmap,然后调用你的 draw() 方法进行处理,最后将结果绘制回屏幕。
void CustomBlurEffect::draw(QPainter *painter) {
// 获取源内容
QPoint offset;
QPixmap pixmap = sourcePixmap(Qt::DeviceCoordinates, &offset);
// 应用模糊算法到 pixmap
QPixmap blurred = applyGaussianBlur(pixmap, radius);
// 将模糊后的图像绘制回去
painter->drawPixmap(offset, blurred);
}
应用场景:创建现代化的 UI 效果、对话框阴影、动态高亮。
1.04 利用 QOpenGLFramebufferObject (FBO) 进行 GPU 加速绘制
对于极其复杂的图形或需要 GPU 加速的效果(如实时滤镜、3D 叠加),在 QOpenGLWidget 或支持 OpenGL 的 QWidget 中,
使用 QOpenGLFramebufferObject。先在 FBO 中进行 OpenGL 渲染,然后将 FBO 的纹理内容通过 QPainter 绘制到最终设备上。
优势:充分利用 GPU,性能远超纯 CPU 的 QPainter 绘制。
应用场景:视频处理、高级图像滤镜、混合 2D/3D 场景。
1.05 抗锯齿 (Antialiasing) 与渲染提示的精细控制
不仅仅是开启 Antialiasing,而是根据绘制内容选择性地启用。
例如,绘制文本和曲线时开启 TextAntialiasing 和 Antialiasing,绘制像素完美的图标时则关闭,以保持边缘清晰。
使用 QPainter::setRenderHint() 精确控制。
painter->setRenderHint(QPainter::Antialiasing, true);
painter->setRenderHint(QPainter::TextAntialiasing, true);
painter->setRenderHint(QPainter::SmoothPixmapTransform, true); // 图像平滑缩放
应用场景:需要兼顾视觉质量和性能的复杂 UI。
1.06 自定义 QStyle 来改变标准控件外观
通过继承 QProxyStyle 并重写 drawControl()、drawComplexControl() 等方法,可以完全重写标准 Qt 控件(如按钮、复选框、滚动条)的绘制逻辑。
这比 QSS 更强大,可以实现复杂的动态效果和状态反馈。
优势:保持控件的完整行为和信号,只改变外观。
应用场景:打造统一的品牌 UI 风格、实现 macOS 或 Material Design 风格的控件。
1.07 利用 QBitmap 和 QRegion 实现非矩形窗口/控件
使用 QBitmap 定义一个黑白蒙版,然后通过 setMask() 应用到 QWidget 或 QPixmap 上,可以创建圆形、不规则形状的窗口或控件。
QRegion 可以用来定义更复杂的、由多个矩形组成的透明区域。
QBitmap mask(size());
mask.fill(Qt::color0); // 透明
QPainter painter(&mask);
painter.setBrush(Qt::color1); // 不透明
painter.drawEllipse(rect()); // 绘制一个圆形的不透明区域
setMask(mask);
应用场景:圆形头像、悬浮工具栏、艺术化窗口。
1.08 离屏渲染 (Offscreen Rendering) 用于预计算
将一些计算量大但不常变化的图形(如渐变背景、复杂图案、阴影)预先绘制到一个 QPixmap 中缓存起来。
在 paintEvent 中,直接将这个缓存的 QPixmap 绘制出来,而不是每次都重新计算和绘制。
关键点:在数据或状态变化时更新缓存。
应用场景:地图背景、复杂的图表网格线、静态装饰元素。
1.09 利用 QPainterPath 创建复杂矢量图形
QPainterPath 不仅用于绘制路径,还可以进行路径运算(如 +, -, &, ^ 使用 QPainterPath::operator+ 等)、contains() 检测、boundingRect() 计算。
可以创建非常复杂的矢量形状,并高效地进行填充和描边。
应用场景:矢量图形编辑器、自定义图表、复杂的交互区域定义。
1.10 响应 DPI 变化进行自适应绘制
在高 DPI 屏幕上,硬编码的像素值会导致 UI 缩放异常。
在 paintEvent 中,使用 devicePixelRatioF() 获取设备的像素比,并据此调整绘制的坐标、大小和笔宽。
确保使用 QPainter 的 WorldTransform 或 ViewTransform 进行缩放,而不是手动计算。
void CustomWidget::paintEvent(QPaintEvent *event) {
QPainter painter(this);
qreal ratio = devicePixelRatioF();
painter.scale(ratio, ratio); // 应用缩放
// 后续绘制使用逻辑坐标 (1:1),Painter 会自动处理高 DPI
painter.drawRect(10, 10, 100, 50); // 这会在高 DPI 屏幕上自动放大
}
应用场景:所有需要在不同分辨率和 DPI 屏幕上正常显示的应用。