我有一个程序,我现在正在将1800万像素或更小的大图像保存到磁盘。在此过程中,我将它们转换为 QImage,在 QDialog 的子类中显示 QImage,然后提示用户保存它们。
我想为用户提供某种进度指示器,因此我推断我实现这一目标的最佳方法是创建一个 QThread,使用线程内的工作线程保存图像,然后向 GUI 发出信号以声明保存已完成。我在保存过程中使用此方法向用户显示进度条。
在这里考虑这个简单的工人类:
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();
};
我的问题是 image::save() 函数通常在映像实际完成写入磁盘之前很久就返回true
。使用当前的代码行,在我的 5600 HDD 上,resultReady()
信号在用户按下按钮后大约 6 秒触发,因此我的 GUI 更新以显示图像已完成保存。
但是,QImage 似乎需要 7 秒到 30 秒才能完成对磁盘的写入。在此期间,用户可能已经结束了应用程序,这会导致磁盘上的映像不完整,因为线程认为自己已完成。也许更糟糕的是,用户可能会继续拍摄多个其他图像,这些图像开始加剧保存图像所花费的时间。
有没有办法准确确定Qt应用程序何时完成将QImage写入硬盘?
您对问题的诊断不正确。一旦image.save
返回,文件系统将处于一致状态,您可以在此时强制退出应用程序:该文件将具有正确的内容。磁盘上的状态是否一致是未知的,但这意味着您不应该重置或强制关闭计算机电源。不过,您的应用程序可以自由退出。
你的问题是另一回事。谁知道呢:您未能提供测试用例。
无需手动处理工作线程和对象。杠杆QtConcurrent
.这应该很容易,而且确实如此。即:
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
};
有关CountDownLatch
的实现,请参阅此问题。
无论如何,在主线程中处理如此大的数据是一个坏主意。对于这些问题,Qt框架提供了很好的工具。在Qt文档中查找QConcurrent。在您的情况下,我会使用 QFutureWatcher 保存文件并发运行检索进度条的数据。
@Ketta:你如何重现你相同的行为?也许硬盘缓冲或其他原因就是原因。对于您的问题,我更喜欢一种更详细的方法,该方法提供更多控制。也许与
QImage image;
QFile file("file.png");
file.open(QIODevice::WriteOnly);
QDataStream out(&file);
image.save(&file, "PNG");
这可能会为您提供控制数据写入的更多可能性。@Kuba:
connect( &m_futureWatcher, SIGNAL ( finished( ) ), this, SLOT( showText ( )) );
......和
m_futureFileReader = QtConcurrent::run(readFile, pIODevice );
m_futureWatcher.setFuture(m_futureFileReader);
作为阅读文本文件的示例,对我来说似乎不是很笨重。