QML and js reading json file and change content "on the go"
-
Hi!
How can I get json content (paths to text, image and video) to show in a listView? Each media it's a Item in listViewI'm studying and modifying the "Qt5_CinematicExperience":http://quitcoding.com/download/Qt5_CinematicExperience_1.0.tgz. This code has a "dummy" model. What I want to do it's update the content everytime that I verify that json file has changed.
A lot has to be made through Javascript correct? What are the best practices?
-
Hey again, I am not certain I understand what you want to do, do you have JSON content in an external file and want to read it so it will be available in QML (as JavaScript) or where is the JSON string coming from?
In my projects I usually use c++ for that and emit the parsed JSON object or array to QML, so check out the Qt class "QJsonDocument":http://qt-project.org/doc/qt-5.0/qtcore/qjsondocument.html
You can also use pure JavaScript to parse the JSON string, but you can't access the file system from QML as far as I know, so you need c++ anyway.
If you want I can publish my JsonFile QML "plugin", that is a simple way to read and write JSON files from QML.
-
Hi again! ;)
I have a JSON file with the sample content:
@ {"id":32,
"type":3,
"title":"another video",
"media":{
"video":{"url":"/media/videos/sample.mp4","filename":"sample.mp4"},
"image":null,
"text":null,
"qrcode":{"url":"/media/qrcodes/ceaa2aa649.png","filename":"ceaa2aa649.png"}
}
}, ...@I need to read this file (I guess using javascript or your plugin ;) and set all the structure in ListView, all this on the fly, because this json file can changed at any moment.
I do not know if this is the best solution. I've made this using python on backend and HTML5+JavaScript on frontend. But I want to change to Qt5 because this application will run in a raspberrypi. And Qt5 seems more faster than using some web browser.
Can I read a local file through "xhr":http://qmlbook.org/ch11/index.html?highlight=xhr#local-files right?
For now I'm running away from c++ ;P I'm trying to do everything with qml and js.
-
yeah you should be able to use XMLHttpRequest and JSON.parse if you don't want any c++ in your project :D
I just made my own c++ plugin because XMLHttpRequest has some limitations, you can't easily check if a file exists and you cant write to files with it I think (so only read access), but in your case that should be fine so just use it I guess.Just if you want to know how my JsonFile plugin looks like in QML:
@
JsonFile {
id: jsonFile
name: "foo.json"
}...
jsonFile.write([1,2,3]) // write any JavaScript array or object
var data = jsonFile.read() // read JSON file into JavaScript object or array
@it is easy to use from QML, not that the c++ part is complicated, but is has some helper functions and stuff like jsonFile.size (file size) or jsonFile.exists and some other stuff.
-
the problem is the update of the json file. Read it again and then change the slide content.
I know it's ask too much, but can someone put some simple code example?
-
I don't know what you mean by update of the file, what changes the JSON file? if the file is dynamically changed outside of your application you have to use a file watcher or or poll the contents every 'x' seconds, that is not possible with QML. Maybe I understand you wrong, sounds a little weird with your JSON file!?
-
I would love to download your plugin. How can I add it to my project?
-
I haven't published it yet, but I can just post the code here it isn't that much.
jsonfile.h
@
#ifndef JSONFILE_H
#define JSONFILE_H#include <QObject>
#include <QFile>
#include <QVariant>class JsonFile : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
Q_PROPERTY(QString fileName READ fileName NOTIFY nameChanged)
Q_PROPERTY(bool exists READ exists)
Q_PROPERTY(bool writeable READ writeable)
Q_PROPERTY(bool readable READ readable)
Q_PROPERTY(qint64 size READ size)
Q_PROPERTY(QString error READ error)public:
explicit JsonFile(QObject *parent = 0);
JsonFile(const QString &name, QObject *parent = 0);inline QString name() const { return m_file.fileName(); } QString fileName() const; inline bool exists() const { return m_file.exists(); } inline bool writeable() const { return m_file.permissions().testFlag(QFileDevice::WriteUser); } inline bool readable() const { return m_file.permissions().testFlag(QFileDevice::ReadUser); } inline qint64 size() const { return m_file.size(); } inline QString error() const { return m_error; } Q_INVOKABLE QString relativeFilePath(const QString &dir = QString()) const; Q_INVOKABLE bool rename(const QString &newName); Q_INVOKABLE inline bool copy(const QString &newName) { return m_file.copy(newName); } Q_INVOKABLE inline bool remove() { return m_file.remove(); } Q_INVOKABLE bool write(const QVariant &data); Q_INVOKABLE QVariant read();
signals:
void nameChanged(const QString &name);public slots:
void setName(const QString &name);private:
QFile m_file;
QString m_error;
};#endif // JSONFILE_H
@jsonfile.cpp
@
#include "jsonfile.h"#include <QUrl>
#include <QFileInfo>
#include <QDir>
#include <QJsonDocument>JsonFile::JsonFile(QObject *parent) :
QObject(parent)
{
}JsonFile::JsonFile(const QString &name, QObject *parent) :
QObject(parent), m_file(name)
{
}void JsonFile::setName(const QString &name)
{
// fix to convert URL's to local file names
QUrl url(name);
QString localName = url.isLocalFile() ? url.toLocalFile() : name;
if (m_file.fileName() != localName) {
m_file.setFileName(localName);
emit nameChanged(localName);
}
}QString JsonFile::fileName() const
{
return QFileInfo(m_file).fileName();
}QString JsonFile::relativeFilePath(const QString &dir) const
{
return QDir(dir).relativeFilePath(m_file.fileName());
}bool JsonFile::rename(const QString &newName)
{
bool success = m_file.rename(newName);
if (success) {
emit nameChanged(newName);
}
return success;
}bool JsonFile::write(const QVariant &data)
{
if (m_file.fileName().isEmpty()) {
m_error = tr("empty name");
return false;
}
QJsonDocument doc = QJsonDocument::fromVariant(data);
if (doc.isNull()) {
m_error = tr("cannot convert '%1' to JSON document").arg(data.typeName());
return false;
}
if (doc.isEmpty()) {
m_error = tr("empty data");
return false;
}
QByteArray json = doc.toJson();
if (!m_file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) {
m_error = tr("cannot open file '%1' for writing: %2")
.arg(m_file.fileName()).arg((m_file.errorString()));
return false;
}
bool success = m_file.write(json) == json.size();
m_file.close();
return success;
}QVariant JsonFile::read()
{
if (m_file.fileName().isEmpty()) {
m_error = tr("empty name");
return QVariant();
}
if (!m_file.open(QIODevice::ReadOnly | QIODevice::Text)) {
m_error = tr("cannot open file '%1' for reading: %2")
.arg(m_file.fileName()).arg((m_file.errorString()));
return QVariant();
}
QByteArray json = m_file.readAll();
m_file.close();
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(json, &error);
if (error.error != QJsonParseError::NoError) {
m_error = tr("invalid JSON file '%1' at offset %2")
.arg(error.errorString()).arg(error.offset);
return QVariant();
}
return doc.toVariant();
}
@just register that class in the QML engine, like always in main.cpp or where you do that.
@
qmlRegisterType<JsonFile>("JsonFile", 1, 0, "JsonFile");
@I think that class should be fairly easy to understand, don't get scared by the amount f properties and methods, most of them are just helper functions as you can see :)
if any error happens (file not found while reading, invalid JSON content, etc) the error message will be available through the "error" property.
Hope that helps :)
-
That help me a lot! Thanks! But how can I print the json content (debug)?
-
the raw JSON content is only available in the c++ file, look at the method JsonFile::read()
@
QByteArray json = m_file.readAll();
@
that json variable holds the content of the file, you can print it to the console with
@
qDebug() << json;
@
might need to include QDebug for that
@
#include <QDebug>
@
at the top of the file (I don't know how much c++ you know?)if you rather want to print the parsed content as JavaScript object or array you can just to that in QML?
-
I "read" the JSON file in Qml but the content it's available in C++ file?
My knowledge in C++ it's almost nothing :(
-
well to explain that a little, everything that comes from outside of QML (a file on your disk, network etc) goes through c++, you might not see it but If you use an Image in QML the file gets loaded and decoded in c++ and then transferred to QML.
Usually you don't need to see the raw JSON content, so the question here is what you want do to? You can just debug the decoded JSON object/array in QML, that should be fine unless there is an error and you need to know why.
In that case you should learn how to use the debugger with QML and c++, you can just set a breakpoint in Qt Creator and see the value at that point without printing the value to the console. You might want to read this article about debugging in QML http://qt-project.org/doc/qt-5.0/qtquick/qtquick-debugging.htmlFor simple debug purposes of javaScript objects your can also use
@
JSON.stringify(obj)
// use it with my JsonFile like this
console.log(JSON.stringify(jsonFile.read()))
@
that might look stupid to parse the JSON string and then convert it back to a string, but it is the easiest way for debugging purposes since the console cannot print objects (you will just see [object Object] or something simular). -
@Xander84 said in QML and js reading json file and change content "on the go":
I haven't published it yet, but I can just post the code here it isn't that much.
jsonfile.h
@
#ifndef JSONFILE_H
#define JSONFILE_H#include <QObject>
#include <QFile>
#include <QVariant>class JsonFile : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
Q_PROPERTY(QString fileName READ fileName NOTIFY nameChanged)
Q_PROPERTY(bool exists READ exists)
Q_PROPERTY(bool writeable READ writeable)
Q_PROPERTY(bool readable READ readable)
Q_PROPERTY(qint64 size READ size)
Q_PROPERTY(QString error READ error)public:
explicit JsonFile(QObject *parent = 0);
JsonFile(const QString &name, QObject *parent = 0);inline QString name() const { return m_file.fileName(); } QString fileName() const; inline bool exists() const { return m_file.exists(); } inline bool writeable() const { return m_file.permissions().testFlag(QFileDevice::WriteUser); } inline bool readable() const { return m_file.permissions().testFlag(QFileDevice::ReadUser); } inline qint64 size() const { return m_file.size(); } inline QString error() const { return m_error; } Q_INVOKABLE QString relativeFilePath(const QString &dir = QString()) const; Q_INVOKABLE bool rename(const QString &newName); Q_INVOKABLE inline bool copy(const QString &newName) { return m_file.copy(newName); } Q_INVOKABLE inline bool remove() { return m_file.remove(); } Q_INVOKABLE bool write(const QVariant &data); Q_INVOKABLE QVariant read();
signals:
void nameChanged(const QString &name);public slots:
void setName(const QString &name);private:
QFile m_file;
QString m_error;
};#endif // JSONFILE_H
@jsonfile.cpp
@
#include "jsonfile.h"#include <QUrl>
#include <QFileInfo>
#include <QDir>
#include <QJsonDocument>JsonFile::JsonFile(QObject *parent) :
QObject(parent)
{
}JsonFile::JsonFile(const QString &name, QObject *parent) :
QObject(parent), m_file(name)
{
}void JsonFile::setName(const QString &name)
{
// fix to convert URL's to local file names
QUrl url(name);
QString localName = url.isLocalFile() ? url.toLocalFile() : name;
if (m_file.fileName() != localName) {
m_file.setFileName(localName);
emit nameChanged(localName);
}
}QString JsonFile::fileName() const
{
return QFileInfo(m_file).fileName();
}QString JsonFile::relativeFilePath(const QString &dir) const
{
return QDir(dir).relativeFilePath(m_file.fileName());
}bool JsonFile::rename(const QString &newName)
{
bool success = m_file.rename(newName);
if (success) {
emit nameChanged(newName);
}
return success;
}bool JsonFile::write(const QVariant &data)
{
if (m_file.fileName().isEmpty()) {
m_error = tr("empty name");
return false;
}
QJsonDocument doc = QJsonDocument::fromVariant(data);
if (doc.isNull()) {
m_error = tr("cannot convert '%1' to JSON document").arg(data.typeName());
return false;
}
if (doc.isEmpty()) {
m_error = tr("empty data");
return false;
}
QByteArray json = doc.toJson();
if (!m_file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) {
m_error = tr("cannot open file '%1' for writing: %2")
.arg(m_file.fileName()).arg((m_file.errorString()));
return false;
}
bool success = m_file.write(json) == json.size();
m_file.close();
return success;
}QVariant JsonFile::read()
{
if (m_file.fileName().isEmpty()) {
m_error = tr("empty name");
return QVariant();
}
if (!m_file.open(QIODevice::ReadOnly | QIODevice::Text)) {
m_error = tr("cannot open file '%1' for reading: %2")
.arg(m_file.fileName()).arg((m_file.errorString()));
return QVariant();
}
QByteArray json = m_file.readAll();
m_file.close();
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(json, &error);
if (error.error != QJsonParseError::NoError) {
m_error = tr("invalid JSON file '%1' at offset %2")
.arg(error.errorString()).arg(error.offset);
return QVariant();
}
return doc.toVariant();
}
@just register that class in the QML engine, like always in main.cpp or where you do that.
@
qmlRegisterType<JsonFile>("JsonFile", 1, 0, "JsonFile");
@I think that class should be fairly easy to understand, don't get scared by the amount f properties and methods, most of them are just helper functions as you can see :)
if any error happens (file not found while reading, invalid JSON content, etc) the error message will be available through the "error" property.
Hope that helps :)
which version of qt used for this code?
i tried use that, but received 2 errors:
@
inline QString name() const { return m_file.fileName(); }
/home/isaac/projetos/estudo/teste_tr/jsonfile.h:23: error: C++ requires a type specifier for all declarations@
@
QString JsonFile::fileName() const
/home/isaac/projetos/estudo/teste_tr/jsonfile.cpp:29: error: out-of-line definition of 'fileName' does not match any declaration in 'JsonFile'
@I'm using 5.14.1