【从零开始的Qt开发指南】(二十四)Qt 界面优化之绘图实战:解锁自定义界面的无限可能
目录
五、特殊的绘图设备:QPixmap、QImage、QPicture
前言
在 Qt 开发中,自带的标准控件虽然能满足大部分基础需求,但面对个性化、高定制化的界面设计时,往往显得力不从心。想象一下,你需要实现一个炫酷的自定义仪表盘、动态变化的波形图,或是带有独特纹理的异形按钮 —— 这时,Qt 的绘图 API 就成了你的 “秘密武器”。
Qt 提供了一套强大且灵活的绘图框架,让开发者能够直接在窗口或控件上绘制任意图形、文字、图片,甚至实现复杂的动画效果。本文将从绘图的基本概念、各种形状绘制、图片操作、高级设置到特殊绘图设备,进行全方位、手把手的实战教学,带你从零掌握 Qt 绘图技巧,让你的界面突破标准控件的限制!下面就让我们正式开始吧!
一、Qt 绘图基本概念:理解 “画家、画板、画笔、画刷”
在开始绘图之前,我们需要先理解 Qt 绘图框架的核心组成 —— 四个关键类,它们的关系就像现实中的绘画场景,非常容易理解:
1.1 绘图核心四要素
| 类名 | 类比角色 | 核心作用 |
|---|---|---|
| QPainter | 画家 | 负责执行绘图操作,提供drawLine、drawRect、drawEllipse等一系列绘图方法 |
| QPaintDevice | 画板 | 绘图的载体,即 “画在哪里”,如 QWidget、QPixmap、QImage 等 |
| QPen | 画笔 | 控制线条的样式,包括颜色、宽度、线型(实线、虚线等) |
| QBrush | 画刷 | 控制封闭区域的填充效果,包括纯色、渐变、纹理等 |
简单来说:QPainter(画家)拿着 QPen(画笔)和 QBrush(画刷),在 QPaintDevice(画板)上创作。这四个类协同工作,构成了 Qt 绘图的基础。
1.2 绘图的关键:paintEvent 事件
与直接在构造函数中绘图不同,Qt 绘图必须在paintEvent事件中执行。这是因为paintEvent会在特定场景下自动触发,确保绘图效果始终保持正确:
- 控件首次创建时
- 控件被遮挡后重新显示时
- 窗口最小化后恢复时
- 控件大小发生变化时
- 主动调用
repaint()或update()方法时
如果在构造函数中绘图,一旦上述场景发生,绘制的内容会被自动清除,无法保持。因此,所有绘图代码都必须放在paintEvent的重写函数中。
1.3 第一个绘图程序:绘制一条直线
下面通过一个最简单的示例,演示 Qt 绘图的基本流程:
步骤 1:在头文件中声明 paintEvent 事件
// widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
protected:
// 声明绘图事件(必须重写此事件进行绘图)
void paintEvent(QPaintEvent *event) override;
};
#endif // WIDGET_H
步骤 2:在源文件中实现绘图逻辑
// widget.cpp
#include "widget.h"
#include <QPainter> // 包含绘图核心类头文件
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
}
Widget::~Widget()
{
}
void Widget::paintEvent(QPaintEvent *)
{
// 1. 创建画家对象,指定画板为当前窗口(this指针)
QPainter painter(this);
// 2. 绘制一条直线:从坐标(20,20)到(200,20)
painter.drawLine(QPoint(20, 20), QPoint(200, 20));
}
步骤 3:运行效果
编译并运行程序,会在窗口顶部看到一条水平直线。这就是 Qt 绘图的最基本流程:创建画家、指定画板、调用绘图方法。

