dave
dave

Reputation: 602

Carcass of QProgressDialog lingers - sometimes

    progress = QtGui.QProgressDialog("Parsing Log", "Stop", 0,numberOfLinesInFile , self)
    progress.setWindowModality(QtCore.Qt.WindowModal)

    for lineNumber, line in enumerate(file):
        # yield a bit to the Qt UI handler
        QtGui.QApplication.processEvents()
        progress.setValue(lineNumber + 1) # lineNumber is zero-based so need the plus one to match the more literal numberOfLinesInFile
        if progress.wasCanceled():
            progressWasCancelled = True
            break


        # ...read and parse lines from file (20mb takes ~10 seconds)


    # crank the progress bar through to completion to get rid of it
    # this seems to forgo the opportunity to use progress.wasCanceled() subsequently?
    progress.setValue(numberOfLinesInFile)


    if not progressWasCancelled:
        self.updateTable(self.requestRoster)

After this, and regardless of the progress dialogue being cancelled or not, the progress dialogue is hidden (it slides back up into the toolbar). But if I switch application ('command tab' on the Mac) then switch back to my application, a ghost of the QProgressDialog is in front of the main application window! Its progress bar is at 100% and the stop button is blue but not pulsing. It is unresponsive. If I move the application window it disappears.

If I call progress.destroy() after progress.setValue(numberOfLinesInFile) that seems to help. But it seems worrying to copy the example from the docs and get bitten, and I don't know the ramifications of destroy().

I was using PySide, I switched to PyQt and same thing.

Also, sometimes progress.setValue(numberOfLinesInFile) causes subsequent reads of progress.wasCancelled() to return false (but sometimes it returns true!) which is why I set my own progressWasCancelled. Its randomness is disturbing.

I'm on Mac 10.6.8, Qt 4.8.2, Python 2.7. Tried with PySide 1.1.0 and PyQt 4.9.4.

Am I doing this all wrong?

Upvotes: 3

Views: 1920

Answers (1)

ekhumoro
ekhumoro

Reputation: 120608

I can't test on a Mac, but I'll try to make a few suggestions which could help solve your issues.

Firstly, if you use a modal progress dialog, there's no need to call processEvents(), as the dialog will handle this itself.

Secondly, this line in your code:

    progress.setValue(lineNumber + 1)

is problematic, because to quote the Qt docs:

For the progress dialog to work as expected, you should initially set this property to 0 and finally set it to QProgressDialog::maximum(); you can call setValue() any number of times in-between.

so you should either call progress.setValue(0) before the loop, or just avoid adding the offset altogether. Also, on the final iteration, lineNumber + 1 will equal the maximum, which will reset the dialog at that point (unless autoReset has been set to False). It is for this reason that the Qt example calls setValue(maximum) after the loop has completed.

Finally, there is no problem with calling destroy() or deleteLater() after you've finished with the progress dialog - in fact, it's a good idea. When you pass self to the QProgressDialog constructor, it will become the parent of the dialog and keep a reference to it. So, unless you explicitly delete it, a new child dialog (plus all it's child objects) will be added every time you call the function that uses it (which could potentially waste a lot of memory).

Here's a demo script that may be improvement:

import sys, time
from PyQt4 import QtGui, QtCore

class Window(QtGui.QWidget):
    def __init__(self):
        QtGui.QWidget.__init__(self)
        self.button = QtGui.QPushButton('Test', self)
        self.button.clicked.connect(self.handleButton)
        layout = QtGui.QVBoxLayout(self)
        layout.addWidget(self.button)

    def handleButton(self):
        file = range(30)
        numberOfLinesInFile = len(file)
        progressWasCancelled = False
        progress = QtGui.QProgressDialog(
            "Parsing Log", "Stop", 0, numberOfLinesInFile, self)
        progress.setWindowModality(QtCore.Qt.WindowModal)
        progress.setMinimumDuration(0)
        for lineNumber, line in enumerate(file):
            progress.setValue(lineNumber)
            if progress.wasCanceled():
                progressWasCancelled = True
                break
            time.sleep(0.05)
        progress.setValue(numberOfLinesInFile)
        print 'cancelled', progress.wasCanceled(), progressWasCancelled
        progress.deleteLater()

if __name__ == '__main__':

    app = QtGui.QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())

Upvotes: 5

Related Questions