L-rac L-rac - 1 month ago 31
Python Question

QStyledItemDelegate paint refresh issues

I am currently trying to wrap my head around the model-view approach and write a thumbnail viewer application.

In this example I simply try to draw 20 boxes, but I get what seems like a random selection which updates on mouse movement. Scrolling makes things even worse and draws sometimes only have the boxes etc. Also the text seems to be not rendered at all.

To me it looks like a refresh/update problem of the paint method.
Am I missing something here?

paint problem

Ultimately I need to display a lot of items. All containing a few labels and a pixmap which needs to be updated at runtime. So I thought painting them would be the fastest solution. Alternatively I tried rendering my widgets in the paint method with:

customWidget.render(painter, QtCore.QPoint(0,0), renderFlags=QtGui.QWidget.DrawChildren)


which seems to be slower, but at least working.

Here is a minimal working example to illustrate my problem:

import sys
from PySide import QtCore
from PySide import QtGui

elements = range(20)

class ElementListModel(QtCore.QAbstractListModel):
def __init__(self, elements = [], parent = None):
super(ElementListModel, self).__init__()
self.__elements = elements

def rowCount(self, parent):
return len(self.__elements)

def data(self, index, role):
if role == QtCore.Qt.DisplayRole:
return str(self.__elements[index.row()])

class ElementThumbDelegate(QtGui.QStyledItemDelegate):
def __init__(self, view, parent=None):
super(ElementThumbDelegate, self).__init__(parent)

def paint(self, painter, options, index):

width = options.rect.width()
height = options.rect.height()

painter.setRenderHint(QtGui.QPainter.Antialiasing)
painter.setPen(QtGui.QColor(255, 255, 255))
painter.setBrush(QtGui.QColor(10, 10, 10))
painter.drawRect(0, 0, width, height)
painter.drawText(options.rect, QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter, str(index.data()))
painter.translate(options.rect.topLeft())

def sizeHint(self, options, index):
return QtCore.QSize(50, 50)

def main():
app = QtGui.QApplication(sys.argv)
viewer = QtGui.QListView()

viewModel = ElementListModel(elements)
viewer.setModel(viewModel)
viewer.setViewMode(QtGui.QListView.IconMode)
viewer.setItemDelegate(ElementThumbDelegate(viewer))

viewer.show()
sys.exit(app.exec_())

if __name__ == '__main__':
main()


Thanks in advance. Any help or pointers where to look next would be highly appreciated!

Answer

It turns out that painter.translate() in combination with drawing the box at the viewer origin (0,0) was fighting the auto positioning and causing the problem.

The translation if the delegate to the appropriate position is handled by the view itself - there is no need to do it manually.

Working code:

import sys
from PySide import QtCore
from PySide import QtGui

elements = range(20)

class ElementListModel(QtCore.QAbstractListModel):
    def __init__(self, elements = [], parent = None):
        super(ElementListModel, self).__init__()
        self.__elements = elements

    def rowCount(self, parent):
        return len(self.__elements)

    def data(self, index, role):
        if role == QtCore.Qt.DisplayRole:
            return str(self.__elements[index.row()])

class ElementThumbDelegate(QtGui.QStyledItemDelegate):
    def __init__(self, view, parent=None):
        super(ElementThumbDelegate, self).__init__(parent)

    def paint(self, painter, options, index):
        painter.setRenderHint(QtGui.QPainter.Antialiasing)
        painter.setPen(QtGui.QColor(255, 255, 255))
        painter.setBrush(QtGui.QColor(10, 10, 10))
        painter.drawRect(options.rect)
        painter.drawText(options.rect, QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter, str(index.data()))

    def sizeHint(self, options, index):
        return QtCore.QSize(50, 50)

def main():
    app = QtGui.QApplication(sys.argv)
    viewer = QtGui.QListView()

    viewModel = ElementListModel(elements)
    viewer.setModel(viewModel)
    viewer.setViewMode(QtGui.QListView.IconMode)
    viewer.setItemDelegate(ElementThumbDelegate(viewer))

    viewer.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()