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
AppControllerfor its own nefarious purposes. - I create a full-screen main window with no window chrome.
- I create a
QDeclarativeViewto 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 theAppControllerinstance 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.)