Qt二维图形之画刷和画笔

Qt中提供了强大的2D绘图系统,可以使用相同的API在屏幕上和绘图·设备上进行绘制,主要基于QPainter、QPainterDevice和QPainterEngine这3个类。QPainter执行绘图操作,QPainterDevice提供绘图设备,是一个二维空间的抽象,QPainterEngine提供一些接口。QPainter可以绘制一切简单的图形,从简单的一条直线到任何复杂的图形。QPainter类可以在一切继承QPainterDevice的子类上进行绘制操作。



1.重绘事件的处理函数 :paintEvent()

在Qt Assistant查paintEvent() 函数,有如下的说明:

void QWidget::paintEvent ( QPaintEvent * event ) [virtual protected]

This event handler can be reimplemented in a subclass to receive paint events passed in event.

基础部件类Qwidget提供的paintEvent函数,是纯虚函数;继承它的子类想用它,必须重新实现它。下列三种情况会发生重绘事件:

a)当窗口部件第一次显示时,系统会自动产生一个绘图事件;
b)repaint()与update()函数被调用时;
c)当窗口部件被其他部件遮挡,然后又再次显示出来时,就会对隐藏的区域产生一个重绘事件。
d) 重新调整窗口大小时。

2.画刷与画笔

(1)QBrush

     画刷与画笔是Qt绘图时,重要属性;前者用QBrush描述,用来填充,后者用QPen描述,来绘制轮廓线。
QBrush的属性我们可以从QBrush类的构造函数中看出,在QtAssistant中输入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)QPen

画笔用QPen类来实现,先看一下QPen类的构造函数,
QPen ()
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 )
QPen ( const QPen & pen )
包含了画笔实用的画刷,线宽,画笔风格,画笔端点风格,画笔连接风格;也可以使用对应的函数进行设置,
void	setBrush ( const QBrush & brush )
void	setCapStyle ( Qt::PenCapStyle style )
void	setColor ( const QColor & color )
void	setJoinStyle ( Qt::PenJoinStyle style )
void	setWidth ( int width )

 
 
  1. QPainter painter(this);  
  2. QPen pen(Qt::green, 3, Qt::DashDotLine, Qt::RoundCap, Qt::RoundJoin);  
  3. painter.setPen(pen);  
等价于
  
  
  1. QPainter painter(this);  
  2. QPen pen;  // creates a default pen  
  3.    
  4. pen.setStyle(Qt::DashDotLine);  
  5. pen.setWidth(3);  
  6. pen.setBrush(Qt::green);  
  7. pen.setCapStyle(Qt::RoundCap);  
  8. pen.setJoinStyle(Qt::RoundJoin);  
  9.    
  10. painter.setPen(pen);  
使用构造函数的优点是代码较短,但是参数含义不明确;使用 set 函数则正好反过来。
Pen Style: 用枚举类型Qt::PenStyle来定义

Cap Style:
The cap style defines how the end points of lines are drawn using QPainter. The cap style only apply to wide lines, i.e. when the width is 1 or greater.当线宽比较大时,才能看书画笔端点风格的差别。

Join Style:
The join style defines how joins between two connected lines can be drawn using QPainter. The join style only apply to wide lines, i.e. when the width is 1 or greater.当两个宽度大于1的线,端点连接时的风格;


代码:

使用画笔:

  1. void MyWidget::paintEvent(QPaintEvent *)  
  2. {  
  3.     QPainter painter(this);  
  4.     painter.drawLine(0,0,100,100);    //画直线  
  5.     //定义画笔  
  6.     QPen pen(Qt::green,5,Qt::DashLine,Qt::FlatCap,Qt::RoundJoin);  
  7.     painter.setPen(pen); //使用画笔  
  8.     QRectF rectangle(70.0, 40.0, 80.0, 60.0);  
  9.     int startAngle = 30 * 16;  
  10.     int spanAngle = 120 * 16;  
  11.     //绘制圆弧  
  12.     painter.drawArc(rectangle, startAngle, spanAngle);  
  13.   
  14.     /**********重新定义画笔***************/  
  15.     pen.setWidth(2);  
  16.     pen.setStyle(Qt::SolidLine);  
  17.   
  18.     painter.setPen(pen); //使用画笔  
  19.     painter.drawRect(50,50,20,100);  
  20. }  

