Qt 2D绘图之三:绘制文字、路径、图像、复合模式
一、绘制文字
除了绘制图形以外,还可以使用QPainter::darwText()函数来绘制文字,也可以使用QPainter::setFont()设置文字所使用的字体,使用QPainter::fontInfo()函数可以获取字体的信息,它返回QFontInfo类对象。在绘制文字时会默认使用抗锯齿。
1.1 基本绘制
下面仍然在上一节的程序中进行代码演示,更改paintEvent()的内容如下:
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.drawText(100, 100, "qter.org-yafeilinux");
}
这样就在(100, 100)的位置绘制了一个字符串。效果如下图所示。
1.2 控制文字的位置
我们先到QPainter的帮助文档页面,然后查看drawText()函数的重载形式,找到:
QPainter::drawText ( const QRectF & rectangle, int flags, const QString & text, QRectF * boundingRect = 0 )
- 第一个参数指定了绘制文字所在的矩形;
- 第二个参数指定了文字在矩形中的对齐方式,它由Qt::AlignmentFlag枚举类型进行定义,不同对齐方式也可以使用“|”操作符同时使用,这里还可以使用Qt::TextFlag定义的其他一些标志,比如自动换行等;
- 第三个参数就是所要绘制的文字,这里可以使用“\n”来实现换行;
- 第四个参数一般不用设置。
下面来看一个例子。为了更明显地看到文字在指定矩形中的位置,我们绘制出这个矩形。将paintEvent()函数更改如下:
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
//设置一个矩形
QRectF rect(50, 50, 300, 200);
//为了更直观地看到字体的位置,我们绘制出这个矩形
painter.drawRect(rect);
painter.setPen(QColor(Qt::red));
//这里先让字体水平居中
painter.drawText(rect, Qt::AlignHCenter, "yafeilinux");
}
现在运行程序,效果如下图所示。
可用的对齐方式如下图所示。
1.3 使用字体
为了绘制漂亮的文字,可以使用QFont类来设置文字字体。大家也可以先在帮助文档中查看该类的介绍。下面将最常用的一些设置进行演示。
将paintEvent()函数更改如下:
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
//设置一个矩形
QRectF rect(50, 50, 300, 200);
//为了更直观地看到字体的位置,我们绘制出这个矩形
painter.drawRect(rect);
painter.setPen(QColor(Qt::red));
//这里先让字体水平居中
painter.drawText(rect, Qt::AlignHCenter, "yafeilinux");
//使用字体
QFont font("宋体", 15, QFont::Bold, true);
//设置下划线
font.setUnderline(true);
//设置上划线
font.setOverline(true);
//设置字母大小写
font.setCapitalization(QFont::SmallCaps);
//设置字符间的间距
font.setLetterSpacing(QFont::AbsoluteSpacing, 10);
//使用字体
painter.setFont(font);
painter.setPen(Qt::blue);
painter.drawText(120, 80, tr("yafeilinux"));
painter.translate(50, 50);
painter.rotate(90);
painter.drawText(0, 0, tr("helloqt"));
}
这里创建了QFont字体对象,使用的构造函数为QFont::QFont ( const QString & family,int pointSize = -1, int weight = -1, bool italic = false ),第一个参数设置字体的family属性,这里使用的字体族为宋体,可以使用QFontDatabase类来获取所支持的所有字体;第二个参数是点大小,默认大小为12;第三个参数为weight属性,这里使用了粗体;最后一个属性设置是否使用斜体。然后我们又使用了其他几个函数来设置字体的格式,最后调用setFont()函数来使用该字体,并使用drawText()函数的另一种重载形式在点(120, 80)绘制了文字。后面又将坐标系统平移并旋转,然后再次绘制了文字。运行程序,效果如下图所示。
二、绘制路径
如果要绘制一个复杂的图形,那么可以使用QPainterPath类,然后使用QPainter::drawPath()来进行绘制。QPainterPath类为绘制操作提供了一个容器,可以用来创建图形并且重复使用。一个绘图路径就是由多个矩形、椭圆、线条或者曲线等组成的对象,一个路径可以是封闭的,例如矩形和椭圆;也可以是非封闭的,例如线条和曲线。
2.1 简单应用
下面看一个例子:添加一个椭圆和一根线在图形路径里。依然在前面的项目中进行讲解。更改paintEvent()函数如下:
void Widget::paintEvent(QPaintEvent *)
{
//添加一个椭圆和一根线在图形路径里
QPainterPath path;
path.addEllipse(100, 100, 50, 50); //添加一个圆心为(100,100),横纵半径都为50的椭圆
path.lineTo(200, 200); //添加一根从当前位置到(200,200)的线
QPainter painter(this);
painter.setPen(Qt::blue);
painter.setBrush(Qt::red);
painter.drawPath(path);
}
当创建一个QPainterPath对象后,可以使用lineTo()、arcTo()、cubicTo()和quadTo()等函数将直线或者曲线添加到路径中。运行程序,效果如下图所示。
2.2 复制图形
如果只是简单的将几个图形拼接在一起,其实完全没有必要用路径,之所以要引入路径,就是因为它的一个非常有用的功能:复制图形路径。更改paintEvent()函数如下:
void Widget::paintEvent(QPaintEvent *)
{
//添加一个椭圆和一根线在路径里
QPainterPath path;
path.addEllipse(100, 100, 50, 50); //添加一个圆心为(100,100),横纵半径都为50的椭圆
path.lineTo(200, 200); //添加一根从当前位置到(200,200)的线
QPainter painter(this);
painter.setPen(Qt::blue);
painter.setBrush(Qt::red);
painter.drawPath(path);
//复制图形路径
QPainterPath path2;
path2.addPath(path);
path2.translate(100,0);
painter.drawPath(path2);
}
现在运行程序,效果如下图所示。
可以看到,对于已经绘制好的路径,可以非常简单地进行重复绘制。
2.3 绘制图形时的当前位置
我们先来看一个例子,将paintEvent()函数更改如下:
void Widget::paintEvent(QPaintEvent *)
{
QPainterPath path;
path.lineTo(100, 100);
path.lineTo(200, 100);
QPainter painter(this);
painter.drawPath(path);
}
程序运行效果如下图所示。
可以看到,创建路径后,默认是从(0, 0)点开始绘制的,当绘制完第一条直线后当前位置是(100, 100)点,从这里开始绘制第二条直线。绘制完第二条直线后,当前位置是(200, 100)。
我们也可以使用moveTo()函数来改变当前点的位置。例如将paintEvent()函数更改如下:
void Widget::paintEvent(QPaintEvent *)
{
QPainterPath path;
path.addRect(50, 50, 40, 40);
//移动到(100, 100)点
path.moveTo(100, 100);
path.lineTo(200, 200);
QPainter painter(this);
painter.drawPath(path);
}
这样当绘制完矩形以后,就会移动到(100, 100)点进行后面的绘制。程序运行效果如下图所示
三、绘制图片
Qt提供了四个类来处理图像数据:QImage、QPixmap、QBitmap和QPicture,它们都是常用的绘图设备。其中QImage主要用来进行I/O处理,它对I/O处理操作进行了优化,而且可以用来直接访问和操作像素;QPixmap主要用来在屏幕上显示图像,它对在屏幕上显示图像进行了优化;QBitmap是QPixmap的子类,用来处理颜色深度为1的图像,即只能显示黑白两种颜色;QPicture用来记录并重演QPainter命令。这一节我们只讲解QPixmap。
3.1 简单绘制图片
(1)这次我们重新创建一个Qt Widgets应用,项目名称为mypixmap,在类信息页面,将基类选择为QDialog,类名使用默认的Dialog即可。
(2)然后在源码目录中复制一张图片,比如这里是一张logo.png图片,如下图所示。
(3)在dialog.h文件中添加重绘事件处理函数的声明:
protected:
void paintEvent(QPaintEvent *);
(4)到dialog.cpp文件中先添加头文件包含#include <QPainter>,然后添加函数的定义:
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
QPixmap pix;
pix.load("../mypixmap/logo.png");
painter.drawPixmap(0, 0, 80, 100, pix);
}
这里使用了相对路径,因为Qt Creator默认是使用影子构建,即编译生成的文件在build-mypixmap-Desktop_Qt_5_8_0_MinGW_32bit-Debug这样的目录里面,而这个目录就是当前目录,所以源码目录就是其上级目录了。大家可以根据自己的实际情况来更改路径,也可以使用绝对路径,不过最好使用资源文件来存放图片。drawPixmap()函数在给定的矩形中来绘制图片,这里矩形的左上角顶点为(0, 0)点,宽80,高100,如果宽高跟图片的大小比例不同,默认会拉伸图片。运行效果如下图所示。
3.2 平移图片
QPainter类中的translate()函数实现坐标原点的改变,改变原点后,此点将会成为新的原点(0,0)。下面来看一个例子。在paintEvent()函数中继续添加如下代码:
//将(100,100)设为坐标原点
painter.translate(100, 100);
painter.drawPixmap(0, 0, 80, 100, pix);
这里将(100,100)设置为了新的坐标原点,所以下面在(0,0)点贴图,就相当于在以前的(100,100)点贴图。运行程序,效果如下图所示。
3.3 缩放图片
我们可以使用QPixmap类中的scaled()函数来实现图片的放大和缩小。在paintEvent()函数中继续添加如下代码:
//获得以前图片的宽和高
qreal width = pix.width();
qreal height = pix.height();
//将图片的宽和高都缩小,并且在给定的矩形内保持宽高的比值不变
pix = pix.scaled(width, height,Qt::KeepAspectRatio);
painter.drawPixmap(90, 90, pix);
其中参数Qt::KeepAspectRatio,是图片缩放的方式。可以将鼠标指针放到该代码上,按下F1键查看其帮助了,如下图所示。
这里有三个值,只看其示例图片就可大致明白,Qt::IgnoreAspectRatio是不保持图片的宽高比;Qt::KeepAspectRatio是在给定的矩形中保持宽高比;最后一个也是保持宽高比,但可能超出给定的矩形。这里给定的矩形是由我们显示图片时给定的参数决定的,例如painter.drawPixmap(0,0,100,100,pix);就是在以(0,0)点为起始点的宽和高都是100的矩形中。运行程序效果如下图所示。
3.4 旋转图片
旋转使用的是QPainter类的rotate()函数,它默认是以原点为中心进行旋转的。如果要改变旋转的中心,可以使用前面讲到的translate()函数完成。在paintEvent()函数中继续添加如下代码:
//让图片的中心作为旋转的中心
painter.translate(40, 50);
painter.rotate(90); //顺时针旋转90度
painter.translate(-40,-50); //使原点复原
painter.drawPixmap(100, 100, 80, 100, pix);
这里必须先改变旋转中心,然后再旋转,然后再将原点复原,才能达到想要的效果。运行程序,如下图所示。
3.5 扭曲图片
实现图片的扭曲,是使用的QPainter类的shear(qreal sh,qreal sv)函数完成的。它有两个参数,前面的参数实现横向变形,后面的参数实现纵向变形。当它们的值为0时,表示不扭曲。在paintEvent()中继续添加如下代码:
painter.shear(0.5, 0); //横向扭曲
painter.drawPixmap(100, 0, 80, 100, pix);
运行效果如下图所示。
四、复合模式
QPainter提供了复合模式(Composition Modes)来定义如何完成数字图像的复合,即如何将源图像的像素和目标图像的像素进行合并。QPainter提供的常用复合模式及其效果如下图所示。 其中普通的类型是SoiirceOver(通常被称为alpha混合),就是正在绘制的源像素混合在已经绘制的目标像素上,源像素的alpha分量定义了它的透明度,这样源图像就会以透明效果在目标图像上进行显示。当设置了复合模式,它就会应用到所有的绘图操作中,例如画笔、画刷、渐变和pixmap/image绘制等。
实例:
void Widget::paintEvent(QPaintEvent *event)
{
QPainter painter;
QImage image(400, 300, QImage::Format_ARGB32_Premultiplied);
painter.begin(&image);
painter.setBrush(Qt::green);
painter.drawRect(100, 50, 200, 200);
painter.setBrush(QColor(0, 0, 255, 150));
painter.drawRect(50, 0, 100, 100);
painter.setCompositionMode(QPainter::CompositionMode_SourceIn);
painter.drawRect(250, 0, 100, 100);
painter.setCompositionMode(QPainter::CompositionMode_DestinationOver);
painter.drawRect(50, 200, 100, 100);
painter.setCompositionMode(QPainter::CompositionMode_Xor);
painter.drawRect(250, 200, 100, 100);
painter.end();
painter.begin(this);
painter.drawImage(0, 0, image);
}
这里先在Qlmage上绘制了一个矩形,然后又在这个矩形的4个角分别绘制了4个小矩形,每个小矩形都使用了不同的复合模式,并且使用了半透明的颜色进行填充。 第一个小矩形没有明确指定复合模式,它默认使用的是SourceOver模式。运行效果如下图所示。
参考:
Qt 2D绘图之三:绘制文字、路径、图像、复合模式的更多相关文章
- Qt 2D绘图之二:抗锯齿渲染和坐标系统
一.抗锯齿渲染 1.1 逻辑绘图 图形基元的大小(宽度和高度)始终与其数学模型相对应,下图示意了忽略其渲染时使用的画笔的宽度的样子. 1.2 物理绘图(默认情况) 在默认的情况下,绘制会产生锯齿,并且 ...
- [Qt2D绘图]-04绘制文字&&绘制路径
注:学习自<Qt Creator 快速入门>第三版. 文档中的示例参考 Qt Example推荐:Painter Paths Example和Vector Deformation ...
- Qt 2D绘图之一:基本图形绘制和渐变填充
Qt中提供了强大的2D绘图系统,可以使用相同的API在屏幕和绘图设备上进行绘制,它主要基于QPainter.QPaintDevice和QPaintEngine这三个类.它们三者的关系如下图所示: QP ...
- qt 2D绘图技巧
2D绘图 Qt4中的2D绘图部分称为Arthur绘图系统.它由3个类支撑整个框架,QPainter,QPainterDevice和QPainterEngine.QPainter用来执行具体的绘图相关操 ...
- Qt 2D绘图之五:图形视图框架的结构和坐标系统
一.图形视图框架的结构 在前面讲的基本绘图中,我们可以自己绘制各种图形,并且控制它们.但是,如果需要同时绘制很多个相同或不同的图形,并且要控制它们的移动.检测它们的碰撞和叠加:或者我们想让自己绘制的图 ...
- Qt 2D绘图之四:绘图中的其他问题
一.重绘事件 前面讲到的所有绘制操作都是在重绘事件处理函数paintEvent()中完成的,是QWidget类中定义的函数.一个重绘事件用来重绘一个部件的全部或者部分区域,下面几个原因中的任意一个都会 ...
- Qt 2D绘图之六:图形视图框架的事件处理与传播
一.简介 图形视图框架中的事件都是首先由视图进行接收,然后传递给场景,再由场景传递给相应的图形项.而对于键盘事件,它会传递给获得焦点的图形项,可以使用QGraphicsScene类的setFocusI ...
- Qt 2D绘图 渐变填充(三种渐变方式)
在qt中提供了三种渐变方式,分别是线性渐变,圆形渐变和圆锥渐变.如果能熟练应用它们,就能设计出炫目的填充效果. 线性渐变: 1.更改函数如下: void Dialog::paintEvent(QPai ...
- Qt 2D绘图高级篇
1.拖动模式 在QGraphicView中提供了三种拖动模式,分别是: QGraphicsView::NoDrag :忽略鼠标事件,不可以拖动. QGraphicsView::ScrollHandDr ...
随机推荐
- 字符编码乱码问题(servlet底层 编码大揭秘)
好多初学者会遇到,请求过去的信息内包含中文(一般会是get方式提交过去的请求会出现).好郁闷,这是为什么呢.有下面分析下,说的不好可以吐槽 话说我们能遇到这种编码的问题,归根结底就是这 这 web开 ...
- linux应用之php开发环境lamp搭建(centos)
搭建linux+apache+mysql+php环境 1.安装apache: yum install httpd httpd-devel 启动apache: /etc/init.d/httpd ...
- 从Github上下载了项目,导入Android Studio,gradle 报错,应该怎么修改
一.从Github上获取源代码 我这里是直接下载ZIP文件 二.在本机的Android Studio上新建一个空白项目,目的主要是与刚从Github上下载的项目文件结构做对比 三.替换gradle文件 ...
- I.MX6 Surfaceflinger 机制
/**************************************************************************** * I.MX6 Surfaceflinger ...
- [APIO 2010] 特别行动队
[题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=1911 [算法] 设前i个士兵"修正"后的最大战斗力为fi 令su ...
- CTSC2012 熟悉的文章
传送门 首先很容易想到对于所有的模式串建出广义后缀自动机,之后对于我们每一个要检查的文本串,先在SAM上跑,计算出来每一个位置能匹配到的最远的位置是多少.(就是当前点减去匹配长度) 之后--考虑DP- ...
- caffe源码学习
本文转载自:https://buptldy.github.io/2016/10/09/2016-10-09-Caffe_Code/ Caffe简介 Caffe作为一个优秀的深度学习框架网上已经有很多内 ...
- CF-798B
B. Mike and strings time limit per test 2 seconds memory limit per test 256 megabytes input standard ...
- http://www.cnblogs.com/dasenglin/p/5821987.html
一安装maven 先安装jdk,配置JAVA_HOME 把下载的maven bin包,解压到指定目录,比如:D:\apache-maven-3.3.9-bin 配置maven的系统变量M2_HOME和 ...
- Android 4.2开发环境搭建
一.工具 jdk1.7; eclipse 4.3(for java ee); Android SDK; 二.安装JDK并配置 安装略,配置如下: 右击 “我的电脑”->属性->高级系统设置 ...