二、绘制各种形状:从基础图形到自定义图案
Qt 的 QPainter 类提供了丰富的绘图方法,支持绘制直线、矩形、圆形、椭圆、文本等多种基础形状。下面我们逐一介绍这些形状的绘制方法,并结合画笔和画刷进行样式定制。
2.1 绘制直线(drawLine)
QPainter 提供了两种重载的drawLine方法,分别适用于不同场景:
方法 1:通过 QPoint 指定起点和终点
// 原型:void drawLine(const QPoint &p1, const QPoint &p2)
painter.drawLine(QPoint(20, 20), QPoint(200, 20)); // 水平直线
painter.drawLine(QPoint(20, 50), QPoint(200, 50)); // 另一条水平直线
方法 2:通过坐标值直接指定
// 原型:void drawLine(int x1, int y1, int x2, int y2)
painter.drawLine(20, 80, 200, 80); // 无需创建QPoint对象,更简洁
完整示例
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
// 绘制三条不同位置的水平直线
painter.drawLine(QPoint(20, 20), QPoint(200, 20));
painter.drawLine(20, 50, 200, 50);
painter.drawLine(QPoint(20, 80), QPoint(200, 80));
}
运行效果:窗口中显示三条平行的水平直线,间距为 30px。
2.2 绘制矩形(drawRect)
绘制矩形需要指定左上角坐标和矩形的宽高,drawRect方法的原型如下:
void drawRect(int x, int y, int width, int height);
x, y:矩形左上角在窗口中的坐标width, height:矩形的宽度和高度
示例:绘制一个简单矩形
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
// 绘制矩形:左上角(20,20),宽100px,高50px
painter.drawRect(20, 20, 100, 50);
}
运行效果:窗口左上角显示一个黑色边框的矩形(默认画笔样式)。
2.3 绘制圆形 / 椭圆(drawEllipse)
drawEllipse方法可以绘制椭圆,当椭圆的长半轴和短半轴相等时,就变成了圆形。其常用原型如下:
void drawEllipse(const QPoint ¢er, int rx, int ry);
center:椭圆的中心点坐标rx:水平方向半径(长半轴)ry:垂直方向半径(短半轴)
示例:绘制圆形
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
// 绘制圆形:中心点(100,100),水平半径50px,垂直半径50px
painter.drawEllipse(QPoint(100, 100), 50, 50);
}
运行效果:显示一个正圆形。

2.4 绘制文本(drawText)
QPainter 不仅能绘制图形,还能绘制文本,并支持自定义字体、颜色等样式。常用方法如下:
// 设置字体
void setFont(const QFont &font);
// 绘制文本:在指定矩形区域内显示文本
void drawText(const QRect &rect, const QString &text);
示例:绘制带样式的文本
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
// 1. 设置字体:华文行楷,24号字
QFont font("华文行楷", 24);
painter.setFont(font);
// 2. 设置画笔颜色(文本颜色由画笔决定)
painter.setPen(Qt::red);
// 3. 绘制文本:在矩形(100,200,600,150)内显示“天行健,君子以自强不息”
painter.drawText(QRect(100, 200, 600, 150), "天行健,君子以自强不息");
}
运行效果:窗口中显示红色、24 号华文行楷字体的文本,文本位于指定矩形区域内。

2.5 自定义画笔(QPen):控制线条样式
默认情况下,QPainter 使用黑色、1px 宽的实线画笔。通过 QPen 类,我们可以自定义画笔的颜色、宽度、线型等属性,让线条更具个性化。
QPen 的核心方法
| 方法 | 作用 | 示例值 |
|---|---|---|
| QPen(const QColor &color) | 构造函数:指定画笔颜色 | QColor (255, 0, 0)(红色) |
| setWidth(int width) | 设置画笔宽度 | 3(3px 宽) |
| setStyle(Qt::PenStyle) | 设置线型 | Qt::DashLine(虚线) |
常用线型(Qt::PenStyle)
| 线型常量 | 说明 |
|---|---|
| Qt::SolidLine | 实线(默认) |
| Qt::DashLine | 虚线(由短横线组成) |
| Qt::DotLine | 点线(由点组成) |
| Qt::DashDotLine | 点划线(交替的横线和点) |
| Qt::DashDotDotLine | 双点划线(横线 + 两个点的循环) |
| Qt::NoPen | 无线条(只填充内部,不绘制边框) |

示例:自定义画笔绘制圆形
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
// 1. 创建画笔:红色
QPen pen(QColor(255, 0, 0));
// 2. 设置画笔宽度:3px
pen.setWidth(3);
// 3. 设置线型:虚线
pen.setStyle(Qt::DashLine);
// 4. 让画家使用该画笔
painter.setPen(pen);
// 5. 绘制圆形
painter.drawEllipse(QPoint(100, 100), 50, 50);
}
运行效果:显示一个红色、3px 宽、虚线边框的圆形。

2.6 自定义画刷(QBrush):填充封闭区域
QBrush 用于填充封闭图形的内部区域,支持纯色、渐变、纹理等多种填充模式。其核心方法如下:
// 构造函数:指定填充颜色
QBrush(const QColor &color);
// 设置填充样式
void setStyle(Qt::BrushStyle style);
常用填充样式(Qt::BrushStyle)
| 样式常量 | 说明 |
|---|---|
| Qt::SolidPattern | 纯色填充(默认) |
| Qt::Dense1Pattern ~ Qt::Dense7Pattern | 不同密度的点填充 |
| Qt::HorPattern | 水平线条填充 |
| Qt::VerPattern | 垂直线条填充 |
| Qt::CrossPattern | 十字线填充 |
| Qt::LinearGradientPattern | 线性渐变填充 |
| Qt::RadialGradientPattern | 径向渐变填充 |
| Qt::TexturePattern | 纹理图片填充 |

