Monday, March 5, 2012

ListView in QML

The ListView is similar to ListViews in other programming languages - it displays a list of items! There is some difference, of course, in the way it is implemented. The data to be displayed is called a model, and if it is some fixed data then it can be defined right in the code by adding a ListModel element like this:


ListModel {
ListElement {
name: "Joe Bloggs"
number: "123 1234"
}
}

That will be a list with just one element. Next, there is the delegate. The delegate defines the way data is displayed. Again, it can be very simple, for example

ListView{
id: listView

...

delegate: Text{text: name}
}

Such delegate will just display some text which is taken from the model.

To make this post slightly less boring, I will first make the ListView read its model from the xml file. For that purpose, the XmlListModel element is used. I will use this sample XML structure:

<?xml version="1.0" encoding="utf-8"?>
<listModel>
<item>
<name>Item One</name>
<size>Medium</size>
<desc>Item One Detailed Description</desc>
</item>
<item>
<name>Item Two</name>
<size>Large</size>
<desc>Item Three Detailed Description</desc>
</item>
<item>
<name>Item Three</name>
<size>Small</size>
<desc>Item Three Detailed Description</desc>
</item>
<item>
<name>Item Four</name>
<size>Small</size>
<desc>Item Four Detailed Description</desc>
</item>
<item>
<name>Item Five</name>
<size>Small</size>
<desc>Item Five Detailed Description</desc>
</item>
</listModel>

The XMLListModel that reads this file will be defined as follows:

XmlListModel{
id: xmlTestListModel
source: "listModel.xml"
query: "/listModel/item"
XmlRole{name: "name"; query: "name/string()" }
XmlRole{name: "size"; query: "size/string()" }
XmlRole{name: "desc"; query: "desc/string()" }
}

I give the element the location where to look for the file, and the XPath to look for individual items. Next, I define all the tags I want to read into the model - in this case, name, size and description, but there can be more. The model will parse the XML file and read all this information, and if I want to use it or not - it's completely up to me. Next, I'll do something with the delegate to show that its activity is not limited to displaying lines of text, it is capable of more complex behaviour. Before that, a quick note about the section property of the ListView: it allows the list to be separated into different parts, and the sections can have a delegate specified. Additionally, QML allows breaking the code into multiple files. I will add the file called SectionHeading.qml and specify the delegate for the section in the following manner:

import QtQuick 1.0

Component {
id: sectionHeading
Rectangle {
width: listViewRect.width
height: childrenRect.height
color: "lightsteelblue"

Text {
text: section
font.bold: true
}
}
}

This delegate displays the section as the rectangle with a background colour, and displays the text that is defined in the variable section, in bold. To reference this delegate I can use the QML file name: section.delegate: SectionHeading{}. At this point the "desc" property from the XML file is not used anywhere - but the XmlListModel already knows it and I can use it later. Here is the full code after making all these modifications:

import QtQuick 1.0

Rectangle{

id: main
color: "lightGrey"
property int rectWidth: 480
property int rectHeight: 480

width: rectWidth
height: rectHeight

Rectangle{
id: listViewPanel
width: rectWidth/3
height: rectHeight
x: 0
y: 0
border.color: "black"
color: "green"
border.width: 1

Text{
text: "Left panel"
}

Rectangle{
id: listViewRect
state: "list"
anchors.fill: parent
width: parent.width
height: parent.height
anchors.margins: 15

XmlListModel{
id: xmlTestListModel
source: "listModel.xml"
query: "/listModel/item"
XmlRole{name: "name"; query: "name/string()" }
XmlRole{name: "size"; query: "size/string()" }
XmlRole{name: "desc"; query: "desc/string()" }
}

ListView{
id: listView

width: parent.width
height:parent.height
anchors.top: parent.top
anchors.bottom: parent.bottom

model: xmlTestListModel
delegate: Text{text: name}

section.property: "size"
section.criteria: ViewSection.FullString
section.delegate: SectionHeading{}
}
}
}

Rectangle{
id: detailsPanel
width: parent.width*2/3
height: parent.height*2/3
x: parent.width/3
y: 0
border.color: "black"
color: "yellow"
border.width: 1

Text{
text: "Details panel"
}
}

Rectangle{
id: loggingPanel
width: parent.width*2/3
height: parent.height/3
border.color: "black"
border.width: 1
x: parent.width/3
y: parent.height*2/3
color: "blue"

Text{
text: "Logging panel"
}
}
}

The ListView is drawn inside a Rectangle. The XmlListModel then parses the XML file specified. The delegate for the ListView only displays the "name" property from the XML file for each item. The "size" property is assigned to the section. It is passed to the section.delegate as a parameter, which then displays it in bold, inside a Rectangle which has lightsteelblue background colour. The end result of the application is displayed below.

The Example Application

References:

QML ListView Element
QML XmlListModel Element
Models and Views: Sections ListView Example
by . Also posted on my website

No comments: