4. QPainter绘图基础:从零开始画点线面
说实话,很多初学者一听到QPainter就觉得头大。我当年也是这样,觉得绘图嘛,不就是画几个形状?直到我在项目中遇到一个自定义仪表盘的需求,才发现QPainter才是Qt自定义控件的灵魂。今天我们就从最基础的东西开始聊。
4.1 QPainter是什么?
QPainter,说白了就是Qt里的画师。你给它一支笔(QPen),它就能画线;你给它一桶漆(QBrush),它就能填色。它负责在QWidget、QPixmap、QImage这些"画布"上作画。
我习惯把QPainter理解成一个"状态机"。你设置好画笔颜色、粗细,画几笔;再换个画刷,填个色。每次设置都会改变它的内部状态,直到你主动改变它。
核心要点:QPainter必须在paintEvent()中使用,而且记得用begin()和end()包裹绘图操作。嗯,这个坑我踩过——在别的地方调用QPainter,程序直接崩溃。
4.2 画笔(QPen)——你的绘图工具
QPen控制的是线条的样式。包括颜色、宽度、线型、端点样式、连接样式。我在项目中调过最细的线是1像素,最粗的线画过20像素的边框。
来看看常用的设置:
// 创建画笔
QPen pen;
pen.setColor(Qt::red); // 红色
pen.setWidth(3); // 3像素宽
pen.setStyle(Qt::DashLine); // 虚线
pen.setCapStyle(Qt::RoundCap); // 圆头端点
pen.setJoinStyle(Qt::RoundJoin); // 圆角连接
// 或者一步到位
QPen pen2(QColor(255, 100, 50), 5, Qt::SolidLine);
| 属性 | 常用值 | 说明 |
|---|---|---|
| 颜色 | Qt::red, QColor(255,0,0) | 支持预定义颜色和RGB |
| 宽度 | 1, 2, 5 | 单位是像素 |
| 线型 | SolidLine, DashLine, DotLine | 实线、虚线、点线 |
| 端点样式 | FlatCap, SquareCap, RoundCap | 平头、方头、圆头 |
| 连接样式 | MiterJoin, BevelJoin, RoundJoin | 尖角、斜角、圆角 |
小技巧:我习惯把常用画笔存成成员变量,避免在paintEvent里反复创建。比如一个红色虚线笔、一个蓝色实线笔,提前准备好,画的时候直接setPen就行。
4.3 画刷(QBrush)——填色大师
画刷负责填充封闭区域。可以是纯色、渐变色,甚至是纹理图案。我记得有一次做温度计控件,用QLinearGradient画刷实现了从蓝到红的渐变效果,看起来特别专业。
// 纯色画刷
QBrush brush(Qt::blue);
// 渐变画刷
QLinearGradient gradient(0, 0, 100, 100);
gradient.setColorAt(0.0, Qt::white);
gradient.setColorAt(1.0, Qt::black);
QBrush gradientBrush(gradient);
// 纹理画刷
QPixmap texture(":/images/pattern.png");
QBrush textureBrush(texture);
画刷的样式也很丰富:SolidPattern(纯色)、DensePattern(密集点)、HorPattern(水平线)、VerPattern(垂直线)等等。你想想看,一个按钮的背景,用画刷填充渐变,比贴图灵活多了。
4.4 绘制基本图形
好,工具准备好了,我们开始画。QPainter提供了十几个绘图函数,今天先讲最常用的四个:点、线、矩形、椭圆。
4.4.1 画点
画点用drawPoint()。单个点可能看不出效果,但配合循环就能画出散点图、波形图。我曾经用这个功能画过一个实时数据监控的折线图,每个数据点就是一个像素点。
void MyWidget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.setPen(QPen(Qt::red, 5));
// 画一个点
painter.drawPoint(100, 100);
// 画一排点
for(int i = 0; i < 10; i++) {
painter.drawPoint(50 + i * 20, 200);
}
}
4.4.2 画线
画线用drawLine()。可以画单条线,也可以用drawLines()画多条线。我个人更喜欢用QPainterPath来画复杂路径,但单条线用drawLine最直接。
// 画一条线
painter.drawLine(10, 10, 200, 10);
// 画多条线
QLine lines[3] = {
QLine(10, 30, 200, 30),
QLine(10, 50, 200, 50),
QLine(10, 70, 200, 70)
};
painter.drawLines(lines, 3);
注意:drawLine的参数是起点和终点的坐标。我曾经犯过一个错误,把宽度和高度当成了终点坐标,结果画出来的线歪到天边去了。记住,是(x1,y1)到(x2,y2)。
4.4.3 画矩形
矩形有两种:普通矩形drawRect()和圆角矩形drawRoundedRect()。圆角矩形在UI设计中特别常用,按钮、卡片、对话框都用它。
// 普通矩形
painter.drawRect(50, 50, 200, 100);
// 圆角矩形
painter.drawRoundedRect(50, 200, 200, 100, 10, 10);
// 填充矩形
painter.setBrush(QBrush(Qt::green));
painter.drawRect(300, 50, 200, 100);
这里要注意,drawRect的参数是(x, y, width, height)。x和y是左上角坐标,width和height是宽高。嗯,这个和很多图形库是一致的。
4.4.4 画椭圆
画椭圆用drawEllipse()。如果宽高相等,画出来的就是正圆。椭圆在仪表盘、进度环、气泡提示里经常出现。
// 椭圆
painter.drawEllipse(50, 50, 200, 100);
// 正圆
painter.drawEllipse(300, 50, 100, 100);
// 填充椭圆
painter.setBrush(QBrush(Qt::yellow));
painter.drawEllipse(50, 200, 150, 80);
你想想看,一个圆形进度条,其实就是用drawEllipse画一个圆,再用drawArc画一段弧。组合起来就是漂亮的进度效果。
4.5 知识体系总览
为了让你更直观地理解本章的知识结构,我画了一张图:
4.6 避坑指南
最后分享几个我踩过的坑:
- 忘记调用begin()和end():QPainter的构造函数会自动调用begin(),但如果你手动创建,一定要记得配对。我曾经在析构函数里忘记end(),导致程序退出时崩溃。
- 在paintEvent外绘图:QPainter只能在paintEvent里使用。想在外部更新?调用update()触发重绘。
- 坐标搞混:记住,Qt的坐标原点在左上角,x向右增加,y向下增加。和数学里的坐标系不一样。
- 性能问题:不要在paintEvent里做复杂计算。我见过有人在绘图时实时计算sin值,结果界面卡成PPT。提前算好,存起来,绘图时只管画。
我的习惯:每次写paintEvent,我都会先保存painter的状态(save()),画完再恢复(restore())。这样即使中间改了画笔颜色,也不会影响后续绘图。算是个好习惯吧。
好了,QPainter的基础就这些。点、线、矩形、椭圆,看着简单,但组合起来能做出很酷的效果。下一节我们会深入QPainter的高级功能,比如渐变、路径、变换等。到时候你会发现,原来自定义控件可以这么好玩。
可可精品资料库