I'm making a program which copies files in Qt. I want to know how can I use QProgressBar
with bool QFile::copy(const QString & fileName, const QString & newName)
. Is this even possible with copy
function? Can the process of copying be paused?
3 Answers
You can't do this using the static QFile::copy() method.
As Maciej stated before you need to write your own class. It should use two QFile objects, one for reading one for writing. Transfer the data in portions (e.g 1% of the entire file size) and emit a progress signal after each portion. You can connect this signal to a progress dialog.
If you need this to work in the background you should implement it using a QThread.
First try to decide if you need a class that does the copy work asynchonously (without blocking the GUI) or synchronously (blocking the GUI). The latter is easier to programming but most times not what is intended (e.g. you can not cancel or pause a copy operation by button click if the GUI is blocked).
You can have a look here for a pretty extensive Qt 4 class: http://docs.huihoo.com/qt/solutions/4/qtcopydialog/qtfilecopier.html but I am not sure if this will help due to its complexity.
This is a minimal example to show:
filecopyer.h
:
/*
* This is a minimal example to show thread-based QFile copy operation with progress notfication.
* See here for QFile limitations: https://doc.qt.io/qt-5/qfile.html#platform-specific-issues
* Copyright (C) 2019 Iman Ahmadvand
*
* This is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* It is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _FILE_COPYER_H
#define _FILE_COPYER_H
#include <QtCore/qstring.h>
#include <QtCore/qobject.h>
#include <QtCore/qfile.h>
#include <QtCore/qfileinfo.h>
#include <QtCore/qvector.h>
#include <QtCore/qthread.h>
class FileCopyer : public QObject {
Q_OBJECT
Q_PROPERTY(qint64 chunksize READ chunkSize WRITE setChunkSize)
Q_PROPERTY(QVector<QString> sourcePaths READ sourcePaths WRITE setSourcePaths)
Q_PROPERTY(QVector<QString> destinationPaths READ destinationPaths WRITE setDestinationPaths)
public:
static const int DEFAULT_CHUNK_SIZE = 1024 * 1024 * 1;
FileCopyer(QThread*);
~FileCopyer();
qint64 chunkSize() const {
return _chunk;
}
void setChunkSize(qint64 ch) {
_chunk = ch;
}
QVector<QString> sourcePaths() const {
return src;
}
void setSourcePaths(const QVector<QString>& _src) {
src = _src;
}
QVector<QString> destinationPaths() const {
return dst;
}
void setDestinationPaths(const QVector<QString>& _dst) {
dst = _dst;
}
protected slots:
void copy();
private:
QVector<QString> src, dst;
qint64 _chunk;
signals:
void copyProgress(qint64 bytesCopied, qint64 bytesTotal);
void finished(bool success);
};
#endif // !_FILE_COPYER_H
filecopyer.cpp
:
/*
* This is a minimal example to show thread-based QFile copy operation with progress notfication.
* See here for QFile limitations: https://doc.qt.io/qt-5/qfile.html#platform-specific-issues
* Copyright (C) 2019 Iman Ahmadvand
*
* This is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* It is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <QtCore/qdebug.h>
#include "filecopyer.h"
FileCopyer::FileCopyer(QThread* _thread) :QObject(nullptr) {
moveToThread(_thread);
setChunkSize(DEFAULT_CHUNK_SIZE);
QObject::connect(_thread, &QThread::started, this, &FileCopyer::copy);
QObject::connect(this, &FileCopyer::finished, _thread, &QThread::quit);
QObject::connect(this, &FileCopyer::finished, this, &FileCopyer::deleteLater);
QObject::connect(_thread, &QThread::finished, _thread, &QThread::deleteLater);
}
FileCopyer::~FileCopyer() {
}
void FileCopyer::copy() {
if (src.isEmpty() || dst.isEmpty()) {
qWarning() << QStringLiteral("source or destination paths are empty!");
emit finished(false);
return;
}
if (src.count() != dst.count()) {
qWarning() << QStringLiteral("source or destination paths doesn't match!");
emit finished(false);
return;
}
qint64 total = 0, written = 0;
for (const auto& f : src)
total += QFileInfo(f).size();
qInfo() << QStringLiteral("%1 bytes should be write in total").arg(total);
int indx = 0;
qInfo() << QStringLiteral("writing with chunk size of %1 byte").arg(chunkSize());
while (indx < src.count()) {
const auto dstPath = dst.at(indx);
QFile srcFile(src.at(indx));
QFile dstFile(dstPath);
if (QFile::exists(dstPath)) {
qInfo() << QStringLiteral("file %1 alreasy exists, overwriting...").arg(dstPath);
QFile::remove(dstPath);
}
if (!srcFile.open(QFileDevice::ReadOnly)) {
qWarning() << QStringLiteral("failed to open %1 (error:%1)").arg(srcFile.errorString());
indx++;
continue; // skip
}
if (!dstFile.open(QFileDevice::WriteOnly)) {
qWarning() << QStringLiteral("failed to open %1 (error:%1)").arg(dstFile.errorString());
indx++;
continue; // skip
}
/* copy the content in portion of chunk size */
qint64 fSize = srcFile.size();
while (fSize) {
const auto data = srcFile.read(chunkSize());
const auto _written = dstFile.write(data);
if (data.size() == _written) {
written += _written;
fSize -= data.size();
emit copyProgress(written, total);
} else {
qWarning() << QStringLiteral("failed to write to %1 (error:%2)").arg(dstFile.fileName()).arg(dstFile.errorString());
fSize = 0;
break; // skip this operation
}
}
srcFile.close();
dstFile.close();
indx++;
}
if (total == written) {
qInfo() << QStringLiteral("progress finished, %1 bytes of %2 has been written").arg(written).arg(total);
emit finished(true);
} else {
emit finished(false);
}
}
main.cpp
:
#include <QtWidgets/QApplication>
#include "filecopyer.h"
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
auto local = new QThread;
auto worker = new FileCopyer(local);
QObject::connect(worker, &FileCopyer::finished, [](bool s) {
s ? qDebug() << "FINISHED" : qDebug() << "FAILED";
});
QObject::connect(worker, &FileCopyer::copyProgress, [](qint64 copy, qint64 total) {
qDebug() << QStringLiteral("PROGRESS => %1").arg(qreal(copy) / qreal(total) * 100.0);
});
worker->setSourcePaths(/* src-paths */); // e.g: ~/content/example.mp4
worker->setDestinationPaths(/* dst-paths */); // e.g /usr/local/example.mp4
local->start();
return a.exec();
}