5#include <QtConcurrent/QtConcurrent>
6#include <QtCore/QPromise>
16 , _watcher(new QFutureWatcher<
bool>(this))
18 connect(_watcher, &QFutureWatcher<bool>::progressValueChanged,
19 this, &QGCCompressionJob::_onProgressValueChanged);
20 connect(_watcher, &QFutureWatcher<bool>::finished,
21 this, &QGCCompressionJob::_onFutureFinished);
24QGCCompressionJob::~QGCCompressionJob()
27 if (_future.isRunning()) {
28 _future.waitForFinished();
36QFuture<bool> QGCCompressionJob::extractArchiveAsync(
const QString &archivePath,
37 const QString &outputDirectoryPath,
40 const auto cancelRequested = std::make_shared<std::atomic_bool>(
false);
47 return _runWithProgress(work, cancelRequested);
50QFuture<bool> QGCCompressionJob::decompressFileAsync(
const QString &inputPath,
51 const QString &outputPath,
54 const auto cancelRequested = std::make_shared<std::atomic_bool>(
false);
61 return _runWithProgress(work, cancelRequested);
69 const QString &outputDirectoryPath,
78 _startOperation(Operation::ExtractArchive, archivePath, outputDirectoryPath, work);
82 const QString &outputDirectoryPath,
91 _startOperation(Operation::ExtractArchiveAtomic, archivePath, outputDirectoryPath, work);
95 const QString &outputPath,
104 _startOperation(Operation::DecompressFile, inputPath, outputPath, work);
108 const QString &fileName,
109 const QString &outputPath)
115 _startOperation(Operation::ExtractFile, archivePath, outputPath, work);
119 const QStringList &fileNames,
120 const QString &outputDirectoryPath)
126 _startOperation(Operation::ExtractFiles, archivePath, outputDirectoryPath, work);
131 if (!_running || !_future.isRunning()) {
135 qCDebug(QGCCompressionJobLog) <<
"Cancelling operation:" <<
static_cast<int>(_operation);
137 if (_cancelRequested) {
138 _cancelRequested->store(
true, std::memory_order_release);
148void QGCCompressionJob::_onProgressValueChanged(
int progressValue)
150 _setProgress(
static_cast<qreal
>(progressValue) / 100.0);
153void QGCCompressionJob::_onFutureFinished()
155 bool success =
false;
157 const bool wasCancelled = _future.isCanceled()
158 || (_cancelRequested && _cancelRequested->load(std::memory_order_acquire));
161 error = QStringLiteral(
"Operation cancelled");
162 qCDebug(QGCCompressionJobLog) <<
"Operation cancelled:" <<
static_cast<int>(_operation);
165 success = _future.result();
169 }
catch (
const std::exception &e) {
170 error = QString::fromUtf8(e.what());
174 qCDebug(QGCCompressionJobLog) <<
"Operation finished:" <<
static_cast<int>(_operation)
175 <<
"success:" << success
176 <<
"error:" <<
error;
178 if (!success && !
error.isEmpty()) {
179 _setErrorString(
error);
180 }
else if (success) {
181 _setErrorString(QString());
184 _setProgress(success ? 1.0 : _progress);
186 _operation = Operation::None;
187 _cancelRequested.reset();
196void QGCCompressionJob::_startOperation(Operation op,
const QString &source,
197 const QString &output,
201 qCWarning(QGCCompressionJobLog) <<
"Operation already in progress";
205 qCDebug(QGCCompressionJobLog) <<
"Starting operation:" <<
static_cast<int>(op)
206 <<
"source:" << source <<
"output:" << output;
211 if (_sourcePath != source) {
212 _sourcePath = source;
215 if (_outputPath != output) {
216 _outputPath = output;
221 _setErrorString(QString());
223 _cancelRequested = std::make_shared<std::atomic_bool>(
false);
226 _future = _runWithProgress(std::move(work), _cancelRequested);
227 _watcher->setFuture(_future);
230QFuture<bool> QGCCompressionJob::_runWithProgress(WorkFunction work,
231 const std::shared_ptr<std::atomic_bool> &cancelRequested)
234 return QtConcurrent::run([work = std::move(work), cancelRequested]() ->
bool {
235 QPromise<bool> promise;
236 QFuture<bool> future = promise.future();
239 promise.setProgressRange(0, 100);
242 auto progressCallback = [&promise, cancelRequested](qint64 bytesProcessed, qint64 totalBytes) ->
bool {
243 if (promise.isCanceled()
244 || (cancelRequested && cancelRequested->load(std::memory_order_acquire))) {
248 int progressValue = 0;
249 if (totalBytes > 0) {
250 progressValue =
static_cast<int>((bytesProcessed * 100) / totalBytes);
251 }
else if (bytesProcessed > 0) {
253 const double normalized =
static_cast<double>(bytesProcessed) / 1048576.0;
254 progressValue =
static_cast<int>(50.0 * (1.0 - (1.0 / (1.0 + normalized))));
257 promise.setProgressValue(progressValue);
261 bool success =
false;
263 if (cancelRequested && cancelRequested->load(std::memory_order_acquire)) {
266 success = work(progressCallback);
268 }
catch (
const std::exception &e) {
269 qCWarning(QGCCompressionJobLog) <<
"Exception during compression operation:" << e.what();
273 promise.setProgressValue(100);
274 promise.addResult(success);
281void QGCCompressionJob::_setProgress(qreal progress)
283 if (!qFuzzyCompare(_progress, progress)) {
284 _progress = progress;
289void QGCCompressionJob::_setRunning(
bool running)
291 if (_running != running) {
297void QGCCompressionJob::_setErrorString(
const QString &
error)
299 if (_errorString !=
error) {
300 _errorString =
error;
QObject wrapper for async compression operations using QtConcurrent/QPromise.
#define QGC_LOGGING_CATEGORY(name, categoryStr)
void runningChanged(bool running)
Emitted when running state changes.
void progressChanged(qreal progress)
Emitted when progress changes (0.0 to 1.0)
void errorStringChanged(const QString &errorString)
Emitted when error string changes.
void extractArchiveAtomic(const QString &archivePath, const QString &outputDirectoryPath, qint64 maxBytes=0)
void extractFiles(const QString &archivePath, const QStringList &fileNames, const QString &outputDirectoryPath)
void extractFile(const QString &archivePath, const QString &fileName, const QString &outputPath)
void cancel()
Cancel current operation.
void sourcePathChanged(const QString &sourcePath)
Emitted when source path changes.
void finished(bool success)
void outputPathChanged(const QString &outputPath)
Emitted when output path changes.
void decompressFile(const QString &inputPath, const QString &outputPath=QString(), qint64 maxBytes=0)
void extractArchive(const QString &archivePath, const QString &outputDirectoryPath, qint64 maxBytes=0)
bool extractArchiveAtomic(const QString &archivePath, const QString &outputDirectoryPath, Format format, ProgressCallback progress, qint64 maxDecompressedBytes)
bool extractArchive(const QString &archivePath, const QString &outputDirectoryPath, Format format, ProgressCallback progress, qint64 maxDecompressedBytes)
bool decompressFile(const QString &inputPath, const QString &outputPath, Format format, ProgressCallback progress, qint64 maxDecompressedBytes)
QString lastErrorString()
Get a human-readable error message from the last operation (thread-local)
@ Auto
Auto-detect from file extension or magic bytes.
bool extractFile(const QString &archivePath, const QString &fileName, const QString &outputPath, Format format)
std::function< bool(qint64 bytesProcessed, qint64 totalBytes)> ProgressCallback
bool extractFiles(const QString &archivePath, const QStringList &fileNames, const QString &outputDirectoryPath, Format format)