Context Properties
So far I have been playing mainly with the QML code. But what if I want to write my main application in C++ and use the QML part for the user interface only? It think it is a common (and recommended) practice to keep the user interface and application code (“business logic”) separate.
So I would like to structure my applications as shown below. The QML part defines the user interface that is shown to the user. The C++ part contains the main function and all the classes that ultimately define the application logic. The QML code should invoke the C++ methods based on the user’s selections.
So how do we make connections between the C++ side and the QML side? There are more than one way to accomplish that. Maybe one of the easiest ways is using so called context properties. These are basically just normal variables in the C++ side that are made visible to the QML side. The property could be a C++ object variable which would make it possible to call the object methods from QML.
To start using the context properties we would first need to include the relevant header file in the C++ source file. The header file is called QDeclarativeContext.
#include <QtGui/QApplication> #include <QtDeclarative/QDeclarativeContext> #include "qmlapplicationviewer.h" Q_DECL_EXPORT int main(int argc, char *argv[]) { QScopedPointer<QApplication> app(createApplication(argc, argv)); QmlApplicationViewer viewer; viewer.setOrientation(QmlApplicationViewer::ScreenOrientationAuto); viewer.setMainQmlFile(QLatin1String("qml/SimpleQt/main.qml")); viewer.showExpanded(); return app->exec(); }
As a simple example let’s try to recreate the TestApp5:
https://n9.dy.fi/2014/01/experimenting-with-the-listview/
In the original application we defined some icon names as QML properties. Using the context properties we could replace the QML properties with C++ variables.
Here’s the updated main.cpp file:
#include <QtGui/QApplication> #include <QtDeclarative/QDeclarativeContext> #include "qmlapplicationviewer.h" Q_DECL_EXPORT int main(int argc, char *argv[]) { QScopedPointer<QApplication> app(createApplication(argc, argv)); QString iconPrefix = "icon-m-messaging-smiley-"; QString iconNames = "angry;cool;cry;evil;grin;happy;heart;kiss;lips-sealed;sad;sarcastic;skeptical;sleepy;surprised;tongue;very-happy;wink;worried"; QString iconSelected = "angry"; QStringList iconList = iconNames.split(";"); QmlApplicationViewer viewer; // This is equivalent to QML: property string iconPrefix: "icon-m-messaging-smiley-" viewer.rootContext()->setContextProperty("iconPrefix",iconPrefix); viewer.rootContext()->setContextProperty("iconDefault",iconSelected); viewer.rootContext()->setContextProperty("iconList",iconList); viewer.setOrientation(QmlApplicationViewer::ScreenOrientationAuto); viewer.setMainQmlFile(QLatin1String("qml/TestApp7/main.qml")); viewer.showExpanded(); return app->exec(); }
Here we are attaching our variables to the rootContext by calling viewer.rootContext()->setContextProperty() method. It is possible to create many different contexts in a context hierachy but for simple applications the rootContext should be enough. The setContextProperty() method takes two arguments: the QML property name and the C++ variable which is mapped to this property. Simple.
The main.qml file is basically a stripped down version from the TestApp5 code as we do not need to define all the QML properties anymore. We can refer to our context properties (like iconDefault) by entering the property name as defined in the setContextProperty() method call.
We will only keep the iconSelected property as we want to update it from our ListPage. Let’s just initialize it from our context property iconDefault.
import QtQuick 1.1 import com.nokia.meego 1.0 PageStackWindow { id: appWindow property string iconSelected: iconDefault property int leftMargin: 32 initialPage: listPage ListPage { id: listPage } IconPage { id: iconPage } ToolBarLayout { id: commonTools visible: true ToolIcon { platformIconId: "toolbar-view-menu" anchors.right: (parent === undefined) ? undefined : parent.right onClicked: (myMenu.status === DialogStatus.Closed) ? myMenu.open() : myMenu.close() } } Menu { id: myMenu visualParent: pageStack MenuLayout { MenuItem { text: qsTr("Sample menu item") } } } }
The ListPage.qml code can be reused as such. Note that the ListView model attribute now refers to our context property called iconList.
import QtQuick 1.1 import com.nokia.meego 1.0 Page { tools: commonTools Label { id:title text: "Please select an icon" font.pixelSize: 40 anchors.left: parent.left; anchors.leftMargin: leftMargin } Rectangle { id: background anchors.top: title.bottom height: parent.height-title.height; width: parent.width } ListView { id: listView model: iconList delegate: listDelegate anchors.fill: background clip: true } Component { id: listDelegate Text { text: modelData font.pixelSize: 40 color: "steelblue" anchors.left: parent.left; anchors.leftMargin: leftMargin MouseArea { anchors.fill: parent onClicked: { iconSelected=modelData pageStack.push(iconPage) } } } } }
Also the IconPage remains unchanged. We are just referring to the context properties instead of QML properties.
import QtQuick 1.1 import com.nokia.meego 1.0 Page { Item { id: item anchors.centerIn: parent width: parent.width; height: 100 Image { anchors.centerIn: parent source: "image://theme/"+iconPrefix+iconSelected } } Label { id:label anchors.top: item.bottom anchors.horizontalCenter: parent.horizontalCenter text: iconSelected } tools: ToolBarLayout { id: iconTools visible: true ToolIcon { platformIconId: "toolbar-back" anchors.left: parent.left onClicked: pageStack.pop() } } }
If you run the application you will see that it behaves exactly like TestApp5.
I’ll push the context property demonstration to GitHub as TestApp7:
https://github.com/n9dyfi/TestApp7.git