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. Factoring out complex delegate - has slowed down

Factoring out complex delegate - has slowed down

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
2 Posts 1 Posters 238 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.
  • B Offline
    B Offline
    Bob64
    wrote on last edited by Bob64
    #1

    I am looking for advice on how to go about factoring out some fairly complex delegate code that is currently embedded directly in a ListView-based component. I would like to be able to reuse the delegate in a slightly different ListView or, potentially, a different view altogether. I have done a preliminary refactor that sort of works but has slowed it down considerably for reasons I don't fully understand. I would like advice on whether I have gone about the refactor in the right way and also whether there is anything obvious that could be causing the slowdown.

    My ListView component contains a table of settable values of different types. It is driven by a model that provides information such as the name of each item, its data type, its current value, etc. Because the entries are of different types, they need different controls to display and edit the values. The data type determines the type of control to use for each entry. This is based on using a Loader in the delegate, with a switch on the data type to select the correct component. (I am currently constrained to use Qt 5.9.6 - not for much longer I hope - so do not have access to DelegateChooser.)

    This is one of the first things I wrote when I started with QML and it has grown somewhat "organically" in the meantime. All of the code is currently inside the ListView-based component. I now want to be able to reuse all of the delegate code so I essentially want to factor the delegate code out of the ListView component.

    This is the structure at the moment:

    ListView {
        ...
        model: myModel
        ...
        
        // Various helper functions used in the delegate
        function deriveType(...) { ... }
        function anotherHelper(...) { ... }
        ...
        
        // This is used by some of the property type Components below
        MyFileDialog { id: dlg }
        
        Component {
            id: intDelegate
            IntControl { ... } // text field geared to integer input
        }
        
        Component {
            id: optionDelegate
            OptionControl { ... } // essentially combo with string options
        }
        
        Component {
            id: fileDelegate
            FileControl { // selects a file path
                dialog: dlg // uses the common dialog above
                ...
            }
        }
        
        ...
        
        delegate: RowLayout {
           Label {
              text: label // 'label' from model
           }
           Loader {
              // Loaded components don't get automatic access to the model
              // fields so need to copy into new variables here that will
              // be in the scope of the loaded components.
              // Various massaging of the data goes on here too using the
              // helper functions.
           
              property loader_type: deriveType( /* access model fields */ ...)
              property ...
              ...
              
              switch (loader_type) {
              case "int":
                  return intDelegate;
              case ...
              }
           }
        }
    

    Virtually everything I have shown above, the Components, the helper functions, the delegate,
    are all related to the delegate implementation so I want to factor this out into a separate component.

    This is what I have done (in ValueDelegate.qml say):

    Item {
        
        // Various helper functions used in the delegate
        function deriveType(...) { ... }
        function anotherHelper(...) { ... }
        ...
        
        // This is used by some of the property type Components below
        MyFileDialog { id: dlg }
        
        Component {
            id: intDelegate
            IntControl { ... } // text field geared to integer input
        }
        ...     
        ... // as above
        
        RowLayout { // exactly as the delegate in the original code above
           Label { ... }
           Loader { ... }
        }
    

    whereas the ListView is now very small and looks more like this:

    ListView {
        model: myModel
        ...
        
        delegate: ValueDelegate {
        }
    }
    

    I have only done a rudimentary factoring out so far. I should probably define properties at the Item level to receive the model fields. At the moment these are visible to the RowLayout in ValueDelegate because the context in which the ValueDelegate is created in the ListView.

    Anyway, this works after a fashion but it is extremely slow. This table is filled based on selections elsewhere in the GUI. When a new selection is made, the model changes, the table is effectively cleared and refreshed. Before this refactor, this was very snappy, but now I am waiting a couple of seconds.

    One thing I am wondering is whether it is because the delegate is now a lot "heavier" than it was before. The delegate is now this Item that has all the helper functions, the Components for the value types as well as the original RowLayout. Whereas in the original code a lot of that existed at the ListView level, it is now all in the delegate itself. I don't have a feel for what sort of overhead this introduces.

    If it is likely that the delegate is too heavyweight now, is there a better approach I could take to refactoring this?

    B 1 Reply Last reply
    0
    • B Bob64

      I am looking for advice on how to go about factoring out some fairly complex delegate code that is currently embedded directly in a ListView-based component. I would like to be able to reuse the delegate in a slightly different ListView or, potentially, a different view altogether. I have done a preliminary refactor that sort of works but has slowed it down considerably for reasons I don't fully understand. I would like advice on whether I have gone about the refactor in the right way and also whether there is anything obvious that could be causing the slowdown.

      My ListView component contains a table of settable values of different types. It is driven by a model that provides information such as the name of each item, its data type, its current value, etc. Because the entries are of different types, they need different controls to display and edit the values. The data type determines the type of control to use for each entry. This is based on using a Loader in the delegate, with a switch on the data type to select the correct component. (I am currently constrained to use Qt 5.9.6 - not for much longer I hope - so do not have access to DelegateChooser.)

      This is one of the first things I wrote when I started with QML and it has grown somewhat "organically" in the meantime. All of the code is currently inside the ListView-based component. I now want to be able to reuse all of the delegate code so I essentially want to factor the delegate code out of the ListView component.

      This is the structure at the moment:

      ListView {
          ...
          model: myModel
          ...
          
          // Various helper functions used in the delegate
          function deriveType(...) { ... }
          function anotherHelper(...) { ... }
          ...
          
          // This is used by some of the property type Components below
          MyFileDialog { id: dlg }
          
          Component {
              id: intDelegate
              IntControl { ... } // text field geared to integer input
          }
          
          Component {
              id: optionDelegate
              OptionControl { ... } // essentially combo with string options
          }
          
          Component {
              id: fileDelegate
              FileControl { // selects a file path
                  dialog: dlg // uses the common dialog above
                  ...
              }
          }
          
          ...
          
          delegate: RowLayout {
             Label {
                text: label // 'label' from model
             }
             Loader {
                // Loaded components don't get automatic access to the model
                // fields so need to copy into new variables here that will
                // be in the scope of the loaded components.
                // Various massaging of the data goes on here too using the
                // helper functions.
             
                property loader_type: deriveType( /* access model fields */ ...)
                property ...
                ...
                
                switch (loader_type) {
                case "int":
                    return intDelegate;
                case ...
                }
             }
          }
      

      Virtually everything I have shown above, the Components, the helper functions, the delegate,
      are all related to the delegate implementation so I want to factor this out into a separate component.

      This is what I have done (in ValueDelegate.qml say):

      Item {
          
          // Various helper functions used in the delegate
          function deriveType(...) { ... }
          function anotherHelper(...) { ... }
          ...
          
          // This is used by some of the property type Components below
          MyFileDialog { id: dlg }
          
          Component {
              id: intDelegate
              IntControl { ... } // text field geared to integer input
          }
          ...     
          ... // as above
          
          RowLayout { // exactly as the delegate in the original code above
             Label { ... }
             Loader { ... }
          }
      

      whereas the ListView is now very small and looks more like this:

      ListView {
          model: myModel
          ...
          
          delegate: ValueDelegate {
          }
      }
      

      I have only done a rudimentary factoring out so far. I should probably define properties at the Item level to receive the model fields. At the moment these are visible to the RowLayout in ValueDelegate because the context in which the ValueDelegate is created in the ListView.

      Anyway, this works after a fashion but it is extremely slow. This table is filled based on selections elsewhere in the GUI. When a new selection is made, the model changes, the table is effectively cleared and refreshed. Before this refactor, this was very snappy, but now I am waiting a couple of seconds.

      One thing I am wondering is whether it is because the delegate is now a lot "heavier" than it was before. The delegate is now this Item that has all the helper functions, the Components for the value types as well as the original RowLayout. Whereas in the original code a lot of that existed at the ListView level, it is now all in the delegate itself. I don't have a feel for what sort of overhead this introduces.

      If it is likely that the delegate is too heavyweight now, is there a better approach I could take to refactoring this?

      B Offline
      B Offline
      Bob64
      wrote on last edited by
      #2

      Seconds after writing all that and posting it, it occurred to me to try one thing, which was to remove the file dialog from the delegate and to pass that in instead. That appears to have solved the speed issue.

      Still interested to hear whether the approach seems OK otherwise - is it OK to have all those embedded Components in the delegate and is there any alternative?

      So I now have something more like this:

      ValueDelegate.qml:

      Item {
           property dlg
           // **remove embedded MyFileDialog from delegate**
           // everything else the same...
      

      The ListView:

      ListView {
          model: myModel
          ...
          MyFileDialog { id: myDlg }
      
          delegate: ValueDelegate {
               dlg: myDlg
          }
      }
      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