目录
[2.1 绘制线段](#2.1 绘制线段)
[2.2 绘制矩形](#2.2 绘制矩形)
[2.3 绘制圆形](#2.3 绘制圆形)
[2.4 绘制文本](#2.4 绘制文本)
[3.1 使用画笔](#3.1 使用画笔)
[4.1 使用画刷](#4.1 使用画刷)
[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 ¢er, 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内部提供了许多功能齐全的部分供我们使用,因此在大部分的场景下很少用到绘图功能,但是不排除一些特别场景,并且这些内置控件的底层也是由绘图一步步画出来的,因此了解绘图也可以更好的了解这些控件的原理。其次就是绘图可以让开发者进行界面开发时拥有很高的自由度,设计一些样式时不再依赖控件作为载体。
最后如果本文有遗漏或者有误的地方欢迎大家在评论区补充,谢谢大家!!