Qt Forum

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Unsolved

    ContentChildren doesn't update after Adding a Component Dynamically in QML

    QML and Qt Quick
    2
    8
    1719
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • carles.sole.grau
      carles.sole.grau last edited by A Former User

      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.

      1 Reply Last reply Reply Quote 0
      • ?
        A Former User last edited by

        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.

        carles.sole.grau 1 Reply Last reply Reply Quote 2
        • carles.sole.grau
          carles.sole.grau @Guest last edited by

          @Wieland
          Thank you very much.
          It helped me a lot.
          Thanks!

          1 Reply Last reply Reply Quote 0
          • carles.sole.grau
            carles.sole.grau last edited by

            @Wieland
            And for destruction? component.destroy() is not enough?
            Thanks

            1 Reply Last reply Reply Quote 0
            • ?
              A Former User last edited by A Former User

              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:

              carles.sole.grau 1 Reply Last reply Reply Quote 1
              • carles.sole.grau
                carles.sole.grau @Guest last edited by carles.sole.grau

                @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.

                ? 1 Reply Last reply Reply Quote 0
                • ?
                  A Former User @carles.sole.grau last edited by A Former User

                  .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.

                  carles.sole.grau 1 Reply Last reply Reply Quote 1
                  • carles.sole.grau
                    carles.sole.grau @Guest last edited by carles.sole.grau

                    @Wieland
                    Thank you for your help

                    1 Reply Last reply Reply Quote 0
                    • First post
                      Last post