第十六章:QT绘图——QPainter、QPen、QBrush与图形绘制

绘图,说白了就是让程序能「画东西」。在QT里,这件事主要由三个类搞定:QPainter(画家)、QPen(笔)和QBrush(刷子)。

我刚开始学QT绘图时,总觉得这玩意儿不就是画个圆、画个方块吗?后来做项目才发现,绘图的水很深——从简单的控件自绘,到复杂的图表、游戏界面,都离不开它。今天我们就把它彻底搞明白。

16.1 QPainter:你的画板

QPainter是QT绘图的核心。它就像你手里的画笔,可以在任何「画布」(QPaintDevice)上作画。常见的画布有QWidget、QPixmap、QImage等。

使用QPainter有个固定套路:

void MyWidget::paintEvent(QPaintEvent *)
{
    QPainter painter(this);  // 在当前的widget上画
    painter.setPen(Qt::blue);
    painter.drawText(20, 20, "Hello, QT!");
}

嗯,这里要注意:绘图代码必须写在paintEvent()里。你不能随便找个函数就开始画,系统会在需要重绘时自动调用paintEvent()。如果你强行在其他地方画,画完一刷新就没了——我当年踩过这个坑。

小技巧: 如果你想让某个控件立即重绘,调用update()而不是直接调用paintEvent()。update()会触发系统在合适的时机重绘,效率更高。

16.2 QPen:线条的灵魂

QPen控制线条的样式:颜色、宽度、线型(实线、虚线、点线等)。

QPen pen;
pen.setColor(QColor(255, 0, 0));   // 红色
pen.setWidth(3);                    // 3像素宽
pen.setStyle(Qt::DashLine);         // 虚线
painter.setPen(pen);

我在项目中遇到过一个问题:画出来的线总是模糊的。后来发现是坐标没对齐——奇数宽度的线,坐标要偏移0.5像素,否则线条边缘会模糊。你想想看,像素是整数格子,线宽3像素时,如果坐标在整数位置,线条会跨越两个像素格子,导致抗锯齿效果变差。

线型枚举 说明 效果
Qt::SolidLine 实线 ——————
Qt::DashLine 虚线 - - - - - -
Qt::DotLine 点线 · · · · · ·
Qt::DashDotLine 点划线 -·-·-·-·

16.3 QBrush:填充的艺术

QBrush负责填充封闭图形的内部。可以是纯色、渐变色,甚至是纹理图案。

QBrush brush;
brush.setColor(Qt::green);
brush.setStyle(Qt::Dense7Pattern);  // 密集的斜线纹理
painter.setBrush(brush);
painter.drawRect(50, 50, 100, 80);

常用的填充样式有:

  • Qt::SolidPattern:纯色填充
  • Qt::HorPattern:水平线纹理
  • Qt::VerPattern:垂直线纹理
  • Qt::CrossPattern:交叉线纹理
  • Qt::Dense1Pattern ~ Dense7Pattern:不同密度的点阵纹理
核心要点: QPen和QBrush是独立的。你可以只设笔不设刷子(只画轮廓),也可以只设刷子不设笔(只填充)。两者都设,就是既有轮廓又有填充。

16.4 绘制基本图形

QT提供了丰富的绘图API,常用的有:

// 画直线
painter.drawLine(QPoint(10, 10), QPoint(100, 100));

// 画矩形
painter.drawRect(20, 20, 80, 60);

// 画圆角矩形
painter.drawRoundedRect(20, 20, 80, 60, 10, 10);

// 画椭圆(圆是椭圆的特例)
painter.drawEllipse(QPoint(100, 100), 50, 30);

// 画圆弧
painter.drawArc(QRect(20, 20, 100, 100), 0, 90 * 16);

// 画多边形
QPolygon polygon;
polygon << QPoint(10, 10) << QPoint(100, 10) << QPoint(50, 80);
painter.drawPolygon(polygon);

我个人习惯把常用图形的绘制封装成工具函数,比如:

