Esempio del modo giusto di usare QThread in PyQt?

Sto cercando di imparare ad usare QThreads in PyQt applicazione Gui. Ho roba che corre per un po’, con (di solito) i punti in cui ho potuto aggiornare una Gui, ma vorrei dividere il lavoro principale a suo thread (a volte tutto si blocca, e sarebbe bello avere finalmente un cancel/provare di nuovo il pulsante, che ovviamente non funziona se l’interfaccia grafica è bloccato perché il Ciclo Principale è bloccato).

Ho letto https://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/. La pagina dice che re-implementare il run metodo non è il modo per farlo. Il problema che sto avendo è trovare un PyQt esempio che ha un thread principale che fa la Gui e un thread che non farlo in quel modo. Il post è per C++, anche se di esempi aiuto, sto ancora un po ‘ perso. Per favore qualcuno può indicarmi un esempio del modo giusto per farlo in Python?

  • Questo appare come un dup di thread in Background con QThread in PyQt. Il secondo esempio accettate risposta appare come una semplice traduzione del codice C++ da il blog è collegato.
  • Inoltre, l’hai scritta nativo Python filettatura codice (con threading.Thread, etc.)? Se non, è possibile lavorare attraverso alcuni esempi di quella prima. (Vedi anche Filettatura in PyQt applicazione: Usare Qt thread o Python thread per vedere se anche avete bisogno di QThread qui.)
  • Grazie, penso che il link era proprio quello che stavo cercando. Avevo visto il secondo link, e ha deciso che io debba utilizzare QThreads perché volevo essere in grado di inviare slot/segnali tra i thread. Ero consapevole del fatto che threading.Thread esiste, ma non hanno usato prima. Ho fatto un sacco di ricerca, e anche visto il primo link, scremato è, visto def run e si è trasferito su, non rendendosi conto che ha mostrato in entrambi i modi!
InformationsquelleAutor Azendale | 2013-06-02



2 Replies
  1. 11

    Qui è un esempio di utilizzo di un apposito thread di lavoro in grado di inviare e ricevere segnali per consentire di comunicare con una GUI.

    Ho fatto due semplici pulsanti, uno che inizia una lunga serie di calcolo in un thread separato, e che termina immediatamente il calcolo e reimposta il thread di lavoro.

    Con forza la terminazione di un thread come è fatto qui non è generalmente il modo migliore per fare le cose, ma ci sono situazioni in cui sempre con garbo uscita non è un’opzione.

    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_())
    • Quando si esegue questa operazione, ho. Qt has caught an exception thrown from an event handler. Throwing exceptions from an event handler is not supported in Qt. You must reimplement QApplication::notify() and catch all exceptions there.
    • Qualsiasi idea di cosa l’eccezione è stata? Ho testato questo frammento di codice che utilizza PyQt4 con Python 2.7 su OS X senza errori. Quando si tenta di utilizzare questo con Python 3.4, di tanto in tanto faccio un errore di segmentazione. Io non sono sicuro se il problema è con il codice scritto o specifiche (versione specifica) attuazione.
    • Non ho indagato ciò che sta gettando ma a quanto pare hai qualcosa che di tanto in tanto lancia un’eccezione in un ciclo di messaggi, che non deve mai succedere.
    • Importante nota se si sta tentando di copiare questo esempio: assicurarsi di memorizzare sia i QThread e il Worker se si crea in una funzione. In caso contrario, al termine della funzione, il garbage collector dump e la Worker, di non correre
    • Ho trovato che c’era un problema con il mio esempio che mi è stato di tentare di riutilizzare il thread e lavoratore oggetti dopo una chiusura forzata. Questo sembra essere ciò che stava portando all’errore di segmentazione che stavo vedendo. Questo non è più segfault, ma di tanto in tanto il thread non è terminato dopo la annulla pulsante è premuto, l’applicazione si blocca.
  2. 0

    Hai ragione a dire che è una buona cosa avere un thread del trattamento, mentre thread principale è fare la GUI. Inoltre, PyQt fornisce thread strumentazione con un signal/slot meccanismo che è thread-safe.

    Questo può sembrare di interesse. Nel loro esempio, essi costruire una 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)

    (semplice interfaccia contenente un elenco di widget che possiamo aggiungere alcuni elementi facendo clic su un pulsante)

    Si può quindi creare la nostra classe thread, un esempio è

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

    Fare ridefinire il run() metodo. Si può trovare un’alternativa al terminate(), vedere il tutorial.

    • L’OP espressamente detto che vuole utilizzare il moveToThread meccanismo, piuttosto che il QThread.run meccanismo. Io non sono sicuro se si ha una buona ragione per questo, ma ancora, non si risponde alla sua domanda.
    • L’OP è giusto: non sottoclasse QThread. Vedere blog.qt.io/blog/2010/06/17/youre-fare-e-sbagliato
    • Qt documenti sulla filettatura fare parlare la creazione di sottoclassi QThread però …
    • E questo blog spiega perché quello di cui parla @blokeley è stato parzialmente sbagliato.

Lascia un commento