Почему QProgressDialog всегда отображается при запуске приложения?
В графическом интерфейсе PyQt у меня будет несколько рабочих (QObjects, связанных с QThread) и 1 QProgressDialog на каждого рабочего. Каждый из них может иметь разную продолжительность жизни, чем другой.
Я наивно сделал следующий пример, где я создаю все необходимые QProgressDialog во время инициализации графического интерфейса .
Однако каждый QProgressDialog, определенный во время инициализации , отображается при запуске, даже если я явно установил для видимости значение False. Я предпринял несколько попыток поиграть с QProgressDialog, и кажется, что все рабочие версии основаны на новом экземпляре QProgressDialog, который создается каждый раз, когда он нам нужен.
Почему QProgressDialog отображается при запуске? Предпочтительно ли создавать экземпляры на лету? (Я не ищу мнения, но формальные элементы, исходящие из кода Qt или документа Qt, которые я, возможно, пропустил)
Существуют различные вопросы, связанные с управлением диалогом прогресса/индикатором прогресса , но ни один из них, похоже, не отвечает на мой вопрос. В документации PyQt QProgressDialog нет объяснения этому, как и в документации QDialog .
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())
Приведенный выше код всегда показывает QProgressDialog при запуске приложения, даже если их видимость никогда не вызывается явно.
Я попробовал следующий вариант, который отлично работает, но я не понимаю логику жизни 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
Я также безуспешно пытался переопределить окно show() по умолчанию:
def show(self):
super(Window, self).show()
self.progress_dialog_A.hide()
self.progress_dialog_B.hide()
1 ответ
Это ожидаемое поведение, и хотя поначалу оно может быть не очень ясным, оно объяснено в документации.
Из подробного описания :
он оценивает время, которое займет операция (на основе времени для шагов), и показывает себя только в том случае, если эта оценка выходит за пределы MinimumDuration()
Это свойство содержит время, которое должно пройти до появления диалогового окна.
Если ожидаемая продолжительность задачи меньше минимальной продолжительности, диалоговое окно вообще не появится. Это предотвращает появление диалогового окна для задач, которые быстро заканчиваются. Для задач, которые, как ожидается, превысят MinimumDuration, диалоговое окно появится после минимального времени Duration или как только будет установлен какой-либо прогресс.
Если установлено значение 0, диалоговое окно всегда отображается, как только установлен какой-либо прогресс. По умолчанию 4000 миллисекунд.
Это достигается внутренним QTimer, который вызываетforceShow()
как только истечет время с заданным
minimumDuration()
(4 секунды по умолчанию).
Возможное решение — остановить таймер сразу после создания экземпляра диалога:
self.progress_dialog_A.findChild(QTimer).stop()
self.progress_dialog_B.findChild(QTimer).stop()