void drawStar(QPainter &painter, QPoint center, int radius)
{
    QPolygon star;
    for (int i = 0; i < 5; ++i) {
        double angle = -M_PI / 2 + i * 2 * M_PI / 5;
        star << QPoint(center.x() + radius * cos(angle),
                        center.y() + radius * sin(angle));
        angle += M_PI / 5;
        star << QPoint(center.x() + radius * 0.4 * cos(angle),
                        center.y() + radius * 0.4 * sin(angle));
    }
    painter.drawPolygon(star);
}

这样在paintEvent里直接调用就行,代码清爽很多。

16.5 渐变与变换

渐变让图形看起来更立体、更专业。QT支持三种渐变:

  • QLinearGradient:线性渐变,从一点到另一点颜色逐渐变化
  • QRadialGradient:径向渐变,从中心向外扩散
  • QConicalGradient:锥形渐变,围绕中心旋转
// 线性渐变
QLinearGradient gradient(0, 0, 100, 100);
gradient.setColorAt(0.0, Qt::red);
gradient.setColorAt(0.5, Qt::yellow);
gradient.setColorAt(1.0, Qt::green);
painter.setBrush(gradient);
painter.drawRect(0, 0, 100, 100);

变换则是对整个坐标系进行操作。常用的有平移、旋转、缩放:

painter.save();              // 保存当前状态
painter.translate(100, 50);  // 平移
painter.rotate(45);          // 旋转45度
painter.scale(1.5, 1.5);     // 缩放1.5倍
painter.drawRect(0, 0, 50, 50);
painter.restore();           // 恢复状态
警告: 变换会影响后续所有绘图操作。记得用save()和restore()包围变换代码,否则你会画出一堆歪歪扭扭的东西。我曾经在画一个仪表盘时忘了restore,结果指针和刻度全乱套了。

16.6 双缓冲绘图

双缓冲是解决绘图闪烁问题的标准方案。原理很简单:先在内存里画好,再一次性贴到屏幕上。

为什么会闪烁?因为直接在屏幕上画时,你画一条线,屏幕就刷新一次。画100条线,屏幕就闪100次。双缓冲相当于先在后台画完,然后「啪」一下贴上去,用户只看到最终结果。

QT里实现双缓冲有两种方式:

方式一:使用QPixmap

void MyWidget::paintEvent(QPaintEvent *)
{
    QPixmap buffer(size());
    buffer.fill(Qt::white);

    QPainter bufferPainter(&buffer);
    // 在缓冲区画各种图形
    bufferPainter.drawLine(...);
    bufferPainter.drawRect(...);

    // 最后一次性贴到屏幕上
    QPainter painter(this);
    painter.drawPixmap(0, 0, buffer);
}

方式二:使用QWidget的autoFillBackground

其实QT的QWidget默认就支持双缓冲。你只要在paintEvent里正常画就行,QT内部会自动处理。但如果你自己用QPainter直接画在屏幕上(比如在鼠标事件里画),那就需要手动实现双缓冲。

我建议: 对于复杂的绘图(比如实时图表、游戏画面),一定要用双缓冲。对于简单的静态界面,QT默认的双缓冲就够了。别过度设计。

知识体系总览

下面这张图帮你理清本章的核心逻辑:

QT绘图系统 QPainter(画家) QPen(笔) QBrush(刷子) 基本图形 直线、矩形、椭圆 圆弧、多边形 渐变与变换 线性/径向/锥形渐变 平移/旋转/缩放 双缓冲 QPixmap缓冲 避免闪烁 paintEvent() 是绘图的唯一入口 所有绘图代码必须写在这里面

这张图把本章的核心知识点串起来了。你从QPainter入手,配合QPen和QBrush,就能画出各种基本图形。再加上渐变和变换,图形会更有层次感。最后用双缓冲解决性能问题——一套组合拳下来,QT绘图你就拿下了。


公众号:蓝海资料掘金营,微信deep3321