pythonpyqtpyqt5signals-slotsqabstractlistmodel

PyQT list view not responding to datachanged signal


I've been following some tutorials and trying to get a list model set up. My main window has two list views that are accessing the same model. When I update an item in one list, the other list doesn't update itself until it gets focus (I click on it). So it looks like the dataChanged signal isn't being emitted, but I can't work out how my code is different to any of the examples I'm basing it from.

main.py

class Main(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(Main, self).__init__(parent)
        self.ui = uic.loadUi("mainwindow.ui", self)

        # Test model and listviews
        data = [10,20,30,40,50]
        myModel = model.MyListModel(data)
        self.ui.listView.setModel(myModel)
        self.ui.listView_2.setModel(myModel)

model.py

class MyListModel(QtCore.QAbstractListModel):
    def __init__(self, data=[], parent=None):
        super(MyListModel, self).__init__(parent)
        self.__data = data

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

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

        if role == QtCore.Qt.ToolTipRole:
            return 'Item at {0}'.format(row)

    def flags(self, index):
        return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable

    def setData(self, index, value, role=QtCore.Qt.EditRole):
        if role == QtCore.Qt.EditRole:
            self.__data[index.row()] = value
            self.dataChanged.emit(index, index)
            return True
        return False

Can anyone see what is wrong here? FYI I'm using PyQT5.2.1 and Python 3.3.


Solution

  • The problem is with the signature of dataChanged signal. In Qt4 it looked like this:

        dataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight)
    

    but in Qt5, it looks like this:

        dataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight,
                    const QVector<int> & roles = QVector<int>())
    

    when I tried your example code with PyQt-5.1.1, I got an error when attempting to emit the signal without the third argument. Strictly speaking, this was incorrect behaviour, because the third argument has a default value. So this is perhaps why the behaviour has changed.

    But it seems that you must now explicitly emit an empty list as the third argument of dataChanged in order for things to work properly in PyQt5:

        self.dataChanged.emit(index, index, [])
    

    or, of course, emit a list of the roles that have actually been changed:

        self.dataChanged.emit(index, index, [QtCore.Qt.EditRole])