I am implementing a method in my class, which will write out data from a TableView Object to a CSV file. However when the program runs, the program writes the data to the file on a USB drive at a very slow rate(3 or 4 seconds), but works fine with the system's internal drive. Is this because i have not used flush() or close(), after writing the file ??
Here is my code
bool ThicknessCalibrationDataDisplay::WriteCSVFileChanges()
{
QModelIndex tableViewModelindex = tableViewModel_->index(0,0);
QFile file(CSVFileName_);
if(!file.exists())
return false;
if(!file.open(QIODevice::WriteOnly))
return false;
for(int i = 0; i < totalRows_ ; i++)
{
for(int j = 0 ; j < totalColumns_; j++)
{
tableViewModelindex = tableViewModel_->index(i,j);
qDebug()<<tableViewModelindex.data();
QString text = tableViewModelindex.data().toString();
QTextStream OutputStream(&file);
if(j == totalColumns_ - 1)
OutputStream<<"\n\r";
else
OutputStream<<',';
}
}
}
This was my code earlier, and now i plan to close the file stream, for a graceful exit.
The Qt API for QFile::close() says
Calls QFile::flush() and closes the file. Errors from flush are
ignored.
So should i just call close(), or is it better to call flush(), log any errors and then call close() ?
Is there any other modification, that i have to make, to improve the write operation ?
Best Answer
The
flush()
vs.close()
is a red herring, it doesn't impact your performance at all.Destruction of a
QTextStream
forces a flush of the file. Flushing the file is slow. You're destroying your text stream every time you iterate through the loop! Create the stream outside of the loop!Here's the source, from Qt 5.1.1:
On Qt 5.1 or newer, you should be using
QSaveFile
if you want to ensure that the disk buffers are flushed once the file is closed. Only this gives you assurance that once you think you're done saving, the data is in fact on the disk.QTextStream
has its own buffers. So, if you want to catch errors while flushing, you need to use theflush()
method on the stream itself, not on theQFile
.It's a very bad idea to do any file access from the GUI thread. Any time anything blocks, your UI thread blocks as well.
Now the question is: how do you make your model safe to access from another thread? If you use a custom model, you probably only need to switch the model into a read-only mode for the duration of writing the file. The read-only mode would be a custom property of the model, such that all
setData
calls would fail. You'd of course need to indicate this to the user. The views of the model would be read-only, but that's much more friendly than blocking the entire GUI.If you think that merely preventing concurrent access to the model with a QMutex would be enough, think again. Any modifications made to the model may potentially change its structure, so your writer would need to properly handle all signals emitted by model. This would make the writer much more complex. A temporarily read-only model lets you have responsive GUI with a temporary inconvenience to the user, at a minimal increase in code complexity.
Some USB drives are abysmally slow.
Your reworked code should, at the minimum, look as follows: