Qt示例 | 2D Painting Example

举报
黑兔子 发表于 2022/10/17 16:41:22 2022/10/17
【摘要】 该示例展示了在Widget和GLWidget中同时执行相同的绘制操作表现出的效果。\ GLWidget的基类是QOpenGLWidget,Widget继承自QWidget类,它们的区别在于GLWidget中的渲染质量和速度取决于系统的OpenGL驱动程序提供的多重采样和硬件加速的支持级别。如果缺少对其中任何一个的支持,驱动程序可能会依赖于软件渲染器,以质量换取速度。\ 显示上的区别在于,调用相同的

示例运行效果:

1.gif

该示例展示了在Widget和GLWidget中同时执行相同的绘制操作表现出的效果。
GLWidget的基类是QOpenGLWidget,Widget继承自QWidget类,它们的区别在于GLWidget中的渲染质量和速度取决于系统的OpenGL驱动程序提供的多重采样和硬件加速的支持级别。如果缺少对其中任何一个的支持,驱动程序可能会依赖于软件渲染器,以质量换取速度。
显示上的区别在于,调用相同的渲染方法,当设置不同的多重采样数时,GLWidget展现的抗锯齿的效果是不一样的。

文件目录:

1653035707(1).jpg

主窗体为window类,左边的绘图窗体为Widget类,右边的绘图窗体为GLWidget类,Helper类为绘制操作类。

具体实现:

Helper类:

1653286970(1).jpg

在本例中,绘制操作由Helper类执行。这样做是为了实现对我们创建的Widget子类和GLWidget子类执行相同的绘制操作。Helper类是项目中最小(最底层)的一个类。
它除了构造函数,只提供了一个paint()函数,用于对小部件子类提供的绘制器进行绘制。

构造函数:

1653287280(1).jpg

构造函数设置了在小部件上绘制内容所需的资源。包括小部件的背景颜色画刷、部件中圆形图案的画刷(这个画刷的颜色是渐变色)、部件中文字的字体、圆形图案的画笔、文字画笔。

QLinearGradient:

这里圆形图案的画刷是一个渐变色,QLinearGradient是Qt中支持的一种渐变填充方式,显示从起点到终点的直线渐变。
QLinearGradient类构造函数的第一个参数指定渐变的起点,第二个参数指定渐变的终点, 然后显示渐变。成员函数setColorAt()设置起点和终点之间要显示的颜色。

paint函数:

1653288953(1).jpg

实际的绘制操作是在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类:

1653297557(1).jpg

Widget类提供了一个基本的自定义小部件,用它来显示由Helper类绘制的简单动画。

除了构造函数之外,它只包含一个paintEvent()函数,以及一个实现动画的槽函数。一个Helper成员变量,和一个记录自上次更新以来所经过的时间的elapsed成员变量。

构造函数:

1653297972(1).jpg

构造函数用来初始化成员变量,保存Helper对象并调用Helper的构造函数,为小部件设置固定大小。

animate函数:

1653299224(1).jpg

这是一个定时器的槽函数,当一个计时器超时时,animate()槽函数将被调用。
这里,elapsed记录自计时器上次超时以来所经过的时间间隔,并在重新绘制小部件之前将其添加到现有值中。因为Helper类中使用的动画每秒钟循环一次,所以这里使用取余操作来确保消耗的变量总是小于1000。

paintEvent函数:

image.png

每次调用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类:

image.png

GLWidget类中包含的函数和成员变量与Widget中的一样。

image.png
GLWidget函数的实现与Widget中的也基本一样,相同的函数不做赘述。
构造函数中,多了一句代码:setAutoFillBackground(false);用来设置小部件背景是否自动填充。

GLWidget使用Helper类和Widget使用Helper类有一点区别,在GLWidget中Helper的构造函数会以指定QGL::SampleBuffers标志的格式调用。如果你的系统的OpenGL驱动支持这个功能,就可以启用抗锯齿。

Window类:

1653356624(1).jpg

Window类中包含一个将在所有小部件之间共享的Helper对象。

构造函数:

image.png

Window类中只有一个构造函数,在构造函数中完成了所有的工作:创建小部件,并将它们一起加入到布局中。
为了实现动画效果,创建一个50毫秒的计时器,并将溢出信号连接到Widget和GLWidget对象的animate()槽函数。定时器启动后,小部件会以每秒20帧左右的速度更新。

main函数:

1653038193(1).jpg

main函数的主要功能:

  1. 开启多重采样:设置多重采样数,从而使锯齿更加平滑,抗锯齿。缺省情况下,多重采样功能是关闭的。
    多重采样是一种抗锯齿技术,它通过在一个像素上进行多次采样多次计算并最终汇总,可使绘制的图像边缘更加平滑。通过这种方式绘制出来的图片质量更高,显得更真实。

  2. 显示Window窗体。

到这里,2D Painting Example示例中的所有代码都介绍完了。基本框架就是Window中有一个Widget和一个GLWidget,在这两个Widget中调用Helper进行绘制。

一点思考:

  • 对于这个项目,因为它的绘图方法会同时用在两个不同的部件中,所以,这里将绘图方法单独封装成一个类。在主窗体中只创建一个,子窗体中调用。
  • 成员变量要初始化,放在构造函数中。
  • Q_DECL_OVERRIDE:写在虚函数后面,防止虚函数写错,例如参数类型错误等,在编译时会报编译错误信息。
  • 在实现这种永动效果的动画时,经常用到时间流逝这个概念,并定义"elapsed"变量来表示。项目里面的对时间取余方法用的非常好,可以保证数的范围在我们设定的区间内,不会越来越大,防止溢出。
【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。