Thursday, March 29, 2012

Connecting QML to C++, More Practical Example

As a first practical application of connecting C++ to QML I had to integrate a File Open/Close dialog to be accessed from QML, because QML does not have this kind of control natively. So here are the requirements to the simple task:

  • Open button starts the Open File dialog, which allows the user to select a text file
  • When the user selects the file, the contents of this file are loaded into an editable field
  • The user can edit the file
  • Save button opens the Save File dialog, which allows the user to save the contents of the editable field into a new or existing text file

The C++ header file will define only two functions, getFileContents will be called when the Open button is clicked, and saveFileContents will be called when the Save button is clicked. Here is the full header file:

//qmlfile.h
#ifndef QMLFILE_H
#define QMLFILE_H

#include

class QMLFile : public QObject
{
Q_OBJECT

public:
explicit QMLFile(QObject *parent = 0);

Q_INVOKABLE QString getFileContents() const;

Q_INVOKABLE void saveFileContents(QString fileContents) const;
};

#endif // QFILE_H

The C++ code file will implement these functions. getFileContents opens the Open File dialog and waits for the user to select a file. When the file is selected, the function tries to open it and, if successful, returns the string that has file contents. saveFileContents is the opposite: it takes a string as a parameter, opens a Save File dialog, waits for the user to select a file or enter a name of the file, and tries to save the string as file contents. Quick and dirty - a lot of things could go wrong and cause exceptions, but that's not the point at this stage.

//qmlfile.cpp
#include
#include
#include
#include "qmlfile.h"

QMLFile::QMLFile(QObject *parent): QObject(parent)
{

}

QString QMLFile::getFileContents() const
{
QString fileName = QFileDialog::getOpenFileName(NULL, tr("Open File"), "/home", tr("Text Files (*.txt)"));
qDebug() << "fileName:" << fileName;
QFile file(fileName);
if(!file.open(QIODevice::ReadOnly | QIODevice::Text))
return "";

QString content = file.readAll();
file.close();
return content;
}

void QMLFile::saveFileContents(QString fileContents) const
{
QString fileName = QFileDialog::getSaveFileName(NULL, tr("Save File"), "/home", tr("Text Files (*.txt)"));

QFile file(fileName);
if(file.open(QIODevice::WriteOnly | QIODevice::Text))
{
qDebug() << "created file:" << fileName;
QTextStream stream(&file);
stream << fileContents << endl;
file.close();
return;
}
else
{
qDebug() << "could not create file:" << fileName;
return;
}
}

Now the only two things that are left is to update the main files: main.cpp and main.qml. In main.cpp I add the two lines similar to the example in the previous post.

//in main.cpp after viewer
QMLFile qmlFile;
viewer.rootContext()->setContextProperty("QMLFile", &qmlFile);

In the main.qml I will implement the onClicked events for Open and Save buttons. Each will only take a single line.

//open file and load contents into TextEdit
txt.text = QMLFile.getFileContents();
//get contents of the TextEdit and save to a file
QMLFile.saveFileContents(txt.text);

Now I can build and run the application and open some file, and if everything goes smoothly you'll see the results similar to the screenshot below - contents of a text file loaded into the QML TextEdit component and displayed.

Text File Loaded into QML TextEdit

For reference, the full main.qml file.

//main.qml
import QtQuick 1.0

Rectangle {
width: 360; height: 360

Rectangle{
id:buttons
height: 50; width: parent.width; anchors.top: parent.top

Rectangle{
id: btnOpen
width: 50; height: parent.height; anchors.left: parent.left

Image{
anchors.fill: parent; source: "images/open.png"
}

MouseArea{
anchors.fill: parent
onClicked: {
txt.text = QMLFile.getFileContents();
}
}
}

Rectangle{
id: btnSave
width:50; height: parent.height; anchors.left: btnOpen.right

Image{
anchors.fill: parent; source: "images/save.png"
}

MouseArea{
anchors.fill: parent
onClicked: {
QMLFile.saveFileContents(txt.text);
}
}
}
}

Rectangle{
id:textHandle
width: parent.width; height: parent.height - buttons.height; anchors.bottom: parent.bottom

TextEdit{
id: txt; anchors.fill: parent
}
}
}

References

QFileDialog Class Reference
Opening a file from a Qt String by . Also posted on my website

No comments: