-
-
Save jazzycamel/8abd37bf2d60cce6e01d to your computer and use it in GitHub Desktop.
from PyQt5.QtCore import * | |
from PyQt5.QtWidgets import * | |
from itertools import count, islice | |
class Threaded(QObject): | |
result=pyqtSignal(int) | |
def __init__(self, parent=None, **kwargs): | |
super().__init__(parent, **kwargs) | |
@pyqtSlot() | |
def start(self): print("Thread started") | |
@pyqtSlot(int) | |
def calculatePrime(self, n): | |
primes=(n for n in count(2) if all(n % d for d in range(2, n))) | |
self.result.emit(list(islice(primes, 0, n))[-1]) | |
class GUI(QWidget): | |
requestPrime=pyqtSignal(int) | |
def __init__(self, parent=None, **kwargs): | |
super().__init__(parent, **kwargs) | |
self._thread=QThread() | |
self._threaded=Threaded(result=self.displayPrime) | |
self.requestPrime.connect(self._threaded.calculatePrime) | |
self._thread.started.connect(self._threaded.start) | |
self._threaded.moveToThread(self._thread) | |
qApp.aboutToQuit.connect(self._thread.quit) | |
self._thread.start() | |
l=QVBoxLayout(self) | |
self._iterationLE=QLineEdit(self, placeholderText="Iteration (n)") | |
l.addWidget(self._iterationLE) | |
self._requestBtn=QPushButton( | |
"Calculate Prime", self, clicked=self.primeRequested) | |
l.addWidget(self._requestBtn) | |
self._busy=QProgressBar(self) | |
l.addWidget(self._busy) | |
self._resultLbl=QLabel("Result:", self) | |
l.addWidget(self._resultLbl) | |
@pyqtSlot() | |
def primeRequested(self): | |
try: n=int(self._iterationLE.text()) | |
except: return | |
self.requestPrime.emit(n) | |
self._busy.setRange(0,0) | |
self._iterationLE.setEnabled(False) | |
self._requestBtn.setEnabled(False) | |
@pyqtSlot(int) | |
def displayPrime(self, prime): | |
self._resultLbl.setText("Result: {}".format(prime)) | |
self._busy.setRange(0,100) | |
self._iterationLE.setEnabled(True) | |
self._requestBtn.setEnabled(True) | |
if __name__=="__main__": | |
from sys import exit, argv | |
a=QApplication(argv) | |
g=GUI() | |
g.show() | |
exit(a.exec_()) |
Its the equivalent of self._threaded.result.connect(self.displayPrime)
, i.e. you pass a reference to the instance of GUI.displayPrime.
I don't have a hyperlink anywhere, but you can basically pass the name of any objects signal to its constructor as a keyword argument with a reference to an instance of the target object's slot.
Fun fact: If using PySide2
instead of PyQt5
, start()
is called on the UI thread instead of the worker thread.
Workaround: Use this thread class instead of the original:
class QThread2(QThread):
started2 = Signal()
def __init__(self):
QThread.__init__(self)
self.started.connect(self.onStarted)
def onStarted(self):
self.started2.emit()
Fun fact: If using
PySide2
instead ofPyQt5
,start()
is called on the UI thread instead of the worker thread.Workaround: Use this thread class instead of the original:
class QThread2(QThread): started2 = Signal() def __init__(self): QThread.__init__(self) self.started.connect(self.onStarted) def onStarted(self): self.started2.emit()
@serg06 Works fine in PySide2 for me (without your 'workaround') with the following import adaptations for PySide2:
from PySide2.QtCore import *
from PySide2.QtWidgets import *
pyqtSignal=Signal
pyqtSlot=Slot
from itertools import count, islice
class Threaded(QObject):
result=pyqtSignal(int)
def __init__(self, parent=None, **kwargs):
super().__init__(parent, **kwargs)
@pyqtSlot()
def start(self): print("Thread started")
@pyqtSlot(int)
def calculatePrime(self, n):
primes=(n for n in count(2) if all(n % d for d in range(2, n)))
self.result.emit(list(islice(primes, 0, n))[-1])
class GUI(QWidget):
requestPrime=pyqtSignal(int)
def __init__(self, parent=None, **kwargs):
super().__init__(parent, **kwargs)
self._thread=QThread()
self._threaded=Threaded()
self._threaded.result.connect(self.displayPrime)
self.requestPrime.connect(self._threaded.calculatePrime)
self._thread.started.connect(self._threaded.start)
self._threaded.moveToThread(self._thread)
qApp.aboutToQuit.connect(self._thread.quit)
self._thread.start()
l=QVBoxLayout(self)
self._iterationLE=QLineEdit(self, placeholderText="Iteration (n)")
l.addWidget(self._iterationLE)
self._requestBtn=QPushButton(
"Calculate Prime", self, clicked=self.primeRequested)
l.addWidget(self._requestBtn)
self._busy=QProgressBar(self)
l.addWidget(self._busy)
self._resultLbl=QLabel("Result:", self)
l.addWidget(self._resultLbl)
@pyqtSlot()
def primeRequested(self):
try: n=int(self._iterationLE.text())
except: return
self.requestPrime.emit(n)
self._busy.setRange(0,0)
self._iterationLE.setEnabled(False)
self._requestBtn.setEnabled(False)
@pyqtSlot(int)
def displayPrime(self, prime):
self._resultLbl.setText("Result: {}".format(prime))
self._busy.setRange(0,100)
self._iterationLE.setEnabled(True)
self._requestBtn.setEnabled(True)
if __name__=="__main__":
from sys import exit, argv
a=QApplication(argv)
g=GUI()
g.show()
exit(a.exec_())
I tested this using Python 3.8.5 and PySide5.15.2 on Ubuntu 20.04.2LTS. What versions/OS are you running and how did you determine which thread code was running in?
Did you test my code or your own code using this method? There are a couple of gotchas with QThread
; for example, if the object you move to the thread with QThread.moveToThread
has a parent, then it will not actually get moved, but will not fail either.
Rob, a quick question. I saw your helpful post of PyQt and read through your code. I'm unfamiliar with the use of
result=self.displayPrime
on line 26. I assume this is the equivalent ofself.result.connect(GUI.displayPrime)
, but wondered if you had a hyperlink so I could read more about this (nice) syntax?Thanks!