I have a subclass of QAbstractListModel
that is the model for a QML ListView
(using PySide6). Each row of the list has a checkbox in it, and when the user checks/unchecks a box, it updates a boolean in that row of the listmodel using my override of setData()
, which works as expected. I also have Button
s that should select/clear all of the checkboxes in the list.
My model subclass provides the following method to select all, which can be called when the user hits the Button
or when other things happen in the application:
def select_all(self):
for i in range(self.rowCount()):
row = self._rows[i]
row['selected'] = True
# Emitting this for each row works...
self.dataChanged.emit(self.index(i), self.index(i), [])
# ... whereas emitting just one signal for ALL rows does NOT work
# self.dataChanged.emit(self.index(0), self.index(self.rowCount()), [])
As you can see from the comments, I need to emit dataChanged
for each row in order for the checkboxes in the ListView
to be updated. Emitting the signal once and using the topLeft
and bottomRight
parameters does not update the state of the checkboxes in the ListView
(but the model data is correctly updated).
According to the documentation, the dataChanged
signal provided by QAbstractItemModel
(and inherited by QAbstractListModel
) has this caveat:
If the items are of the same parent, the affected ones are those between topLeft and bottomRight inclusive. If the items do not have the same parent, the behavior is undefined.
It seems likely that I'm running into the scenario where the rows in my model do NOT have the same parent, and therefore, "the behavior is undefined." I suppose that makes sense, because I never do anything to establish a parent/child relationship for any of my rows. I also saw this answer which implies that Qt behaves differently when the topLeft
and bottomRight
indices are the same. So, I would like to understand this better, and I have a few questions:
QAbstractItemModel
instead of QAbstractListModel
? Does it make any kind of sense for a list?QAbstractListModel
such that dataChanged
can be emitted once to update multiple rows?Thanks!
While the base implementation of Qt item views just updates indiscriminately the view whenever the topLeft
and bottomRight
indexes doesn't match (so you can just provide two "random", but still sibling indexes), the indexes must not only share a common parent (which for one and two dimensional models is always an invalid index), but also both valid.
With this line, the second index is not valid:
self.dataChanged.emit(self.index(0), self.index(self.rowCount()), [])
This is because the indexes are always 0-based, and the index of the last row is actually rowCount - 1
. While they theoretically are siblings, since they share the same parent (the parent of a root index is invalid, as it is the parent of an invalid index), they are not both valid.
So, the correct syntax is:
self.dataChanged.emit(self.index(0), self.index(self.rowCount() - 1), [])
^^^^
Note that the roles
argument is optional and already defaults to an empty list (QVector in C++), so unless you actually want to specify the changed roles, you don't need to provide that.