I am trying to DRY up a PyQt program that calls QProgressDialog at various times. Instead of each occurrence calling a variation of this:
self.cat_progress = QProgressDialog(self.root)
self.cat_progress.setMinimumWidth(800)
self.cat_progress.setWindowTitle('Downloading Data')
self.cat_progress.setMinimumDuration(0)
self.cat_progress.setWindowModality(Qt.ApplicationModal)
self.cat_progress_label = QLabel('')
self.cat_progress.setLabel(self.cat_progress_label)
total_requests = 25
self.cat_progress.setMaximum(total_requests)
self.current_progress = 1
self.cat_progress.setValue(self.current_progress)
self.message_phase = 'Starting Collection'
self.progress_bar_max = 0
self.cat_progress_label.setText(self.message_phase)
I made a class, so that each time a progressDialog is needed I can use 3 lines, instead of 20+.
class ProgressBar(QDialog):
def __init__(self, title, label, prog_max=100):
super().__init__()
self.progress = QProgressDialog(self)
self.progress.setMinimumWidth(600)
self.progress.setWindowTitle(title)
self.progress.setMinimum(0)
self.progress.setValue(0)
self.progress.setWindowModality(Qt.WindowModal)
self.progress.setMaximum(prog_max)
self.progress.setWindowFlag(Qt.WindowContextHelpButtonHint,False) # This removes the '?' from the dialog
self.progress.setStyleSheet("""
QProgressBar {
border: 2px solid grey;
border-radius: 5px;
text-align: center
}
QProgressBar::chunk {
background-color: rgb(139, 183, 240);
width: 25px
}
QPushButton {
border: 2px solid grey;
border-radius: 5px;
padding: 5% 8%;
}
""")
self.progress_label = QLabel(label)
self.progress.setLabel(self.progress_label)
def advance_progress(self, label):
self.progress_label.setText(label)
self.progress.setValue(self.progress.value() + 1)
def canceled(self):
self.stop_progress()
def stop_progress(self):
self.deleteLater()
The issue is that one usage was relying on the built-in Cancel button's wasCanceled()
boolean value. As the program runs a loop, if progress.wasCanceled(): ...start a different process
. Now that progress
is from a class and not directly made from QProgressDialog, I get an AttributeError: 'ProgressBar' object has no attribute 'wasCanceled'
. Clicking this button will escape the dialog, but it does not progress into the next process as I need.
I've tried to add signals, connect a cancelled function, to set a userClicked boolean, etc. Some things broke the Cancel so it just resets and starts over, infinitely, when you click cancel. Some self 'clicked' the button (I had a print statement output) as the loop ran, but didn't stop the action. Once, it got to that if
statement and broke out on its own.
How do I keep this as an object and also know when a user has clicked Cancel?
Small demo that now works! [the key is to inherit from the right widget and to not call a new instance of QProgressDialog]:
import sys
import time
from PyQt5.QtWidgets import QMainWindow, QApplication, QProgressDialog, QLabel, QPushButton
from PyQt5.QtCore import Qt
class ProgressBar(QProgressDialog):
def __init__(self, title, label, prog_max=100):
super().__init__()
self.setMinimumWidth(600)
self.setWindowTitle(title)
self.setMinimum(0)
self.setValue(0)
self.setWindowModality(Qt.WindowModal)
self.setMaximum(prog_max)
self.setStyleSheet("""
QProgressBar {
border: 2px solid grey;
border-radius: 5px;
text-align: center
}
QProgressBar::chunk {
background-color: rgb(139, 183, 240);
width: 25px
}
QPushButton {
border: 2px solid grey;
border-radius: 5px;
padding: 5% 8%;
}
""")
self.progress_label = QLabel(label)
self.setLabel(self.progress_label)
def advance_progress(self, label):
self.progress_label.setText(label)
self.setValue(self.value() + 1)
def canceled(self):
self.stop_progress()
def stop_progress(self):
self.deleteLater()
class Testing(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('Testing Progress')
btn = QPushButton('Start progress bar')
btn.clicked.connect(self.run_progress)
self.setCentralWidget(btn)
def run_progress(self):
# Title, Label, setMaximum
progress = ProgressBar('Window Running Progress', 'Testing from Element', 30)
progress.advance_progress('Starting Scan ...')
i = 0
while i < 30:
print(i)
time.sleep(2)
progress.advance_progress(f'Counting {i}')
i += 1
if progress.wasCanceled():
alert('User Stopped!')
progress.stop_progress()
progress.stop_progress()
if __name__ == '__main__':
app = QApplication(sys.argv)
test = Testing()
test.show()
sys.exit(app.exec_())