[SOLVED] QML with nested Repeaters, created dynamically
-
I am trying to use QML to create some reports driven by an xml data file, which looks like this:
@<xml>
<Customer name="Fred">
<Invoice date="1/1/2015" status="Paid">
<Product category="Shirt" price="10"/>
<Product category="Pants" price="20"/>
</Invoice>
<Invoice date="1/2/2015" status="Pending">
<Product category="Socks" price="5"/>
<Product category="Pants" price="15"/>
</Invoice>
</Customer>
</xml>
@The resulting report should effectively use something like this:
@import QtQuick 2.1
import QtQuick.XmlListModel 2.0Column {
Repeater {
model: XmlListModel {
id: model1
source: "data.xml"
query: "/xml/Customer"
XmlRole { name: "name"; query: "@name/string()" }
}
delegate: Column {
Text { text: name; color: "orange" }
Repeater {
model: XmlListModel {
id: model2
source: model1.source
query: model1.query + "[" + (index + 1) + "]" + "/Invoice"
XmlRole { name: "date"; query: "@date/string()" }
XmlRole { name: "status"; query: "@status/string()" }
}
delegate: Column {
Row {
Text { text: date; color: "red" }
Text { text: " - " + status; color: "green" }
}
Repeater {
model: XmlListModel {
id: model3
source: model2.source
query: model2.query + "[" + (index + 1) + "]" + "/Product"
XmlRole { name: "name"; query: "@category/string()" }
XmlRole { name: "price"; query: "@price/number()" }
}
delegate: Column {
Row {
Text { text: name }
Text { text: " = $" + price; color: "purple" }
}
}
}
}
}
}
}
}
@This example QML document uses three nested repeaters to dig into the hierarchical xml data and produce the expected output. To take it a step further, I'd like to be able to use a different order in the xml data. Whereas this example is Customer/Invoice/Product, I'd like to be able to reorder it into Product/Invoice, with different orders of elements and different counts of elements. The xml source data would be rearranged to fit the expected report output, of course. Eventually it should handle any number of elements without limits.
What I've tried to do is use QQuickItem::findChild(), but soon realized I can't get a handle into the Repeater delegates to be able to rearrange them.
I was able to break each repeater into a separate file, and do something like this successfully:
@view->setSource(QUrl("qrc:/reportbase.qml"));
QQuickItem* item = view->rootObject();
item->setProperty("source", fileName);//first iteration
QQmlEngine* engine = view->engine();
QQuickItem* col = item->findChild<QQuickItem*>("outerColumn");
QQmlContext* context = engine->contextForObject(col);
QQmlComponent component(engine, QUrl("qrc:/reportfragment.qml"));
QQuickItem* object = qobject_cast<QQuickItem*>(component.create(context));
object->setParentItem(col);QObject* model = object->findChild<QObject*>("xmlListModel");
model->setProperty("source", fileName);
model->setProperty("query", "/xml/Customer");QObject* role = model->findChild<QObject*>("roleName");
role->setProperty("query", "@name/string()");//second iteration
QQmlContext* context2 = engine->contextForObject(object);
QQmlComponent component2(engine, QUrl("qrc:/reportfragment.qml"));
QQuickItem* object2 = qobject_cast<QQuickItem*>(component2.create(context2));
object2->setParentItem(object);
@But produced a column of repeaters without the nesting. Has anybody done something similar to this? Or am I going about it all wrong?
-
I ended up passing a path string from the c++ side that looks like "/xml/Customer/Order/Product", parsing that in the QML side via JavaScript, and used the Qt.createQmlObject() method to do the nesting. It's not as ugly as I thought it might be, and I agree that it is nice keeping the display code contained in the QML side of things.