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.


  • Moderators

    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


  • Moderators

    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 call destroy() 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.


  • Moderators

    .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 use onContentChildrenChanged looks good to me.



  • @Wieland
    Thank you for your help


Log in to reply
 

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