Solved 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 :)