Qt中有一个Graphics View框架,使用这个框架,我们可以做一个简单的小启动动画。这篇文章不在于实现这个效果,而注重于实现过程发现的问题、解决思路以及对整个框架的理解。
1.创建场景、视图
我们需要创建一个开机动画,首先需要有地方来进行动画的显示,所以添加了视图、但这里的视图只有显示功能,所以我们还需要添加一个能够放置动画元素的地方,也就是场景。
2.设置场景
创建场景后,我们可以根据我们的需要,进行添加动画的背景了。但此时会出现问题,单纯添加背景后,背景图片会显示不正常,这是由于场景的坐标体系(以窗口中心为原点)与视图的坐标体系(以窗口左上角为原点)不同,造成了显示异常。
图 1setSenceRect可以看做是:将(无限大的)场景设置为width*height大小的矩形框,起始位置在view的(0,0)
3.创建图元
有了背景与视图后,我们还需要添加开机动画中的需要元素,即图元。在此之前,我们需要先创建图元,但尝试使用QGraphicsItem创建对象时,会发现无法创建对象。因为QGraphicsItem类中有两个纯虚函数,分别是:
QRectF boundingRect() = 0; 以及 void paint() = 0;
所以,QGraphicsItem是一个抽象类,这就需要我们去自己写一个继承于此类的派生类,并重定义这两个纯虚函数,才能创建图元对象。
4.添加图元到场景、设置位置
创建好图元后,我们要将他们添加到场景中,才能进行显示。但我们会发现,图元的坐标体系与场景相同,而不同于视图。可是我们在添加图元时并没有进行坐标转换,也可以显示到正常位置,这是因为,此时的setPos是相对于视图的,我们是在视图类构造中创建的图元对象,所以类似于我们在添加控件时的操作,传入的坐标是相对于视图的坐标,也就不需要坐标转换。
5.移动图元、碰撞检测
创建好图元后,我们需要实现的操作是两个图元碰撞后停下,并弹出确认窗口。
首先我们需要对图元进行移动,Qt中有提供一个用于移动图元的虚函数advance(),x为正则向右移动,x为负则向左移动,y同理。但与上面不同的是,此时我们的移动是基于图元自身坐标的移动,就需要我们进行坐标转换,Qt也为我们提供了这样一个函数mapToScene(),可以进行坐标转换。
而我们需要制作的是一个动画,我们必须多次、自动地调用advance函数,所以我们使用Qt中的计时器,设置了间隔时间20ms,每当到达设定时间之后就发送信号timeout();调用槽函数advance();实现图元的动画效果
通过advance函数偏移后,我们要检测移动后的两个图元是否碰撞,我们就需要使用Qt中的另一个函数collidingItems();进行碰撞检测,
这个函数的返回值是一个图元类的链表容器,所以我们只需要判断容器的元素数量即可。
接着,碰撞之后会emit sendStop()信号,这个信号用于停下计时器。但实现时出现弹窗两次的现象,查阅Qt官方手册后发现,是由于此处advance函数会自动调用两次,一次phase==0,另一次phase==1,函数中自带的phase就是用来判断此次调用是advance前还是advance后的,所以我们只需判断选择其中一次发送sendStop()信号即可。
另外,我们需要进行弹窗提示,所以又重新写了一个槽函数closeTime();在里面关闭计时器,并且使用QMessageBox的静态成员information,再在弹窗后显示Login窗口。