Dynamically create a whole new QML object from C++



  • Hello everyone,

    I have a QML component Foo defined it its own file foo.qml. In my QtQuick app, I need to dynamically create instances of this component as a respond of a user action. I was able to do it using Qt.createQmlObject().

    Now I want to have a matching C++ object with exposed properties/methods so when the QML component Foo is changed, its C++ equivalent is updated. To do so, I have created a C++ class FooModel that subclasses QtQuickItem and registered my C++ new type using qmlRegisterType().

    My problem is : I want to dynamically instanciate FooModel and as a result, have a Foo component to be created in the QML side of the application, so no more use of Qt.createQmlObject(). And when I say instanciate, I mean to fully define the Foo component in my C++ object, and not loading foo.qml using QQmlComponent.create(). The idea behind this is to get rid of the foo.qml file.

    I looked into the documentation and the Internet, but I cannot find any example that matches my need.
    So my questions are :
    -is it possible to fully create a QML component from C++,? Something like :

    Rect* rect = new Rect(QMLRoot);
    Text* text = new Text(rect);
    MouseArea* marea = new MouseArea(rect);
    

    -if it is possible, how can you handle events, for example how do you plug to MouseArea events?

    I have the feeling that I am trying to do something that is not possible, but I am not a Qt expert so I am looking for your advices.
    Thanks for any help!



  • Defining a visual QML component in C++ is done by inheriting from QQuickItem. Look for the "Custom Scene Graph Items" section.

    Unfortunately most (all?) of the building blocks such as Rectangle and Text lack public C++ APIs. Is there a reason behind the avoidance of QQmlComponent?



  • Hello and thanks for your answer.
    The reason I want to avoid loading an existing QML file is because I want this kind of relationship: each instance of my C++ object will have its own instance of QML object. By doing so, I want all the QML properties to be linked to the C++ properties of the instance.

    I know that I can expose a C++ object into the QML context with:

    QQmlApplicationEngine engine;
    MyObject sc;
    engine.rootContext()->setContextProperty("myobject",&sc);
    

    And then in my Foo.qml file use it to access the Q_PROPERTY I have declared in the class MyObject:

    Rectangle{
       width:sc.width
    }
    

    But for me (and I may be wrong), doing this will expose a global "myobject" variable that will be used by every instances of Foo.qml that I will dynamically create with Qt.createQmlObject or Qt.createComponent. And I want a one-instance-to-one-instance system.

    I hope I made myself clear :/



  • I think that I understand. For some nontrivial number of QML objects, the goal is to bind to a unique property whose value can be controlled from C++. SetContextProperty does indeed create a global variable that will have the same value for all items that reference it.

    It sounds like these C++ objects are sources for properties, but probably don't have visual properties.

    One option is to compose the visual portion in QML, with a QObject-derived, qmlRegisterType() registered object as the source for the C++ properties.

    MyItem.qml

    import org.examples 1.0
    Rectangle {
        CppObj { id: cppObj }
        Text { text: cppObj.text }
    }
    

    main.cpp

    class CppObj : public QObject {
        Q_OBJECT
        Q_PROPERTY(QString text ...)
    public:
        CppObj(QObject * parent = nullptr);
        ...
    } ;
    
    CppObj::CppObj(QObject *parent) : QObject(parent)
    {
        // register the object instance or otherwise initialize properties
    }
    
    int main()
    {
        ...
        qmlRegisterType<CppObj>("org.examples", 1, 0, "CppObj");
        // Now load the root document
        ...
    }
    

    If this works for the use case, QQmlParserStatus might be of interest.



  • Hello,
    Just to be sure, in your example, in which context do you dynamically create the QML component? In the C++ context or the QML context? Is that what you mean in the constructor?

    I will have a look at QQmlParserStatus, never used that class before so I may ask you some questions about it too.



  • @bguivarch said in Dynamically create a whole new QML object from C++:

    Hello,
    Just to be sure, in your example, in which context do you dynamically create the QML component? In the C++ context or the QML context? Is that what you mean in the constructor?

    It doesn't matter. After the type registration, any use of MyItem will instantiate an instance of CppObj. The CppObj's parent is the MyItem instance, and will be destroyed when MyItem is.

    The comment in CppObj::CppObj() is a place holder for doing something based on the 1:1 QML to C++ object relationship previously mentioned. I wasn't able to come up with a better, concise example construct based on the problem description.



  • Ok thanks for your help.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.