Qt 界面优化:绘图详解 —— 从基础图形到图片处理

📑 目录

  • [1. 绘图](#1. 绘图)
    • [1.1 基本概念](#1.1 基本概念)
    • [1.2 绘制各种形状](#1.2 绘制各种形状)
    • [1.3 绘制图片](#1.3 绘制图片)
    • [1.4 其他设置](#1.4 其他设置)
    • [1.5 特殊的绘图设备](#1.5 特殊的绘图设备)
  • [2. 小总结](#2. 小总结)
  • [3. 经典面试题](#3. 经典面试题)

1. 绘图

1.1 基本概念

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

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

💡 所谓的"控件",本质上也是通过画图的方式画上去的。

画图 API 和控件之间的关系,可以类比成机器指令和高级语言之间的关系。控件是对画图 API 的进一步封装;画图 API 是控件的底层实现。

绘图 API 核心类
类 说明
QPainter "绘画者"或者"画家"。用来绘图的对象,提供了一系列 drawXXX 方法,可以允许我们绘制各种图形。
QPaintDevice "画板"。描述了 QPainter 把图形画到哪个对象上。像咱们之前用过的 QWidget 也是一种 QPaintDevice(QWidget 是 QPaintDevice 的子类)。
QPen "画笔"。描述了 QPainter 画出来的线是什么样的。
QBrush "画刷"。描述了 QPainter 填充一个区域是什么样的。

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

💡 关于 paintEvent

paintEvent 会在以下情况下被触发:

  • 控件首次创建。
  • 控件被遮挡,再解除遮挡。
  • 窗口最小化,再恢复。
  • 控件大小发生变化时。
  • 主动调用 repaint() 或者 update() 方法。(这两个方法都是 QWidget 的方法)。
  • ...

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

1.2 绘制各种形状

1.2.1 绘制线段

示例 1:

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

参数:

  • p1:绘制起点坐标
  • p2:绘制终点坐标

1、在 widget.h 头文件中声明绘图事件;

cpp 复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

    //声明会话事件
    void paintEvent(QPaintEvent *event) override;

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

2、在 widget.cpp 文件中重写 paintEvent() 方法;

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include<QPainter>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::paintEvent(QPaintEvent *event)
{
    //实例化Painter对象
    //参数this表示:在当前窗口中绘图,也就是绘图设备(画板)是QWidget
    QPainter painter(this);

    //画一条线
   painter.drawLine(QPoint(20,50),QPoint(200,50));
}

实现效果如下:

示例 2:

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

参数:

  • x1, y1:绘制起点坐标
  • x2, y2:绘制终点坐标
cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include<QPainter>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::paintEvent(QPaintEvent *event)
{
    //实例化Painter对象
    //参数this表示:在当前窗口中绘图,也就是绘图设备(画板)是QWidget
    QPainter painter(this);

    //画一条线
   painter.drawLine(QPoint(20,50),QPoint(200,50));

   //画一条线的另一种方法
   painter.drawLine(20,100,200,100);
}
1.2.2 绘制矩形
cpp 复制代码
void QPainter::drawRect(int x, int y, int width, int height);

参数:

  • x:窗口横坐标;
  • y:窗口纵坐标;
  • width:所绘制矩形的宽度;
  • height:所绘制矩形的高度;

📚 此处的窗口的横纵坐标指的是矩形左上角的坐标。

示例:

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include<QPainter>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::paintEvent(QPaintEvent *event)
{
    //实例化Painter对象
    //参数this表示:在当前窗口中绘图,也就是绘图设备(画板)是QWidget
    QPainter painter(this);
   //绘制矩形
   painter.drawRect(50,50,100,50);
}
1.2.3 绘制圆形
cpp 复制代码
void QPainter::drawEllipse(const QPoint &center, int rx, int ry)

参数:

  • center:中心点坐标
  • rx:x方向上的半径
  • ry:y方向上的半径

示例:

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include<QPainter>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::paintEvent(QPaintEvent *event)
{
    //实例化Painter对象
    //参数this表示:在当前窗口中绘图,也就是绘图设备(画板)是QWidget
    QPainter painter(this);
   //绘制椭圆
   painter.drawEllipse(QPoint(150,150),50,50);
}

实现效果:

1.2.4 绘制文本

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

示例:

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include<QPainter>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::paintEvent(QPaintEvent *event)
{
    //实例化Painter对象
    //参数this表示:在当前窗口中绘图,也就是绘图设备(画板)是QWidget
    QPainter painter(this);
    //1.设置字体
    QFont font("华文行楷",20);
    painter.setFont(font);
    //2.设置画笔颜色
    painter.setPen(Qt::red);
    //3.绘制文字
    painter.drawText(QRect(100,200,600,150),"为中华之崛起而读书");
}

实现效果如下:

1.2.5 设置画笔

QPainter 在绘制时,是有一个默认的画笔的。在使用时也可以自定义画笔。在 Qt 中,QPen 类中定义了 QPainter 应该如何绘制形状、线条和轮廓。同时通过 QPen 类可以设置画笔的线宽、颜色、样式、画刷等。

画笔的颜色可以在实例化画笔对象时进行设置,画笔的宽度是通过 setWidth() 方法进行设置,画笔的风格是通过 setStyle() 方法进行设置,设置画刷主要是通过 setBrush() 方法。

  • 设置画笔颜色:QPen::QPen(const QColor &color) ------ 画笔的颜色主要是通过 QColor 类设置;
  • 设置画笔宽度:void QPen::setWidth(int width)
  • 设置画笔风格:void QPen::setStyle(Qt::PenStyle style)

画笔的风格有:

示例:画笔的使用

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include<QPoint>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
}
Widget::~Widget()
{
    delete ui;
}
void Widget::paintEvent(QPaintEvent *event)
{
    //1.实例化画家对象
    QPainter painter(this);
    //2.设置画笔
    QPen pen(QColor(0,255,0));
    //3.设置画笔宽度
    pen.setWidth(3);
    //4.设置画笔风格
    pen.setStyle(Qt::DashLine);
    //5.让画家使用风格
    painter.setPen(pen);
    //6.绘制圆
    painter.drawEllipse(QPoint(150,150),80,50);
}

实现效果如下:

1.2.6 设置画刷

在 Qt 中,画刷是使用 QBrush 类来描述,画刷大多用于填充。QBrush 定义了 QPainter 的填充模式,具有样式、颜色、渐变以及纹理等属性。

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

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

示例:

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include<QPoint>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
}
Widget::~Widget()
{
    delete ui;
}
void Widget::paintEvent(QPaintEvent *event)
{
    //1.实例化画家对象
    QPainter painter(this);
    //2.设置画笔
    QPen pen(QColor(255,0,0));
    //3.设置画笔宽度
    pen.setWidth(3);
    //4.设置画笔风格
    pen.setStyle(Qt::DashLine);
    //5.让画家使用风格
    painter.setPen(pen);
    //6.设置画刷 给封闭图形填充颜色
    QBrush brush(Qt::cyan);
    //7.设置画刷风格
    brush.setStyle(Qt::Dense1Pattern);
    //8.让画家使用画刷
    painter.setBrush(brush);
    //9.绘制圆
    painter.drawEllipse(QPoint(150,150),50,50);
}

实现效果:

1.3 绘制图片

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

QImage 主要用来进行 I/O 处理,它对 I/O 处理操作进行了优化,而且可以用来直接访问和操作像素;

QPixmap 主要用来在屏幕上显示图像,它对在屏幕上显示图像进行了优化;

QBitmap 是 QPixmap 的子类,用来处理颜色深度为 1 的图像,即只能显示黑白两种颜色;

QPicture 用来记录并重演 QPainter 命令。这一节只讲解 QPixmap。

1.3.1 绘制简单图片
  1. 新建 Qt 项目,基类选择 QWidget,项目名称为 QPainter。在 widget.h 头文件中声明绘画事件;
cpp 复制代码
class Widget : public QWidget
{
    Q_OBJECT
public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
    //声明会话事件
    void paintEvent(QPaintEvent *event) override;
private:
    Ui::Widget *ui;
};
  1. 添加资源文件;

    首先准备一些图片资源文件,并将这些图片资源文件放在同一个文件夹中,将该文件夹复制到本项目中;

  2. 选中项目文件,鼠标右键 ---> add new...

  3. 点击 "add new..." 之后,出现如下界面:

  4. 选择 "Choose..." 之后,给资源文件命名;

  5. 点击 "下一步",出现如下界面,点击 "完成";

  6. 给资源文件添加前缀,并将资源文件添加至项目中;

  7. 将所有的资源文件添加到项目中,方便后续使用;

  8. 点击 "构建并运行" 按钮,将资源文件添加到项目中;

  9. 在 widget.cpp 文件中实现画图片功能;

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include<QPainter>
#include<QPixmap>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::paintEvent(QPaintEvent *event)
{
    //1.实例化画家对象
    QPainter painter(this);
    //2.绘制图片
    painter.drawPixmap(0,0,QPixmap(":/picture/panda.png"));
}

实现效果如下:

1.3.2 平移图片

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

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include<QPainter>
#include<QPixmap>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::paintEvent(QPaintEvent *event)
{
    //1.实例化画家对象
    QPainter painter(this);
    //2.将画板的左上角坐标移动到(100,100)处,实现移动图像
    painter.translate(100,100);
    //3.绘制图片
    painter.drawPixmap(0,0,QPixmap(":/picture/panda.png"));

实现效果如下:

1.3.3 缩放图片

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

示例:

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include<QPainter>
#include<QPixmap>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::paintEvent(QPaintEvent *event)
{
    //实例化画家对象
    QPainter painter(this);
    //绘制图片
    painter.drawPixmap(0,0,QPixmap(":/picture/panda.png"));
    //以(300,400)点开始画图,图片尺寸变为 50*60
    painter.drawPixmap(900,900,100,80,QPixmap(":/picture/panda.png"));
}

实现效果如下:

1.3.4 旋转图片

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

示例:

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include<QPainter>
#include<QPixmap>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::paintEvent(QPaintEvent *event)
{
    //实例化画家对象
    QPainter painter(this);
    //  图片的中心作为旋转中心
    painter.translate(200,300);
    painter.rotate(90);//顺时针旋转90度
    painter.translate(-200,-300);//使原点复原
    //以(300,400)点开始画图,图片尺寸变为50*60
    painter.drawPixmap(0,0,300,280,QPixmap(":/picture/panda.png"));
}

实现效果如下:

1.4 其他设置

1.4.1 移动画家位置

有时候在绘制多个图形时,想使用同一坐标位置,那么绘制出来的图形肯定会重合,此时,可以通过移动画家的位置来使图形不发生重合。

示例 1:未移动画家位置

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include<QPainter>
#include<QPixmap>
#include<QPoint>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
}
Widget::~Widget()
{
    delete ui;
}
void Widget::paintEvent(QPaintEvent *event)
{
    //1.实例化画家对象
    QPainter painter(this);

    //使用同一坐标,此时的两个圆会重合,绘制出来的只有一个圆
    painter.drawEllipse(QPoint(100,200),50,50);
    painter.drawEllipse(QPoint(100,200),50,50);
}

实现效果如下:

示例 2:移动画家位置

  • 使用 translate 移动画家所在位置。
cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include<QPainter>
#include<QPixmap>
#include<QPoint>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
}
Widget::~Widget()
{
    delete ui;
}
void Widget::paintEvent(QPaintEvent *event)
{
    //1.实例化画家对象
    QPainter painter(this);
    painter.drawEllipse(QPoint(100,200),50,50);
    //让画家移动
    painter.translate(200,0);
    painter.drawEllipse(QPoint(100,200),50,50);
}

实现效果如下:

1.4.2 保存/加载画家的状态

在绘制图形的过程中,可以通过 save() 函数来保存画家的状态,使用 restore() 函数还原画家状态。

save() 函数原型如下:

cpp 复制代码
void QPainter::save();

restore() 函数原型如下:

cpp 复制代码
void QPainter::restore();

示例:

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include<QPainter>
#include<QPixmap>
#include<QPoint>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::paintEvent(QPaintEvent *event)
{
    //1.实例化画家对象
    QPainter painter(this);

    painter.drawEllipse(QPoint(100,200),50,50);//画图
    painter.translate(200,0);//让画家移动
    painter.save();//保存画家状态

    painter.drawEllipse(QPoint(100,200),50,50);//画图
    painter.translate(200,0);//让画家移动
    painter.restore();//还原画家状态

   painter.drawEllipse(QPoint(100,200),50,50);
}

实现效果如下:

📚 说明:

上述示例中,在画第三个圆之前,由于还原了画家的状态,所以此时画家的位置坐标会移动到画家状态保存的地方,所以在绘制第三个圆的位置时实际是和第二个圆发生了重叠。

1.5 特殊的绘图设备

前面的代码中我们是使用 QWidget 作为绘图设备。在 Qt 中还存在下列三个比较特殊的绘图设备。此处我们也简要介绍。

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

QPixmap 核心特性:

  • 使用 QPainter 直接在上面进行绘制图形。
  • 通过文件路径加载并显示图片。
  • 搭配 QPainter 的 drawPixmap() 函数,可以把这个图片绘制到一个 QLabel、QPushButton 等控件上。
  • 和系统/显示设备强相关,不同系统/显示设备下,QPixmap 的显示可能会有所差别。

示例:

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include<QPixmap>
#include<QPainter>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //Pixmap绘图设备,绘图设备尺寸为:500*500
    QPixmap pix(500,500);
    QPainter painter(&pix);//实例化画家对象
    painter.setPen(Qt::blue);//设置画笔颜色
    painter.drawEllipse(QPoint(100,100),100,100);

    //保存绘制的图片
    pix.save("D:\\Qt\\code\\Paint\\pixmap");
}
Widget::~Widget()
{
    delete ui;
}
1.5.2 QImage

QImage 的核心特性:

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

代码示例:QImage 作为绘图设备的使用

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include<QPixmap>
#include<QPainter>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    //绘图设备的大小为500*500,绘图格式为:QImage::Format_RGB32
    QImage img(500,500,QImage::Format_RGB32);
    img.fill(Qt::white);//填充色为白色,默认为黑色
    QPainter painter(&img);//声明画家,画图设备为img
    painter.setPen(QPen(Qt::cyan));//设置画笔颜色为蓝绿色(青色)
    painter.drawEllipse(QPoint(200,200),100,100);//画图
    img.save("D:\\Qt\\code\\Paint\\pixmap\\img.png");
}
Widget::~Widget()
{
    delete ui;
}

代码示例:QImage 绘图时对像素的修改

  1. 新建 Qt 项目,添加图片资源文件到项目中;如下图示:

  2. 在 widget.h 头文件中声明绘图事件;

cpp 复制代码
class Widget : public QWidget
{
    Q_OBJECT
public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
    //声明绘图事件
    void paintEvent(QPaintEvent* event);
private:
    Ui::Widget *ui;
};
  1. 在 widget.cpp 文件中重写绘图事件,使用 QImage 对图片像素进行修改;
  • 通过 setPixel 设置某个像素的颜色值。
  • 使用 qRgb 表示一个具体的颜色。
cpp 复制代码
void Widget::paintEvent(QPaintEvent *event)
{
    //实例化画家对象
    QPainter painter(this);
    QImage img;

    img.load("/pix.png");//加载图片

    //修改像素点
    for(int i = 100;i < 200;i++)
    {
        for(int j = 100;j < 200;j++)
        {
            QRgb rgb = qRgb(255,0,0);
            img.setPixel(i,j,rgb);
        }
    }
    painter.drawImage(0,0,img);
}
1.5.3 QPicture

QPicture 核心特性:

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

注意:

QPicture 加载的必须是自身的存档文件,而不能是任意的 png,jpg 等图片文件。

💡 QPicture 类似于很多游戏的 Replay 功能。

例如像 war3 这样的经典游戏,即使是一场 60 分钟的膀胱局,生成的 replay 文件,也不过几百个 KB。

此处的 Replay 功能并非是把整个游戏画面都录制保存下来,而是记录了地图中发生的所有事件(地图元素,玩家单位操作,中立生物行为等...)。

当回放 Replay 的时候其实就是把上述记录的事件再一条一条的执行一遍即可还原之前的游戏场景了。

如果要记录下 QPainter 的命令,首先要使用 QPainter::begin() 函数,将 QPicture 实例作为参数传递进去,以便告诉系统开始记录,记录完毕后使用 QPainter::end() 命令终止。如下示例:

示例:

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include<QPainter>
#include<QPicture>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QPicture pic;
    QPainter painter;
    painter.begin(&pic);//开始往pic绘图设备上绘图
    painter.setPen(QPen(Qt::red));//设置画笔颜色为红色
    painter.drawEllipse(QPoint(200,200),100,100);
    painter.end();//结束绘画
    pic.save("D:\\Qt\\code\\Paint\\picture\\pic.pic");
}

Widget::~Widget()
{
    delete ui;
}


void Widget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);

    QPicture pic;
    pic.load("D:\\Qt\\code\\Paint\\picture\\pic.pic");
    painter.drawPicture(0,0,pic);
}

