How can I implement a progress bar in a pop-up window that monitors the progress of a running function from a so-called Worker class (i.e. time/CPU-consuming task) by means of a QThread?
I have checked countless examples and tutorials, but the fact that the progressbar shows up in a pop-up window seems to make everything harder. I believe what I want is a rather simple thing but I keep failing at it and I ran out of ideas.
I have an example of what I am trying to achieve, which is based on this answer:
import sys
import time
from PyQt5.QtCore import QThread, pyqtSignal, QObject, pyqtSlot
from PyQt5.QtWidgets import QApplication, QPushButton, QWidget, QHBoxLayout, QProgressBar, QVBoxLayout
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Widget")
self.h_box = QHBoxLayout(self)
self.main_window_button = QPushButton("Start")
self.main_window_button.clicked.connect(PopUpProgressB)
self.h_box.addWidget(self.main_window_button)
self.setLayout(self.h_box)
self.show()
class Worker(QObject):
finished = pyqtSignal()
intReady = pyqtSignal(int)
@pyqtSlot()
def proc_counter(self): # A slot takes no params
for i in range(1, 100):
time.sleep(1)
self.intReady.emit(i)
self.finished.emit()
class PopUpProgressB(QWidget):
def __init__(self):
super().__init__()
self.pbar = QProgressBar(self)
self.pbar.setGeometry(30, 40, 500, 75)
self.layout = QVBoxLayout()
self.layout.addWidget(self.pbar)
self.setLayout(self.layout)
self.setGeometry(300, 300, 550, 100)
self.setWindowTitle('Progress Bar')
self.show()
self.obj = Worker()
self.thread = QThread()
self.obj.intReady.connect(self.on_count_changed)
self.obj.moveToThread(self.thread)
self.obj.finished.connect(self.thread.quit)
self.thread.started.connect(self.obj.proc_counter)
self.thread.start()
def on_count_changed(self, value):
self.pbar.setValue(value)
if __name__ == '__main__':
app = QApplication(sys.argv)
main_window = MainWindow()
sys.exit(app.exec_())
When I run the latter (e.g. in PyCharm Community 2019.3), the program crashes and I don't get any clear error message.
When I debug it though, it looks like it works, as I am able to see what I intended to achieve:
I have a series of questions:
- Why does it crash?
- Why does it work during debugging?
- Sould I just give up and implement the progress bar (anchored) in the main window of the app?
- I already implemented a similar thing in the past but without threading: within the loop of the worker function (i.e. CPU-consuming function) I had to add
QApplication.processEvents()
so that at each iteration the progressbar was effectively updated. It is apparently suboptimal to do things this way. Is it still a better alternative to what I am trying to achieve now?
I beg your pardon if there is something obvious that I am missing, or if this has been already be answered somewehere (duplicate): I am unable to find the answer to this problem. Thank you very much in advance.