Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. General talk
  3. Brainstorm
  4. passing lists of data to a Component
QtWS25 Last Chance

passing lists of data to a Component

Scheduled Pinned Locked Moved Solved Brainstorm
13 Posts 3 Posters 1.6k 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.
  • mzimmersM Offline
    mzimmersM Offline
    mzimmers
    wrote on last edited by
    #1

    Hi all -

    I'm in need of an idea for passing data into a Component that I've defined. Some but not all of the data originates from a model.

    The model maintains a list of equipment items (Equipment is a QObject-based class). Properties of the Equipment class have been exposed to QML. One property is the equipment category, which can be one of ~40 values. The display will vary based on this category. For example, for a pump I may expose several speed settings, while for a heater I'll expose one or more temperature settings.

    Currently I've hard-coded the implementation for one category. The code looks like this:

    Flickable {
        EquipmentConfigList { 
            equipment: equipmentCopy // from the model
        }
    }
    
    // EquipmentConfigList.qml
    ColumnLayout {
    
        Loader {
            sourceComponent: equipmentListLine
            property string title: "Equipment Name"
            property string value: equipment.name
            property color valueColor: Colors.primaryDarkBlue
        }
        Loader {
            sourceComponent: equipmentListLine
            property string title: "Model Name"
            property string value: equipment.modelId
            property color valueColor: Colors.primaryDarkBlue
        } // etc.
        
        Component {
            id: equipmentListLine
            ColumnLayout {
            ...
    

    This approach is obviously highly imperfect. I could use a Repeater instead of the loader, but I'm stuck on how to "feed" it data. A ListModel won't allow me to use elements from my object (the "not from a script" error). Any suggestions on what I could use here?

    Thanks for any ideas...

    1 Reply Last reply
    0
    • mzimmersM mzimmers

      @GrecKo Before I close this topic, I do have a couple of follow-up questions:

      1. I don't seem to be using a list in the "classical" sense, in that I'm only displaying one item. I realize that a delegate (and therefore, a DelegateChooser) needs a list, but...is there a preferred way to implement what I'm doing? Without a list?

      2. (sort of related) is it OK to use an object as the model for a ListView? It seems to work OK, but there's nothing in the docs that explicitly says I can do this, so I want to make sure this is licit.

      Thanks...

      GrecKoG Offline
      GrecKoG Offline
      GrecKo
      Qt Champions 2018
      wrote on last edited by
      #12

      @mzimmers I didn't realize you didn't want to use this is a model/view but yes it is indeed supported to have a single object as a model. You were 1 link away ;) https://doc.qt.io/qt-6/qtquick-modelviewsdata-modelview.html#object-instances-as-models

      using Loader like mentioned by TomZ is an alternative.

      mzimmersM 1 Reply Last reply
      3
      • TomZT Offline
        TomZT Offline
        TomZ
        wrote on last edited by
        #2

        Yap, that's a bit messy...

        You obviously know about the Loader.setSource second argument. Which doesn't work for a Component.

        Here is one solution:

        Loader {
          onLoaded: item.someProperty = someValue
        }
        

        Your component should have that property and your code here can set it to anything.
        Biggest downside is that the property is null for a small while which may cause you to have warnings unless you check for that in usages.

        Now, the main upside here is that you can call into C++ code and pass in a 'parent' object for memory management (QObject constructor arg). If you pass in the 'item' (property of loader) then your created QObject's livespan will be tied to the loaded item.

        I use this method often in things like a delegate. Your delegate is a thin wrapper around a Loader, the loader selects the Compenent on some properties in the 'modelData' and then assigns the modelData to some property of the component on load.

        mzimmersM 1 Reply Last reply
        0
        • TomZT TomZ

          Yap, that's a bit messy...

          You obviously know about the Loader.setSource second argument. Which doesn't work for a Component.

          Here is one solution:

          Loader {
            onLoaded: item.someProperty = someValue
          }
          

          Your component should have that property and your code here can set it to anything.
          Biggest downside is that the property is null for a small while which may cause you to have warnings unless you check for that in usages.

          Now, the main upside here is that you can call into C++ code and pass in a 'parent' object for memory management (QObject constructor arg). If you pass in the 'item' (property of loader) then your created QObject's livespan will be tied to the loaded item.

          I use this method often in things like a delegate. Your delegate is a thin wrapper around a Loader, the loader selects the Compenent on some properties in the 'modelData' and then assigns the modelData to some property of the component on load.

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

          @TomZ thanks for the reply. One of my goals here is to make the EquipmentConfigList.qml file reusable. I'm looking for a way to aggregate the data I need to display (I would expect to do this inside my Flickable or my reference to the EquipmentConfigList) so that I can use a Repeater (or something similar) to display all the information that's passed to EquipmentConfigList.qml. In C++ I'd use an array of structs (probably) but I don't know how to do this in QML. Any thoughts?

          EDIT:

          This is sort of pseudocode outline of what I'd like to do:

          EquipmentA.qml
          EquipmentB.qml
          EquipmentC.qml
          
          // EquipmentConfig.qml
          
          if (category is A) {
              list = makeListForCategoryA // choosing only the properties I want to display for A
              EquipmentConfigList(list)
          } else if (category is B) {
              list = makeListForCategoryB
              EquipmentConfigList(list)
          } // etc.
          

          My EquipmentA/B/C.qml files ideally wouldn't have to be QML, though I'm not sure what I'd replace them with. But the gist is to have a reusable list that I can conditionally populate based on the category of the equipment object.

          Thanks...

          TomZT 1 Reply Last reply
          0
          • mzimmersM mzimmers

            @TomZ thanks for the reply. One of my goals here is to make the EquipmentConfigList.qml file reusable. I'm looking for a way to aggregate the data I need to display (I would expect to do this inside my Flickable or my reference to the EquipmentConfigList) so that I can use a Repeater (or something similar) to display all the information that's passed to EquipmentConfigList.qml. In C++ I'd use an array of structs (probably) but I don't know how to do this in QML. Any thoughts?

            EDIT:

            This is sort of pseudocode outline of what I'd like to do:

            EquipmentA.qml
            EquipmentB.qml
            EquipmentC.qml
            
            // EquipmentConfig.qml
            
            if (category is A) {
                list = makeListForCategoryA // choosing only the properties I want to display for A
                EquipmentConfigList(list)
            } else if (category is B) {
                list = makeListForCategoryB
                EquipmentConfigList(list)
            } // etc.
            

            My EquipmentA/B/C.qml files ideally wouldn't have to be QML, though I'm not sure what I'd replace them with. But the gist is to have a reusable list that I can conditionally populate based on the category of the equipment object.

            Thanks...

            TomZT Offline
            TomZT Offline
            TomZ
            wrote on last edited by
            #4

            @mzimmers I think you are on the right track, but you seem to be stuck on some minor issues.

            First, remember the strong seperation of model/view. In this specific case you can easily have one model that feeds all 3 types of views. You KNOW that a view will only ask for certain properties if the data is a certain type, as such you can optimize your model code to match (add asserts, even).

            So, in short, to feed your 3 types of views you should only have one backing model. It simply has different properties you query for a specific view.

            Second,
            you can use a Loader with a construction like:

            ListView {
               model: someModel
               Loader {
                 source: {
                   if (model.someProperty)
                     return "./Page1.qml";
                  if (model.foo)
                     return "./Page2.qml";
                  return "./Page3.qml";
               }
            }
            

            Then your pages simply need to use the model property to fetch the data they want to use.

            Page1.qml
            
            Label {
               text: model.something
            }
            
            mzimmersM 1 Reply Last reply
            2
            • TomZT TomZ

              @mzimmers I think you are on the right track, but you seem to be stuck on some minor issues.

              First, remember the strong seperation of model/view. In this specific case you can easily have one model that feeds all 3 types of views. You KNOW that a view will only ask for certain properties if the data is a certain type, as such you can optimize your model code to match (add asserts, even).

              So, in short, to feed your 3 types of views you should only have one backing model. It simply has different properties you query for a specific view.

              Second,
              you can use a Loader with a construction like:

              ListView {
                 model: someModel
                 Loader {
                   source: {
                     if (model.someProperty)
                       return "./Page1.qml";
                    if (model.foo)
                       return "./Page2.qml";
                    return "./Page3.qml";
                 }
              }
              

              Then your pages simply need to use the model property to fetch the data they want to use.

              Page1.qml
              
              Label {
                 text: model.something
              }
              
              mzimmersM Offline
              mzimmersM Offline
              mzimmers
              wrote on last edited by mzimmers
              #5

              @TomZ I think this is moving in the right direction. A couple points:

              I am indeed maintaining a single model. It contains a list of all the equipment items in the system.

              There will be multiple equipment items of the same category in a system. For this reason, I need to somehow inform my Page1/2/3.qml files of the equipment object I'm using. By the time I'm in this QML area, I've already accessed the model and have a single instance of Equipment in my QML.

              Also (minor) when I changed my Flickable to a ListView, it no longer flicks. Here's the complete code for the section:

              // Flickable {
              ListView {
                  boundsBehavior: Flickable.StopAtBounds
                  clip: true
                  contentHeight: contentItem.childrenRect.height
                  implicitWidth: parent.width
                  implicitHeight: innerPane.availableHeight
                  EquipmentConfigList { // this would become Page1/2/3.qml
                      id: equipmentConfigList
                      Layout.fillHeight: true
                      Layout.preferredWidth: rightColumn.availableWidth
                      equipment: equipmentCopy
                  }
              }
              

              Any idea why it doesn't flick for me anymore?

              Thanks for the help...

              GrecKoG 1 Reply Last reply
              0
              • mzimmersM mzimmers

                @TomZ I think this is moving in the right direction. A couple points:

                I am indeed maintaining a single model. It contains a list of all the equipment items in the system.

                There will be multiple equipment items of the same category in a system. For this reason, I need to somehow inform my Page1/2/3.qml files of the equipment object I'm using. By the time I'm in this QML area, I've already accessed the model and have a single instance of Equipment in my QML.

                Also (minor) when I changed my Flickable to a ListView, it no longer flicks. Here's the complete code for the section:

                // Flickable {
                ListView {
                    boundsBehavior: Flickable.StopAtBounds
                    clip: true
                    contentHeight: contentItem.childrenRect.height
                    implicitWidth: parent.width
                    implicitHeight: innerPane.availableHeight
                    EquipmentConfigList { // this would become Page1/2/3.qml
                        id: equipmentConfigList
                        Layout.fillHeight: true
                        Layout.preferredWidth: rightColumn.availableWidth
                        equipment: equipmentCopy
                    }
                }
                

                Any idea why it doesn't flick for me anymore?

                Thanks for the help...

                GrecKoG Offline
                GrecKoG Offline
                GrecKo
                Qt Champions 2018
                wrote on last edited by
                #6

                If you want to have different delegate components based on a type role, use DelegateChooser instead of Loader for delegates.

                If you want to have a single generic delegate, you could define all the possible fields and conditionally show them if there is a corresponding property. Or fetch the properties from the meta-object. Do you want to do that though? What if you want to display a gauge/slider? How would you define the min/max value?

                mzimmersM 1 Reply Last reply
                2
                • GrecKoG GrecKo

                  If you want to have different delegate components based on a type role, use DelegateChooser instead of Loader for delegates.

                  If you want to have a single generic delegate, you could define all the possible fields and conditionally show them if there is a corresponding property. Or fetch the properties from the meta-object. Do you want to do that though? What if you want to display a gauge/slider? How would you define the min/max value?

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

                  @GrecKo said in passing lists of data to a Component:

                  What if you want to display a gauge/slider? How would you define the min/max value?

                  You're a mind reader - I will indeed need to do that elsewhere (the customer wants separate pages for viewing and editing the Equipment fields). I was starting with the easy (readonly) stuff.

                  I'm still having trouble putting this all together, though. I need a Flickable (or ListView) that will conditionally display properties of the Equipment object based on the category of the equipment.

                  Here's the basis of the Equipment class:

                  enum EquipmentCategory {
                      CATEGORY_UNKNOWN,
                      CATEGORY_VSP,
                      CATEGORY_HEATER,
                      CATEGORY_HEATPUMP,
                      // ...
                  }
                  class Equipment : public QObject
                  {
                      Q_OBJECT
                      QML_ELEMENT
                  
                      EquipmentCategory m_category = CATEGORY_UNKNOWN; // exposed to QML
                      // ...
                  }
                  

                  So, how do I use a DelegateChooser here:

                  Flickable {
                      // what is my model?
                      DelagateChooser {
                          role: category
                          DelegateChoice {
                              role: CATEGORY_VSP // this doesn't seem right
                              VspDelegate {} // contained an another file
                          }
                          DelegateChoice {
                              role: CATEGORY_HEATER
                              HeaterDelegate {} // contained an another file
                          }
                      }
                  }
                  

                  Is this on the right track?

                  Thanks...

                  GrecKoG 1 Reply Last reply
                  0
                  • mzimmersM mzimmers

                    @GrecKo said in passing lists of data to a Component:

                    What if you want to display a gauge/slider? How would you define the min/max value?

                    You're a mind reader - I will indeed need to do that elsewhere (the customer wants separate pages for viewing and editing the Equipment fields). I was starting with the easy (readonly) stuff.

                    I'm still having trouble putting this all together, though. I need a Flickable (or ListView) that will conditionally display properties of the Equipment object based on the category of the equipment.

                    Here's the basis of the Equipment class:

                    enum EquipmentCategory {
                        CATEGORY_UNKNOWN,
                        CATEGORY_VSP,
                        CATEGORY_HEATER,
                        CATEGORY_HEATPUMP,
                        // ...
                    }
                    class Equipment : public QObject
                    {
                        Q_OBJECT
                        QML_ELEMENT
                    
                        EquipmentCategory m_category = CATEGORY_UNKNOWN; // exposed to QML
                        // ...
                    }
                    

                    So, how do I use a DelegateChooser here:

                    Flickable {
                        // what is my model?
                        DelagateChooser {
                            role: category
                            DelegateChoice {
                                role: CATEGORY_VSP // this doesn't seem right
                                VspDelegate {} // contained an another file
                            }
                            DelegateChoice {
                                role: CATEGORY_HEATER
                                HeaterDelegate {} // contained an another file
                            }
                        }
                    }
                    

                    Is this on the right track?

                    Thanks...

                    GrecKoG Offline
                    GrecKoG Offline
                    GrecKo
                    Qt Champions 2018
                    wrote on last edited by
                    #8

                    @mzimmers said in passing lists of data to a Component:

                    role: CATEGORY_VSP // this doesn't seem right

                    use roleValue here. And properly expose your enum type to QML.

                    The role property of DelegateChooser is a string, so specify the role name as a string here.

                    mzimmersM 2 Replies Last reply
                    1
                    • GrecKoG GrecKo

                      @mzimmers said in passing lists of data to a Component:

                      role: CATEGORY_VSP // this doesn't seem right

                      use roleValue here. And properly expose your enum type to QML.

                      The role property of DelegateChooser is a string, so specify the role name as a string here.

                      mzimmersM Offline
                      mzimmersM Offline
                      mzimmers
                      wrote on last edited by
                      #9
                      This post is deleted!
                      1 Reply Last reply
                      0
                      • GrecKoG GrecKo

                        @mzimmers said in passing lists of data to a Component:

                        role: CATEGORY_VSP // this doesn't seem right

                        use roleValue here. And properly expose your enum type to QML.

                        The role property of DelegateChooser is a string, so specify the role name as a string here.

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

                        @GrecKo so, this is what I have now (and it seems to work):

                        ListView {
                            anchors.fill: parent
                            model: equipmentCopy
                            clip: true
                            boundsBehavior: Flickable.StopAtBounds
                            delegate: DelegateChooser {
                                role: "category"
                                DelegateChoice {
                                    roleValue: NgaUI.CATEGORY_VSP
                                    delegate: EquipmentConfigList {
                                        equipment: equipmentCopy
                                    }
                                }
                            }
                        }
                        

                        It's a little foreign to me, as I've never used an object as a model, but everything seems to work. Thanks for all the help on this.

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

                          @GrecKo Before I close this topic, I do have a couple of follow-up questions:

                          1. I don't seem to be using a list in the "classical" sense, in that I'm only displaying one item. I realize that a delegate (and therefore, a DelegateChooser) needs a list, but...is there a preferred way to implement what I'm doing? Without a list?

                          2. (sort of related) is it OK to use an object as the model for a ListView? It seems to work OK, but there's nothing in the docs that explicitly says I can do this, so I want to make sure this is licit.

                          Thanks...

                          GrecKoG 1 Reply Last reply
                          0
                          • mzimmersM mzimmers

                            @GrecKo Before I close this topic, I do have a couple of follow-up questions:

                            1. I don't seem to be using a list in the "classical" sense, in that I'm only displaying one item. I realize that a delegate (and therefore, a DelegateChooser) needs a list, but...is there a preferred way to implement what I'm doing? Without a list?

                            2. (sort of related) is it OK to use an object as the model for a ListView? It seems to work OK, but there's nothing in the docs that explicitly says I can do this, so I want to make sure this is licit.

                            Thanks...

                            GrecKoG Offline
                            GrecKoG Offline
                            GrecKo
                            Qt Champions 2018
                            wrote on last edited by
                            #12

                            @mzimmers I didn't realize you didn't want to use this is a model/view but yes it is indeed supported to have a single object as a model. You were 1 link away ;) https://doc.qt.io/qt-6/qtquick-modelviewsdata-modelview.html#object-instances-as-models

                            using Loader like mentioned by TomZ is an alternative.

                            mzimmersM 1 Reply Last reply
                            3
                            • GrecKoG GrecKo

                              @mzimmers I didn't realize you didn't want to use this is a model/view but yes it is indeed supported to have a single object as a model. You were 1 link away ;) https://doc.qt.io/qt-6/qtquick-modelviewsdata-modelview.html#object-instances-as-models

                              using Loader like mentioned by TomZ is an alternative.

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

                              @GrecKo yeah, this is a weird use case. I actually do want a list, just not a list in the Qt model/view/delegate sense. Ideally, I'd like to create a list (array/vector/whatever) of the properties I want to display, and pass it into a universal component for display, but creating and using that structure seems to be a bit out of reach for JS/QML/me. It's OK; this approach is working, if a bit inelegant.

                              1 Reply Last reply
                              0
                              • mzimmersM mzimmers has marked this topic as solved on

                              • Login

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