Best practice - exposing QThread to qml

  • Re: QFile copy multiple files progress bar

    From my previous topic I decided to move file copying to another thread and increment progress not by copied file count but by copied size.

    So this is what I got:

    class Copier : public QThread
        Q_PROPERTY(double minimum READ minimum NOTIFY minimumChanged)
        Q_PROPERTY(double maximum READ maximum NOTIFY maximumChanged)
        Q_PROPERTY(double progress READ progress NOTIFY progressChanged)
        Copier(QObject *parent = nullptr);
        Q_INVOKABLE void copy(const QString &sourceFolder, const QString &destFolder);
        double minimum() const;
        double maximum() const;
        double progress() const;
        void minimumChanged(double minimum);
        void maximumChanged(double maximum);
        void progressChanged(double value);
        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())
    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 {
            return true;
        if (folderContents(sourceFolder, QString()))
            return fileList;
            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())
            if ( && {
                qint64 internalBufferSize = srcFile.size() < bufferSize ? srcFile.size() : bufferSize;
                while (1) {
                    buffer =;
                    if (buffer.isEmpty())
                    mProgress += internalBufferSize;
                    emit progressChanged(mProgress);
    void Copier::run()
    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.topMargin: 50
            anchors.right: control.right
            text: "copy"
            onClicked: {
                copier.copy("/home/eligijus/BigDisk/testArea/testArea", "/media/eligijus/usb")

    Important things to note:
    How I expose Copier. I create Copier object in qml and call function copy which starts thread.
    Copier is derived from QThread
    copying is done in Copier run() reimplemented method.

    Bottom line is what I have done a common practice or some kind of a hack?
    I'm new to multithreading and I'm really interested in your feedback, comments.

  • Moderators

    when you don't receive any warnings/errors in the console and everything is working as you expect it, your implementation looks fine ;)

  • Qt Champions 2016

    Just my 2 cents. Usually, you should not declare a QML component using QThread type. Because the QML component may be destroyed before the thread completed. It should have a QObject as a bridge to kick off the thread and manage its result.

    Moreover, in your case, I think QtConcurrent is a more suitable and easy-to-use solution than declaring your own custom QThread object.

  • @benlau Thanks for your insight. I glanced over QtConcurrent and it didn't seem to be applicable to my problem. I'll take a closer look at it.

Log in to reply

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