示例:使用画刷填充圆形
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
// 1. 设置画笔(边框样式)
QPen pen(QColor(255, 0, 0));
pen.setWidth(3);
pen.setStyle(Qt::DashLine);
painter.setPen(pen);
// 2. 设置画刷(填充样式):青色,高密度点填充
QBrush brush(Qt::cyan);
brush.setStyle(Qt::Dense1Pattern);
painter.setBrush(brush);
// 3. 绘制圆形(会自动填充内部)
painter.drawEllipse(QPoint(100, 100), 50, 50);
}
运行效果:圆形边框为红色虚线,内部填充青色高密度点图案。

三、绘制图片:加载、平移、缩放与旋转
Qt 提供了 QPixmap、QImage 等类处理图片,其中 QPixmap 专门用于屏幕显示优化,是绘图中加载和显示图片的首选。下面我们介绍图片的基本绘制、平移、缩放、旋转等常用操作。
3.1 绘制简单图片(drawPixmap)
绘制图片的核心方法是drawPixmap,其原型如下:
void drawPixmap(int x, int y, const QPixmap &pixmap);
x, y:图片在窗口中的左上角坐标pixmap:要绘制的图片对象(通过文件路径加载)
实现步骤
步骤 1:准备图片资源
- 在项目文件夹中创建
picture文件夹,放入图片文件(如 4.jpg)。- 在 Qt Creator 中右键项目 -> 新建文件 -> Qt -> Qt Resource File,创建
res.qrc资源文件。- 打开
res.qrc,点击 “Add Prefix”(默认前缀为 “/”),再点击 “Add Files”,选择picture文件夹中的图片。
步骤 2:绘制图片代码
头文件:
// widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
protected:
void paintEvent(QPaintEvent *event) override;
};
#endif // WIDGET_H
源文件:
// widget.cpp
#include "widget.h"
#include <QPainter>
#include <QPixmap>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
}
Widget::~Widget()
{
}
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
// 加载图片(资源文件路径::/picture/4.jpg)
QPixmap pixmap(":/picture/4.jpg");
// 绘制图片:左上角坐标(0,0)
painter.drawPixmap(0, 0, pixmap);
}
运行效果:图片从窗口左上角开始显示,尺寸与原图一致。

3.2 平移图片(translate)
图片平移本质上是改变绘图坐标的原点,通过 QPainter 的translate方法实现:
// 平移坐标原点:将原点移动到(x, y)
void translate(int x, int y);
示例:平移图片
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
// 1. 平移坐标原点:向右平移100px,向下平移100px
painter.translate(100, 100);
// 2. 绘制图片:此时(0,0)对应原坐标的(100,100)
painter.drawPixmap(0, 0, QPixmap(":/picture/4.jpg"));
}
运行效果:图片从原坐标 (100,100) 位置开始显示,相当于向右下方平移了 100px。

3.3 缩放图片
缩放图片可以通过drawPixmap的重载方法实现,直接指定绘制后的图片尺寸。
void drawPixmap(int x, int y, int width, int height, const QPixmap &pixmap);
width, height:缩放后的图片宽度和高度
示例:同时显示原图和缩放图
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
QPixmap pixmap(":/picture/4.jpg");
// 1. 绘制原图:左上角(0,0),尺寸与原图一致
painter.drawPixmap(0, 0, pixmap);
// 2. 绘制缩放图:左上角(300,400),尺寸缩放到50x60px
painter.drawPixmap(300, 400, 50, 60, pixmap);
}
运行效果:窗口左上角显示原图,右下角显示缩小后的图片。

