Solved Problem with model, or Instantiator
-
I have a QAbstractListModel with some methods that I think are correct:
void JsonObjectStore::insert(int ind, QJSValue jsondata) { if(ind < 0 || ind > rowCount()) return; auto pjsondata = prepareData(jsondata); if(!pjsondata) return; QAbstractListModel::beginInsertRows(index(ind,0), ind, ind); m_jsonObjectList.insert(ind, pjsondata); QAbstractListModel::endInsertRows(); emit countChanged(); } void JsonObjectStore::remove(int ind, int n) { if(ind < 0 || (ind + n) > rowCount()){ qDebug() << "JsonObjectStore::remove index+count too big:" << ind << n << rowCount(); return; } QAbstractListModel::beginRemoveRows(index(ind,0), ind, ind+(n-1)); for(int count=ind; count < ind+n && count < m_jsonObjectList.size(); ++count) delete m_jsonObjectList[count]; m_jsonObjectList.remove(ind, n); // does this return an error/crash if extends past list end? QAbstractListModel::endRemoveRows(); emit countChanged(); }
Insert seems to trigger the down stream Instantiator just fine, but I am not seeing the Instantiator call onObjectRemoved. I originally assumed it was the remove method at fault, but I cannot tell what is wrong with it.
Instantiator { id: editor_repeater active: true model: loaded_objects property var grid_object: edit_grid_board property int currentIndex: -1 delegate: EditorObject { id: etdelegate params: datarole inst_index: index width: editor_repeater.grid_object.cell_width * gw height: editor_repeater.grid_object.cell_height * gh anchors.left: parent.left anchors.top: parent.top clip: true selected: editor_repeater.currentIndex === index onDroppedObject: { if(dropobj.drop.keys.includes("pathkey")){ loaded_objects.setProperty(index, inst_obj.dropProperties.path, dropobj.drop.source.dropdata.path.toString()) loaded_objects.dirty = true } } MouseArea { id: etdelegate_mousearea anchors.fill: parent acceptedButtons: Qt.LeftButton | Qt.RightButton onClicked: { //console.log("clicked") if(mouse.button === Qt.LeftButton){ editor_repeater.currentIndex = index }else if(mouse.button === Qt.RightButton){ etdelegate_contextmenu.popup() } } } Menu { id: etdelegate_contextmenu MenuItem { text: "Delete" onTriggered: { editor_repeater.model.remove(index) } } } } onObjectAdded: { var pos_object = grid_object.getDelegateInsertObject(object.gx, object.gy) pos_object.data.push(object) } onObjectRemoved: { console.log(object) } }
There is a bunch of junk in Instantiator, but you get the idea. The object does not seem to dissapear like I thought it would. When I right click to call remove again I get the warning about it being out of bounds. So it is not in the QVector m_jsonObjectList anymore. I don't understand why it is not seeing the beginRemoveRows and endRemoveRows.
-
Here is a better example:
#ifndef CUSTOMMODEL_H #define CUSTOMMODEL_H #include <QObject> #include <QAbstractListModel> class BasicModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(int count READ rowCount NOTIFY countChanged) int m_count; QVector<int> m_list; public: BasicModel(QObject* parent=nullptr) : QAbstractListModel(parent) { } int rowCount(const QModelIndex &parent = QModelIndex()) const{ return m_list.count(); } QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const{ if(index.row() < 0 || index.row() >= rowCount()) return QVariant(); if(role != Qt::DisplayRole) return QVariant(); return m_list[index.row()]; } signals: void countChanged(); public slots: void insert(int ind, int data){ if(ind < 0 || ind > rowCount()) return; QAbstractListModel::beginInsertRows(index(ind,0), ind, ind); m_list.insert(ind, data); QAbstractListModel::endInsertRows(); emit countChanged(); } void remove(int ind){ if(ind < 0 || ind >= rowCount()) return; QAbstractListModel::beginRemoveRows(index(ind,0), ind, ind); m_list.remove(ind); QAbstractListModel::endRemoveRows(); emit countChanged(); } }; #endif // CUSTOMMODEL_H
main.cpp:
#include <QGuiApplication> #include <QQmlApplicationEngine> #include "custommodel.h" int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app(argc, argv); qmlRegisterType<BasicModel>("BasicModels",1,0,"BasicModel"); QQmlApplicationEngine engine; const QUrl url(QStringLiteral("qrc:/main.qml")); QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, &app, [url](QObject *obj, const QUrl &objUrl) { if (!obj && url == objUrl) QCoreApplication::exit(-1); }, Qt::QueuedConnection); engine.load(url); return app.exec(); }
main.qml:
import QtQuick 2.15 import QtQuick.Window 2.15 import QtQml.Models 2.15 import BasicModels 1.0 Window { visible: true width: 640 height: 480 title: qsTr("Test Instantiator Custom Model") BasicModel { id: basicmodel } ListModel { id: objectmodel } Instantiator { id: instantiator model: basicmodel delegate: Text { text: display } onObjectAdded: { console.log("onObjectAdded", object.text) } onObjectRemoved: { console.log("onObjectRemoved", object.text) } Component.onCompleted: { instantiator.model.insert(instantiator.model.count, 1) instantiator.model.insert(instantiator.model.count, 2) console.log(instantiator.model.count) Qt.callLater(removeItems) } function removeItems(){ while(instantiator.model.count){ instantiator.model.remove(0) } console.log(instantiator.model.count) } } }
It calls the onObjectRemoved on the ListModel, but not on the BasicModel. I thought ListModel was based on QAbstractListModel, so I don't know what the issue is.
-
This better illustrates the issue:
import QtQuick 2.15 import QtQuick.Window 2.15 import QtQml.Models 2.15 import BasicModels 1.0 Window { visible: true width: 640 height: 480 title: qsTr("Test Instantiator Custom Model") BasicModel { id: basicmodel } ListModel { id: objectmodel } Instantiator { id: instantiator model: basicmodel delegate: Text { text: display } onObjectAdded: { console.log("onObjectAdded", object.text) } onObjectRemoved: { console.log("onObjectRemoved", object.text) } Component.onCompleted: { console.log("instantiator") instantiator.model.insert(instantiator.model.count, 1) instantiator.model.insert(instantiator.model.count, 2) console.log(instantiator.model.count) Qt.callLater(removeItems) } function removeItems(){ console.log("instantiator") while(instantiator.model.count){ instantiator.model.remove(0) } console.log(instantiator.model.count) } } Instantiator { id: instantiator2 model: objectmodel delegate: Text { text: display } onObjectAdded: { console.log("onObjectAdded", object.text) } onObjectRemoved: { console.log("onObjectRemoved", object.text) } Component.onCompleted: { console.log("instantiator2") instantiator2.model.insert(instantiator.model.count, {display:1}) instantiator2.model.insert(instantiator.model.count, {display:2}) console.log(instantiator2.model.count) Qt.callLater(removeItems) } function removeItems(){ console.log("instantiator2") while(instantiator2.model.count){ instantiator2.model.remove(0) } console.log(instantiator2.model.count) } } }
-
Okay, stupid mistake:
void insert(int ind, int data){ if(ind < 0 || ind > rowCount()) return; QAbstractListModel::beginInsertRows(QModelIndex(), ind, ind); m_list.insert(ind, data); QAbstractListModel::endInsertRows(); emit countChanged(); } void remove(int ind){ if(ind < 0 || ind >= rowCount()) return; QAbstractListModel::beginRemoveRows(QModelIndex(), ind, ind); m_list.remove(ind); QAbstractListModel::endRemoveRows(); emit countChanged(); }