Qt中提供了强大的2D绘图系统,可以使用相同的API在屏幕和绘图设备上进行绘制,它主要基于QPainter、QPaintDevice和QPaintEngine这三个类。它们三者的关系如下图所示:
- QPainter用来执行绘图操作;
- QPaintEngine提供了一些接口,可以用于QPainter在不同的设备上进行绘制;
- QPaintDevice提供绘图设备,它是一个二维空间的抽象,可以使用QPainter在其上进行绘制。
绘图系统中由QPainter来完成具体的绘制操作,提供了大量髙度优化的函数来完成GUI编程所需要的大部分绘制工作。QPainter可以绘制一切想要的图形,从最简单的一条直线到其他任何复杂的图形,还可以用来绘制文本和图片。QPainter可以在继承自QPaintDevice类的任何对象上进行绘制操作。
QPainter—般在一个部件重绘事件( PaintEvent )的处理函数paintEvent ()中进行绘制,首先要创建QPainter对象(画笔),然后进行图形的绘制, 最后销毁QPainter对象。
一、基本图形的绘制
在QPainter中提供了一些方便的函数来绘制常用的图形,而且还可以设置线条和边框的画笔以及进行填充的画刷。
新建Qt Gui应用,项目名称为 myDrawing,基类选择QWidget,类名为Widget。建立完成后,在widget.h文件中声明重绘事件处理函数:
protected: void paintEvent(QPaintEvent *);
然后到widget.cpp文件中添加头文件#include <QPainter>。
1.1 绘制图形
在widget.cpp文件中对paintEvent()函数进行如下定义:
void Widget::paintEvent(QPaintEvent *){ QPainter painter(this); //绘制线条 painter.drawLine(QPoint(0, 0), QPoint(100, 100));}
这里先创建了—个QPainter 对象,使用了QPainter::QPainter(QPaintDevice *device)构造函数,并指定了this为绘图设备,即表明在该部件上进行绘制。使用这个构造函数创建的对象会立即开始在设备上绘制,自动调用begin()函数,然后在QPainter的析构函数中调用end()函数结束绘制。
如果在构建QPainter对象时不想指定绘制设备,那么可以使用不带参数的构造函数,然后使用QPainter:: begin (QPaintDevice *device)在开始绘制时指定绘制设备,等绘制完成后再调用end()函 数结束绘制。上面函数中的代码等价于:
QPainter painter;painter.begin(this);painter.drawLine(QPoint(0, 0), QPoint(100, 100));painter.end();
这两种方式都可以完成绘制,无论使用哪种方式,都要指定绘图设备,否则将无法进行绘制。第二行代码使用drawLine()函数绘制了一条线段,这里使用了该函数的一种重载形式QPainter::drawLine ( const QPoint & p1, const QPoint & p2 )其中p1和p2分别是线段的起点和终点。这里的QPoint(0, 0)就是窗口的原点,默认是窗口的左上角(不包含标题栏)。效果如下图所示。
除了绘制简单的线条以外,QPainter中还提供了一些绘制其他常用图形的函数, 其中最常用的几个如下表所示。
函数 | 功能 |
---|---|
drawArc() | 绘制圆弧 |
drawChord() | 绘制弦 |
drawConvexPolygon() | 绘制凸多边形 |
drawEllipse() | 绘制椭圆 |
drawLine() | 绘制线条 |
drawPie() | 绘制扇形 |
drawPoint() | 绘制点 |
drawPolygon() | 绘制多边形 |
drawPolyline() | 绘制折线 |
drawRect() | 绘制矩形 |
drawRoundedRect() | 绘制圆角矩形 |
另外我们将光标定位到QPainter类名上,然后按下键盘上的F1按键,这时会自动跳转到该类的帮助页面。当然,也可以到帮助模式,直接索引查找该类名。在帮助里面我们可以看到很多相关的绘制函数,如下图所示。
我们任意点击一个函数名,就会跳转到该函数的介绍段落。例如我们点击drawEllipse()函数,就跳转到了该函数的介绍处,上面还提供了一个例子。如下图所示。我们可以直接将例子里面的代码复制到paintEvent()函数里面,测试效果。
1.2 使用画笔
QPen定义了用于QPainter应该怎样画线或者轮廓线。画笔具有样式style() 、宽度width() 、画刷brush() 、笔帽样式capStyle()和连接样式joinStyle()等属性。先介绍QPen类的构造函数:
QPen(const QBrush &brush, qreal width, Qt::PenStyle s = Qt::SolidLine, Qt::PenCapStyle c = Qt::SquareCap, Qt::PenJoinStyle j = Qt::BevelJoin);
- 画刷brush()用于填充画笔所绘制的线条。
- 画笔的样式style()定义了线的样式。
- 笔帽样式capStyle()定义了使用QPainter绘制的线的末端;
- 连接样式joinStyle()则定义了两条线如何连接起来。
- 画笔宽度width()或widthF()定义了画笔的宽。注意,不存在宽度为 0 的线。假设你设置 width 为 0,QPainter依然会绘制出一条线,而这个线的宽度为 1 像素。
这么多参数既可以在构造时指定,也可以使用 set 函数指定,完全取决于你的习惯。使用setWidth(),setBrush(),setCapStyle()和setJoinStyle()函数可以轻松修改各种设置。
画笔样式
再将paintEvent()函数的内容更改如下:
void Widget::paintEvent(QPaintEvent *){ //创建画笔 QPen pen(Qt::green, 5, Qt::DotLine, Qt::RoundCap, Qt::RoundJoin); //使用画笔绘制圆弧 painter.setPen(pen); QRectF rectangle(70.0, 40.0, 80.0, 60.0); int startAngle = 30 * 16; int spanAngle = 120 * 16; painter.drawArc(rectangle, startAngle, spanAngle);}
上面创建完画笔后,使用了setPen()来为painter设置画笔,然后使用画笔绘制了一个圆弧。绘制圆弧函数的一种重载形式为QPainter::drawArc ( const QRectF & rectangle, int startAngle, int spanAngle ),这里的三个参数分别对应于需要指定弧线所在的矩形、起始角度和跨越角度,如下图所示。
QRectF:: QRectF (qreal x, qreal y, qreal width, qreal height)可以使用浮点数为参数来确定一个矩形,它需要指定左上角的坐标(x,y)、宽width和髙height。如果只想使用整数来确定一个矩形,那么可以使用QRect类。这里角度的数值为实际度数乘以16,在时钟表盘中,0度指向3时的位置,角度数值为正则表示逆时针旋转,角度数值为负则表示顺时针旋转,整个一圈的数值为5760(即360X16)。
1.3 使用画刷
QBrush类提供了画刷来填充图形,一个画刷使用它的颜色和风格(例如它的填充模式)来定义。先介绍QBrush类的构造函数:
QBrush(const QColor &color, Qt::BrushStyle bs=Qt::SolidPattern);
在Qt中使用的颜色一般都由QColor类来表示,它支持RGB、HSV和CMYK等颜色模型。里面如果是三个参数,那么分别是红、绿、蓝分量的值,也就是经常说的rgb,取值范围都是0-255,比如这里的(255, 0, 0)就表明红色分量为255,其他分量为0,那么出来就是红色。如果是四个参数,最后一个参数alpha是设置透明度的,取值范围也是0-255,0表示完全透明,而255表示完全不透明。在Qt中还提供了20种预定义的颜色,如下图所示。
QBrush样式的填充模式使用Qt::BrushStyle枚举变量来定义,包含了基本模式填充、渐变填充和纹理填充,所有枚举变量如下图所示。默认的风格是Qt :: NoBrush(取决于你如何构建画笔),不填充形状。标准的填充风格是Qt :: SolidPattern。设置画刷风格的方式有两种,一种是利用构造函数,另外一种是利用setstyle函数。
再将paintEvent()函数的内容更改如下:
void Widget::paintEvent(QPaintEvent *){ QPainter painter(this); QPen pen; //画笔 pen.setColor(QColor(255, 0, 0)); QBrush brush(QColor(0, 255, 0, 125)); //画刷 painter.setPen(pen); //添加画笔 painter.setBrush(brush); //添加画刷 painter.drawRect(50, 50, 200, 100); //绘制矩形}
这里分别新建了一个画笔QPen和画刷QBrush。其中画笔使用了setColor()函数为其设置了颜色,而画刷是在构建的时候直接为其设置的颜色。然后我们将画笔和画刷设置到了painter上,并使用drawRect()绘制了一个矩形,其左上角顶点在(50, 50),宽为200,高为100。运行程序,效果如下图所示。
二、渐变填充
在画刷中也可以使用渐变填充。QGradient类就是用来和QBrush一起指定渐变填充的。Qt现在支持三种类型的渐变填充:
- 线性渐变(linear gradient)在开始点和结束点之间插入颜色;
- 辐射渐变(radial gradient)在焦点和环绕它的圆环间插入颜色;
- 锥形渐变(Conical)在圆心周围插入颜色。
这三种渐变分别由QGradient的三个子类来表示,QLinearGradient表示线性渐变,QRadialGradient表示辐射渐变,QConicalGradient表示锥形渐变。
(1)线性渐变
QLinearGradient::QLinearGradient ( const QPointF & start, const QPointF & finalStop )
线性渐变需要指定开始点start和结束点finalStop,然后将开始点和结束 点之间的区域进行等分,开始点的位置为0.0,结束点的位置为1.0,它们之间的位置按照距离比例进行设定,然后使用 QGradient::setColorAt (qreal position, const QColorj &color)函数在指定的位置position插人指定的颜色color,当然,这里的position的值要在0〜1之间。
这里还可以使用setSpread()函数来设置填充的扩散方式,即指明在指定区域以外的区域怎样进行填充。扩散方式由QGradient::Spread枚举变量定义,它一共有3个 值,分别是QGradiem::PadSpread,使用最接近的颜色进行填充,这是默认值;QGradient:: ReflectSpread在渐变区域以外将反射渐变;QGradiem:: RepeatSpread在渐变区域以外的区域重复渐变。要使用渐变填充,可以直接在setBrush()中使用,这时画刷风格会自动设置为相对应的渐变填充。在线性渐变中这3种扩散方式的效果如下图所示。
(2)辐射渐变
QRadialGradient::QRadialGradient ( const QPointF & center, qreal radius, const QPointF & focalPoint )
辐射渐变需要指定圆心 center 和半径 radius,这样就确定 了一个圆,然后再指定一个焦点focalPoint。焦点的位置为0,圆环的位置为1,然后在焦点和圆环间插人颜色。辐射渐变也可以使用setSpread()函数设置渐变区域以外区域的扩散方式,3种扩散方式的效果如下图所示。
(3)锥形渐变
QConicalGradient::QConicalGradient ( const QPointF & center, qreal angle )
锥形渐变需要指定中心点center和一个角度angle(其值在0到360之间),然后沿逆时针从给定的角度开始环绕中心点插入颜色。这里给定的角度沿逆时针方向开始的位置为0,旋转一圈后为1。setSpread()函数对于锥形渐变没有效果。
(4)示例程序
示例程序如下:
void Widget::paintEvent(QPaintEvent *){ //线性渐变 QLinearGradient linearGradient(QPointF(40, 190),QPointF(70, 190)); //插入颜色 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(100, 100, 90, 40); //辐射渐变 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)); painter.setBrush(radialGradient); painter.drawEllipse(QPointF(100, 200), 50, 50); //锥形渐变 QConicalGradient conicalGradient(QPointF(250, 190), 60); conicalGradient.setColorAt(0.2, Qt::cyan); conicalGradient.setColorAt(0.9, Qt::black); painter.setBrush(conicalGradient); painter.drawEllipse(QPointF(250, 200), 50, 50);}
执行程序,效果如下:
参考: