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. Calling C++ from QML
Forum Updated to NodeBB v4.3 + New Features

Calling C++ from QML

Scheduled Pinned Locked Moved Solved QML and Qt Quick
6 Posts 3 Posters 338 Views 1 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.
  • SPlattenS Offline
    SPlattenS Offline
    SPlatten
    wrote on last edited by
    #1

    I am working on a test project to learn QML and C++ integration, here is the QML:

    import QtQuick 2.0
    import QtQuick.Controls 1.4
    import QtQuick.Controls.Styles 1.4
    import QtQuick.Layouts 1.3
    import QtQuick.Extras 1.4
    import QtQuick.Window 2.12
    import SimonsCPP 1.0
    
    Window {
        id: root
        visible: true
        width: 640
        height: 480
        title: qsTr("Playground / Staging Arena")
    
        property bool staleHdg: false
    
        SimonP {
            id: simonP
        }
    
        Text {
            id:hdgValue
            anchors.top: parent.top
            color: "green"
            font.pointSize: parent.width < 150 ? 30 : 16
            text: simonP.value
            horizontalAlignment: Text.AlignRight
            verticalAlignment: Text.AlignTop
            width: 64
        }
        Text {
            id:hdgLabel
            anchors.top: hdgValue.bottom
            color: "black"
            text: "HDG"
            horizontalAlignment: Text.AlignLeft
            verticalAlignment: Text.AlignTop
            font.pointSize: parent.width < 150 ? 30 : 16
            width: 40
        }
        Image {
            id: staleIndicator
            visible: staleHdg
            anchors.left: hdgValue.right
            source: "/stale.svg"
        }
        Button {
            id: btnData
            text: "Click Me..."
            anchors.top: hdgLabel.bottom
            onClicked: function() {
                var currValue = hdgValue.text
                   ,newValue = String(parseFloat(currValue) + 1);
                var tmp = typeof SimonP;
                var tmp2 = typeof simonP;
                var tmp3 = typeof tmp.setValue;
                var tmp4 = typeof tmp2.setValue;
                SimonP.setValue(newValue);
                console.log( "currentValue: " + currValue + ", newValue: " + newValue );
            }
        }
    }
    

    Here is the C++, prototype:

    #ifndef SIMONP_H
    #define SIMONP_H
    
    #include <QObject>
    #include <QString>
    #include <qqml.h>
    
    class SimonP : public QObject {
        Q_OBJECT
        Q_PROPERTY(QString value READ value WRITE setValue)
        QML_ELEMENT
    
    private:
        QString mstrValue;
    
    public:
        explicit SimonP(QObject *parent_ = nullptr);
    
        Q_INVOKABLE void setValue(const QString& crstrValue);
        QString value() { return mstrValue; }
    
    signals:
    };
    #endif // SIMONP_H
    

    And the implementation:

    #include "simonp.h"
    
    SimonP::SimonP(QObject* parent_) : QObject(parent_) {
        mstrValue = "123";
    }
    
    void SimonP::setValue(const QString& crstrValue) {
        mstrValue = crstrValue;
    }
    

    In the QML when I click the button, I have a function onClicked with a breakpoint:

                var currValue = hdgValue.text
                   ,newValue = String(parseFloat(currValue) + 1);
                var tmp = typeof SimonP;
                var tmp2 = typeof simonP;
                var tmp3 = typeof tmp.setValue;
                var tmp4 = typeof tmp2.setValue;
                SimonP.setValue(newValue);
                console.log( "currentValue: " + currValue + ", newValue: " + newValue );
    

    I can see that tmp and tmp2 are "object", however both tmp3 and tmp4 are "undefined". How do I fix this so I can call setValue from the QML?

    Kind Regards,
    Sy

    J.HilkJ 1 Reply Last reply
    0
    • SPlattenS SPlatten

      @J-Hilk , thank you, where? I think you may be misreading the code. When I call setValue it does have parentheses, in the tests using typeof I am not intending to call the function but test the type and I expect to see tmp3 or tmp4 or both to have a value of "function".

      SPlattenS Offline
      SPlattenS Offline
      SPlatten
      wrote on last edited by
      #4

      Fixed:

                  var currValue = simonP.value
                     ,newValue = String(parseFloat(currValue) + 1);
                  var tmp = typeof SimonP;
                  var tmp2 = typeof simonP;
                  var tmp3 = typeof SimonP.setValue;
                  var tmp4 = typeof simonP.setValue;
                  simonP.setValue(newValue);
                  hdgValue.text = newValue;
                  console.log( "currentValue: " + currValue + ", newValue: " + newValue );
      

      This works!, tmp4 now shows "function" and the value is updated.

      Kind Regards,
      Sy

      1 Reply Last reply
      0
      • SPlattenS SPlatten

        I am working on a test project to learn QML and C++ integration, here is the QML:

        import QtQuick 2.0
        import QtQuick.Controls 1.4
        import QtQuick.Controls.Styles 1.4
        import QtQuick.Layouts 1.3
        import QtQuick.Extras 1.4
        import QtQuick.Window 2.12
        import SimonsCPP 1.0
        
        Window {
            id: root
            visible: true
            width: 640
            height: 480
            title: qsTr("Playground / Staging Arena")
        
            property bool staleHdg: false
        
            SimonP {
                id: simonP
            }
        
            Text {
                id:hdgValue
                anchors.top: parent.top
                color: "green"
                font.pointSize: parent.width < 150 ? 30 : 16
                text: simonP.value
                horizontalAlignment: Text.AlignRight
                verticalAlignment: Text.AlignTop
                width: 64
            }
            Text {
                id:hdgLabel
                anchors.top: hdgValue.bottom
                color: "black"
                text: "HDG"
                horizontalAlignment: Text.AlignLeft
                verticalAlignment: Text.AlignTop
                font.pointSize: parent.width < 150 ? 30 : 16
                width: 40
            }
            Image {
                id: staleIndicator
                visible: staleHdg
                anchors.left: hdgValue.right
                source: "/stale.svg"
            }
            Button {
                id: btnData
                text: "Click Me..."
                anchors.top: hdgLabel.bottom
                onClicked: function() {
                    var currValue = hdgValue.text
                       ,newValue = String(parseFloat(currValue) + 1);
                    var tmp = typeof SimonP;
                    var tmp2 = typeof simonP;
                    var tmp3 = typeof tmp.setValue;
                    var tmp4 = typeof tmp2.setValue;
                    SimonP.setValue(newValue);
                    console.log( "currentValue: " + currValue + ", newValue: " + newValue );
                }
            }
        }
        

        Here is the C++, prototype:

        #ifndef SIMONP_H
        #define SIMONP_H
        
        #include <QObject>
        #include <QString>
        #include <qqml.h>
        
        class SimonP : public QObject {
            Q_OBJECT
            Q_PROPERTY(QString value READ value WRITE setValue)
            QML_ELEMENT
        
        private:
            QString mstrValue;
        
        public:
            explicit SimonP(QObject *parent_ = nullptr);
        
            Q_INVOKABLE void setValue(const QString& crstrValue);
            QString value() { return mstrValue; }
        
        signals:
        };
        #endif // SIMONP_H
        

        And the implementation:

        #include "simonp.h"
        
        SimonP::SimonP(QObject* parent_) : QObject(parent_) {
            mstrValue = "123";
        }
        
        void SimonP::setValue(const QString& crstrValue) {
            mstrValue = crstrValue;
        }
        

        In the QML when I click the button, I have a function onClicked with a breakpoint:

                    var currValue = hdgValue.text
                       ,newValue = String(parseFloat(currValue) + 1);
                    var tmp = typeof SimonP;
                    var tmp2 = typeof simonP;
                    var tmp3 = typeof tmp.setValue;
                    var tmp4 = typeof tmp2.setValue;
                    SimonP.setValue(newValue);
                    console.log( "currentValue: " + currValue + ", newValue: " + newValue );
        

        I can see that tmp and tmp2 are "object", however both tmp3 and tmp4 are "undefined". How do I fix this so I can call setValue from the QML?

        J.HilkJ Offline
        J.HilkJ Offline
        J.Hilk
        Moderators
        wrote on last edited by
        #2

        @SPlatten add parentheses, setValue is a function after all


        Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


        Q: What's that?
        A: It's blue light.
        Q: What does it do?
        A: It turns blue.

        SPlattenS 1 Reply Last reply
        0
        • J.HilkJ J.Hilk

          @SPlatten add parentheses, setValue is a function after all

          SPlattenS Offline
          SPlattenS Offline
          SPlatten
          wrote on last edited by SPlatten
          #3

          @J-Hilk , thank you, where? I think you may be misreading the code. When I call setValue it does have parentheses, in the tests using typeof I am not intending to call the function but test the type and I expect to see tmp3 or tmp4 or both to have a value of "function".

          Kind Regards,
          Sy

          SPlattenS 1 Reply Last reply
          0
          • SPlattenS SPlatten

            @J-Hilk , thank you, where? I think you may be misreading the code. When I call setValue it does have parentheses, in the tests using typeof I am not intending to call the function but test the type and I expect to see tmp3 or tmp4 or both to have a value of "function".

            SPlattenS Offline
            SPlattenS Offline
            SPlatten
            wrote on last edited by
            #4

            Fixed:

                        var currValue = simonP.value
                           ,newValue = String(parseFloat(currValue) + 1);
                        var tmp = typeof SimonP;
                        var tmp2 = typeof simonP;
                        var tmp3 = typeof SimonP.setValue;
                        var tmp4 = typeof simonP.setValue;
                        simonP.setValue(newValue);
                        hdgValue.text = newValue;
                        console.log( "currentValue: " + currValue + ", newValue: " + newValue );
            

            This works!, tmp4 now shows "function" and the value is updated.

            Kind Regards,
            Sy

            1 Reply Last reply
            0
            • GrecKoG Offline
              GrecKoG Offline
              GrecKo
              Qt Champions 2018
              wrote on last edited by GrecKo
              #5

              Use your Q_PROPERTY instead. You don't need to mark the setter as Q_INVOKABLE.
              Add a NOTIFY signal to it to use property bindings ( https://doc.qt.io/qt-6/qtqml-cppintegration-exposecppattributes.html#exposing-properties ). With this the QML enfine is made aware of the property changes and will update the text of hdgValue automatically.

              onClicked: function() {
                  var currValue = simonP.value
                  var newValue = String(parseFloat(currValue) + 1);
                  simonP.value = newValue;
                  // hdgValue.text = newValue; // no need to do that if you have a NOTIFY signal, it is actually counter productive and break the binding.
              }
              

              If it makes sense in your case, you could just define a float/double property in SimonP and do the following:

              onClicked: ++simonP.value
              

              You are overcomplicating your code.

              SPlattenS 1 Reply Last reply
              1
              • GrecKoG GrecKo

                Use your Q_PROPERTY instead. You don't need to mark the setter as Q_INVOKABLE.
                Add a NOTIFY signal to it to use property bindings ( https://doc.qt.io/qt-6/qtqml-cppintegration-exposecppattributes.html#exposing-properties ). With this the QML enfine is made aware of the property changes and will update the text of hdgValue automatically.

                onClicked: function() {
                    var currValue = simonP.value
                    var newValue = String(parseFloat(currValue) + 1);
                    simonP.value = newValue;
                    // hdgValue.text = newValue; // no need to do that if you have a NOTIFY signal, it is actually counter productive and break the binding.
                }
                

                If it makes sense in your case, you could just define a float/double property in SimonP and do the following:

                onClicked: ++simonP.value
                

                You are overcomplicating your code.

                SPlattenS Offline
                SPlattenS Offline
                SPlatten
                wrote on last edited by SPlatten
                #6

                Finished prototype and re-worked so it doesn't use a timer, QML:

                import QtQuick 2.0
                import QtQuick.Controls 2.15
                import QtQuick.Controls.Styles 1.4
                import QtQuick.Layouts 1.3
                import QtQuick.Extras 1.4
                import QtQuick.Window 2.12
                import SimonsCPP 1.0
                
                Window {
                    id: root
                    visible: true
                    width: 640
                    height: 480
                    title: qsTr("Playground / Staging Arena")
                
                    function checkStaleStatus(strToolTip) {
                        if ( typeof strToolTip != "string" ) {
                            simonP.stale()
                            return
                        }
                        staleIndicator.visible = (strToolTip.length > 0)
                        hdgValue.ToolTip.text = strToolTip
                    }
                
                    SimonP {
                        id: simonP
                        onValueChanged: {
                            checkStaleStatus(strToolTip)
                        }
                    }
                    Label {
                        id:hdgValue
                        anchors.top: parent.top
                        color: "green"
                        font.pointSize: parent.width < 150 ? 30 : 16
                        text: String(simonP.value)
                        horizontalAlignment: Text.AlignRight
                        verticalAlignment: Text.AlignTop        
                        ToolTip.visible: ToolTip.text.length > 0 && ma.containsMouse
                
                        MouseArea {
                            id: ma
                            anchors.fill: parent
                            hoverEnabled: true
                            onContainsMouseChanged: checkStaleStatus()
                        }
                        width: 256
                    }
                    Label {
                        id:hdgLabel
                        anchors.horizontalCenter:  hdgValue.horizontalCenter
                        anchors.top: hdgValue.bottom
                        color: "black"
                        text: "HDG"
                        horizontalAlignment: Text.AlignHCenter
                        verticalAlignment: Text.AlignTop
                        font.pointSize: parent.width < 150 ? 30 : 16
                        width: 32
                    }
                    Image {
                        id: staleIndicator
                        visible:  false
                        anchors.left: hdgValue.right
                        source: "/stale.svg"
                    }
                    Button {
                        id: btnData
                        text: "Click Me..."
                        anchors.top: hdgLabel.bottom
                        onClicked: {
                            var currValue = simonP.value
                               ,newValue = parseFloat(currValue) + 1;
                            simonP.setValue(newValue);
                            hdgValue.text = String(newValue);
                        }
                    }
                    Component.onCompleted: {
                        simonP.stale()
                    }
                }
                

                SimonP.H:

                #ifndef SIMONP_H
                #define SIMONP_H
                
                #include <QObject>
                #include <qqml.h>
                
                class SimonP : public QObject {
                    Q_OBJECT
                    Q_PROPERTY(double value READ dataValue WRITE setValue NOTIFY toolTip)
                    QML_ELEMENT
                
                private:
                    bool mblnStale;
                    double mdblEpoch, mdblValue;
                
                    static double msdblValid;
                
                public:
                    explicit SimonP(QObject *parent_ = nullptr);
                
                    double dataValue() { return mdblValue; }
                    Q_INVOKABLE void setValue(double dblValue);
                    Q_INVOKABLE bool stale();
                    double timestamp();
                
                signals:
                    void toolTip(QString strToolTip);
                };
                #endif // SIMONP_H
                

                SimonP.CPP:

                #include <QDateTime>
                
                #include "simonp.h"
                
                static const long sclngSecsInMin(60);
                static const long sclngMinsInHour(60);
                static const long sclngHoursInDay(24);
                static const long sclngMsInSec(1000);
                static const long sclngMsInMin(sclngSecsInMin * sclngMsInSec);
                static const long sclngMsInHour(sclngMinsInHour * sclngMsInMin);
                static const long sclngMsInDay(sclngHoursInDay * sclngMsInHour);
                static const char scszDelim[](", ");
                
                double SimonP::msdblValid = 5000.0; //5 seconds in milliseconds
                
                SimonP::SimonP(QObject* parent_) : QObject(parent_) {
                    mdblEpoch = mdblValue = 0.0;
                }
                
                double SimonP::timestamp() {
                    return mdblEpoch;
                }
                
                void SimonP::setValue(double dblValue) {
                    mdblValue = dblValue;
                    mdblEpoch = QDateTime::currentMSecsSinceEpoch();
                    //Update stale age
                    stale();
                }
                
                bool SimonP::stale() {
                    double dblAge(QDateTime::currentMSecsSinceEpoch());
                    QString strToolTip;
                    bool blnStale;
                    if ( mdblEpoch > 0.0 ) {
                        dblAge -= mdblEpoch;
                    }
                    blnStale = dblAge >= msdblValid;
                
                    if ( blnStale == true ) {
                        long lngRemaining(static_cast<long>(dblAge));
                        long lngDays(lngRemaining / sclngMsInDay);
                        lngRemaining -= (lngDays * sclngMsInDay);
                        long lngHours(lngRemaining / sclngMsInHour);
                        lngRemaining -= (lngHours * sclngMsInHour);
                        long lngMins(lngRemaining / sclngMsInMin);
                        lngRemaining -= (lngMins * sclngMsInMin);
                        long lngSecs(lngRemaining / sclngMsInSec);
                        lngRemaining -= (lngSecs * sclngMsInSec);
                        long lngMS(lngRemaining);
                
                        if ( lngDays > 0 ) {
                            strToolTip.append(QString("%1 days").arg(lngDays));
                        }
                        if ( lngHours > 0 ) {
                            if ( strToolTip.isEmpty() != true ) {
                                strToolTip.append(scszDelim);
                            }
                            strToolTip.append(QString("%1 hours").arg(lngHours));
                        }
                        if ( lngMins > 0 ) {
                            if ( strToolTip.isEmpty() != true ) {
                                strToolTip.append(scszDelim);
                            }
                            strToolTip.append(QString("%1 minutes").arg(lngMins));
                        }
                        if ( strToolTip.isEmpty() != true ) {
                            strToolTip.append(scszDelim);
                        }
                        strToolTip.append(QString("%1").arg(lngSecs));
                
                        if ( lngMS > 0 ) {
                            strToolTip.append(QString(".%1").arg(lngMS));
                        }
                        strToolTip.append(" seconds");
                        strToolTip.prepend("Stale: ");
                    }
                    emit toolTip(strToolTip);
                    return blnStale;
                }
                

                All works great now, tool tip is shown when data is stale and mouse moves over value.

                Kind Regards,
                Sy

                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