21. Qt绘图

📅 2026/7/20 21:48:09 👁️ 阅读次数 📝 编程学习
21. Qt绘图

一、概述

虽然Qt已经内置了很多控件,但是不能保证现有控件就可以应对所有场景。很多时候我们需要更强的“自定制”能力。

Qt提供了画图相关的API,可以允许我们在窗口上绘制任意的图像形状,来完成更加复杂的界面设计。

  • 所谓的“控件”,本质也是通过画图的方式画上去的;
  • 画图API和控件之间的关系,可以类比成机器指令和高级语言之间的关系;
  • 控件是对画图API的进一步封装;
  • 画图API是控件的底层实现;

绘图API核心类:

绘图API的使用,一般不会在QWidget的构造函数中使用,而是要放到painEvent事件中:

  • painEvent会在以下情况被触发:
  • 控件首次创建;
  • 控件被遮挡,再解除遮挡;
  • 窗口最小化,再恢复;
  • 控件的大小发生变化;
  • 主动调用repain() 或 update()方法
  • ……

因此,如果把绘图api放到构造函数中调用,那么一旦出现上诉情况,界面的绘制效果就无法确保符合预期。

二、各种形状绘制

2.1 绘制线段

void MainWindow::paintEvent(QPaintEvent *) { QPainter painter(this); // p1 -> p2 painter.drawLine(QPoint(20, 50), QPoint(200, 50)); // x1,y1, -> x2,y2 painter.drawLine(20, 100, 200, 100); }

2.2 绘制矩形

void MainWindow::paintEvent(QPaintEvent *) { QPainter painter(this); // x,y,width,height painter.drawRect(20, 20, 100, 50); }

2.3 绘制圆形

void MainWindow::paintEvent(QPaintEvent *) { QPainter painter(this); // center-pos, rX, rY 以点为圆心,画一个rx,ry的圆 painter.drawEllipse(QPoint(100, 100), 50, 50); painter.drawEllipse(QPoint(100, 100), 20, 40); // x, y, w, h 在以x,y为左上角顶点画一个w * h的矩形,然后画一个内切圆 painter.drawEllipse(100, 100, 100, 50); }

2.4 绘制文本

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

void MainWindow::paintEvent(QPaintEvent *) { QPainter painter(this); //需系统中存在该字体 QFont font("华文行楷", 24); painter.setFont(font); painter.setPen(Qt::green); painter.drawText(QRect(100, 100, 500, 500), "Hey 画上灶炉与柴火"); }

三、绘制设置

3.1 画笔

(1)简介

QPainter在绘制时,存在一个默认画笔。在绘制时也可以自定义画笔。

在Qt中,QPen类定义了QPainter应该如何绘制形状、线条、轮廓。同时,通过QPen类可以设置画笔的线宽、颜色、样式、画刷等:

  • 设置画笔颜色:Qpen::QPen(const QColor &color)
  • 设置画笔宽度:void QPen::setWidth(int width)
  • 设置画笔风格:void QPen::setStyle(Qt::PenStyle style)

(2)画笔风格

enum PenStyle { // pen style NoPen, SolidLine, DashLine, DotLine, DashDotLine, DashDotDotLine, CustomDashLine #ifndef Q_MOC_RUN , MPenStyle = 0x0f #endif };

(3)示例

void MainWindow::paintEvent(QPaintEvent *) { QPainter painter(this); QPen pen(QColor(255, 0, 0)); pen.setWidth(3); pen.setStyle(Qt::DashLine); painter.setPen(pen); painter.drawEllipse(QPoint(100, 100), 50, 50); }

3.2 画刷

(1)简介

在Qt中,使用QBrush类来描述画刷,画刷大多用于填充。

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

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

(2)画刷格式

(3)示例

void MainWindow::paintEvent(QPaintEvent *) { QPainter painter(this); //设置画笔 QPen pen(QColor(255, 0, 0)); pen.setWidth(3); pen.setStyle(Qt::DashLine); painter.setPen(pen); //设置画刷 QBrush brush(Qt::blue); brush.setStyle(Qt::Dense1Pattern); painter.setBrush(brush); painter.drawEllipse(QPoint(100, 100), 50, 50); }

四、绘制图片

4.1 简介

Qt提供了四个类来处理图像数据:QImage、QPixmap、QBitmap、QPicture,他们都是常用的绘图设备。

  • QImage主要用于进行I/O处理,它对I/O处理操作进行了优化,可以用来直接访问和操作像素;
  • QPixmap主要用于在屏幕上显示图像,它针对屏幕上显示图像进行了优化;
  • QBitmap是QPixmap的子类,用于处理颜色深度为1的图像,即只能显示黑白两种颜色
  • QPicture用于记录并重演QPainter命令

4.2 绘制简单图片

void MainWindow::paintEvent(QPaintEvent *) { QPainter painter(this); painter.drawPixmap(0, 0, QPixmap(getPictureFilePath())); }

4.3 平移图片

平移图片实际是通过改变坐标来实现,QPainter类中提供了translate()函数来实现坐标原点的改变。

void MainWindow::paintEvent(QPaintEvent *) { QPainter painter(this); painter.translate(100, 100); painter.drawPixmap(0, 0, QPixmap(getPictureFilePath())); }

4.4 缩放图片

在Qt中,图片的放大和缩小可以使用QPainter类中的drawPixmap()重载函数来实现:

void MainWindow::paintEvent(QPaintEvent *) { QPainter painter(this); //绘制点(50,50),图片尺寸w*h: 100 * 120 painter.drawPixmap(50, 50, 100, 120, QPixmap(getPictureFilePath())); }

4.5 旋转图片

图片的旋转使用的是QPainter类中的rotate()函数,它默认以原点为中心进行旋转,如果需要改变中心,使用translate()函数改变当前绘制原点。

void MainWindow::paintEvent(QPaintEvent *) { QPainter painter(this); //转移绘制原点 painter.translate(100, 200); //设置旋转角度: 正-顺时针,负-逆时针 painter.rotate(90); painter.translate(-100, -200); painter.drawPixmap(0, 0, 100, 100, QPixmap(getPictureFilePath())); }

注意:每次translate移动的坐标,都是以当前原点进行计算

①原点移动到(100, 200):以窗口原点参照,原点位置(100, 200),x轴→,y轴↓

②顺时针旋转90度:以窗口原点参照,原点位置(100, 200), x轴↓,y轴←

③原点移动到(-100, -200):以窗口原点参照,原点位置(300, 100), x轴↓,y轴←

④绘制图片:以窗口原点参照,图片位置(300, 100),顺时针旋转90度

五、绘画设置

5.1 移动画家位置

在需要进行多次绘制时,如果想使用同一坐标,那么绘制出来的图形会出现重合,此时可以移动画家位置来避免。

void MainWindow::paintEvent(QPaintEvent *) { QPainter painter(this); //坐标原点0,0 painter.drawEllipse(QPoint(100, 100), 50, 50); painter.translate(200, 0); //坐标原点200, 0 painter.drawEllipse(QPoint(100, 100), 50, 50); }

5.2 保存/加载画家状态

在绘制的过程中,可以使用save()来保存画家的状态,使用restore()来恢复画家的状态。

注意:

  • save是以栈结构保存当前状态,restore就是从栈中取出栈顶;所以可以保存多个状态,再逐个取出,遵循后进先出原则;
  • 如果多调用了restore(),qt会做判空检测不会崩溃(暂不确定是否所有qt版本都会兜底),最好确保save与restore成对出现;
  • 如果少调用了restore,会导致绘画状态残留,可能导致后续所有绘制偏离预期
void MainWindow::paintEvent(QPaintEvent *) { QPainter painter(this); painter.save(); //保存状态 //移动画家 painter.translate(200, 100); painter.drawEllipse(QPoint(0, 0), 50, 50); //还原状态 painter.restore(); painter.drawRect(0, 0, 50, 50); }

六、特殊的绘图设备

一般常用的绘图设备为QWidget,在Qt中还存在下列三个比较特殊的绘图设备:

  • QPixmap:用于在显示器上显示图片;
  • QImage:用于对图片进行像素级修改;
  • QPicture:用于对QPinter的一系列操作进行存档;

6.1 QPixmap

核心特性:

  • 使用QPainter直接在上面进行绘制图形;
  • 通过文件路径加载并显示图片;
  • 搭配QPinter的drawPixmap()函数,可以将该图片绘制到一个QWidget上;
  • 和系统/显示设备强相关,不同系统/显示设备下,QPixmap的显示可能会有所差异;

示例:

void MainWindow::myDraw() { QPixmap pix(500, 500); pix.fill(Qt::white); //填充图片背景 QPainter painter(&pix); painter.setPen(Qt::red); painter.drawEllipse(QPoint(100, 100), 100, 100); //保存绘制的图片 QString strSavePath = QDir(getPictureFileDir()).filePath("pix.png"); if (!pix.save(strSavePath)) { qWarning() << "Failed to save image:" << strSavePath; } }

执行后会将绘制的图片保存到指定位置:

6.2 QImage

核心特性:

  • 使用QPainter直接在上面进行绘制图形;
  • 通过文件路径加载并显示图片;
  • 能够针对图片进行像素级别的操作(操作某个指定的像素);
  • 独立于硬件的绘制系统,能够在不同系统之上提供一致的显示;

示例:

① 使用QImage绘图设备进行绘图

void MainWindow::myDraw() { //绘图设备大小500*500,绘图格式QImage::Format_RGB32 QImage img(500, 500, QImage::Format_RGB32); img.fill(Qt::white); //填充图片背景 QPainter painter(&img); painter.setPen(Qt::green); painter.drawEllipse(QPoint(100, 100), 100, 100); //保存绘制的图片 QString strSavePath = QDir(getPictureFileDir()).filePath("img.png"); if (!img.save(strSavePath)) { qWarning() << "Failed to save image:" << strSavePath; } }

②使用QImage加载图片,并针对指定像素进行修改

void MainWindow::paintEvent(QPaintEvent *) { QPainter painter(this); QImage img; img.load( QDir(getPictureFileDir()).filePath("img.png")); //修改像素点 for(int i = 100; i < 200; ++i) { for(int j = 100; j < 200; ++j) { QRgb rgb = qRgb(0, 0, 255); img.setPixel(i, j, rgb); } } //将修改后的图片绘制到当前widget中 painter.drawImage(0, 0, img); }

6.3 QPicture

核心特性:

  • 使用QPainter直接在上面进行绘图;
  • 通过文件路径加载并显示图片;
  • 能够记录QPainter的操作步骤;
  • 独立于硬件的绘制系统,能够在不同的系统上提供一致的显示;

注意:

  1. QPicture加载的必须是自身的存档文件,不能是任意的png、jpg等图片文件
  2. 记录QPainter的命令,需要先使用QPinter::begin()函数,将QPicture实例作为参数传递进去,以便告诉系统开始记录,记录完毕后使用QPainter::end()命令终止

示例:

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); QPicture pic; QPainter painter; painter.begin(&pic);//开始绘制 painter.setPen(Qt::blue); painter.drawEllipse(QPoint(200, 200), 100, 100); painter.end();//结束绘制 pic.save(getPictureFileDir() + "pic.pic"); } void MainWindow::paintEvent(QPaintEvent *) { QPainter painter(this); //重现绘图指令 QPicture pic; pic.load(getPictureFileDir() + "pic.pic"); painter.drawPicture(0, 0, pic); }