Przykład właściwego sposobu użycia QThread w PyQt?
Próbuję nauczyć się używać QThreads w aplikacji PyQt Gui. Mam rzeczy, które działa na chwilę ,z (zazwyczaj) punkty, w których można zaktualizować Gui, ale chciałbym podzielić główne pracy się do własnego wątku (czasami rzeczy utknie,i byłoby miło, aby w końcu mieć anuluj / spróbuj ponownie przycisk, który oczywiście nie działa, jeśli Gui jest zamrożony, ponieważ pętla główna jest zablokowana).
I ' ve read https://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/. ta strona mówi, że ponowne wdrożenie metody run
nie jest sposobem, aby to zrobić. Problem, który mam, to znalezienie przykładu PyQt, który ma główny wątek robiący Gui i wątek roboczy, który nie robi tego w ten sposób. Post na blogu jest dla C++, więc chociaż przykłady pomagają, nadal jestem trochę zagubiony. Czy ktoś mógłby mi wskazać przykład jak to zrobić w Pythonie?
2 answers
Oto roboczy przykład oddzielnego wątku roboczego, który może wysyłać i odbierać sygnały, aby umożliwić mu komunikację z GUI.
Zrobiłem dwa proste przyciski, jeden, który rozpoczyna długie obliczenia w osobnym wątku, a drugi, który natychmiast kończy obliczenia i resetuje wątek roboczy.
Przymusowe zakończenie wątku, jak to się tutaj robi, nie jest ogólnie najlepszym sposobem na robienie rzeczy, ale są sytuacje, w których zawsze z wdziękiem wychodzenie nie jest opcja.
from PyQt4 import QtGui, QtCore
import sys
import random
class Example(QtCore.QObject):
signalStatus = QtCore.pyqtSignal(str)
def __init__(self, parent=None):
super(self.__class__, self).__init__(parent)
# Create a gui object.
self.gui = Window()
# Create a new worker thread.
self.createWorkerThread()
# Make any cross object connections.
self._connectSignals()
self.gui.show()
def _connectSignals(self):
self.gui.button_cancel.clicked.connect(self.forceWorkerReset)
self.signalStatus.connect(self.gui.updateStatus)
self.parent().aboutToQuit.connect(self.forceWorkerQuit)
def createWorkerThread(self):
# Setup the worker object and the worker_thread.
self.worker = WorkerObject()
self.worker_thread = QtCore.QThread()
self.worker.moveToThread(self.worker_thread)
self.worker_thread.start()
# Connect any worker signals
self.worker.signalStatus.connect(self.gui.updateStatus)
self.gui.button_start.clicked.connect(self.worker.startWork)
def forceWorkerReset(self):
if self.worker_thread.isRunning():
print('Terminating thread.')
self.worker_thread.terminate()
print('Waiting for thread termination.')
self.worker_thread.wait()
self.signalStatus.emit('Idle.')
print('building new working object.')
self.createWorkerThread()
def forceWorkerQuit(self):
if self.worker_thread.isRunning():
self.worker_thread.terminate()
self.worker_thread.wait()
class WorkerObject(QtCore.QObject):
signalStatus = QtCore.pyqtSignal(str)
def __init__(self, parent=None):
super(self.__class__, self).__init__(parent)
@QtCore.pyqtSlot()
def startWork(self):
for ii in range(7):
number = random.randint(0,5000**ii)
self.signalStatus.emit('Iteration: {}, Factoring: {}'.format(ii, number))
factors = self.primeFactors(number)
print('Number: ', number, 'Factors: ', factors)
self.signalStatus.emit('Idle.')
def primeFactors(self, n):
i = 2
factors = []
while i * i <= n:
if n % i:
i += 1
else:
n //= i
factors.append(i)
if n > 1:
factors.append(n)
return factors
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.button_start = QtGui.QPushButton('Start', self)
self.button_cancel = QtGui.QPushButton('Cancel', self)
self.label_status = QtGui.QLabel('', self)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.button_start)
layout.addWidget(self.button_cancel)
layout.addWidget(self.label_status)
self.setFixedSize(400, 200)
@QtCore.pyqtSlot(str)
def updateStatus(self, status):
self.label_status.setText(status)
if __name__=='__main__':
app = QtGui.QApplication(sys.argv)
example = Example(app)
sys.exit(app.exec_())
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-25 16:40:59
Masz rację, że dobrze jest mieć wątek roboczy wykonujący przetwarzanie, podczas gdy główny wątek wykonuje GUI. Ponadto PyQt dostarcza Oprzyrządowanie do gwintów z mechanizmem sygnałowo-szczelinowym, który jest bezpieczny dla gwintów.
To może brzmieć interesująco . W swoim przykładzie budują GUI
import sys, time
from PyQt4 import QtCore, QtGui
class MyApp(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setGeometry(300, 300, 280, 600)
self.setWindowTitle('threads')
self.layout = QtGui.QVBoxLayout(self)
self.testButton = QtGui.QPushButton("test")
self.connect(self.testButton, QtCore.SIGNAL("released()"), self.test)
self.listwidget = QtGui.QListWidget(self)
self.layout.addWidget(self.testButton)
self.layout.addWidget(self.listwidget)
def add(self, text):
""" Add item to list widget """
print "Add: " + text
self.listwidget.addItem(text)
self.listwidget.sortItems()
def addBatch(self,text="test",iters=6,delay=0.3):
""" Add several items to list widget """
for i in range(iters):
time.sleep(delay) # artificial time delay
self.add(text+" "+str(i))
def test(self):
self.listwidget.clear()
# adding entries just from main application: locks ui
self.addBatch("_non_thread",iters=6,delay=0.3)
(simpel ui zawierający widżet listy, do którego dodamy niektóre elementy, klikając przycisk)
Możesz następnie utworzyć naszą własną klasę wątku, jeden exemple jest
class WorkThread(QtCore.QThread):
def __init__(self):
QtCore.QThread.__init__(self)
def __del__(self):
self.wait()
def run(self):
for i in range(6):
time.sleep(0.3) # artificial time delay
self.emit( QtCore.SIGNAL('update(QString)'), "from work thread " + str(i) )
self.terminate()
Redefiniujesz metodę run()
. Możesz znaleźć alternatywę dla terminate()
, zobacz samouczek.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-02 06:21:17