Qt_绘图

目录

1、绘图核心类

2、QPainter类的使用

[2.1 绘制线段](#2.1 绘制线段)

[2.2 绘制矩形](#2.2 绘制矩形)

[2.3 绘制圆形](#2.3 绘制圆形)

[2.4 绘制文本](#2.4 绘制文本)

3、QPen类的使用

[3.1 使用画笔](#3.1 使用画笔)

4、QBrush类的使用

[4.1 使用画刷](#4.1 使用画刷)

5、绘制图片

[5.1 测试QPixmap](#5.1 测试QPixmap)

[5.1.1 图片移动](#5.1.1 图片移动)

[5.1.2 图标缩小](#5.1.2 图标缩小)

[5.1.3 旋转图片](#5.1.3 旋转图片)

[5.1.4 将QPixmap作为绘图设备](#5.1.4 将QPixmap作为绘图设备)

[5.2 测试QImage](#5.2 测试QImage)

[5.3 测试QPicture](#5.3 测试QPicture)

结语


前言:

Qt提供了DIY度极强的绘图功能,目的就是解决在特殊场景时,使用系统提供的控件无法达到预期的效果。Qt的绘图功能提供了相关的方法,允许开发者在界面中绘制任意的图形形状,以完成更复杂的界面设计。

1、绘图核心类

要想使用绘图功能,则必须用Qt提供的与绘图相关的类来实例化出对象,常用的核心类介绍如下(通过以下类的直译也可以看出其作用):

|------------------|--------------------------------------------------------------------------------------------|
| QPainter(画家) | 进行绘图操作的最基本的类,该类提供了⼀系列drawXXX方法,允许在界面中绘制各种图形 |
| QPen(画笔) | 描述用QPainter画出来的线的样式 |
| QBrush(画刷) | 用来填充用QPainter画出来区域 |
| QPaintDevice(画板) | 描述用QPainter画出来图形在哪个对象上,比如画在QWidget上,则QWidget就是画板,而且QWidget本身就继承自QPaintDevice,所以他本就可以作为被画对象 |


值得注意的是:绘图操作不能直接在界面的构造函数处执行,比如不能在QWidget构造函数中进行绘图,原因很简单,可以理解为绘图的前提是必须得有画板,而执行到QWidget构造函数时,还没有生成完整的画板,因此哪怕此时绘图了也不能在最终的界面上显示出来,因为"画快了"。

所以把绘图操作放到Qt提供的paintEvent事件中(即重写该事件,实现绘图功能),而paintEvent事件会在以下情况自动触发并执行对应的动作:

1、控件首次创建的时候。

2、控件被遮挡后再解除遮挡的时候。(比如用一个界面去挡住另一个界面,然后将他们分开,他们不会影响彼此的界面内容,原因就是paintEvent的执行动作在重新绘图)

3、将窗口最小化的时候。(然后恢复窗口,窗口的内容不会受到影响(注:窗口实际上就是控件))

4、将控件进行放大缩小的时候。

5、在代码中主动调用repaint()或者update()函数。(这两个函数都是QWidget提供的)

因此当paintEvent事件触发时就会重新进行绘图,这也是为什么在计算机屏幕中可以不断的来回切换各种图形界面,给人的感觉是计算机如同一本书一样可以来回翻页,实际上是计算机强大的计算能力在不断的重新绘图。

2、QPainter类的使用

介绍QPainter类中的系列函数drawxxx的使用,即进行一些基本的绘图。

2.1 绘制线段

使用QPainter提供的函数drawLine进行绘制线段,该函数介绍如下:

cpp 复制代码
void drawLine(const QPoint &p1, const QPoint &p2);
//p1:绘制起点坐标
//p2:绘制终点坐标

在widget.h中声明paintEvent事件,代码如下:

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);//重写paintEvent事件

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

在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)
{
    //this表示在当前窗口中绘画
    QPainter painter(this);

    //在20,20坐标到200,20坐标画一条线
    painter.drawLine(QPoint(20,20),QPoint(200,20));
}

运行结果如下:

2.2 绘制矩形

使用QPainter提供的函数drawRect进行绘制线段,该函数介绍如下:

cpp 复制代码
void QPainter::drawRect(int x, int y, int width, int height);
//x:矩形左上角横坐标;
//y:矩形左上角纵坐标;
//width:矩形的宽度;
//height:矩形的⾼度

widget.h代码依旧是声明paintEvent事件,因此不再展示widget.h代码,直接展示widget.cpp代码:

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)
{
    //this表示在当前窗口中绘画
    QPainter painter(this);
    //在坐标(20,20)处绘制一个长200宽200的矩形
    painter.drawRect(20,20,200,200);
}

运行结果:

2.3 绘制圆形

使用QPainter提供的函数drawEllipse进行绘制线段,该函数介绍如下:

cpp 复制代码
void QPainter::drawEllipse(const QPoint &center, int rx, int ry)
//center:中⼼点坐标
//rx:圆的宽度
//ry:圆的高度

widget.cpp代码如下:

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)
{
    //this表示在当前窗口中绘画
    QPainter painter(this);

    //将绘画颜色改成红色
    painter.setPen(Qt::red);

    //在坐标(100,100)处换一个宽50高50的红色圆形
    painter.drawEllipse(QPoint(100,100),50,50);
}

运行结果:

2.4 绘制文本

QPainter不仅可以绘图,还可以绘制文本,这样在界面上写字时就不再需要QLabel作为文本的载体了。使用drawText()函数来绘制文章,还可以配合setFont()函数来设置字体样式,让字体看起来更加饱满。drawText函数介绍如下:

cpp 复制代码
void drawText(const QPoint &position, const QString &text)
//position表示文本的坐标
//text表示文本内容

值得注意的是,这里文本坐标是指文本最左侧的基线出的位置,如下图:

测试代码如下:

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)
{
    //this表示在当前窗口中绘画
    QPainter painter(this);

    //设置字体样式
    painter.setFont(QFont("华文行楷",30));
    //设置画笔
    painter.setPen(Qt::green);

    painter.drawText(QPoint(150,200),"雨打蕉叶又潇潇了几夜");

}

运行结果:

3、QPen类的使用

使用QPainter绘图时,默认有一个画笔(没有画笔则无法绘图,只不过默认画笔就是最普通的文本样式),当然也可以自定义画笔。在Qt中,QPen类表示画笔,该类定义了QPainter绘图的颜色,线条宽度、线条样式。同时通过QPen类提供的函数来设置上述属性,常用函数如下:

|-----------------------------------|-------------------|
| QPen(const QColor &color) | 通过QPen构造函数来设置画笔颜色 |
| void setWidth(int width) | 设置画笔画出线条的宽度 |
| void setStyle(Qt::PenStyle style) | 设置画笔画出线条的样式 |

其中Qt::PenStyle提供了以下的风格:

将上述的枚举常量传给setStyle,则画笔就可以画出不同的样式。

3.1 使用画笔

对上述画矩形的代码进行画笔修饰,代码如下:

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)
{
    //this表示在当前窗口中绘画
    QPainter painter(this);

    //自定义画笔
    QPen pen(QColor(0,255,0));

    //设置画笔宽度
    pen.setWidth(30);

    //设置画笔样式
    pen.setStyle(Qt::DotLine);

    //将画笔设置到画家中
    painter.setPen(pen);

    //在坐标(20,20)处绘制一个长200宽200的矩形
    painter.drawRect(20,20,200,200);
}

运行结果:

4、QBrush类的使用

QBrush类表示画刷,作用是填充图形,可以通过QBrush提供的函数,对填充样式进行更改,比如颜色、风格等。常用函数介绍如下:

|-------------------------------------|---------|
| QPen(const QColor &color) | 设置画刷的颜色 |
| void setStyle(Qt::BrushStyle style) | 设置画刷的风格 |

其中Qt::BrushStyle是一个枚举类型,他提供了多个枚举常量供开发者选择画刷的风格,如下图:

4.1 使用画刷

对上述画笔代码进行添加画刷,代码如下:

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)
{
    //this表示在当前窗口中绘画
    QPainter painter(this);

    //自定义画笔
    QPen pen(QColor(0,255,0));

    //设置画笔宽度
    pen.setWidth(30);

    //设置画笔样式
    pen.setStyle(Qt::DotLine);

    //将画笔设置到画家中
    painter.setPen(pen);

    //自定义画刷
    QBrush brush(Qt::cyan);

    //设置画刷的风格
    brush.setStyle(Qt::Dense1Pattern);

    //将画刷设置到画家中
    painter.setBrush(brush);

    //在坐标(20,20)处绘制一个长200宽200的矩形
    painter.drawRect(20,20,200,200);
}

运行结果:

5、绘制图片

上文的例子说明了如何在界面中手动绘图,然而Qt还提供了对现有图片进行处理的类,分别是QImage、QPixmap、QBitmap和QPicture,这些都是Qt封装好的绘图设备。其中,QImage可以对图片进行像素级别的操作,QPixmap可以将图片显示到屏幕上,QBitmap是QPixmap的子类,只能显示黑白两种颜色,QPicture用来记录并重演QPainter命令。

5.1 测试QPixmap

QPainter提供了drawPixmap函数,该函数可以将QPixmap中的资源显示在界面上,该函数介绍如下:

cpp 复制代码
void drawPixmap(int x, int y, const QPixmap &pm)
//x表示图片左上角的横坐标
//y表示图片左上角的纵坐标
//pm表示图片资源

首先需要创建一个虚拟目录(虚拟目录详细见:Qt_控件的QWidget属性介绍-第五点),如下图:

代码如下:

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)
{
    QPainter painter(this);

    //创建QPixmap对象
    QPixmap map(":/kklt.png");
    //将图片设置在坐标20,20处
    painter.drawPixmap(20,20,map);
}

运行结果:

5.1.1 图片移动

将上述图片进行移动操作,移动的本质是将QPainter进行移动,而不是直接对QPixmap进行移动,代码如下:

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)
{
    QPainter painter(this);

    //创建QPixmap对象
    QPixmap map(":/kklt.png");

    //移动画家
    painter.translate(100,100);

    //将图片设置在坐标20,20处
    painter.drawPixmap(20,20,map);
}

运行结果:

5.1.2 图标缩小

将上述图片进行缩小,函数仍然使用drawPixmap,该函数也拥有缩小的功能,代码如下:

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)
{
    QPainter painter(this);

    //创建QPixmap对象
    QPixmap map(":/kklt.png");

//    //移动画家
//    painter.translate(100,100);

    //将图片设置在坐标20,20处,尺寸不变
    painter.drawPixmap(20,20,map);
    //将图片设置在坐标500,20处,尺寸为50*50像素
    painter.drawPixmap(650,20,50,50,map);
}

运行结果:

5.1.3 旋转图片

旋转图片的逻辑稍微复杂点,使用QPainter类中的rotate()函数进行旋转,并且旋转的不是图片,而是QPainter本身,所以会导致一种情况,即旋转之后的QPainter不在当前窗口中,然后我们在该QPainter上显示的图片也不会在当前窗口中显示,如下图:

从这里也可以得到一个结论:QPainter是一种类似于"附在"窗口界面上的媒介,我们往窗口中写入的资源实际上是写到该QPainter中,然后QPainter再写入至窗口中,即QPainter和窗口界面是相互独立的,且QPainter可以移动,但是当QPainter超出了窗口,则QPainter就不能将超出部分的资源写入窗口中了。因此要将旋转之后的QPainter再进行移动,将其移动回窗口界面中,注意旋转过后的原点位置以及坐标系都变了。


测试代码如下:

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)
{
    QPainter painter(this);

    //创建QPixmap对象
    QPixmap map(":/kklt.png");

    //旋转QPainter,顺时针旋转90°
    painter.rotate(90);

    //将QPainter移回窗口界面中
    painter.translate(0,-500);

    //将图片设置在坐标20,20处,尺寸不变
    painter.drawPixmap(20,20,50,50,map);

}

运行结果:

5.1.4 将QPixmap作为绘图设备

上述中的QPixmap是作为图片载体,然而QPixmap还可以作为绘图的对象,即将QPixmap作为画板。测试代码如下:

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);

    //将QPixmap作为画板,尺寸为500*500px
    QPixmap pixmap(300,300);

    //将pixmap的背景色调成白色
    pixmap.fill(Qt::white);

    //让画家在QPixmap上绘画
    QPainter painter(&pixmap);

    //在QPixmap上画一个圆
    painter.drawEllipse(QPoint(100,100),50,50);

    //为了观察是否画在QPixmap上,将QPixmap上的内容保存到文件中
    pixmap.save("D:\\Qt_code\\QPixmap\\pix.png");
}

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

