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. Eager load views
Forum Updated to NodeBB v4.3 + New Features

Eager load views

Scheduled Pinned Locked Moved Solved QML and Qt Quick
11 Posts 3 Posters 676 Views 1 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.
  • R Online
    R Online
    Redman
    wrote on last edited by
    #1

    I need to preload various views, which are reachable through an application wide button bar at the botttom.

    Right now I have a Loader that loads everything

        property var allViewPaths: []
        property int viewIdxLoader: 0
    
        CoreViewDefinition {
            id: coreViews
    
            onProcessFinished: {
                root.loadViews(root.viewIdxLoader)
            }
        }
    
        onLoadViews: function (idx) {
            viewLoader.setSource(allViewPaths[idx].viewUrl)
        }
    
    // Responsible for eager loading all views on application startup
        Loader {
            id: viewLoader
    
            anchors.fill: parent
    
            asynchronous: true
    
            onLoaded: {
                // Signal that view is loaded
                root.viewLoaded(item)
    
                // Increase the idx, so the next view can be loaded
                root.viewIdxLoader++
    
                if (root.viewIdxLoader < root.allViewPaths.length)
                    root.loadViews(viewIdxLoader)
            }
    

    This creates all the views that need to be created.

    But how do I go about keeping those created views stored and accesible when one of the buttons is pressed?

    1 Reply Last reply
    0
    • GrecKoG Offline
      GrecKoG Offline
      GrecKo
      Qt Champions 2018
      wrote on last edited by
      #8

      I'm not sure I'm getting the full picture here but I would use a StackLayout or a StackView here.

      StackLayout {
          id: stackLayout
          Repeater {
              // ...
              Loader {
                  required property var modelData
                  required property var index
                  source: modelData.viewUrl
                  asynchronous: true
              }
          }
      }
      

      and in your buttons do: onClicked: stackLayout.currentIndex: index

      1 Reply Last reply
      1
      • GrecKoG Offline
        GrecKoG Offline
        GrecKo
        Qt Champions 2018
        wrote on last edited by
        #2

        Use multiple Loaders.

        Do you need the views to be loaded one after the other or would you be fine with loading them all in parallel?

        R 1 Reply Last reply
        0
        • GrecKoG GrecKo

          Use multiple Loaders.

          Do you need the views to be loaded one after the other or would you be fine with loading them all in parallel?

          R Online
          R Online
          Redman
          wrote on last edited by Redman
          #3

          @GrecKo The order in which the views have to be loaded doesnt matter.
          I intend to implement a SplashScreen on application start, which waits for various checks to be passed before showing the GUI.
          Loading those views is on of these checks.

          Currently my GUI is built uop hierarchically as followed:
          application.qml (overall settings)
          --|- AppView.qml (entry point for custom client gui)
          -----|-ViewSelector (here lives the code, that is shown in my first post)

          The displaying of the views is happening in AppView. The loading of the views is happening in ViewSelector.

          How do I transfer the loaded views one level up to AppView?

          1 Reply Last reply
          0
          • A Offline
            A Offline
            ankou29666
            wrote on last edited by ankou29666
            #4

            Transfering one level up a proprety is done through property alias at the root level of the component.

            I truely do not understand the need of loading multiple elements in sequence. No matter you load them one after each other, or all in parallel, the loading speed is going to be the same.
            Just load all you need when you needed, and don't bother with attempting to load them in sequence, you increase your workload for no gain.

            I'm not even sure that your use of Loader actually works, you don't even use neither source nor sourceComponent in your Loader. This is definitely not the usual way of using a Loader element.

            If you really want your components to be loaded in sequence, something like

            Rectangle
            {
                Loader
                {
                    id: comp1
                    source: "path/to/comp1"
                }
            
                Loader
                {
                    id: comp2
                    active: comp1.progress===1  // Loads comp2 only once the loading of comp1 has progressed to 100%
                    source: "path/to/comp2"
                }
            }
            

            will probably make it much easier to achieve.

            Edit: setting the active property to comp1.status === Loader.Ready might be more appropriate than using it's progress.

            R 1 Reply Last reply
            0
            • A ankou29666

              Transfering one level up a proprety is done through property alias at the root level of the component.

              I truely do not understand the need of loading multiple elements in sequence. No matter you load them one after each other, or all in parallel, the loading speed is going to be the same.
              Just load all you need when you needed, and don't bother with attempting to load them in sequence, you increase your workload for no gain.

              I'm not even sure that your use of Loader actually works, you don't even use neither source nor sourceComponent in your Loader. This is definitely not the usual way of using a Loader element.

              If you really want your components to be loaded in sequence, something like

              Rectangle
              {
                  Loader
                  {
                      id: comp1
                      source: "path/to/comp1"
                  }
              
                  Loader
                  {
                      id: comp2
                      active: comp1.progress===1  // Loads comp2 only once the loading of comp1 has progressed to 100%
                      source: "path/to/comp2"
                  }
              }
              

              will probably make it much easier to achieve.

              Edit: setting the active property to comp1.status === Loader.Ready might be more appropriate than using it's progress.

              R Online
              R Online
              Redman
              wrote on last edited by
              #5

              @ankou29666 said in Eager load views:

              Transfering one level up a proprety is done through property alias at the root level of the component.

              Thanks. And how do I achieve that in this case?

              // The Frame contains the preloaded selected view from ViewSelector
                  Frame {
                      anchors.centerIn: parent
                      height: parent.height * 0.90
                      width: parent.width * 0.98
              
              
                      // How do I get the view from onViewLoaded() here to be displayed
              
                  }
              
                  // The button bar at the bottom of the application
                  // Lets us navigate through the different views
                  //  Loads the views on application startup
                  ViewSelector {
                      onViewLoaded: function (item) {
                            // Signal is emitted when the Loader in ViewSelector has successfully loaded a view, as seen in my original post
                           // What to do here?
                  }
              
              

              I truely do not understand the need of loading multiple elements in sequence. No matter you load them one after each other, or all in parallel, the loading speed is going to be the same.
              Just load all you need when you needed, and don't bother with attempting to load them in sequence, you increase your workload for no gain.

              If I load the views when needed, the application takes some time because there is database queries running to populate the views

              I'm not even sure that your use of Loader actually works, you don't even use neither source nor sourceComponent in your Loader. This is definitely not the usual way of using a Loader element.

              I do setSource() in the onLoadViews slot.

              If you really want your components to be loaded in sequence, something like

              I do not care in what order they are loaded. They just have to be ready when I access them.

              A 1 Reply Last reply
              0
              • R Redman

                @ankou29666 said in Eager load views:

                Transfering one level up a proprety is done through property alias at the root level of the component.

                Thanks. And how do I achieve that in this case?

                // The Frame contains the preloaded selected view from ViewSelector
                    Frame {
                        anchors.centerIn: parent
                        height: parent.height * 0.90
                        width: parent.width * 0.98
                
                
                        // How do I get the view from onViewLoaded() here to be displayed
                
                    }
                
                    // The button bar at the bottom of the application
                    // Lets us navigate through the different views
                    //  Loads the views on application startup
                    ViewSelector {
                        onViewLoaded: function (item) {
                              // Signal is emitted when the Loader in ViewSelector has successfully loaded a view, as seen in my original post
                             // What to do here?
                    }
                
                

                I truely do not understand the need of loading multiple elements in sequence. No matter you load them one after each other, or all in parallel, the loading speed is going to be the same.
                Just load all you need when you needed, and don't bother with attempting to load them in sequence, you increase your workload for no gain.

                If I load the views when needed, the application takes some time because there is database queries running to populate the views

                I'm not even sure that your use of Loader actually works, you don't even use neither source nor sourceComponent in your Loader. This is definitely not the usual way of using a Loader element.

                I do setSource() in the onLoadViews slot.

                If you really want your components to be loaded in sequence, something like

                I do not care in what order they are loaded. They just have to be ready when I access them.

                A Offline
                A Offline
                ankou29666
                wrote on last edited by
                #6

                @Redman said in Eager load views:

                Thanks. And how do I achieve that in this case?

                You declare your component to be loaded like

                Item
                {
                    property alias someNewProperty: internalComponant.itsProperty
                }
                

                Watch this video about creating components in QML, there is the explanation about aliases.
                https://www.youtube.com/watch?v=qzSNju-h1pk&list=PL6CJYn40gN6hdNC1IGQZfVI707dh9DPRc&index=17

                @Redman said in Eager load views:

                If I load the views when needed, the application takes some time because there is database queries running to populate the views

                It's gonna take time sooner or later. If you don't want your app to be slowed down while in use, then it's gonna slow down your startup.

                @Redman said in Eager load views:

                I do setSource() in the onLoadViews slot.

                Has this ever worked ? This is not the way you should use the Loader element. It seems to me that you are using QML the imperative way (as if you were writing C or C++) instead of declarative.

                A little video about the Loader element
                https://www.youtube.com/watch?v=nteJeojg07k&list=PL6CJYn40gN6hdNC1IGQZfVI707dh9DPRc&index=21

                and this is the whole playlist.
                https://www.youtube.com/playlist?list=PL6CJYn40gN6hdNC1IGQZfVI707dh9DPRc

                Your CoreViewDefinition element provides an onProcessFinished signal, and thus probably also provides a processFinished property.

                All you have to do is to load your components like

                Item
                {
                    id: rootItem
                    CoreViewDefinition
                    {
                        id: coreViews
                    }
                
                    Loader
                    {
                        id: busy
                        active: !coreViews.processFinished // BusyIndicator active until the process is finished
                        sourceComponent: BusyIndicator { ... }
                    }
                
                
                    Loader
                    {
                        id: comp1
                        active: coreViews.processFinished
                        source: "path/to/comp1.qml"
                    }    
                    Loader
                    {
                        id: comp2
                        active: coreViews.processFinished
                        // Or if you can bind the active property to comp1.status if you want to load your all your elements into a daisy chain
                        source: "path/to/comp2.qml"
                    }
                }
                
                R 1 Reply Last reply
                0
                • A ankou29666

                  @Redman said in Eager load views:

                  Thanks. And how do I achieve that in this case?

                  You declare your component to be loaded like

                  Item
                  {
                      property alias someNewProperty: internalComponant.itsProperty
                  }
                  

                  Watch this video about creating components in QML, there is the explanation about aliases.
                  https://www.youtube.com/watch?v=qzSNju-h1pk&list=PL6CJYn40gN6hdNC1IGQZfVI707dh9DPRc&index=17

                  @Redman said in Eager load views:

                  If I load the views when needed, the application takes some time because there is database queries running to populate the views

                  It's gonna take time sooner or later. If you don't want your app to be slowed down while in use, then it's gonna slow down your startup.

                  @Redman said in Eager load views:

                  I do setSource() in the onLoadViews slot.

                  Has this ever worked ? This is not the way you should use the Loader element. It seems to me that you are using QML the imperative way (as if you were writing C or C++) instead of declarative.

                  A little video about the Loader element
                  https://www.youtube.com/watch?v=nteJeojg07k&list=PL6CJYn40gN6hdNC1IGQZfVI707dh9DPRc&index=21

                  and this is the whole playlist.
                  https://www.youtube.com/playlist?list=PL6CJYn40gN6hdNC1IGQZfVI707dh9DPRc

                  Your CoreViewDefinition element provides an onProcessFinished signal, and thus probably also provides a processFinished property.

                  All you have to do is to load your components like

                  Item
                  {
                      id: rootItem
                      CoreViewDefinition
                      {
                          id: coreViews
                      }
                  
                      Loader
                      {
                          id: busy
                          active: !coreViews.processFinished // BusyIndicator active until the process is finished
                          sourceComponent: BusyIndicator { ... }
                      }
                  
                  
                      Loader
                      {
                          id: comp1
                          active: coreViews.processFinished
                          source: "path/to/comp1.qml"
                      }    
                      Loader
                      {
                          id: comp2
                          active: coreViews.processFinished
                          // Or if you can bind the active property to comp1.status if you want to load your all your elements into a daisy chain
                          source: "path/to/comp2.qml"
                      }
                  }
                  
                  R Online
                  R Online
                  Redman
                  wrote on last edited by Redman
                  #7

                  @ankou29666 said in Eager load views:

                  You declare your component to be loaded like

                  Item
                  {
                      property alias someNewProperty: internalComponant.itsProperty
                  }
                  

                  With your post I was able to think of a different approach. Dynamically create the needed number of Loaders in a Repeater and load the view.

                  ViewSelector.qml

                      CoreViewDefinition {
                          id: coreViews
                          processViewPaths: false
                  
                          onProcessFinished: function (view) {
                              root.processViews(view)
                  
                              // allViewPaths is populated, render the views
                              loaderRepeater.model = root.allViewPaths
                          }
                      }
                  
                      Repeater {
                          id: loaderRepeater
                  
                          delegate: Loader {
                              required property var modelData
                              required property var index
                  
                              source: modelData.viewUrl
                              asynchronous: true
                              visible: false
                          }
                      }
                  

                  This seems to work. I am sure this is, again, too imperative. But I am a bit in a hurry here.

                  Now, when I select via the button bar a view that should be displayed I do following:

                  ViewSelector.qml

                  
                      property alias view: viewAlias.view
                  
                          Item {
                      
                              id: viewAlias
                              property var view
                          }
                  
                  Repeater {
                                  id: buttonRepeater
                  
                                  model: root.allViewPaths ? root.allViewPaths : undefined
                                  delegate: Item {
                                      id: item
                                      required property var modelData
                                      required property var index
                                      height: btn.height
                                      width: btn.width
                  
                                      RowLayout {
                                          anchors.fill: parent
                                          spacing: 5
                  
                                          Button {
                                              id: btn
                  
                                              enabled: true
                  
                                              backgroundColor: "white"
                                              icon: modelData.icon
                                              iconSize: 30
                                              view: modelData.viewUrl
                  
                                              onClicked: {
                                                  viewAlias.view = loaderRepeater.itemAt(index).item
                                                  root.viewSelected()
                                              }
                                          }
                                      }
                                  }
                              }
                  

                  AppView.qml

                      ViewSelector {
                          id: viewSelector
                          y: root.height - 50
                          x: root.width / 2 - viewSelector.buttonBarWidth / 2
                  
                          onViewSelected: function () {
                                          selectedView.view = viewSelector.view
                                          selectedView.view.visible = true        
                          }
                      }
                  
                      Frame {
                          anchors.centerIn: parent
                          height: parent.height * 0.90
                          width: parent.width * 0.98
                          Item {
                              id: selectedView
                  
                              property var view
                          }
                      }
                  

                  I hope its clear, that I want to propagate via an alias the selected loaded view from the child ViewSelector to the parent AppView, and insert it into the Frame to display it.

                  Unfortunately, this does not work. I am missing important information/knowledge on how to pass around a loaded view (if that is even possible)

                  1 Reply Last reply
                  0
                  • GrecKoG Offline
                    GrecKoG Offline
                    GrecKo
                    Qt Champions 2018
                    wrote on last edited by
                    #8

                    I'm not sure I'm getting the full picture here but I would use a StackLayout or a StackView here.

                    StackLayout {
                        id: stackLayout
                        Repeater {
                            // ...
                            Loader {
                                required property var modelData
                                required property var index
                                source: modelData.viewUrl
                                asynchronous: true
                            }
                        }
                    }
                    

                    and in your buttons do: onClicked: stackLayout.currentIndex: index

                    1 Reply Last reply
                    1
                    • A Offline
                      A Offline
                      ankou29666
                      wrote on last edited by
                      #9

                      The Loader inside the repeater doesn't seem to me necessary, because they basically do the same job, they instanciate components. (with the difference that the Repeater instanciates multiple componants when the Loader instanciates only one)

                      Your solution actually works, but you could also use a Repeater with a DelegateChooser, this is rather that way that the Repeater is intended to be used.

                      GrecKoG 1 Reply Last reply
                      0
                      • R Redman has marked this topic as solved on
                      • A ankou29666

                        The Loader inside the repeater doesn't seem to me necessary, because they basically do the same job, they instanciate components. (with the difference that the Repeater instanciates multiple componants when the Loader instanciates only one)

                        Your solution actually works, but you could also use a Repeater with a DelegateChooser, this is rather that way that the Repeater is intended to be used.

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

                        @ankou29666 The Loader inside was if the asynchronous loading was needed.

                        1 Reply Last reply
                        1
                        • A Offline
                          A Offline
                          ankou29666
                          wrote on last edited by
                          #11

                          oh yep, didn't notice that detail

                          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