ContentChildren doesn't update after Adding a Component Dynamically in QML
-
Hi everyone,
I have a problem in a project when I'm trying to adding dynamically a Component into a Toolbar (qtcontrols 2). My sample code is:ToolBar { id: toolbar property var myNewComponent; Component.onCompleted: { var component = Qt.createComponent("MyNewComponent.qml"); if (component.status === Component.Ready) { myNewComponent = component.createObject(toolbar, { width: toolbar.width, height: toolbar.implicitHeight}); } var totalHeight = 0; console.log("Num of Children: " + toolbar.contentChildren.length) for(var i in toolbar.contentChildren) { console.log("Children " + i + " is a: " + toolbar.contentChildren[i]) console.log("Children " + i + " height is: " + toolbar.contentChildren[i].height) } } Rectangle { id: myRect color: "green" width: toolbar.width height: toolbar.implicitHeight anchors.bottom: parent.bottom } }
It seems like after adding the component myNewComponent, the contentChildren does not update because it seems like there is just one item (myRect).
Console:
qml: Num of Children: 1 qml: Children 0 is a: QQuickRectangle(0x2496f40) qml: Children 0 height is: 48
Anyone knows if it's a bug, or if there is a code error?
Thank you very much.
-
Hi! You need to use the toolbar's
contentItem
as the parent for your items:import QtQuick 2.7 import QtQuick.Controls 2.0 import QtQuick.Layouts 1.0 ApplicationWindow { visible: true width: 640 height: 480 title: qsTr("Hello World") function add_item() { var component = Qt.createComponent("MyNewComponent.qml"); var posX = toolbar.contentItem.children.length * 25 component.createObject(toolbar.contentItem, {x: posX}); console.log("Num of Children: " + toolbar.contentItem.children.length) } header: ToolBar { id: toolbar Component.onCompleted: console.log("Num of Children: " + toolbar.contentItem.children.length) } Button { text: "Add another item" anchors.centerIn: parent onClicked: add_item() } }
It's also hidden in the documentation ;-)
Items declared as children of a Pane are automatically parented to the Pane's contentItem. Items created dynamically need to be explicitly parented to the contentItem.
-
@Wieland
Thank you very much.
It helped me a lot.
Thanks! -
@Wieland
And for destruction? component.destroy() is not enough?
Thanks -
No need to destroy
component
, once it goes out of scope, the JS garabage collector will destroy it (sooner or later), just like every other JS variable. Also, destroying the component does not affect your items. The component is like the blueprint for a car: If you build cars using a blueprint and then burn the blueprint, the cars will still be there. To remove items from the scene your have to remove them from the toolbar's contentItem children list. The easiest way to do that is to just calldestroy()
on the item you want to get rid of. The contentItem's children list is a QObject and will notice when one of it's children get's destroyed. The list will then remove said item automatically. The following code does exactly this:MyItem.qml
import QtQuick 2.0 Rectangle { property string text: "" width: 20 height: 20 anchors.verticalCenter: parent.verticalCenter color: "orange" Text { text: parent.text anchors.centerIn: parent } Component.onDestruction: console.log("Destructing item " + text) }
main.qml
import QtQuick 2.7 import QtQuick.Controls 2.0 ApplicationWindow { id: mainWindow visible: true width: 640 height: 480 title: "Fun with items" color: "darkgrey" function push_item() { var item_number = toolbar.contentItem.children.length var posX = item_number * 25 + 5 var component = Qt.createComponent("MyItem.qml"); component.createObject(toolbar.contentItem, {x: posX, text: item_number}); } function pop_item() { var len = toolbar.contentItem.children.length if (len === 0) return toolbar.contentItem.children[len-1].destroy() } header: ToolBar { id: toolbar } footer: Rectangle { height: 32 width: mainWindow.width color: "black" Text { x: 5 anchors.verticalCenter: parent.verticalCenter color: "white" text: "Number of items: " + toolbar.contentItem.children.length } } Row { anchors.centerIn: parent spacing: 5 Button { text: "Add item" onClicked: push_item() } Button { text: "Remove last item" onClicked: pop_item() } Button { text: "Exit" onClicked: mainWindow.close() } } }
And this is what it looks like:
-
@Wieland
Thanks again!
The example is very clear to understand.
But from my needs, I still have a problem,
Regarding on your example, if in pop function I add:function pop_item() { var len = toolbar.contentItem.children.length if (len === 0) return toolbar.contentItem.children[len-1].destroy() console.log("NUM OF CHILDS: " + toolbar.contentItem.children.length) }
It's like in the moment when console.log it's done, there is still this item.
Is it because this item is still destorying? And what can we do? Is there a signal like onDestroyed?I try with that (but I don't know if it's really the most correct way):
header: ToolBar { id: toolbar onContentChildrenChanged:{ console.log("NUM OF CHILDS: " + toolbar.contentItem.children.length) } }
Thanks again.
-
.destroy()
doesn't destruct the object immediately but schedules it for deletion. It will be destroyed once the eventloop processes the next events. So in this case, destruction takes place when pop_item() returns. Your solution to useonContentChildrenChanged
looks good to me. -
@Wieland
Thank you for your help