绘图类:QPainter、QPainterDevice和QPainterEngine。QPainter执行绘图操作;QPainterDevice提供绘图设备,是一个二维空间的抽象;QPainterEngine提供一些接口。
一、基本绘制和填充
Qpainter一般在一个的部件重绘事件(Paint Event)的处理函数paintEvent()中进行绘制,首先要创建QPainter对象,然后进行图形的绘制,最后销毁QPainter对象。
1.基本图形的绘制和填充
(1)绘制图形
①重绘事件的处理函数 :paintEvent()
void QWidget::paintEvent ( QPaintEvent * event ) [virtual protected]
基础部件类Qwidget提供的paintEvent函数,是纯虚函数;继承它的子类想用它,必须重新实现它。下列四种情况会发生重绘事件:
- 当窗口部件第一次显示时,系统会自动产生一个绘图事件;
- repaint()与update()函数被调用时;
- 当窗口部件被其他部件遮挡,然后又再次显示出来时,就会对隐藏的区域产生一个重绘事件。
- 重新调整窗口大小时。
②QPainter中的绘制函数
函数 功能
drawArc() 弧
drawChord() 弦
drawConvexPolygon() 凸多边形
drawEllipse() 椭圆
drawImage() QImage表示的图像
drawLine() 线
drawLines() 多条线
drawPath() 路径
drawPicture() 按QPainter指令绘制
drawPie() 扇形
drawPixmap() QPixmap表示的图像
drawPoint() 点
drawPoints() 多个点
drawPolygon() 多边形
drawPolyline() 多折线
drawRect() 矩形
drawRects() 多个矩形
drawRoundRect() 圆角矩形
drawText() 文字
drawTiledPixmap() 平铺图像
drawLineSegments() 绘制折线
(2)使用画笔:QPen
Qpen类为QPainter提供了画笔来绘制线条和形状的轮廓,构造函数:
QPen()
QPen(const QPen &pen)
QPen(Qt::PenStyle style)
QPen(const QColor &color)
QPen(const QBrush &brush, qreal width, Qt::PenStyle style = Qt::SolidLine, Qt::PenCapStyle cap = Qt::SquareCap, Qt::PenJoinStyle join = Qt::BevelJoin ) //依次为画笔使用的画刷,线宽,画笔风格,画笔端点风格,画笔连接风格;
//也可以使用对应的函数进行设置:
void setBrush ( const QBrush & brush )
void setCapStyle ( Qt::PenCapStyle style )
void setColor ( const QColor & color )
void setJoinStyle ( Qt::PenJoinStyle style )
void setWidth ( int width )
画笔风格介绍:
画笔端点风格:
画笔连接风格介绍:
(3)使用画刷:QBrush
构造函数:
QBrush ()
QBrush ( Qt::BrushStyle style )
QBrush ( const QColor & color, Qt::BrushStyle style = Qt::SolidPattern )
QBrush ( Qt::GlobalColor color, Qt::BrushStyle style = Qt::SolidPattern )
QBrush ( const QColor & color, const QPixmap & pixmap )
QBrush ( Qt::GlobalColor color, const QPixmap & pixmap )
QBrush ( const QPixmap & pixmap )
QBrush ( const QImage & image )
QBrush ( const QBrush & other )
QBrush ( const QGradient & gradient )
//QBrush 定义了 QPainter 的填充模式(style),具有样式、颜色(QColor)、渐变(QGradient)以及纹理(QPximap)等属性。
-
style定义了填充模式,通过枚举类型Qt::BrushStyle来实现,默认值是Qt::NoBrush,不进行任何填充;填充模式包括基本填充模式,渐变填充,和纹理填充模式。画刷风格及效果
-
画刷的 gradient() 定义了渐变填充。这个属性只有在样式是 Qt::LinearGradientPattern、Qt::RadialGradientPattern 或者 Qt::ConicalGradientPattern 之一时才有效。渐变可以由 QGradient 对象表示。
Qt 提供了三种渐变:QLinearGradient、QConicalGradient 和 QRadialGradient,它们都是 QGradient 的子类。 -
当画刷样式是 Qt::TexturePattern 时,texture() 定义了用于填充的纹理。
注意,即使你没有设置样式为 Qt::TexturePattern,当你调用 setTexture() 函数时,QBrush 会自动将 style() 设置为 Qt::TexturePattern。 -
画刷的color定义了填充的颜色,这个颜色可以使用Qt预定义的颜色常量(Qt::GlobalColor),也可以使用QColor对象。
2.渐变填充
- 线性渐变(linear gradient)在开始点和结束点之间插入颜色;
- 辐射渐变(radial gradient)在焦点和环绕它的圆环间插入颜色;
- 锥形渐变(Conical)在圆心周围插入颜色。
这三种渐变分别由QGradient的三个子类来表示,QLinearGradient表示线性渐变,QRadialGradient表示辐射渐变,QConicalGradient表示锥形渐变。
(1)线性渐变
QLinearGradient::QLinearGradient ( const QPointF & start, const QPointF & finalStop )
需要指定开始点start和结束点finlStop,然后将开始点和结束点之间的区域进行设定,它们之间的位置按照距离比例进行设定,然后使用QGradient::setColorAt(qreal position,const QColor & color)函数指定的位置position插入指定的颜色。
这里的position的值要在0到1之间。
还可以使用setSpread()函数来设置填充的扩散方式,由QGradient::Spread枚举类型定义,一共有三个
- QGradient::PadSpread——使用最接近的颜色进行填充,这是默认值,如果不使用setSpread()指定的扩散方式,那么就会默认使用这种方式;
- QGradient::RepeatSpread——在渐变区域以外的区域重复渐变;
- QGradient::ReflectSpread——在渐变区域以外将反射渐变。
三种扩散方式
(2)辐射渐变
QRadialGradient::QRadialGradient ( const QPointF & center, qreal radius, const QPointF & focalPoint )
需要指定圆心center和半径radius,这样就确定了一个圆,然后再指定一个焦点focalPoint。焦点的位置为0,圆环的位置为1,然后在焦点和圆环间插入颜色。
也可以使用setSpread()函数来设置填充的扩散方式。
三种扩散方式
(3)锥形渐变
QConicalGradient::QConicalGradient ( const QPointF & center, qreal angle )
需要指定中心点center和一个角度angle(其值在0到360之间),然后沿逆时针从给定的角度开始环绕中心点插入颜色。
二、坐标系统
Qt的坐标系统是由QPainter类控制的,而QPainter是在绘图设备上绘制的。绘图设备类QPaintDevice是所有可以绘制的对象的基类,它的子类主要有QWidget、QPixmap、QPicture、QImage和QPrinter等。
一个绘图设备的默认坐标系统中原点(0, 0)在其左上角,x坐标向右增长,y坐标向下增长。
在基于像素的设备上,默认的单位是一个像素,而在打印机上默认的单位是一个点(1/72英寸)。
QPainter的逻辑坐标与绘图设备的物理坐标之间的映射由QPainter的变换矩阵、视口和窗口处理。逻辑坐标和物理坐标默认是一致的。
1.抗锯齿渲染
(1)逻辑表示
一个图形元素(图元)的大小(宽度和高度)总与其数学模型相对应。
(2)抗锯齿绘图
抗锯齿绘图 :抗锯齿(Anti-aliased)又被称为反锯齿或者反走样,就是对图像的边缘进行平滑处理,使其看起来更加柔和流畅的一种技术。
QPainter进行绘制时可以使用QPainter::RenderHint渲染提示来指定是否要使用抗锯齿功能,渲染提示的取值如下表所示:
常量 描述
QPainter::Autialiasing 指示绘图引擎在可能的情况下应进行边缘的抗锯齿
QPainter::TextAutialiasing 指示绘图引擎在可能的情况下应该绘制抗锯齿的文字
QPainter::SmoothPixmaoTranform
在默认的情况下,绘制会产生锯齿,并且使用这样的规则进行绘制:当使用宽度为一个像素的画笔进行渲染时,像素会在数学定义的点的右边和下边进行渲染,
当使用一个拥有偶数像素的画笔进行渲染时,像素会在数学定义的点的周围对称渲染;而当使用一个拥有奇数像素的画笔进行渲染时,像素会被渲染到数学定义的点的右边和下边,
如果在绘制时使用了抗锯齿渲染提示,即使用QPainter::setRenderHint ( RenderHint hint, bool on = true )函数,将参数hint设置为了QPainter::Antialiasing。那么像素就会在数学定义的点的两侧对称的进行渲染,
2.坐标变换
(1)基本变换
默认情况下,QPainter 在相关设备自身的坐标系统上操作,但它也完全支持仿射坐标转换。
可以使用 QPainter::scale() 来缩放坐标系统,使用 QPainter::rotate() 来顺时针旋转它,使用 QPainter::translate() 来平移它。
还可以使用 QPainter::shear() 函数绕着原点扭曲坐标系统,所有的转换操作都作用在 QPainter 的转换矩阵上,可以使用 QPainter::worldTransform() 进行检索,一个矩阵转换平面上的一个点到另一个点。
如果要反复地进行相同的转换,可以使用 QTransform 对象和 QPainter::worldTransform()、QPainter::setWorldTransform() 函数。
通过调用 QPainter::save() 函数(将矩阵保存在内部堆栈上),可以随时保存 QPainter 的转换矩阵,QPainter::restore() 函数用于恢复上次的结果。
矩阵转换的一个常见的需求是:在不同的绘图设备上使用相同的绘图代码。如果没有转换,结果将与绘图设备的分辨率紧密地联系在一起。
打印机有很高的分辨率,例如:600 DPI(每英寸点数),而屏幕的通常为 72 - 100 DPI。
(2)窗口——视口转换
当使用 QPainter 绘图时,我们使用逻辑坐标来指定点,然后将逻辑坐标转换成绘图设备的物理坐标。
逻辑坐标到物理坐标的映射,由 QPainter 世界变换 worldTransform()(“坐标转换”中所描述的部分)、及 QPainter 的 viewport()、window() 处理。
视口(viewport)表示由任意矩形指定的物理坐标,窗口(window)则用逻辑坐标描述了相同的矩形。默认情况下,物理坐标和逻辑坐标一致,与绘图设备的矩形是等价的。
三、其他绘制
1.绘制文字
除了绘制图形以外,还可以使用QPainter::darwText()函数来绘制文字,也可以使用QPainter::setFont()设置文字所使用的字体,使用QPainter::fontInfo()函数可以获取字体的信息,它返回QFontInfo类对象。在绘制文字时会默认使用抗锯齿。
(1)基本绘制
如:
void Widget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
painter.drawText(100, 100, "qter.org-yafeilinux");
}
这样就在(100, 100)的位置绘制了一个字符串。
(2)控制文字的位置
绘制文本函数的一种重载形式:
QPainter::drawText ( const QRectF & rectangle, int flags, const QString & text, QRectF * boundingRect = 0 )
第一个参数指定了绘制文字所在的矩形;
第二个参数指定了文字在矩形中的对齐方式,它由Qt::AlignmentFlag枚举类型进行定义,不同对齐方式也可以使用“|”操作符同时使用,这里还可以使用Qt::TextFlag定义的其他一些标志,比如自动换行等;
第三个参数就是所要绘制的文字,这里可以使用“\n”来实现换行;
第四个参数一般不用设置。
(3)使用字体
为了绘制漂亮的文字,可以使用QFont类来设置文字字体。
使用的构造函数为:
QFont::QFont ( const QString & family,int pointSize = -1, int weight = -1, bool italic = false ),
第一个参数设置字体的family属性,可以使用QFontDatabase类来获取所支持的所有字体;
第二个参数是点大小,默认大小为12;
第三个参数为weight属性;
最后一个属性设置是否使用斜体。
2.绘制路径
如果要绘制一个复杂的图形,那么可以使用QPainterPath类,然后使用QPainter::drawPath()来进行绘制。QPainterPath类为绘制操作提供了一个容器,可以用来创建图形并且重复使用。
一个绘图路径就是由多个矩形、椭圆、线条或者曲线等组成的对象,一个路径可以是封闭的,例如矩形和椭圆;也可以是非封闭的,例如线条和曲线。
使用:
当创建一个QPainterPath对象后就会以坐标原点为当前点进行绘制,可以随时使用moveTo()函数改变当前点。
可以使用lineTo()、arcTo()、cubicTo()和quadTo()等函数将直线或者曲线添加到路径中。
QPainterPath::cubicTo ( const QPointF & c1, const QPointF & c2, const QPointF & endPoint )函数可以在当前点和endPoint点之间添加一个三次贝塞尔曲线,其中的c1和c2是控制点。
quadTo()函数可以绘制一个二次贝塞尔曲线。
可以使用addEllipse()、addPath()、addRect()、addRegion()、addText()和addPolygon()来向路径中添加一些图形或者文字,它们都从当前点开始进行绘制,绘制完成后以结束点作为新的当前点,这些图形都是由一组直线或者曲线组成的。
另外还可以使用addPath()来添加其他的路径,这样会从本路径的当前点和要添加路径的第一个组件间添加一条直线。
可以使用currentPosition()函数获取当前点,使用moveTo()函数改变当前点;
当组建好路径后可以使用drawPath()函数来绘制路径。
填充规则:
填充路径时也要使用填充规则,这里一共有两个填充规则:Qt::OddEvenFill和Qt::WindingFill。
- Qt::OddEvenFill
使用的是奇偶填充规则,具体来说就是:如果要判断一个点是否在图形中,那么可以从该点向图形外引一条水平线,如果该水平线与图形的交点的个数为奇数,那么该点就在图形中。这个规则是默认值。 - Qt::WindingFill
使用的是非零弯曲规则,具体来说就是:如果要判断一个点是否在图形中,那么可以从该点向图形外引一条水平线,那么如果该水平线与图形的边线相交,这个边线是顺时针绘制的,就记为1,是逆时针绘制的就记为-1,然后将所有数值相加,如果结果不为0,那么该点就在图形中。
3.绘制图像
Qt提供了四个类来处理图像数据:QImage、QPixmap、QBitmap和QPicture,它们也都是常用的绘图设备。
- Image主要用来进行I/O处理,它对I/O处理操作进行了优化,而且也可以用来直接访问和操作像素;
- QPixmap主要用来在屏幕上显示图像,它对在屏幕上显示图像进行了优化;
- QBitmap是QPixmap的子类,它是一个便捷类,用来处理颜色深度为1的图像,即只能显示黑白两种颜色;
- QPicture用来记录并重演QPainter命令。
(1)QImage
QImage类提供了一个与硬件无关的图像表示方法,可以直接访问像素数据,也可以作为绘图设备。
因为QImage是QPaintDevice的子类,所以QPainter可以直接在QImage对象上进行绘制。当在QImage上使用QPainter时,绘制操作会在当前GUI线程以外的其他线程中执行。
QImage支持的图像格式包含了单色、8位、32位和alpha混合格式图像。QImage提供了获取图像各种信息的相关函数,还提供了一些转换图像的函数。
QImage使用了隐式数据共享,所以可以进行值传递,QImage对象还可以使用数据流或者进行比较。
(2)QPixmap
QPixmap可以作为一个绘图设备将图像显示在屏幕上。QPixmap中的像素在内部由底层的窗口系统来进行管理。
因为QPixmap是QPaintDevice的子类,所以QPainter也可以直接在它上面进行绘制。要想访问像素,只能使用QPainter的相应函数,或者将QPixmap转换为QImage。而与QImage不同,QPixmap中的fill()函数可以使用指定的颜色初始化整个pixmap图像。
可以使用toImage()和fromImage()函数在QImage和QPixmap之间进行转换。通常情况下,QImage类用来加载一个图像文件,随意操纵图像数据,然后将QImage对象转换为QPixmap类型再显示到屏幕上。当然,如果不需要对图像进行操作,那么也可以直接使用QPixmap来加载图像文件。
与QImage不同,QPixmap依赖于具体的硬件。QPixmap类也是使用隐式数据共享,可以作为值进行传递。
QPixmap可以很容易的使用QLabel或QAbstractButton的子类(比如QPushButton)来显示在屏幕上。
QLabel拥有一个pixmap属性,而QAbstractButton拥有一个icon属性。我们还可以使用grabWidget()和grabWindow()等静态函数来实现截屏功能,使用mask()等函数实现遮罩效果。
(3)QPicture
QPicture是一个可以记录和重演QPainter命令的绘图设备。QPicture可以使用一个平台无关的格式(.pic格式)将绘图命令序列化到IO设备中,所有可以绘制在QWidget部件或者QPixmap上的内容,都可以保存在QPicture中。QPicture与分辨率无关,在不同设备上的显示效果都是一样的。
4.复合模式
用来定义如何完成数字图像的复合,即如何将源图像的像素和目标图像的像素进行合并。
QPainter提供的常用复合模式及其效果
四、双缓冲绘图
所谓双缓冲(double-buffers)绘图,就是在进行绘制时,先将所有内容都绘制到一个绘图设备(如QPixmap)上,然后再将整个图像绘制到部件上显示出来。
使用双缓冲绘图可以避免显示时的闪烁现象。
五、绘图中的其他问题
- 重绘事件 :触发原因包括repaint()函数或者update()函数被调用;被隐藏的部件现在被重新显示;其他一些原因。
- 剪切 :setClipRect()、setClipRegion()和setClipPath() 等函数。
- 读取和写入图像 :QImage::load()和QPixmap::load()函数 ,QImageReader类 ,QImageWriter类 ,QImage::save()和QPixmap::save()函数 。
- 播放gif动画 :QMovie类 。
- 渲染SVG文件 :QSvgWidget类 。