Exchange Data and Objects between C++ and QML and vice versa

In this post I show how to exchange data and objects between C++ and QML and vice versa. I initially created a simple example from some old code due to a question on IRC. While I was at it I decided to go a little further and add some more examples and also write this short note.

In the following listing the QML part is shown. This should also serve as a little appetizer such that you see what expects you in this post if you decide to read on. 😉

import QtQuick 1.0
import test 1.0

Rectangle {
  width: 360
  height: 360
  Text {
    id: textField
    text: qsTr("Click me.")
    anchors.centerIn: parent
  }
  MouseArea {
    anchors.fill: parent
    onClicked: {
      textField.text = "Clicked...";

      // Passing objects from QML to C++
      b.doA(aa);
      b.doC(c);

      // Setting a property works as expected as well.
      aa.test = "foobar";
      b.doA(aa);
      b.doC(c);

      // Dynamic object creation in QML
      var newA = Qt.createQmlObject('import QtQuick 1.0; import test 1.0; A {test: "foo"}', parent);
      b.doA(newA);

      // Object creation in C++ and passing to QML
      var anotherA = b.makeA();
      console.log(anotherA.test);

      // Use an object created in C++ in QML.
      c.a = anotherA;
      b.doC(c);

      // Using QML lists
      console.log("");
      d.doVl();
      console.log("");
      b.doVl(d.vl);

      // It is also possible to pass dynamically created lists.
      console.log("");
      b.doVl(["j1", 2, 3, "j4"]);

      // See the code of b.h to see how to access the objects in the list from C++.
      console.log("");
      b.doAlist([aa, aaa]);

      // Create a list of objects in C++ and use it in QML.
      console.log("");
      d.vl = b.makeAList();
      console.log(d.vl[0].test);
      console.log(d.vl[1].test);
    }
  }
  A{id: aa; test: "bar"}
  A{id: aaa; test: "blah"}
  B{id: b}
  C{id: c; a: aa}
  D{id: d;  vl: ["i1", "i2", 3, 4, aa]}
}

The elements A, B, C, and D are created from custom classes inheriting from QObject. In the following the code for each class is shown in a separate listing.

Class A as shown in the following listing simply has a single property of type QString. This property is accessible via QML. That’s what Q_PROPERTY actually does.

#ifndef A_H
#define A_H

#include

class A : public QObject
{
  Q_OBJECT
  Q_PROPERTY(QString test READ test WRITE setTest NOTIFY testChanged)
public:
  explicit A(QObject *parent = 0) : QObject(parent){}

  QString test(){return myTest;}

  void setTest(QString t){
    myTest = t;
    testChanged(myTest);
  }

signals:
  void testChanged(QString t);

private:
  QString myTest;
};

#endif // A_H

Class B as shown in the following listing does not contain any data but only has some methods that will be called from the QML part. Consequently, these methods need to be declared as Q_INVOKABLE.

#ifndef B_H
#define B_H

#include
#include
#include "a.h"
#include "c.h"

class B : public QObject
{
  Q_OBJECT
public:
  explicit B(QObject *parent = 0) : QObject(parent){}

  Q_INVOKABLE void doA(A *a){
    qDebug() << "doing A: " << a->test();
  }

  Q_INVOKABLE void doC(C *c){
    qDebug() << "doing C: " << c->a()->test();
  }

  Q_INVOKABLE void doAlist(QVariantList vl){
    qDebug() << "Doing AList... ";
    for(int i = 0; i < vl.size(); i++){
      qDebug() << "vl.at(" << i << "): " << vl.at(i);
      // Get the actual object out of a QVariant.
      A *a = qobject_cast<A *>(vl.at(i).value<QObject *>());
      qDebug() << "Data from A" << i << ": " << a->test();
    }
  }

  Q_INVOKABLE void doVl(QVariantList vl){
    qDebug() << "QVariantList passed as parameter.\nSize of vl: " << vl.size();
    for(int i = 0; i < vl.size(); i++){
      qDebug() << "vl.at(" << i << "): " << vl.at(i);
    }
  }

  Q_INVOKABLE A* makeA(){
    A* a = new A();
    a->setTest("Another A");
    return a;
  }

  Q_INVOKABLE QVariantList makeAList(){
    QVariantList vl;

    A* a1 = new A();
    a1->setTest("newA1");
    vl.append(qVariantFromValue((QObject*)a1));

    A* a2 = new A();
    a2->setTest("newA2");
    vl.append(qVariantFromValue((QObject*)a2));

    return vl;
  }
};

#endif // B_H

Class C is shown in the following listing. This class has a property of type A that is accessible via QML.

#ifndef C_H
#define C_H

#include
#include

class C : public QObject
{
  Q_OBJECT
  Q_PROPERTY(A* a READ a WRITE setA NOTIFY aChanged)
public:
  explicit C(QObject *parent = 0) : QObject(parent){}

  A* a(){return myA;}

  void setA(A *a){
    myA = a;
    aChanged(myA);
  }

signals:
  void aChanged(A*);

private:
  A *myA;
};

#endif // C_H

Class D is shown in the following listing. This class has a property of type QVariantList that is accessible via QML. QVariantList is the C++ type used for QML lists.

#ifndef D_H
#define D_H

#include <QDebug>
#include <QObject>
#include <QVariantList>