实现效果:

通过 QPicture 重现绘图指令后,实现的效果如下:

2. 小总结

本章我们系统学习了 Qt 绘图相关的核心知识,从最基础的 QPainter、QPaintDevice、QPen、QBrush 四大核心类,到各种基本图形的绘制(线段、矩形、圆形、文本),再到画笔和画刷的详细配置,最后深入探讨了图片的绘制与变换(平移、缩放、旋转)以及三种特殊绘图设备(QPixmap、QImage、QPicture)的适用场景。

核心要点回顾:

  • 🎨 绘图入口 :绘图代码必须放在 paintEvent 中,而不是构造函数里,这样才能保证界面在各种情况下都能正确重绘。
  • 🖌️ 画笔(QPen):控制线条的颜色、宽度和风格,是绘制轮廓的关键。
  • 🖍️ 画刷(QBrush):控制填充区域的样式,支持纯色、渐变、纹理等多种填充模式。
  • 🖼️ 图片处理三兄弟 :QPixmap 负责屏幕显示(与硬件相关)、QImage 负责像素级操作(跨平台一致)、QPicture 负责记录和重演绘图指令(类似游戏 Replay)。
  • 🔄 画家状态管理 :save() 和 restore() 可以保存/恢复画家的位置和状态,避免复杂的坐标计算。

