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.

context-properties-03

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
1 2 3 4