QFile copy multiple files progress bar



  • I copy a bunch of files from one location to another. File paths are stored in QStringlist and I iterate over list using for loop and copy files using QFile::copy. I want to have some kind of progress bar but when I tried to implement one but it didn't refresh. The gui locked up and only refreshed after all files were copied.
    Is there a way to prevent gui from locking up so progress bar can refresh?


  • Qt Champions 2016

    Hi
    The quick hax is to call QApplication::processEvents()
    http://www.informit.com/articles/article.aspx?p=1405544&seqNum=3

    A more solid way is to use a worker thread and signal to it pr file and
    update the progressbar. As far as i know QFile can be used in another thread.



  • @Eligijus
    Do you intend a progressbar which shows progress through how many files have been copied (i.e. just updated a fixed amount after each file copy completed), or which updates during each file's copy progress (e.g. based on total number of bytes copied)? This will be relevant for whatever solution.



  • @JNBarchan shows progress of how many files have been copied. I.e. I have 100 files and progress bar will go from 1 to 100 incrementing by 1 after a file has been successfully copied.



  • @Eligijus
    So you only need to update progressbar outside of any QFile::copy, in between each call, which makes it much simpler. Show us your attempt "but when I tried to implement one but it didn't refresh"? In some shape or form, it's not getting refreshed while you keep doing copy code, the UI isn't getting any update time till afterwards....



  • @JNBarchan I'm updating progressbar in qml

    for (var i = 0; i < files.length; ++i) {
    	progressBar.value = i
    	manager.copy(sourcePath + files[i],destPath + files[i])
    }
    

    I can't provide c++ widget version at the moment I'll have to write something up.
    But I guess @mrjj answer is valid here. I either have to call processEvents or move copying to another thread.


  • Qt Champions 2016

    @Eligijus
    Yep that should make it update pr file.
    Make sure to read link about blocking user input so its not possible to choose exit while copying.



  • This is the solution I came up with. Maybe someone will find it useful.

    class Copier : public QThread
    {
        Q_OBJECT
        Q_PROPERTY(double minimum READ minimum NOTIFY minimumChanged)
        Q_PROPERTY(double maximum READ maximum NOTIFY maximumChanged)
        Q_PROPERTY(double progress READ progress NOTIFY progressChanged)
    
    public:
        Copier(QObject *parent = nullptr);
    
        Q_INVOKABLE void copy(const QString &sourceFolder, const QString &destFolder);
        double minimum() const;
        double maximum() const;
        double progress() const;
    
    signals:
        void minimumChanged(double minimum);
        void maximumChanged(double maximum);
        void progressChanged(double value);
    
    private:
        QStringList getFolderContents(const QString &sourceFolder) const;
        qint64 fileSize(const QString &sourceFolder, const QStringList &fileList);
        void copyFiles();
    
        virtual void run() Q_DECL_OVERRIDE;
    
        QString mSourceFolder;
        QString mDestFolder;
        QStringList mFileList;
        double mMinimum;
        double mMaximum;
        double mProgress;
    };
    
    Copier::Copier(QObject *parent)
        : QThread(parent),
          mMinimum(0), mMaximum(0), mProgress(0)
    {
    
    }
    
    void Copier::copy(const QString &sourceFolder, const QString &destFolder)
    {
        mSourceFolder = sourceFolder;
        mDestFolder = destFolder;
        mFileList = getFolderContents(sourceFolder);
        mMinimum = 0;
        mMaximum = fileSize(sourceFolder, mFileList);
        emit minimumChanged(mMinimum);
        emit maximumChanged(mMaximum);
    
        if (!isRunning())
            start();
    }
    
    double Copier::minimum() const
    {
        return mMinimum;
    }
    
    double Copier::maximum() const
    {
        return mMaximum;
    }
    
    double Copier::progress() const
    {
        return mProgress;
    }
    
    QStringList Copier::getFolderContents(const QString &sourceFolder) const
    {
        QStringList fileList;
        std::function<bool(const QString&, const QString&)> folderContents;
        folderContents = [&](const QString &sourceFolder, const QString &destFolder)->bool {
            QDir sourceDir(sourceFolder);
    
            if (!sourceDir.exists())
                return false;
    
            foreach (const QFileInfo &fileInfo, sourceDir.entryInfoList(QDir::NoDotAndDotDot | QDir::NoSymLinks
                                                                        | QDir::Dirs | QDir::Files)) {
                QString srcFilePath = fileInfo.filePath();
                QString dstFilePath = destFolder.isEmpty() ? fileInfo.fileName() : destFolder + QDir::separator() + fileInfo.fileName();
                if (fileInfo.isDir()) {
                    if (!folderContents(srcFilePath, dstFilePath))
                        return false;
                } else {
                    fileList.append(dstFilePath);
                }
            }
    
            return true;
        };
    
        if (folderContents(sourceFolder, QString()))
            return fileList;
        else
            return QStringList();
    }
    
    qint64 Copier::fileSize(const QString &sourceFolder, const QStringList &fileList)
    {
        qint64 total = 0;
    
        foreach (const QString &fileName, fileList)
            total += QFile(sourceFolder + QDir::separator() + fileName).size();
    
        return total;
    }
    
    void Copier::copyFiles()
    {
        qint64 bufferSize = 4194304;
        QByteArray buffer;
        mProgress = 0;
        emit progressChanged(mProgress);
    
        foreach (const QString &fileName, mFileList) {
            QString srcFilePath = mSourceFolder + QDir::separator() + fileName;
            QString dstFilePath = mDestFolder + QDir::separator() + fileName;
            QFile srcFile(srcFilePath);
            QFile dstFile(dstFilePath);
    
            QFileInfo dst(dstFilePath);
            QDir dir = dst.dir();
            if (!dst.isDir() && !dir.exists())
                dir.mkpath(dir.path());
    
            if (srcFile.open(QIODevice::ReadOnly) && dstFile.open(QIODevice::WriteOnly)) {
                qint64 internalBufferSize = srcFile.size() < bufferSize ? srcFile.size() : bufferSize;
    
                while (1) {
                    buffer = srcFile.read(internalBufferSize);
                    if (buffer.isEmpty())
                        break;
    
                    dstFile.write(buffer);
                    mProgress += internalBufferSize;
                    emit progressChanged(mProgress);
                }
                srcFile.close();
                dstFile.close();
            }
        }
    }
    
    void Copier::run()
    {
        copyFiles();
    }
    
    Window {
        visible: true
        width: 640
        height: 480
        title: qsTr("Hello World")
    
    
        ProgressBar {
            id: control
            x: 50
            y: 50
            width: 400
            height: 100
            padding: 2
            from: copier.minimum
            to: copier.maximum
            value: copier.progress
    
            background: Rectangle {
                color: "black"
            }
    
            contentItem: Item {
                Rectangle {
                    width: control.visualPosition * parent.width
                    height: parent.height
                    color: "red"
                }
            }
        }
        Copier {
            id: copier
        }
    
        Button {
            anchors.top: control.bottom
            anchors.topMargin: 50
            anchors.right: control.right
            text: "copy"
            onClicked: {
                copier.copy("/home/eligijus/BigDisk/testArea/testArea", "/media/eligijus/usb")
            }
        }
    }
    


  • Hi All,

    I have used Qt 5.7.1 , i have created GUI application in that one of the screen having file copy . When clicked that button file will be copy from source to destination with progress bar , So how to create this please give me any suggestion

    Thanks,
    RYS


  • Qt Champions 2016

    @Raghavendra-Surpur

    Hi and welcome.
    For a fast solution, simply use QFile
    QFile::copy("/path/file", "/path/copy-of-file");

    Progressbar is not possible.

    If you really want progressbar that can update while copying, you must write your own file copy function.

    https://stackoverflow.com/questions/10195343/copy-a-file-in-a-sane-safe-and-efficient-way


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.