Search code examples
pythonpyqtpyside6

QProgressBar with QThread is not working in python


Greeting

I am trying to make QProgressBar with QThread. but it's not working. I used threads, but it seems as if everything runs on the main thread.

it's my code

import sys
from PySide6.QtWidgets import QApplication, QProgressBar, QWidget, QPushButton, QVBoxLayout
from PySide6.QtCore import QThread, Signal
import time


class Thread(QThread):
    change_value = Signal()

    def run(self):
        self.change_value.emit()
            
class Form(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        self.pgsb = QProgressBar()
        self.pb = QPushButton("Pause")
        self.th = Thread()
        self.init_widget()
        self.th.change_value.connect(self.update_progressbar)
        self.th.start()

    def init_widget(self):
        layout = QVBoxLayout()
        self.setLayout(layout)
        layout.addWidget(self.pgsb)
        layout.addWidget(self.pb)

    def update_progressbar(self):
        cnt = 0
        while True:
            if cnt == 100:
                cnt = 0
            cnt += 1
            self.pgsb.setValue(cnt)
            time.sleep(0.5)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    form = Form()
    form.show()
    sys.exit(app.exec())

it is not working. However, the following code works fine.

import sys
from PySide6.QtWidgets import QApplication, QProgressBar, QWidget, QPushButton, QVBoxLayout
from PySide6.QtCore import QThread, Signal
import time


class Thread(QThread):
    change_value = Signal(int)

    def run(self):
        cnt = 0
        while True:
            if cnt == 100:
                cnt = 0
            cnt += 1
            self.change_value.emit(cnt)
            time.sleep(0.5)


class Form(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        self.pgsb = QProgressBar()
        self.th = Thread()
        self.init_widget()
        self.th.change_value.connect(self.update_progressbar)
        self.th.start()

    def init_widget(self):
        layout = QVBoxLayout()
        self.setLayout(layout)
        layout.addWidget(self.pgsb)

    def update_progressbar(self, value):
        self.pgsb.setValue(value)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    form = Form()
    form.show()
    sys.exit(app.exec())

Based on what is currently happening, the first code appears to cause UI updates on the main thread. But I don't understand. Why is the result of this code different?


Solution

  • the GUI thread (main thread) in QT is responsible for drawing widgets to the screen, in your first example you are creating an infinite loop in the main thread (because the slot is attached to a widget in the main thread), which prevent the main thread from doing redraws or processing user interaction.

    note that the function is called by the thread that contains the object that has the slot, not the thread that contains the signal, you can find out more in Threads Events QObjects

    in the second example another thread is in an infinite loop while the main thread is free to process user events and redraw the widgets.

    as a general rule you should never do any long operation in the main thread or your application won't be responsive and won't be updated, instead have other threads do the long operations and notify the main thread to update the GUI using signals.