Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. ContentChildren doesn't update after Adding a Component Dynamically in QML

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

Scheduled Pinned Locked Moved QML and Qt Quick
8 Posts 2 Posters 2.2k Views
  • 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.grauC Offline
    carles.sole.grauC Offline
    carles.sole.grau
    wrote on last edited by A Former User
    #1

    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
    0
    • ? Offline
      ? Offline
      A Former User
      wrote on last edited by
      #2

      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.grauC 1 Reply Last reply
      2
      • ? A Former User

        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.grauC Offline
        carles.sole.grauC Offline
        carles.sole.grau
        wrote on last edited by
        #3

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

        1 Reply Last reply
        0
        • carles.sole.grauC Offline
          carles.sole.grauC Offline
          carles.sole.grau
          wrote on last edited by
          #4

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

          1 Reply Last reply
          0
          • ? Offline
            ? Offline
            A Former User
            wrote on last edited by A Former User
            #5

            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.grauC 1 Reply Last reply
            1
            • ? 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.grauC Offline
              carles.sole.grauC Offline
              carles.sole.grau
              wrote on last edited by carles.sole.grau
              #6

              @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
              0
              • carles.sole.grauC 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.

                ? Offline
                ? Offline
                A Former User
                wrote on last edited by A Former User
                #7

                .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.grauC 1 Reply Last reply
                1
                • ? 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.grauC Offline
                  carles.sole.grauC Offline
                  carles.sole.grau
                  wrote on last edited by carles.sole.grau
                  #8

                  @Wieland
                  Thank you for your help

                  1 Reply Last reply
                  0

                  • Login

                  • Login or register to search.
                  • First post
                    Last post
                  0
                  • Categories
                  • Recent
                  • Tags
                  • Popular
                  • Users
                  • Groups
                  • Search
                  • Get Qt Extensions
                  • Unsolved