Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. communicating from c++ to qml
Forum Updated to NodeBB v4.3 + New Features

communicating from c++ to qml

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
6 Posts 3 Posters 3.3k Views 2 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • S Offline
    S Offline
    Shiv
    wrote on last edited by
    #1

    Hi all
    I am new to qt qml i have tried communicating from qml to c++ by exposing a class using context or by qml registering .Now i have placed a button and i am able to communicate to the c++ .But i am unable to do the reverse that is communicating from c++ to qml.
    For example i am firing a QTimer in c++ say on every trigger a variable is incremented i want to display the same in qml how to proceed .Can anyone guide me with a simple example.Thank you

    1 Reply Last reply
    0
    • SGaistS Offline
      SGaistS Offline
      SGaist
      Lifetime Qt Champion
      wrote on last edited by
      #2

      Hi,

      You can use a Q_PROPERTY for that for example and each time your value is changed emit the corresponding signal. Then in qml you'll assign that property to what you want to use to show its value.

      Hope it helps

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      1 Reply Last reply
      0
      • S Offline
        S Offline
        Shiv
        wrote on last edited by
        #3

        Hi @SGaist
        Thanks for you kind reply.I am actually using Q_PROPERTY in my c++ with read ,write and notify value but how to assign Q_PROPERTY in qml any small example will clear my doubt.
        Presently what i am doing is.

        //C++ class
        //MyTestClass
        Q_PROPERTY(int val READ val WRITE setValue NOTIFY valChanged)
        //In main
        
        //I am setting a root context and exposing this whole class.
        //in Qml
        //I have a button and in onClicked signal  i am able
        // to access the data in c++ class.
        

        But I want to trigger my signal from C++ to qml , trigger in the sense without any click event in qml.

        Thank you

        1 Reply Last reply
        0
        • R Offline
          R Offline
          Red Baron
          wrote on last edited by Red Baron
          #4

          You have two ways of doing that:

          • Get the QML item in your C++ code and change its properties/invoke its methods
          • Connect a C++ signal/slot to QML slot/signal

          Using properties and invoking methods

          QQuickView, QQuickWidget etc. offer the rootObject() method, which returns the QQuickItem that is the top-level item of your QML code. There are a couple of things you have to keep in mind:

          1. All involved have to have an objectName (not to be confused with id in QML, which is a totally different thing (very confusing omho)) including your widget (see 2 for C++ code):

            Rectangle {
              id: button // Used for accessing the item from QML
              objectName: "button" // Used for accessing the item from C++
            }
            
          2. You need to add the widget as a contextProperty to the QML
            Example:

            MyQQuickWidget::MyQQuickWidget(const QUrl &source, QWidget *parent) : QQuickWidget(parent) {
                // Add the widget to the root QML context as a context property
                this->setObjectName("quickwidget"); // quickwidget will be used to reference your widget within your QML code
                this->rootContext()->setContextProperty(this->objectName(), this);
                this->setSource(source);
                ...
            }
            

          After doing that you can simply retrieve the respective items in your C++ code. For example

          QQuickItem* button = this->rootObject()->findChild<QQuickItem*>("button");
          

          will retrieve the Rectangle I've used in 1. as an example. You should of course always check if the returned value is == Q_NULLPTR to avoid access violation if the child cannot be found and you try to use it anyway.

          After successfully retrieving your QML item you can access its properties or even invoke it's method. For example

          button->property("width").value<int>()
          

          returns the width of your item. In order to avoid a situation where you access a property that doesn't exists I strongly recommend to check the validity of the property like so

          int width = 0;
          if(button->property("width").isValid()) {
            width = button->property("width").value<int>();
            // Do something with width
            // For example increase it twice the length
             button->setProperty("width", width*2);
          }
          

          I just wrote a ListView with a ListModel where I'm controlling a lot of their aspects in my C++ code. ListModel for example has the move(int from, int to, int items) method. You can use QMetaObject::invokeMethod(...) to trigger some sort of a method that is part of the QML object. I'm quite new to QML so I don't know all the methods that are out there for Rectangle and whatnot but in the case of ListModel I did

          QObject* listModel = this->rootObject()->findChild<QObject*>("listModel"); // listModel is the objectName of my ListModel used by a ListView
          // Check if listModel retrieved correctly
          // Optional: check if "move" is a method available for the retrieved QObject - a little bit more complex then with checking if a property exists
          // ...
          
          int from = 0; // Or whatever
          int to = 10; // Or whatever
          int items = 1; // Move a single item
          
          // Use the listModel object to trigger its move(int from, int to, int items) method
          QMetaObject::invokeMethod(listModel, "move",
                                    Q_ARG(int, from),
                                    Q_ARG(int, to),
                                    Q_ARG(int, items));
          

          Using Q_PROPERTY is also something that is used widely. If you want to expose C++ data to QML context you need to make that data a property of your C++ object etc. (you still have to add your C++ object to the QML context using its object name though).


          Using slots and signals

          I find the great difference between how slots and signals are declared in C++ and QML very annoying but since its a huge part of how Qt works one might also use this technique. ;)

          Here's an example how to:

          • Trigger a C++ slot using QML signal
          • Trigger a QML slot using a C++ signal

          main.qml (stored inside QRC file with the alias main)

          import QtQuick 2.0
          
          Rectangle {
              id: test
              width:  200
              height: 50
              x: 10
              y: 10
              signal handleText(string msg)
          
              Text {
                  id: textItem
                  objectName: "textItem"
                  anchors.centerIn: test
                  text: "Text set in QML"
          
                  Connections {
                      target: commObject
                      onChangeText: {
                          textItem.text = newText;
                      }
                  }
              }
          
              MouseArea {
                  hoverEnabled: false
                  anchors.fill: parent
                  onClicked: {
                      test.handleText(textItem.text)
                  }
              }
          }
          

          main.cpp

          #include <QtGui/QGuiApplication>
          #include <QtQuick/QQuickItem>
          #include <QtQuick/QQuickView>
          #include <QQmlContext>
          #include <QTimer>
          #include "CommObject.h"
          
          int main(int argc, char *argv[])
          {
              QGuiApplication app(argc, argv);
          
              // Create instance of object with slots and signals
              CommObject co;
              co.setObjectName("commObject");
          
              // Create view
              QQuickView view;
              // Add reference to object to view's rootContext as property
              view.rootContext()->setContextProperty("commObject", &co);
              // Load QML in view
              view.setSource(QUrl("qrc:/main"));
          
              // Retrieve top-level object which is a Rectangle
              QObject *rect = dynamic_cast<QObject*>(view.rootObject());
              // Connect QML signal to C++ slot
              QObject::connect(rect, SIGNAL(handleText(QString)),
                               &co, SLOT(slotRetrieveText(QString)));
          
              view.show();
          
              // Now lets trigger the emission of the C++ signal to the QML slot using a timer
              QTimer timer;
              timer.setInterval(5000);
              co.setText("Text changed from C++");
              QObject::connect(&timer, SIGNAL(timeout()), &co, SLOT(slotTriggerChangeText()));
              timer.start();
          
              return app.exec();
          }
          

          CommObject.h

          #ifndef COMMOBJECT_H
          #define COMMOBJECT_H
          
          #include <QObject>
          
          class CommObject : public QObject
          {
              Q_OBJECT
          public:
              explicit CommObject(QObject *parent = Q_NULLPTR);
              void setText(const QString &msg);
          
          signals:
              void changeText(const QString& newText);
          
          public slots:
              void slotTriggerChangeText();
              void slotRetrieveText(const QString &msg);
          
          private:
              QString text;
          };
          
          #endif // COMMOBJECT_H
          

          CommObject.cpp

          #include "CommObject.h"
          #include <QDebug>
          
          CommObject::CommObject(QObject *parent) :
              QObject(parent),
              text("")
          {
          }
          
          void CommObject::setText(const QString &msg)
          {
              this->text = msg;
          }
          
          void CommObject::slotTriggerChangeText()
          {
              qDebug() << "Triggering change text signal";
              emit changeText(this->text);
          }
          
          void CommObject::slotRetrieveText(const QString &msg)
          {
              qDebug() << "Received text from QML: \"" << msg << "\"";
          }
          

          Declaring signal handleText(string msg) (in QML) actually does two things - it creates a signal and a slot with the slot being automatically generated and named as onHandleText (notice the on and the uppercase first letter of your signal's name - if you miss these two things your slot will never get triggered). This applies even to signals coming from C++ context. In our case we have changeText(QString newText) so the QML slot is automatically generated as onChangeText. Another thing that is important to remember is that you HAVE TO have a name for the argument(s) the C++ signal carries and that name has to be exactly what you use inside your QML slot which receives the C++ signal. If you write

          Text {
            ...
            Connections {
                      target: commObject
                      onChangeText: {
                          textItem.text = msg;
                      }
                  }
          }
          

          it will throw a ReferenceError: msg is not defined since internally newText is what is used to access the string that the C++ signal carries. Last but not least (yet another annoying thing) - the target in the Connections item is for the source of the signal (which in our case is the C++ CommObject). The naming is extremely confusing and I hope that they change it in future releases (I'm currently using Qt 5.7).

          You can easily adapt the examples above to your scenario. I decided to write it in a more general way for future reference.

          1 Reply Last reply
          5
          • S Offline
            S Offline
            Shiv
            wrote on last edited by
            #5

            HI @Red-Baron

            Thank you for the detailed explanation and example I will try to implement and let you know .

            Thanks

            R 1 Reply Last reply
            0
            • S Shiv

              HI @Red-Baron

              Thank you for the detailed explanation and example I will try to implement and let you know .

              Thanks

              R Offline
              R Offline
              Red Baron
              wrote on last edited by
              #6

              @Shiv You're welcome. Do tell if you get stuck down the road. :)

              1 Reply Last reply
              0

              • Login

              • Login or register to search.
              • First post
                Last post
              0
              • Categories
              • Recent
              • Tags
              • Popular
              • Users
              • Groups
              • Search
              • Get Qt Extensions
              • Unsolved