Qt 2D绘图之一:基本图形绘制和渐变填充
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);
}
执行程序,效果如下:
参考:
Qt 2D绘图之一:基本图形绘制和渐变填充的更多相关文章
- Qt 2D绘图之五:图形视图框架的结构和坐标系统
一.图形视图框架的结构 在前面讲的基本绘图中,我们可以自己绘制各种图形,并且控制它们.但是,如果需要同时绘制很多个相同或不同的图形,并且要控制它们的移动.检测它们的碰撞和叠加:或者我们想让自己绘制的图 ...
- Qt 2D绘图之六:图形视图框架的事件处理与传播
一.简介 图形视图框架中的事件都是首先由视图进行接收,然后传递给场景,再由场景传递给相应的图形项.而对于键盘事件,它会传递给获得焦点的图形项,可以使用QGraphicsScene类的setFocusI ...
- Qt 2D绘图之三:绘制文字、路径、图像、复合模式
一.绘制文字 除了绘制图形以外,还可以使用QPainter::darwText()函数来绘制文字,也可以使用QPainter::setFont()设置文字所使用的字体,使用QPainter::font ...
- Qt 2D绘图之二:抗锯齿渲染和坐标系统
一.抗锯齿渲染 1.1 逻辑绘图 图形基元的大小(宽度和高度)始终与其数学模型相对应,下图示意了忽略其渲染时使用的画笔的宽度的样子. 1.2 物理绘图(默认情况) 在默认的情况下,绘制会产生锯齿,并且 ...
- qt 2D绘图技巧
2D绘图 Qt4中的2D绘图部分称为Arthur绘图系统.它由3个类支撑整个框架,QPainter,QPainterDevice和QPainterEngine.QPainter用来执行具体的绘图相关操 ...
- iOS:quartz2D绘图(给图形绘制阴影)
quartz2D既可以绘制原始图形,也可以给原始图形绘制阴影. 绘制阴影时,需要的一些参数:上下文.阴影偏移量.阴影模糊系数 注意:在drawRect:方法中同时调用绘制同一个图形时,在对绘制的图形做 ...
- matlab学习笔记8 基本绘图命令-特殊图形绘制
一起来学matlab-matlab学习笔记8 基本绘图命令_3 特殊图形绘制 觉得有用的话,欢迎一起讨论相互学习~Follow Me 参考书籍 <matlab 程序设计与综合应用>张德丰等 ...
- Quartz 2D中的基本图形绘制
在iOS中绘图一般分为以下几个步骤: 1.获取绘图上下文 2.创建并设置路径 3.将路径添加到上下文 4.设置上下文状态 5.绘制路径 6.释放路径 在UIKit中默认已经为我们准备好了一个图形上下文 ...
- Qt 2D绘图之四:绘图中的其他问题
一.重绘事件 前面讲到的所有绘制操作都是在重绘事件处理函数paintEvent()中完成的,是QWidget类中定义的函数.一个重绘事件用来重绘一个部件的全部或者部分区域,下面几个原因中的任意一个都会 ...
随机推荐
- W5500EVB TCP Server演示
之前给大家展示了p=6471" style="margin:0px; padding:0px; border:0px; font-family:inherit; font-size ...
- asp.net连接Access数据库实现登陆功能
这里话就不多说了,直接演示代码. 连接access数据库首先需要配置web.config <appSettings> <add key="AccessConnString& ...
- 百度新算法与网站SEO提升
- MyEclipse 8.5 启动过程优化
前言:MyEclipse5.5 大小 139M:MyEclipse6.5 大小 451M:MyEclipse7.0 大小 649M:MyEclipse8.0 大小 772.3MB(速度方面比7.1和7 ...
- map数据的分组,list数据排序 数据筛选
sfit0144 (李四) 2015-01-10 18:00:251Sfit0734 (Sfit0734) 2015-01-10 18:00:38go homesfit0144 (李四) 2015-0 ...
- js appendChild 自带remove和append两个功能
一提到appendChild ,我们就知道是在父节点上增加子节点. 经常使用到它就是动态增加一个元素,将其加入到指定的父节点下. 其实appendChild除了这个功能之外,他还有一个功能:如果子节点 ...
- kbmMemTable关于内存表的使用,以及各种三层框架的评价
关于内存表的使用(kbmMemTable) 关于内存表的使用说明一. Delphi使用内存表1.1 Delphi创建内存表步骤:1. 创建一个Ttable实例.2. 设置一个DataBaseName为 ...
- futimens函数的使用【学习笔记】
#include "apue.h" #include <fcntl.h> int main(int argc,char *argv[]) { int i,fd; str ...
- IO、FileInputStream、(二十)
1.IO流概述及其分类 * 1.概念(什么是IO?) * IO流用来处理设备之间的数据传输 * Java对数据的操作是通过流的方式 * Java用于操作流的类都在IO包中 * 流按流向分为两种:输入流 ...
- 对象数组、集合、链表(java基础知识十五)
1.对象数组的概述和使用 * 需求:我有5个学生,请把这个5个学生的信息存储到数组中,并遍历数组,获取得到每一个学生信息. Student[] arr = new Student[5]; //存储学生 ...