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. Delegate creation and multithreaded property updating
Forum Updated to NodeBB v4.3 + New Features

Delegate creation and multithreaded property updating

Scheduled Pinned Locked Moved Solved QML and Qt Quick
47 Posts 3 Posters 11.3k 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.
  • GrecKoG GrecKo

    @Asperamanca said in Delegate creation and multithreaded property updating:

    I use the creation in C++ only on a higher level in my scene. So e.g. I can create an item in C++ that in turn contains a TableView

    Why? That's adding some extra coupling between c++ and QML. Can't you just expose your model and access it when needed in QML?

    @Asperamanca said in Delegate creation and multithreaded property updating:

    When you want to add that table component, you

    1. Create the property class
    2. Find the "itemContainer" by traversing the tree of QQuickItems and looking for an object named "itemContainer"
    3. Create the QML component "MyTable" in C++, pass your property class as initial property, and pass the itemContainer as parent item. You can also use the root item of a scene if you don't need any static QML.

    That seems overly complicated to me.

    A Offline
    A Offline
    Asperamanca
    wrote on last edited by
    #37

    @GrecKo said in Delegate creation and multithreaded property updating:

    Why? That's adding some extra coupling between c++ and QML. Can't you just expose your model and access it when needed in QML?

    Good question. Several reasons:

    1. When I started the project, I was not aware of a QML way to just expose a model and have and item automatically created for each entry in the model. Something like a crossover between Loader and TableView. Does something like this exist now?
    2. I need very precise control when items are created. For example, I might create data classes for 100k items (they are cheap to instantiate), and only create QML components for those that will be actually visible. The mythical components from item 1 might be able to do that, but I'd need to see it documented to understand whether it would be suitable for my use case.
    3. I minimize JavaScript usage, because I have good tooling and static analyzers for C++, but not for JavaScript, and also we have developers trained in C++, and please don't tell me "everyone can write JavaScript". Well, yes. Bad, unsafe JavaScript.
    4. This led to offloading pretty much all logic into C++, with the exception of layouting. QML is my layout language, and it is very good for that.
    5. Item hit testing, most of the event handling....all done in C++ in a unified manner that I can roll out to 22 different types of component with ease. Did I mention I have written several bug reports about QML's handler components? Handling the QEvents yourself is much more reliable, I eliminated a whole layer of buggy code that way.
    6. Finally, I am not writing a library. I am writing an application. Both the C++ and QML code are specific to the use case, so it's OK that C++ and QML code have to match. Even so, I fail to see "extra" coupling. The kind of coupling is different: Instead of using a Qt model to communicate between C++ and QML, I have defined my own API. Each component must provide both a C++ property class and a QML component identifier via factory. The rest of the code is generic. My dataset contains component FOO? Create a FOO property class up front. Once I have some data (and with every data change), I query my loader whether we need the UI for it. If we want it, I query the factory for the correct QML component for FOO, and load it.

    It's rolled out in a product and easy to work with, and I haven't found any performance issues with it, either.

    1 Reply Last reply
    1
    • GrecKoG GrecKo

      @Asperamanca said in Delegate creation and multithreaded property updating:

      I use the creation in C++ only on a higher level in my scene. So e.g. I can create an item in C++ that in turn contains a TableView

      Why? That's adding some extra coupling between c++ and QML. Can't you just expose your model and access it when needed in QML?

      @Asperamanca said in Delegate creation and multithreaded property updating:

      When you want to add that table component, you

      1. Create the property class
      2. Find the "itemContainer" by traversing the tree of QQuickItems and looking for an object named "itemContainer"
      3. Create the QML component "MyTable" in C++, pass your property class as initial property, and pass the itemContainer as parent item. You can also use the root item of a scene if you don't need any static QML.

      That seems overly complicated to me.

      A Offline
      A Offline
      Asperamanca
      wrote on last edited by
      #38

      @GrecKo But I do agree that for OP's use case, the model is the better approach.

      1 Reply Last reply
      0
      • GrecKoG Offline
        GrecKoG Offline
        GrecKo
        Qt Champions 2018
        wrote on last edited by
        #39
        1. & 2.
          I don't really understand the question. Models&Views does that.
          A ListView can do that with the added benefit of delegate pooling.
          If the ListView does not fit your use case (you might not need it to position the delegates). A Repeater (or Instantiator) plus a proxy model might do the trick.

        2. This doesn't involve JS, just a couple more QML objects with basic bindings.

        3. You still could do that in C++ if you want.

        Your solution does work, I'm just saying it might not be the most straight forward one or necessary for everyone.

        A 1 Reply Last reply
        0
        • GrecKoG GrecKo
          1. & 2.
            I don't really understand the question. Models&Views does that.
            A ListView can do that with the added benefit of delegate pooling.
            If the ListView does not fit your use case (you might not need it to position the delegates). A Repeater (or Instantiator) plus a proxy model might do the trick.

          2. This doesn't involve JS, just a couple more QML objects with basic bindings.

          3. You still could do that in C++ if you want.

          Your solution does work, I'm just saying it might not be the most straight forward one or necessary for everyone.

          A Offline
          A Offline
          Asperamanca
          wrote on last edited by
          #40

          @GrecKo said in Delegate creation and multithreaded property updating:

          & 2.
          I don't really understand the question. Models&Views does that.
          A ListView can do that with the added benefit of delegate pooling.
          If the ListView does not fit your use case (you might not need it to position the delegates). A Repeater (or Instantiator) plus a proxy model might do the trick.

          Please walk me through it, if you would be so kind.
          When I want to display a table of items, I use a TableView. The model can be huge, but only the delegates actually needed for display are created (and a few more to ensure smooth scrolling, maybe). So far, so good.

          In my case, I do not want to arrange my components in a pre-defined way. The user has positioned them in a certain position, and maybe there is some (also user-specified) runtime logic that could move them around. Also, whether they are actually visible right now might depend on runtime logic.

          A ListView is a Flickable. A flickable does not make sense for my use case (the components are geometrically unrelated to each other).

          A Repeater (AFAIK) only instantiates the same type of item. I could use it, but I would need a wrapper item.
          Then again "The Repeater type creates all of its delegate items when the repeater is first created.". That means, if I have 100k data classes, I will get 100k QML items of some kind. Now, I'd need to measure how expensive that would be, and it's certainly not free.

          A Loader can only load a single component. I could probably pair it with Repeater to get what I want. I think it would pretty much do the same thing as my current code, at the cost of always loading the 100k Loaders. Also, how do you use a loader to load a component from a QML module directly? Eventually, I want my components to be pre-compiled.

          Did I miss an option?

          I admit, part of the reason I chose this approach is also that I'm not much of a ModelView guy, so I didn't consider using ModelView for anything else but where a ready-to-use View component already exists.

          F GrecKoG 2 Replies Last reply
          0
          • A Asperamanca

            @GrecKo said in Delegate creation and multithreaded property updating:

            & 2.
            I don't really understand the question. Models&Views does that.
            A ListView can do that with the added benefit of delegate pooling.
            If the ListView does not fit your use case (you might not need it to position the delegates). A Repeater (or Instantiator) plus a proxy model might do the trick.

            Please walk me through it, if you would be so kind.
            When I want to display a table of items, I use a TableView. The model can be huge, but only the delegates actually needed for display are created (and a few more to ensure smooth scrolling, maybe). So far, so good.

            In my case, I do not want to arrange my components in a pre-defined way. The user has positioned them in a certain position, and maybe there is some (also user-specified) runtime logic that could move them around. Also, whether they are actually visible right now might depend on runtime logic.

            A ListView is a Flickable. A flickable does not make sense for my use case (the components are geometrically unrelated to each other).

            A Repeater (AFAIK) only instantiates the same type of item. I could use it, but I would need a wrapper item.
            Then again "The Repeater type creates all of its delegate items when the repeater is first created.". That means, if I have 100k data classes, I will get 100k QML items of some kind. Now, I'd need to measure how expensive that would be, and it's certainly not free.

            A Loader can only load a single component. I could probably pair it with Repeater to get what I want. I think it would pretty much do the same thing as my current code, at the cost of always loading the 100k Loaders. Also, how do you use a loader to load a component from a QML module directly? Eventually, I want my components to be pre-compiled.

            Did I miss an option?

            I admit, part of the reason I chose this approach is also that I'm not much of a ModelView guy, so I didn't consider using ModelView for anything else but where a ready-to-use View component already exists.

            F Offline
            F Offline
            felsi
            wrote on last edited by felsi
            #41

            I don't want to bother you guys anymore, but i have some final thoughts...
            Trying to avoid bindings as much as possible has become a bit of a habit and i use connections+js a lot...
            It is clear now, but i did not expect, that connections+loader can have these side effects.
            Maybe this is worth to be mentioned somewhere in the documentation with a small note.
            And, by the way, is the following safe?

            Button
            {
                property PlaylistTrack playlistTrack
                onPlaylistTrackChanged: updateTrack()
                
                property Track track: playlistTrack.track
                onTrackChanged: updateTrack()
                
                function updateTrack()
                {
                    ...
                }
                    
            }
            
            
            A 1 Reply Last reply
            0
            • F Offline
              F Offline
              felsi
              wrote on last edited by
              #42

              Just a small simplification mistake, i don't call it twice, of course...

              1 Reply Last reply
              0
              • F felsi

                I don't want to bother you guys anymore, but i have some final thoughts...
                Trying to avoid bindings as much as possible has become a bit of a habit and i use connections+js a lot...
                It is clear now, but i did not expect, that connections+loader can have these side effects.
                Maybe this is worth to be mentioned somewhere in the documentation with a small note.
                And, by the way, is the following safe?

                Button
                {
                    property PlaylistTrack playlistTrack
                    onPlaylistTrackChanged: updateTrack()
                    
                    property Track track: playlistTrack.track
                    onTrackChanged: updateTrack()
                    
                    function updateTrack()
                    {
                        ...
                    }
                        
                }
                
                
                A Offline
                A Offline
                Asperamanca
                wrote on last edited by
                #43

                @felsi Whether it's safe would mainly depend on what you do inside updateTrack() (do you check for null, etc.)
                I can see no fault in calling updateTrack() in both change handlers.

                1 Reply Last reply
                0
                • A Asperamanca

                  @GrecKo said in Delegate creation and multithreaded property updating:

                  & 2.
                  I don't really understand the question. Models&Views does that.
                  A ListView can do that with the added benefit of delegate pooling.
                  If the ListView does not fit your use case (you might not need it to position the delegates). A Repeater (or Instantiator) plus a proxy model might do the trick.

                  Please walk me through it, if you would be so kind.
                  When I want to display a table of items, I use a TableView. The model can be huge, but only the delegates actually needed for display are created (and a few more to ensure smooth scrolling, maybe). So far, so good.

                  In my case, I do not want to arrange my components in a pre-defined way. The user has positioned them in a certain position, and maybe there is some (also user-specified) runtime logic that could move them around. Also, whether they are actually visible right now might depend on runtime logic.

                  A ListView is a Flickable. A flickable does not make sense for my use case (the components are geometrically unrelated to each other).

                  A Repeater (AFAIK) only instantiates the same type of item. I could use it, but I would need a wrapper item.
                  Then again "The Repeater type creates all of its delegate items when the repeater is first created.". That means, if I have 100k data classes, I will get 100k QML items of some kind. Now, I'd need to measure how expensive that would be, and it's certainly not free.

                  A Loader can only load a single component. I could probably pair it with Repeater to get what I want. I think it would pretty much do the same thing as my current code, at the cost of always loading the 100k Loaders. Also, how do you use a loader to load a component from a QML module directly? Eventually, I want my components to be pre-compiled.

                  Did I miss an option?

                  I admit, part of the reason I chose this approach is also that I'm not much of a ModelView guy, so I didn't consider using ModelView for anything else but where a ready-to-use View component already exists.

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

                  @Asperamanca said in Delegate creation and multithreaded property updating:

                  In my case, I do not want to arrange my components in a pre-defined way. The user has positioned them in a certain position, and maybe there is some (also user-specified) runtime logic that could move them around. Also, whether they are actually visible right now might depend on runtime logic.

                  A Repeater (AFAIK) only instantiates the same type of item. I could use it, but I would need a wrapper item.
                  Then again "The Repeater type creates all of its delegate items when the repeater is first created.". That means, if I have 100k data classes, I will get 100k QML items of some kind. Now, I'd need to measure how expensive that would be, and it's certainly not free.

                  A Repeater with a DelegateChooser can handle this case. As for not creating the 100k data classes you could only expose the one you want to be instantiated in a model of your own or expose everything and have a proxy model do the work of filtering out the elements you don't want.

                  A 1 Reply Last reply
                  0
                  • GrecKoG GrecKo

                    @Asperamanca said in Delegate creation and multithreaded property updating:

                    In my case, I do not want to arrange my components in a pre-defined way. The user has positioned them in a certain position, and maybe there is some (also user-specified) runtime logic that could move them around. Also, whether they are actually visible right now might depend on runtime logic.

                    A Repeater (AFAIK) only instantiates the same type of item. I could use it, but I would need a wrapper item.
                    Then again "The Repeater type creates all of its delegate items when the repeater is first created.". That means, if I have 100k data classes, I will get 100k QML items of some kind. Now, I'd need to measure how expensive that would be, and it's certainly not free.

                    A Repeater with a DelegateChooser can handle this case. As for not creating the 100k data classes you could only expose the one you want to be instantiated in a model of your own or expose everything and have a proxy model do the work of filtering out the elements you don't want.

                    A Offline
                    A Offline
                    Asperamanca
                    wrote on last edited by
                    #45

                    @GrecKo I was not aware of DelegateChooser. Thanks!

                    F 1 Reply Last reply
                    0
                    • A Asperamanca

                      @GrecKo I was not aware of DelegateChooser. Thanks!

                      F Offline
                      F Offline
                      felsi
                      wrote on last edited by felsi
                      #46

                      Good to know, thanks!
                      It would require the usage of on<Signal> handlers for all properties of PlaylistTrack.
                      So i took this trivial solution:

                      Button {
                      
                          Component.onCompleted:{
                              completed = true;
                              updateTrack();
                          }
                      
                          property bool completed: false
                          property PlaylistTrack playlistTrack
                          onPlaylistTrackChanged: {if(completed)updateTrack();}
                      
                          Connections {
                              target: playlistTrack
                              function onTrackChanged(){updateTrack();}
                          }
                      
                          function updateTrack()
                          {
                              ...    
                          }
                      }
                      

                      Just in case someone comes along.
                      And do not use only a connection to trigger something (e.g. show something), always tie it to a property!
                      So long, thank you, guys, i wish you the best!

                      1 Reply Last reply
                      0
                      • A Offline
                        A Offline
                        Asperamanca
                        wrote on last edited by
                        #47

                        I prefer less logic in when I call a function, and more logic where the function simply exits when some parameters are still missing, but that's a...philosophical difference.

                        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