一、绘制系统简介
QPainter、QPaintDevice和QPaintEngine这三个类。其中QPainter用来执行绘图操作;QPaintDevice提供绘图设备,它是一个二维空间的抽象,可以使用QPainter在其上进行绘制;QPaintEngine提供了一些接口,可以用于QPainter在不同的设备上进行绘制。在绘图系统中由QPainter来完成具体的绘制操作,QPainter类提供了大量高度优化的函数来完成GUI编程所需要的大部分绘制工作。QPainter可以绘制一切想要的图形,从最简单的一条直线到其他任何复杂的图形,它还可以用来绘制文本和图片。QPainter可以在继承自QPaintDevice类的任何对象上进行绘制操作。QPainter一般在一个部件的重绘事件(Paint Event)处理函数paintEvent()中进行绘制,首先要创建QPainter对象,然后进行图形的绘制,最后销毁QPainter对象。
#include "mainwindow.h"
#include <QPainter>
#include <Qt>
#include <QPen>
PaintWidget::PaintWidget(QWidget *parent):QWidget(parent){
this->resize(800,600);//设置了窗口的大小和标题
this->setWindowTitle(tr("绘制系统"));
}
/*
* QPainter 有很多以 draw 开头的函数,用于各种图形的绘制,比如这里的 drawLine(),
* drawRect() 以及 drawEllipse() 等。当绘制轮廓线时,使用 QPainter 的 pen() 属性。比如,
* 我们调用了 painter.setPen(Qt::red) 将 pen 设置为红色,则下面绘制的矩形具有红色的轮廓
* 线。接下来,我们将 pen 修改为绿色, 5 像素宽( painter.setPen(QPen(Qt::green, 5))),又
* 设置了画刷为蓝色。这时候再调用 draw 函数,则是具有绿色 5 像素宽轮廓线、蓝色填充的椭圆。
*/
void PaintWidget::paintEvent(QPaintEvent *){
QPainter painter(this);
painter.setPen(Qt::gray);// 设置线段的颜色
painter.drawLine(80,100,600,500);//线段的起止坐标
painter.setPen(Qt::red);// 设置矩形的颜色
painter.drawRect(10,10,100,400);//矩形的对角坐标
painter.setPen(QPen(Qt::green,5));//绿色5像素宽轮廓线
painter.setBrush(Qt::blue);//设置画刷为蓝色
painter.drawEllipse(50,150,400,200);//椭圆
}
二、画刷和画笔
画刷和画笔。前者使用 QBrush 描述,大多用于填充;后者使用QPen 描述,大多用于绘制轮廓线。
QBrush 定义了 QPainter 的填充模式,具有样式、颜色、渐变以及纹理等属性。画刷的 style()定义了填充的样式,使用 Qt::BrushStyle 枚举,默认值是 Qt::NoBrush,也就是不进行任何填充。画刷的 color()定义了填充模式的颜色。这个颜色可以是 Qt 预定义的颜色常量,也就是Qt::GlobalColor,也可以是任意 QColor 对象。画刷的 gradient()定义了渐变填充。这个属性只有在样式是 Qt::LinearGradientPattern、Qt::RadialGradientPattern 或者 Qt::ConicalGradientPattern 之一时才有效。渐变可以由QGradient 对象表示。 Qt 提供了三种渐变: QLinearGradient、 QConicalGradient 和QRadialGradient,它们都是 QGradient 的子类。当画刷样式是 Qt::TexturePattern 时, texture()定义了用于填充的纹理。注意,即使你没有设置样式为 Qt::TexturePattern,当你调用 setTexture()函数时, QBrush 会自动将 style()设置为 Qt::TexturePattern。
QPen 定义了用于 QPainter 应该怎样画线或者轮廓线。画笔具有样式、宽度、画刷、笔帽样式和连接样式等属性。画笔的样式 style()定义了线的样式。画刷 brush()用于填充画笔所绘制的线条。笔帽样式 capStyle()定义了使用 QPainter 绘制的线的末端;连接样式joinStyle()则定义了两条线如何连接起来。画笔宽度 width()或 widthF()定义了画笔的宽。注意,不存在宽度为 0 的线。假设你设置 width 为 0, QPainter 依然会绘制出一条线,而这个线的宽度为 1 像素。也就是说,画笔宽度通常至少是 1 像素。这么多参数既可以在构造时指定,也可以使用 set 函数指定。
#include "widget.h"
#include <QPainter>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
resize(800,600);
setWindowTitle(tr("画刷和画笔"));
}
Widget::~Widget()
{
}
void Widget::paintEvent(QPaintEvent *){
/*
* 这里首先为该部件创建了一个QPainter对象,this表明绘图设备就是Widget部件,
* painter用于后面的绘制。然后使用drawLine()函数绘制了一条线段,线段的起点
* 为(0, 0),终点为(100, 100) ,这里的单位是像素。
*/
QPainter painter(this);
painter.drawLine(QPointF(0,0),QPointF(100,100));
/*
*这里分别新建了一个画笔pen和画刷brush。其中画笔使用了setColor()函数为其设置了颜色,
* 而画刷是在构建的时候直接为其设置的颜色。这里的颜色都是使用QColor类提供的,里面
* 如果是三个参数,那么分别是红、绿、蓝分量的值,也就是经常说的rgb,取值范围都是0-255,
* 比如这里的(255, 0, 0)就表明红色分量为255,其他分量为0,那么出来就是红色。如果是四个
* 参数,最后一个参数alpha是设置透明度的,取值范围也是0-255,0表示完全透明,而255表示完
* 全不透明。然后我们设置painter使用画笔和画刷,并通过drawRect()绘制了一个矩形,其左上
* 角顶点在(100, 100),宽为200,高为100。
*/
QPen pen;
pen.setColor(QColor(255,0,0));
QBrush brush(QColor(0,255,0,125));
painter.setPen(pen);
painter.setBrush(brush);
painter.drawRect(100,100,200,100);
/*
* 画笔还有许多其他的设置,可以查看该类的帮助文档。例如,可以使用pen.setStyle()来设置画笔样式。
* 画刷也有很多其他设置,这个也可以查看其帮助文档。在Qt中为画刷提供了一些可用的样式,可以使用
* setStyle()函数来设置。
*/
QPen pen2(Qt::DotLine);//设置画笔形状为由点组成的线
QBrush brush2(Qt::blue);
brush2.setStyle(Qt::HorPattern);//画刷的形状为栅格线
painter.setPen(pen2);
painter.setBrush(brush2);
painter.drawRect(300,200,200,100);
/*
* 这里要说明的是,画弧线时,角度被分成了十六分之一,就是说,要想画30度,取值就得是30*16。
* 它有起始角度和跨度,还有位置矩形。
*/
QRectF rectangle(100,100,200,100);
int startAngle=30*16;
int spanAngle=120*16;
painter.drawArc(rectangle,startAngle,spanAngle);
}
三、渐变系统
QGradient类就是用来和QBrush一起指定渐变填充的。Qt现在支持三种类型的渐变填充:
线性渐变(linear gradient)在开始点和结束点之间插入颜色;
辐射渐变(radial gradient)在焦点和环绕它的圆环间插入颜色;
锥形渐变(Conical)在圆心周围插入颜色。
这三种渐变分别由QGradient的三个子类来表示,QLinearGradient表示线性渐变,QRadialGradient表示辐射渐变,QConicalGradient表示锥形渐变。
#include "widget.h"
#include <QPainter>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
resize(600,600);
setWindowTitle(tr("渐变系统"));
label=new QLabel(this);
label->move(50,50);
label->setText(tr("线性渐变"));
label2=new QLabel(this);
label2->move(200,50);
label2->setText(tr("辐射渐变"));
label3=new QLabel(this);
label3->move(350,50);
label3->setText(tr("锥形渐变"));
}
Widget::~Widget()
{
}
void Widget::paintEvent(QPaintEvent *){
/*
* 对于线性渐变QLinearGradient::QLinearGradient ( const QPointF & start, constQPointF &
* finalStop )需要指定开始点start和结束点finalStop,然后将开始点和结束点之间的区域进行等分,
* 开始点的位置为0.0,结束点的位置为1.0,而它们之间的位置按照距离比例进行设定,然后使用
* QGradient::setColorAt ( qreal position, const QColor & color )函数在指定的位置position
* 插入指定的颜色color,当然,这里的position的值要在0到1之间。这里还可以使用setSpread()函数来
* 设置填充的扩散方式,即指明在指定区域以外的区域怎样进行填充。扩散方式由QGradient::Spread枚举
* 类型定义,它一共有三个值,分别是QGradient::PadSpread,使用最接近的颜色进行填充,这是默认值,
* 如果我们不使用setSpread()指定扩散方式,那么就会默认使用这种方式;QGradient::RepeatSpread,
* 在渐变区域以外的区域重复渐变;QGradient::ReflectSpread,在渐变区域以外将反射渐变。要使用渐变
* 填充,可以直接在setBrush()中使用,这时画刷风格会自动设置为相对应的渐变填充。
*/
QLinearGradient linearGradient(QPointF(40,150),QPointF(60,160));//渐变的起止点坐标
linearGradient.setColorAt(0,Qt::yellow);
linearGradient.setColorAt(0.5,Qt::red);
linearGradient.setColorAt(1,Qt::green);
linearGradient.setSpread(QGradient::RepeatSpread);
QPainter painter(this);
painter.setBrush(linearGradient);
painter.drawRect(30,100,100,100);
/*
*对于辐射渐变QRadialGradient::QRadialGradient( const QPointF & center, qreal radius,
* const QPointF & focalPoint )需要指定圆心center和半径radius,这样就确定了一个圆,然后
* 再指定一个焦点focalPoint。焦点的位置为0,圆环的位置为1,然后在焦点和圆环间插入颜色。
* 辐射渐变也可以使用setSpread()函数设置在渐变区域以外的区域的扩散方式。
*/
QRadialGradient radialGradient(QPointF(100,190),50,QPointF(275,200));
radialGradient.setColorAt(0,QColor(255,255,100,150));
radialGradient.setColorAt(1,QColor(0,0,0,50));
radialGradient.setSpread(QGradient::RepeatSpread);
painter.setBrush(radialGradient);
painter.drawEllipse(QPoint(225,150),50,50);//指定中心坐标,长半轴和短半轴
/*
*对于锥形渐变QConicalGradient::QConicalGradient ( const QPointF & center,qreal angle )
* 需要指定中心点center和一个角度angle(其值在0到360之间),然后沿逆时针从给定的角度开始
* 环绕中心点插入颜色。这里给定的角度沿逆时针方向开始的位置为0,旋转一圈后为1。setSpread()
* 函数对于锥形渐变没有效果。
*/
QConicalGradient conicalGradient(QPoint(375,150),60);
conicalGradient.setColorAt(0.2,Qt::green);
conicalGradient.setColorAt(0.9,Qt::black);
painter.setBrush(conicalGradient);
painter.drawEllipse(QPoint(375,150),50,50);
}
四、坐标系统
save() 和 restore()。save() 就是保存下当前状态; restore()则恢复上一次保存的结果。这两个函数必须成对出现:QPainter 使用栈来保存数据,每一次 save(),将当前状态压入栈顶, restore() 则弹出栈顶进行恢复。
平移 translate,旋转 rotate,缩放 scale 和扭曲 shear。在这段代码中,我们首先在 (10, 10) 点绘制一个红色的 50×100 矩形。保存当前状态,将坐标系平移到 (100, 0),绘制一个黄色的矩形。注意, translate()操作平移的是坐标系,不是矩形。因此,我们还是在 (10, 10) 点绘制一个 50×100 矩形,现在,它跑到了右侧的位置。然后恢复先前状态,也就是把坐标系重新设为默认坐标系(相当于进行 translate(-100, 0)),再进行下面的操作。之后也是类似的。由于我们只是保存了默认坐标系的状态,因此我们之后的 translate()横坐标值必须增加,否则就会覆盖掉前面的图形。所有这些操作都是针对坐标系的,因此在绘制时,我们提供的矩形的坐标参数都是不变的。
#include "widget.h"
#include <QPainter>
paintWidget::paintWidget(QWidget *parent)
: QWidget(parent)
{
resize(800,800);
setWindowTitle(tr("坐标系统"));
}
paintWidget::~paintWidget()
{
}
void paintWidget::paintEvent(QPaintEvent *){
QPainter painter(this);
painter.fillRect(10,10,50,100,Qt::red);
painter.save();//保存下当前状态
//向右平移100个像素,向下平移20个像素,注意,translate() 操作平移的是坐标系,不是矩形。
painter.translate(100,20);
painter.fillRect(10,10,50,100,Qt::yellow);
painter.restore();//恢复上一次保存的结果
painter.save();
painter.translate(300,-20);//向右平移100个像素,向上平移20个像素
painter.rotate(90);//顺时针旋转90度
painter.fillRect(10,10,50,100,Qt::gray);
painter.restore();
painter.save();
painter.translate(200,100);
painter.scale(2,3);//横坐标扩大两倍,纵坐标扩大3倍
painter.fillRect(10,10,50,100,Qt::blue);
painter.restore();
painter.save();
painter.translate(400,0);
painter.shear(0,1);//横向不变,纵向扭曲一倍
painter.fillRect(10,10,50,100,Qt::green);
painter.restore();
}
五、绘制图片和文字
QPainter::darwText()函数来绘制文字,也可以使用QPainter::setFont()设置文字所使用的字体,使用QPainter::fontInfo()函数可以获取字体的信息,它返回QFontInfo类对象。在绘制文字时会默认使用抗锯齿。
QPixmap 专门为图像在屏幕上的显示做了优化; QBitmap 是 QPixmap 的一个子类,它的色深限定为 1,你可以使用 QPixmap 的 isQBitmap() 函数来确定这个 QPixmap 是不是一个QBitmap。QImage 专门为图像的像素级访问做了优化。 QPicture 则可以记录和重现 QPainter的各条命令。
QPixmap 继承了 QPaintDevice, 因此, 你可以使用 QPainter 直接在上面绘制图形。QPixmap 也可以接受一个字符串作为一个文件的路径来显示这个文件, 比如你想在程序之中打开 png、 jpeg之类的文件,就可以用QPixmap。使用 QPainter::drawPixmap() 函数可以把这个文件绘制到一个 QLabel、QPushButton 或者其他的设备上面。正如前面所说的那样, QPixmap 是针对屏幕进行特殊优化的,因此,它与实际的底层显示设备息息相关。注意,这里说的显示设备并不是硬件,而是操作系统提供的原生的绘图引擎。所以,在不同的操作系统平台下, QPixmap 的显示可能会有所差别。
#include "widget.h"
#include <QPainter>
#include <QPixmap>
#include <QtGlobal>
paintWidget::paintWidget(QWidget *parent)
: QWidget(parent)
{
resize(800,800);
setWindowTitle(tr("绘制文字和图像"));
}
paintWidget::~paintWidget()
{
}
void paintWidget::paintEvent(QPaintEvent *){
QPainter painter(this);
//设置一个矩形
QRectF rect(50,50,600,500);
//为了更直观地看到字体的位置,我们绘制出这个矩形
painter.drawRect(rect);
painter.setPen(QColor(Qt::red));
//字体水平居中
painter.drawText(rect,Qt::AlignHCenter,"Eric");
/*
*这里创建了QFont字体对象,使用的构造函数为QFont::QFont
* ( const QString & family,int pointSize = -1, int weight = -1, bool italic = false ),
* 第一个参数设置字体的family属性,这里使用的字体族为宋体,可以使用QFontDatabase类来获取所
* 支持的所有字体;第二个参数是点大小,默认大小为12;第三个参数为weight属性,这里使用了粗体;
* 最后一个属性设置是否使用斜体。然后我们又使用了其他几个函数来设置字体的格式,最后调用setFont()
* 函数来使用该字体,并使用drawText()函数的另一种重载形式在点(120, 80)绘制了文字。后面又将坐标
* 系统平移并旋转,然后再次绘制了文字。
*/
QFont font("宋体",15,QFont::Bold,true);
font.setUnderline(true);//设置下划线
font.setCapitalization(QFont::SmallCaps);//设置字母大小写
font.setLetterSpacing(QFont::AbsoluteSpacing,10);//设置字符间的间距
painter.setFont(font);
painter.setPen(Qt::blue);
painter.save();
painter.drawText(100,100,tr("lee"));
painter.translate(50, 50);//坐标平移
painter.rotate(90);//顺时针旋转90度
painter.drawText(0,0,tr("Eric Lee"));
painter.restore();
/*
* drawPixmap()函数在给定的矩形中来绘制图片,这里矩形的左上角顶点为(200, 100)点,宽100,高100,
* 如果宽高跟图片的大小比例不同,默认会拉伸图片。
*/
painter.save();
QPixmap pix;
pix.load(":/image/hj");//从资源文件中加载图片
painter.drawPixmap(200,100,100,100,pix);
painter.restore();
/*
* QPainter类中的translate()函数实现坐标原点的改变,改变原点后,此点将会成为新的原点。
*/
painter.translate(200,200);
painter.drawPixmap(0,0,100,100,pix);
/*
* 获得以前图片的宽和高,将图片的宽和高都缩小,并且在给定的矩形内保持宽高的比值不变
*/
qreal width=pix.width();
qreal height=pix.height();
pix=pix.scaled(width*0.1,height*0.1,Qt::KeepAspectRatio);
painter.drawPixmap(100,100,pix);
/*
* 让图片的中心作为旋转的中心,顺时针旋转90度
*/
painter.translate(50,50);
painter.rotate(90);
painter.translate(-50,-50); //使原点复原
painter.drawPixmap(100, 100, 100, 100, pix);
/*
* 实现图片的扭曲,是使用的QPainter类的shear(qreal sh,qreal sv)函数完成的。
* 它有两个参数,前面的参数实现横向变形,后面的参数实现纵向变形。当它们的值为0时,表示不扭曲。
*/
painter.shear(0.5,0);
painter.drawPixmap(150, 50, 100, 100, pix);
}
六、图形视图框架
Graphics View 提供了一种接口,用于管理大量自定义的 2D 图形元素,并与之进行交互;还提供了用于将这些元素进行可视化显示的观察组件,并支持缩放和旋转。
Graphics View 框架包含了一套完整的事件体系,可以用于与场景中的元素进行双精度的交互。这些元素同样支持键盘事件、鼠标事件等。 Graphics View 使用了 BSP 树(Binary SpacePartitioning tree,这是一种被广泛应用于图形学方面的数据结构)来提供非常快速的元素发现,也正因为如此,才能够实现一种上百万数量级元素的实时显示机制。
Graphics View 是一个基于元素(item)的 MV 架构的框架。它可以分成三个部分:元素 item、场景 scene 和视图 view。基于元素的意思是,它的每一个组件都是一个独立的元素。这是与我们之前讲到过的QPainter 状态机机制不同。回忆一下,使用 QPainter 绘图,大多是采用一种面向过程的描述方式:首先使用 drawLine()画一条直线,然后使用 drawPolygon()画一个多边形。对于Graphics View,相同的过程可以是,首先创建一个场景(scene),然后创建一个直线对象和一个多边形对象,再使用场景的 add()函数,将直线和多边形添加到场景中,最后通过视图进行观察,就可以看到了。乍看起来,后者似乎更加复杂,但是,如果你的图像中包含了成千上万的直线、多边形之类,管理这些对象要比管理 QPainter 的绘制语句容易得多。并且,这些图形对象也更加符合面向对象的设计要求:一个很复杂的图形可以很方便的复用。
MV 架构的意思是, Graphics View 提供一个 model 和一个 view(正如 MVC 架构,只不过 MV 架构少了 C 这么一个组件)。所谓模型(model)就是我们添加的种种对象;所谓视图(view)就是我们观察这些对象的视口。同一个模型可以由很多视图从不同的角度进行观察,这是很常见的需求。使用 QPainter 很难实现这一点,这需要很复杂的计算,而Graphics View 可以很容易的实现。Graphics View 提供了 QGraphicsScene 作为场景,即是允许我们添加图形的空间,相当于整个世界; QGraphicsView 作为视口,也就是我们的观察窗口,相当于照相机的取景框,这个取景框可以覆盖整个场景,也可以是场景的一部分; QGraphicsItem 作为图形元件,以便添加到场景中去, Qt 内置了很多图形,比如直线、多边形等,它们都是继承自QGraphicsItem。
#include "widget.h"
#include <QApplication>
#include <QGraphicsView>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
/*
* 首先创建一个场景,也就是 QGraphicsScene 对象。然后我们使用addLine()函数
* 向场景中添加了一个直线,起始点和终点坐标分别是 (0, 0) 和 (150, 150)。
* 可以想象,这是一个边长 150px 的正方形的对角线。通过这两步,我们已经有了
* 场景和元素。之后,我们创建一个 GraphicsView 对象,绑定到一个场景上(也就是
* 我们前面创建的 scene 对象)。注意, QGraphicsScene 不是 QWidget 的子类,
* 因此该构造函数并不是调用的 QGraphicsView(QWidget *parent)。
*/
QGraphicsScene scene;
//QGraphicsScene的sceneRect属性供QGraphicsView 确定视图默认的滚动条区域,
//并且协助 QGraphicsScene 管理元素索引。
scene.setSceneRect(0,0,300,300);
scene.addLine(0,0,150,150);
QGraphicsView view(&scene);
view.setWindowTitle("图形视图");
view.show();
return a.exec();
}
标签:Qt,系统,50,视图,QPainter,100,绘制,painter
From: https://blog.51cto.com/u_6526235/7787891