使用画刷:
  1. void MyWidget::paintEvent(QPaintEvent *)  
  2. {  
  3.     QPainter painter(this);  
  4.     QBrush brush(QColor(0, 0, 255), Qt::Dense4Pattern);//创建画刷  
  5.     painter.setBrush(brush);                          //使用画刷  
  6.     painter.drawEllipse(220, 20, 50, 50);            //绘制椭圆  
  7.     //设置纹理  
  8.     brush.setTexture(QPixmap("../mydrawing/eryuelan.JPG"));  
  9.     //重新使用画刷  
  10.     painter.setBrush(brush);  
  11.     //定义四个点  
  12.     static const QPointF points[4] = {  
  13.         QPointF(270.0, 80.0),  
  14.         QPointF(290.0, 10.0),  
  15.         QPointF(350.0, 30.0),  
  16.         QPointF(390.0, 70.0)  
  17.     };  
  18.     //使用四个点绘制多边形  
  19.     painter.drawPolygon(points, 4);  
  20. }  



反走样:

显然,我们通过这条语句,将Antialiasing属性(也就是反走样)设置为 true。经过这句设置,我们就打开了QPainter的反走样功能。还记得我们曾经说过,QPainter是一个状态机,因此,只要这里我们打开了它,之后所有的代码都会是反走样绘制的了。由于反走样需要比较复杂的算法,在一些对图像质量要求不是很高的应用中,是不需要进行反走样的。为了提高效率,一般的图形绘制系统,如 Java2D、OpenGL 之类都是默认不进行反走样的。

虽然反走样比不反走样的图像质量高很多,但是,没有反走样的图形绘制还是有很大用处的。首先,就像前面说的一样,在一些对图像质量要求不高的环境下,或者说性能受限的环境下,比如嵌入式和手机环境,一般是不进行反走样的。另外,在一些必须精确操作像素的应用中,也是不能进行反走样的。这是由于反走样技术本身的限制的。请看下面的图片:

Photoshop 走样与反走样对比

这是使用 Photoshop 的铅笔和画笔工具绘制的 1 像素的点,放大 3200% 的视图。在一定程度上,我们可以认为,Photoshop 的铅笔工具是不进行反走样,而画笔是要进行反走样的。在放大的情况下就会知道,有反走样的情况下是不能进行精确到 1 像素的操作的。因为反走样很难让你控制到 1 个像素。这不是 Photoshop 画笔工具的缺陷,而是反走样算法的问题。反走样之所以看起来比较模糊,就是因为它需要以一种近似色来替换原始的像素色,这样一来就会显得模糊而圆滑。




渐变:

渐变是绘图中很常见的一种功能,简单来说就是可以把几种颜色混合在一起,让它们能够自然地过渡,而不是一下子变成另一种颜色。渐变的算法比较复杂,写得不好的话效率会很低,好在很多绘图系统都内置了渐变的功能,Qt 也不例外。渐变一般是用在填充里面的,所以,设置渐变是在QBrush里面。

Qt 提供了三种渐变:线性渐变(QLinearGradient)、辐射渐变(QRadialGradient)和角度渐变(QConicalGradient)。我们可以在 Qt API 手册中看到这几种渐变的区别:

线性渐变:
QLinearGradient 示例

辐射渐变:
QRadialGradient 示例

角度渐变:
QConicalGradient 示例

具体细节可以参考文档。下面我们通过一个示例看看如何使用渐变进行填充:

