Tuesday, November 1, 2011

QmlApplicationViewer and setContextProperty

Qt SDK 1.1.3 application wizard creates a nice stub for starting the application, QmlApplicationViewer. It takes care of some of the differences in starting the application in different environments like Desktop, Symbian and Harmattan. It also handles Harmattan booster for improving application start-up time and sets-up QML debugging.

So using it, and even transferring an existing application to using it, is worth to do.

Unfortunately, the stub QmlApplicationViewer created by QtCreator does not work correctly in Harmattan if you want to set context properties to access C++ functionality from QML. Here's how I got it working without breaking the application in Desktop and Harmattan environments.

First of all, here's the basic skeleton program created by QtCreator where I want "myContextProperty" to be accessible from QML:

main.cpp:

#include <QtGui/QApplication>
#include <QtDeclarative>

#include "qmlapplicationviewer.h"

Q_DECL_EXPORT int main(int argc, char *argv[])
{
    QScopedPointer<QApplication> 
       app(createApplication(argc, argv));
    QScopedPointer<QmlApplicationViewer> 
       viewer(QmlApplicationViewer::create());

    // Here you create "myContextProperty" that 
    // can be assigned as context property for QML.
    // See Qt documents or my earlier postings 
    // for examples.
    // 
    // After that, assign it as context property:

    QDeclarativeContext *ctxt = viewer->rootContext();
    ctxt->setContextProperty("myContextProperty", 
                             &myContextProperty);

    viewer->setOrientation(QmlApplicationViewer::ScreenOrientationAuto);
    viewer->setMainQmlFile(QLatin1String("qml/main.qml"));
    viewer->showExpanded();

    return app->exec();
}
This is how it should work out of the box. But not with the QmlApplicationViewer created by the current QtQuick application wizard. You can see the problem yourself from the source of QmlApplicationViewer, but the basic gist of the issue is that for Harmattan the QmlApplicationViewer::create() constructs a view from MDeclarativeCache, whose root context can not be accessed from QmlApplicationViewer. So above, you are setting a context properties for something that's not really used in QML.

To fix this, we add a function to QmlApplicationViewer to access it.

qmlapplicationviewer/qmlapplicationviewer.h:

public:
    QDeclarativeContext *rootContext();

qmlapplicationviewer/qmlapplicationviewer.cpp:

QDeclarativeContext *QmlApplicationViewer::rootContext()
{
    return d->view->rootContext();
}
Note that since qmlapplicationviewer is created by the wizard, the comments in it quite correctly warn against modifying. So when updating the SDK or QtCreator, check if some automatic updates happened add/remove above code if necessary.

One more thing... the working of the new qmlapplicationviewer also depends on updating the project file. There's some nice things anyway compared to the previous SDK, so well worth the effort in my opinion.

9 comments:

  1. Right, so it seems there is more methods that need to be accessible. In that case it'd probably be better to just make an accessor for for the underlying d->view in the class. Less modifications to the class that way.

    ReplyDelete
  2. Great job, thanks for the suggestion! Now also formally reported:
    https://bugreports.qt.nokia.com/browse/QTCREATORBUG-6490

    ReplyDelete
  3. Thank you, useful info. I come from desktop Qt, which has always been excellent. I'm really underwhelmed by the lack of polish in the mobile SDK.

    ReplyDelete
  4. Thank you so much.
    It save me from trouble.

    ReplyDelete
  5. This comment has been removed by the author.

    ReplyDelete
  6. C:\QtSDK\...\qmlapplicationviewer\qmlapplicationviewer.cpp:178: error: 'class QmlApplicationViewerPrivate' has no member named 'view'

    ReplyDelete
  7. The same here. What is the status of this bug and what is a soluton?

    ReplyDelete
    Replies
    1. With newest QtSDK you don't anymore need this workaround. Just recreate the copy new qmlapplicationviewer.cpp/.h to your project.

      Delete