QFile and QSaveFile are classes in the CopperSpice Core library. Both of these classes contain methods which can be used to save text to a file. This example will explain why using QSaveFile is often over looked and when it is the better choice.
#include <QtCore>
#include <QtGui>
class MainWindow : public QWidget
{
public:
MainWindow();
private:
void selectFileName();
void save_F();
void save_SF();
QPlainTextEdit *text;
QLineEdit *fName;
};
This declaration for MainWindow declares three slot methods and two edit widgets. The widgets could have been declared in the body of the code and passed as parameters. We chose this approach for readability.
MainWindow::MainWindow()
{
setMinimumSize(650, 350);
text = new QPlainTextEdit();
text->setPlainText("Modify this text as desired.");
QFont font = text->font();
font.setPointSize(11);
text->setFont(font);
QLabel *label = new QLabel("File Name:");
fName = new QLineEdit();
QPushButton *saveAs_pb = new QPushButton();
saveAs_pb ->setText("Select File Name");
QPushButton *save_F_pb = new QPushButton();
save_F_pb->setText("Save with QFile");
QPushButton *save_SF_pb = new QPushButton();
save_SF_pb->setText("Save with QSaveFile");
QPushButton *close_pb = new QPushButton();
close_pb->setText("Close");
QGridLayout *grid = new QGridLayout();
grid->setHorizontalSpacing(20);
grid->setVerticalSpacing(15);
grid->addWidget(text, 3, 1, 1, 2);
grid->addWidget(label, 5, 1);
grid->addWidget(fName, 5, 2);
grid->addItem(new QSpacerItem(0, 20, QSizePolicy::Fixed,
QSizePolicy::Fixed), 4, 1);
QHBoxLayout *layout_pb = new QHBoxLayout();
layout_pb->addStretch();
layout_pb->addSpacing(25);
layout_pb->addWidget(saveAs_pb);
layout_pb->addSpacing(65);
layout_pb->addWidget(save_F_pb);
layout_pb->addSpacing(65);
layout_pb->addWidget(save_SF_pb);
layout_pb->addSpacing(65);
layout_pb->addWidget(close_pb);
layout_pb->addSpacing(25);
layout_pb->addStretch();
QVBoxLayout *layoutMain = new QVBoxLayout(this);
layoutMain->setContentsMargins(30, 40, 30, 15);
layoutMain->addLayout(grid);
layoutMain->addSpacing(75);
layoutMain->addLayout(layout_pb);
connect(saveAs_pb, &QPushButton::clicked,
this, &MainWindow::selectFileName);
connect(save_F_pb, &QPushButton::clicked,
this, &MainWindow::save_F);
connect(save_SF_pb, &QPushButton::clicked,
this, &MainWindow::save_SF);
connect(close_pb, &QPushButton::clicked,
this, &QWidget::close);
}
The code on lines 5 through 10 set up a QPlainTextEdit where the contents of the file is entered.
Lines 12 and 13 configure the edit widget for the file name. The value for fName can be entered manually or populated by clicking the “Select File Name” push button.
The set up for the four push buttons appears on lines 15 through 25. The code from 27 to 55 adjusts the look of the user interface.
Signal / Slot Connections
Lines 57, 60, and 63 set up what actions should be taken when one of the first three push buttons are clicked. The following block of code are the implementations of the three slot methods specified by these calls to connect().
void MainWindow::selectFileName()
{
QFileDialog::FileDialogOptions options =
QFileDialog::DontResolveSymlinks;
QString filter("Text Files (*.txt)");
QString target = QFileDialog::getSaveFileName(this,
"Enter File Name", "output.txt", filter,
nullptr, options);
if (! target.isEmpty()) {
fName->setText(target);
}
}
void MainWindow::save_F()
{
QFile outFile(fName->text());
outFile.open(QIODevice::WriteOnly);
outFile.write(text->toPlainText().toUtf8());
QMessageBox::information(this, "Saved with QFile",
"File saved to:\n" + outFile.fileName());
}
void MainWindow::save_SF()
{
QSaveFile outFile(fName->text());
outFile.open(QIODevice::WriteOnly);
outFile.write(text->toPlainText().toUtf8());
outFile.commit();
QMessageBox::information(this, "Saved with QSaveFile",
"File saved to:\n" + outFile.fileName());
}
The slot method selectFileName() calls the static method getSaveFileName() in QFileDialog. This static method will display a file dialog window where the user can select an existing file name or move to any valid directory and then enter a new file name. If a file name is provided then line 13 will copy it to the fName widget.
When the method save_F() is invoked a new QFile object is created and the contents of the text widget is saved to the file name specified by fName. In a user application this method should not create the file if the value of fName is empty. There should also be a check after line 21 to ensure outFile was opened successfully.
The save_SF() method is almost identical to save_F(). The only addition is on line 34 which calls commit(). The QSaveFile class works by saving the contents of the file to a temporary file. Calling commit() closes the temporary file then renames it to the user specified name, which in our example is fName. The commit() method returns a bool which indicates if the file was successfully written and renamed.
The QFile class has more I/O functionality and is used more frequently for reading and writing to a file. So when does it make sense to use the QSaveFile class? QSaveFile writes text or binary files without losing existing data if the write process fails. It should be used when saving large files, overwriting a file, or you need to ensure the the output file is never partially written.
Main Function
Since the source code for main() has not changed there is no need to show it again. Refer to example 3 or download the full source for this example.
Running the Example
To build and run this example use the same CMake build file and commands as we showed for the first example. The only suggested modification is on line 2 of the CMakeLists.txt file, change “example_1” to “example_48”.