File IO in Qt5

I've been thinking about developing a desktop writing application for awhile now, and 2015 has become the year that I hit at this goal in earnest. Prior to this project, I hadn't really done much in the way of desktop GUI applications so I took the internet's advice and got started learning Qt. I decided to use the newer 'QtQuick' application over the mainstay 'QtWidgets' application due to the promise of separating view code into JavaScript like QML and having the application logic written in C++.

Goal

In this tutorial we're going to build a small QtQuick application that saves the contents of a TextArea to a file called 'text.txt'. This file should appear in the application's debug folder. The view is written in QML, and all the application logic is written in C++. It assumes no Qt knowledge. Gists of the finished files can be found at the bottom if you're just looking for sample code.

Background

In order to run this tutorial I assume you have Qt, and Qt Creator installed so we don't need to have long and meaningful conversations about configuring builds. Once Qt is all installed open up the editor and start a new QtQuick application (not a Qt Quick UI -- this doesn't put the appropriate C++ infrastructure in place). You are welcome to name your project anything you like, I've called the project 'example' because I have never had an original thought in my head. I use the Qt Quick Controls component set because it gives you tools that help build a native look and feel see here for more info on that. You can next your way to the project.

QML

QML is the markup language used to design the look and feel of QtQuick applications. Qt generates quite a bit of boilerplate code that we will be deleting -- open up the main.qml file and delete the menuBar, MainForm and MessageDialog.

Within the remaining ApplicationWindow we are going to add a Button and a TextArea. We can add these components in one of two ways -- either by dragging and dropping the components you need into the window using the 'Design' panel in the left hand menu, or you can write out the QML in the editor. You can arrange the components however you like, just remember to give the text area an id -- I called mine saveMe.

C++ Classes and Project Files

Next we need to create the C++ class where we handle the file saving, if we right click on the sources folder we will be able to Create New C++ Class -- we can call this class anything we like, for the sake of clarity I've gone with FileIO this should create both a source (.cpp) and a header (.h) file.

As a quick aside, other guides may ask you to add these files to your project file (ie. 'example.pro') in my experience, Qt adds new C++ classes to the project automatically now and tweaking this file leads to fire and brimstone. However, if you are using an older version of Qt, or you are seeing errors it doesn't hurt to make sure fileio was added to both your sources

SOURCES += main.cpp \
    fileio.cpp

and your headers

HEADERS += \
    fileio.h

FileIO.h

This is our FileIO's header file, header files allow us to make the interface of a class visible to other cpp files -- in particular we will eventually want FileIO's method to be visible to main.cpp. First, let's register a save method, it should be invokable, take in the contents of the Text Area that we want to save as a QString, and not return anything. We add the following line to our header underneath the public: section.

Q_INVOKABLE void save(QString text);

Because we want to be able to access the method publicly we change the class declaration to:

class FileIO : public QObject

And we need to add the Q_OBJECT macro by adding it directly after the method declaration, and before the public: section. The Q_OBJECT macro is used to signal that this file should be run through the meta-object compiler which enables us to use, among other thing Qt's signal and slot system allowing communication between different objects.

After we add the macro, we need to make sure we include QObject in the header:

#include <QObject>

FileIO.cpp

Finally we get to the meat of FileIO the .cpp source file. First, we want to add a method with the same signature as the one we put in the header, we don't want to put this in the constructor, or the desstructor it should be a stand alone method.

void FileIO::save(QString text){ }

Inside that method we create a QFile named file in which we will save our text, I've decided to call mine 'text.txt' but really, any title will do.

QFile file("text.txt");

Next, we add a conditional to ensure we have read/write access on the file -- since we just created the file we may not need this but you probably won't always be writing to a hard coded file so it's a good idea to stick this in here.

if(file.open(QIODevice::ReadWrite)){ }

Now, we create a QTextInputStream, which is used to control input between our program and an external file. We add the text to the stream and then write that stream to the file given -- currently our hardcoded one. Our file saver is now complete, but we still cannot access it from the QML.

QTextStream stream(&file);
stream << text << endl;

Also, be sure to include all the utilities you're using

#include <QFile>

#include <QTextStream>

main.cpp

In order to use this C++ class we need to register it in the main.cpp file, you can do that like this:

engine.rootContext()->setContextProperty("FileIO", new FileIO());

Make sure you include the header of the class you created, ie.

#include "fileio.h"

As well as the QContext which we used above.

#include <QQmlContext>

Pulling it all Together

Our plugin is now available to be used, we simply need to call from the QML it like we would call any C++ class. We add an onClicked property to out button from the first section and call the FileIO save method.

onClicked: FileIO.save(saveMe.text);

Where saveMe is the id you gave the text area.

If you save and run this project you should be able to click the button and find a file in your application's build folder called text.txt with the contents of the text area printed in it.

Well, that's it, my first ever tutorial -- gists of the files can be found below -- email me or tweet me if you have any questions!

Source Code