Вам необходимо разрешить обработку событий во время работы цикла, чтобы приложение могло оставаться отзывчивым.
Что еще более важно, для длительных задач вам необходимо предоставить пользователю возможность остановить цикл после его запуска.
Один простой способ сделать это — запустить цикл с помощью таймера, а затем периодически вызывать 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), решение этой проблемы зависит полностью от характера задач, которые должны выполняться одновременно с графическим интерфейсом. Более фундаментально, это определяется тем, что CPython имеет глобальную блокировку интерпретатора ( ГИЛ).
Я не собираюсь пытаться объяснить GIL CPython: вместо этого я просто рекомендую посмотреть этот превосходный видео PyCon Дэйва Бизли, и на этом остановимся.
Как правило, при попытке запуска графического интерфейса пользователя одновременно с фоновой задачей первый вопрос, который следует задать: связана ли задача с вводом-выводом или процессором?
Если это связано с вводом-выводом (например, доступ к локальной файловой системе, загрузка из Интернета и т. д.), то решение обычно довольно простое, потому что CPython всегда освобождает GIL для операций ввода-вывода. Фоновую задачу можно просто выполнить асинхронно или выполнить рабочий поток, и ничего особенного делать не нужно, чтобы графический интерфейс оставался отзывчивым.
Основные трудности возникают с задачами, связанными с процессором, когда возникает второй вопрос: можно ли разбить задачу на серию небольших шагов?
Если это возможно, то решение состоит в том, чтобы периодически отправлять запросы в поток GUI для обработки его текущего стека ожидающих событий. Приведенный выше демо-скрипт является грубым примером этой техники. Чаще задача выполняется в отдельном рабочем потоке, который выдает сигнал обновления графического интерфейса по мере завершения каждого шага задачи. (Примечание: важно убедиться, что рабочий поток никогда сам не пытается выполнять какие-либо операции, связанные с графическим интерфейсом).
Но если задачу нельзя разбить на маленькие шаги, то ни одно из обычных решений многопоточного типа не сработает. Графический интерфейс просто зависнет до тех пор, пока задача не будет завершена, независимо от того, используются ли потоки или нет.
Для этого окончательного сценария единственным решением является использование отдельного процесса, а не отдельного потока, т. е. использование многопроцессорный модуль. Конечно, это решение будет эффективным только в том случае, если в целевой системе доступно несколько ядер ЦП. Если есть только одно ядро ЦП, с которым можно играть, в основном ничего нельзя сделать, чтобы помочь (кроме переключения на другую реализацию Python или вообще на другой язык).
person
ekhumoro
schedule
07.11.2012