I'm wanting to customize the look of a QProgressBar with a stylesheet. I'd like to give it that animated, 'indeterminate' look. So I made an animated GIF to stick in the background of the QProgressBar::chunk.

It shows up fine, but the image is static, with no animation. Any suggestions or workarounds?

I'm running PyQt 4.9.4 on OS X 10.8.

有帮助吗?

解决方案

So, this is a slightly more complex problem that you're facing.

The QProgressBar relies on information from a loop or some determined amount to represent the percentage complete. When you set the value through your code, Qt will set the value of the progress bar, redraw it and thus show the updated value. This actually takes a few milliseconds of processing within your loop. If you didn't do this, then Qt's optimization wouldn't ever redraw. This is sychronous.

The "indeterminate" look that you are going for (like an AJAX gif spinner or bar) is used on the web because the process actually moved off the client-side and is being processed on a server and is waiting for the response. The client's process is not blocked at all, so it is free to update the interface with the movie.

You can achieve the look you're going for by using QMovie in a QLabel:

movie = QMovie()
movie.setFileName('/path/to/ajax_loader.gif')
movie.start()

label = QLabel(parent)
label.setMovie(movie)

This will make your indeterminate spinner. However, it will only play as long as the event loop is processing events. Once you start your actual worker process, it blocks the event loop so your movie will start playing.

You'll need to actually "pump" the events to the player in your loop to get it to update. You can do this by:

app = QApplication.instance()

# show my label
label.show()

for obj in objs:
    # process stuff on my obj
    app.processEvents()

# hide the label
label.hide()

Of course, depending on how long each individual action you're doing in your loop takes, your spinner/loader movie will be "stuck" until the events are processed again.

Really, the best way to achieve the look you are going for is with a threaded application. You can use QThread to perform all of your actions and display the loader image to the user while it is processing. This is more similar to the way ajax works - the main Qt Event Loop will continue running as your worker thread processes everything - in an unknown amount of time. This is asynchronous.

Something like:

class MyThread(QThread):
    def run( self ):
       # perform some actions
       ...

class MyDialog(QDialog):
    def __init__( self, parent ):
        # initialize the dialog
        ...
        self._thread = MyThread(self)
        self._thread.finished.connect(self.refreshResults)

        self.refresh()

    def refresh( self ):
        # show the ajax spinner
        self._ajaxLabel.show()

        # start the thread and wait for the results
        self._thread.start()

    def refreshResults( self ):
        # hide the ajax spinner
        self._ajaxLabel.hide()

        # process the thread results
        ...

I have a loader widget that I use whenever I do stuff like this in my GUI library. If you want to see the code for it/use it, its at http://dev.projexsoftware.com/projects/projexui and the class is XLoaderWidget (projexui.widgets.xloaderwidget)

The setup is same as above, but would just be:

from projexui.widgets.xloaderwidget import XLoaderWidget

class MyThread(QThread):
    def run( self ):
       # perform some actions
       ...

class MyDialog(QDialog):
    def __init__( self, parent ):
        # initialize the dialog
        ...
        self._thread = MyThread(self)
        self._thread.finished.connect(self.refreshResults)

        self.refresh()

    def refresh( self ):
        # show the ajax spinner
        XLoaderWidget.start(self)

        # start the thread and wait for the results
        self._thread.start()

    def refreshResults( self ):
        # hide the ajax spinner
        XLoaderWidget.stop(self)

        # process the thread results
        ...
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top