Interacting with QML Objects from C++



  • I am trying to change QML component property from C++, also I am trying to connect QML component signal to C++ slot. But unfortunately it doesn't work.

    Code example:

    QML:

        Rectangle {
            id: test_rect
            objectName: "test_rect"
            width: dp(100)
            height: dp(100)
            color: "red"
            visible: true
            enabled: true
    
            signal clicked(string theValue);
    
            MouseArea {
                anchors.fill: parent
    
                onClicked: {
                    test_rect.clicked("HELLO FROM QML")
                }
            }
    
            onColorChanged: {
                console.log(color)
            }
    
            onVisibleChanged: {
                console.log(visible)
            }
    
            onClicked: {
                console.log("CLICKED")
            }
        }
    

    C++:

        QQmlApplicationEngine anEngine;
        anEngine.load(QUrl(QStringLiteral("qrc:/main.qml")));
        QObject *aRoot = anEngine.rootObjects()[0];
        if(aRoot != NULL)
        {
          QQuickItem *aRect = aRoot->findChild<QQuickItem*>("test_rect");
          if(aRect)
          {
            qDebug("Test rect is found");
            if(QQuickItem::connect(aRect, SIGNAL(clicked(QString)),
                                this, SLOT(onTestRectClicked(QString))))
            {
              qDebug("Connected");
              aRect->setProperty("color", "green");
              //aRect->setVisible(false); // Also doesn't work
            }
          }
        }
    

    Also, I don't recieve signal clicked to my slot onTestRectClicked

    When I am changing color or visibility of test_rect from C++, in QML slots onColorChanged and onVisibleChanged the correct values are printed to the console, but color or visibility is not changed.

    Please, help


  • Moderators

    Hi @Khachatur,

    QQuickItem::connect(aRect, SIGNAL(clicked(QString)),this, SLOT(onTestRectClicked(QString)))
    

    It wont work unless you have declared onTestRectClicked as a SLOT ? Where have you done that ?



  • Hi @p3c0,

    In my C++ class header file:

    private slots:
      void onTestRectClicked(const QString &theValue);
    

  • Moderators

    @Khachatur So didn't it get invoked ?
    Was the connection successful ? Was that aRect found ?



  • @p3c0

    Yes, it is not invoked. Also, I can't change the properties(color or visibility) of test_rect from C++.

    I have changed onCLicked of test_rect :

            onClicked: {
                console.log(visible)
                console.log(color)
                console.log("CLICKED")
            }
    

    After launching of application, I am clicking on that test_rect rectangle and I get such output:

    qml: true
    qml: #ff0000 // red color
    qml: CLICKED
    

    then I am clicking the button which calls the C++ code defined abow and in slots onColorChanged and onVisibleChanged I get:

    qml: false
    qml: #008000 // green color
    

    but nothing is changed.

    Then I click again to that red rectangle and I get again the same output

    qml: true
    qml: #ff0000 // red color
    qml: CLICKED
    

    I don't understand what I am doing wrong. May be there are some threads in which the UI elements can be changed? And I am changing properties in wrong thread?



  • @p3c0
    Yes, the aRect is found and the connection is successful. For testing purposes I tired to connect non-existing signal pressed and I got this:
    QObject::connect: No such signal QQuickRectangle_QML_147::pressed(QString) in OperationCreatePoint.cpp:99
    QObject::connect: (sender name: 'test_rect')


  • Moderators

    @Khachatur Just tested your code and found out that if you remove this if condition

    if(aRoot != NULL)
        {
           ...
    

    it works. Can you confirm ?
    However I'm too not sure why this works if the condition is removed.



  • @p3c0

    I have removed that condition and it is still not working...

    Is it possible that rootObjects or findchild return pointer to the copy of original object?


  • Moderators

    @Khachatur No I guess. Because changing property of those objects reflects the changes.
    Try removing the second if condition too.



  • @p3c0

    I am new in qt and qml, therefore sorry... It was my bad... again :(

    The code below is correct when you are loading the main.qml first time in main.cpp and then get pointer to root object.

    QQmlApplicationEngine anEngine;
    anEngine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    QObject *aRoot = anEngine.rootObjects()[0];
    QQuickWindow* aWindow = qobject_cast<QQuickWindow* >(aRoot);
    if (aWindow)
    {
     aWindow->show();
    }
    

    I have used code above in my operation class to get pointer to root object of main.qml.

    But such approach is incorrect, because it will not return the pointer to the root object of main.qml, but will create new instance of main.qml and will return pointer to the root object of new created.

    To get the root object of already displayed main.qml file I am using:

        QQmlEngine *anEngine = QQmlEngine::contextForObject(myViewer)->engine();
        QObject* aRoot = ((QQmlApplicationEngine*)anEngine)->rootObjects()[0];
    

    Where myViewer has C++ class type and is registered as qml type qmlRegisterType<Viewer>("Viewer", 1, 0, "Viewer"); and defined in main.qml.

    Currently, everything is working, I can change properties of qml objects and can recieve signals from qml.

    But, is there another way to get poiner to the root object of main.qml?

    P.S. Thank you for your response


  • Moderators

    @Khachatur

    But such approach is incorrect, because it will not return the pointer to the root object of main.qml, but will create new instance of main.qml and will return pointer to the root object of new created.

    Do you mean there are two instances of main.qml ? Which are they ?



  • @p3c0

    Yes, I mean that there are two instances of main.qml.

    Doing this you will get the new instance of main.qml:

    QQmlApplicationEngine anEngine;
    anEngine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    QObject *aRoot = anEngine.rootObjects()[0];
    

    I didn't get what does it mean "Which are they"?


  • Moderators

    @Khachatur How did you check whether they are 2 separate instance ?



  • @p3c0

    I don't know the way how to check directly whether they are 2 separate instances, but I was relying on logic.

    In QML slots onColorChanged and onVisibleChanged of test_rect I am printing the 'color' and 'visible' values.

            onColorChanged: {
                console.log(color)
            }
    
            onVisibleChanged: {
                console.log(visible)
            }
    

    When I am changing property of QML objects from C++ code:

              aRect->setProperty("color", "green");
              aRect->setVisible(false);
    

    In output console I get the correct values of color and visibility (green and false respectively), but rectangle still remains red and visible.

    Therefore I make decision that I am working with another instance of main.qml and I was right.


  • Moderators

    @Khachatur No they are exactly the same objects.
    Consider the following example:
    main.qml

    import QtQuick 2.4
    import QtQuick.Window 2.2
    
    Window {
        visible: true
        width: 150
        height: 150
    
        Rectangle {
            anchors.fill: parent
        }
    
        Component.onCompleted: console.log("From QML: ",this) // prints the root object
    }
    

    your main.cpp

    QQmlApplicationEngine anEngine;
    anEngine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    
    QObject *aRoot = anEngine.rootObjects()[0];
    qDebug() << "From C++: " << aRoot;
    

    After running if you look at the outputs you can see the same address on both sides which indicates it is the same object.
    I think there is some other problem in your code.
    Can you post or upload an updated complete minimal working example which will show the problem ?



  • @p3c0

    I have checked.

    This code I am using at the first time in file main.cpp when I am launching the application

    QQmlApplicationEngine anEngine;
    anEngine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    
    QObject *aRoot = anEngine.rootObjects()[0];
    qDebug() << "From C++: " << aRoot;
    

    Then I am using the same code in my OperationCreatePoint.cpp class and I get the different address of aRoot.

    The output is:
    From main.cpp: ApplicationWindow_QMLTYPE_57_QML_74(0x4711600)
    From OperationCreatePoint.cpp: ApplicationWindow_QMLTYPE_143_QML_160(0x8478fc0)


  • Moderators

    @Khachatur Some confusion. Do you mean you load main.qml twice ? One is main.cpp and another in OperationCreatePoint.cpp ?
    Atleast from the output it seems.



  • @p3c0

    English is not my native language, may be you misunderstood me... Anyway thank you for your response and sorry for misunderstood


  • Moderators

    @Khachatur That's fine :)
    If you still have the problem you can post the updated complete minimal working example so that it would be more helpful in finding the problem.



  • @p3c0

    Yes, I meant I was loading main.qml twice in main.cpp and OperationCreatePoint.cpp. Currently, I have not any problem, Thanks :)

    I just have another question about QML FileDialog component, but I guess... should I create new thread?

    I am using the FileDialog to load and save files. When I am saving file I want to pass to my FileDialog the default file name, but unfortunately FileDialog have not such property, only fileUrl property which is ReadOnly. I don't want to implement my own file dialog component...


  • Moderators

    @Khachatur AFAIK it is still not implemented. Taking this into consideration some one has already implemented their own. See here.



  • @p3c0

    That is exactly what I need. Thank you :)


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.