Why is a dynamically by C++ generated QML Item not aware of other pre-existing Items?

qml, qt, qtquick2

Solution

dynamicallyLoadedObject->setProperty("height", "fillMeWithItemsContainer.height;");
dynamicallyLoadedObject->setProperty("width", "height * 4/3");

This will not actually set a JavaScript binding on the properties. Instead, it will try to assign e.g. the string `"fillMeWithItemsContainer.height;"` to the property, which will fail since the property is of type `int`, not of type `QString`. Assigning bindings to properties is actually not possible from within C++ (with some exceptions like `QQmlBinding`).

dynamicallyLoadedObject->setProperty("parent", QVariant::fromValue<QObject*>(targetItem ));

As Sergei mentioned, you need to call `QQuickItem::setParentItem` instead of setting the `parent` property. That is also a bit more typesafe than the general string-based `setProperty` API. Without a parent item, a QQuickItem will not be visible. Reparenting will only change the parent item, which will affect layouting and a few other things. It will not change the context of the object. A context defines which objects/IDs are in the scope. The context can not be changed after an item has been created. Even if changing the parent would change the context, it is too late - the object has been created, and IDs/object are only looked up in the creation phase.

The solution is to pass the correct context to `QQmlComponent::create()`, which actually has an optional argument. You need to create your item in the context of `fillMeWithItemsContainer`, so you need to get a pointer to it (you did that already with `findChild`) and then retrieve its context, which is possible with `QQmlEngine::contextForObject()`. That should give you enough to figure out how to make it work.

I agree with Sergei though, you should prefer to dynamically create objects in JavaScript instead. Changing QML from within C++ is a layering violation, you should never access QML from C++, only the other way around, to have a nicer separation between UI and program logic.

Problem

I have a `QQuickView` that displays a QML file which itself consists of several QML Items (in separate files). I'd like to add Items dynamically using C++ code. The dynamically added item should resize with the parent one, i.e. `width` and `height` properties reference parent. For example, my target Item in QML looks like this: ``` // TargetContainer.qml Grid { id: fillMeWithItemsContainer objectName: "fillMeWithItemsContainer" } ``` The Item I want to add dynamically (maybe multiple times) looks like this: ``` // DynamicItem.qml Rectangle { color: "white" height: fillMeWithItemsContainer.height width: height * 4/3 } ``` Note that the rectangle references the container it is intended to reside in regarding height. `quickView` is populated with `TargetContainer`: ``` QQuickView *quickView = new QQuickView(); quickView->setSource(QUrl("qrc:/foo/bar/TargetContainer.qml")); ``` So I load a component ``` QQmlComponent dynamicallyLoadedComponent( quickView->engine(), QUrl("qrc:/foo/bar/DynamicItem.qml") ); ``` And I create an Object out of it. ``` QObject *dynamicallyLoadedObject = dynamicallyLoadedComponent.create(); ``` Here I get an error (in application output view): ``` DynamicItem.qml:4: ReferenceError: fillMeWithItemsContainer is not defined ``` `quickView` should be aware of the existence of `fillMeWithItemsContainer`, because it has been created before. However, `fillMeWithItemsContainer` is not a parent of `dynamicallyLoadedObject` (yet) and this could be the problem. So I find the target Item by ``` QQuickItem *targetItem = quickView->rootObject()->findChild<QQuickItem*>("fillMeWithItemsContainer"); ``` And reparent the previously created object ``` dynamicallyLoadedObject->setProperty("parent", QVariant::fromValue<QObject*>(targetItem )); ``` Note: I tried `dynamicallyLoadedObject->setParent()` before, but this seems to be a different kind of parent (`QObject` vs. parent property). However, the width and height properties of `dynamicallyLoadedObject` are set to 0 (because of the reference error, I assume) and won't change. Even if I set them again programatically ``` dynamicallyLoadedObject->setProperty("height", "fillMeWithItemsContainer.height;"); dynamicallyLoadedObject->setProperty("width", "height * 4/3"); ``` nothing changes. If I define DynamicItem directly in QML it works: ``` Grid { id: fillMeWithItemsContainer objectName: "fillMeWithItemsContainer" DynamicItem {} } ``` How do I make sure that dynamically added items can access Items that have been in the QML view before? Alternatively: What am I doing wrong?

Original source