PyQt4.11+Python2.7下QSplashScreen需两次processEvents才能正常显示
问题分析与解决方案
首先咱们得揪出核心问题:你在主线程里直接执行耗时函数,完全阻塞了UI事件循环,导致QSplashScreen的图片绘制没办法及时完成。
第一次调用app.processEvents()时,虽然触发了启动屏的显示事件,但紧接着就钻进了耗时循环,主线程被占得满满当当,根本没机会完成图片的渲染工作,所以你看到的是灰色方框。后面在第一次耗时函数后再加一次processEvents(),相当于给了事件循环喘口气的机会,才赶在第二次耗时任务前完成了图片绘制,所以启动屏能正常显示,但这种方式完全依赖事件队列的状态,自然不稳定。
正确的解决思路:把耗时任务丢到子线程
Qt的UI操作必须在主线程完成,但耗时任务绝对不能占着主线程不放,得放到子线程里执行。这样主线程不会被阻塞,能正常处理UI绘制和各种事件,启动屏的显示自然就稳定了。下面是修改后的完整代码:
import sys from PyQt4 import QtCore, QtGui class WorkerThread(QtCore.QThread): # 定义信号,任务全部完成时触发 task_finished = QtCore.pyqtSignal() def run(self): # 这里执行你的耗时任务,可以是多次调用或者单个大任务 self.timeConsumingFunction() self.timeConsumingFunction() # 任务完成后发送信号 self.task_finished.emit() def timeConsumingFunction(self): b = 0 for a in range(100000000): b += a print(b) class splashTest(QtGui.QDialog): def __init__(self, parent=None): super(splashTest, self).__init__(parent) self.setGeometry(0, 0, 800, 400) self.tabWidget = QtGui.QTabWidget(self) self.tabWidget.setGeometry(QtCore.QRect(0, 0, 500, 500)) self.tabWidget.setObjectName("tabWidget") self.tab1 = QtGui.QWidget() self.tab2 = QtGui.QWidget() self.tab3 = QtGui.QWidget() self.tabWidget.addTab(self.tab1, "tab1") self.tabWidget.addTab(self.tab2, "tab2") self.tabWidget.addTab(self.tab3, "Click Me") self.tabWidget.setCurrentIndex(1) self.tabWidget.currentChanged.connect(self.whichTabIsSelected) # 初始化线程对象 self.worker_thread = None def whichTabIsSelected(self): if self.tabWidget.currentIndex() == 2: # 创建并显示启动屏 splash_pix = QtGui.QPixmap('splash.png') splash = QtGui.QSplashScreen(splash_pix, QtCore.Qt.WindowStaysOnTopHint) splash.setMask(splash_pix.mask()) splash.show() # 创建子线程,绑定信号与槽 self.worker_thread = WorkerThread() # 任务完成后关闭启动屏 self.worker_thread.task_finished.connect(lambda: splash.finish(self.tabWidget)) # 线程结束后自动清理资源 self.worker_thread.task_finished.connect(self.worker_thread.deleteLater) # 启动线程 self.worker_thread.start() else: pass if __name__ == "__main__": app = QtGui.QApplication(sys.argv) myapp = splashTest() myapp.show() sys.exit(app.exec_())
为什么这个方案更可靠?
- 耗时任务在子线程独立执行,主线程完全不被阻塞,QSplashScreen的绘制事件能被正常处理,启动屏会立刻显示完整图片。
- 通过Qt的信号槽机制,在任务完成后自动关闭启动屏,逻辑清晰且稳定,完全不用依赖
processEvents()的调用时机。
你之前尝试的睡眠定时器、重绘、更新这些方法之所以无效,本质上都是因为主线程被耗时任务卡死了,这些操作都需要主线程处理事件才能生效,自然解决不了根本问题。
内容的提问来源于stack exchange,提问作者PhilFPH
相关产品推荐
相关产品推荐