3. 经典面试题

Q1:QPainter、QPaintDevice、QPen、QBrush 四者的关系是什么?

A1:QPainter 是"画家",负责执行具体的绘制操作,提供了一系列 drawXXX 方法;QPaintDevice 是"画板",指定了 QPainter 将图形绘制到哪个对象上(如 QWidget、QPixmap、QImage 等);QPen 是"画笔",决定了绘制线条的颜色、宽度和风格;QBrush 是"画刷",决定了填充区域的颜色和样式。简单来说,QPainter 拿着 QPen 和 QBrush 在 QPaintDevice 上作画。

Q2:为什么绘图代码要放在 paintEvent 中,而不是构造函数里?

A2:因为 paintEvent 会在多种情况下被自动触发,包括:控件首次创建、被遮挡后重新显示、窗口最小化后恢复、控件大小改变、主动调用 repaint() 或 update() 等。如果放在构造函数中,一旦出现上述情况,界面就无法正确重绘,导致显示异常。放在 paintEvent 中则可以确保任何时刻界面都能被正确绘制。

Q3:QPixmap、QImage、QPicture 三者有什么区别?各自的应用场景是什么?

A3:三者都是绘图设备,但侧重点不同:

  • QPixmap:针对屏幕显示做了优化,主要用于在界面上快速显示图片,但与具体显示设备相关,不同系统下显示效果可能有差异。
  • QImage:针对 I/O 和像素操作做了优化,可以独立于硬件提供一致的显示效果,适合需要对图片进行像素级修改的场景(如通过 setPixel 修改某个像素的颜色)。
  • QPicture:用于记录和重演 QPainter 的绘图指令,类似于游戏的 Replay 功能,文件体积小,回放时逐条执行记录的指令即可还原场景。注意它只能加载自身的存档文件,不能加载任意图片格式。

