How to create dynamic QML component by XML
This article explains how to create dynamic QML component by XML.
By now we develop software according to a DDU mode, i.e: Develop-Deploy-Upgrade. When Software has been deployed, if there are upgrades, we often go to internet and download upgrade packages and then upgrade the software. The UIs are often limited to some pages. With the progress of IT technique, people would think if there is a way to avoid upgrading or we just only upgrade in servers, when people look through the information, the client UI will change by the server design, That is the idea I share with you and I will demonstrate in Qt/QML Meego development.
Introduction
One day I take part in a team to develop the CMB-meego project. I am responsible for the film ticket buying module. When I accept the task. I think at once out to realize it using XmlListModel plus PathView technique to finish it. but when I would complete it, the customer came and said it's not their wish, the Page is limited, when they want to change the style,the page won't change at all, I write the page like below:
import QtQuick 1.0
import QtWebKit 1.0
Rectangle
{
id: film
width: 100
height: 62
Component {
id: delegate
Column {
Image {
anchors.horizontalCenter:
name.horizontalCenter;
width: 120; height: 120;
source: src
}
Text { text: title; font.pointSize: 16 }
}
}
PathView {
anchors.fill: parent
model:zhaohang_xmlModel
delegate: delegate
path: Path {
startX: 120; startY: 100
PathQuad { x: 120; y: 25; controlX: 260; controlY: 75 }
PathQuad { x: 120; y: 100; controlX: -20; controlY: 75 }
}
}
XmlListModel {
id: zhaohang_xmlModel
source: http//q.mobi:8080/movie/m/gm.p?
query: "/page/vbox/cover/item"
XmlRole { name: "title"; query: "@title/string()" }
XmlRole { name: "src"; query: "@pic/string()" }
XmlRole { name: "subtitle"; query: "@subtitle/string()" }
XmlRole { name: "request"; query: "@url/string()" }
}
}
We also discuss using webbrowser component. The webbrowser component can't supply increasing user experiences. So if there is a way we define the style and model in server, produce xml document, then we parse the document in client and reproduce the user interface. this is the subject I write today. I name it "How to create dynamic QML component by XML".
Solution
Article Metadata
Tested with
Platform Security
Article
We define the XML document as below:
<page>
<header>
<hbox class="cmb2_header1">
<image class="image" src="cmb_icon_3.png" align="center" />
<image class="cmb2_image1" src="cmb2_favorite.png" hover="cmb2_favorites.png">
<request page="http//q.mobi:8080/zhaohang-widget-new2/normal.do" params="method=shoucang1" />
</image>
</hbox>
</header>
<vbox>
<vbox class="vbox2">
<gridbox>
<image src="girdbox_icon_19.png" hover="girdbox_icon_19a.png">
<request page="http//q.mobi:8080/zhaohang-widget-new2/normal.do" params="method=yp" />
</image>
<image src="girdbox_icon_1.png" hover="girdbox_icon_1a.png">
<request page="http//q.mobi:8080/zhaohang-widget-new2/normal.do" params="method=tehui1" />
</image>
...
</gridbox>
</vbox>
</page>
In this XML document, we define about ten kinds of element, they are page、header、 hbox、vbox---they represent the container in Qt development,and class represent the css style as html,it define like "cmb2_header1{background:url(:/cmbImages/tube/header_bg3.png)" ,image as a Image in QML, src as source and request as a singal when you click on the image. params as parameter passby etc. How can we parse this XML document and reunion as a QML page, I study the QML structure and find it is combined with components such as Rectangle、BorderImage、Image、Text etc and layouted by Row、Flow、Column etc. and we have an Item component, it can load a page like below:
Item {
height: 60
width: 160
Loader {
anchors.fill: parent
source:"c:/test.qml"
}
}
Especially,every component is started with "{" and end with "}", these are I think about and I find a kind of solution. We parse the XML document, when we encounter a container tag such as page、hbox、vbox、gridbox,we product a new page. we use Item to load in its parent page. in pages we have the layout, for example hbox has Row, vbox has Column and gridbox has Flow etc Now let's see how to produced.
PageQML is class to produce a new page, it has construct function and destructor function, it will produce a page?.qml in the directory passed by strDir parameter, pageNumber is a static int type, I use it to produce defferent page's id and the page's number replace the "?" above.
#include "pageqml.h"
PageQML::PageQML(QString strDir,QString type)
{
this->strDir = strDir;
this->type = type;
//Produce a new page
file.setFileName(strDir+"/page"+ ++pageNumber+".qml");
QDir(strDir).mkdir(strDir);
if (!file.open(QIODevice::WriteOnly)) {
qDebug(strDir.toAscii().data());
qDebug("Cannot open file for writing: page.qml ");
}
else
{
QTextStream out(&file);
out<<"import QtQuick 1.0"<<endl;
out<<"Rectangle {"<<endl;
out<<"id:page"<<pageNumber<<endl;
out<<"width: 160"<<endl;
out<<"height: 40"<<endl;
out<<"BorderImage {"<<endl;
out<<"anchors.fill: parent"<<endl;
}
}
PageQML::~PageQML()
{
this->strDir = strDir;
QTextStream out(&file);
out<<"}\n"<<endl;//BorderImage
out<<"}\n"<<endl;//Page
file.close();
}
int PageQML::pageNumber=0;
In ParseXml class we parse XML string like this
bool ParseXml::XmlAnalyzer(QString strData,QString strDir)
{
PageQML super_p(strDir,"first");
super_p.setVboxLayout();
...
QString temp("initial");
if ( node.nodeName() == "header" )
{
super_p.addItem();
//add an Item in first page
PageQML p(strDir+"/header","header");//make a new director header and produce a new page
ParseContainerNode(headerChildNode,p);
..
}
void ParseXml::ParseContainerNode(QDomNode containerNode,PageQML &p)
{
if(containerNode.nodeName() == "vbox")
{
p.addItem();
PageQML pbox(p.strDir+"/vbox","vbox");
....
p.closeItem(p.strDir+"/vbox/page"+PageQML::pageNumber+".qml")
}
////////////////////////////////////
void PageQML::closeItem(QString source)
{
QTextStream out(&file);
QString temp("source: \""+source+"\"");
out<<temp<<endl;
out<<"}"<<endl;
out<<"}"<<endl;
}
Result
Does this works?, Let's see the directory produced.
D:/QT/MerchantsBank_Qml-build-simulator/debug/page1.qml
D:/QT/MerchantsBank_Qml-build-simulator/debug/page2.qml
D:/QT/MerchantsBank_Qml-build-simulator/debug/header/page3.qml
D:/QT/MerchantsBank_Qml-build-simulator/debug/header/hbox/page4.qml
D:/QT/MerchantsBank_Qml-build-simulator/debug/vbox/page5.qml
D:/QT/MerchantsBank_Qml-build-simulator/debug/vbox/vbox/page6.qml
D:/QT/MerchantsBank_Qml-build-simulator/debug/vbox/vbox/vbox/page7.qml
D:/QT/MerchantsBank_Qml-build-simulator/debug/vbox/vbox/gridbox/page8.qml
Page1.qml
import QtQuick 1.0
Rectangle {
id:page1
width: 160
height: 40
BorderImage {
anchors.fill: parent
Column
{
anchors.fill: parent
Item {
height: 60
anchors.right: parent.right
anchors.rightMargin: 0
anchors.left: parent.left
anchors.leftMargin: 0
Loader {
anchors.fill: parent
source: "D:/QT/MerchantsBank_Qml-build-simulator/debug/header/page2.qml"
}
}
Item {
height: 60
anchors.right: parent.right
anchors.rightMargin: 0
anchors.left: parent.left
anchors.leftMargin: 0
Loader {
anchors.fill: parent
source: "D:/QT/MerchantsBank_Qml-build-simulator/debug/vbox/page4.qml"
}
}
}
}
}
Page7.qml
import QtQuick 1.0
Rectangle {
id:page7
width: 160
height: 40
BorderImage {
anchors.fill: parent
Flow
{
anchors.fill: parent
BorderImage {
source:"D:/QT/MerchantsBank_Qml-build-simulator/debug/cmbImages/tube/girdbox_left_bg_shu.png"
Image {
source:"D:/QT/MerchantsBank_Qml-build-simulator/debug/cmbImages/tube/girdbox_icon_19.png"
}
}
BorderImage {
source:"D:/QT/MerchantsBank_Qml-build-simulator/debug/cmbImages/tube/girdbox_left_bg_shu.png"
Image {
source:"D:/QT/MerchantsBank_Qml-build-simulator/debug/cmbImages/tube/girdbox_icon_1.png"
}
}
BorderImage {
source:"D:/QT/MerchantsBank_Qml-build-simulator/debug/cmbImages/tube/girdbox_left_bg_shu.png"
Image {
source:"D:/QT/MerchantsBank_Qml-build-simulator/debug/cmbImages/tube/girdbox_icon_4.png"
}
}
BorderImage {
source:"D:/QT/MerchantsBank_Qml-build-simulator/debug/cmbImages/tube/girdbox_left_bg_shu.png"
Image {
source:"D:/QT/MerchantsBank_Qml-build-simulator/debug/cmbImages/tube/girdbox_icon_9.png"
}
}
BorderImage {
source:"D:/QT/MerchantsBank_Qml-build-simulator/debug/cmbImages/tube/girdbox_left_bg_shu.png"
Image {
source:"D:/QT/MerchantsBank_Qml-build-simulator/debug/cmbImages/tube/girdbox_icon_3.png"
}
}
BorderImage {
source:"D:/QT/MerchantsBank_Qml-build-simulator/debug/cmbImages/tube/girdbox_left_bg_shu.png"
Image {
source:"D:/QT/MerchantsBank_Qml-build-simulator/debug/cmbImages/tube/girdbox_icon_8.png"
}
}
BorderImage {
source:"D:/QT/MerchantsBank_Qml-build-simulator/debug/cmbImages/tube/girdbox_left_bg_shu.png"
Image {
source:"D:/QT/MerchantsBank_Qml-build-simulator/debug/cmbImages/tube/girdbox_icon_10.png"
}
}
BorderImage {
source:"D:/QT/MerchantsBank_Qml-build-simulator/debug/cmbImages/tube/girdbox_left_bg_shu.png"
Image {
source:"D:/QT/MerchantsBank_Qml-build-simulator/debug/cmbImages/tube/girdbox_icon_18.png"
}
}
BorderImage {
source:"D:/QT/MerchantsBank_Qml-build-simulator/debug/cmbImages/tube/girdbox_left_bg_shu.png"
Image {
source:"D:/QT/MerchantsBank_Qml-build-simulator/debug/cmbImages/tube/cmb2_shoujicaip.png"
}
}
}
}
}


Hamishwillee - Why not just load the qml directly?
I like this article, and I'm sure the idea will be useful.
One question though, why not just load the QML as QML directly from the server?hamishwillee 08:49, 22 August 2011 (EEST)
Zebinluo - Re: Load qml from server directly
If you load the QML from the server directly, Maybe you will have to download the pictures/images as well, This method will increase network flow amount. because the internet url cannot match the local drive diretory,the images are located on local drive.zebinluo 05:31, 23 August 2011 (EEST)
Croozeus -
Hi Zebinluo,
Interesting article, however I am not sure if I understand your above comment- if the qml file is downloaded directly from the web, you can get rid of the XML parsing. No? As you said the images are on the the local drive, so the qml can have the local path for images.
Perhaps you can also give it a try with qml files and publish your findings.
//Pankaj.croozeus 19:29, 25 August 2011 (EEST)