QThread (PyQt)
QThread lets you use threads in your PyQt application. Multi threading means the use of multiple runnable processes in order to make better use of the CPU’s time by allowing it to do more than one thing at once. In simple terms: to do many jobs at the same time.
QThread is the core underlying class of the Qt threads class. To start a thread using QThread, you must create a subclass of QThread and then override the QThread.run method. When using threads, you can get the Thread instance directly and call its start() method to start the thread.
QThread example
To work with threading in Python PyQt, first we need to import these:
from PyQt5.QtCore import QThread, pyqtSignal
Create a new class that inherits from QThread. It should have a run() function with an infinite while loop.
class Worker(QThread):
sig = pyqtSignal(str)
def __init__(self, parent=None):
super(Worker, self).__init__(parent)
self.count = 0
def run(self):
while True:
time.sleep(1)
self.count += 1
if (self.count % 5 == 0):
self.sig.emit(f"thread {self.count}")
This would create an inifte loop thread. If you want to be able to stop the thread, add a new method stop() and make the loop depened on the variable.
class Worker(QThread):
sig = pyqtSignal(str)
def __init__(self, parent=None):
super(Worker, self).__init__(parent)
self.count = 0
self.run = False
def run(self):
self.run = True
while self.run:
time.sleep(1)
self.count += 1
if (self.count % 5 == 0):
self.sig.emit(f"thread {self.count}")
print(self.count)
def stop(self):
self.run = False
The thread emits a signal using sig.emit, this can be connected to a widget in PyQt. Then thread can thus update the PyQt GUI using it’s signal.
QThread demo
The program below starts a thread when you click start and stops it otherwise. It displays the output of the thread in the console and emits a signal while running.
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QPushButton, QLabel
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtCore import QThread, pyqtSignal
import time
# Create the thread class
class Worker(QThread):
sig = pyqtSignal(str)
def __init__(self, parent=None):
super(Worker, self).__init__(parent)
self.count = 0
self.run = False
# This method should run forever
def run(self):
self.run = True
while self.run:
time.sleep(1)
self.count += 1
self.sig.emit(f"thread {self.count}")
print(self.count)
# To stop the thread
def stop(self):
self.run = False
# The GUI window
class Window(QMainWindow):
def __init__(self):
super().__init__()
# Add the start button
startButton = QPushButton(self)
startButton.setText("Start Thread")
startButton.move(64,32)
startButton.clicked.connect(self.startButton_clicked)
# Add the stop button
stopButton = QPushButton(self)
stopButton.setText("Stop Thread")
stopButton.move(64,64)
stopButton.clicked.connect(self.stopButton_clicked)
# Add a label for thead output
self.label = QLabel(self)
self.label.move(200,32)
self.label.setText("Updates")
# Window properties
self.setGeometry(50,50,320,150)
self.setWindowTitle("PyQt5 threading example")
self.show()
# Create thread
self.thread = Worker()
# Connect thread signal to the updateLabel method
self.thread.sig.connect(self.updateLabel)
# Update label text
def updateLabel(self, text):
self.label.setText(text)
# Start thread
def startButton_clicked(self):
print("Start button clicked")
self.thread.start()
# Stop thread
def stopButton_clicked(self):
print("Stop button clicked")
self.thread.stop()
if __name__ == '__main__':
app = QApplication(sys.argv)
screen = Window()
screen.show()
app.exec()
As you can see, we define a worker that inherits from QThread and implements the run() method, using time.sleep to simulate a time-consuming operation.
QThread vs thread
QThread comes with pyqt5, and threading comes with python, both of which require you to write your own thread classes, inherit from them, override the run method, and then start them with start. So should I use QThread or Threading?
My understanding is that in pyqt, it is better to use QThread, which can send signals, and use the signal-slot mechanism to easily interact with the GUI across threads. On top of that, it is part of PyQt.
QThread Example
The following example shows how you can start, stop, pause and resume a thread. It also shows how you can interact with the GUI from within the thread.
When you click the start button, it starts updating the progressbar. This simulation can be paused, resumed or cancelled.
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys
class Thread(QThread):
# Thread value signal
valueChange = pyqtSignal(int)
# Initialize the thread
def __init__(self):
super(Thread, self).__init__()
self.isPause = False
self.isCancel=False
self.cond = QWaitCondition()
self.mutex = QMutex()
# Pause thread
def pause(self):
print("Pause")
self.isPause = True
# Resume thread
def resume(self):
print("Resume paused Qthread")
self.isPause = False
self.cond.wakeAll()
# Cancel thread
def cancel(self):
print("Cancel")
self.isCancel=True
# The thread loop
def run(self):
# The thread loop, updates the progressbar
for i in range(100):
# Thread lock on
self.mutex.lock()
if self.isPause:
self.cond.wait(self.mutex)
if self.isCancel:
self.valueChange.emit(0)
break
# Emit signal
self.valueChange.emit(i)
self.msleep(100)
# Thread lock off
self.mutex.unlock()
class MyDialog(QDialog):
def __init__(self):
super().__init__()
# Create the GUI
self.layout=QVBoxLayout(self)
self.progressBar=QProgressBar()
self.btnBegin=QPushButton("Start")
self.btnPause=QPushButton("Pause")
self.btnPause.setEnabled(False)
self.btnResume=QPushButton("Resume")
self.btnResume.setEnabled(False)
self.btnCancel=QPushButton("Cancel")
self.btnCancel.setEnabled(False)
# Add widgets to the screen
self.layout.addWidget(self.progressBar)
self.layout.addWidget(self.btnBegin)
self.layout.addWidget(self.btnPause)
self.layout.addWidget(self.btnResume)
self.layout.addWidget(self.btnCancel)
# Bind signals
self.btnBegin.clicked.connect(self.__onClickedBtnbegin)
self.btnPause.clicked.connect(self.__onClickedBtnpause)
self.btnResume.clicked.connect(self.__onClickedBtnresume)
self.btnCancel.clicked.connect(self.__onClickedBtncancel)
# Slot function, start button clicked
def __onClickedBtnbegin(self):
self.btnBegin.setEnabled(False)
self.btnPause.setEnabled(True)
self.btnResume.setEnabled(False)
self.btnCancel.setEnabled(True)
# Create thread
self.thread=Thread()
# Signal connect to GUI
self.thread.valueChange.connect(self.progressBar.setValue)
self.thread.start()
# Slot function, pause thread
def __onClickedBtnpause(self):
self.btnBegin.setEnabled(False)
self.btnPause.setEnabled(False)
self.btnResume.setEnabled(True)
self.btnCancel.setEnabled(True)
self.thread.pause()
# Slot function, resume thread
def __onClickedBtnresume(self):
self.btnBegin.setEnabled(False)
self.btnPause.setEnabled(True)
self.btnResume.setEnabled(False)
self.btnCancel.setEnabled(True)
self.thread.resume()
# Slot function, cancel thread
def __onClickedBtncancel(self):
self.btnBegin.setEnabled(True)
self.btnPause.setEnabled(False)
self.btnResume.setEnabled(False)
self.btnCancel.setEnabled(False)
self.thread.cancel()
if __name__=="__main__":
app=QApplication(sys.argv)
dialog=MyDialog()
dialog.show()
sys.exit(app.exec_())
That’s all on QThread. But there’s a lot more to PyQt!