class D : public QObject
{
  Q_OBJECT
  Q_PROPERTY(QVariantList vl READ vl WRITE setVl NOTIFY vlChanged)

public:
  explicit D(QObject *parent = 0) : QObject(parent){}

  QVariantList vl(){return myVl;}

  void setVl(QVariantList vl){
    myVl = vl;
    vlChanged(myVl);
  }

  Q_INVOKABLE void doVl(){
    qDebug() << "Size of myVl: " << myVl.size();
    for(int i = 0; i < myVl.size(); i++){
      qDebug() << "myVl.at(" << i << "): " << myVl.at(i);
    }
  }

signals:
  void vlChanged(QVariantList);

private:
  QVariantList myVl;

};
#endif // D_H

Last but not least above classes need to be made available to QML. This is shown in the following listing which shows the main method of the example.

#include
#include "qmlapplicationviewer.h"
#include

#include "a.h"
#include "b.h"
#include "c.h"
#include "d.h"

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

  // Make C++ classes available to QML.
  qmlRegisterType<A>("test", 1, 0, "A");
  qmlRegisterType<B>("test", 1, 0, "B");
  qmlRegisterType<C>("test", 1, 0, "C"); 
  qmlRegisterType<D>("test", 1, 0, "D"); 

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

  return app->exec(); 
} 


When run the output of this example when the MouseArea is clicked is as follows:

doing A: "bar"
doing C: "bar"
doing A: "foobar"
doing C: "foobar"
doing A: "foo"
Another A
doing C: "Another A"

Size of myVl:  5 
myVl.at( 0 ):  QVariant(QString, "i1") 
myVl.at( 1 ):  QVariant(QString, "i2") 
myVl.at( 2 ):  QVariant(double, 3) 
myVl.at( 3 ):  QVariant(double, 4) 
myVl.at( 4 ):  QVariant(QObject*, A(0x8dbc000) ) 

QVariantList passed as parameter.
Size of vl:  5 
vl.at( 0 ):  QVariant(QString, "i1") 
vl.at( 1 ):  QVariant(QString, "i2") 
vl.at( 2 ):  QVariant(double, 3) 
vl.at( 3 ):  QVariant(double, 4) 
vl.at( 4 ):  QVariant(QObject*, A(0x8dbc000) ) 

QVariantList passed as parameter.
Size of vl:  4 
vl.at( 0 ):  QVariant(QString, "j1") 
vl.at( 1 ):  QVariant(double, 2) 
vl.at( 2 ):  QVariant(double, 3) 
vl.at( 3 ):  QVariant(QString, "j4") 

Doing AList...  
vl.at( 0 ):  QVariant(QObject*, A(0x8dbc000) ) 
Data from A 0 :  "foobar" 
QObject* 
vl.at( 1 ):  QVariant(QObject*, A(0x8dbc050) ) 
Data from A 1 :  "blah" 
QObject* 

newA1
newA2

So, this simple example shows how objects can be passed from C++ to QML and vice versa no matter how these objects had been created or where (in the C++ or QML part). For further reading have a look at the according QML documentation.

This entry was posted in Qt/QML, Snippets and tagged , . Bookmark the permalink.

11 Responses to Exchange Data and Objects between C++ and QML and vice versa

  1. na says:

    In your example ur send a single object and display it in QML..but how about array of object?

    • ruedigergad says:

      You’re right.
      I assume with “array of objects” you mean QMLs “[1, 2, 3, someDataType]” notation for QML lists.
      I extended the above example and added demonstrations on how to pass Lists from QML to C++ and vice versa.

  2. Pingback: Update QML C++ Object Example « ruedigergad

  3. thp says:

    Thanks for this useful example 🙂

    A short improvement suggestion: For the set method, it usually makes sense to check if the value really changed, and only emit the changed signal then, i.e. instead of having:

    void setValue(int value) {
    _value = value;
    emit valueChanged();
    }

    You should probably do it like this:

    void setValue(int value) {
    if (value != _value) {
    _value = value;
    emit valueChanged();
    }
    }

    This might improve performance a bit and avoid extraneous updates if you (accidentally or not) set the same value multiple times.

    See here: http://qt-project.org/doc/qt-4.8/qtbinding.html#modifying-properties

    • ruedigergad says:

      Thanks! 🙂
      I also see in the guide you linked that there may be an improvement over the current code for accessing properties in the C++ part. Namely using QDeclarativeProperty::read and ::write instead of casting via qobject_cast.

  4. July says:

    is there a direct link to the whole source of your example.
    test 1.0 what is this?

  5. July says:

    also there is a jump in lines in the includes for the classes.
    Is there more code there?
    Please let me know I am trying to run your example as soon as possible.

  6. Pingback: Full Source Code for QML vs. C++ Example « ruedigergad

  7. Marcin says:

    And what’s with abstract classes with virtual fuctions?
    Let’s assume we have base
    class A : public QObject
    and two classes inheriting A
    class B : public A
    class C : public A

    Basing on user’s choice either B or C will be used.

    In plain C++ I’d do

    A* aptr;
    if ( /*user wants B*/ ) aptr = new B;
    else /*user wants C*/ aptr = new C;
    // do something more
    aptr -> virtualFunction()

    The functions will be called at various moments so I can’t use one onClicked: to create an object. The object will need to exist for some time

    How can I do it with QML?

    Thanks in advance

  8. Pingback: QML Duck-typing Example « ruedigergad

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.