handling deletion of item in C++ model
-
Hi all -
I have a C++ defined model:
typedef std::shared_ptr<Schedule> SchedulePtr; typedef QList<SchedulePtr> ScheduleList; class ScheduleModel : public QAbstractListModel { Q_OBJECT QML_ELEMENT ScheduleList m_list;Exposed properly to QML, and used as follows:
// ScheduleScreen.qml GridView { id: gridView model: scheduleModel delegate: ScheduleCard { id: scheduleCard schedule: scheduleObject // a role in my model. // ScheduleCard.qml Pane { property Schedule schedule Label { text: schedule.name // a Q_PROPERTY in my Schedule class.Everything seems to work fine, until I delete an item from my model, at which point, the console gives me a bunch of TypeErrors like this one:
TypeError: Cannot read property 'name' of nullI think I understand why this is happening (the object's been deleted), but I'm wondering if there's something I could be doing to cause a more graceful handling of the deletion.
Thanks...
-
int ScheduleModel::rowCount(const QModelIndex &parent) const { // For list models only the root node (an invalid parent) should return the list's size. For all // other (valid) parents, rowCount() should return 0 so that it does not become a tree model. if (parent.isValid()) return 0; return m_list.size(); }@mzimmers
Your C++ code looks good. You know I know nothing about QML. If this is true you ought be able to knock up a one page standalone QML project with that code and test against a standard model or your model to test. It ought not go wrong.Signals like QAbstractItemModel::rowsAboutToBeRemoved() and QAbstractItemModel::rowsRemoved() are emitted by any model. They come from your
begin/endRemoveRows()calls. Your QML view should see that and work accordingly. I don't know if you can test from QML this is being received just to check. -
@mzimmers
If the object is deleted there may be no point in having the row on the model.So, you need to call
beginRemoveRowsbefore deleting the object.@Mesrine my removeRows() routine:
bool ScheduleModel::removeRows(int row, int count, const QModelIndex &parent) { beginRemoveRows(parent, row, row + count - 1); m_list.remove(row, count); endRemoveRows(); return true; }This is all I do when I'm deleting an item. Am I missing a step? My model is a list of shared_ptrs.
-
@Mesrine my removeRows() routine:
bool ScheduleModel::removeRows(int row, int count, const QModelIndex &parent) { beginRemoveRows(parent, row, row + count - 1); m_list.remove(row, count); endRemoveRows(); return true; }This is all I do when I'm deleting an item. Am I missing a step? My model is a list of shared_ptrs.
@mzimmers
It seems you are doing what the docs say.
I do not know why I also add
emit countChanged(count());
BetweenbeginRemoveRowsandendRemoveRowswherecountis a QProperty I define like
Q_PROPERTY(int count READ count NOTIFY countChanged)
.
This may not solve your issue but you can try until a better answer comes up. -
@mzimmers
It seems you are doing what the docs say.
I do not know why I also add
emit countChanged(count());
BetweenbeginRemoveRowsandendRemoveRowswherecountis a QProperty I define like
Q_PROPERTY(int count READ count NOTIFY countChanged)
.
This may not solve your issue but you can try until a better answer comes up. -
@Mesrine sorry it took me so long to get back to this. Your suggestion is intriguing, but I'm not sure I fully understand it. To what slot does countChanged() connect?
-
int ScheduleModel::rowCount(const QModelIndex &parent) const { // For list models only the root node (an invalid parent) should return the list's size. For all // other (valid) parents, rowCount() should return 0 so that it does not become a tree model. if (parent.isValid()) return 0; return m_list.size(); } -
int ScheduleModel::rowCount(const QModelIndex &parent) const { // For list models only the root node (an invalid parent) should return the list's size. For all // other (valid) parents, rowCount() should return 0 so that it does not become a tree model. if (parent.isValid()) return 0; return m_list.size(); }@mzimmers
Your C++ code looks good. You know I know nothing about QML. If this is true you ought be able to knock up a one page standalone QML project with that code and test against a standard model or your model to test. It ought not go wrong.Signals like QAbstractItemModel::rowsAboutToBeRemoved() and QAbstractItemModel::rowsRemoved() are emitted by any model. They come from your
begin/endRemoveRows()calls. Your QML view should see that and work accordingly. I don't know if you can test from QML this is being received just to check. -
@mzimmers
Your C++ code looks good. You know I know nothing about QML. If this is true you ought be able to knock up a one page standalone QML project with that code and test against a standard model or your model to test. It ought not go wrong.Signals like QAbstractItemModel::rowsAboutToBeRemoved() and QAbstractItemModel::rowsRemoved() are emitted by any model. They come from your
begin/endRemoveRows()calls. Your QML view should see that and work accordingly. I don't know if you can test from QML this is being received just to check. -
@JonB makes sense. I can accept any signal in QML, so that's not the challenge. What I'm wondering is what I should do on the QML side once I realize that a model element has been deleted. (I realize this isn't in your wheelhouse.)
@mzimmers
If you are displaying a list of them in a "grid view" or "table view" I would not expect you to have to do anything. Like theQTableViewwidget I would expect it to see those signals and handle it itself without getting null references. If you keep something around in the QML referencing the deleted row/item then that might error? -
M mzimmers has marked this topic as solved on