8
votes

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
Answered with code here: stackoverflow.com/questions/32952474/…dtech

3 Answers

4
votes

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.

2
votes

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();
}
-1
votes

Check out the class that QFile derives from QIODevice there is a signal bytesWritten() connect to it and stat the file size via QFileInfo(fromFile).size() to get a read how to use signals check my other answer