Why does layoutChanged.emit() not update the QListView when using a QSortFilterProxyModel?

Viewed 434

I am having trouble getting QListView to work with proxy models.

I started using QListViews with models without any issues, and changed data during runtime using datachanged.emit() signals of that model.

However, when I try to change data in a proxy-model based QListView, that data is not getting updated in the interface.

I boiled it down to a minimal example, but still cannot see the root cause of this behaviour:

from PyQt5.QtWidgets import (QWidget, QHBoxLayout, QApplication, QListView)
from PyQt5 import QtCore


# model for QListView
class ListModel(QtCore.QAbstractListModel):
    def __init__(self, items):
        super().__init__()
        self.items = items

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

    def rowCount(self, index):
        return len(self.items)


class MainWindow(QWidget):
    def __init__(self):
        super().__init__()
        # creating list with "standard" model and an initial dataset => this works
        list_view_1 = QListView()
        list_view_1.setModel(ListModel(['original', 'model']))

        # creating list with proxy based on source model and an initial dataset => this works
        list_view_2 = QListView()
        proxy = QtCore.QSortFilterProxyModel()
        proxy.setSourceModel(ListModel(['original', 'proxy+model']))
        list_view_2.setModel(proxy)

        # changing data in model and emitting layout change => this works
        list_view_1.model().items = ['changed', 'model']
        list_view_1.model().layoutChanged.emit()

        # changing data in proxy and emitting layout change => this does not work?
        list_view_2.model().items = ['changed', 'proxy+model']
        list_view_2.model().layoutChanged.emit()

        # adding layout to the interface
        hbox = QHBoxLayout()
        hbox.addWidget(list_view_1)
        hbox.addWidget(list_view_2)

        self.setLayout(hbox)
        self.show()


app = QApplication([])
window = MainWindow()
app.exec_()

None of my searches led to an explanation of this behaviour, a related stack thread only mentioned the order of the models, which I already implemented that way.

Can anyone point me to a solution on how to update data in a "proxy-model" correctly?

1 Answers

The problem is that you are creating an attribute (items) in the proxy model instead of updating the source model items, the solution is to update the source model items:

# ...
# changing data in model and emitting layout change => this works
list_view_1.model().items = ["changed", "model"]
list_view_1.model().layoutChanged.emit()

# changing data in proxy and emitting layout change => this does not work?
list_view_2.model().sourceModel().items = ["changed", "proxy+model"]
list_view_2.model().layoutChanged.emit()
# ...
Related