像以前一样,我们也只给出了paintEvent()的代码。这段代码看起来也相当清晰:首先我们打开了反走样,然后创建一个QLinearGradient对象实例。QLinearGradient也就是线性渐变,其构造函数有四个参数,分别是 x1,y1,x2,y2,即渐变的起始点和终止点。在这里,我们从 (60, 50) 点开始渐变,到 (200, 200) 点止。关于坐标的具体细节,我们会在后面的章节中详细介绍。渐变的颜色是在setColorAt()函数中指定的。下面是这个函数的签名:

这个函数的作用是,把 position 位置的颜色设置成 color。其中,position 是一个 [0, 1] 闭区间的数字。也就是说,position 是相对于我们建立渐变对象时做的那个起始点和终止点区间的一个比例。以这个线性渐变为例,在从 (60, 50) 到 (200, 200) 的线段上,在 0.2,也就五分之一处设置成白色,在 0.6 也就是五分之三处设置成绿色,在 1.0 也就是终点处设置成黑色。创建QBrush对象时,把这个渐变对象传递进去,然后就可以运行了:

线性渐变示例

下面我们开始一个更复杂,也更实用一些的例子:绘制一个色轮(color wheel)。所谓色轮,其实就是一个带有颜色的圆盘(或许你没听说过这个名字,但是你肯定见过这个东西),下面是色轮的运行结果:

色轮示例

我们来看看它的代码:

首先还是新建 QPainter 对象,开启反走样。然后我们将圆盘半径定义为 150。下面创建一个角度渐变实例,其构造函数同样接受三个参数:

前两个参数 cx 和 cy 组成角度渐变的中心点,第三个参数是渐变的起始角度。在我们的例子中,我们将渐变中心点设置为 (0, 0),起始角度为 0。类似线性渐变,角度渐变的setColorAt()函数同样接受两个参数,第一个是角度比例,第二个是颜色。例如,

将 0 度角设置为红色;

将 60 度角设置为黄色。由于一个圆周是 360 度,所以 60.0/360.0 即是这个角度的比例。其余代码以此类推。最后一句,我们将 1.0 处设置为红色,也就是重新回到起始处。至于颜色的分布,这是由颜色空间定义的,有兴趣的朋友可以查阅有关颜色模型的理论。

这是我们唯一不熟悉的函数。QPainter::translate(x, y)函数意思是,将坐标系的原点设置到 (x, y) 点。原本坐标系原点位于左上角,我们使用translate(r, r),将坐标原点设置为 (r, r)。这么一来,左上角的点的坐标就应该是 (-r, -r)。

最后,我们使用drawEllipse()函数绘制圆盘。注意,由于我们已经把坐标原点设置为 (r, r),因此,在绘制时,圆心应该是新的坐标 (0, 0),而不是原来的 (r, r)。

PS:为了理解translate()函数的作用,可以思考下,如果去掉translate()函数的调用,我们的程序应该如何修改。答案是:

不仅我们需要修改最后的绘制语句,还需要注意修改QConicalGradient定义时传入的中心点的坐标。

(2)辐射渐变

QRadialGradient::QRadialGradient ( const QPointF & center, qreal radius, const QPointF & focalPoint )
Constructs a radial gradient with the given center, radius and focalPoint.
需要指定圆心center和半径radius,这样就可以确定一个圆,然后再指定一个焦点focalPoint;焦点的位置为0, 圆环的位置为1,然后在焦点和圆环之间插入颜色。同样可以使用setspread()指定扩散方式。
  1. //辐射渐变  
  2.    QRadialGradient radialGradient(QPointF(200, 190), 50, QPointF(275, 200));  
  3.    radialGradient.setColorAt(0, QColor(255, 255, 100, 150));  
  4.    radialGradient.setColorAt(1, QColor(0, 0, 0, 50));  
  5.    painter.setBrush(radialGradient);  
  6.    painter.drawEllipse(QPointF(200, 190), 50, 50); 



  • 0
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值