您需要在循环运行时允许事件处理,以使应用程序保持响应。
更重要的是,对于长时间运行的任务,您需要提供一种方法让用户在开始后停止循环。
一种简单的方法是使用计时器启动循环,然后在循环运行时定期调用qApp.processEvents。
这是一个执行此操作的演示脚本:
import sys, time
from PyQt4 import QtGui, QtCore
class ProgressBar(QtGui.QWidget):
def __init__(self, parent=None, total=20):
super(ProgressBar, self).__init__(parent)
self.progressbar = QtGui.QProgressBar()
self.progressbar.setMinimum(1)
self.progressbar.setMaximum(total)
self.button = QtGui.QPushButton('Start')
self.button.clicked.connect(self.handleButton)
main_layout = QtGui.QGridLayout()
main_layout.addWidget(self.button, 0, 0)
main_layout.addWidget(self.progressbar, 0, 1)
self.setLayout(main_layout)
self.setWindowTitle('Progress')
self._active = False
def handleButton(self):
if not self._active:
self._active = True
self.button.setText('Stop')
if self.progressbar.value() == self.progressbar.maximum():
self.progressbar.reset()
QtCore.QTimer.singleShot(0, self.startLoop)
else:
self._active = False
def closeEvent(self, event):
self._active = False
def startLoop(self):
while True:
time.sleep(0.05)
value = self.progressbar.value() + 1
self.progressbar.setValue(value)
QtGui.qApp.processEvents()
if (not self._active or
value >= self.progressbar.maximum()):
break
self.button.setText('Start')
self._active = False
app = QtGui.QApplication(sys.argv)
bar = ProgressBar(total=101)
bar.show()
sys.exit(app.exec_())
更新
假设您使用的是Python的C实现(即CPython),那么解决此问题完全取决于必须与GUI同时运行的任务的性质。更根本地,它由于CPython具有全局解释器锁定(GIL)而确定。
我不打算对CPython的GIL进行任何解释:相反,我将简单推荐观看Dave Beazley的这个出色的PyCon视频,并且就此结束。
通常,在尝试与后台任务并发运行GUI时,要问的第一个问题是:任务是IO限制还是CPU限制?
如果是IO限制(例如访问本地文件系统,从互联网下载等),则解决方案通常非常简单,因为CPython始终会释放GIL以进行I/O操作。后台任务可以异步地完成,或者由工作线程执行,并且不需要执行任何特殊操作以保持GUI响应。
主要困难发生在CPU限制的任务中,此时有第二个问题需要问:该任务是否可以分解成一系列小步骤?
如果可以,则解决方案是定期向GUI线程发送请求,以处理其当前挂起事件的堆栈。上面的演示脚本是此技术的一个简单示例。更通常地,任务将在单独的工作线程中执行,该线程将在完成任务的每个步骤时发出gui-update信号。(注意:确保工作线程永远不会尝试进行任何与GUI相关的操作很重要)。
但是,如果任务不能分解为小步骤,则不会有任何常规线程类型的解决方案有效。GUI将一直冻结,直到任务完成,无论是否使用线程。
对于这种最终情况,唯一的解决方案是使用单独的进程,而不是单独的线程-即利用多处理模块。当然,此解决方案只在目标系统具有多个CPU核心时才有效。如果只有一个CPU核心可供使用,基本上无法做任何事情以提高性能(除了切换到不同的Python实现或完全不同的语言)。