Creating a QAbstractListModel on the stack and passing to QML
-
Hi!
I have a question regarding the following construct:Suppose you find this function in the data function of a class derived from QAbstractListModel, which is exposed to QML via a context property. The class "OtherModel" is another class derived from QAbstractListModel.
//[...] switch(role) { case OtherModelRole: { QSharedPointer<OtherModel> model = QSharedPointer<OtherModel>(new OtherModel()); model->addData(dataprovider->data()); return QVariant::fromValue(model); } } //[...]
What I am trying to accomplish is that a delegate may request additional information from the model, which again is represented in a different type of model (in this example "OtherModel"). I would like to create this data on demand and would rather avoid storing it somewhere. The motivation here is to use QSharedPointer to manage the lifetime of this object and have it safely cleaned up after the model view is closed or otherwise utilized.
Although the code snippet seems to work just fine at first, it sporadically leads to a crash, sometimes involving heavy scrolling, sometimes just opening the model. What I suppose happens here is that the lifetime of the QSharedPointer is managed by the C++ back end which leads to deletion sooner or later, thus causing a read access violation at some point.
Now my question is:
Is there an alternative to this construct which allows me the same flexibility, or maybe even something better?
Are my assumptions about why the application is crashing correct? I was fighting a nasty heap error over the last couple days which involved me passing a reference to a QAbstractListModel rather than a pointer to QML and I am trying to understand this connection better.Thank you!
Edit: The stack trace of the crash:
1 QQmlAdaptorModel::setModel qqmladaptormodel.cpp 943 0x6558d7ab 2 QQmlAdaptorModel::objectDestroyed qqmladaptormodel.cpp 1018 0x6558dbbf 3 QQmlData::destroyed qqmlengine.cpp 1951 0x658414ed 4 QQmlData::destroyed qqmlengine.cpp 795 0x65840d52 5 QObject::~QObject qobject.cpp 920 0x66642962 6 QAbstractItemModel::~QAbstractItemModel qabstractitemmodel.cpp 1553 0x665879d6 7 QAbstractListModel::~QAbstractListModel qabstractitemmodel.cpp 3743 0x6658b8f8 8 OtherModel::~OtherModel 9 OtherModel::scalar deleting destructor 10 QtSharedPointer::CustomDeleter<OtherModel,QtSharedPointer::NormalDeleter>::execute qsharedpointer_impl.h 195 0xe2e270 11 QtSharedPointer::ExternalRefCountWithCustomDeleter<OtherModel,QtSharedPointer::NormalDeleter>::deleter qsharedpointer_impl.h 217 0xe2dd05 12 QtSharedPointer::ExternalRefCountData::destroy qsharedpointer_impl.h 157 0xd17f59 13 QSharedPointer<OtherModel>::deref qsharedpointer_impl.h 467 0xe2dd77 14 QSharedPointer<OtherModel>::deref qsharedpointer_impl.h 460 0xe2dd23 15 QSharedPointer<OtherModel>::~QSharedPointer<OtherModel> qsharedpointer_impl.h
-
I don't have a solution for you I'm afraid but I can confirm that I also saw crashes when I tried to do something similar. In my case I was trying to implement a "tree and property table view" such that selecting a branch of the tree shows properties associated with that branch in the table below. I was doing something quite similar to you and, similarly, it seemed to work at first but would crash at some point.
In the end, I resolved it by avoiding allocating my property models on the fly but instead had a single 'persistent' model object whose content is adjusted depending on the current branch selection. This worked for me because there is only one visible property table.
-
That's the solution I usually choose when encountering such a problem, but in my case there can be multiple views open at the same time, which do not need to be in sync.
If anyone else has a suggestion how to resolve this problem I am open to suggestions. -
I don't know how to prevent the qml from deleting the shared pointer.
So I return a new classs to qml, it works well. Hope anyone can tell me if it corrected.} else if(role == modeRole) { if(deviceVariable.modeModelPtr.isNull()) return QVariant(); //return QVariant::fromValue(deviceVariable.modeModelPtr.data()); return QVariant::fromValue(new ModeModel(*deviceVariable.modeModelPtr)); } };
DeviceVariables { // .... QSharedPointer<ModeModel> modeModelPtr; } class ModeModel : public QAbstractListModel { public: //.... ModeModel(const ModeModel &other) : QAbstractListModel(other.parent()) { modeList = other.modeList; } private: QList<ModeData> modeList; };
I debug the ~ModeModel() and found the qml will auto delete the new ModeModel() which I provide in the cpp.