it-swarm.com.de

Beispiel für die richtige Verwendung von QThread in PyQt?

Ich versuche zu lernen, wie man QThreads in einer PyQt Gui-Anwendung verwendet. Ich habe Sachen, die eine Weile laufen, mit (normalerweise) Punkten, an denen ich eine Gui aktualisieren könnte, aber ich möchte die Hauptaufgabe auf einen eigenen Thread aufteilen (manchmal bleiben Sachen hängen, und es wäre schön, irgendwann eine zu haben Abbrechen/Erneut versuchen, was offensichtlich nicht funktioniert, wenn die GUI eingefroren ist, weil der Main Loop blockiert ist).

Ich habe gelesen https://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/ . Auf dieser Seite heißt es, dass die Neuimplementierung der run -Methode nicht der richtige Weg ist. Das Problem, das ich habe, ist, ein PyQt-Beispiel zu finden, bei dem ein Hauptthread die GUI ausführt und ein Arbeitsthread, bei dem dies nicht der Fall ist. Der Blog-Beitrag ist für C++. Obwohl die Beispiele helfen, bin ich immer noch ein wenig verloren. Kann mir jemand bitte ein Beispiel für die richtige Vorgehensweise in Python zeigen?

30
Azendale

Hier ist ein Arbeitsbeispiel für einen separaten Arbeitsthread, der Signale senden und empfangen kann, um die Kommunikation mit einer GUI zu ermöglichen.

Ich habe zwei einfache Schaltflächen erstellt, eine, die eine lange Berechnung in einem separaten Thread startet, und eine, die die Berechnung sofort beendet und den Arbeitsthread zurücksetzt.

Das gewaltsame Beenden eines Threads wie hier ist im Allgemeinen nicht die beste Methode, aber es gibt Situationen, in denen ein ordnungsgemäßes Beenden nicht möglich ist.

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_())
12
amicitas

Sie haben Recht, dass es eine gute Sache ist, wenn ein Arbeitsthread die Verarbeitung durchführt, während der Hauptthread die GUI ausführt. Außerdem bietet PyQt Thread-Instrumentierung mit einem Signal/Slot-Mechanismus, der thread-sicher ist.

Dies könnte von Interesse sein . In ihrem Beispiel erstellen sie eine 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)

(einfache Benutzeroberfläche mit einem Listen-Widget, zu dem wir durch Klicken auf eine Schaltfläche einige Elemente hinzufügen)

Sie können dann unsere eigene Thread-Klasse erstellen, ein Beispiel ist

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()

Sie definieren die run() -Methode neu. Eine Alternative zu terminate() finden Sie im Tutorial.

0
kiriloff