In this PyQt6 article we are going to learn How to Build Responsive Applications with PyQt6 Multithreading, there are a lot of users demand for fast and responsive applications. however building such applications can be challenging because of the complexity of the tasks involved. one way to overcome this challenge is to use multithreading. In this article we are going to explore how to build responsive applications with PyQt6 multithreading, for this purpose we are going to use PyQt6 library of Python, so first of all let’s talk about PyQt6.
What is PyQt6 ?
PyQt6 is popular cross platform GUI framework for Python. it allows developers to create desktop applications with native look and feel. Multithreading is powerful technique that allows an application to perform multiple tasks simultaneously. by using multithreading we can keep the user interface responsive while performing time consuming tasks in the background, you can use pip for installation of PyQt6.
1 |
pip install PyQt6 |
What is Multithreading?
Multithreading is a technique in which an application can perform multiple tasks simultaneously. it involves creating threads that run concurrently with the main thread of the application. each thread has its own set of instructions, and they execute independently of each other.
Advantages of Multithreading
Main advantage of multithreading is that it can make an application more responsive. for example, if an application is performing time consuming task, such as downloading large file, it can create separate thread to perform this task. this way, the main thread can continue to respond to user input while download is in progress.
however, multithreading also has its own set of challenges. for example, threads can interfere with each other if they access the same resources at the same time. therefore, it is important to synchronize access to shared resources.
How to Build Responsive Applications with PyQt6 Multithreading
For demonstrating how to build responsive applications with PyQt6 multithreading, we are going to create simple application that performs time- consuming task in the background while keeping the user interface responsive.
This is the complete code for our example application:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
import sys import time from PyQt6.QtCore import QThread, pyqtSignal from PyQt6.QtWidgets import QApplication, QMainWindow, QPushButton, QLabel class Worker(QThread): # signal to indicate that the thread has finished finished = pyqtSignal() # signal to indicate the progress of the task progress = pyqtSignal(int) def __init__(self): super().__init__() def run(self): for i in range(101): # Simulate time consuming task time.sleep(0.1) # this emit progress signal self.progress.emit(i) # Emit finished signal when the task is complet self.finished.emit() class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("GeeksCoders") self.label = QLabel("Click the button to start the task", self) self.label.move(50, 50) self.button = QPushButton("Start", self) self.button.move(50, 100) self.button.clicked.connect(self.start_task) self.show() def start_task(self): # Disable the button self.button.setEnabled(False) # Create a worker thread self.worker = Worker() # Connect the progress signal self.worker.progress.connect(self.update_progress) # Connect the finished signal self.worker.finished.connect(self.task_complete) # Start the thread self.worker.start() def update_progress(self, value): # Update the label text self.label.setText(f"Task in progress: {value}%") def task_complete(self): # Update the label text self.label.setText("Task complete") # Enable the button self.button.setEnabled(True) if __name__ == "__main__": app = QApplication(sys.argv) window = MainWindow() sys.exit(app.exec()) |
So now let’s describe this code, in this code first we have imported required libraries.
1 2 3 4 |
import sys import time from PyQt6.QtCore import QThread, pyqtSignal from PyQt6.QtWidgets import QApplication, QMainWindow, QPushButton, QLabel |
After that we have defined worker class which inherits from QThread. this class is responsible for executing time consuming task in the background without blocking the main thread.
Worker class has two signals progress and finished. progress signal is emitted to indicate the progress of the task, while finished signal is emitted when the task is complete.
1 2 3 4 5 6 7 |
class Worker(QThread): finished = pyqtSignal() progress = pyqtSignal(int) def __init__(self): super().__init__() |
In the run() method of Worker class, loop is executed to simulate the time consuming task. inside the loop sleep() function is used to pause the execution of the loop for 0.1 seconds, and the progress signal is emitted with the current value of the loop index. when the loop is complete, the finished signal is emitted.
1 2 3 4 5 6 7 8 |
def run(self): for i in range(101): time.sleep(0.1) self.progress.emit(i) self.finished.emit() |
After creating Worker class, MainWindow class is defined which inherits from QMainWindow. In the init() method of the MainWindow class, label and button are created using QLabel and QPushButton. label displays a message to the user, while the button is used to start time consuming task.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("GeeksCoders") self.label = QLabel("Click the button to start the task", self) self.label.move(50, 50) self.button = QPushButton("Start", self) self.button.move(50, 100) self.button.clicked.connect(self.start_task) self.show() |
When the user clicks on the button, start_task() method is called. this method disables the button to prevent user from clicking it again while the task is running. it then creates an instance of the Worker class and connects the progress and finished signals to the update_progress() and task_complete() methods. and finally it starts worker thread by calling its start() method.
1 2 3 4 5 6 7 8 9 10 11 |
def start_task(self): self.button.setEnabled(False) self.worker = Worker() self.worker.progress.connect(self.update_progress) self.worker.finished.connect(self.task_complete) self.worker.start() |
update_progress() method updates the label text with the current progress of the task. task_complete() method updates the label text to indicate that the task is complete and enables the button.
1 2 3 4 5 6 7 8 9 |
def update_progress(self, value): self.label.setText(f"Task in progress: {value}%") def task_complete(self): self.label.setText("Task complete") self.button.setEnabled(True) |
Run the complete code and this will be the result