Struggling with PySide – I want Help with UI Freezing Issue

Hey everyone,

I am working on a PySide6 project and I have issue. I have a simple GUI with a button that triggers a function but the UI completely freezes while the function is running. I know this is because the function is blocking the main thread but I am not sure of the best way to fix it.

I have read a bit about using QThread or QThreadPool but honestly, threading always confuses me. I also tried moving the function to a separate thread but now I am facing issues where the UI does not update properly or the thread does not stop as expected.

What is the best approach to running a long task in PySide without freezing the UI? Would QThreadPool be better than QThread or is there an easier way to do this? I came across a Java Course Online while searching for solutions but I want to hear from someone with hands-on PySide experience.

If anyone has a simple example or a good resource to understand this better, I appreciate it!
Also i have check this Creating PySide2 UI without .ui / Qt Designer still need help.

Thank you… :blush:

Hello and welcome on the forum,

You can use QThreadPool with QRunnable, which is a high level interface for threading. Then you can use Signals and Slots to communicate between you UI thread and your separate thread for keeping your UI responsive and up to date.

Here is a simple example. Note that you need to inherit both from QObject and QRunnable in your implementation. QObject is for Signals and Slots.

import sys
import time

from PySide6.QtCore import QObject, QRunnable, QThreadPool, Signal, Slot
from PySide6.QtWidgets import (
    QApplication,
    QWidget,
    QPushButton,
    QVBoxLayout,
    QProgressBar,
    QLabel,
)


class LongTask(QObject, QRunnable):
    finished = Signal(str)
    error = Signal(str)
    progress = Signal(int)

    def __init__(self, task_id, parent=None):
        QObject.__init__(self, parent)
        QRunnable.__init__(self)
        self.task_id = task_id
        self._is_cancelled = False

    @Slot()
    def cancel(self):
        self._is_cancelled = True

    def run(self):
        try:
            for i in range(101):
                if self._is_cancelled:
                    self.error.emit(f"Task {self.task_id} cancelled.")
                    return
                time.sleep(0.05)  # Simulate a long task
                self.progress.emit(i)

            self.finished.emit(f"Task {self.task_id} completed.")
        except Exception as e:
            self.error.emit(f"Task {self.task_id} error: {e}")


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

        self.threadpool = QThreadPool()
        self.threadpool.setMaxThreadCount(2)  # Limit threads

        self.start_button = QPushButton("Start Task")
        self.cancel_button = QPushButton("Cancel Task")
        self.cancel_button.setEnabled(False)
        self.progress_bar = QProgressBar()
        self.progress_bar.setValue(0)
        self.result_label = QLabel("")

        layout = QVBoxLayout()
        layout.addWidget(self.start_button)
        layout.addWidget(self.cancel_button)
        layout.addWidget(self.progress_bar)
        layout.addWidget(self.result_label)
        self.setLayout(layout)

        self.start_button.clicked.connect(self.start_task)
        self.cancel_button.clicked.connect(self.cancel_task)

        self.task = None

    def start_task(self):
        self.start_button.setEnabled(False)
        self.cancel_button.setEnabled(True)
        self.progress_bar.setValue(0)
        self.result_label.setText("Task started...")

        self.task = LongTask("Task1")  # Unique ID

        self.task.finished.connect(self.task_finished)
        self.task.error.connect(self.task_error)
        self.task.progress.connect(self.task_progress)

        self.threadpool.start(self.task)

    def cancel_task(self):
        if self.task:
            self.task.cancel()

    def task_finished(self, result):
        self.result_label.setText(result)
        self.start_button.setEnabled(True)
        self.cancel_button.setEnabled(False)

    def task_error(self, error_message):
        self.result_label.setText(error_message)
        self.start_button.setEnabled(True)
        self.cancel_button.setEnabled(False)

    def task_progress(self, progress):
        self.progress_bar.setValue(progress)


if __name__ == "__main__":
    # Try getting an existing instance in case we are in a notebook
    app = QApplication.instance() or QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())
1 Like

Also i have check this Creating PySide2 UI without .ui / Qt Designer still need help.

What kind of help?