Is there a way to add sections to QListView in PySide or PyQt?
<p>This question is an exact duplicate of <a href="https://stackoverflow.com/questions/25560547/is-there-a-way-to-add-sections-to-qlistview">this</a> unanswered question, except that I'm using Python.</p>

<p>I've got this.</p>

<p><a href="https://i.stack.imgur.com/5qOhq.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/5qOhq.png" alt="enter image description here"></a></p>

<p>And am looking for this.</p>

<p><a href="https://i.stack.imgur.com/sCX8o.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/sCX8o.png" alt="enter image description here"></a></p>

<p>I'm looking for hints as to how to approach this. Here's what I've considered so far.</p>

<ol>
<li>Add "virtual items" to the model itself. I'd rather not do this, in order to keep the model free of view related data. I intend to add additional views onto this model.</li>
<li>Add a proxy model per view. The proxy could add additional items and sort them appropriately. Albeit cleaner than (1), I'm not entirely convinced for the same reasons.</li>
<li>Subclass QListView, but I'm struggling to understand what to override.</li>
<li>Just write my own view; with a for-loop and QLabels, and synchronise with the model best I can. Gotta do what you gotta do.</li>
</ol>

<p>Help!</p>

<p>Source.</p>

<pre><code>import sys
from PySide import QtCore, QtGui


Label = QtCore.Qt.DisplayRole
Section = QtCore.Qt.UserRole + 1


class Model(QtCore.QAbstractListModel):
    def __init__(self, parent=None):
        super(Model, self).__init__(parent)
        self.items = list()

    def data(self, index, role):
        item = self.items[index.row()]
        if role == Label:
            return item["label"]

        if role == Section:
            return item["section"]

    def append(self, item):
        """Append item to end of model"""
        self.beginInsertRows(QtCore.QModelIndex(),
                             self.rowCount(),
                             self.rowCount())

        self.items.append(item)
        self.endInsertRows()

    def rowCount(self, parent=None):
        return len(self.items)

app = QtGui.QApplication(sys.argv)
model = Model()

for item in ({"label": "Ben", "section": "Human"},
             {"label": "Steve", "section": "Human"},
             {"label": "Alpha12", "section": "Robot"},
             {"label": "Mike", "section": "Toaster"}):
    model.append(item)

view = QtGui.QListView()
view.setWindowTitle("My View")
view.setModel(model)
view.show()

app.exec_()
</code></pre>

<p><br></p>

<h3>Update 1 - Additional Information</h3>

<p>For clarity, this question is about QListView rather than about alternatives to it. The reason being that the rest of the application is developing in an MVC-like fashion, where one or more views are drawing the unique set of data present in the one model.</p>

<p>This particular view includes sections, the other views, which aren't necessarily QListView's, shouldn't know anything about sections. For example, one view may be a counter, listing the number of items available. Another might be a pie, showing the ratio between items starting with the letter 'A'.</p>

<p>For further reference, what I'm looking for is exactly what ListView does in QML.</p>

<ul>
<li><a href="http://doc.qt.io/qt-5/qml-qtquick-listview.html#section-prop" rel="nofollow noreferrer">http://doc.qt.io/qt-5/qml-qtquick-listview.html#section-prop</a></li>
</ul>

<p>That is, a single model with an additional delegate for optional sections. In this case, the view doesn't require the model to contain these added members, but rather draws them based on the existing data.</p>

<h3>Example</h3>

<ul>
<li><a href="https://github.com/pyblish/pyblish-qml/blob/3c420ac35ce286420475eeff73181f1b5738af26/pyblish_qml/qml/Overview.qml#L85" rel="nofollow noreferrer">Source</a></li>
</ul>

<p><img src="https://cloud.githubusercontent.com/assets/2152766/7021167/f893ebd0-dd16-11e4-9ae2-bc23b4767f98.gif" alt=""></p>

<p><br></p>

<h3>Update 2 - Work in progress</h3>

<p>Ok, so I've got the extra items added to the bottom of the view using a QSortFilterProxyModel, but I'm struggling to understand:</p>

