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.5k 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.
  • M Offline
    M Offline
    mzimmers
    wrote on 15 Mar 2024, 21:52 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
    • M mzimmers
      20 Mar 2024, 13:28

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

      G Offline
      G Offline
      GrecKo
      Qt Champions 2018
      wrote on 22 Mar 2024, 14:06 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.

      M 1 Reply Last reply 22 Mar 2024, 14:42
      3
      • T Offline
        T Offline
        TomZ
        wrote on 17 Mar 2024, 21:00 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.

        M 1 Reply Last reply 18 Mar 2024, 16:04
        0
        • T TomZ
          17 Mar 2024, 21:00

          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.

          M Offline
          M Offline
          mzimmers
          wrote on 18 Mar 2024, 16:04 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...

          T 1 Reply Last reply 18 Mar 2024, 22:26
          0
          • M mzimmers
            18 Mar 2024, 16:04

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

            T Offline
            T Offline
            TomZ
            wrote on 18 Mar 2024, 22:26 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
            }
            
            M 1 Reply Last reply 18 Mar 2024, 23:27
            2
            • T TomZ
              18 Mar 2024, 22:26

              @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
              }
              
              M Offline
              M Offline
              mzimmers
              wrote on 18 Mar 2024, 23:27 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...

              G 1 Reply Last reply 19 Mar 2024, 00:46
              0
              • M mzimmers
                18 Mar 2024, 23:27

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

                G Offline
                G Offline
                GrecKo
                Qt Champions 2018
                wrote on 19 Mar 2024, 00:46 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?

                M 1 Reply Last reply 19 Mar 2024, 01:45
                2
                • G GrecKo
                  19 Mar 2024, 00:46

                  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?

                  M Offline
                  M Offline
                  mzimmers
                  wrote on 19 Mar 2024, 01:45 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...

                  G 1 Reply Last reply 19 Mar 2024, 15:14
                  0
                  • M mzimmers
                    19 Mar 2024, 01:45

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

                    G Offline
                    G Offline
                    GrecKo
                    Qt Champions 2018
                    wrote on 19 Mar 2024, 15:14 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.

                    M 2 Replies Last reply 19 Mar 2024, 23:02
                    1
                    • G GrecKo
                      19 Mar 2024, 15:14

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

                      M Offline
                      M Offline
                      mzimmers
                      wrote on 19 Mar 2024, 23:02 last edited by
                      #9
                      This post is deleted!
                      1 Reply Last reply
                      0
                      • G GrecKo
                        19 Mar 2024, 15:14

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

                        M Offline
                        M Offline
                        mzimmers
                        wrote on 19 Mar 2024, 23:28 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
                        • M Offline
                          M Offline
                          mzimmers
                          wrote on 20 Mar 2024, 13:28 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...

                          G 1 Reply Last reply 22 Mar 2024, 14:06
                          0
                          • M mzimmers
                            20 Mar 2024, 13:28

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

                            G Offline
                            G Offline
                            GrecKo
                            Qt Champions 2018
                            wrote on 22 Mar 2024, 14:06 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.

                            M 1 Reply Last reply 22 Mar 2024, 14:42
                            3
                            • G GrecKo
                              22 Mar 2024, 14:06

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

                              M Offline
                              M Offline
                              mzimmers
                              wrote on 22 Mar 2024, 14:42 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
                              • M mzimmers has marked this topic as solved on 3 Apr 2024, 17:03

                              7/13

                              19 Mar 2024, 01:45

                              • Login

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