..

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.

pyqt thread

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.

pyqt qthread start, stop, pause

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!