<ol>
<li>How do I assign them their corresponding data?</li>
<li>How do I sort them into place, above their "child" items?</li>
</ol>

<p><a href="https://i.stack.imgur.com/y7inR.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/y7inR.png" alt="enter image description here"></a></p>

<pre><code>import sys
from PySide import QtCore, QtGui


Label = QtCore.Qt.DisplayRole
Section = QtCore.Qt.UserRole + 1
IsSection = QtCore.Qt.UserRole + 2


class Item(object):
    @classmethod
    def paint(cls, painter, option, index):
        rect = QtCore.QRectF(option.rect)

        painter.save()

        if option.state &amp; QtGui.QStyle.State_MouseOver:
            painter.fillRect(rect, QtGui.QColor("#DEE"))

        if option.state &amp; QtGui.QStyle.State_Selected:
            painter.fillRect(rect, QtGui.QColor("#CDD"))

        painter.drawText(rect.adjusted(20, 0, 0, 0),
                         index.data(Label))

        painter.restore()

    @classmethod
    def sizeHint(cls, option, index):
        return QtCore.QSize(option.rect.width(), 20)


class Section(object):
    @classmethod
    def paint(self, painter, option, index):
        painter.save()
        painter.setPen(QtGui.QPen(QtGui.QColor("#666")))
        painter.drawText(QtCore.QRectF(option.rect), index.data(Label))
        painter.restore()

    @classmethod
    def sizeHint(self, option, index):
        return QtCore.QSize(option.rect.width(), 20)


class Delegate(QtGui.QStyledItemDelegate):
    def paint(self, painter, option, index):
        if index.data(IsSection):
            return Section.paint(painter, option, index)
        else:
            return Item.paint(painter, option, index)

    def sizeHint(self, option, index):
        if index.data(IsSection):
            return Section.sizeHint(option, index)
        else:
            return Item.sizeHint(option, index)


class Model(QtCore.QAbstractListModel):
    def __init__(self, parent=None):
        super(Model, self).__init__(parent)
        self.items = list()

    def data(self, index, role):
        item = self.items[index.row()]

        return {
            Label: item["label"],
            Section: item["section"],
            IsSection: False
        }.get(role)

    def append(self, item):
        self.beginInsertRows(QtCore.QModelIndex(),
                             self.rowCount(),
                             self.rowCount())

        self.items.append(item)
        self.endInsertRows()

    def rowCount(self, parent=None):
        return len(self.items)


class Proxy(QtGui.QSortFilterProxyModel):
    def data(self, index, role):
        if index.row() &gt;= self.sourceModel().rowCount():

            return {
                Label: "Virtual Label",
                Section: "Virtual Section",
                IsSection: True
            }.get(role)

        return self.sourceModel().data(index, role)

    def rowCount(self, parent):
        sections = 0

        prev = None
        for item in self.sourceModel().items:
            cur = item["section"]

            if cur != prev:
                sections += 1

            prev = cur

        # Note: This includes 1 additional, duplicate, section
        # for the bottom item. Ordering of items in model is important.
        return self.sourceModel().rowCount() + sections

    def index(self, row, column, parent):
        return self.createIndex(row, column, parent)

    def mapToSource(self, index):
        if not index.isValid():
            return QtCore.QModelIndex()

        return self.sourceModel().createIndex(index.row(),
                                              index.column(),
                                              QtCore.QModelIndex())

    def parent(self, index):
        return QtCore.QModelIndex()


app = QtGui.QApplication(sys.argv)
model = Model()

for item in ({"label": "Ben", "section": "Human"},
             {"label": "Steve", "section": "Human"},
             {"label": "Alpha12", "section": "Robot"},
             {"label": "Mike", "section": "Toaster"},
             {"label": "Steve", "section": "Human"},
             ):
    model.append(item)

proxy = Proxy()
proxy.setSourceModel(model)

delegate = Delegate()

view = QtGui.QListView()
view.setWindowTitle("My View")
view.setModel(proxy)
view.setItemDelegate(delegate)
view.show()

app.exec_()
</code></pre>