Reading and writing files in QML (Qt)

If your files is text only, you can use XMLHttpRequest (both for reading and writing), like this:

function openFile(fileUrl) {
    var request = new XMLHttpRequest();
    request.open("GET", fileUrl, false);
    request.send(null);
    return request.responseText;
}

function saveFile(fileUrl, text) {
    var request = new XMLHttpRequest();
    request.open("PUT", fileUrl, false);
    request.send(text);
    return request.status;
}

Here is demo app (Qt 5.6):

import QtQuick 2.6
import QtQuick.Dialogs 1.2
import QtQuick.Controls 1.5

ApplicationWindow {
    visible: true
    width: 640
    height: 480
    title: qsTr("Demo App")

    function openFile(fileUrl) {
        var request = new XMLHttpRequest();
        request.open("GET", fileUrl, false);
        request.send(null);
        return request.responseText;
    }

    function saveFile(fileUrl, text) {
        var request = new XMLHttpRequest();
        request.open("PUT", fileUrl, false);
        request.send(text);
        return request.status;
    }

    FileDialog {
        id: openFileDialog
        nameFilters: ["Text files (*.txt)", "All files (*)"]
        onAccepted: textEdit.text = openFile(openFileDialog.fileUrl)
    }

    FileDialog {
        id: saveFileDialog
        selectExisting: false
        nameFilters: ["Text files (*.txt)", "All files (*)"]
        onAccepted: saveFile(saveFileDialog.fileUrl, textEdit.text)
    }

    menuBar: MenuBar {
        Menu {
            title: qsTr("File")
            MenuItem {
                text: qsTr("&Open")
                onTriggered: openFileDialog.open()
            }
            MenuItem {
                text: qsTr("&Save")
                onTriggered: saveFileDialog.open()
            }
            MenuItem {
                text: qsTr("Exit")
                onTriggered: Qt.quit();
            }
        }
    }

    TextArea {
        id: textEdit
        anchors.fill: parent
        text:
            "Lorem ipsum dolor sit amet, consectetur adipisicing elit, " +
            "sed do eiusmod tempor incididunt ut labore et dolore magna " +
            "aliqua. Ut enim ad minim veniam, quis nostrud exercitation " +
            "ullamco laboris nisi ut aliquip ex ea commodo cosnsequat. ";
    }
}

P.S. Note that all modern browsers will throw security exception if you will try use functions like above, but QML allow it (even for file rewrites). Not sure is it by design or mistake, though.


The example written by Nokia in the tutorial is not a pure QML program. It contains both C++ and QML. This kind of program is usually a C++ program which loads a QML file and renders it. C++ programs usually begin with a function called int main(int argc, char *argv[]);. In your case, it is this "main()" function which loads your QML main file (main.qml) file and renders it.

But before loading the QML main file, you have to tell the QML system that you will use a custom QML class called FileIO. For this, you will have to use the int qmlRegisterType<T>(const char * package, int majorVersion, int minorVersion, char * classNameInQML); C++ function. It takes about 5 parameters :

  • T : the C++ template parameter. It is your C++ class (FileIO).
  • package : all QML classes are in package which are versionned. This is the name of the package.
  • majorVersion : all QML classes are in package which are versionned. This is the major version number of the package.
  • minorVersion : all QML classes are in package which are versionned. This is the minor version number of the package.
  • classNameInQML : all QML classes are in package which are versionned. This is the name of your class that you will use in QML files Most of the time the name is the same than the C++ class name.

For using this function, you have to include a C++ header in the C++ file where you write it :

  • If you use Qt 4, the header is <QtDeclarative>.
  • If you use Qt 5, the header is <QtQml>.

In the end you should have some stuff like this :

main.cpp (file with the main() C++ function) :

// C++ header to include for using qmlRegisterType();
#include <QtDeclarative>    // If you use Qt4
#include <QtQml>            // If you use Qt5

// Some stuff used by the main(); function
#include <QApplication>
#include <QLatin1String>

#include "ui/qtquickapplicationviewer.hpp"    // Something which manages your QML files. Qt Creator will generate it for you if you use it to code..
#include "fileio.h"    // Your FileIO C++ class

/**
 * @fn Q_DECL_EXPORT int main(int argc, char *argv[])
 * @brief The C++ main(); function. Your program begins HERE.
 */
Q_DECL_EXPORT int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    // ...

    // Declaring your C++ class to the QML system
    qmlRegisterType<FileIO>("MyCustomClasses", 1, 0, "FileIOQML");

    // ...

    // Loading your main QML file
    QLatin1String mainQMLFile = "./ui/qml/main.qml";
    QtQuickApplicationViewer viewer;
    viewer.setMainQmlFile(mainQMLFile);

    // Showing how beautiful your QML interface is :)
    viewer.showExpanded();

    // Now let's play with your QML interface is :)
    return app.exec();
}

main.qml file to load (right from the Nokia tutorial) :

import QtQuick 1.1
import MyCustomClasses 1.0

Rectangle {
    width: 360
    height: 360
    Text {
        id: myText
        text: "Hello World"
        anchors.centerIn: parent
    }

    FileIOQML {
        id: myFile
        source: "my_file.txt"
        onError: console.log(msg)
    }

    Component.onCompleted: {
        console.log( "WRITE"+ myFile.write("TEST"));
        myText.text =  myFile.read();
    }
}

NB : I have changed some "FileIO" from the Nokia tutorial in order to avoid confusions.


A complete example of FileIO can be found on this page: https://qmlbook.github.io/ch17-extensions/extensions.html#fileio-implementation

class FileIO : public QObject {
    ...
    Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
    Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
    ...
public:
    Q_INVOKABLE void read();
    Q_INVOKABLE void write();
    ...
}

We will leave out the properties, as they are simple setters and getters.

The read method opens a file in reading mode and reads the data using a text stream.

void FileIO::read()
{
    if(m_source.isEmpty()) {
        return;
    }
    QFile file(m_source.toLocalFile());
    if(!file.exists()) {
        qWarning() << "Does not exits: " << m_source.toLocalFile();
        return;
    }
    if(file.open(QIODevice::ReadOnly)) {
        QTextStream stream(&file);
        m_text = stream.readAll();
        emit textChanged(m_text);
    }
}

When the text is changed it is necessary to inform others about the change using emit textChanged(m_text). Otherwise, property binding will not work.

The write method does the same but opens the file in write mode and uses the stream to write the contents.

void FileIO::write()
{
    if(m_source.isEmpty()) {
        return;
    }
    QFile file(m_source.toLocalFile());
    if(file.open(QIODevice::WriteOnly)) {
        QTextStream stream(&file);
        stream << m_text;
    }
}

And source code can be found here: https://github.com/qmlbook/qmlbook/tree/master/docs/ch17-extensions/src/fileio

Tags:

Qt

Qml