How to set the parent item of a QDeclarativeComponent prior creation



  • I am trying to move the creation of qml pages from QML to C++:

    First, I create the parent page
    @
    mView = new QDeclarativeView();
    mView->setSource(QUrl("qrc:/libpages/parentPage.qml"));
    mView->show();
    @

    then I create the other pages with QDeclarativeComponent.:
    @
    QDeclarativeComponent *component = new QDeclarativeComponent(mView->engine(), mView->rootObject());
    component->loadUrl(QUrl("qrc:/libpages/myPage.qml"));
    ...
    @

    I want to inherit the context of the parentPage, so I use QDeclarativeEngine::contextForObject(mView->rootObject()).
    I want also to define the parent item of myPage.qml to be parentPage.qml.
    @
    QObject * object = component->beginCreate(QDeclarativeEngine::contextForObject(mView->rootObject()));
    if (NULL == object)
    {
    component->completeCreate();
    }
    else
    {
    object->setProperty("parent", QVariant::fromValue(qobject_cast<QDeclarativeItem*>(mView->rootObject())));
    object->setProperty("visible", QVariant(false));
    component->completeCreate();
    }
    @

    First question: is the above code correct ?
    Second question: is there an easier way to set the parent element of myPage.qml, prior to the beginCreate() call? The qml code "object Component::createObject ( Item parent, object properties )" accepts a parent element. Is there an equivalent in c++ ?

    Thanks
    Anthony



  • I found today that this way of initializing the object make my application crash.
    This is caused by a DeferredDelete event because my object had no parent.

    I understood that I need also to call "QObject::setParent(QObject*)".
    Initializing the "parent" property of the QDeclarativeItem is necessary to place the item in the scene but it is not enough. This is 2 different things.

    The new code is
    @
    QObject * object = component->beginCreate(QDeclarativeEngine::contextForObject(mView->rootObject()));
    if (NULL == object)
    {
    component->completeCreate();
    }
    else
    {
    object->setProperty("parent", QVariant::fromValue(qobject_cast<QDeclarativeItem*>(mView->rootObject())));
    object->setParent(mView->rootObject());
    object->setProperty("visible", QVariant(false));
    component->completeCreate();
    }
    @

    Is there a way to achieve the init of both "parents" within one call ?



  • Out of interest, why are you using beginCreate() instead of create()?
    Note that in QML you can pass a parent as a parameter (ie, component.createObject(parentArg)).
    In C++ you can set the ownership explicitly to be CppOwnership which will prevent the engine from cleaning up the item due to (implied) JS ownership.

    Using object->setProperty("parent", ...) is generally wrong. If you have a QObject and you want to set its (ownership) parent, use QObject::setParent().



  • [quote author="chriadam" date="1334877825"]Out of interest, why are you using beginCreate() instead of create()?
    [/quote]
    My code uses "Component.onCompleted" to execute some javascript code that will use the parent.width. In case I call create(), "Component.onCompleted" will be executed with a "parent" property set to "null". If I set later on the "parent" property, the "Component.onCompleted" javascript code will not be executed again. So I must set explicitly a "onParentChanged" handler to call again the init code. This causes a new execution of the javascript code plus property bindings for the "parent" property, thus creating a longer creation time.

    As explained in http://qt-project.org/doc/qt-4.8/qdeclarativecomponent.html#beginCreate, beginCreate and completeCreate allows me to distinguish the init steps and I can insert between boths the init of the parent item.
    [quote]
    Note that in QML you can pass a parent as a parameter (ie, component.createObject(parentArg)).
    [/quote]
    From my point of view, component.createObject(parentArg) will set the "parent" property of the QDeclarativeItem plus maybe the QObject::setParent(QObject*).
    [quote]
    In C++ you can set the ownership explicitly to be CppOwnership which will prevent the engine from cleaning up the item due to (implied) JS ownership.
    [/quote]
    I understand better. If I want to avoid my object being destroyed, I can replace
    @
    object->setParent(mView->rootObject());
    @

    with
    @
    QDeclarativeEngine::setObjectOwnership(object, QDeclarativeEngine::CppOwnership);
    @

    Thanks
    [quote]

    Using object->setProperty("parent", ...) is generally wrong. If you have a QObject and you want to set its (ownership) parent, use QObject::setParent().
    [/quote]

    I have not confused object->setProperty("parent", ...) and QObject::setParent().
    In fact, the call
    @
    object->setProperty("parent", QVariant::fromValue(qobject_cast<QDeclarativeItem*>(mView->rootObject())));
    @
    is equivalent to
    @
    qobject_cast<QDeclarativeItem*>(object)->setParentItem(qobject_cast<QDeclarativeItem*>(mView->rootObject()));
    @
    This call is necessary so that the QML item has a parent item and can be displayed.

    So, my initial question came again, is there a way to simplify the following code with keeping in mind that "Component.onCompleted" must be executed with a correct parent item set (like createObject in javascript ?).
    @
    QObject * object = component->beginCreate(QDeclarativeEngine::contextForObject(mView->rootObject()));
    if (NULL == object)
    {
    component->completeCreate();
    }
    else
    {
    object->setProperty("parent", QVariant::fromValue(qobject_cast<QDeclarativeItem*>(mView->rootObject())));
    object->setParent(mView->rootObject()); //OR QDeclarativeEngine::setObjectOwnership(object, QDeclarativeEngine::CppOwnership);
    object->setProperty("visible", QVariant(false));
    component->completeCreate();
    }
    @



  • Yikes, ok, that setParentItem / "parent" property name thing is interesting - I didn't know that ;-) Thanks.
    The fact that the parent isn't set prior to the onCompleted callback being called sounds like a bug, to me.
    /edit: well, the fact that we don't have API to allow a parent argument, perhaps would be a better way of putting it. What you do currently, seems ok to me too, as the beginCreate()/completeCreate() split exists to allow the finer control which you use.

    Regarding ownership, setting a parent object will prevent the object from being collected, but won't (necessarily) prevent the parent object from being collected (unless it explicitly has CppOwnership). In your case, I believe that it will have explicit CppOwnership anyway, since it is the root object of the view. Thus, I think my advice about explicitly setting the ownership semantics of the child object was unnecessary. Sorry for the confusion!

    I'll look into the "parent not set until after the onCompleted handler being called" issue and then respond to this thread again.

    Cheers,
    Chris.


Log in to reply
 

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