QProgressDialog object AttributeError: 'ProgressBar' object has no attribute 'wasCanceled'

89 Views Asked by At

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_())
0

There are 0 best solutions below