运行结果:

5.2 测试QImage

QImage也可以像QPixmap一样作为画板,并且也可以保存到磁盘的文件夹中,此功能就不再测试了。主要测试QImage更改像素的功能,首先重写paintEvent事件,通过函数setPixel来设置某个像素的颜色值,使用qRgb表示⼀个具体的颜色值,测试代码如下:

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)
{
    QPainter painter(this);

    QImage image;
    image.load(":\kklt.jpg");

    //修改像素点
    for(int i = 0;i<200;i++)
        for(int j = 0;j<200;j++)
        {
            QRgb rgb = qRgb(0,255,255);
            image.setPixel(i,j,rgb);
        }

    painter.drawImage(20,20,image);
}

运行结果:

5.3 测试QPicture

QPicture最重要的功能是能够记录QPainter的操作步骤,他跟上述几个类不一样,上述的类是作为QPainter的画板,QPainter将最终的成品绘制在面板中呈现给用户看。而QPicture是记录QPainter绘制的操作,然后将该操作进行复盘,再将复盘的结果呈现给用户看,他们本质上大同小异。


首先需要将QPicture实例作为参数传递给QPainter::begin()函数,以便告诉系统开始记录,记录完毕后使用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 picture;
    QPainter painter;

    painter.begin(&picture);
    painter.drawEllipse(20,20,50,50);//画一个圆
    painter.end();

    //此时picture中存放的就是QPainter的操作步骤,将picture保存到磁盘上
    picture.save("D:\\Qt_code\\QPicture\\pic.pic");
}

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

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

    //从磁盘上加载到picture1中
    QPicture picture1;
    picture1.load("D:\\Qt_code\\QPicture\\pic.pic");
    
    //将picture1的内容用painter1画出来,此时picture1里面存放的并不是图形
    painter1.drawPicture(30,30,picture1);

}

