QML Application Structuring Approaches: Difference between revisions

From Qt Wiki
Jump to navigation Jump to search
No edit summary
 
No edit summary
Line 1: Line 1:
'''English'''<br />
[[Category:Developing_with_Qt::Qt Quick]] [[Category:HowTo]]<br />'''English'''<br />[toc align_right=&quot;yes&amp;quot; depth=&quot;3&amp;quot;]


=<span class="caps">QML</span> Application Structuring Approaches=
= QML Application Structuring Approaches =


==Introduction==
== Introduction ==


A common approach to deal with a complex application is to decompose it in several smaller units. This leads to better structuring of application and more productive development process as a whole. <span class="caps">QML</span> offers two constructs that service this approach – ''Component'' and ''Loader'' elements. Roughly speaking, the both elements bring similar functionality. But the ways they do this are different. We may refer to the ''Component'' approach as static one and the ''Loader'' approach as dynamic one. We first re-call ''Component'' and ''Loader'' definitions, than compare the two approaches and in the main part of this article describe several ''Loader'' element use cases. There are two other approaches to structure a <span class="caps">QML</span> application also – dynamic creation of objects and module importing. They are beyond the scope of this article.
A common approach to deal with a complex application is to decompose it in several smaller units. This leads to better structuring of application and more productive development process as a whole. QML offers two constructs that service this approach – ''Component'' and ''Loader'' elements. Roughly speaking, the both elements bring similar functionality. But the ways they do this are different. We may refer to the ''Component'' approach as static one and the ''Loader'' approach as dynamic one. We first re-call ''Component'' and ''Loader'' definitions, than compare the two approaches and in the main part of this article describe several ''Loader'' element use cases. There are two other approaches to structure a QML application also – dynamic creation of objects and module importing. They are beyond the scope of this article.


==Component Element Definition==
== Component Element Definition ==