3.4 旋转图片(rotate)
图片旋转通过rotate方法实现,默认以坐标原点为旋转中心。如果需要以图片中心为旋转中心,需要结合translate方法调整坐标。
方法原型
// 顺时针旋转angle度(角度制)
void rotate(qreal angle);
示例:以图片中心为旋转中心旋转 90 度
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
QPixmap pixmap(":/picture/4.jpg");
// 1. 获取图片尺寸
int pixWidth = pixmap.width();
int pixHeight = pixmap.height();
// 2. 平移坐标原点到图片中心(目标旋转中心)
painter.translate(200, 300);
// 3. 顺时针旋转90度
painter.rotate(90);
// 4. 还原坐标原点(确保图片左上角对齐旋转中心)
painter.translate(-200, -300);
// 5. 绘制图片
painter.drawPixmap(200 - pixHeight/2, 300 - pixWidth/2, pixmap);
}
关键步骤解析
- 先将坐标原点平移到目标旋转中心(如 (200,300))。
- 执行旋转操作(旋转 90 度)。
- 将坐标原点还原,避免后续操作受影响。
- 绘制图片时,调整图片位置,确保旋转中心与图片中心重合。
运行效果:图片以 (200,300) 为中心顺时针旋转 90 度。

四、绘图高级设置:移动画家、保存与恢复状态
在复杂绘图场景中,我们经常需要绘制多个图形,或对画家状态进行临时调整。Qt 提供了移动画家位置、保存 / 恢复画家状态等功能,让绘图更灵活。
4.1 移动画家位置(translate)
除了用于图片平移,translate方法还可以用于移动画家位置,避免多个图形重合。
示例:绘制两个不重合的圆形
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
// 1. 绘制第一个圆:中心点(100,200)
painter.drawEllipse(QPoint(100, 200), 50, 50);
// 2. 移动画家位置:向右平移200px
painter.translate(200, 0);
// 3. 绘制第二个圆:此时(100,200)对应原坐标(300,200)
painter.drawEllipse(QPoint(100, 200), 50, 50);
}
运行效果:窗口中显示两个并排的圆形,间距为 200px,不会重合。

4.2 保存与恢复画家状态(save/restore)
当我们临时修改了画家的画笔、画刷、坐标等状态后,可能需要恢复到之前的状态。QPainter 提供了save和restore方法,通过栈结构管理状态:
save():保存当前画家状态(压入栈)。restore():恢复到最近一次保存的状态(弹出栈)。
示例:保存状态后绘制多个图形
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
// 1. 绘制第一个圆(默认状态)
painter.drawEllipse(QPoint(100, 200), 50, 50);
// 2. 移动画家位置并保存状态
painter.translate(200, 0);
painter.save(); // 保存“平移200px”后的状态
// 3. 绘制第二个圆(平移后的位置)
painter.drawEllipse(QPoint(100, 200), 50, 50);
// 4. 再次移动画家位置
painter.translate(200, 0);
// 5. 恢复到最近保存的状态(平移200px的状态)
painter.restore();
// 6. 绘制第三个圆(恢复后的位置,与第二个圆重合)
painter.drawEllipse(QPoint(100, 200), 50, 50);
}
运行效果
- 第一个圆在原坐标 (100,200)。
- 第二个圆在原坐标 (300,200)(平移 200px)。
- 第三个圆恢复到平移 200px 的状态,与第二个圆重合,最终显示两个圆。
应用场景
- 临时修改画笔 / 画刷样式后,需要恢复默认样式。
- 多次平移、旋转后,需要回到之前的坐标状态。
五、特殊的绘图设备:QPixmap、QImage、QPicture
除了 QWidget,Qt 还提供了三种特殊的绘图设备,分别适用于不同场景:
- QPixmap:优化屏幕显示,适合绘制到控件上。
- QImage:支持像素级操作,适合图片处理。
- QPicture:记录绘图指令,适合存档和重演。
5.1 QPixmap:屏幕显示优化
QPixmap 是专门为屏幕显示设计的绘图设备,与系统显示硬件紧密相关,显示效率高。其核心特性:
- 支持直接加载图片文件。
- 可作为 QPainter 的画板,绘制图形后保存为图片文件。
- 不同系统下显示效果可能略有差异(与系统渲染机制相关)。
示例:使用 QPixmap 绘制图形并保存
// widget.cpp
#include "widget.h"
#include <QPainter>
#include <QPixmap>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
// 1. 创建QPixmap绘图设备:尺寸500x500
QPixmap pix(500, 500);
// 2. 创建画家,指定画板为pix
QPainter painter(&pix);
// 3. 设置画笔颜色为红色
painter.setPen(Qt::red);
// 4. 绘制圆形:中心点(100,100),半径100px
painter.drawEllipse(QPoint(100, 100), 100, 100);
// 5. 保存绘制结果到文件(C盘Test_Pic文件夹)
pix.save("C:\\Users\\Lenovo\\Desktop\\Test_Pic\\pix.png");
}
void Widget::paintEvent(QPaintEvent *)
{
// 此处无需额外绘图,上述操作在构造函数中执行
}
运行效果
程序运行后,会在C:\Users\Lenovo\Desktop\Test_Pic文件夹中生成pix.png文件,图片中包含一个红色边框的圆形。