测试结果:

结语

以上就是关于Qt绘图的讲解,Qt内部提供了许多功能齐全的部分供我们使用,因此在大部分的场景下很少用到绘图功能,但是不排除一些特别场景,并且这些内置控件的底层也是由绘图一步步画出来的,因此了解绘图也可以更好的了解这些控件的原理。其次就是绘图可以让开发者进行界面开发时拥有很高的自由度,设计一些样式时不再依赖控件作为载体。

最后如果本文有遗漏或者有误的地方欢迎大家在评论区补充,谢谢大家!!

相关推荐
懒大王爱吃狼29 分钟前
Python教程:python枚举类定义和使用
开发语言·前端·javascript·python·python基础·python编程·python书籍
秃头佛爷2 小时前
Python学习大纲总结及注意事项
开发语言·python·学习
待磨的钝刨2 小时前
【格式化查看JSON文件】coco的json文件内容都在一行如何按照json格式查看
开发语言·javascript·json
XiaoLeisj3 小时前
【JavaEE初阶 — 多线程】单例模式 & 指令重排序问题
java·开发语言·java-ee
励志成为嵌入式工程师4 小时前
c语言简单编程练习9
c语言·开发语言·算法·vim
捕鲸叉5 小时前
创建线程时传递参数给线程
开发语言·c++·算法
A charmer5 小时前
【C++】vector 类深度解析:探索动态数组的奥秘
开发语言·c++·算法
Peter_chq5 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
记录成长java6 小时前
ServletContext,Cookie,HttpSession的使用
java·开发语言·servlet
前端青山6 小时前
Node.js-增强 API 安全性和性能优化
开发语言·前端·javascript·性能优化·前端框架·node.js