Exchanging data between QML and C++

QML and C++ objects cancommunicate withone another through signals, slots and property modifications.For a C++object, any data that is exposed to Qt's Meta-Object System - that is,properties,signals, slots and Q_INVOKABLE methods - becomeavailable toQML. On the QML side, all QML object data is automatically madeavailable to themeta-object system and can be accessed from C++.

Calling functions

QML functions can be calledfrom C++ andvice-versa.

All QML functions are exposedto themeta-object system and can be called usingQMetaObject::invokeMethod().Here is a C++application that uses this to call a QML function:

1.   //MyItem.qml

2.   import QtQuick 1.0

3.    

4.   Item  {

5.       functionmyQmlFunction(msg)  {

6.           console.log("Gotmessage:", msg)

7.           return "somereturnvalue"

8.       }

  9. }

1.   //main.cpp

2.   QDeclarativeEngine engine;

3.   QDeclarativeComponent component(&engine,"MyItem.qml");

4.   QObject *object = component.create();

5.    

6.   QVariant returnedValue;

7.   QVariant msg = "Hello fromC++";

8.   QMetaObject::invokeMethod(object,"myQmlFunction",

9.           Q_RETURN_ARG(QVariant, returnedValue),

10.          Q_ARG(QVariant, msg));

11.   

12.  qDebug() << "QMLfunctionreturned:" <<returnedValue.toString();

  13. delete object;

Notice the Q_RETURN_ARG() and Q_ARG() argumentsfor QMetaObject::invokeMethod()must be specifiedasQVariant types, as this is thegenericdata type used for QML functions and return values.

To call a C++ function fromQML, thefunction must be either a Qt slot, or a function marked with theQ_INVOKABLE macro, to be available toQML.In the following example, the QML code invokes methods on the myObject object,which has beenset using QDeclarativeContext::setContextProperty():

1.   //MyItem.qml

2.   import QtQuick 1.0

3.    

4.   Item  {

5.      width: 100; height: 100

6.    

7.       MouseArea  {

8.          anchors.fill: parent

9.          onClicked:  {

10.            myObject.cppMethod("Hello from QML")

11.            myObject.cppSlot(12345)

12.          }

13.      }

  14. }

 

1.   class MyClass : public QObject

2.    {

3.       Q_OBJECT

4.   public:

5.       Q_INVOKABLE void cppMethod(const QString&msg)  {

6.           qDebug() << "Calledthe C++method with"<< msg;

7.       }

8.    

9.   public slots:

10.      void cppSlot(int number)  {

11.          qDebug() << "Calledthe C++slot with" <<number;

12.      }

13.  };

14.   

15.  int main(int argc, char *argv[])  {

16.      QApplication app(argc, argv);

17.   

18.      QDeclarativeView view;

19.      MyClass myClass;

20.     view.rootContext()->setContextProperty("myObject", &myClass);

21.   

22.      view.setSource(QUrl::fromLocalFile("MyItem.qml"));

23.      view.show();

24.   

25.      return app.exec();

  26. }

QML supports the calling ofoverloaded C++functions. If there are multiple C++ functions with the same namebut differentarguments, the correct function will be called according to thenumber and thetypes of arguments that are provided.

Receiving signals

All QML signals areautomatically availableto C++, and can be connected to using QObject::connect()like any ordinary Qt C++signal. In return, any C++ signal can be received by aQML object usingsignal handlers.

Here is a QML component with asignal named qmlSignal. This signal isconnected to a C++ object's slot using QObject::connect(),so that the cppSlot() methodis called wheneverthe qmlSignal is emitted:

1.   //MyItem.qml

2.   import QtQuick 1.0

3.    

4.   Item  {

5.       id: item

6.      width: 100; height: 100

7.    

8.       signal qmlSignal(stringmsg)

9.    

10.      MouseArea  {

11.         anchors.fill: parent

12.         onClicked:item.qmlSignal("Hello from QML")

13.      }

  14. }

 

1.   class MyClass : public QObject

2.    {

3.       Q_OBJECT

4.   public slots:

5.       void cppSlot(const QString &msg)  {

6.           qDebug() << "Calledthe C++slot with message:" << msg;

7.       }

8.   };

9.    

10.  int main(int argc, char *argv[])  {

11.      QApplication app(argc, argv);

12.   

13.      QDeclarativeView view(QUrl::fromLocalFile("MyItem.qml"));

14.      QObject *item = view.rootObject();

15.   

16.      MyClass myClass;

17.      QObject::connect(item,SIGNAL(qmlSignal(QString)),

18.                     &myClass,SLOT(cppSlot(QString)));

19.   

20.      view.show();

21.      return app.exec();

  22. }

 

To connect to Qt C++ signalsfrom withinQML, use a signal handler with the on<SignalName> syntax.If the C++ objectis directly creatable from within QML (see Defining new QML elements above) then thesignalhandler can be defined within the object declaration. In the followingexample,the QML code creates a ImageViewer object,and the imageChanged and loadingError signalsof the C++ objectare connected to through onImagedChanged and onLoadingError signalhandlers in QML:

