【Qt】绘图

目录

[一. 核心类介绍](#一. 核心类介绍)

[二. 注意事项](#二. 注意事项)

三.绘制各种形状

3.1.绘制线段

3.2.绘制矩形

3.3.绘制椭圆

3.4.绘制文本

三.设置画笔

四.设置画刷

五.绘制图片


嘿,小伙伴,咱们之前学 Qt 不都是在跟各种控件打交道嘛,什么按钮、文本框之类的。说白了,这些玩意儿本质上都是 Qt 用代码"画"出来的。

因为它们太常用了,Qt 就提前帮咱们都画好了,咱们直接拿来用就行,特别省事儿。

但你在实际做项目的时候,肯定会遇到这种情况:哎,Qt 自带的这些控件,好像不够酷啊,或者根本实现不了我想要的那个效果。这时候咋办呢?

别担心,Qt 早就替咱们想好了!它提供了一整套画画的"神笔"------就是绘图 API。用上它,你就能自己动手,丰衣足食,想画啥就画啥,彻底放飞自我,做出各种炫酷的自定义控件和效果。

不过咱也得实话实说,在大部分日常开发里,Qt 现成的控件已经完全够用了。所以你也不用急着把所有绘图 API 都啃下来,等真需要的时候,再来学这部分也完全来得及!


一. 核心类介绍

绘画涉及到几个核心的类

你可以把它们想象成一个画家在画室里工作的完整场景:

  1. QPainter - "画家"本人

这家伙就是整个绘图过程的核心执行者,是那个真正动手画画的人。

  • 他什么都会画:画直线、画方框、画圆圈、写字、贴图片......十八般武艺样样精通。

  • 他干活需要听你指挥,你让他画什么,他就画什么。

  • 但是,他不能凭空作画,他必须得有个地方 可以画,比如一块画布或者一面墙。而且他需要你给他提供合适的工具,比如一支笔和一桶颜料。

简单说:他就是那个负责"动手画"的人。

  1. QPaintDevice - "画板"或"画布"

这就是画家作画的载体 ,是那个实实在在的、能让他下笔的表面

  • 它定义了"画在哪里"这个问题。

  • 它可以是各种各样的东西:比如你电脑上的一个窗口、一张内存里的图片,或者是一块专门用来做3D渲染的特殊区域。

  • 无论画家多么厉害,他都得有一块这样的"画板"才能施展才华。没有画板,画家就无用武之地。

简单说:它就是画家作画的"地盘"或"场地"。

  1. QPen - "画笔"

这就是画家手里拿的那支 ,它专门负责决定图形轮廓的样子。

  • 它决定了线条的颜色(是用黑色墨水还是红色彩笔?)。

  • 它决定了线条的粗细(是细细的签字笔还是粗粗的马克笔?)。

  • 它还决定了线条的样式(是画一条实线,还是一串虚线或者点线?)。

  • 它甚至能决定线条拐角和末尾的形状(是方方正正的,还是圆润的?)。

简单说:它管的是所有"线条和边框"长什么样。

  1. QBrush - "画刷"或"油漆桶"

当画家画好一个封闭图形的边框后(比如一个方框的内部),这个工具就上场了。它负责填充这个图形内部的区域。

  • 它最简单的形式就是一桶单一颜色的油漆,比如把方框里面涂成纯黄色。

  • 它也可以很高级,比如像一把能做出渐变效果的刷子,让颜色从蓝色平滑地过渡到绿色。

  • 它甚至能像一块印花滚筒,用预设的图案或者一张图片来填充区域,比如填充出木纹或者网格的效果。

简单说:它管的是所有"图形内部"怎么填充才好看。

把它们串起来:一个完整的工作流程

现在,我们把它们放到一起,看看是怎么配合的:

  1. 你先给画家(QPainter )找好一块画布(QPaintDevice),比如一个空白的窗口。

  2. 然后,你递给他一支精心挑选的画笔(QPen),告诉他:"用这支2毫米粗的蓝色虚线笔画轮廓。"

  3. 接着,你再递给他一个画刷(QBrush),告诉他:"用这个能画出红到黄渐变的刷子来填充内部。"

  4. 最后,你给画家下命令:"好了,现在在这个画布上,给我画一个圆角矩形。"

  5. 画家听令,用你给的蓝色虚线笔勾出轮廓,再用渐变刷子把里面填满,一个漂亮的图形就出现在窗口上了。


二. 注意事项

一个关键的注意事项是:与绘制相关的操作通常不建议放在 QWidget 的构造函数中执行。

这是因为在构造阶段,控件尚未完成初始化和显示,此时进行绘制是无效的。

Qt 为此专门提供了一个 paintEvent 事件处理函数,我们应在此函数中执行所有绘制逻辑。

与之对应的是 QPaintEvent 事件,它在以下几种典型情况下会被触发,从而引起重绘:

  1. 控件首次创建完成并显示时

    比如在 QWidget 上绘制内容,必须在控件构造完成并显示之后才会生效。paintEvent 会在首次显示时被调用,确保绘制内容正确呈现。

  2. 控件被遮挡后重新显露时

    如果控件之前被其他窗口或元素遮挡,当遮挡物移开时,系统会触发重绘,以保证之前被遮挡部分的内容能够正确恢复显示。

  3. 窗口从最小化状态还原时

    窗口最小化后再恢复显示,其内容需要重新绘制,此时也会触发 paintEvent

  4. 控件大小发生变化时

    当控件尺寸改变(如用户拖拽调整窗口大小),通常需要重新绘制以适应新的尺寸,此时也会自动调用 paintEvent

  5. 主动在代码中调用 repaint() 或 update() 时

    我们可以在代码中主动调用 repaint()(立即重绘)或 update()(异步调度重绘)来触发 paintEvent,从而实现手动刷新界面。

因此,将绘制代码统一放置在 paintEvent 中,能够确保界面在各种情况下都能正确、及时地更新显示内容,避免出现内容丢失或显示异常的问题。

两位"重绘助手":repaint() 和 update()

它们都是 QWidget 的成员函数,作用都是最终触发 paintEvent,但脾气完全不同:

  • repaint() - "急性子"

行为:它一被调用,立即、强制地产生一个绘制事件,并且会阻塞当前正在执行的代码,直到 paintEvent 函数执行完毕,画面画好了,它才返回。

优点:响应快,立竿见影。

缺点:因为会阻塞,如果重绘很复杂,可能会让界面卡顿一下。频繁调用它效率很低。

什么时候用:很少用。除非在某些对实时性要求极高、不能有丝毫延迟的场合(比如动画)。

  • update() - "聪明人"(最常用!)

行为:它一被调用,并不是立刻去画,而是给 Qt 系统"发个消息":"喂,我这儿需要重画一下,你方便的时候处理一下。" 然后它就立刻返回了,不会阻塞你的代码。

优点:Qt 会把多个连续的 update() 请求合并成一次 paintEvent 调用,效率非常高,避免了不必要的重复绘制,不会造成界面卡顿。

缺点:有极短的延迟,不是立刻执行。

什么时候用:几乎任何时候你需要主动触发重绘,都应该用它。比如你改变了一个数据(比如进度值),然后调用 update(),请求界面更新来反映这个新数据。

三.绘制各种形状

3.1.绘制线段

这两个函数的功能是完全一样的:在屏幕上画一条直线。它们唯一的区别在于传递参数的方式不同。

函数一:使用 QPoint 对象

cpp 复制代码
void drawLine(const QPoint &p1, const QPoint &p2);

参数解释:

p1:一个 QPoint 对象,表示线条的起点坐标。

p2:一个 QPoint 对象,表示线条的终点坐标。

什么是 QPoint?

QPoint 是 Qt 中的一个类,专门用来表示一个二维平面上的点,它包含两个属性:x 坐标 和 y 坐标。

如何使用这个函数?

你需要先创建两个 QPoint 对象,然后把它们传递给函数。

示例:

cpp 复制代码
// 创建起点坐标 (100, 50)
QPoint startPoint(100, 50);
// 创建终点坐标 (400, 200)
QPoint endPoint(400, 200);

// 调用函数,从 startPoint 到 endPoint 画一条线
drawLine(startPoint, endPoint);

函数二:直接使用坐标值

cpp 复制代码
void drawLine(int x1, int y1, int x2, int y2);

参数解释:

x1, y1:两个整数,分别表示起点坐标的 X 轴位置 和 Y 轴位置。

x2, y2:两个整数,分别表示终点坐标的 X 轴位置 和 Y 轴位置。

如何使用这个函数?

你直接传入四个整数,分别代表起点和终点的 X, Y 坐标。

示例:

cpp 复制代码
// 调用函数,从 (100, 50) 到 (400, 200) 画一条线
drawLine(100, 50, 400, 200);

话不多说,我们直接看例子

这段代码 QPainter painter(this); 在栈上创建了一个 QPainter 对象。由于它是局部自动变量,其生命周期随作用域结束而自动释放,因此我们无需手动管理它的内存。

需要特别说明的是,此处的 this 指针并非用于指定父对象,而是用于指定绘制设备------它表明我们将在这个窗口部件(即当前对象)上进行绘制操作。这种初始化方式是 Qt 中常见的绘图设备指定方式,确保了绘图操作能够正确作用于目标设备。

3.2.绘制矩形

cpp 复制代码
void QPainter::drawRect(int x,int y, int width, int height);
  • x: 矩形左上角的x坐标(相对于绘图设备的坐标系统)
  • y: 矩形左上角的y坐标
  • width: 矩形的宽度(以像素为单位,可以是负数,表示向左或向上扩展)
  • height: 矩形的高度(同样可以是负数)

3.3.绘制椭圆

cpp 复制代码
void QPainter::drawEllipse(const QPoint &center, int rx, int ry)

函数功能:以给定的中心点为中心,以rx和ry为半径,绘制一个椭圆。

这个函数需要你提供三个信息(参数)来告诉"画家"椭圆应该画在哪里、画成多大:

const QPoint &center

  • 作用:指定椭圆的中心点。
  • 类型:QPoint 是一个类,用来表示一个二维平面上的点,它包含 x(横坐标)和 y(纵坐标)信息。
  • 解读:你通过这个参数告诉画家:"请把椭圆的中心点放在这个位置。" 例如 QPoint(100, 50) 表示中心点在 x=100, y=50 的位置。

int rx

  • 作用:指定椭圆在水平方向(X轴)的半径。
  • 解读:这个值决定了椭圆有多"宽"。rx 就是从中心点向左右两边延伸的距离。例如,如果 rx 是 50,那么椭圆在水平方向的总宽度就是 100。

int ry

  • 作用:指定椭圆在垂直方向(Y轴)的半径。
  • 解读:这个值决定了椭圆有多"高"。ry 就是从中心点向上下两边延伸的距离。例如,如果 ry 是 30,那么椭圆在垂直方向的总高度就是 60。

注意:在Qt的坐标系统中,原点(0,0)位于左上角,x轴向右增长,y轴向下增长。

示例:如果中心点坐标为QPoint(100,100),rx为50,ry为30,那么将绘制一个水平方向从50到150(中心100±50),垂直方向从70到130(中心100±30)的椭圆。

另外,如果rx和ry相等,则绘制的是一个圆。

3.4.绘制文本

QPainter类中不仅提供了绘制图形的功能,还可以使⽤QPainter::drawText()函数来绘制⽂字,也可 以使⽤QPainter::setFont() 设置字体等信息。

cpp 复制代码
void QPainter::drawText(int x, int y, const QString &text)
void QPainter::drawText(const QPointF &position, const QString &text)
  • (x, y) / position : 这是文字基线的起点坐标。

    • 关键理解:什么是基线? 想象一下英语练习本的四线三格。基线就是那第三条主线,大部分字母(如 'a', 'x', 'c')的底部都坐落在基线上。而像 'y', 'g', 'p' 这样的字母的尾巴会延伸到基线以下(称为下行部分)。所以,这个坐标点并不是文字矩形的左上角。

  • text: 要绘制的字符串。

示例 : 如果你在 (10, 20) 处绘制文字 "Hello",那么 "H" 的左下角大约就在 (10, 20) 这个位置。

三.设置画笔

QPainter 在进⾏绘制时,默认会使⽤系统预定义的画笔。

此外,开发⼈员也可以根据需求⾃定义画笔属性。

在 Qt 中,QPen 类⽤于控制 QPainter 绘制图形、线条和轮廓的⽅式。

通过 QPen,可以灵活设置画笔的线宽、颜⾊、样式以及画刷等属性。

⼀般来说,画笔的颜⾊可以在创建 QPen 对象时通过构造函数直接指定,该颜⾊由 QColor 类进⾏定义。

⽽画笔的宽度可以使⽤ setWidth() ⽅法进⾏调整,该值表⽰绘制线条的像素宽度。

画笔的样式则可以调⽤ setStyle() ⽅法来设置,例如实线、虚线、点线等 Qt::PenStyle 枚举类型所定义的⻛格。

此外,还可以通过 setBrush() ⽅法为画笔设置画刷,从⽽实现更丰富的填充效果,例如渐变或纹理。

常⽤的 QPen 设置⽅法包括:

  • 设置画笔颜⾊
    QPen::QPen(const QColor &color)

    该构造函数可在初始化时直接传⼊ QColor 对象,以设定画笔颜⾊。

  • 设置画笔宽度
    void QPen::setWidth(int width)

    此⽅法⽤于指定画笔的线宽,单位为像素。

  • 设置画笔样式
    void QPen::setStyle(Qt::PenStyle style)

    通过该⽅法可设置线条的绘制样式,如 Qt::SolidLineQt::DashLine 等。

通过灵活组合上述属性,可以满⾜不同场景下的绘制需求,实现丰富多样的图形外观。

画笔的⻛格我们可以去官方文档那里寻找一下

话不多说,我们直接看例子

四.设置画刷

在Qt中,画刷是使⽤QBrush类来描述,画刷⼤多⽤于填充。

QBrush定义了QPainter的填充模式, 具有样式、颜⾊、渐变以及纹理等属性。

画刷的格式中定义了填充的样式,使⽤Qt::BrushStyle枚举,默认值是Qt::NoBrush,也就是不进⾏ 任何填充。

可以通过Qt助⼿查找画刷的格式。

设置画刷主要通过void QPen::setBrush(constQBrush&brush)⽅法,其参数为画刷的格式

如下图⽰:

不是很明显啊,但是我们仔细就能看出来啊。

我们换一种填充方式

怎么样?是不是很好看了。

五.绘制图片

Qt 提供了四个常用的图像处理类,分别是 QImage、QPixmap、QBitmap 和 QPicture,它们都属于 Qt 绘图设备的重要组成部分,各自在特定场景中发挥着重要作用:

  • QImage 主要设计用于图像数据的输入输出(I/O)操作。该类针对读写操作进行了深度优化,并支持直接访问和操作像素,非常适合对图像进行处理和转换;

  • QPixmap 专注于在屏幕上高效显示图像。它底层依赖于系统的图形资源,在绘制到屏幕时具有较好的性能表现,因此常用于界面中显示图片;

  • QBitmap 是 QPixmap 的一个子类,专门用于处理颜色深度为 1 的图像,即只支持黑白两色的掩码或图案,常见于定制光标、画笔样式等场景;

  • QPicture 则是一个用于记录和回放 QPainter 绘图指令的类,它不存储像素数据,而是保存绘图命令,便于跨会话重用绘图过程。

在本节中,我们将重点介绍在实际开发中最常用的 QPixmap 类,详细讲解其基本用法和典型应用场景。


QPainter::drawPixmap这个函数有多个重载版本(即参数列表不同),我们讲解最常用的几个。

形式1:指定位置 (x, y)

cpp 复制代码
void QPainter::drawPixmap(int x, int y, const QPixmap &pixmap);
  • x :图片左上角在画布上的X坐标。

  • y :图片左上角在画布上的Y坐标。

  • pixmap:要绘制的图片本身。

我们先创建一个项目,然后我们准备一些图片资源啊

接下来我们创建qrc文件

接下来我们就编写代码

缩放图片

形式2:指定位置和大小(缩放)

cpp 复制代码
void QPainter::drawPixmap(int x, int y, int width, int height, const QPixmap &pixmap);

这个版本比上一个多了两个参数:

  • width :你希望图片在画布上显示的宽度

  • height :你希望图片在画布上显示的高度

通俗解释

"画家,请把你手里的 pixmap 这张图片,拉伸或压缩到 width 乘 height 这么大,然后把它的左上角对准画布的 (x, y) 这个点,贴上去。"

重要特性

如果指定的 widthheight 与图片原始尺寸不同,图片会被自动缩放以适应你指定的大小。

我们来写一个项目

旋转图片

这里的旋转本质是QPainter对象进行了旋转,绘制出来的内容也就进行了旋转

1. 作用

你可以把 QPainter 想象成一个画家,他面前有一张画布(你的窗口或部件)。画布上有一个看不见的坐标系。

  • 默认情况下,这个坐标系的原点(0,0)在画布的左上角,X轴向右,Y轴向下。
  • 当你调用 painter.rotate(angle) 后,就相当于画家本人(或者说他手中的画笔坐标系) 绕着原点(0,0)顺时针旋转了 angle 度。
  • 旋转之后,画家再执行任何绘图指令,比如 painter.drawRect(0, 0, 100, 50),这个矩形就会出现在旋转后的新坐标系里,从而看起来像是被"转"了一个角度。

核心要点: 它旋转的是整个坐标系,而不是单个图形。一旦旋转,后续的所有绘制都会受影响。

2. 参数

QPainter::rotate 只有一个参数:

angle (类型是 qreal,即 double)

  • 含义:旋转的角度。
  • 单位:度。
  • 方向:顺时针 为正方向。也就是说,传入一个正数,坐标系会顺时针旋转。

我们来修改代码

运行一下

什么都没有!

旋转中心始终是原点 (0, 0)

这是最容易困惑的地方。**旋转永远是绕着(0,0)点发生的。**如果你的图形不在(0,0)附近,旋转时它就会像行星一样"绕"着原点转一个大圈,而不是在原地旋转。

那我们应该咋办呢?

解决方案: 结合使用 QPainter::translate。

如果你想绕着一个特定点(比如一个矩形的中心)旋转,你需要:

  • 先用 translate(dx, dy) 将坐标系原点平移到你想要的旋转中心。
  • 然后调用 rotate(angle) 进行旋转。
  • 最后,在以新原点为中心的坐标系下绘制你的图形。

运行一下

相关推荐
用户805533698033 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner3 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz8 天前
QML Hello World 入门示例
qt
xcyxiner11 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner11 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner12 天前
DicomViewer (添加模型类)3
qt
xcyxiner12 天前
DicomViewer (目录调整) 2
qt
xcyxiner12 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
LDR00614 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术14 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript