环境: VS2017+Qt5.14
实现功能: 一个QWidget窗口,初始状态是隐藏的,在这个QWidget中有一个QLabel, 在QWidget显示的同时,打开定时器,定时器时间间隔为42ms,在定时器响应函数中执行QLabel上图片的定时切换,从而达到动画的效果。
具体实现: QWidget执行show()之后,立马打开定时器,在定时器的timeout()中执行ui.label->setPixmap(m_Pixmap[i]);
遇到的问题: show()函数执行完毕,定时器start()执行完毕,但是定时器的响应函数迟迟不响应。有时可能等一两秒就可以反应过来,有时可能需要八九秒才可以反应过来,反应过来后,定时器正常运行,动画正常显示。或者在定时器不响应的时候,用鼠标在窗口上移动一下,定时器的响应函数就可以正常响应了。但是响应之前就像界面卡住了一样。
于是开始查找原因及解决办法,把所有相关代码注释掉后一点点放开,发现造成这种现象的原因就是因为调用show()函数引起的。这就有点鸡肋了,如果是因为show()函数引起的,说明是主线程在走UI的一些东西卡住了,这怎么解决?!
在网上查了很多种解释和方法,show()和hide()函数确实会引起界面刷新,从而导致主线程卡住,如果是静态页面,一般不会有什么问题,但是如果是动态页面,就会出现程序卡住的现象。
解决办法: 一种方法是使用stackWidget,调用stackWidget的setCurrentIndex()实现页面的切换,避免使用show()、hide()函数。我试过这种方法,但是因为我们的窗口样式和父子继承关系很复杂,所以stackWidget加入进来后,显示效果并不能达到我们的需求,所以放弃这种方法。
另一种方法是一个比较有局限性的方法,就是调用Qt的事件轮循机制。
m_bExit = false; while (!m_bExit) { QCoreApplication::processEvents(QEventLoop::AllEvents, 100); }
在需要退出事件轮循的地方将m_bExit = true;
调用事件轮循后,界面就不会出现卡死的现象了,因为在处理主进程的同时可以去处理其他的消息、事件。这样就实现了同时处理主进程和其他线程的事件,达到在线程中刷新界面的效果。
注:
因为我的这部分代码属于在show()的同时操作界面,所以即使开线程,也需要在线程中发信号出来,执行相应操作,所以如果用线程+信号槽机制还是会卡死。
目前上面这种方法是我试过的效果最好的方法,但是他会有一个问题,就是如果在这个while()循环执行期间退出程序,程序是退不掉的,需要手动调用m_bExit = true,或者销毁资源的函数,先退出while循环,然后再执行程序的close()才可以。