Mixed QML/C++ objects

One of the good things of QML is that you can have both C++ and QML code and interact between them. For example, from C++, you can access the QML tree and invoke methods, change properties, etc.

The other way around, you can also define C++ objects and interact with them from QML. Here you have two ways of doing it:

  • Instantiating from C++ and adding the objects to the QML context, meaning that you can invoke the public slots and access the properties.
  • Registering the QML type with qmlRegisterType and then instantiating it from QML.

At the Qt documentation you can find examples about how to implement the different approaches so I won’t talk too much about them and I’ll focus in a special case of the last approach.

Let’s see a QML code like this:

Item {
    id: myObject
    function doCoolStuff() {
        // doing really cool stuff here
    }
    Rectangle {
        anchors.fill: parent
        color: "red"
    }
}

Button {
    text: "Do cool stuff!"
    onClicked: myObject.doCoolStuff()
}

Imagine now that painting that rectangle is something that must be really done by your object, because it is needed and inherent to it, and so is the function. If the code is written only in QML, the answer is obvious, just move the whole Item code to a .qml file and leave the main code like this:

MyObject {
    id: myObject
}

Button {
    text: "Do cool stuff!"
    onClicked: myObject.doCoolStuff()
}

Let’s suppose that you need some C++ code in that object for whatever reason (you want to use some GNOME library, for instance). In this case you need to write it in C++ to define the public slots in that language. Our first step would be something like this:

#include <QtCore>
#include <QtDeclarative>

 class MyObject : public QDeclarativeItem
 {
     Q_OBJECT

// Define some Q_PROPERTY here with its methods

 public slots:
     void doCoolStuff(void);

// We could even define some signals
 };

For the slot, it does not matter if you declare it as slot or with Q_INVOKABLE because QML will see it both ways. Of course, don’t forget to write the cpp file with the implementation for the slot 😉 . And you also need to declare the QML type, for example in your main.cpp file:

#include <QtDeclarative>
#include "myobject.h"

int main(int argc, char *argv[])
{
    // ...
    qmlRegisterType("myapp", 1, 0, "myobject");
    //...
}

So far it is not different from what you can read at Defining new QML elements section of the Qt doc.

But what about painting the rectangle? We can do it with Qt code, but as we are lazy bastards and want to use QML for that, we have two options. One of them is forgetting about it in the C++ code and having something ugly like:

import myapp.myobject 1.0

MyObject {
    Rectangle {
        anchors.fill: parent
        color: "red"
    }
}

But come on, that rectangle is part of the object and nobody should write the Rectangle themselves. So we can take the better approach of moving the Rectangle to a different MyObject.qml file and loading it in the constructor of the C++ class:

#define QML_PATH "/path/to/the/qml/files/"

MyObject::MyObject(QDeclarativeItem *parent = 0) : QDeclarativeItem(parent)
{
    QDeclarativeEngine engine;
    QDeclarativeComponent component(&engine, QUrl::fromLocalFile(QML_PATH "MyObject.qml"));
    QDeclarativeItem *rect = dynamic_cast<QDeclarativeItem *>(component.create());
    rect->setParentItem(this);
}

Voilà.

2 comments

Leave a Reply

Your email address will not be published. Required fields are marked *

What is 12 + 5 ?
Please leave these two fields as-is:
IMPORTANT! To be able to proceed, you need to solve the following simple math (so we know that you are a human) :-)