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. accessing elements of a ListModel
Forum Updated to NodeBB v4.3 + New Features

accessing elements of a ListModel

Scheduled Pinned Locked Moved Solved QML and Qt Quick
27 Posts 3 Posters 6.8k Views 2 Watching
  • 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.
  • mzimmersM Offline
    mzimmersM Offline
    mzimmers
    wrote on last edited by mzimmers
    #1

    Hi all -

    I need to extract a QML Text type (or equivalent) from one of two ListModels.

    The view represents bottles in a rack. The size of the rack is determined from an environment variable, and can be one of two values (16 or 19 bottles). But...the rack isn't always full.

    I have 2 ListModels for the positioning and sizing of the bottle slots:

      ListModel {
      id: bottleModel_16
      ListElement {
        // position 1
        x: 400
        y: 17
        height: 75
        width: 75
      }
      ...
    }
      ListModel {
      id: bottleModel_19
      ...
    }
    

    Model selection is as follows:

    Column {
      id: bottles
      Repeater {
    	id: bottleRepeater
    	model: (nbrBottlesInRack === 16) ? bottleModel_16 : bottleModel_19
      ...
      }
    

    A function reads a list of bottles from a ViewModel:

    function updateBottles() {
    var listSize = nbrBottlesInRack
    var i
    var bottle
    
    for (i = 0; i < listSize; ++i) {
      bottle = reagentManager.bottleList[i]
      if (bottle === undefined) {
    	bottleRepeater.itemAt(i).cellText = "??"
            bottleRepeater.itemAt(i).cellColor = "white"
      } else {
         ...
      }
      ...
    }
    

    Sometimes, the number of bottles returned won't match the number of slots in the rack. For these slots, I'm currently coloring them in white, and giving them a label of "??" as you can see.

    The issue: I'd like to add a default text field to each element in the models. Something like this (but I know this won't work):

        ListElement {
          // position 1
          x: 400
          y: 17
          height: 75
          width: 75
          Text {
              id: text
              text: qsTr("ETH")
          }
        }
    

    So, two questions:

    • how do I add a text field to a ListElement?
    • how do I access this field in my updateBottles() function above?

    Thanks...

    DiracsbracketD 1 Reply Last reply
    0
    • fcarneyF Offline
      fcarneyF Offline
      fcarney
      wrote on last edited by
      #2

      Why aren't you modifying the source data (ie the ListModel)?
      If you modify the value using itemAt of the repeater you may be breaking the binding to the data in the ListModel.
      ListElements are not Items and cannot hold Items.

      C++ is a perfectly valid school of magic.

      mzimmersM 1 Reply Last reply
      1
      • fcarneyF Offline
        fcarneyF Offline
        fcarney
        wrote on last edited by
        #3

        I am confused as to what you are trying to accomplish with using itemAt.

        C++ is a perfectly valid school of magic.

        mzimmersM 1 Reply Last reply
        0
        • fcarneyF fcarney

          I am confused as to what you are trying to accomplish with using itemAt.

          mzimmersM Offline
          mzimmersM Offline
          mzimmers
          wrote on last edited by
          #4

          @fcarney said in accessing elements of a ListModel:

          I am confused as to what you are trying to accomplish with using itemAt.

          I'm using itemAt() to access the elements of the repeater. It seems to work...not a good idea?

          1 Reply Last reply
          0
          • mzimmersM mzimmers

            Hi all -

            I need to extract a QML Text type (or equivalent) from one of two ListModels.

            The view represents bottles in a rack. The size of the rack is determined from an environment variable, and can be one of two values (16 or 19 bottles). But...the rack isn't always full.

            I have 2 ListModels for the positioning and sizing of the bottle slots:

              ListModel {
              id: bottleModel_16
              ListElement {
                // position 1
                x: 400
                y: 17
                height: 75
                width: 75
              }
              ...
            }
              ListModel {
              id: bottleModel_19
              ...
            }
            

            Model selection is as follows:

            Column {
              id: bottles
              Repeater {
            	id: bottleRepeater
            	model: (nbrBottlesInRack === 16) ? bottleModel_16 : bottleModel_19
              ...
              }
            

            A function reads a list of bottles from a ViewModel:

            function updateBottles() {
            var listSize = nbrBottlesInRack
            var i
            var bottle
            
            for (i = 0; i < listSize; ++i) {
              bottle = reagentManager.bottleList[i]
              if (bottle === undefined) {
            	bottleRepeater.itemAt(i).cellText = "??"
                    bottleRepeater.itemAt(i).cellColor = "white"
              } else {
                 ...
              }
              ...
            }
            

            Sometimes, the number of bottles returned won't match the number of slots in the rack. For these slots, I'm currently coloring them in white, and giving them a label of "??" as you can see.

            The issue: I'd like to add a default text field to each element in the models. Something like this (but I know this won't work):

                ListElement {
                  // position 1
                  x: 400
                  y: 17
                  height: 75
                  width: 75
                  Text {
                      id: text
                      text: qsTr("ETH")
                  }
                }
            

            So, two questions:

            • how do I add a text field to a ListElement?
            • how do I access this field in my updateBottles() function above?

            Thanks...

            DiracsbracketD Offline
            DiracsbracketD Offline
            Diracsbracket
            wrote on last edited by
            #5

            @mzimmers said in accessing elements of a ListModel:

            ListElement {
            // position 1
            x: 400
            y: 17
            height: 75
            width: 75
            Text {
            id: text
            text: qsTr("ETH")
            }
            }

            Why is using:

             ListElement {
                  // position 1
                  x: 400
                  y: 17
                  height: 75
                  width: 75
                  text: qsTr("ETH")
                }
            

            not an option?

            mzimmersM 1 Reply Last reply
            0
            • fcarneyF fcarney

              Why aren't you modifying the source data (ie the ListModel)?
              If you modify the value using itemAt of the repeater you may be breaking the binding to the data in the ListModel.
              ListElements are not Items and cannot hold Items.

              mzimmersM Offline
              mzimmersM Offline
              mzimmers
              wrote on last edited by
              #6

              @fcarney said in accessing elements of a ListModel:

              Why aren't you modifying the source data (ie the ListModel)?
              If you modify the value using itemAt of the repeater you may be breaking the binding to the data in the ListModel.
              ListElements are not Items and cannot hold Items.

              That's essentially what my 2nd question was: how do I refer to the model from a function? More specifically, [how] can I loop through the members of the list, and modify them? I don't know the syntax for doing this.

              1 Reply Last reply
              0
              • DiracsbracketD Diracsbracket

                @mzimmers said in accessing elements of a ListModel:

                ListElement {
                // position 1
                x: 400
                y: 17
                height: 75
                width: 75
                Text {
                id: text
                text: qsTr("ETH")
                }
                }

                Why is using:

                 ListElement {
                      // position 1
                      x: 400
                      y: 17
                      height: 75
                      width: 75
                      text: qsTr("ETH")
                    }
                

                not an option?

                mzimmersM Offline
                mzimmersM Offline
                mzimmers
                wrote on last edited by
                #7

                @Diracsbracket I'm sure that is a good option. My problem isn't how to populate the model; it's how to extract information from individual elements from within a loop.

                DiracsbracketD 1 Reply Last reply
                0
                • mzimmersM mzimmers

                  @Diracsbracket I'm sure that is a good option. My problem isn't how to populate the model; it's how to extract information from individual elements from within a loop.

                  DiracsbracketD Offline
                  DiracsbracketD Offline
                  Diracsbracket
                  wrote on last edited by
                  #8

                  @mzimmers said in accessing elements of a ListModel:

                  My problem isn't how to populate the model;

                  I'm afraid I don't get it indeed. As far as I understand it, your problem is exactly how to populate (in this case, modify) the model. As @fcarney said, the way to go is to update the model itself.
                  And you can access elements of the model using the get() method of the model.

                  mzimmersM 1 Reply Last reply
                  1
                  • DiracsbracketD Diracsbracket

                    @mzimmers said in accessing elements of a ListModel:

                    My problem isn't how to populate the model;

                    I'm afraid I don't get it indeed. As far as I understand it, your problem is exactly how to populate (in this case, modify) the model. As @fcarney said, the way to go is to update the model itself.
                    And you can access elements of the model using the get() method of the model.

                    mzimmersM Offline
                    mzimmersM Offline
                    mzimmers
                    wrote on last edited by
                    #9

                    @Diracsbracket thank you for that -- I actually discovered the get() function just after my previous post (doh).

                    So...that's how I extract or modify the data in the ListModel. Now...how do I get that data to display? Currently, unless I update the repeater (which fcarney said I shouldn't do), I don't seem to pick up the text in the model.

                    Thanks again...

                    DiracsbracketD 1 Reply Last reply
                    0
                    • mzimmersM mzimmers

                      @Diracsbracket thank you for that -- I actually discovered the get() function just after my previous post (doh).

                      So...that's how I extract or modify the data in the ListModel. Now...how do I get that data to display? Currently, unless I update the repeater (which fcarney said I shouldn't do), I don't seem to pick up the text in the model.

                      Thanks again...

                      DiracsbracketD Offline
                      DiracsbracketD Offline
                      Diracsbracket
                      wrote on last edited by
                      #10

                      @mzimmers
                      If the model data is updated, the repeater delegates should automatically refresh as well, assuming that you nowhere have broken the bindings.

                      import QtQuick 2.15
                      import QtQuick.Controls 2.15
                      import QtQuick.Window 2.15
                      
                      Window {
                          width: 640
                          height: 480
                          title: 'UmTreeView'
                          visible: true
                      
                          ListModel {
                              id: lmodel
                              ListElement{
                                  txt: "text1"
                              }
                              ListElement{
                                  txt: "text2"
                              }
                          }
                      
                          Column {
                              Repeater {
                                  model: lmodel
                      
                                  delegate: Text {
                                      text: txt
                                  }
                              }
                          }
                      
                          Button {
                              y: 100
                              text: "Button"
                              onClicked: {
                                  for (var i=0; i<lmodel.count;++i)
                                  {
                                      lmodel.get(i).txt = "Hello"
                                  }
                              }
                          }
                      }
                      
                      mzimmersM 1 Reply Last reply
                      1
                      • DiracsbracketD Diracsbracket

                        @mzimmers
                        If the model data is updated, the repeater delegates should automatically refresh as well, assuming that you nowhere have broken the bindings.

                        import QtQuick 2.15
                        import QtQuick.Controls 2.15
                        import QtQuick.Window 2.15
                        
                        Window {
                            width: 640
                            height: 480
                            title: 'UmTreeView'
                            visible: true
                        
                            ListModel {
                                id: lmodel
                                ListElement{
                                    txt: "text1"
                                }
                                ListElement{
                                    txt: "text2"
                                }
                            }
                        
                            Column {
                                Repeater {
                                    model: lmodel
                        
                                    delegate: Text {
                                        text: txt
                                    }
                                }
                            }
                        
                            Button {
                                y: 100
                                text: "Button"
                                onClicked: {
                                    for (var i=0; i<lmodel.count;++i)
                                    {
                                        lmodel.get(i).txt = "Hello"
                                    }
                                }
                            }
                        }
                        
                        mzimmersM Offline
                        mzimmersM Offline
                        mzimmers
                        wrote on last edited by
                        #11

                        @Diracsbracket OK, I follow that (I think). Your example gave me the idea to do this:

                        ListModel {
                        id: bottleModel_19
                        
                          ListElement {
                            // position 1
                        	bottleLabel: "ETH"
                          }
                          ...
                          
                        Column {
                          id: bottles
                          Repeater {
                            id: bottleRepeater
                            model: (nbrBottlesInRack === 16) ? bottleModel_16 : bottleModel_19
                            Bottle {
                              cellText: model.bottleLabel
                        

                        It seems to work OK...does it look all right to you?

                        1 Reply Last reply
                        0
                        • fcarneyF Offline
                          fcarneyF Offline
                          fcarney
                          wrote on last edited by
                          #12

                          @mzimmers said in accessing elements of a ListModel:

                          cellText: model.bottleLabel

                          The properties of the ListElement should be accessible as just their names:

                          cellText: bottleLabel
                          

                          The names of the properties should be showing up as accessible properties within the delegate Bottle.

                          C++ is a perfectly valid school of magic.

                          mzimmersM 1 Reply Last reply
                          0
                          • fcarneyF fcarney

                            @mzimmers said in accessing elements of a ListModel:

                            cellText: model.bottleLabel

                            The properties of the ListElement should be accessible as just their names:

                            cellText: bottleLabel
                            

                            The names of the properties should be showing up as accessible properties within the delegate Bottle.

                            mzimmersM Offline
                            mzimmersM Offline
                            mzimmers
                            wrote on last edited by mzimmers
                            #13

                            @fcarney that worked for the bottleLable property, but not the others. Perhaps because the other properties' names are the same as properties local to Bottle, and the interpreter gets confused?

                            On the subject of eliminating my use of itemAt() in the repeater: how do you propose I re-implement this?

                            function updateBottles() {
                              ...
                              for (i = 0; i < listSize; ++i) {
                                bottle = reagentManager.bottleList[i]
                                if (bottle === undefined) {
                                  bottleRepeater.itemAt(i).cellColor = Theme.neutralLight
                                } else {
                                  bottleRepeater.itemAt(i).cellColor = Theme.dark
                                }
                                ...
                            

                            This function is called whenever the view becomes visible, to update the bottle data from a C++ Q_PROPERTY.

                            EDIT:

                            I've replaced this functionality as follows:

                              Column {
                              Repeater {
                                Bottle {
                                cellColor: rack.getColor(index)
                            
                            function getColor(i) {
                              var l_color
                              var volume
                              var minVolume
                              var amountNeeded
                              var bottle
                            
                              bottle = reagentManager.bottleList[i]
                              if (bottle === undefined) {
                                l_color = Theme.neutralLight
                              } else {
                                volume = bottle.volume
                                minVolume = bottle.minVolume
                                amountNeeded = bottle.amountNeeded
                                l_color = ((volume - minVolume) >= amountNeeded) ? "green" : "red"
                              }
                              return l_color
                            }
                            

                            This eliminates the write to the repeater that fcarney said is a bad idea, but I get the impression I'm still not really doing this right and/or making unnecessary work for myself. Thoughts?

                            Thanks...

                            EDIT 2:

                            So, I changed my Bottle delegate (is that the correct term?) to look like this:

                                    Bottle {
                                      cellX: model.x
                                      cellY: model.y
                                      cellHeight: model.height
                                      cellWidth: model.width
                                      cellText: (reagentManager.bottleList[index] !== undefined)
                                                ? reagentManager.bottleList[index].name
                                                : bottleLabel
                                      cellColor: rack.getColor(index)
                                      bottleScaleFactor: scaleFactor
                                    }
                            

                            I believe that I can now do away with the updateBottles() function completely. How does this look to the experienced people here?

                            1 Reply Last reply
                            0
                            • fcarneyF Offline
                              fcarneyF Offline
                              fcarney
                              wrote on last edited by
                              #14

                              @mzimmers said in accessing elements of a ListModel:

                              if (bottle === undefined) {
                              bottleRepeater.itemAt(i).cellColor = Theme.neutralLight
                              } else {
                              bottleRepeater.itemAt(i).cellColor = Theme.dark
                              }

                              Bottle {
                                cellColor: reagentManager.bottleList[index] === undefined ? Theme.neutralLight : Theme.dark
                              }
                              

                              Assuming a 1:1 relationship to repeater and bottleList.
                              All properties in Bottle should be set from information flowing into it in a declarative manner.
                              QML is a declarative language. Writing functions to set things is sometimes unavoidable, but should be minimized.

                              C++ is a perfectly valid school of magic.

                              mzimmersM 1 Reply Last reply
                              0
                              • fcarneyF fcarney

                                @mzimmers said in accessing elements of a ListModel:

                                if (bottle === undefined) {
                                bottleRepeater.itemAt(i).cellColor = Theme.neutralLight
                                } else {
                                bottleRepeater.itemAt(i).cellColor = Theme.dark
                                }

                                Bottle {
                                  cellColor: reagentManager.bottleList[index] === undefined ? Theme.neutralLight : Theme.dark
                                }
                                

                                Assuming a 1:1 relationship to repeater and bottleList.
                                All properties in Bottle should be set from information flowing into it in a declarative manner.
                                QML is a declarative language. Writing functions to set things is sometimes unavoidable, but should be minimized.

                                mzimmersM Offline
                                mzimmersM Offline
                                mzimmers
                                wrote on last edited by
                                #15

                                @fcarney noted. The logic is actually a little more complicated than I showed in my example. Here's the real function:

                                function getColor(i) {
                                        var l_color
                                        var volume
                                        var minVolume
                                        var amountNeeded
                                        var bottle
                                
                                        bottle = reagentManager.bottleList[i]
                                        if (bottle === undefined) {
                                            l_color = Theme.neutralLight
                                        } else {
                                            volume = bottle.volume
                                            minVolume = bottle.minVolume
                                            amountNeeded = bottle.amountNeeded
                                            l_color = ((volume - minVolume) >= amountNeeded) ? "green" : "red"
                                        }
                                        return l_color
                                

                                I'd be happy to do away with this if it didn't make the QML too wordy...

                                1 Reply Last reply
                                0
                                • fcarneyF Offline
                                  fcarneyF Offline
                                  fcarney
                                  wrote on last edited by
                                  #16

                                  Are you taking data from 2 sources? If so you can build up a ListModel and combine multiple sources of data using a function. Then use that model as the model for your repeater.

                                  The set function of ListModel takes a js object as input. Any time the lists change you can rebuild that list which will automatically update all the Bottles in the repeater.

                                  C++ is a perfectly valid school of magic.

                                  mzimmersM 1 Reply Last reply
                                  0
                                  • fcarneyF fcarney

                                    Are you taking data from 2 sources? If so you can build up a ListModel and combine multiple sources of data using a function. Then use that model as the model for your repeater.

                                    The set function of ListModel takes a js object as input. Any time the lists change you can rebuild that list which will automatically update all the Bottles in the repeater.

                                    mzimmersM Offline
                                    mzimmersM Offline
                                    mzimmers
                                    wrote on last edited by
                                    #17

                                    @fcarney said in accessing elements of a ListModel:

                                    Are you taking data from 2 sources?

                                    Yes I am: I have the positions and dimensions in a QML ListModel. The "real" information about the bottle (size, contents, expiration date, etc) comes from a view model.

                                    If so you can build up a ListModel and combine multiple sources of data using a function. Then use that model as the model for your repeater.

                                    I like the sound of this. How do I put this in a loop, so I don't have to do something like this?

                                      ListModel {
                                        id: bottleModel_19
                                    
                                        ListElement {
                                          // position 1
                                          x: 407
                                          y: 18
                                          height: 78
                                          width: 78
                                          bottleLabel: (reagentManager.bottleList[index] !== undefined)
                                                       ? reagentManager.bottleList[index].name
                                                       : "ETH"
                                        }
                                        ...
                                    
                                    1 Reply Last reply
                                    0
                                    • fcarneyF Offline
                                      fcarneyF Offline
                                      fcarney
                                      wrote on last edited by fcarney
                                      #18

                                      No, use the methods for ListModel like insert.

                                      onChangeOfSourceList: {
                                        bottleModel_19.clear()
                                        for(var count=0; count<x; ++count){ 
                                          // insert, get, and set use js objects
                                          bottleModel_19.insert(count, {
                                            "x":list1[count].whatever,
                                            ...  // repeat for items from both lists
                                          })
                                        }
                                      }
                                      

                                      Edit: Maybe append is better. Also clear list each time you recreate the list.

                                      C++ is a perfectly valid school of magic.

                                      1 Reply Last reply
                                      1
                                      • mzimmersM Offline
                                        mzimmersM Offline
                                        mzimmers
                                        wrote on last edited by
                                        #19

                                        Does onChangeOfSourceList correspond to the signal I emit when I've updated the list? Here's my property:

                                          Q_PROPERTY(Bottles bottleList
                                                         MEMBER m_bottleList
                                                             NOTIFY bottleListChanged)
                                        

                                        I tried onbottleListChanged, but this isn't correct.

                                        1 Reply Last reply
                                        0
                                        • fcarneyF Offline
                                          fcarneyF Offline
                                          fcarney
                                          wrote on last edited by
                                          #20

                                          Yes, something that emits a signal that you are calling when you update lists.
                                          It will be:

                                          onBottleListChanged
                                          

                                          Notice the B gets capitalized. Is there another signal that can trigger this too? Then you will need to create a function that gets called in each instance.

                                          function updateListModel(){
                                            ...
                                          }
                                          onBottleListChanged: updateListModel()
                                          onOtherListChanged: updateListModel()
                                          

                                          C++ is a perfectly valid school of magic.

                                          mzimmersM 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