Qt示例 | 2D Painting Example
示例运行效果:
该示例展示了在Widget和GLWidget中同时执行相同的绘制操作表现出的效果。
GLWidget的基类是QOpenGLWidget,Widget继承自QWidget类,它们的区别在于GLWidget中的渲染质量和速度取决于系统的OpenGL驱动程序提供的多重采样和硬件加速的支持级别。如果缺少对其中任何一个的支持,驱动程序可能会依赖于软件渲染器,以质量换取速度。
显示上的区别在于,调用相同的渲染方法,当设置不同的多重采样数时,GLWidget展现的抗锯齿的效果是不一样的。
文件目录:
主窗体为window类,左边的绘图窗体为Widget类,右边的绘图窗体为GLWidget类,Helper类为绘制操作类。
具体实现:
Helper类:
在本例中,绘制操作由Helper类执行。这样做是为了实现对我们创建的Widget子类和GLWidget子类执行相同的绘制操作。Helper类是项目中最小(最底层)的一个类。
它除了构造函数,只提供了一个paint()函数,用于对小部件子类提供的绘制器进行绘制。
构造函数:
构造函数设置了在小部件上绘制内容所需的资源。包括小部件的背景颜色画刷、部件中圆形图案的画刷(这个画刷的颜色是渐变色)、部件中文字的字体、圆形图案的画笔、文字画笔。
QLinearGradient:
这里圆形图案的画刷是一个渐变色,QLinearGradient是Qt中支持的一种渐变填充方式,显示从起点到终点的直线渐变。
QLinearGradient类构造函数的第一个参数指定渐变的起点,第二个参数指定渐变的终点, 然后显示渐变。成员函数setColorAt()设置起点和终点之间要显示的颜色。
paint函数:
实际的绘制操作是在paint()函数中执行的。
参数为:一个在绘制设备上绘制的QPainter(这里绘制设备为QWidget或QGLWidget);
一个QPaintEvent,QPaintEvent提供关于将要绘制的区域的信息;
以及自上次更新绘制设备以来所经过的时间(单位:毫秒)。
这里完成了对小部件背景颜色的填充、对圆形图案的绘制以及对文字的绘制。
螺旋效果的实现是先实现静态的圆形的螺旋图案,然后开启一个定时器使用指定的运行时间使它们动起来,使它们看起来像围绕着坐标系统的原点在向外移动。
translate:
按给定的偏移量平移坐标系;坐标原点原本在部件的左上角的位置,为(0,0),调用translate(100,100)后,部件的坐标原点向右挪了100个像素,向下挪了100个像素。这时新的坐标原点在距离部件左上角(100,100)的位置,这里要注意,移动后的坐标原点的值仍为(0,0)。
save和restore:
保存和恢复QPainter的状态。在绘制复杂图形时候,我们常用这个功能,来保证图形的正确绘制,不因为多次变换QPainter属性导致绘图错误,只需要调用这一组函数,就能实现QPainter状态的存储。
rotate:
顺时针旋转坐标系。参数为角度,单位:度。
drawEllipse:
绘制由给定矩形定义的椭圆。
Widget类:
Widget类提供了一个基本的自定义小部件,用它来显示由Helper类绘制的简单动画。
除了构造函数之外,它只包含一个paintEvent()函数,以及一个实现动画的槽函数。一个Helper成员变量,和一个记录自上次更新以来所经过的时间的elapsed成员变量。
构造函数:
构造函数用来初始化成员变量,保存Helper对象并调用Helper的构造函数,为小部件设置固定大小。
animate函数:
这是一个定时器的槽函数,当一个计时器超时时,animate()槽函数将被调用。
这里,elapsed记录自计时器上次超时以来所经过的时间间隔,并在重新绘制小部件之前将其添加到现有值中。因为Helper类中使用的动画每秒钟循环一次,所以这里使用取余操作来确保消耗的变量总是小于1000。
paintEvent函数:
每次调用update()函数,都会出发paintEvent()函数,在paintEvent()函数中,调用Helper的paint()函数实现绘制事件。
beigin和end:
开始绘制绘制设备和绘制结束。
当创建无参的QPainter painter时,需要手动调用painter.begin(this)和painter.end();
当创建有参的QPainter painter(this)时,不需要手动调用painter.begin(this)和painter.end(),系统会自动调用。
setRenderHint(QPainter::Antialiasing):
反走样是图形学中的重要概念,用以防止通常所说的“锯齿”现象的出现。很多系统的绘图 API 里面都内置了有关反走样的算法,不过由于性能问题,默认一般是关闭的,Qt 也不例外。
通过这条语句,将Antialiasing属性设置为默认的true,就打开了QPainter的反走样功能,也就是抗锯齿。
GLWidget类:
GLWidget类中包含的函数和成员变量与Widget中的一样。
GLWidget函数的实现与Widget中的也基本一样,相同的函数不做赘述。
构造函数中,多了一句代码:setAutoFillBackground(false);用来设置小部件背景是否自动填充。
GLWidget使用Helper类和Widget使用Helper类有一点区别,在GLWidget中Helper的构造函数会以指定QGL::SampleBuffers标志的格式调用。如果你的系统的OpenGL驱动支持这个功能,就可以启用抗锯齿。
Window类:
Window类中包含一个将在所有小部件之间共享的Helper对象。
构造函数:
Window类中只有一个构造函数,在构造函数中完成了所有的工作:创建小部件,并将它们一起加入到布局中。
为了实现动画效果,创建一个50毫秒的计时器,并将溢出信号连接到Widget和GLWidget对象的animate()槽函数。定时器启动后,小部件会以每秒20帧左右的速度更新。
main函数:
main函数的主要功能:
-
开启多重采样:设置多重采样数,从而使锯齿更加平滑,抗锯齿。缺省情况下,多重采样功能是关闭的。
多重采样是一种抗锯齿技术,它通过在一个像素上进行多次采样多次计算并最终汇总,可使绘制的图像边缘更加平滑。通过这种方式绘制出来的图片质量更高,显得更真实。 -
显示Window窗体。
到这里,2D Painting Example示例中的所有代码都介绍完了。基本框架就是Window中有一个Widget和一个GLWidget,在这两个Widget中调用Helper进行绘制。
一点思考:
- 对于这个项目,因为它的绘图方法会同时用在两个不同的部件中,所以,这里将绘图方法单独封装成一个类。在主窗体中只创建一个,子窗体中调用。
- 成员变量要初始化,放在构造函数中。
- Q_DECL_OVERRIDE:写在虚函数后面,防止虚函数写错,例如参数类型错误等,在编译时会报编译错误信息。
- 在实现这种永动效果的动画时,经常用到时间流逝这个概念,并定义"elapsed"变量来表示。项目里面的对时间取余方法用的非常好,可以保证数的范围在我们设定的区间内,不会越来越大,防止溢出。
- 点赞
- 收藏
- 关注作者
评论(0)