Send data from C++ to QML model



  • Hello All,

    Probably this has been discussed thousands of times and there are many posts about it, but I couldn't find any complete and useful one. I have a Qt Application with C++. My GUI is defined using multiple .qml files. Now I want to send a data from the C++ code to the QML model. After checking some posts, I came with this:

    //audiometadata.h
    #ifndef AUDIOMETADATA_H
    #define AUDIOMETADATA_H
    
    #include <QObject>
    
    class AudioMetadata : public QObject
    {
        Q_OBJECT
        Q_PROPERTY(qint32 gAudioSongPlayedTime READ gAudioSongPlayedTime WRITE setGAudioSongPlayedTime NOTIFY gAudioSongPlayedTimeChanged)
        Q_PROPERTY(qint32 gAudioSongTotalTime READ gAudioSongTotalTime WRITE setGAudioSongTotalTime NOTIFY gAudioSongTotalTimeChanged)
    
    public:
        explicit AudioMetadata(QObject *parent = 0);
        ~AudioMetadata();
        qint32 gAudioSongPlayedTime();
        void setGAudioSongPlayedTime(qint32 value);
        qint32 gAudioSongTotalTime();
        void setGAudioSongTotalTime(qint32 value);
    
    signals:
        void gAudioSongPlayedTimeChanged();
        void gAudioSongTotalTimeChanged();
    
    private:
        qint32 m_audioSongPlayedTime;
        qint32 m_audioSongTotalTime;
    
    public slots:
    };
    
    #endif // AUDIOMETADATA_H
    
    //audiometadata.cpp
    #include "audiometadata.h"
    #include <QDebug>
    
    AudioMetadata::AudioMetadata(QObject *parent) : QObject(parent)
    {
        m_audioSongPlayedTime = 20;
        m_audioSongTotalTime = 100;
    }
    
    
    AudioMetadata::~AudioMetadata() {}
    
    
    qint32 AudioMetadata::gAudioSongPlayedTime()
    {
        return m_audioSongPlayedTime;
    }
    
    
    void AudioMetadata::setGAudioSongPlayedTime(qint32 value)
    {
        qDebug() << "Audio Song play time " << value;
        if(m_audioSongPlayedTime != value)
        {
            m_audioSongPlayedTime = value;
            qDebug() << "Audio Song play time " << m_audioSongPlayedTime;
            emit gAudioSongPlayedTimeChanged();
        }
        return;
    }
    
    
    qint32 AudioMetadata::gAudioSongTotalTime()
    {
        return m_audioSongTotalTime;
    }
    
    
    void AudioMetadata::setGAudioSongTotalTime(qint32 value)
    {
        if(m_audioSongTotalTime != value)
        {
            m_audioSongTotalTime = value;
            emit gAudioSongPlayedTimeChanged();
        }
        return;
    }
    
    //main.cpp
    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    #include <QtQml>
    #include "audiometadata.h"
    
    
    int main(int argc, char *argv[])
    {
        QGuiApplication app(argc, argv);
    
        qmlRegisterType<AudioMetadata>("com.xequtor.cluster.audiometadata", 1, 0, "AudioMetadata");
    
        QQmlApplicationEngine engine;
        engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    
        AudioMetadata songData;
        songData.setGAudioSongTotalTime(320);
        songData.setGAudioSongPlayedTime(167);
    
        return app.exec();
    }
    
    //the .qml file, where I want to use the data
    import QtQuick 2.0
    import QtGraphicalEffects 1.0
    import com.xequtor.cluster.audiometadata 1.0
    
    Item {
        id: mediaContainer
        width: 400
        height: 768
    
        AudioMetadata {
            id: audioPlayerData
        }
    
        property string lAlbumArtGenericPath: "images/AUDIO_ALBUM_ART_GENERIC.png"
        property string gAlbumArtDynamicPath: "images/AUDIO_ALBUM_ART_DRE.png"
        property bool gAlbumArtAvailable: false
        property string gMetaDataTextLine1: "Dr. Dre feat. Snoop Dogg"
        property string gMetaDataTextLine2: "The Next Episode"
        property string gMetaDataTextLine3: "The Chronic"
        property int gCurrentSongTotalTime: audioPlayerData.gAudioSongTotalTime
        property int gCurrentSongPlayedTime: audioPlayerData.gAudioSongPlayedTime
        property string lCurrentSongTotalTime: "00:00"
    
    // .............. Some additonal code
    
     onGCurrentSongPlayedTimeChanged: {lCurrentSongTotalTime = getTrackTime(gCurrentSongPlayedTime); console.log(lCurrentSongTotalTime)}
    
    // ........... Some more code
    
     Text {
                    id: currentSongPlayedTime
                    anchors.verticalCenter: trackProgressContainer.verticalCenter
                    anchors.left: progressBarBackground.right
                    anchors.leftMargin: 20
                    width: 150
                    height: 20
                    horizontalAlignment: Text.AlignHCenter
                    verticalAlignment: Text.AlignVCenter
                    font.family: "Arial"
                    font.pointSize: 16
                    color: "#FFFFFF"
                    text: lCurrentSongTotalTime
                }
    

    I am able to get the data with the values from the class constructor (20 and 100). But when I create an object in the main.cpp file and call the WRITE functions from it, they are not updated in the qml model and the old values from the constructor (20 and 100) are still used. I saw in the console that the WRITE functions are called with the new values, but somehow they are not received by the QML model. What am I doing wrong? If you have a similar post or some complete example, which shows how is all this done, can you give me the link?

    Best Regards,
    Ahmed



  • I think it is because you create one object in the QML code, and another object in the C++ code. These are not the same object, and therefore do not share the same values for their properties.

    If you update the values from the QML, what happens then? So somewhere in the QML code, do audioPlayerData.gAudioSongPlayedTime = 666

    Maybe you could add a timer that sets gAudioSongPlayedTime, eg

    Timer
    {
        interval: 10
        repeat: true
        running: true
        onTriggered: audioPlayerData.gAudioSongPlayedTime = audioPlayerData.gAudioSongPlayedTime + 10
    }
    

    If this is the issue, and you need your object to be persistent, you might need to look into making it a singleton object: http://doc.qt.io/qt-5/qtqml-cppintegration-definetypes.html#registering-non-instantiable-types



  • Yes stcorp is correct, The object you are updating and the object you have in qml are different.

    you can either follow stcorp suggession. or you have to expose the object as context property as shown below and the same object you need to use in qml as well

        QGuiApplication app(argc, argv);
    
    //    qmlRegisterType<AudioMetadata>("com.xequtor.cluster.audiometadata", 1, 0, "AudioMetadata");
    
        QQmlApplicationEngine engine;
        engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    
        AudioMetadata songData;
        view.engine()->rootContext()->setContextProperty("songData", &songData);
        view.setSource(QUrl::fromLocalFile("MyItem.qml"));
    
    
        songData.setGAudioSongTotalTime(320);
        songData.setGAudioSongPlayedTime(167);
    
        return app.exec();
    
    


  • Thanks for the answers, guys. I was able to send data from the C++ code to the QML. However, I still have some problems. I did the following:

    //main.cpp
    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    #include <QtQml>
    #include "audiometadata.h"
    
    
    int main(int argc, char *argv[])
    {
        QGuiApplication app(argc, argv);
        QQmlApplicationEngine engine;
        engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    
        AudioMetadata songData;
        engine.rootContext()->setContextProperty("songData", &songData);
    
        songData.setGAudioSongTotalTime(320);
        songData.setGAudioSongPlayedTime(167);
    
        return app.exec();
    }
    
    //main.qml
    import QtQuick.Window 2.2
    import QtGraphicalEffects 1.0
    
    Window {
        id: window
        visible: true
        width: 1600
        height: 768
        color: "#000000"
        title: qsTr("Fancy Cluster")
    
    
        MouseArea {
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.verticalCenter: parent.verticalCenter
            onClicked: {
                console.log(qsTr('Clicked on background. Text: "' + textEdit.text + '"'))
            }
        }
    
        GaugeRTTs {
            gRttCruiseControlMode: 5
            gRttLeftTurnSignalMode: 2
            gRttRightTurnSignalMode: 2
    
        }
    
        Information {
            id: infoContainer
            x: 1174
            height: 768
            anchors.right: parent.right
            anchors.rightMargin: 0
        }
    
        Media {
            id: mediaContainer
            height: 768
            anchors.left: parent.left
            anchors.leftMargin: 0
    
           gCurrentSongPlayedTime: songData.gAudioSongPlayedTime
            gCurrentSongTotalTime: songData.gAudioSongTotalTime
        }
    
        Gauge {
            anchors.horizontalCenter: parent.horizontalCenter;
            anchors.verticalCenter: parent.verticalCenter
        }
    }
    

    Did not change anything else. Everything works fine, but I am getting ReferenceError: songData is not defined error in the beginning. How can this error be fixed?

    Also, is there any other way to communicate between C++ code and Qml and vice versa, or this is the only way?

    Best Regards,



  • @Xequtor I think you need to set your context property after creating the QQmlApplicationEngine, but before loading main.qml. I'm not completely sure, but give it a try and see what happens



  • Ah yeah, that was it. Thanks :)


Log in to reply
 

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