4

I have written a customer Class (inheriting from QObject) in C++ and registered it's type successfully with QML. Currently I'm creating objects of this class statically in C++ and storing a pointer to them in a Model which implements QAbstractListModel. In QML in I can access the objects perfectly as items of the Model.

The customObject is a non-visual object.

I'm visualising in another part of the GUI application (QML) the objects in a ListView with a delegate.

However now I would like to create objects from my custom Class dynamically in QML and store them also in the Model. This is where I'm struggling. I hoped I could create a customObject like this:

import com.myProject.myCustomStuff 1.0

...


Button{
   id: createObjBtn
   text: "create new CustomObj"
   onClicked:{
      var obj = MyCustomObj;
      myObjectManager.addObj(obj);  // object holding the implemented QAbstactListModel
      console.log(typeof(obj)); // returns [Object object]
      console.log(Qt.isQtObject(obj)) // returns false
   }
}

I would appreciate your thoughts. Maybe someone knows a way to do this correctly?

Thanks!

Update 1

As requested by Simon-Warta, here is the Constructor implementation of MyCustomObj.

MyCustomObj.cpp

MyCustomObj::MyCustomObj(QObject *parent) : QObject(parent)
{
    QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
}

2 Answers 2

1

You are confusing the functionality intent of the classes. The QAbstractListModel is intended as a wrapper around a container, yes, you could put the container inside the QAbstractListModel derived class, but you don't really have to, the container can be just about any C++ class, not necessarily even QObject derived, it can be just a QVector<Something> that you can reach from the model via a pointer. Which is good for cases where you have many objects, and not all need to have models at all the time, since those models are pretty heavy.

You don't really need to concern yourself with the ownership, leave that at the C++ side, the same goes for the actual object creation, have a slot called that adds the new object to the container while also using the model's beginInsertRows() and endInsertRows() so that any views will be notified to update efficiently, the new object should also be created in that slot, you can pass any data needed for it from QML, just make sure all data is registered with the Qt meta system so it can work with QVariant for QML-C++ interop.

So it should be something like:

myObjectManager.create(/* any needed data goes here */)

And create() passes eventual data to the C++ side, where you create the object, call beginInsertRows(), add the object to the model's underlying storage, then call endInsertRows() and you are done.

I'd prefer to keep the ownership in the C++ side (and I don't mean explicitly), where I have control over it. Qt kind of sucks in a big way when dealing with object ownership shared between C++ and QML. Ideally, there should be a single shared pointer class that will work across both C++ and QML, so the object is deleted once all reference to it are gone. But that is just not the "Qt way" - the Qt shared pointers do not work with QML, nor do the standard C++ shared pointers, there is actually an entirely different shared reference class for QML, which is not even part of the public API IIRC, very ugly and shortsighted design that only widens the gap between C++ and QML and the associated inconvenience factor.

Sign up to request clarification or add additional context in comments.

4 Comments

thanks! This sounds like very good advice. I will create in C++ a Factory Method to my myObjectManager Class and then handle the object creation, storage and deletion completely in C++.
I am confused by "The QAbstractListModel is intended as a wrapper around a container, yes, you could put the container inside the QAbstractListModel derived class, but you don't really have to"...can you explain this more? I think we always put a container inside (in its derived class) and that's kind of wrapping it as well but you seem to say these are two different things?
@zar - for example, in my case I have millions of objects, but I don't need to visualize all of them at once. So in this case, I use an "attachment" model which doesn't contain the data, just provides a list model interface for a list view to access it. The intent of the model class is to provide access to the data, the actual data location is irrelevant.
Sound interesting but I am not clear how you realize the 'attached' model other than just derive from QAbstractListModel and have the container there? Could you use delegate model instead? In fact that's what I plan to use to show a sub section of data from model but my overall data is not that big at all.
1

I don't know if this is the shortest way but this should do for you. I am starting with the basics for those other up-voters.

MyCustomObj.h

class MyCustomObj : public QObject
{
    Q_OBJECT

public:
    // ...

    Q_INVOKABLE void funfunction();

MyCustomObj.cpp

void MyCustomObj::funfunction()
{
    qDebug("Fun with QML");
}

main.cpp

qmlRegisterType<MyCustomObj>("com.myProject.myCustomStuff", 1, 0, "MyCustomObj");

app.qml

import com.myProject.myCustomStuff 1.0

ApplicationWindow {
    id: mainWindow

    Component {
        id: myComponent

        MyCustomObj {

        }
    }

    Component.onCompleted: {
        var obj = myComponent.createObject(mainWindow)
        if (!obj) console.error("Error creating object")

        console.log(typeof(obj))
        console.log(Qt.isQtObject(obj))

        obj.funfunction()
    }

}

createObject optionally takes properties to be passed to the component.

Storing

Since you are responsible for deleting the objects now, I'd recommend to use shared pointers, such that the objects get destroyed when the List is destroyed.

Your implementation of QAbstactListModel, let's call it MyModel has an adder function like that:

#include <memory> // for std::shared_ptr

class MyModel : public QAbstractListModel
{
    Q_OBJECT

public:

    // ..

    Q_INVOKABLE addObj(MyCustomObj* obj)
    {
        objectlist_.append(std::shared_ptr<MyCustomObj>(obj));
    }

private:
    QList<std::shared_ptr<MyCustomObj>> objectlist_
}

8 Comments

thanks for your answer. But I think it is not what I was looking for. I want to store the dynamically generated myCustomObj in a C++ Model (which implements QAbstractListModel). This Model holds references to all <*MyCustomObjs>.
Well, that would be one more step. Did you think about ownership and resource management? Would you like Javscript or Cpp to own the the generated objects?
Yes, in the Constructor of MyCustomObj I specifically assign ownership to C++.
This will not work because then Javaascript and C++ will both try to destroy the object at some point. Could you please add the code of this constructor? As far as I can see, you should not create those objects in QML but in C++
Your constructor looks good to me. I added a paragraph about addObj
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.