There’s many times that you may want to access a C++ object from your QML. One obvious example is if you’ve already written a data model in C++; you can leverage that model with QML-based user interface. Any other time you need to access things you can only touch from C++, like hardware integration not provided by Qt Mobility, is another example.
As a simple example, consider a case where you want to expose a C++-based application controller (think model-view-controller) to your QML. You’ve got a class, AppController
, with some Qt metaobject invocable methods, either expressed as properties, slots or using Q_INVOKABLE
. Mine looks like this:
class AppController : public QObject { Q_OBJECT Q_PROPERTY(int width READ width NOTIFY sizeChanged) Q_PROPERTY(int height READ height NOTIFY sizeChanged) public: explicit AppController(QObject *parent = 0); void setSize(const QSize& size) { mSize = size; emit sizeChanged(); }; int width() { return mSize.width(); }; int height() { return mSize.height(); }; Q_INVOKABLE void magicInvocation(); signals: void sizeChanged(); private: QSize mSize; };
Because QML binds using Qt’s metaobject system, the C++ object you want to expose to QML must be a descendant of QObject
. Our AppController
class is pretty simple; it’s just carrying the size of the window displaying the QML view, along with some C++ method my QML invokes named magicInvocation
. (If I told you what it did… you get the idea.)
Of course, we need to fire up a QML viewer with our QML, and add an instance of AppController
to the object hierarchy in the QML engine. I do that in my application’s main
, which looks like this:
int main(int argc, char *argv[]) { QApplication app(argc, argv); QRect screen = QApplication::desktop()->screenGeometry(); QMainWindow* window = new QMainWindow(0, Qt::FramelessWindowHint ); QDeclarativeView* view = new QDeclarativeView(window); AppController* controller(window); // The only thing we show is the declarative view. window->setCentralWidget(&view); // Size the window to be as big as it can be, except we don't // want it too big on our dev workstations. if (screen.width() <= kDesiredWidth) { window->showMaximized(); } else { window->setGeometry(QRect( (screen.width()-kDesiredWidth)/2, (screen.height()-kDesiredHeight)/2, kDesiredWidth, kDesiredHeight)); window->show(); } controller.setSize(window.size()); // Proxy in our app controller so QML get its properties and show our QML view->rootContext()->setContextProperty("controller", controller); view->setSource(QUrl("qml/main.qml")); int result = app.exec(); delete window; return result; }
Pretty basic stuff here:
- I get the screen size, used by the
AppController
for its own nefarious purposes. - I create a full-screen main window with no window chrome.
- I create a
QDeclarativeView
to display the QML, and make the main window’s main widget the newQDeclarativeView
. - I create an instance of
AppController
. - I do some funny stuff with the main window’s size so I don’t go crazy working on my desktop’s 22″ monitor, restricting the maximum possible size of the main window for test purposes.
- Using the
QDeclarativeView
‘sQDeclarativeEngine
, I add theAppController
instance to the QML context, giving it the namecontroller
. - I set the initial source of the QML to the QML entry point for my user interface, included as a file in my application’s package (not as a resource, but you could also choose to package it as a Qt application resource if you want.)
- Finally, I pass control to
QApplication
‘s main loop, and return its result code when its event loop exits.
The magic is QDeclarativeEngine::setContextProperty
, which binds a QObject
-derived instance to a specific name in the QML context. Once I do this, in my QML I can access this just as I would any QML or JavaScript object; its name is controller
. So I might write controller.magicInvocation()
to invoke my magic function in an onPressed
signal handler, for instance.
(This is well-documented, but I found it handy to break this point out into a separate example to refer to. It’s also a predecessor for several upcoming posts, so it’s here so that those posts can refer back to this one.)