[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:
https://web.archive.org/web/20100704185712/http://www.diotavelli.net/PyQtWiki/Animated%20items%20using%20delegates
It's not ideal, but you can at least use it as a starting point, perhaps.
Regards,
David
More information about the PyQt
mailing list