Я получаю доступ к QThread, чтобы не блокировать мой графический интерфейс, выполняя, возможно, более длинные потоки в фоновом режиме. Я пытаюсь на практике написать простое приложение: таймер обратного отсчета, который я могу начать, нажав на кнопку «Пуск», тем самым инициировав цикл обратного отсчета, который я могу приостановить, нажав кнопку «Пауза».Простое применение QThread
Я хочу сделать это «правильным способом» использования QThread (пояснил over here), то есть подклассифицирует QObject, а затем присоединяет экземпляр этого подкласса к QThread через moveToThread. Я сделал GUI с QtDesigner и это то, что я изменил до сих пор (от других примеров в Сети) в Python:
from PyQt4 import QtGui, QtCore
from PyQt4.QtCore import QThread, SIGNAL
import time, sys, mydesign
class SomeObject(QtCore.QObject):
def __init__(self, lcd):
super(self.__class__, self).__init__()
self.lcd = lcd
self.looping = True
finished = QtCore.pyqtSignal()
def pauseLoop(self):
print "Hello?"
self.looping = False
def longRunning(self):
count = 10
self.lcd.display(count)
while count > 0 and self.looping:
time.sleep(1)
count -= 1
self.lcd.display(count)
self.finished.emit()
class ThreadingTutorial(QtGui.QMainWindow, mydesign.Ui_MainWindow):
def __init__(self):
super(self.__class__, self).__init__()
self.setupUi(self)
#an instance of SomeObject gets attached to
#an instance of wrapper class QThread()
#objThread is a wrapper object for an instance of
# self-defined class SomeObject
self.objThread = QtCore.QThread()
self.obj = SomeObject(self.lcdNumber)
self.obj.moveToThread(self.objThread)
#connect all the signals
self.obj.finished.connect(self.objThread.quit)
self.objThread.started.connect(self.obj.longRunning)
self.startCountdown.clicked.connect(self.objThread.start)
self.pauseButton.clicked.connect(self.obj.pauseLoop)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
form = ThreadingTutorial()
form.show()
app.exec_()
Графический интерфейс пользователя не заблокирован, но функция pauseLoop() только получает казненных после петли законченный. Как мне сделать это, чтобы выполнить мою задачу, чтобы иметь возможность приостановить цикл в longRunning()?
Thx заранее!
обновление:
С помощью Стива и замечаний three_pineapples, я придумал следующее решение, используя внутренний цикл событий в objThread:
from PyQt4 import QtGui, QtCore
from PyQt4.QtCore import QThread, SIGNAL, QTimer
import time, sys, mydesign
class SomeObject(QtCore.QObject):
def __init__(self):
super(self.__class__, self).__init__()
self.looping = True
self.count = 10
self.timer = QTimer(self)
self.timer.start(1000)
self.connect(self.timer, SIGNAL("timeout()"), self.longRunning)
finished = QtCore.pyqtSignal()
iterated = QtCore.pyqtSignal()
def pauseLoop(self):
self.looping = False
def longRunning(self):
if self.count > 0 and self.looping:
self.count -= 1
self.iterated.emit()
else:
self.finished.emit()# sends signal for stopping event loop
class ThreadingTutorial(QtGui.QMainWindow, mydesign.Ui_MainWindow):
def __init__(self):
super(self.__class__, self).__init__()
self.setupUi(self)
#an instance of SomeObject gets attached to
#an instance of wrapper class QThread()
#objThread is a wrapper object for an instance of
# self-defined class SomeObject
self.objThread = QtCore.QThread()
self.obj = SomeObject()
self.lcdNumber.display(self.obj.count)
self.obj.moveToThread(self.objThread)
#connect all the signals
self.obj.finished.connect(self.objThread.quit)
self.obj.iterated.connect(lambda: self.lcdNumber.display(self.obj.count))
self.objThread.started.connect(self.obj.longRunning)
self.startCountdown.clicked.connect(self.objThread.start)
self.pauseButton.clicked.connect(self.obj.pauseLoop)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
form = ThreadingTutorial()
form.show()
app.exec_()
Обратите внимание, что вы никогда не должны пытаться использовать объекты GUI из другого потока. Также вы не можете перемещать объект GUI в другой поток. Вы получите случайные сбои приложений (segfaults). Я ваши примеры выше, вы по-прежнему получаете доступ к виджету LCD из вторичной нити, что плохо! Вместо этого вы должны посылать сигнал в основной поток каждый раз, когда хотите обновить виджет, чтобы вы могли безопасно взаимодействовать с графическим интерфейсом. –
Я изменил свой ответ, чтобы проиллюстрировать комментарий three_pineapples. – Steve
Спасибо за хороший момент. Я буду держать это правило в уме. Я также внесу поправки в свое решение по этому правилу. – user2949762