2016-07-13 4 views
2

У меня есть программа, в которой я теперь сохраняю большие, 18 мегапикселей или меньше изображений на диск. По пути я конвертирую их в QImage, показываю QImage в подклассе QDialog, а затем предлагаю пользователю их сохранить.Как проверить, что QImage :: save() закончил запись на диск?

Я хотел, чтобы у пользователя был какой-то индикатор прогресса, поэтому я рассуждал о том, что лучший способ достичь этого - создать 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(); 
}; 

Моя проблема заключается в том, что изображение :: сохранить() функция возвращает true часто задолго до того, как изображение фактически закончил писать на диск. С этой текущей строкой кода на моем 5600 HDD сигнал resultReady() запускается примерно через 6 секунд после нажатия пользователем кнопки, и, таким образом, мои обновления графического интерфейса, чтобы показать, что изображение закончило сохранение.

Однако QImage, кажется, занимает от 7 до секунд до 30 секунд, чтобы закончить запись на диск. В течение этого времени пользователь мог бы закончить приложение, что приводит к неполному изображению на диске, поскольку поток считает себя выполненным. Возможно, еще хуже, пользователь мог бы взять несколько других изображений, которые начинают усугублять длительность сохраненных изображений.

Есть ли способ определить, когда приложение Qt закончило писать QImage на жесткий диск?

ответ

2

Ваш диагноз проблемы неверен. Как только 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 см this question.

+0

Вы правы. Теперь я понимаю, что моя проблема заключается в том, что функции, которые используются для включения и отключения содержимого графического интерфейса, эффективно небезопасны из-за общих объектов потоковой передачи. Я тестировал процесс сохранения изображений, быстро запуская кнопку сохранения всякий раз, когда она была доступна. Я думаю, что, делая это, я имел шанс создать несколько потоков, которые будут сохраняться на жестком диске примерно одновременно, вызывая перегрузку. Затем один из них закончит, и я закрою программу, пока другой поток все еще находится в процессе сохранения. Что-то в этом роде. – Ketta

+0

Как в стороне, в вашем примере для CountDownLatch, который вы связали, вы хотите использовать фигурные скобки по всему коду в определениях Locker? Я попытался посмотреть на этот пример и, похоже, вы хотели использовать круглые скобки для различных функций, верно? Я еще не могу комментировать другие темы или я бы разместил это там. – Ketta

+0

@ Ketta Код, который я опубликовал, был скомпилирован и протестирован. Так может выглядеть современный C++. C++ 98/03 используется для перегрузки тех же круглых скобок, что означает слишком много вещей: группировка выражений, вызов функции/метода и вызов конструктора. В C++ 11 вызов конструктора получает собственный синтаксис с '{}' фигурными скобками. Это позволяет легко отличать вызовы методов и другие выражения от инициализации объекта. –

0

Обращайтесь с такими большими данными в основной теме, это плохая идея, во всяком случае. Для таких проблем структура Qt предлагает отличные инструменты. Посмотрите в документации Qt для QConcurrent. В вашем случае я бы сохранил файлы с Параллельный запуск для получения данных для индикатора выполнения используйте QFutureWatcher.

+0

Думаю, я понимаю, о чем вы говорите. Я реализовал экземпляры QFuture и QFutureWatcher, которые направляют эту же функцию выше, вставляемые в подкласс QDialog, который я использую. Это, похоже, облегчает проблему и позволяет сэкономить изображения через регулярные промежутки времени, а не беспорядок, который у меня был раньше, но я немного смущен относительно того, что здесь представляет собой реальная разница. Как использование QtConcurrent гарантирует, что элемент QImage :: save() возвращается или очень близко к тому времени, когда изображение полностью записано на диск? Или вы думаете, что мне не хватает причины проблемы? – Ketta

+0

На самом деле, я думаю, я отменяю это. Мне удалось заставить ту же проблему и с этим решением. QFutureWatcher сигнализирует 'закончен()' после того, как 'QImage :: save (...)' возвращается так же, как и раньше, поэтому я все еще заканчиваю ситуациями, когда графический интерфейс приложения восстанавливается из процесса до того, как файл изображения фактически был полностью записанный на жесткий диск ... Возможно, я неправильно понимаю ваше решение. Хотя я задаюсь вопросом, просто ли это, что QImage передает сохранение в другой поток самостоятельно или что-то в этом роде, и в этом случае я не знаю, что нужно сделать ... – Ketta

+0

Обратите внимание, что 'QFutureWatcher' является довольно громоздким и необходимо только поддерживать C++ 98. С C++ 11 вы должны использовать функтор и излучать любой сигнал, который вы хотите от этого функтора. Сигналы Qt могут быть безопасно выбрасываться из любого потока, включая внешние потоки, конечно, если вы используете автоматические или поставленные типы соединений для работы с сигналом. –

0

@ 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); 

В качестве примера для чтения текстовых файлов не кажется очень громоздким для меня.