PDA

View Full Version : PyQt, Custom delegate for html format text causes jittery refreshing [GIF included]



DoTheEvo
5th April 2015, 12:41
Link to project's GitHub (https://github.com/DoTheEvo/ANGRYsearch)

I am attempting to make an instant-as-you-type-file-search for linux inspired by Everything-search-engine (http://s1.webmshare.com/54KKv.webm).
It's in PyQt5 and Python3. Week ago I got it in to pretty decent shape by my standard:

http://i.imgur.com/6SciMhk.gif

Now for the last few days I was attempting to get the search term to appear bold in the results in the listview and for that I had to use delegate so that I can just use bold tags.
I struggled through and got it somewhat working, without some decent understanding of how it works as I have not found much tutorial resources on the delegates in PyQt/PySide, just completed done things that I could observe and tinker around with.

Now when its relatively done I observed visual degradation in quality when compared to the version without a delegate.

Heres a gif of it in action showing first with the delegate and then without (http://i.imgur.com/1ewgIMX.gif)

gfycat backup link (https://fat.gfycat.com/DentalDelightfulErmine.webm)

I would describe it that the listview seems to have multiple empty frames where it shows nothing before content appears, which make it look like its its blinking.
This does not appear when not using the delegate.


Here is the code of the delegate in question:



class HTMLDelegate(QStyledItemDelegate):
def paint(self, painter, option, index):
options = QStyleOptionViewItem(option)
self.initStyleOption(options, index)
style = QApplication.style() if options.widget is None \
else options.widget.style()

doc = QTextDocument(self)
doc.setHtml(options.text)

options.text = ""
style.drawControl(QStyle.CE_ItemViewItem, options, painter)

ctx = QAbstractTextDocumentLayout.PaintContext()

if option.state & QStyle.State_Selected:
ctx.palette.setColor(QPalette.Text, option.palette.color(
QPalette.Active, QPalette.HighlightedText))

textRect = style.subElementRect(QStyle.SE_ItemViewItemText, options)
painter.save()
painter.translate(textRect.topLeft())
painter.setClipRect(textRect.translated(-textRect.topLeft()))
doc.documentLayout().draw(painter, ctx)

painter.restore()

def sizeHint(self, option, index):
options = QStyleOptionViewItem(option)
self.initStyleOption(options, index)

doc = QTextDocument(self)
doc.setDocumentMargin(1)
doc.setHtml(options.text)
return QSize(doc.idealWidth(), 23)


Is this solvable? Or is it the performance price for using custom delegate and theres just no way around it?

anda_skoa
5th April 2015, 13:07
Have you tried reusing the QTextDocument?
I.e. keeping it as a member of your delegate instance instead of creating a new one on every paint?

Cheers,
_

DoTheEvo
5th April 2015, 16:44
Thanks for the suggestion, I tried the code like this, but no change in behavior, it's still "blinking"



class HTMLDelegate(QStyledItemDelegate):
def __init__(self, parent=None):
super(HTMLDelegate, self).__init__(parent)
self.doc = QTextDocument(self)

def paint(self, painter, option, index):
options = QStyleOptionViewItem(option)
self.initStyleOption(options, index)
style = QApplication.style()

self.doc.setHtml(options.text)

options.text = ""
style.drawControl(QStyle.CE_ItemViewItem, options, painter)

ctx = QAbstractTextDocumentLayout.PaintContext()

if option.state & QStyle.State_Selected:
ctx.palette.setColor(QPalette.Text, option.palette.color(
QPalette.Active, QPalette.HighlightedText))

textRect = style.subElementRect(QStyle.SE_ItemViewItemText, options)
painter.save()
painter.translate(textRect.topLeft())
painter.setClipRect(textRect.translated(-textRect.topLeft()))
self.doc.documentLayout().draw(painter, ctx)

painter.restore()

def sizeHint(self, option, index):
options = QStyleOptionViewItem(option)
self.initStyleOption(options, index)

self.doc.setDocumentMargin(1)
self.doc.setHtml(options.text)
return QSize(self.doc.idealWidth(), 23)

ChrisW67
5th April 2015, 21:54
Try setting the clipping rectangle on the QPaintContext rather than the painter. You may find the layout engine can make a smarter choice of how to paint.

anda_skoa
5th April 2015, 21:59
Another potential slow down can be save/restore on a painter.
It is often faster to just "undo" the changes, e.g. translate back to "undo" the translation.

Cheers,
_

DoTheEvo
6th April 2015, 09:14
THANK YOU ALL FOR REPLYING!

It was my bad at the very beginning
I was doing setItemDelegate() on every keypress instead of just putting it there right after creating the listview

All is well

http://i.imgur.com/8Kj3tEl.gif