QML Containers: Difference between revisions
No edit summary |
AutoSpider (talk | contribs) (Simplify punctuation) |
||
(8 intermediate revisions by 3 users not shown) | |||
Line 1: | Line 1: | ||
[[Category:Developing_with_Qt::Qt Quick]] [[Category:HowTo]] | {{Cleanup|reason=Needs to be updated to QtQuick 2}} | ||
{{LangSwitch}} | |||
[[Category:Developing_with_Qt::Qt Quick]] | |||
[[Category:HowTo]] | |||
== Introduction == | == Introduction == | ||
Line 11: | Line 9: | ||
== ListModel Element == | == ListModel Element == | ||
It implements a classical list – an ordered collection of data items. Each item is accessible through ''ListModel'' element name and an index in the ordering. The index starts from 0. The data items are other QML elements – ''ListElement''. They define properties and assign values to them in usual manner, but with stronger | It implements a classical list – an ordered collection of data items. Each item is accessible through ''ListModel'' element name and an index in the ordering. The index starts from 0. The data items are other QML elements – ''ListElement''. They define properties and assign values to them in usual manner, but with stronger [http://doc.qt.io/qt-4.8/qml-listelement.html restrictions]. In QML documentation the ''ListElements'' properties are called roles. | ||
Here is an example that illustrates these definitions: | Here is an example that illustrates these definitions: | ||
Line 67: | Line 65: | ||
</code> | </code> | ||
What is important here is that we could have several ''ListElement'' elements, but each defines the same properties. You could see the similarity with arrays in other programming languages where the | What is important here is that we could have several ''ListElement'' elements, but each defines the same properties. You could see the similarity with arrays in other programming languages where the array's elements must be of the same type. | ||
With the ''ListModel'' element are associated typical operations for lists: ''insert(), append(), remove(), get()'' , etc. Consult | With the ''ListModel'' element are associated typical operations for lists: ''insert(), append(), remove(), get()'' , etc. Consult [http://doc.qt.io/qt-4.8/qml-listmodel.html#methods documentation] for their usage. | ||
What could we do with the ''ListModel'' container? Firstly, the container should be constructed by the definition and operations eventually. Next, the data items in the container could be accessed and used in other QML elements. Finally, all data items of a ''ListModel'' element could be rendered as a collection through views such as the ''ListView'' for example. The views are special QML elements servicing as viewers – see | What could we do with the ''ListModel'' container? Firstly, the container should be constructed by the definition and operations eventually. Next, the data items in the container could be accessed and used in other QML elements. Finally, all data items of a ''ListModel'' element could be rendered as a collection through views such as the ''ListView'' for example. The views are special QML elements servicing as viewers – see [http://doc.qt.io/qt-4.8/qdeclarativeelements.html here]. | ||
The demo snippet above demonstrates how ''ListModel'' container is used to hold some data. Note that we not use a ''ListVeiw'' for rendering the data. Usually ''ListModel'' is paired with ''ListView'' element. Our goal is to show how a ''ListModel'' serves as a data container supplying data to other QML elements. | The demo snippet above demonstrates how ''ListModel'' container is used to hold some data. Note that we not use a ''ListVeiw'' for rendering the data. Usually ''ListModel'' is paired with ''ListView'' element. Our goal is to show how a ''ListModel'' serves as a data container supplying data to other QML elements. | ||
Line 99: | Line 97: | ||
== Long Texts == | == Long Texts == | ||
If you have a long text as a ''ListElement'' property value you may prefer to hold it in a separate place not in the corresponding ''ListModel'' definition. One solution is to use ''JavaScript'' arrays – see | If you have a long text as a ''ListElement'' property value you may prefer to hold it in a separate place not in the corresponding ''ListModel'' definition. One solution is to use ''JavaScript'' arrays – see [http://wiki.qt.io/QML_Multi-line_Texts_Handling here]. Another solution is to define an empty ''ListModel'', which is used as a template and the respective property value (long text) is defined dynamically in a script block. | ||
There are two approaches. The first uses the ''setProperty()'' method of the ''ListModel'' element. Here is a code fragment as example: | There are two approaches. The first uses the ''setProperty()'' method of the ''ListModel'' element. Here is a code fragment as example: | ||
Line 153: | Line 151: | ||
The property values of a ''ListElement'' could be ''JavaScript'' expressions and particularly a return value from a ''JavaScript'' function call. In the snippet above the function is named ''longTextFragment()'' and the long text is placed and formatted in the function body. | The property values of a ''ListElement'' could be ''JavaScript'' expressions and particularly a return value from a ''JavaScript'' function call. In the snippet above the function is named ''longTextFragment()'' and the long text is placed and formatted in the function body. | ||
The full demo code for the above use cases you could find | The full demo code for the above use cases you could find [http://bit.ly/1fpxKyX here]. | ||
== list and variant Data Types as Containers == | == list and variant Data Types as Containers == | ||
QML offers two data types – ''list'' and ''variant'' – which could be used as arrays. They are very similar, but too different semantically at the same time. The documentation on ''list'' and ''variant'' data types is | QML offers two data types – ''list'' and ''variant'' – which could be used as arrays. They are very similar, but too different semantically at the same time. The documentation on ''list'' and ''variant'' data types is [http://doc.qt.io/qt-4.8/qdeclarativebasictypes.html here]. Consult the properties usage [http://doc.qt.io/qt-4.8/propertybinding.html#list-properties document] also. | ||
The ''list'' data type collects QML elements, which are of the type as the type in the ''list'' type definition or inherited from that type. The ''list'' data are defined as a custom property like that: | The ''list'' data type collects QML elements, which are of the type as the type in the ''list'' type definition or inherited from that type. The ''list'' data are defined as a custom property like that: | ||
Line 205: | Line 203: | ||
</code> | </code> | ||
The both sequences – ''list'' and ''variant'' – are immutable. That means that once defined elements in a ''list'' or ''variant'' data could not be changed. For the ''variant'' data there is a tricky | The both sequences – ''list'' and ''variant'' – are immutable. That means that once defined elements in a ''list'' or ''variant'' data could not be changed. For the ''variant'' data there is a tricky [http://doc.qt.io/qt-4.8/qml-variant.html approach] to go around this limitation. | ||
The complete code of examples in this section could be downloaded | The complete code of examples in this section could be downloaded [http://bit.ly/1fpxKyX` here]. | ||
== QML Containers and Texts == | == QML Containers and Texts == | ||
Line 221: | Line 219: | ||
</code> | </code> | ||
The complete snippet is | The complete snippet is [http://bit.ly/1fpxKyX here]. | ||
If we want to be able to format text messages we could use a l_ist_ data type to hold QML Text elements: | If we want to be able to format text messages we could use a l_ist_ data type to hold QML Text elements: | ||
Line 232: | Line 230: | ||
</code> | </code> | ||
The complete code for this use case is | The complete code for this use case is [http://bit.ly/1fpxKyX here]. | ||
As the both ''variant'' and ''list'' containers just hold data we need a solution for rendering them. The process has two steps: firstly we get the data and then render them. The proposed solution is to use a QML loader and it is illustrated in the diagram bellow. | As the both ''variant'' and ''list'' containers just hold data we need a solution for rendering them. The process has two steps: firstly we get the data and then render them. The proposed solution is to use a QML loader and it is illustrated in the diagram bellow. | ||
[[Image: | [[Image:ListDiagram.jpg|Diagram]] | ||
The loader – ''id:light'' – loads a template ''VirtualText.qml''. It is a QML component file. The template is an empty structure and contains a QML ''Text'' element that corresponds to QML ''Text'' elements of the ''variant'' (''list'') containers. The goal of the template is to render ''Text'' elements stored in containers. Firstly the loader ''light'' loads and renders the empty template: | The loader – ''id:light'' – loads a template ''VirtualText.qml''. It is a QML component file. The template is an empty structure and contains a QML ''Text'' element that corresponds to QML ''Text'' elements of the ''variant'' (''list'') containers. The goal of the template is to render ''Text'' elements stored in containers. Firstly the loader ''light'' loads and renders the empty template: | ||
Line 254: | Line 252: | ||
The demo snippets play as follows – see screenshots bellow. We have two areas on the screen. One where the texts are displayed and another which accepts mouse clicks. The first mouse click displays the first container text element and so on. After the list is exhausted the program quits. | The demo snippets play as follows – see screenshots bellow. We have two areas on the screen. One where the texts are displayed and another which accepts mouse clicks. The first mouse click displays the first container text element and so on. After the list is exhausted the program quits. | ||
[[Image: | [[Image:ScreenHunterformattedfirst.jpg|Screenshot - text1]] | ||
[[Image: | [[Image:ScreenHunterformattedthird.jpg|Screenshot - text3]] | ||
== QML Containers and Images == | == QML Containers and Images == | ||
Line 270: | Line 268: | ||
The same techniques as in the previous section are used to define a template (''VirtualImage.qml'') and a loader, which loads images sequentially. Bellow you see some screenshots. | The same techniques as in the previous section are used to define a template (''VirtualImage.qml'') and a loader, which loads images sequentially. Bellow you see some screenshots. | ||
[[Image: | [[Image:ScreenHunteballs.jpg|Screenshot - image1]] | ||
[[Image: | [[Image:ScreenHunterfunnyballs.jpg|Screenshot - image2]] | ||
The functionality that is implemented is similar to | The functionality that is implemented is similar to "Loader and Inline Component" use case analyzed [http://wiki.qt.io/QML-Application-Structuring-Approaches here]. | ||
The full code is available | The full code is available [http://bit.ly/1fpxKyX here]. | ||
== QML Containers and JavaScript Objects == | == QML Containers and JavaScript Objects == | ||
Line 323: | Line 321: | ||
</code> | </code> | ||
The discussed constructs are similar to ones in other programming languages and known as | The discussed constructs are similar to ones in other programming languages and known as "array of functions", 'array of arrays". | ||
The complete example could be downloaded | The complete example could be downloaded [http://bit.ly/1fpxKyX here]. | ||
== QML Containers and Components == | == QML Containers and Components == | ||
Line 359: | Line 357: | ||
</code> | </code> | ||
The complete snippet is | The complete snippet is [http://bit.ly/1fpxKyX here]. | ||
''Note'': All code snippets analyzed above are included in a | ''Note'': All code snippets analyzed above are included in a [http://bit.ly/1fpxKyX downloadable] package, which contains: | ||
Subdirectory - - Files | Subdirectory - - Files | ||
Line 373: | Line 371: | ||
containerFormattedText - - containerFormattedText.qml, VirtualText.qml | containerFormattedText - - containerFormattedText.qml, VirtualText.qml | ||
containerImage - - containerImage.qml, VirtualImage.qml, balls.jpg, | containerImage - - containerImage.qml, VirtualImage.qml, balls.jpg, four_balls.jpg, funny_balls.jpg | ||
containerJavaScript - - containerJavaScript.qml, jsfile.js | containerJavaScript - - containerJavaScript.qml, jsfile.js | ||
containerComponent - - containerComponent.qml, ComponentTest1.qml, | containerComponent - - containerComponent.qml, ComponentTest1.qml, ComponentTest2.qml | ||
Latest revision as of 13:23, 23 August 2015
This article may require cleanup to meet the Qt Wiki's quality standards. Reason: Needs to be updated to QtQuick 2 Please improve this article if you can. Remove the {{cleanup}} tag and add this page to Updated pages list after it's clean. |
En Ar Bg De El Es Fa Fi Fr Hi Hu It Ja Kn Ko Ms Nl Pl Pt Ru Sq Th Tr Uk Zh
Introduction
In programming we are used to applying some language constructs for data collections like arrays for example. In QML these constructs are lists. They are implemented as ListModel elements and list and variant types. In the text bellow we call these constructs containers. A container holds linearly ordered elements, which are accessed by an index. The indexing uses special methods as in case of ListModel element or well-known square bracket notation in case of list and variant types. This article explores application of QML containers and how they are controlled dynamically.
ListModel Element
It implements a classical list – an ordered collection of data items. Each item is accessible through ListModel element name and an index in the ordering. The index starts from 0. The data items are other QML elements – ListElement. They define properties and assign values to them in usual manner, but with stronger restrictions. In QML documentation the ListElements properties are called roles.
Here is an example that illustrates these definitions:
//This snippet illustrates how ListModel is used as a container for other QML elements
import QtQuick 1.1
Rectangle {id:top; width:500;height:500
property alias getText:arrangeText //Makes arrangeText elements visible in top
Rectangle {
id:dummy
Component.onCompleted:{
top.color="lightsteelblue"
//Demonstrates how ListElement properties are accessed
top.getText.firstText.text=ourContainer.get(0).test1
top.getText.secondText.text=ourContainer.get(1).test1
top.getText.thirdText.text=ourContainer.get(2).test1
top.getText.forthText.text=ourContainer.get(2).moreParameters.get(0).year
}
}// end dummy
ListModel {
id: ourContainer
ListElement {
test1:"FirstText as title"
authorA:"Smith"
moreParameters:[ListElement{year:"1947"}]
}
ListElement {
test1:"SecondText as abstract"
authorA:"Kavendish"
moreParameters:[ListElement{year:"2001"}]
}
ListElement {
test1:"ThirdText as table of content"
authorA:"Vandey"
moreParameters:[ListElement{year:"Start date: 1999"}]
}
}
Column{id:arrangeText
property alias firstText:first
property alias secondText:second
property alias thirdText:third
property alias forthText:parameters
spacing:30
Text{id:first}
Text{id:second}
Text{id:third}
Text{id:parameters}
}
}//end rectangle top
What is important here is that we could have several ListElement elements, but each defines the same properties. You could see the similarity with arrays in other programming languages where the array's elements must be of the same type.
With the ListModel element are associated typical operations for lists: insert(), append(), remove(), get() , etc. Consult documentation for their usage.
What could we do with the ListModel container? Firstly, the container should be constructed by the definition and operations eventually. Next, the data items in the container could be accessed and used in other QML elements. Finally, all data items of a ListModel element could be rendered as a collection through views such as the ListView for example. The views are special QML elements servicing as viewers – see here.
The demo snippet above demonstrates how ListModel container is used to hold some data. Note that we not use a ListVeiw for rendering the data. Usually ListModel is paired with ListView element. Our goal is to show how a ListModel serves as a data container supplying data to other QML elements.
The snippet is constructed as follows. Firstly, we define a ListModel element ourContainer and within it three ListElement. The QML snippet file introduces four Text elements – first, second, third, parameters. The functionality of the snippet is displaying the text properties values of the Text elements, which in turn are properties values of the ListElements. For convenience the displayed texts are arranged in columns. The role of custom defined properties in the snippet is to grant access to different QML elements. You remember the access rules for QML elements tree: from bottom to top entities are visible, but the contrary is not true. The rectangle dummy is used to form a script block where we get the data from the container and assign them to respective properties of Text elements.
Nested ListElements
In our example we define a property – moreparameters – which receives values that are other ListElements.
ListElement {
test1:"ThirdText as table of content"
authorA:"Vandey"
moreParameters:[ListElement{year:"Start date: 1999"}]
}
In other words, we could have nested ListElements. To have access to nested values in the example we use get() method two times:
top.getText.forthText.text=ourContainer.get(2).moreParameters.get(0).year
A property of a ListElement could be of list type, but be aware that elements of that list must be other ListElements.
Long Texts
If you have a long text as a ListElement property value you may prefer to hold it in a separate place not in the corresponding ListModel definition. One solution is to use JavaScript arrays – see here. Another solution is to define an empty ListModel, which is used as a template and the respective property value (long text) is defined dynamically in a script block.
There are two approaches. The first uses the setProperty() method of the ListModel element. Here is a code fragment as example:
Rectangle {id:top; width:500;height:500
Rectangle {
id:dummy
Component.onCompleted:{
ourContainer.setProperty(0,"test1",
"New property value\n Second line\n Third line")
}
}// end dummy
ListModel {
id: ourContainer
ListElement {
test1:""
}
}
Text{id:first
text:ourContainer.get(0).test1
}
}//end rectangle top
The property value of the List Element, which holds the long text, initially is an empty string. Later this value is changed dynamically in a separate script block. What is the role of the dummy rectangle above – just to construct a separate place where to put the long text.
The second approach defines an empty ListModel, which has no ListElements at all. Dynamically we use the set() method of the ListModel to add a long text. Have a look at a code sample:
Rectangle {id:top; width:500;height:500
function longTextFragment() {
var back= "First line - defined in the function body\n"+
"Second line - defined in the function body"
return back
}
ListModel {
id: ourContainer
}
Rectangle {
id:dummy
Component.onCompleted:{
ourContainer.set(0,{"test1":longTextFragment()})
console.log("Test the new entry in ourContainer\n",ourContainer.get(0).test1)
first.text=ourContainer.get(0).test1
}
}// end dummy
Text{id:first
}
}//end rectangle top
The property values of a ListElement could be JavaScript expressions and particularly a return value from a JavaScript function call. In the snippet above the function is named longTextFragment() and the long text is placed and formatted in the function body.
The full demo code for the above use cases you could find here.
list and variant Data Types as Containers
QML offers two data types – list and variant – which could be used as arrays. They are very similar, but too different semantically at the same time. The documentation on list and variant data types is here. Consult the properties usage document also.
The list data type collects QML elements, which are of the type as the type in the list type definition or inherited from that type. The list data are defined as a custom property like that:
property list<Rectangle> exampleDefinition: [Rectangle{width:50;height:50},
Rectangle{},Rectangle{id:last;}
]
The initializing part of the definition (right side) is optional. We could define just a list data name and at a later moment assign elements to the list.
property list<Item> sample
…
Rectangle{id:rect1;width:40;height:40}
Rectangle {id:rect2;width:100;height:100}
Rectangle {id:dummy
Component.onCompleted:{sample=[rect1,rect2];
console.log("rect1 width = ",sample[0].width)
}
}
…
All elements in a list data definition have to be of the same type. Basic QML data types as int, string, etc. could not be elements of a list data.
The variant data type uses the same square brackets to form a list of entities. They could be basic QML types (but not QML elements types) or JavaScript objects. It is important to note that variant data type list entities could be of different types.
…
function fun(){
return "Return value from fun()"
}
…
property string text1:"Some text"
property string text2:"Another text"
…
property variant anotherArrayType:["This is a string",text1+text2, 30*2,fun()]
…
Rectangle {id:dummy1
Component.onCompleted:{
console.log(anotherArrayType[1],anotherArrayType[2],anotherArrayType[3])
}
…
The both sequences – list and variant – are immutable. That means that once defined elements in a list or variant data could not be changed. For the variant data there is a tricky approach to go around this limitation.
The complete code of examples in this section could be downloaded here.
QML Containers and Texts
Let us consider a typical use case for program systems. We have a set of text messages and need to store them in a container. In different situations we get a message from the container and display it.
The simplest solution is to define a variant list and hold the messages in it as strings:
property variant someMessages:["This is the first message[[Image:",
"This is the second message|]]",
"This is the third message!"]
The complete snippet is here.
If we want to be able to format text messages we could use a l_ist_ data type to hold QML Text elements:
property list<Item> someMessages:[Text{text:"First message";color:"white"},
Text{text:"Second Message";color:"red";font{bold:true}},
Text{text:"Third message";font{italic:true;pixelSize:16}}
]
The complete code for this use case is here.
As the both variant and list containers just hold data we need a solution for rendering them. The process has two steps: firstly we get the data and then render them. The proposed solution is to use a QML loader and it is illustrated in the diagram bellow.
The loader – id:light – loads a template VirtualText.qml. It is a QML component file. The template is an empty structure and contains a QML Text element that corresponds to QML Text elements of the variant (list) containers. The goal of the template is to render Text elements stored in containers. Firstly the loader light loads and renders the empty template:
light.source="VirtualText.qml";
After loading, the text property of the loaded item is changed to text property of corresponding element in the variant (list) container:
light.item.accessText=someMessages[counter]
The property counter serves as container index and a loop is organized to accept several clicks. The property accessText defined in VirtualText.qml grants access to text property of virtual Text element.
The demo snippets play as follows – see screenshots bellow. We have two areas on the screen. One where the texts are displayed and another which accepts mouse clicks. The first mouse click displays the first container text element and so on. After the list is exhausted the program quits.
QML Containers and Images
This use case is very similar to the previous one about text messages. Now the container should hold images and its elements are QML Image elements:
property list<Item> someImages:[Image{id:first;source:"balls.jpg"},
Image{id:second;source:"funny_balls.jpg"},
Image{id:third;source:"four_balls.jpg"}]
The same techniques as in the previous section are used to define a template (VirtualImage.qml) and a loader, which loads images sequentially. Bellow you see some screenshots.
The functionality that is implemented is similar to "Loader and Inline Component" use case analyzed here.
The full code is available here.
QML Containers and JavaScript Objects
The QML variant type container could have elements, which are JavaScript objects defined in an external JavaScript file. Suppose we have the following JavaScript file:
jsfile.js
var externalObject="External JS Object"
var array=[1,2,3]
function func() {
console.log("The function 'func()'is invoked")
return "Returned value from function 'func()'"
}
function funSecond(){
return "Returned value from function 'funSecond()'"
}
Then we may have the following variant type container definition in the main QML file:
property variant jsVariable:[Extended.externalObject,Extended.func(),
Extended.array,Extended.funSecond()]
To test the above constructs we define a rectangle in the main file that forms a script block:
Rectangle{id:dummy
Component.onCompleted:{
console.log("Value of 'externalObject' variable: ",jsVariable[0])
console.log("Function 'func()' is called: ",jsVariable[1])
var pix=jsVariable[1]
var funSecondValue;
console.log("Value of 'pix' variable that is return value of 'func()': ",pix)
//Here we have an array of arrays
console.log("Value of the second element of 'array' object: ",jsVariable[2][1])
//We have an array of functions and could decide which one to call
if(test==true){
funSecondValue=jsVariable[3];
console.log(funSecondValue);
}
}
}
The discussed constructs are similar to ones in other programming languages and known as "array of functions", 'array of arrays".
The complete example could be downloaded here.
QML Containers and Components
We are considering the situation where QML component files are used. For simplicity suppose we have two components defined as files ComponentTest1.qml and ComponentTest2.qml.
The question now is: could we arrange these components in a list or variant type container? The answer is positive one and we have two solutions.
Firstly, let us consider the following definition:
property list<Item> componentList:[ComponentTest1{},ComponentTest2{}]
We see that elements of the componentList container are instances of the components – curly brackets are applied. The components are accessible through indexing and for example
componentList[0].width
gives the value of the width property of the ComponentTest1.
Secondly, we may use variant type to store the names of components like this:
property variant componentUrl:["ComponentTest1.qml", "ComponentTest2.qml"]
Further, a loader could be used to load elements of the componentUrl container:
Loader{id:light; source: componentUrl[0]}
The complete snippet is here.
Note: All code snippets analyzed above are included in a downloadable package, which contains:
Subdirectory - - Files
listContainer - - Listcontainer.qml
listContainerSet - - listContainerProperty.qml, listContainerSet.qml
containerText - - containerText.qml, VirtualText.qml
containerFormattedText - - containerFormattedText.qml, VirtualText.qml
containerImage - - containerImage.qml, VirtualImage.qml, balls.jpg, four_balls.jpg, funny_balls.jpg
containerJavaScript - - containerJavaScript.qml, jsfile.js
containerComponent - - containerComponent.qml, ComponentTest1.qml, ComponentTest2.qml