[PyQt] Animated Progress Bars in QTableView - how?

David Boddie david at boddie.org.uk
Sat May 31 00:58:15 BST 2014

On Fri, 30 May 2014 11:56:56 +0100 (BST), Dave Gradwell wrote:

> I have a QTableView in which I want to draw a column with progress bars.
> I considered the Qt Torrent example application and re-implemented the
> paint function of the QStyledItemDelegate like this:
>     def paint(self, painter, option, index):
>         if index.column() == 1:
>             progressBar = QtGui.QStyleOptionProgressBarV2()
>             progressBar.state = QtGui.QStyle.State_Enabled
>             progressBar.direction = QtGui.QApplication.layoutDirection()
>             progressBar.rect = option.rect
>             progressBar.fontMetrics = QtGui.QApplication.fontMetrics()
>             progressBar.minimum = 0
>             progressBar.maximum = 100
>             progressBar.textAlignment = QtCore.Qt.AlignCenter
>             progressBar.textVisible = True
>             progressBar.progress = 20 # for testing
>             QtGui.QApplication.style().drawControl(QtGui.QStyle.CE_Progress
> Bar, progressBar, painter)
>         return super(progressBarDelegate, self).paint(painter, option,
> index)
> However, this gave me a column of greyed-out progress bars - with no
> animation.  
> http://www.bonhardapple.com/shared-with-others/delegated-style.png This
> isn't what the screenshots in the docs portray of the example.

Just out of interest, does the example actually animate the progress bars?

> Then I found out about QTableView's setIndexWidget().
> I tried to implement setIndexWidget() whenever data() was called on the
> model but no progress bars were shown. So I thought I'd try
> setIndexWidget() in the delegate paint() code. I created a dictionary
> (self.parent.progressBarPool) to track which progress bars had been
> manufactured - to avoid excessive creation/deletion I guess.
>     def paint(self, painter, option, index):
>         if index.column() == 1:
>             if not index.row() in self.parent.progressBarPool:
>                 progressBar = QtGui.QProgressBar()
>                 self.parent.progressBarPool[index.row()] = progressBar
>                 progressBar.setMinimum(0)
>                 # progressBar.setAutoFillBackground(True) # docs say this
> needs to be true, but you get a better row-highlight appearance with it
> False self.parent.tableView.setIndexWidget(index, progressBar) else:
>                 progressBar = self.parent.progressBarPool[index.row()]
>             progressBar.setValue(20) # for testing
>         return super(progressBarDelegate, self).paint(painter, option,
> index)
> This looked great...
> http://www.bonhardapple.com/shared-with-others/first-glance.png
> ... however, when I scrolled, the progress bars didn't move in concert with
> the rest of the scroll area.   They floated above the cells which scrolled
> away underneath them.
> http://www.bonhardapple.com/shared-with-others/scrolled.png

I'd avoid using cell widgets for this. It should be possible with delegates.

> I've now hacked things a bit so that the self.parent.progressBarPool is
> deleted when there's a wheelEvent. This causes a flickery redraw on (one
> type of) scroll but feels like a growing stack of hacks rather than a
> solution. Accordingly, I would appreciate any advice on the right way to
> do this.

There is no right way to do this - or official right way, at least - because
I don't think delegates were designed with this use case in mind. However, it
is possible to hack something together using timers. I once wrote an example
(not using progress bars, mind) that shows animated delegates. It's on the
sadly now defunct PyQt Wiki, but archive.org still has a copy:


It's not ideal, but you can at least use it as a starting point, perhaps.



More information about the PyQt mailing list