A <span class="caps">QML</span> component is a separate, autonomous unit that contains other <span class="caps">QML</span> elements. It could be defined in two modes – as an inline unit in a <span class="caps">QML</span> file or as a separate <span class="caps">QML</span> file. The main point with <span class="caps">QML</span> component is that it could be reused many times trough its very convenient interface. For more details about ''Component'' syntax and interface consult the [http://doc.qt.nokia.com/4.7/qml-component.html documentation.] ''[doc.qt.nokia.com]''
A QML component is a separate, autonomous unit that contains other QML elements. It could be defined in two modes – as an inline unit in a QML file or as a separate QML file. The main point with QML component is that it could be reused many times trough its very convenient interface. For more details about ''Component'' syntax and interface consult the &quot;documentation.&quot;:http://doc.qt.nokia.com/4.7/qml-component.html


How does the ''Component'' element support application code structuring and particularly its decomposition? In case of component file it is obvious – the application is split in several components. They are included in the main file and instantiated via their interfaces, for example like this:
How does the ''Component'' element support application code structuring and particularly its decomposition? In case of component file it is obvious – the application is split in several components. They are included in the main file and instantiated via their interfaces, for example like this:


<span class="caps">QML</span> File
QML File


Including of the component means that we get an instance of the component viewed as a template. The basic component is used to generate instances only and they do not depend anymore on it. We may have several instances and they behavior independently, treated like basic <span class="caps">QML</span> elements. Note that ''Component'' interface allows to re-define component properties – predefined ones as well as defining new ones. This is the case of the ''color'' property in the snippet above. We could even just instantiate the component and not alter any property.
<code><br />…<br />Banner { //The component file Banner.qml, located in the directory of the<br /> //QML File is instantiated here<br />id:ban1<br />color:”red”<br />}<br />…


We may have many instances of ''Banner'' component provided that they have different ''id''.
AnotherComponent { //Here another component is instantiated<br />…<br />}<br />…<br />Banner { //Here we use the same component file Banner.qml. The instantiated component is<br /> //different from ban1 – different id,<br />id:ban2 //property color has different value, etc.<br />color:”lightblue”<br />}<br />…<br /></code>


By now we are considering properties defined at the top level of the component file. What about siblings’/children’ properties in a component file? Let us consider the following case:
Including of the component means that we get an instance of the component viewed as a template. The basic component is used to generate instances only and they do not depend anymore on it. We may have several instances and they behavior independently, treated like basic QML elements. Note that ''Component'' interface allows to re-define component properties - predefined ones as well as defining new ones. This is the case of the ''color'' property in the snippet above. We could even just instantiate the component and not alter any property.


''ExampleOne.qml'' – Component file
<code><br />…<br />Banner {}<br />…<br /></code>


''componentScope.qml'' – It is using component ExampleOne
We may have many instances of ''Banner'' component provided that they have different ''id''.


As you see if we want to re-define the property ''color'' of ''nameText'' child we have to make it visible at the top level. To get this we define an alias of the ''nameText'' at the top level. This alias is re-defined in ''componentScope.qml file''.
By now we are considering properties defined at the top level of the component file. What about siblings’/children’ properties in a component file? Let us consider the following case:


Could we develop more complex structures with components? For instance, could we have nested components? By nested components we mean that a component contains another component. Structures like these are acceptable:
''ExampleOne.qml'' - Component file


''nestedComponents.qml'' – Main file
<code><br />// This component file is used in componentScope.qml file<br />// The point is how to have access to color property out of the scope of Text element


''Comp1.qml'' – First level component file
import QtQuick 1.1<br />Rectangle {<br /> width: 300<br /> height: 200<br /> property alias nameTextTop:nameText // This alias is defined at top level and<br /> // bound to Text element<br /> Text{id:nameText<br /> color:&quot;blue &quot;<br /> text:&quot;Some text fragment&amp;quot;<br /> }<br />}<br /></code>


''Comp2.qml'' – Second level component file
''componentScope.qml'' – It is using component ExampleOne


The inline definition syntax of the component element is very similar to this one of other <span class="caps">QML</span> elements. It starts with a key word ''Component'' followed by element block (represented by ''{ }'' pair). Within the block you may define and nest other <span class="caps">QML</span> elements – refer to Nokia [http://doc.qt.nokia.com/4.7/qml-component.html documentation] ''[doc.qt.nokia.com]''.
<code><br />// The component ExampleOne is instantiated and its color property is change to red<br />import QtQuick 1.1


On the other hand, inline ''Component'' offers other kind of structuring. It encapsulates a piece of <span class="caps">QML</span> code that could be assigned as a property value of other <span class="caps">QML</span> elements. Particularly this mechanism is used in the definition of the ''Loader'' element – see the next section. This kind of structuring is sometimes referred as aggregation. An example how inline ''Component'' could be utilized is demonstrated in section '''Loader and Inline Component'''.
Rectangle {<br /> width: 360<br /> height: 360<br /> ExampleOne{id:instance<br /> nameTextTop.color:&quot;red&amp;quot;<br /> }<br />}<br /></code>


==Loader Element Definition==
As you see if we want to re-define the property ''color'' of ''nameText'' child we have to make it visible at the top level. To get this we define an alias of the ''nameText'' at the top level. This alias is re-defined in ''componentScope.qml file''.


''Loader'' element serves as a handler, which loads dynamically a <span class="caps">QML</span> unit formed as a separate <span class="caps">QML</span> file or inline component. Dynamically means that the <span class="caps">QML</span> unit to be loaded is determined at run-time, for example if a mouse button is clicked. The loaded unit is described as value of ''source'' or ''sourceComponent'' property of the ''Loader''. The ''source'' value is a <span class="caps">URL</span> value – the path to <span class="caps">QML</span> file to be loaded. The ''sourceComponent'' value is ''id'' name of an inline component. Where can we bind values to these properties? The rule is: at places where the ''Loader id'' name is visible. In the following sections you see many examples of implementation of this rule. For more information on Loader see [http://doc.qt.nokia.com/4.7/qml-loader.html#details documentation] ''[doc.qt.nokia.com]''.
Could we develop more complex structures with components? For instance, could we have nested components? By nested components we mean that a component contains another component. Structures like these are acceptable:


One interesting point is how the loaded <span class="caps">QML</span> unit could be accessed. The ''Loader'' element has a default property ''item''. It is read-only and has ''Item'' type. In other words, loaded <span class="caps">QML</span> unit is a <span class="caps">QML</span> ''Item'' element. We recall that it is not visible and serves to group other <span class="caps">QML</span> elements. An illustrating snippet on item property follows:
''nestedComponents.qml'' - Main file


''LoaderItem.qml''
<code><br />// Main file uses a component – Comp1. It in turn applies a nested component Comp2.<br />import QtQuick 1.0<br />Rectangle {<br /> width: 360<br /> height: 360<br /> Comp1{} //This component has a nested component Comp2<br /> Text {<br /> anchors.centerIn: parent<br /> text: &quot;Top Level - we use a component Comp1&amp;quot;<br /> }<br />}<br /></code>


''Page.qml''
''Comp1.qml'' - First level component file


We could set up ''source'' or ''sourceComponent'' properties in the ''Loader'' definition. Now the ''Loader'' element is processed like any other <span class="caps">QML</span> element and its target will be rendered. In this case the behavior is similar to the one of component element. But note that we may change programmatically properties values of ''source'' or ''sourceComponent'' at a later time.
<code><br />import QtQuick 1.0<br />//First level component Comp1.qml<br />Rectangle {<br /> width: 100<br /> height: 62<br /> color:&quot;red&amp;quot;<br /> Comp2{} // This component becomes part of Co&amp;quot;mp1<br />}&quot;:http://doc.qt.nokia.com/4.7/qml-component.html</code>


==Component Element vs. Loader Element==
''Comp2.qml'' - Second level component file


Now we are going to compare component file and ''Loader'' element. They both offer similar functionality, but differ in ways they are applied.
<code><br />import QtQuick 1.0<br />//This is a component we nest in another component Comp1<br />Rectangle {<br /> x:50;y:40<br /> width: 100<br /> height: 62<br /> color:&quot;green&amp;quot;<br />}<br /></code>


In case of a component file we have the following process. We declare the component (rather its interface) in a fixed way in the main file for example. <span class="caps">QML</span> parser immediately checks if a component file with the name as in component declaration exists. The parser searches in the directory where is placed the file.
The inline definition syntax of the component element is very similar to this one of other QML elements. It starts with a key word ''Component'' followed by element block (represented by ''{ }'' pair). Within the block you may define and nest other QML elements – refer to Nokia &quot;documentation&amp;quot;:http://doc.qt.nokia.com/4.7/qml-component.html.


The ''Loader'' acts a bit differently. When a <span class="caps">QML</span> file containing a ''Loader'' element is parsed, the ''Loader'' constructs itself like any other <span class="caps">QML</span> element. The ''Loader'' could define the source destination of the file to be loaded or not. In the first case the file is loaded. In the second case this is postponed to the moment we define the source destination of the file in a script element for example. Note that in case of ''source'' property we may point to a file located anywhere on our local computer or on the Internet. If we have many <span class="caps">QML</span> entities that are not needed permanently, there is no need to keep them in memory – rather load them as they are necessary. This leads to a better structuring of the application and more efficient computer memory management. Be aware that any consequent activation of a ''Loader'' destroys the previously loaded items. If we set an empty string as ''source'' property value or undefined value to ''sourceComponent'' property, the memory used by the ''Loader'' is released.
On the other hand, inline ''Component'' offers other kind of structuring. It encapsulates a piece of QML code that could be assigned as a property value of other QML elements. Particularly this mechanism is used in the definition of the ''Loader'' element – see the next section. This kind of structuring is sometimes referred as aggregation. An example how inline ''Component'' could be utilized is demonstrated in section '''Loader and Inline Component'''.


Because the file to be loaded by a ''Loader'' could be determined in run-time, the ''Loader'' approach is referred to as dynamic one. The component approach is called static one.
== Loader Element Definition ==


Combining loaders, components and other <span class="caps">QML</span> elements it is important to understand the hierarchy of elements and how they overlap each other. Let us create a demo example, which will illustrate <span class="caps">QML</span> elements rendering hierarchy. We are considering a <span class="caps">QML</span> main file that has two ''Loaders'', one component and other standard <span class="caps">QML</span> elements. We re-define the ''z'' property of elements to control their overlapping.
''Loader'' element serves as a handler, which loads dynamically a QML unit formed as a separate QML file or inline component. Dynamically means that the QML unit to be loaded is determined at run-time, for example if a mouse button is clicked. The loaded unit is described as value of ''source'' or ''sourceComponent'' property of the ''Loader''. The ''source'' value is a URL value – the path to QML file to be loaded. The ''sourceComponent'' value is ''id'' name of an inline component. Where can we bind values to these properties? The rule is: at places where the ''Loader id'' name is visible. In the following sections you see many examples of implementation of this rule. For more information on Loader see &quot;documentation&amp;quot;:http://doc.qt.nokia.com/4.7/qml-loader.html#details.


''elementHierarchy.qml'' – main file
One interesting point is how the loaded QML unit could be accessed. The ''Loader'' element has a default property ''item''. It is read-only and has ''Item'' type. In other words, loaded QML unit is a QML ''Item'' element. We recall that it is not visible and serves to group other QML elements. An illustrating snippet on item property follows:


''firstLoader.qml'' – file loaded by ''newLoad Loader''
''LoaderItem.qml''


''secondComp.qml'' – loaded by ''loadSecond Loader''
<code><br />// QML file Page.qml is loaded by Loader top. Its color property is red<br />// Using Loader item property the color property is changed to blue<br />import QtQuick 1.1<br />Rectangle {<br /> width: 360<br /> height: 360<br /> Loader {id:top }<br /> Rectangle {Component.onCompleted:top.source=&quot;Page.qml&amp;quot;}<br /> Text {<br /> anchors.centerIn: parent<br /> text: &quot;Hello World&amp;quot;<br /> MouseArea {<br /> anchors.fill: parent<br /> onClicked: {top.item.color=&quot;blue&amp;quot;<br /> }<br /> }<br /> }<br />}<br /></code>


''CompNew.qml'' – component file
''Page.qml''


The next picture illustrates how <span class="caps">QML</span> elements are displayed and overlapping. As a rule elements are rendered in the order they are defined in the main file. In the snippets above ''z'' property is changed for text element ''textInner'' (z=3) and for rectangle element ''second'' (z=2) and they are rendered at the top element hierarchy.
<code><br />import QtQuick 1.1<br />Rectangle {<br /> width: 100<br /> height: 62<br /> color:&quot;red&amp;quot;<br />}<br /></code>


[[Image:elementsHier.jpg|Levels of elements]]
We could set up ''source'' or ''sourceComponent'' properties in the ''Loader'' definition. Now the ''Loader'' element is processed like any other QML element and its target will be rendered. In this case the behavior is similar to the one of component element. But note that we may change programmatically properties values of ''source'' or ''sourceComponent'' at a later time.


You may experiment changing ''z'' property.
== Component Element vs. Loader Element ==


==Loader Element Use Cases==
Now we are going to compare component file and ''Loader'' element. They both offer similar functionality, but differ in ways they are applied.


===Simple Wizard Template===
In case of a component file we have the following process. We declare the component (rather its interface) in a fixed way in the main file for example. QML parser immediately checks if a component file with the name as in component declaration exists. The parser searches in the directory where is placed the file.


Our application consists of several pages (3 in the snippet bellow), each of them formatted as a separate <span class="caps">QML</span> file. These files could be located in different directories, but for simplicity we suppose they reside in the directory where is located our main file. Each page has its own ''Loader'' element and when a ''Click me'' button is pressed the ''Loader'' loads a next wizard page.
The ''Loader'' acts a bit differently. When a QML file containing a ''Loader'' element is parsed, the ''Loader'' constructs itself like any other QML element. The ''Loader'' could define the source destination of the file to be loaded or not. In the first case the file is loaded. In the second case this is postponed to the moment we define the source destination of the file in a script element for example. Note that in case of ''source'' property we may point to a file located anywhere on our local computer or on the Internet. If we have many QML entities that are not needed permanently, there is no need to keep them in memory – rather load them as they are necessary. This leads to a better structuring of the application and more efficient computer memory management. Be aware that any consequent activation of a ''Loader'' destroys the previously loaded items. If we set an empty string as ''source'' property value or undefined value to ''sourceComponent'' property, the memory used by the ''Loader'' is released.


''wizardSimple.qml'' – main file
Because the file to be loaded by a ''Loader'' could be determined in run-time, the ''Loader'' approach is referred to as dynamic one. The component approach is called static one.


''Page1.qml'' – first wizard page
Combining loaders, components and other QML elements it is important to understand the hierarchy of elements and how they overlap each other. Let us create a demo example, which will illustrate QML elements rendering hierarchy. We are considering a QML main file that has two ''Loaders'', one component and other standard QML elements. We re-define the ''z'' property of elements to control their overlapping.


''Page2.qml'' – second wizard page
''elementHierarchy.qml'' – main file


''Page3.qml'' – last wizard page
<code><br />import QtQuick 1.1<br />Rectangle {id:first<br /> width: 360; height: 360<br /> color:&quot;lightblue&amp;quot;<br /> Loader {id:newLoad<br /> }<br /> Text {id:textInner<br /> z:3<br /> text: &quot;Sample text with z=7 property &quot;<br /> }<br /> Rectangle {id:second<br /> width:100;height:100<br /> z:2<br /> x:80<br /> color:&quot;pink&amp;quot;<br /> Text {text:&quot;with z=2&amp;quot;}<br /> }<br /> CompNew{id:far<br /> x:20;y:20<br /> }<br /> Loader{id:loadSecond<br /> source:&quot;secondComp.qml&amp;quot;<br /> }<br /> Rectangle {id:dummy<br /> width:80;height:40;x:140;y:80<br /> color:&quot;gray&amp;quot;<br /> Component.onCompleted:{<br /> newLoad.source=&quot;firstLoader.qml&amp;quot;<br /> }<br /> }<br />}<br /></code>


The above wizard implementation is very simple, but suffers of some drawbacks:<br /> • When a new page is loaded the previously loaded page stays unnecessary into memory. <br /> • We use similar loaders for each page their IDs are different only.<br /> • The order of pages loading is fixed one – the source property values are set in advance.<br /> • There is no mechanism for exchange of information between the caller and the called page and vise-verse.
''firstLoader.qml'' file loaded by ''newLoad Loader''


===More Complex Navigation===
<code><br />import QtQuick 1.0<br />Rectangle {<br /> width: 200<br /> height: 100<br /> y:30<br /> color:&quot;red&amp;quot;<br />}<br /></code>


Now we will try to release the restrictions of the loading scheme from the previous section. Firstly, following the Simon Judge [http://www.mobilephonedevelopment.com/qt-qml-tips/ idea] ''[mobilephonedevelopment.com]'', we will apply a common ''Loader'' for all pages. Secondly, we suppose that the next page to be loaded is not known in advance – it is determined after a condition is evaluated. And finally, we will use signals to pass information between pages and demonstrate how multi-window screens could be constructed.
''secondComp.qml'' - loaded by ''loadSecond Loader''


A common approach to trigger some actions after an event is fired is to define an own signal like that (for more information [http://doc.qt.nokia.com/4.7/qmlevents.html here] ''[doc.qt.nokia.com]''):
<code><br />import QtQuick 1.0<br />Rectangle {<br /> width: 100<br /> height: 62<br /> x:40;y:45<br /> color:&quot;green&amp;quot;<br />}<br /></code>


''signal MySignal(string message, int trigger)''
''CompNew.qml'' – component file


where ''signal'' is <span class="caps">QML</span> keyword, ''MySignal'' is the name of newly defined signal, ''message'' is a parameter of type ''string'' and ''trigger'' is another parameter. The parameters could be of different <span class="caps">QML</span> types. For our purposes we want to pass a string that is the name of wizard page to be loaded and an index to identify the application state.
<code><br />import QtQuick 1.1<br />Rectangle {id:comp<br /> width: 62<br /> height: 62<br /> color:&quot;yellow&amp;quot;<br /> Text {text:&quot;CompNew&amp;quot;}<br />}<br /></code>


The main idea is as follows:
The next picture illustrates how QML elements are displayed and overlapping. As a rule elements are rendered in the order they are defined in the main file. In the snippets above ''z'' property is changed for text element ''textInner'' (z=3) and for rectangle element ''second'' (z=2) and they are rendered at the top element hierarchy.


• When we want to load a page we emit our own signal.<br /> • This signal is caught by a ''Loader'' defined somewhere.<br /> • Our signal has a parameter to which we bound the name of the page to be loaded – as an <span class="caps">URL</span> value.
[[Image:http://i1072.photobucket.com/albums/w362/vabo123/elementsHier.jpg|Levels of elements]]


'''Use Case''': We will consider an application that consists of a main page (''Page1'') and two other pages (''Page2'', ''Page3''). The pages should be loaded sequentially and construct a wizard this way. Some of pages could be loaded checking for a logical condition creating the wizard branches.
You may experiment changing ''z'' property.


'''Objectives''': To demonstrate how a flexible wizard could be implemented based on <span class="caps">QML</span> ''Loader'' element. Additionally we may complicate the wizard constructing some multi-windows screens.
== Loader Element Use Cases ==


'''Design''': Pages are loaded one by one. The application starts loading automatically ''Page1''. To load the next page we should click the left mouse button. For ''Page1'' we have two wizard branches depending on a logical value. We introduce a current index for pages (an integer) around which the application logic is build.
=== Simple Wizard Template ===


'''Development''': For each page we define the same own signal:
Our application consists of several pages (3 in the snippet bellow), each of them formatted as a separate QML file. These files could be located in different directories, but for simplicity we suppose they reside in the directory where is located our main file. Each page has its own ''Loader'' element and when a ''Click me'' button is pressed the ''Loader'' loads a next wizard page.


''signal handlerLoader(string name, int index)''
''wizardSimple.qml'' – main file


To activate this signal (emit the signal) we have to invoke it as a method in a script block. A ''MouseArea onClicked'' script block is used. In invocation the name of the page to be loaded is passed as parameter. Emitted signal is attached (automatically) to the signal handler ''onHandlerLoader'', which in turn is connected to a target ''pageLoader.item''. The connection is carried out by a <span class="caps">QML</span> ''Connections'' element in the <span class="caps">QML</span> file ''flexibleLoader.qml''. The target ''pageLoader.item'' is in fact the object loaded by the ''Loader'' (''id:pageLoader'') defined in the file ''flexibleLoader.qml''. Note that the target property of the ''Connections'' element is of type ''Object''. When ''Page2'' is loaded we want a second window (''NewWindow.qml'') to be loaded into the same screen. This is done by a second ''Loader'' (''id:window''), which is defined in the file ''flexibleLoader.qml''. The decision to load this second window is taken in ''onHandlerLoader'' block on the base of returned value of ''trigger'' parameter. Note that the ''Loader'' destroys previously loaded entities when loading new items.
<code><br />import QtQuick 1.1<br />Rectangle {id:top<br /> width: 360<br /> height:360<br /> Loader {id:main} //Declaration of a Loader. It will be activated later.<br /> Text {<br /> text: &quot;Main file&amp;quot;<br /> }<br /> Button{id:buttonMain<br /> //Position the button in top rectangle<br /> anchors.bottom:top.bottom; anchors.right: top.right<br /> MouseArea {<br /> anchors.fill: parent<br /> onClicked: {main.source=&quot;Page1.qml&amp;quot;; //Load a new page<br /> buttonMain.z=–1 //Hide button<br /> }<br /> }<br /> }<br />}<br /></code>


'''Code''':
''Page1.qml'' – first wizard page


''flexibleLoader.qml'' – application main file
<code><br />import QtQuick 1.1<br />Rectangle {id:page1Container<br /> width: 200<br /> height: 300<br /> x:40;y:40<br /> color:&quot;pink&amp;quot;<br /> Loader{id:page1} //Declaration of a new Loader element<br /> Text{<br /> text:&quot;First page loaded&amp;quot;<br /> }<br /> Button{id:buttonPage1<br /> //Position the button in page1Container rectangle<br /> anchors.bottom:page1Container.bottom;<br /> anchors.right: page1Container.right<br /> MouseArea {<br /> anchors.fill: parent<br /> onClicked: {page1.source=&quot;Page2.qml&amp;quot;;<br /> buttonPage1.z=–1 //Hide button<br /> }<br /> }<br /> }<br />}<br /></code>


''Page1.qml'' – page 1
''Page2.qml'' – second wizard page


Files ''Page2.qml'' and ''Page3.qml'' are similar to ''Page1.qml''.
<code><br />import QtQuick 1.1<br />Rectangle {id:page2Container<br /> width: 200<br /> height: 200<br /> x:80;y:80<br /> color:&quot;yellow&amp;quot;<br /> Loader{id:page2}<br /> Text{<br /> text:&quot;Second page loaded&amp;quot;<br /> }<br /> Button{id:buttonPage2<br /> anchors.bottom:page2Container.bottom;<br /> anchors.right: page2Container.right<br /> MouseArea {<br /> anchors.fill: parent<br /> onClicked: {page2.source=&quot;Page3.qml&amp;quot;;buttonPage2.z=<s>1<br /> }<br /> }<br /> }<br />}<br /></code>
<br />''Page3.qml'' – last wizard page
<br /><code><br />import QtQuick 1.1<br />Rectangle {<br /> width: 120<br /> height: 62<br /> x:100;y:100<br /> color:&quot;green&amp;quot;<br /> Text {<br /> text:&quot;Last Loaded Page&amp;quot;<br /> }<br />}<br /></code>
<br />The above wizard implementation is very simple, but suffers of some drawbacks:<br />• When a new page is loaded the previously loaded page stays unnecessary into memory.<br />• We use similar loaders for each page – their IDs are different only.<br />• The order of pages loading is fixed one – the source property values are set in advance.<br />• There is no mechanism for exchange of information between the caller and the called page and vise-verse.
<br />h3. More Complex Navigation
<br />Now we will try to release the restrictions of the loading scheme from the previous section. Firstly, following the Simon Judge &quot;idea&amp;quot;:http://www.mobilephonedevelopment.com/qt-qml-tips/, we will apply a common ''Loader'' for all pages. Secondly, we suppose that the next page to be loaded is not known in advance – it is determined after a condition is evaluated. And finally, we will use signals to pass information between pages and demonstrate how multi-window screens could be constructed.
<br />A common approach to trigger some actions after an event is fired is to define an own signal like that (for more information &quot;here&amp;quot;:http://doc.qt.nokia.com/4.7/qmlevents.html):
<br /> ''signal MySignal(string message, int trigger)''


'''Download Package''': Necessary files: ''flexibleLoader.qml'', ''Page1.qml'', ''Page2.qml'', ''Page3.qml'', ''Help.qml'', ''NewWindow.qml''. Get them from [http://bit.ly/1lnH77m here] ''[bit.ly]''.
<br />where ''signal'' is QML keyword, ''MySignal'' is the name of newly defined signal, ''message'' is a parameter of type ''string'' and ''trigger'' is another parameter. The parameters could be of different QML types. For our purposes we want to pass a string that is the name of wizard page to be loaded and an index to identify the application state.
<br />The main idea is as follows:  
<br />• When we want to load a page we emit our own signal.<br />• This signal is caught by a ''Loader'' defined somewhere.<br />• Our signal has a parameter to which we bound the name of the page to be loaded – as an URL value.
<br />'''Use Case''': We will consider an application that consists of a main page (''Page1'') and two other pages (''Page2'', ''Page3''). The pages should be loaded sequentially and construct a wizard this way. Some of pages could be loaded checking for a logical condition creating the wizard branches.
<br />'''Objectives''': To demonstrate how a flexible wizard could be implemented based on QML ''Loader'' element. Additionally we may complicate the wizard constructing some multi-windows screens.
<br />'''Design''': Pages are loaded one by one. The application starts loading automatically ''Page1''. To load the next page we should click the left mouse button. For ''Page1'' we have two wizard branches depending on a logical value. We introduce a current index for pages (an integer) around which the application logic is build.  
<br />'''Development''': For each page we define the same own signal:
<br /> ''signal handlerLoader(string name, int index)''


===Loader and Component Together===
<br />To activate this signal (emit the signal) we have to invoke it as a method in a script block. A ''MouseArea onClicked'' script block is used. In invocation the name of the page to be loaded is passed as parameter. Emitted signal is attached (automatically) to the signal handler ''onHandlerLoader'', which in turn is connected to a target ''pageLoader.item''. The connection is carried out by a QML ''Connections'' element in the QML file ''flexibleLoader.qml''. The target ''pageLoader.item'' is in fact the object loaded by the ''Loader'' (''id:pageLoader'') defined in the file ''flexibleLoader.qml''. Note that the target property of the ''Connections'' element is of type ''Object''. When ''Page2'' is loaded we want a second window (''NewWindow.qml'') to be loaded into the same screen. This is done by a second ''Loader'' (''id:window''), which is defined in the file ''flexibleLoader.qml''. The decision to load this second window is taken in ''onHandlerLoader'' block on the base of returned value of ''trigger'' parameter. Note that the ''Loader'' destroys previously loaded entities when loading new items.
 
<br />'''Code''':
'''Use Case''': Assume the page we are loading uses an external component (component file) and from this component we want to load a next page. What is new in this use case comparing with the previous one? The call to the <br />''Loader'' is not located at the top level now. It is nested in a component. When the ''Loader'' is defined in the main application the pages are loaded in its context. This requires the ''Loader source'' property to be determined at the top level also. To solve this problem we use cascading definitions of properties. Firstly, have a look at a diagram illustrating the use case:
<br />''flexibleLoader.qml'' – application main file
 
<br /><code><br />import QtQuick 1.1<br />Item {id:top<br /> width:300<br /> height:200<br /> Loader{id:window}<br /> signal handlerLoader(string name, int index)<br /> Loader {<br /> id:pageLoader<br /> source:&quot;Page1.qml&amp;quot;<br /> }<br /> Connections {<br /> target:pageLoader.item<br /> onHandlerLoader:{pageLoader.source=name;<br /> if(index===2)<br /> window.source=&quot;NewWindow.qml&amp;quot;;<br /> }<br /> }<br />}<br /></code>
[[Image:complexLoader.jpg|Loader and Component]]
<br />''Page1.qml'' – page 1
 
<br /><code><br />import QtQuick 1.1<br />Rectangle {id:test1<br /> width: 100<br /> height: 62<br /> color:&quot;red&amp;quot;<br /> // switchPage1 value is logically controlled somewhere in the code<br /> property bool switchPage1:true // false<br /> property int page1Index:2<br /> signal handlerLoader(string name, int index)<br /> MouseArea {<br /> anchors.fill:parent<br /> onClicked:{if (switchPage1==true)<br /> handlerLoader(&quot;Page2.qml&amp;quot;,page1Index);<br /> else handlerLoader(&quot;Help.qml&amp;quot;,0);<br /> }<br /> }<br />}<br /></code>
'''Design''': It follows the diagram above. The main file ''componentLoader.qml'' loads ''Page1.qml'', which in turn loads ''Page2.qml''. The ''Page2.qml'' file instantiates a component – ''Inserted.qml''. From within ''Inserted.qml'' the ''Loader'' in ''componentLoader.qml'' is invoked.
<br />Files ''Page2.qml'' and ''Page3.qml'' are similar to ''Page1.qml''.
 
<br />'''Download Package''': Necessary files: ''flexibleLoader.qml'', ''Page1.qml'', ''Page2.qml'', ''Page3.qml'', ''Help.qml'', ''NewWindow.qml''. Get them from &quot;here&amp;quot;:http://bit.ly/1lnH77m.
'''Development''': The issue here is that the signal ''handlerLoader'' has to be activated at the top level of the file ''Page2.qml''. When instantiating component ''Inserted'' we define an alias to the top level ''id'': ''property alias next1:pop''. The signal is invoked in ''Inserted.qml'' through a qualified name ''next1.handlerLoader(“Page3.qml”,0)''.
<br />h3. Loader and Component Together
<br />'''Use Case''': Assume the page we are loading uses an external component (component file) and from this component we want to load a next page. What is new in this use case comparing with the previous one? The call to the<br />''Loader'' is not located at the top level now. It is nested in a component. When the ''Loader'' is defined in the main application the pages are loaded in its context. This requires the ''Loader source'' property to be determined at the top level also. To solve this problem we use cascading definitions of properties. Firstly, have a look at a diagram illustrating the use case:
<br />[[Image:http://i1072.photobucket.com/albums/w362/vabo123/complexLoader.jpg|Loader and Component]]
<br />'''Design''': It follows the diagram above. The main file ''componentLoader.qml'' loads ''Page1.qml'', which in turn loads ''Page2.qml''. The ''Page2.qml'' file instantiates a component – ''Inserted.qml''. From within ''Inserted.qml'' the ''Loader'' in ''componentLoader.qml'' is invoked.
<br />'''Development''': The issue here is that the signal ''handlerLoader'' has to be activated at the top level of the file ''Page2.qml''. When instantiating component ''Inserted'' we define an alias to the top level ''id'': ''property alias next1:pop''. The signal is invoked in ''Inserted.qml'' through a qualified name</s> ''next1.handlerLoader(&quot;Page3.qml&amp;quot;,0)''.


'''Code''': Main file ''componentLoader.qml'' and files ''Page1.qml'' and ''Page3.qml'' are similar to these ones of the previous section.
'''Code''': Main file ''componentLoader.qml'' and files ''Page1.qml'' and ''Page3.qml'' are similar to these ones of the previous section.


''Page2.qml'' – this file instantiates component file ''Insert.qml''
''Page2.qml'' – this file instantiates component file ''Insert.qml''
<code><br />import QtQuick 1.1<br />Rectangle {id:pop<br /> width: 100<br /> height: 62<br /> x:30<br /> color:&quot;yellow&amp;quot;<br /> property int page2Index:3<br /> signal handlerLoader(string name, int index)<br /> Inserted{id:comp1<br /> property alias next1:pop<br /> y:20<br /> }<br />}<br /></code>


''Inserted.qml'' – this is component file
''Inserted.qml'' – this is component file


'''Download Package''': Necessary files: ''componentLoader.qml'', ''Page1.qml'', ''Page2.qml'', ''Page3.qml'', ''Inserted.qml''. Get them from [http://bit.ly/1lnH77m here] ''[bit.ly]''.
<code><br />import QtQuick 1.1<br />Rectangle {<br /> width: 100<br /> height: 62<br /> color:&quot;blue&amp;quot;<br /> MouseArea {<br /> anchors.fill: parent<br /> onClicked: {next1.handlerLoader(&quot;Page3.qml&amp;quot;,0)}<br /> }<br />}<br /></code>
 
'''Download Package''': Necessary files: ''componentLoader.qml'', ''Page1.qml'', ''Page2.qml'', ''Page3.qml'', ''Inserted.qml''. Get them from &quot;here&amp;quot;:http://bit.ly/1lnH77m.


===Loader and Inline Component===
=== Loader and Inline Component ===


'''Use case''': We want to implement an advertising banner composed from images and texts. We have several images combined with explanatory texts and these pairs are displayed cyclically. Additionally a button is needed to be bound to each displayed pair – for example to get more information on advertised topic. Have a look at some screenshots.
'''Use case''': We want to implement an advertising banner composed from images and texts. We have several images combined with explanatory texts and these pairs are displayed cyclically. Additionally a button is needed to be bound to each displayed pair – for example to get more information on advertised topic. Have a look at some screenshots.


[[Image:firstImage.jpg|First Image]]
[[Image:http://i1072.photobucket.com/albums/w362/vabo123/firstImage.jpg|First Image]]


[[Image:forthImage.jpg|Fourth Image]]
[[Image:http://i1072.photobucket.com/albums/w362/vabo123/forthImage.jpg|Fourth Image]]


'''Objectives''': To demonstrate how a loader and an inline component could be used together. Moreover nested components and <span class="caps">QML</span> ''Timer'' element are highlighted.
'''Objectives''': To demonstrate how a loader and an inline component could be used together. Moreover nested components and QML ''Timer'' element are highlighted.


'''Design''': Bellow you have a diagram illustrating what and how <span class="caps">QML</span> elements are employed.
'''Design''': Bellow you have a diagram illustrating what and how QML elements are employed.


[[Image:elementsHierarchy.jpg|Diagram]]
[[Image:http://i1072.photobucket.com/albums/w362/vabo123/elementsHierarchy.jpg|Diagram]]


'''Development''': The cycling is implemented through a <span class="caps">QML</span> ''Timer'' element. Each time the ''Timer'' generates an ''onTriggered'' signal, the ''sourceComponent'' property of the ''Loader'' (''id:test'') is changed to load the next pair image-text. Images are arranged in a JavaScript array – defined in JavaScript function ''act()''. The texts are placed in the JavaScript array ''externalArray'' – defined in a separate file ''arrayExt.js'' – just to show how this approach works. The pairs image-text are modeled with a inline component – ''id:varText'' – which is loaded sequentially by the loader. The necessary button is implemented as an external component – file ''Button.qml'' – and which is nested in the component ''varText''. The handler, connected to this button, is illustrated with ''console.log()'' function output.
'''Development''': The cycling is implemented through a QML ''Timer'' element. Each time the ''Timer'' generates an ''onTriggered'' signal, the ''sourceComponent'' property of the ''Loader'' (''id:test'') is changed to load the next pair image-text. Images are arranged in a JavaScript array – defined in JavaScript function ''act()''. The texts are placed in the JavaScript array ''externalArray'' – defined in a separate file ''arrayExt.js'' – just to show how this approach works. The pairs image-text are modeled with a inline component – ''id:varText'' – which is loaded sequentially by the loader. The necessary button is implemented as an external component – file ''Button.qml'' – and which is nested in the component ''varText''. The handler, connected to this button, is illustrated with ''console.log()'' function output.


'''Code''': Each cycle forms a state. The states are indentified via the property ''loop'', which gets ''int'' values and is used as arrays current index.
'''Code''': Each cycle forms a state. The states are indentified via the property ''loop'', which gets ''int'' values and is used as arrays current index.


''bannerComponent.qml''
''bannerComponent.qml''
<code><br />import QtQuick 1.0<br />import &quot;arrayExt.js&amp;quot; as FileExt<br />Rectangle {id:topLevel<br /> function act() {<br /> var element=[&quot;fall1.jpg&amp;quot;, &quot;fall2.jpg&amp;quot;,&quot;fall3.jpg&amp;quot;,&quot;fall4.jpg&amp;quot;];<br /> currentImage= element[loop];<br /> currentText=FileExt.externalArray[loop];<br /> test.sourceComponent=varText;<br /> if(loop==3)<br /> loop=0;<br /> else<br /> loop=loop+1;<br /> }<br />Loader {id:test<br /> anchors.centerIn:parent<br />}<br />property string currentImage<br />property string currentText:&quot; &quot;<br />property int loop:0<br /> width: 500<br /> height: 300<br /> color:&quot;#f7e5db&amp;quot;<br /> gradient: Gradient {<br /> GradientStop {<br /> position: 0.00;<br /> color: &quot;#ecd3d3&amp;quot;;<br /> }<br /> GradientStop {<br /> position: 0.71;<br /> color: &quot;#d2e8e8&amp;quot;;<br /> }<br />}<br />Component {<br /> id: varText<br /> Rectangle {id:top<br /> color:topLevel.color<br /> width: 500<br /> height:300<br /> Image { id:innerImage<br /> anchors.top: parent.top<br /> anchors.topMargin:30<br /> anchors.horizontalCenter: parent.horizontalCenter<br /> width:parent.width/3;height: parent.height/2<br /> source:currentImage<br /> }<br /> Text{id:answer<br /> anchors.top:innerImage.bottom<br /> anchors.topMargin:30<br /> horizontalAlignment: Text.AlignHCenter<br /> width:parent.width;<br /> text:currentText<br /> }<br /> Button {id:bottom<br /> anchors.top: answer.bottom<br /> anchors.topMargin:30<br /> anchors.right:top.right<br /> anchors.rightMargin:10<br /> MouseArea {id:place<br /> anchors.fill: parent<br /> onClicked:{console.log(&quot;State index = &quot;,loop)}<br /> }<br /> }<br /> }//end rectangle top<br />} //end component<br />Timer {<br /> interval: 3000; running: true; repeat: true<br /> onTriggered: {<br /> act();<br /> } //end onTriggered<br />} //end Timer<br />} //end rectangle container<br /></code>


''Button.qml''
''Button.qml''
<code><br />import QtQuick 1.0<br />Rectangle {<br /> width: 90<br /> height: 20<br /> color: &quot;#cbd9f3&amp;quot;<br /> radius:10<br /> Text {<br /> width:parent.width;height:parent.height<br /> horizontalAlignment: Text.AlignHCenter<br /> verticalAlignment: Text.AlignVCenter<br /> text:&quot;Click me&amp;quot;<br /> font.pointSize: 8<br /> }<br /> }<br /></code>


''arrayExt.js''
''arrayExt.js''


'''Download Package''': Necessary files: ''bannerComponent.qml'', ''Button.qml'', ''arrayExt.js'', ''fall1.jpg'', ''fall2.jpg'', ''fall3.jpg'', ''fall4.jpg''. Get them from [http://bit.ly/1lnH77m here] ''[bit.ly]''.
<code><br />/* This external JavaScript file contains the text blocks */<br />var externalArray = [&quot;Fist Image it is the first element of the JavaScript array ‘element’&quot;,<br /> &quot;Second Image its index in the array is 1&amp;quot;,<br /> &quot;Third Image - its index in the array is 2&amp;quot;,<br /> &quot;Fourth Image - its index in the array is 3&amp;quot;];<br /></code>
 
Note: The download package is organized in subdirectories corresponding to respective sections: <br /> • Component Element Definition – Component<br /> • Loader Element Definition Loader<br /> • Component Element vs. Loader Element ComponentvsLoader<br /> • Simple Wizard Template – SimpleWizard<br /> • More Complex Navigation – ComplexNavigation<br /> • Loader and Component Together – LoaderandComponent<br /> • Loader and Inline Component – LoaderInline
 
[http://bit.ly/1lnH77m Download Package] ''[bit.ly]''.


===Categories:===
'''Download Package''': Necessary files: ''bannerComponent.qml'', ''Button.qml'', ''arrayExt.js'', ''fall1.jpg'', ''fall2.jpg'', ''fall3.jpg'', ''fall4.jpg''. Get them from &quot;here&amp;quot;:http://bit.ly/1lnH77m.


* [[:Category:Developing with Qt|Developing_with_Qt]]
Note: The download package is organized in subdirectories corresponding to respective sections:<br />• Component Element Definition - Component<br />• Loader Element Definition – Loader<br />• Component Element vs. Loader Element – ComponentvsLoader<br />• Simple Wizard Template - SimpleWizard<br />• More Complex Navigation - ComplexNavigation<br />• Loader and Component Together - LoaderandComponent<br />• Loader and Inline Component - LoaderInline
** [[:Category:Developing with Qt::Qt-Quick|Qt Quick]]
* [[:Category:HowTo|HowTo]]

Revision as of 14:23, 23 February 2015


English
[toc align_right="yes&quot; depth="3&quot;]

QML Application Structuring Approaches

Introduction

A common approach to deal with a complex application is to decompose it in several smaller units. This leads to better structuring of application and more productive development process as a whole. QML offers two constructs that service this approach – Component and Loader elements. Roughly speaking, the both elements bring similar functionality. But the ways they do this are different. We may refer to the Component approach as static one and the Loader approach as dynamic one. We first re-call Component and Loader definitions, than compare the two approaches and in the main part of this article describe several Loader element use cases. There are two other approaches to structure a QML application also – dynamic creation of objects and module importing. They are beyond the scope of this article.

Component Element Definition

A QML component is a separate, autonomous unit that contains other QML elements. It could be defined in two modes – as an inline unit in a QML file or as a separate QML file. The main point with QML component is that it could be reused many times trough its very convenient interface. For more details about Component syntax and interface consult the "documentation.":http://doc.qt.nokia.com/4.7/qml-component.html

How does the Component element support application code structuring and particularly its decomposition? In case of component file it is obvious – the application is split in several components. They are included in the main file and instantiated via their interfaces, for example like this:

QML File

<br /><br />Banner { //The component file Banner.qml, located in the directory of the<br /> //QML File is instantiated here<br />id:ban1<br />color:”red”<br />}<br />…

AnotherComponent { //Here another component is instantiated<br />…<br />}<br />…<br />Banner { //Here we use the same component file Banner.qml. The instantiated component is<br /> //different from ban1 – different id,<br />id:ban2 //property color has different value, etc.<br />color:”lightblue”<br />}<br />…<br />

Including of the component means that we get an instance of the component viewed as a template. The basic component is used to generate instances only and they do not depend anymore on it. We may have several instances and they behavior independently, treated like basic QML elements. Note that Component interface allows to re-define component properties - predefined ones as well as defining new ones. This is the case of the color property in the snippet above. We could even just instantiate the component and not alter any property.

<br /><br />Banner {}<br /><br />

We may have many instances of Banner component provided that they have different id.

By now we are considering properties defined at the top level of the component file. What about siblings’/children’ properties in a component file? Let us consider the following case:

ExampleOne.qml - Component file

<br />// This component file is used in componentScope.qml file<br />// The point is how to have access to color property out of the scope of Text element

import QtQuick 1.1<br />Rectangle {<br /> width: 300<br /> height: 200<br /> property alias nameTextTop:nameText // This alias is defined at top level and<br /> // bound to Text element<br /> Text{id:nameText<br /> color:&quot;blue &quot;<br /> text:&quot;Some text fragment&amp;quot;<br /> }<br />}<br />

componentScope.qml – It is using component ExampleOne

<br />// The component ExampleOne is instantiated and its color property is change to red<br />import QtQuick 1.1

Rectangle {<br /> width: 360<br /> height: 360<br /> ExampleOne{id:instance<br /> nameTextTop.color:&quot;red&amp;quot;<br /> }<br />}<br />

As you see if we want to re-define the property color of nameText child we have to make it visible at the top level. To get this we define an alias of the nameText at the top level. This alias is re-defined in componentScope.qml file.

Could we develop more complex structures with components? For instance, could we have nested components? By nested components we mean that a component contains another component. Structures like these are acceptable:

nestedComponents.qml - Main file

<br />// Main file uses a component – Comp1. It in turn applies a nested component Comp2.<br />import QtQuick 1.0<br />Rectangle {<br /> width: 360<br /> height: 360<br /> Comp1{} //This component has a nested component Comp2<br /> Text {<br /> anchors.centerIn: parent<br /> text: &quot;Top Level - we use a component Comp1&amp;quot;<br /> }<br />}<br />

Comp1.qml - First level component file

<br />import QtQuick 1.0<br />//First level component Comp1.qml<br />Rectangle {<br /> width: 100<br /> height: 62<br /> color:&quot;red&amp;quot;<br /> Comp2{} // This component becomes part of Co&amp;quot;mp1<br />}&quot;:http://doc.qt.nokia.com/4.7/qml-component.html

Comp2.qml - Second level component file

<br />import QtQuick 1.0<br />//This is a component we nest in another component Comp1<br />Rectangle {<br /> x:50;y:40<br /> width: 100<br /> height: 62<br /> color:&quot;green&amp;quot;<br />}<br />

The inline definition syntax of the component element is very similar to this one of other QML elements. It starts with a key word Component followed by element block (represented by { } pair). Within the block you may define and nest other QML elements – refer to Nokia "documentation&quot;:http://doc.qt.nokia.com/4.7/qml-component.html.

On the other hand, inline Component offers other kind of structuring. It encapsulates a piece of QML code that could be assigned as a property value of other QML elements. Particularly this mechanism is used in the definition of the Loader element – see the next section. This kind of structuring is sometimes referred as aggregation. An example how inline Component could be utilized is demonstrated in section Loader and Inline Component.

Loader Element Definition

Loader element serves as a handler, which loads dynamically a QML unit formed as a separate QML file or inline component. Dynamically means that the QML unit to be loaded is determined at run-time, for example if a mouse button is clicked. The loaded unit is described as value of source or sourceComponent property of the Loader. The source value is a URL value – the path to QML file to be loaded. The sourceComponent value is id name of an inline component. Where can we bind values to these properties? The rule is: at places where the Loader id name is visible. In the following sections you see many examples of implementation of this rule. For more information on Loader see "documentation&quot;:http://doc.qt.nokia.com/4.7/qml-loader.html#details.

One interesting point is how the loaded QML unit could be accessed. The Loader element has a default property item. It is read-only and has Item type. In other words, loaded QML unit is a QML Item element. We recall that it is not visible and serves to group other QML elements. An illustrating snippet on item property follows:

LoaderItem.qml

<br />// QML file Page.qml is loaded by Loader top. Its color property is red<br />// Using Loader item property the color property is changed to blue<br />import QtQuick 1.1<br />Rectangle {<br /> width: 360<br /> height: 360<br /> Loader {id:top }<br /> Rectangle {Component.onCompleted:top.source=&quot;Page.qml&amp;quot;}<br /> Text {<br /> anchors.centerIn: parent<br /> text: &quot;Hello World&amp;quot;<br /> MouseArea {<br /> anchors.fill: parent<br /> onClicked: {top.item.color=&quot;blue&amp;quot;<br /> }<br /> }<br /> }<br />}<br />

Page.qml

<br />import QtQuick 1.1<br />Rectangle {<br /> width: 100<br /> height: 62<br /> color:&quot;red&amp;quot;<br />}<br />

We could set up source or sourceComponent properties in the Loader definition. Now the Loader element is processed like any other QML element and its target will be rendered. In this case the behavior is similar to the one of component element. But note that we may change programmatically properties values of source or sourceComponent at a later time.

Component Element vs. Loader Element

Now we are going to compare component file and Loader element. They both offer similar functionality, but differ in ways they are applied.

In case of a component file we have the following process. We declare the component (rather its interface) in a fixed way in the main file for example. QML parser immediately checks if a component file with the name as in component declaration exists. The parser searches in the directory where is placed the file.

The Loader acts a bit differently. When a QML file containing a Loader element is parsed, the Loader constructs itself like any other QML element. The Loader could define the source destination of the file to be loaded or not. In the first case the file is loaded. In the second case this is postponed to the moment we define the source destination of the file in a script element for example. Note that in case of source property we may point to a file located anywhere on our local computer or on the Internet. If we have many QML entities that are not needed permanently, there is no need to keep them in memory – rather load them as they are necessary. This leads to a better structuring of the application and more efficient computer memory management. Be aware that any consequent activation of a Loader destroys the previously loaded items. If we set an empty string as source property value or undefined value to sourceComponent property, the memory used by the Loader is released.

Because the file to be loaded by a Loader could be determined in run-time, the Loader approach is referred to as dynamic one. The component approach is called static one.

Combining loaders, components and other QML elements it is important to understand the hierarchy of elements and how they overlap each other. Let us create a demo example, which will illustrate QML elements rendering hierarchy. We are considering a QML main file that has two Loaders, one component and other standard QML elements. We re-define the z property of elements to control their overlapping.

elementHierarchy.qml – main file

<br />import QtQuick 1.1<br />Rectangle {id:first<br /> width: 360; height: 360<br /> color:&quot;lightblue&amp;quot;<br /> Loader {id:newLoad<br /> }<br /> Text {id:textInner<br /> z:3<br /> text: &quot;Sample text with z=7 property &quot;<br /> }<br /> Rectangle {id:second<br /> width:100;height:100<br /> z:2<br /> x:80<br /> color:&quot;pink&amp;quot;<br /> Text {text:&quot;with z=2&amp;quot;}<br /> }<br /> CompNew{id:far<br /> x:20;y:20<br /> }<br /> Loader{id:loadSecond<br /> source:&quot;secondComp.qml&amp;quot;<br /> }<br /> Rectangle {id:dummy<br /> width:80;height:40;x:140;y:80<br /> color:&quot;gray&amp;quot;<br /> Component.onCompleted:{<br /> newLoad.source=&quot;firstLoader.qml&amp;quot;<br /> }<br /> }<br />}<br />

firstLoader.qml – file loaded by newLoad Loader

<br />import QtQuick 1.0<br />Rectangle {<br /> width: 200<br /> height: 100<br /> y:30<br /> color:&quot;red&amp;quot;<br />}<br />

secondComp.qml - loaded by loadSecond Loader

<br />import QtQuick 1.0<br />Rectangle {<br /> width: 100<br /> height: 62<br /> x:40;y:45<br /> color:&quot;green&amp;quot;<br />}<br />

CompNew.qml – component file

<br />import QtQuick 1.1<br />Rectangle {id:comp<br /> width: 62<br /> height: 62<br /> color:&quot;yellow&amp;quot;<br /> Text {text:&quot;CompNew&amp;quot;}<br />}<br />

The next picture illustrates how QML elements are displayed and overlapping. As a rule elements are rendered in the order they are defined in the main file. In the snippets above z property is changed for text element textInner (z=3) and for rectangle element second (z=2) and they are rendered at the top element hierarchy.

Levels of elements

You may experiment changing z property.

Loader Element Use Cases

Simple Wizard Template

Our application consists of several pages (3 in the snippet bellow), each of them formatted as a separate QML file. These files could be located in different directories, but for simplicity we suppose they reside in the directory where is located our main file. Each page has its own Loader element and when a Click me button is pressed the Loader loads a next wizard page.

wizardSimple.qml – main file

<br />import QtQuick 1.1<br />Rectangle {id:top<br /> width: 360<br /> height:360<br /> Loader {id:main} //Declaration of a Loader. It will be activated later.<br /> Text {<br /> text: &quot;Main file&amp;quot;<br /> }<br /> Button{id:buttonMain<br /> //Position the button in top rectangle<br /> anchors.bottom:top.bottom; anchors.right: top.right<br /> MouseArea {<br /> anchors.fill: parent<br /> onClicked: {main.source=&quot;Page1.qml&amp;quot;; //Load a new page<br /> buttonMain.z=–1 //Hide button<br /> }<br /> }<br /> }<br />}<br />

Page1.qml – first wizard page

<br />import QtQuick 1.1<br />Rectangle {id:page1Container<br /> width: 200<br /> height: 300<br /> x:40;y:40<br /> color:&quot;pink&amp;quot;<br /> Loader{id:page1} //Declaration of a new Loader element<br /> Text{<br /> text:&quot;First page loaded&amp;quot;<br /> }<br /> Button{id:buttonPage1<br /> //Position the button in page1Container rectangle<br /> anchors.bottom:page1Container.bottom;<br /> anchors.right: page1Container.right<br /> MouseArea {<br /> anchors.fill: parent<br /> onClicked: {page1.source=&quot;Page2.qml&amp;quot;;<br /> buttonPage1.z=–1 //Hide button<br /> }<br /> }<br /> }<br />}<br />

Page2.qml – second wizard page

<br />import QtQuick 1.1<br />Rectangle {id:page2Container<br /> width: 200<br /> height: 200<br /> x:80;y:80<br /> color:&quot;yellow&amp;quot;<br /> Loader{id:page2}<br /> Text{<br /> text:&quot;Second page loaded&amp;quot;<br /> }<br /> Button{id:buttonPage2<br /> anchors.bottom:page2Container.bottom;<br /> anchors.right: page2Container.right<br /> MouseArea {<br /> anchors.fill: parent<br /> onClicked: {page2.source=&quot;Page3.qml&amp;quot;;buttonPage2.z=<s>1<br /> }<br /> }<br /> }<br />}<br />


Page3.qml – last wizard page


<br />import QtQuick 1.1<br />Rectangle {<br /> width: 120<br /> height: 62<br /> x:100;y:100<br /> color:&quot;green&amp;quot;<br /> Text {<br /> text:&quot;Last Loaded Page&amp;quot;<br /> }<br />}<br />


The above wizard implementation is very simple, but suffers of some drawbacks:
• When a new page is loaded the previously loaded page stays unnecessary into memory.
• We use similar loaders for each page – their IDs are different only.
• The order of pages loading is fixed one – the source property values are set in advance.
• There is no mechanism for exchange of information between the caller and the called page and vise-verse.
h3. More Complex Navigation
Now we will try to release the restrictions of the loading scheme from the previous section. Firstly, following the Simon Judge "idea&quot;:http://www.mobilephonedevelopment.com/qt-qml-tips/, we will apply a common Loader for all pages. Secondly, we suppose that the next page to be loaded is not known in advance – it is determined after a condition is evaluated. And finally, we will use signals to pass information between pages and demonstrate how multi-window screens could be constructed.
A common approach to trigger some actions after an event is fired is to define an own signal like that (for more information "here&quot;:http://doc.qt.nokia.com/4.7/qmlevents.html):
signal MySignal(string message, int trigger)


where signal is QML keyword, MySignal is the name of newly defined signal, message is a parameter of type string and trigger is another parameter. The parameters could be of different QML types. For our purposes we want to pass a string that is the name of wizard page to be loaded and an index to identify the application state.
The main idea is as follows:
• When we want to load a page we emit our own signal.
• This signal is caught by a Loader defined somewhere.
• Our signal has a parameter to which we bound the name of the page to be loaded – as an URL value.
Use Case: We will consider an application that consists of a main page (Page1) and two other pages (Page2, Page3). The pages should be loaded sequentially and construct a wizard this way. Some of pages could be loaded checking for a logical condition creating the wizard branches.
Objectives: To demonstrate how a flexible wizard could be implemented based on QML Loader element. Additionally we may complicate the wizard constructing some multi-windows screens.
Design: Pages are loaded one by one. The application starts loading automatically Page1. To load the next page we should click the left mouse button. For Page1 we have two wizard branches depending on a logical value. We introduce a current index for pages (an integer) around which the application logic is build.
Development: For each page we define the same own signal:
signal handlerLoader(string name, int index)


To activate this signal (emit the signal) we have to invoke it as a method in a script block. A MouseArea onClicked script block is used. In invocation the name of the page to be loaded is passed as parameter. Emitted signal is attached (automatically) to the signal handler onHandlerLoader, which in turn is connected to a target pageLoader.item. The connection is carried out by a QML Connections element in the QML file flexibleLoader.qml. The target pageLoader.item is in fact the object loaded by the Loader (id:pageLoader) defined in the file flexibleLoader.qml. Note that the target property of the Connections element is of type Object. When Page2 is loaded we want a second window (NewWindow.qml) to be loaded into the same screen. This is done by a second Loader (id:window), which is defined in the file flexibleLoader.qml. The decision to load this second window is taken in onHandlerLoader block on the base of returned value of trigger parameter. Note that the Loader destroys previously loaded entities when loading new items.
Code:
flexibleLoader.qml – application main file


<br />import QtQuick 1.1<br />Item {id:top<br /> width:300<br /> height:200<br /> Loader{id:window}<br /> signal handlerLoader(string name, int index)<br /> Loader {<br /> id:pageLoader<br /> source:&quot;Page1.qml&amp;quot;<br /> }<br /> Connections {<br /> target:pageLoader.item<br /> onHandlerLoader:{pageLoader.source=name;<br /> if(index===2)<br /> window.source=&quot;NewWindow.qml&amp;quot;;<br /> }<br /> }<br />}<br />


Page1.qml – page 1


<br />import QtQuick 1.1<br />Rectangle {id:test1<br /> width: 100<br /> height: 62<br /> color:&quot;red&amp;quot;<br /> // switchPage1 value is logically controlled somewhere in the code<br /> property bool switchPage1:true // false<br /> property int page1Index:2<br /> signal handlerLoader(string name, int index)<br /> MouseArea {<br /> anchors.fill:parent<br /> onClicked:{if (switchPage1==true)<br /> handlerLoader(&quot;Page2.qml&amp;quot;,page1Index);<br /> else handlerLoader(&quot;Help.qml&amp;quot;,0);<br /> }<br /> }<br />}<br />


Files Page2.qml and Page3.qml are similar to Page1.qml.
Download Package: Necessary files: flexibleLoader.qml, Page1.qml, Page2.qml, Page3.qml, Help.qml, NewWindow.qml. Get them from "here&quot;:http://bit.ly/1lnH77m.
h3. Loader and Component Together
Use Case: Assume the page we are loading uses an external component (component file) and from this component we want to load a next page. What is new in this use case comparing with the previous one? The call to the
Loader is not located at the top level now. It is nested in a component. When the Loader is defined in the main application the pages are loaded in its context. This requires the Loader source property to be determined at the top level also. To solve this problem we use cascading definitions of properties. Firstly, have a look at a diagram illustrating the use case:
Loader and Component
Design: It follows the diagram above. The main file componentLoader.qml loads Page1.qml, which in turn loads Page2.qml. The Page2.qml file instantiates a component – Inserted.qml. From within Inserted.qml the Loader in componentLoader.qml is invoked.
Development: The issue here is that the signal handlerLoader has to be activated at the top level of the file Page2.qml. When instantiating component Inserted we define an alias to the top level id: property alias next1:pop. The signal is invoked in Inserted.qml through a qualified name next1.handlerLoader("Page3.qml&quot;,0).

Code: Main file componentLoader.qml and files Page1.qml and Page3.qml are similar to these ones of the previous section.

Page2.qml – this file instantiates component file Insert.qml

<br />import QtQuick 1.1<br />Rectangle {id:pop<br /> width: 100<br /> height: 62<br /> x:30<br /> color:&quot;yellow&amp;quot;<br /> property int page2Index:3<br /> signal handlerLoader(string name, int index)<br /> Inserted{id:comp1<br /> property alias next1:pop<br /> y:20<br /> }<br />}<br />

Inserted.qml – this is component file

<br />import QtQuick 1.1<br />Rectangle {<br /> width: 100<br /> height: 62<br /> color:&quot;blue&amp;quot;<br /> MouseArea {<br /> anchors.fill: parent<br /> onClicked: {next1.handlerLoader(&quot;Page3.qml&amp;quot;,0)}<br /> }<br />}<br />

Download Package: Necessary files: componentLoader.qml, Page1.qml, Page2.qml, Page3.qml, Inserted.qml. Get them from "here&quot;:http://bit.ly/1lnH77m.

Loader and Inline Component

Use case: We want to implement an advertising banner composed from images and texts. We have several images combined with explanatory texts and these pairs are displayed cyclically. Additionally a button is needed to be bound to each displayed pair – for example to get more information on advertised topic. Have a look at some screenshots.

First Image

Fourth Image

Objectives: To demonstrate how a loader and an inline component could be used together. Moreover nested components and QML Timer element are highlighted.

Design: Bellow you have a diagram illustrating what and how QML elements are employed.

Diagram

Development: The cycling is implemented through a QML Timer element. Each time the Timer generates an onTriggered signal, the sourceComponent property of the Loader (id:test) is changed to load the next pair image-text. Images are arranged in a JavaScript array – defined in JavaScript function act(). The texts are placed in the JavaScript array externalArray – defined in a separate file arrayExt.js – just to show how this approach works. The pairs image-text are modeled with a inline component – id:varText – which is loaded sequentially by the loader. The necessary button is implemented as an external component – file Button.qml – and which is nested in the component varText. The handler, connected to this button, is illustrated with console.log() function output.

Code: Each cycle forms a state. The states are indentified via the property loop, which gets int values and is used as arrays current index.

bannerComponent.qml

<br />import QtQuick 1.0<br />import &quot;arrayExt.js&amp;quot; as FileExt<br />Rectangle {id:topLevel<br /> function act() {<br /> var element=[&quot;fall1.jpg&amp;quot;, &quot;fall2.jpg&amp;quot;,&quot;fall3.jpg&amp;quot;,&quot;fall4.jpg&amp;quot;];<br /> currentImage= element[loop];<br /> currentText=FileExt.externalArray[loop];<br /> test.sourceComponent=varText;<br /> if(loop==3)<br /> loop=0;<br /> else<br /> loop=loop+1;<br /> }<br />Loader {id:test<br /> anchors.centerIn:parent<br />}<br />property string currentImage<br />property string currentText:&quot; &quot;<br />property int loop:0<br /> width: 500<br /> height: 300<br /> color:&quot;#f7e5db&amp;quot;<br /> gradient: Gradient {<br /> GradientStop {<br /> position: 0.00;<br /> color: &quot;#ecd3d3&amp;quot;;<br /> }<br /> GradientStop {<br /> position: 0.71;<br /> color: &quot;#d2e8e8&amp;quot;;<br /> }<br />}<br />Component {<br /> id: varText<br /> Rectangle {id:top<br /> color:topLevel.color<br /> width: 500<br /> height:300<br /> Image { id:innerImage<br /> anchors.top: parent.top<br /> anchors.topMargin:30<br /> anchors.horizontalCenter: parent.horizontalCenter<br /> width:parent.width/3;height: parent.height/2<br /> source:currentImage<br /> }<br /> Text{id:answer<br /> anchors.top:innerImage.bottom<br /> anchors.topMargin:30<br /> horizontalAlignment: Text.AlignHCenter<br /> width:parent.width;<br /> text:currentText<br /> }<br /> Button {id:bottom<br /> anchors.top: answer.bottom<br /> anchors.topMargin:30<br /> anchors.right:top.right<br /> anchors.rightMargin:10<br /> MouseArea {id:place<br /> anchors.fill: parent<br /> onClicked:{console.log(&quot;State index = &quot;,loop)}<br /> }<br /> }<br /> }//end rectangle top<br />} //end component<br />Timer {<br /> interval: 3000; running: true; repeat: true<br /> onTriggered: {<br /> act();<br /> } //end onTriggered<br />} //end Timer<br />} //end rectangle container<br />

Button.qml

<br />import QtQuick 1.0<br />Rectangle {<br /> width: 90<br /> height: 20<br /> color: &quot;#cbd9f3&amp;quot;<br /> radius:10<br /> Text {<br /> width:parent.width;height:parent.height<br /> horizontalAlignment: Text.AlignHCenter<br /> verticalAlignment: Text.AlignVCenter<br /> text:&quot;Click me&amp;quot;<br /> font.pointSize: 8<br /> }<br /> }<br />

arrayExt.js

<br />/* This external JavaScript file contains the text blocks */<br />var externalArray = [&quot;Fist Image  it is the first element of the JavaScript array element&quot;,<br /> &quot;Second Image  its index in the array is 1&amp;quot;,<br /> &quot;Third Image - its index in the array is 2&amp;quot;,<br /> &quot;Fourth Image - its index in the array is 3&amp;quot;];<br />

Download Package: Necessary files: bannerComponent.qml, Button.qml, arrayExt.js, fall1.jpg, fall2.jpg, fall3.jpg, fall4.jpg. Get them from "here&quot;:http://bit.ly/1lnH77m.

Note: The download package is organized in subdirectories corresponding to respective sections:
• Component Element Definition - Component
• Loader Element Definition – Loader
• Component Element vs. Loader Element – ComponentvsLoader
• Simple Wizard Template - SimpleWizard
• More Complex Navigation - ComplexNavigation
• Loader and Component Together - LoaderandComponent
• Loader and Inline Component - LoaderInline