1.   class ImageViewer : publicQDeclarativeItem

2.    {

3.       Q_OBJECT

4.       Q_PROPERTY(QUrl imageREAD image WRITE setImage NOTIFYimageChanged)

5.   public:

6.       ...

7.   signals:

8.       void imageChanged();

9.       void loadingError(const QString&errorMsg);

  10. };

 

1.   ImageViewer  {

2.       onImageChanged:console.log("Imagechanged!")

3.       onLoadingError:console.log("Imagefailed toload:",errorMsg)

  4. }

(Note that if a signal has beendeclared asthe NOTIFY signal for a property, QML allows it to be received withan on<Property>Changed handler even ifthesignal's name does not follow the<Property>Changed namingconvention. In theabove example, if the "imageChanged" signal wasnamed"imageModified" instead, the onImageChanged signalhandler wouldstill be called.)

If, however, the object withthe signal isnot created from within the QML code, and the QML item only has areference tothe created object - for example, if the object was set usingQDeclarativeContext::setContextProperty()-then the Connections element can be usedinstead tocreate the signal handler:


1.
   ImageViewer viewer;

2.    

3.   QDeclarativeView view;

4.   view.rootContext()->setContextProperty("imageViewer", &viewer);

5.    

6.   view.setSource(QUrl::fromLocalFile("MyItem.qml"));

   7. view.show();

 

1.   //MyItem.qml

2.   import QtQuick 1.0

3.    

4.   Item  {

5.       Connections  {

6.          target: imageViewer

7.           onImageChanged:console.log("Imagehas changed!")

8.       }

   9. }

C++ signals can use enum valuesasparameters provided that the enum is declared in the class that is emittingthesignal, and that the enum is registered using Q_ENUMS. See Using enumerations of acustomtype below for details.

Modifying properties

Any properties declared in a QML objectareautomatically accessible from C++. Given a QML item like this:

1.   //MyItem.qml

2.   import QtQuick 1.0

3.    

4.   Item  {

5.       property int someNumber: 100

6.   }

The value of the someNumber property can be set andreadusing QDeclarativeProperty,orQObject::setProperty()and QObject::property():

1.                 QDeclarativeEngine engine;

2.   QDeclarativeComponentcomponent(&engine, "MyItem.qml");

3.   QObject *object = component.create();

4.    

5.   qDebug() << "Propertyvalue:" << QDeclarativeProperty::read(object, "someNumber").toInt();

6.   QDeclarativeProperty::write(object, "someNumber", 5000);

7.    

8.   qDebug() << "Propertyvalue:" << object->property("someNumber").toInt();

9.   object->setProperty("someNumber", 100);

You should always use QObject::setProperty(), QDeclarativeProperty or QMetaProperty::write()tochange a QML property value, to ensure the QML engine is made aware oftheproperty change. For example, say you have a custom element PushButton with a buttonText property that internallyreflects thevalue of a m_buttonText membervariable.Modifying the member variable directly like this is not a good idea:

1.   //BAD!

2.   QDeclarativeComponent component(engine, "MyButton.qml");

3.   PushButton *button = qobject_cast<PushButton*>(component.create());

4.   button->m_buttonText = "Clickme";

Since the value is changed directly,thisbypasses Qt's meta-object system andthe QML engine is notmade aware of the property change. This means propertybindings to buttonText would not beupdated, andany onButtonTextChanged handlers would notbecalled.

Any Qt properties -that is, those declaredwith the Q_PROPERTY() macro-are accessible from QML. Here is a modified version of the earlier example onthis page; here, the ApplicationData class has abackgroundColor property. Thispropertycan be written to and read from QML:

1.   class ApplicationData : publicQObject

2.    {

3.       Q_OBJECT

4.       Q_PROPERTY(QColorbackgroundColor

5.              READbackgroundColor

6.             WRITE setBackgroundColor

7.             NOTIFY backgroundColorChanged)

8.    

9.   public:

10.      void setBackgroundColor(constQColor &c)  {

11.          if (c != m_color)  {

12.            m_color = c;

13.             emitbackgroundColorChanged();

14.          }

15.      }

16.   

17.      QColor backgroundColor() const {

18.          return m_color;

19.      }

20.   

21.  signals:

22.      void backgroundColorChanged();

23.   

24.  private:

25.      QColor m_color;

   26. };

 

1.   //MyItem.qml

2.   import QtQuick 1.0

3.    

4.   Rectangle  {

5.      width: 100; height: 100

6.      color: applicationData.backgroundColor

7.    

8.       MouseArea  {

9.          anchors.fill: parent

10.         onClicked:applicationData.backgroundColor = "red"

11.      }

   12. }

Notice the backgroundColorChanged signal is declaredas theNOTIFY signal for the backgroundColorproperty. If a Qtproperty does not have anassociated NOTIFY signal, the property cannot be usedforProperty Binding inQML, as the QML enginewould not be notified when the value changes. If you areusing custom types inQML, make sure their properties have NOTIFY signals sothat they can be used inproperty bindings.

See Tutorial: Writing QMLextensionswith C++ for further details and examples on usingQt propertieswith QML.