Open In App

Prevent Freezing in Python PYQT GUIs with QThread

Last Updated : 28 May, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

When performing long-running tasks, GUIs can become unresponsive or "freeze," frustrating users and making the application appear unprofessional. This is a common problem in applications built with PyQt, a set of Python bindings for the Qt application framework. To address this issue, PyQt provides QThread, a class that allows developers to run code in separate threads, keeping the GUI responsive. This article will introduce PyQt's QThread and demonstrate two different methods to use it to prevent GUI freezing.

What is PyQt's QThread?

QThread is a class in PyQt that allows developers to run code in parallel with the main thread, which handles the GUI. By moving time-consuming operations to a QThread, the main thread remains free to handle user interactions and updates, preventing the application from freezing.

Understanding GUI Freezing

GUI freezing typically occurs when time-consuming operations, such as data processing or network requests, are performed within the main thread of the application. Since the main thread is responsible for handling user input and updating the interface, performing intensive tasks in this thread can cause the GUI to become unresponsive. This results in a poor user experience, as users may perceive the application as slow or unresponsive.

Illustrate the Issue with a Simple Example

Consider a PyQt application that calculates the factorial of a large number when a button is clicked. Here's how the code might look:

In this example, clicking the "Calculate Factorial" button triggers a time-consuming calculation of the factorial of a large number. Since this calculation is performed within the main thread, the GUI becomes unresponsive until the calculation is complete, resulting in a frozen interface.

Python
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QLabel

def calculate_factorial():
    result = 1
    for i in range(1, 100000):
        result *= i
    return result

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("GUI Freezing Example")
        self.setGeometry(100, 100, 300, 200)

        self.label = QLabel(self)
        self.label.setGeometry(50, 50, 200, 30)

        self.button = QPushButton("Calculate Factorial", self)
        self.button.setGeometry(50, 100, 200, 30)
        self.button.clicked.connect(self.on_button_click)

    def on_button_click(self):
        factorial = calculate_factorial()
        self.label.setText(f"Factorial: {factorial}")

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

Output:

Due to the large factorial, it's not calculating and freezing the GUI with an error message.

f1
ValueError: Exceeds the limit (4300 digits) for integer string conversion; use sys.set_int_max_str_digits() to increase the limit

Preventing GUI Freezes in Python with PYQT QThread

To prevent GUI freezing, we can use PyQt's QThread to perform time-consuming tasks in separate threads. By moving intensive operations to separate threads, we ensure that the main thread remains available to handle user input and update the interface.

Let's modify the previous example to use QThread for calculating the factorial in a separate thread:

In this modified example, we create a subclass of QThread called FactorialThread to perform the factorial calculation in a separate thread. The run method of FactorialThread performs the calculation, and once it's complete, it emits a signal result_ready containing the calculated factorial. We connect this signal to a slot on_factorial_calculated in the MainWindow class, which updates the GUI with the result.

By using QThread to perform the factorial calculation in a separate thread, the main GUI thread remains responsive, and users can continue interacting with the application while the calculation is ongoing.

Python
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QLabel
from PyQt5.QtCore import QThread, pyqtSignal

class FactorialThread(QThread):
    result_ready = pyqtSignal(int)

    def __init__(self):
        super().__init__()

    def run(self):
        result = 1
        for i in range(1, 100000):
            result *= i
        self.result_ready.emit(result)

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("QThread Example")
        self.setGeometry(100, 100, 300, 200)

        self.label = QLabel(self)
        self.label.setGeometry(50, 50, 200, 30)

        self.button = QPushButton("Calculate Factorial", self)
        self.button.setGeometry(50, 100, 200, 30)
        self.button.clicked.connect(self.on_button_click)

        self.thread = FactorialThread()
        self.thread.result_ready.connect(self.on_factorial_calculated)

    def on_button_click(self):
        self.button.setEnabled(False)
        self.thread.start()

    def on_factorial_calculated(self, result):
        self.label.setText(f"Factorial: {result}")
        self.button.setEnabled(True)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

Output

f2

Conclusion

GUI freezing can significantly degrade the user experience of an application. By leveraging PyQt's QThread, developers can prevent GUI freezing by offloading time-consuming tasks to separate threads. This ensures that the main thread remains available to handle user input and update the interface, resulting in a smoother and more responsive user experience.


Next Article
Article Tags :
Practice Tags :

Similar Reads