Exchange char buffer between Qt c++ and QML
-
Hello,
I am quite new to QML and face a problem when trying to draw a chart from a char buffer.
I use QML as GUI frontend and connect the backend application written in Qt C++ via signals and slots to the UI.
This works fine for primitive types. There i use a signal in my app like newDataAvailable(QVariant data), connect it to a function in my main.qml which then forwards the data by emitting signals that are fetched by the respective components. But i don't know how to exchange more involved types.
I have a buffer of audio data which needs to be displayed from time to time in the UI. Therefore i made a Chart Component with the respective drawing logic using canvas.
But how can i forward the data from the application to the UI, such that it is available for drawing?This question is quite vague, but I hope you get my problem.
Thank you for your help!
Kind regards
-
You need to register types that you need.
"Here":http://qt-project.org/doc/qt-5.1/qtqml/qtqml-cppintegration-exposecppattributes.html and "here":http://qt-project.org/doc/qt-4.8/qml-extending.html is written about this ability and similar situation. -
use QByteArray to wrap your char buffer. QByteArray object can be a property of a QObject-derived class. You should define a custom QObject-derived class that has a QByteArray property. and then register your class to QML engine.
-
Thank you for your responses, I tried this and did the following:
My class myObj which contains the buffer got a new property and methods
@
Q_PROPERTY(QByteArray audioBuffer READ getAudioBuffer)
public:
QByteArray getAudioBuffer() { return m_AudioBuffer; }
private:
QByteArray m_audioBuffer
@
Additionally, the function where i want to draw the chart was modified such that
@
qint64 currentSampleCount = myCustomBuffer.getSize();
char* tmp_buf = new char[currentSampleCount];
myCustomBuffer.read(tmp_buf, currentSampleCount);
m_audioBuffer = QByteArray::fromRawData(tmp_buf, currentSampleCount);
emit triggerDraw();
@And my main.cpp was modified such that
@
QGuiApplication app(argc, argv);QtQuick2ApplicationViewer viewer; myObj obj; viewer.rootContext()->setContextProperty("audioData", &obj); viewer.setMainQmlFile(QStringLiteral("./qml/TestProject/main.qml")); QObject *object = viewer.rootObject(); QObject::connect(&obj, SIGNAL(triggerDraw()), object, SLOT(on_drawBuffer())); viewer.showExpanded(); return app.exec();
@
And in my main.qml I fetch the signal and trigger a new one which is then fetched by the chart component which simply does:
@
console.log("AudioBuffer has length: " + audioData.audioBuffer.length);
@But the result is always undefined. What is wrong in my setup? And when i can access the audioBuffer, how to iterate over the single char elements?
Thanks again.
-
bq.
@
qint64 currentSampleCount = myCustomBuffer.getSize();
char* tmp_buf = new char[currentSampleCount];
myCustomBuffer.read(tmp_buf, currentSampleCount);
@First of all, you might be sure, that your sample size is 1b because of char size.
Secondary, Is type of audioData.audioBuffer defined correctly? -
Hi,
that the size of char is equal to 1 is assured.
I don't get the second part of your answer.
"audioData" is the name i used for registering the instance of myObj to the QML engine, see third listing, line 5. And audioBuffer is the property defined for myObj, see first listing line 1.Or do you mean something different?
-
Could you show result of this:
@console.debug(audioData.audioBuffer);@
?
That's what i mean - the type of audioBuffer in qml js. -
@
console.debug("AudioBuffer" + audioData.audioBuffer);
console.log("AudioBuffer has length: " + audioData.audioBuffer.length);
@Output:
calling getAudioBuffer
AudioBuffer
calling getAudioBuffer
AudioBuffer hast length: undefinedcalling getAudioBuffer is a debug output i put into the getter MyObj::getAudioBuffer
Does not seem to be correct, or? -
It's difficult to me to say about correctness this output because of fact that I don't know anything about way, which was presented by Vincent007.
But I have great doubt that QByteArray represents as nothing at QML.
You can try another words to get array-size, usually it may be size, count, and etc.
In all examples, that i've seen, QByteArray is used as binary source of data.
"Here":http://qt-project.org/doc/qt-5/qtqml-cppintegration-data.html there is some information about type conversion. And there is nothing about QByteArray.
If i was you i would try QVariant.
I hope you'll do it. -
Thanks for your help Roumed,
I am not binded to the current approach. Could you explain a few more words on the approach you mentioned in your first answer?
Thank you!
-
If you study Qt 5 source code, you can find Q_PROPERTY(QByteArray ......
"Q_PROPERTY(QByteArray fragmentShader READ fragmentShader WRITE setFragmentShader NOTIFY fragmentShaderChanged)":http://code.woboq.org/qt5/qtdeclarative/src/quick/items/qquickshadereffect_p.h.html
Therefore, a QByteArray object can be used as a property.
-
Okey, this sources confirm, that QByteArray can be used as property in QML, but if we look at sources of QByteArray there are no Q_INVOKABLE methods.
Some blogs, forums and articles in Google says that at most cases QByteArray uses as Blob, e.g. blob contains wave. But processing of this object is hidden. So, i still have no idea how to use representation of QByteArray in QML, but i interested in it too.
Vincent007, could you give us an example with using count property and getting an element?Dualfaces, i thought, that you have a special type to store audio data, something like AudioBuffer. So, in this case you need to make Q_INVOKABLE methods, all functionallity, that you need. In this case this object must be QObject-derived and register for qml(see "this":http://qt-project.org/doc/qt-5.1/qtqml/qtqml-cppintegration-definetypes.html#registering-c-types-with-the-qml-type-system for more information). You still can do something like this. Thats my idea, but using QByteArray is more sweet.
-
QByteArray is not an QObject-derived class, so member functions of QByteArray cannot be called in QML. Workaround is needed.
bytearraywrapper.h
@
#ifndef BYTEARRAYWRAPPER_H
#define BYTEARRAYWRAPPER_H#include <QObject>
class ByteArrayWrapper : public QObject
{
Q_OBJECT
Q_PROPERTY(QByteArray data MEMBER _data NOTIFY dataChanged)
public:
explicit ByteArrayWrapper(QObject *parent = 0);signals:
void dataChanged();
public slots:
int size();
char at( int i ) const;
private:
QByteArray _data;
};#endif // BYTEARRAYWRAPPER_H
@
bytearraywrapper.cpp
@
#include "bytearraywrapper.h"ByteArrayWrapper::ByteArrayWrapper(QObject *parent) :
QObject(parent),_data("abcdefg")
{
}int ByteArrayWrapper::size()
{
return _data.size();
}char ByteArrayWrapper::at(int i) const
{
return _data.at(i);
}
@
in QML
@
Component.onCompleted: {
console.log(audioBuffer.data)
console.log(audioBuffer.size())
console.log(audioBuffer.at(3))
}
@ -
main.cc
@
main () {
QGuiApplication app(argc, argv);QtQuick2ApplicationViewer viewer; myObj obj;
//! -->
qmlRegisterType<ByteArrayWrapper, 1>("ByteArrayWrapper", 1, 0, "CppType");
//! <--
viewer.rootContext()->setContextProperty("audioData", &obj);
viewer.setMainQmlFile(QStringLiteral("./qml/TestProject/main.qml"));
QObject *object = viewer.rootObject();QObject::connect(&obj, SIGNAL(triggerDraw()), object, SLOT(on_drawBuffer())); viewer.showExpanded(); return app.exec();
}
@
And getAudioBuffer() returns ByteArrayBuffer*. This is important.
Thank you Vincent