一、Qt绘图系统概述
说到Qt绘图系统,我得先跟你聊聊它的定位。
Qt绘图系统,说白了就是一套让你能在屏幕上「画东西」的框架。你想想看,不管是自定义控件、图表展示,还是游戏界面,最终都要落实到「怎么画出来」这个问题上。Qt把这套能力封装得相当优雅,我用了这么多年,真心觉得它设计得漂亮。
1.1 什么是Qt绘图系统
Qt绘图系统是一套基于QPainter的2D图形渲染框架。它不依赖任何第三方图形库,完全基于Qt自身实现。你可以在任何QPaintDevice上绘制——无论是窗口、图片,还是打印机。
我记得刚接触Qt时,有个困惑:为什么Qt不直接用OpenGL或者DirectX?后来才明白,Qt绘图系统追求的是跨平台一致性。你在Windows上画一个圆,到了macOS上还是那个圆,像素级一致。这一点,在实际项目中太重要了。
核心特点:
- 纯软件渲染,不依赖GPU(当然也可以结合OpenGL)
- 跨平台绘制结果一致
- 支持反锯齿、渐变、透明度等高级效果
- 与Qt事件系统、布局系统无缝集成
1.2 绘图系统的三大核心组件
Qt绘图系统由三个核心类构成,它们各司其职,配合默契。我习惯把它们比作「画家、画布和画笔引擎」。
| 组件 | 类名 | 角色 |
|---|---|---|
| 画家 | QPainter | 执行绘制操作 |
| 画布 | QPaintDevice | 提供绘制表面 |
| 画笔引擎 | QPaintEngine | 底层渲染实现 |
QPainter — 画家
QPainter是你要直接打交道的类。它提供了所有绘制接口:画线、画矩形、画圆、画文字、画路径……基本上你能想到的2D图形,它都有。
我个人的习惯是:永远在paintEvent()里创建QPainter。别在别的地方用,否则你会遇到各种奇怪的绘制问题。
小技巧:QPainter的状态机设计很巧妙。你可以用save()/restore()来保存和恢复绘制状态,比如画笔颜色、变换矩阵等。这在绘制复杂图形时特别有用。
QPaintDevice — 画布
QPaintDevice是所有可绘制对象的基类。常见的子类有:
- QWidget — 窗口和控件
- QPixmap — 屏幕优化过的图像
- QImage — 独立于硬件的图像,支持像素级操作
- QPicture — 记录和回放绘制命令
- QPrinter — 打印机
这里有个坑,我曾经踩过:QPixmap和QImage看起来差不多,但QPixmap是存储在显存里的,适合频繁绘制;QImage在内存里,适合像素操作。如果你要逐像素修改图像,用QImage;如果只是显示,用QPixmap。
QPaintEngine — 画笔引擎
QPaintEngine是底层实现,你一般不会直接用它。它负责把QPainter的绘制命令转换成具体的图形输出。不同的QPaintDevice对应不同的引擎实现。
嗯,这里要注意:不要直接调用QPaintEngine的方法。它是内部实现细节,Qt官方也不保证API稳定。我见过有人为了性能直接操作引擎,结果Qt升级后代码全废了。
1.3 绘图的基本流程
搞清楚了组件,咱们来看看绘图到底怎么走。流程其实很简单,就三步:
- 创建QPainter对象,绑定到QPaintDevice上
- 调用绘制方法,比如drawRect()、drawText()
- 销毁QPainter对象,结束绘制
看个最简单的例子:
void MyWidget::paintEvent(QPaintEvent *)
{
QPainter painter(this); // 第一步:创建画家,绑定到当前窗口
painter.setPen(Qt::blue); // 设置蓝色画笔
painter.setBrush(Qt::yellow); // 设置黄色画刷
painter.drawRect(50, 50, 200, 100); // 第二步:画矩形
painter.drawText(100, 100, "Hello Qt"); // 画文字
// 第三步:painter析构,自动结束绘制
}
你可能会问:为什么非要析构才结束?因为QPainter的构造函数里会调用begin(),析构函数里会调用end()。end()会做一些清理工作,比如刷新缓冲区。如果你手动调用begin()/end(),也可以,但我建议直接用RAII方式——让对象生命周期管理绘制周期。
警告:千万不要在paintEvent()之外创建QPainter并绘制到QWidget上。这会导致绘制不可控,甚至程序崩溃。我曾经在定时器里直接画,结果界面闪烁得跟迪斯科一样。
1.4 知识体系总览
为了让你对本章内容有个整体印象,我画了张图:
这张图把本章的核心内容串起来了。左边是三大组件,右边是绘制流程。你记住这个结构,后面学起来就清晰多了。
1.5 避坑指南
最后,分享几个我实际项目中遇到的坑:
我曾经犯过的错:
- 在非UI线程里调用QPainter绘制QWidget — 界面直接崩了。记住:QPainter只能在GUI线程使用。
- 忘记调用QPainter::end()就重复begin() — 会触发断言失败。用RAII方式能避免。
- 在paintEvent()里new QPainter但不delete — 内存泄漏。用栈对象最安全。
好了,第一章就到这里。Qt绘图系统其实不复杂,核心就是理解那三个组件怎么配合。你把这个搞懂了,后面学具体绘制技巧就水到渠成。
可可精品资料库