Q4:如何实现图片的旋转?如果要绕指定中心点旋转该怎么做?

A4:图片旋转使用 QPainter::rotate() 函数,默认以原点(0,0)为中心旋转。如果要绕指定中心点旋转,需要先调用 translate() 将坐标原点平移到目标中心点,再调用 rotate() 进行旋转,绘制完成后再用 translate() 平移回来(或使用 save()/restore() 保存和恢复画家状态)。

相关推荐
MayZork1 小时前
Qt 文件操作:QSaveFile 与 QTemporaryFile(含 QTemporaryDir)实战
开发语言·qt
北冥you鱼1 小时前
Go 语言空接口(interface{})使用场景与最佳实践
开发语言·windows·golang
xh didida1 小时前
Linux -- 基础IO
linux·服务器·开发语言·c++
个 人 练 习 生2 小时前
C++ string 类模拟实现:从底层理解字符串(上)
开发语言·c++·经验分享·学习·程序人生
2601_966949652 小时前
多市场量化策略的数据接口应该如何设计:从数据层架构到策略接入
开发语言·python·数据分析·pandas·量化交易·股票数据·quantdash
坤坤子吖2 小时前
C++智能指针:RAII、shared_ptr与内存泄漏
开发语言·c++·笔记·学习
奋斗的阿狸_19862 小时前
ESP32-S3 + ES7210 四通道麦克风录音上传方案
c语言·开发语言·fpga开发
沐言人生2 小时前
又一个神级考公脑库-22,375篇真题终于按考点整理了
后端·面试
weixin199701080162 小时前
《1688图片空间API踩坑:img.upload 与 album.* 的防盗链与CDN缓存问题》(附Python源码)
开发语言·python·缓存