Eager load views
-
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?
-
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
-
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?
-
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?
@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?
-
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. -
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.@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 theonLoadViews
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.
-
@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 theonLoadViews
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.
@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 theonLoadViews
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=21and this is the whole playlist.
https://www.youtube.com/playlist?list=PL6CJYn40gN6hdNC1IGQZfVI707dh9DPRcYour 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" } }
-
@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 theonLoadViews
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=21and this is the whole playlist.
https://www.youtube.com/playlist?list=PL6CJYn40gN6hdNC1IGQZfVI707dh9DPRcYour 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" } }
@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 aRepeater
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)
-
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
-
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.
-
-
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.
@ankou29666 The Loader inside was if the asynchronous loading was needed.
-
oh yep, didn't notice that detail