PyQt4 QItemDelegate example with QListView and QAbstractListModel



This is my OLD blog. I've copied this post over to my NEW blog at:

http://www.saltycrane.com/blog/2008/01/pyqt4-qitemdelegate-example-with/

You should be redirected in 2 seconds.



I am currently working on a mini project which uses a QListView to display items in a list box. I am happy with most of the default behavior in the list view, however, I want to change how the highlighting of selected items is displayed. Currently, in my Windows environment, selecting an item in the list highlights the item in dark blue. This is fine, however, when the list box loses focus, the highlight color turns to a light gray, which is hard for me to see. I would like the selection to have a red highlight, whether the widget has focus or not.

My solution is to add a custom delegate to my list view. Normally, a standard view uses a default delegate (QItemDelegate) to render and edit the model's data. To customize the way the data is displayed in the view, I subclass QItemDelegate and implement a custom paint() method to set the background color to red for selected items. (Note, it is possible to specify certain formatting (including background color) using ItemDataRoles in the QAbstractListModel subclass, however, using a custom delegate is more powerful, and I didn't want to mix appearance-related code with my data model.)

In the example below, I started with the simple QListView / QAbstractListModel example, and added MyDelegate, a subclass of QItemDelegate. This class reimplements the paint() method to highlight selected items in red.

See also: Qt 4.3 QItemDelegate documentation

import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *

####################################################################
def main():
    app = QApplication(sys.argv)
    w = MyWindow()
    w.show()
    sys.exit(app.exec_())

####################################################################
class MyWindow(QWidget):
    def __init__(self, *args):
        QWidget.__init__(self, *args)

        # create objects
        list_data = [1,2,3,4]
        lm = MyListModel(list_data, self)
        de = MyDelegate(self)
        lv = QListView()
        lv.setModel(lm)
        lv.setItemDelegate(de)

        # layout
        layout = QVBoxLayout()
        layout.addWidget(lv)
        self.setLayout(layout)

####################################################################
class MyDelegate(QItemDelegate):
    def __init__(self, parent=None, *args):
        QItemDelegate.__init__(self, parent, *args)

    def paint(self, painter, option, index):
        painter.save()

        # set background color
        painter.setPen(QPen(Qt.NoPen))
        if option.state & QStyle.State_Selected:
            painter.setBrush(QBrush(Qt.red))
        else:
            painter.setBrush(QBrush(Qt.white))
        painter.drawRect(option.rect)

        # set text color
        painter.setPen(QPen(Qt.black))
        value = index.data(Qt.DisplayRole)
        if value.isValid():
            text = value.toString()
            painter.drawText(option.rect, Qt.AlignLeft, text)

        painter.restore()

####################################################################
class MyListModel(QAbstractListModel):
    def __init__(self, datain, parent=None, *args):
        """ datain: a list where each item is a row
        """
        QAbstractTableModel.__init__(self, parent, *args)
        self.listdata = datain

    def rowCount(self, parent=QModelIndex()):
        return len(self.listdata)

    def data(self, index, role):
        if index.isValid() and role == Qt.DisplayRole:
            return QVariant(self.listdata[index.row()])
        else:
            return QVariant()

####################################################################
if __name__ == "__main__":
    main()

No comments:

About

This is my *OLD* blog. I've copied all of my posts and comments over to my NEW blog at:

http://www.saltycrane.com/blog/.

Please go there for my updated posts. I will leave this blog up for a short time, but eventually plan to delete it. Thanks for reading.