In a PyQt GUI, I will have several workers (QObjects associated to QThread), and 1 QProgressDialog per worker. Each one may have a different lifespan than the other.
I naively made the following example, where I create all necessary QProgressDialog at GUI init time.
However, each QProgressDialog defined at init time shows up at startup, even if I explicitly set visibility to False. I made several attempts to play with QProgressDialog, and it seems that the working versions are all based on a new QProgressDialog instance being created each time we need it.
Why is the QProgressDialog showing at startup ? Is it preferrable to instantiate on-the-fly ? (I am not looking for opinions, but for formal elements coming from either Qt code or Qt doc I may have missed)
There are various questions dealing with progress dialog/progress bar management, but none of them seem to address my question. The PyQt QProgressDialog documentation has no explanation for this, neither has QDialog documentation.
MCVE:
import sys
import time
from PyQt5.QtCore import QThread, Qt, QObject, pyqtSignal
from PyQt5.QtWidgets import (QApplication, QMainWindow, QProgressDialog, QPushButton)
class WorkerA(QObject):
finished = pyqtSignal()
def do_it(self):
time.sleep(5)
self.finished.emit()
class Window(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setMinimumSize(250, 250)
self.button = QPushButton(self)
self.button.setText("Start worker A")
self.button.clicked.connect(self.start_worker_A)
self.thread_A = QThread()
self.thread_A.setObjectName("ThreadA")
self.worker_A = WorkerA()
self.progress_dialog_A = QProgressDialog("Work A in progress", None, 0, 0, self)
self.progress_dialog_A.setWindowModality(Qt.ApplicationModal)
self.progress_dialog_A.setCancelButton(None)
self.progress_dialog_A.setWindowTitle("Work A")
self.progress_dialog_A.setVisible(False)
self.progress_dialog_A.hide()
self.thread_B = QThread()
self.thread_B.setObjectName("ThreadB")
self.worker_B = None
self.progress_dialog_B = QProgressDialog("Work B in progress", None, 0, 0, self)
self.progress_dialog_B.setWindowModality(Qt.ApplicationModal)
self.progress_dialog_B.setCancelButton(None)
self.progress_dialog_B.setWindowTitle("Work B")
self.progress_dialog_B.setVisible(False)
self.progress_dialog_B.hide()
def start_worker_A(self):
if not self.thread_A.isRunning():
self.button.setEnabled(False)
self.worker_A.moveToThread(self.thread_A)
self.thread_A.started.connect(self.worker_A.do_it)
self.thread_A.started.connect(self.progress_dialog_A.show)
self.worker_A.finished.connect(self.thread_A.quit)
self.worker_A.finished.connect(self.progress_dialog_A.hide)
self.worker_A.finished.connect(self.enable_button)
self.thread_A.start()
else:
pass
def enable_button(self):
self.button.setEnabled(True)
if __name__ == "__main__":
app = QApplication(sys.argv)
win = Window()
win.show()
sys.exit(app.exec())
The code above always shows the QProgressDialog at app startup, even if their visibility is never explicitly invoked.
I tried the following variant, which works fine, but I don't understand the lifetime logic of QProgressDialog.
class Window(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setMinimumSize(250, 250)
self.button = QPushButton(self)
self.button.setText("Start worker A")
self.button.clicked.connect(self.start_worker_A)
self.thread_A = QThread()
self.thread_A.setObjectName("ThreadA")
self.worker_A = WorkerA()
self.progress_dialog_A : QProgressDialog = None
# obviously no further operations here on self.progress_dialog_A
# rest of init as first code sample above
def start_worker_A(self):
if not self.thread_A.isRunning():
self.button.setEnabled(False)
# QProgressDialog instantiated only when needed
# any previous reference to QProgressDialog is garbaged collected ?
# and a new instance created on the fly
self.progress_dialog_A = QProgressDialog("Work A in progress", None, 0, 0, self)
self.progress_dialog_A.setWindowModality(Qt.ApplicationModal)
self.progress_dialog_A.setCancelButton(None)
self.progress_dialog_A.setWindowTitle("Work A")
self.progress_dialog_A.setVisible(False)
self.progress_dialog_A.hide()
self.worker_A.moveToThread(self.thread_A)
self.thread_A.started.connect(self.worker_A.do_it)
self.thread_A.started.connect(self.progress_dialog_A.show)
self.worker_A.finished.connect(self.thread_A.quit)
self.worker_A.finished.connect(self.progress_dialog_A.hide)
self.worker_A.finished.connect(self.enable_button)
self.thread_A.start()
else:
pass
# rest of code unchanged
I also tried overriding the default window show() with no success:
def show(self):
super(Window, self).show()
self.progress_dialog_A.hide()
self.progress_dialog_B.hide()