Within the last week, the Qt Declarative module was merged into Qt trunk. It will be released in Qt 4.7, but there are also ways to make it work on Qt 4.6, so it’s a good time to experiment.
Here’s my contribution, An address book written with QML using data retrieved from Akonadi, shared with KAddressbook.
The design of the QML app is just a reworked version of one of the QML demos. Like all of the other demos, it is designed with a small form factor device in mind, such as a phone. The tree of collections on the left side would likely not be in a real phone-based app, but will be replaced with something that I have not written yet.
This is a nice demo though of the kind of code reuse available using Akonadi and model/view, and the kinds of flexibility and innovation that opens up as a result. QML brings all of the must-have features like flickable scrolling and easy, smooth animations.
Using QAbstractItemModel in QML
QML has some api for model/view like constructs. One of the most useful when you want to reuse existing components is the ability to put a QAbstractItemModel into the QML world and access the data through the interface.
That is exactly what is done in KAddressbook and the other Akonadi powered PIM applications, using the EntityTreeModel. That large chunk of code can be simply reused and injected into a QML application. Additionally, proxy models can be used to manipulate the data before injecting it into the qml view, just like in “traditional” model/view. The use of KSelectionProxyModel allows me to cause a selection in the tree to change the content of the qml view.
Note that in order to use proxy models with qml, you need this patch to QAbstractProxyModel which will hopefully be available in the next version of Qt 4.6.
Getting the model into the QML view is only part of the challenge. Getting the data out of the model is the next trick. In the QML source of that demo, the name and email are filled in with calls like “model.data.name” and model.data.email”. “model” in that context is the EntityTreeModel I injected into the QML view, and so the syntax looks analogous to something like
QAbstractItemModel *model = getModel(); model->data(NameRole); model->data(EmailRole);
There is a little bit of magic going on to make that possible.
One of the most flexible parts of the QAbatractItemModel API (and the most open to abuse with dirty hacks :) ) is the system for custom roles. The data() method can be implemented to return a custom object when given a custom role, usually defined in an enum somewhere. The returned object can be anything that can be inserted into a QVariant, including most relevantly in my case, QObject*.
For the QML role called “data” the model returns a QObject subclass which defines some Q_PROPERTIES of what it wants to expose to QML. The ones I have implemented are name, email, phone, image and address, so through the magic of QMetaObject introspection, QML turns model.data.name into the display name for each element.