I have a program where I am now saving large, 18 megapixel or less images to the disk. Along the way I convert them to a QImage, display the QImage in a subclass of a QDialog, and then prompt the user to save them.
I wanted to have some sort of progress indicator for the user, so I reasoned out that my best way to achieve that would be to create a QThread, save the image with a worker within the thread, and then emit a signal back to the GUI to declare that saving has finished. I use this method to show a progress bar to the user during the saving process.
Consider this simple worker class here:
class Worker : public QObject
{
Q_OBJECT
public slots:
void doWork(const QImage &image, const QString &file) {
if(image.save(file, "PNG", 100))
emit resultReady();
else
{
// handle error
return;
}
}
signals:
void resultReady();
};
My problem is that the image::save() function returns true
often long before the image has actually finished writing to the disk. With this current line of code, on my 5600 HDD, the resultReady()
signal is fired roughly 6 seconds after the user presses the button, and thus my GUI updates to show that the image has finished saving.
However, the QImage seems to take anywhere from 7ish seconds to up to 30ish seconds to finish writing to disk. During this time, the user could have ended the application, which causes an incomplete image on the disk as the thread considers itself to be finished. Perhaps worse still, the user could have gone on to take multiple other images that begin to exacerbate the duration spent saving images.
Is there a way to determine exactly when a Qt application has finished writing a QImage to the hard disk?
Your diagnosis of the issue is incorrect. Once image.save
has returned, the filesystem is in a consistent state and you can forcibly exit the application at that very point: the file will have correct contents. Whether the on-disk state is consistent is unknown, but all that means is that you shouldn't reset nor forcibly power-off the machine. Your application is free to quit, though.
Your issue is something else. Who knows what: you failed to provide a test case.
It's not necessary to deal with worker threads and objects manually. Leverage QtConcurrent
. It's supposed to be easy, and it is. Namely:
class MyWindow : public QWidget {
Q_OBJECT
CountDownLatch m_latch;
Q_SIGNAL void imageSaved(const QString & filename);
Q_SIGNAL void imageSaveFailed(const QString & filename);
void saveImage(const QImage & image, const QString & filename) {
auto lock = m_latch.lock();
QtConcurrent::run([=]{ // captures this, lock, image and filename
if (image.save(filename, "PNG", 100))
emit imageSaved(filename);
else
emit imageSaveFailed(filename);
});
}
...
// MyWindow's destructor will block until all image savers are done
};
For implementation(s) of CountDownLatch
, see this question.