5.2 QImage:像素级操作
QImage 是独立于硬件的绘图设备,支持直接访问和修改像素,适合图片处理场景。其核心特性:
- 支持像素级操作(
setPixel方法)。- 不同系统下显示效果一致(跨平台兼容性好)。
- 适合 I/O 操作(加载、保存图片)。
示例 1:使用 QImage 绘制图形并保存
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
// 1. 创建QImage绘图设备:尺寸500x500,格式RGB32(32位彩色)
QImage img(500, 500, QImage::Format_RGB32);
// 2. 填充背景色为白色(默认背景为黑色)
img.fill(Qt::white);
// 3. 创建画家,指定画板为img
QPainter painter(&img);
// 4. 设置画笔颜色为青色
painter.setPen(QPen(Qt::cyan));
// 5. 绘制圆形:中心点(200,200),半径100px
painter.drawEllipse(QPoint(200, 200), 100, 100);
// 6. 保存图片
img.save("C:\\Users\\Lenovo\\Desktop\\Test_Pic\\img.jpg");
}
运行结果:

示例 2:修改图片像素
头文件:
// widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
protected:
void paintEvent(QPaintEvent *event) override;
};
#endif // WIDGET_H
源文件:
// widget.cpp
#include "widget.h"
#include <QPainter>
#include <QImage>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
}
Widget::~Widget()
{
}
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
// 1. 加载图片
QImage img;
img.load(":/picture/3.jpg");
// 2. 修改像素:将坐标(100~200, 100~200)的区域改为蓝色
for (int i = 100; i < 200; i++) {
for (int j = 100; j < 200; j++) {
// qRgb(0,0,255)表示蓝色
QRgb rgb = qRgb(0, 0, 255);
img.setPixel(i, j, rgb);
}
}
// 3. 绘制修改后的图片
painter.drawImage(0, 0, img);
}
运行效果
示例 1 生成一张白色背景、青色圆形的图片;

示例 2 加载原图后,将中间 100x100px 的区域改为蓝色并显示。

5.3 QPicture:记录绘图指令
QPicture 是一种特殊的绘图设备,它不存储图片像素,而是记录 QPainter 的绘图指令(如drawLine、drawRect等),后续可以通过这些指令重演绘图过程。其核心特性:
- 记录绘图指令,文件体积小。
- 跨平台兼容性好,重演效果一致。
- 只能加载 QPicture 格式的文件(.pic),不能加载 png、jpg 等普通图片。
示例:记录绘图指令并重演
头文件:
// widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
protected:
void paintEvent(QPaintEvent *event) override;
};
#endif // WIDGET_H
源文件:
// widget.cpp
#include "widget.h"
#include <QPainter>
#include <QPicture>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
// 1. 创建QPicture对象
QPicture pic;
// 2. 创建画家,开始记录绘图指令
QPainter painter;
painter.begin(&pic);
// 3. 绘制图形(指令会被记录到pic中)
painter.setPen(QPen(Qt::red));
painter.drawEllipse(QPoint(200, 200), 100, 100);
// 4. 结束记录
painter.end();
// 5. 保存记录的指令到文件
pic.save("C:\\Users\\Lenovo\\Desktop\\Test_Pic\\pic.pic");
}
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
// 1. 创建QPicture对象,加载保存的指令文件
QPicture pic;
pic.load("C:\\Users\\Lenovo\\Desktop\\Test_Pic\\pic.pic");
// 2. 重演绘图指令
painter.drawPicture(0, 0, pic);
}
运行效果
程序运行时,会先记录绘制红色圆形的指令并保存为pic.pic文件,然后在paintEvent中重演该指令,在窗口中显示红色圆形。


应用场景
- 复杂绘图的存档与回放(如数据可视化图表的历史记录)。
- 跨平台的绘图指令传输(无需担心像素格式差异)。
总结
通过本文的学习,相信你已经掌握了 Qt 绘图的核心技巧和实战方法。绘图是 Qt 界面优化的重要手段,能够帮助你实现各种个性化、高定制化的界面需求。建议在实际项目中多动手实践,结合具体场景灵活运用画笔、画刷、坐标变换等功能,不断提升绘图能力。祝你在 Qt 开发之路上,打造出更多炫酷、实用的自定义界面!
至此有关于Qt开发的基础知识已经全部为大家介绍完毕!后续我还会带大家利用Qt从零开始搭建一个企业级项目,敬请期待!
更多推荐













所有评论(0)