Drayke
Drayke

Reputation: 397

Copying Files in Threads to prevent a freezed application

I wrote a C++/QT Application with "Installer" features. Everything workes fine, but when I click outside the window while my programm is in a "copy process", it somehow loses focus and freezes until the copy process is over, then everything is displayed normaly with a QProgressBar value of 100%.

Im copying like this:

void Installer_C::copy(QString aSrcPath, QString aDstPath)
{    
  //handles
  hSrc = CreateFileW(..); //source file
  hDst = CreateFileW(..); //destination
  //copy
  ReadFile(...);
  LockFile(...);
  WriteFile(...); //->returnes bytesWritten
  UnlockFile(...);
  updateQProgressBar(bytesWritten); //updates progressbar in my application
  CloseHandle(...);
}

This function is called in a foreach loop iterating through a QStringList with files (located in my launchInstall() function).
Due to my problems, I thought about creating Threads for this copy process. Is it more efficient to create a new thread for each Installer_C::copy() call, or to just create one Thread to call the launchInstall() function (I think it wouldn't help much).
Or a better Question: Would it even solve my problem that the application freezes? And how shall I do it so that the ProgressBar still will be updated from this thread?

Upvotes: 0

Views: 783

Answers (2)

t3ft3l--i
t3ft3l--i

Reputation: 1412

As I know there are two possible ways to solve this problem in Qt:

  1. Using QCoreApplication::processEvents(). As docs said it's not a bad solution and suitable for handling long operations. I think it could be usefull for you. For example, you could call it after copying each file (or couple of files).
  2. Using multithreading. It's a great approach to devide GUI threads and logical threads. You could adapt Kirill's solution for your goals.

So my suggestion is: if you need easy and quick working solution, the first way is for you.

If you want to make well-designed and more complex application and you are ready to code little bit more, use the second approach.

Upvotes: 0

Kirill Chernikov
Kirill Chernikov

Reputation: 1420

I think, the best way to solve your problem is to create one addition thread to copy process. You can use QThread (Qt documentation: QThread) class to create a thread, which will copy files. The main thread will execute your GUI and it will be available during files copying.

Small example for copying thread:

class CopyThread : public QThread
{
    Q_OBJECT
private:
    QStringList oldfiles_;
    QStringList newfiles_;
public:
    CopyThread(const QStringList& oldfiles,
               const QStringList& newfiles,
               QObject * parent = 0)
        : QThread(parent)
        , oldfiles_(oldfiles)
        , newfiles_(newfiles)
    {}

    void run() Q_DECL_OVERRIDE 
    {
        int min = qMin(oldfiles_.count(), newFiles.count());
        for(int i=0; i<min; ++i)
        {
            copyFile(oldfiles_.at(i), newFiles_.at(i));
            emit signalCopyFile(oldfiles_.at(i), newFiles_.at(i));
        }
    }
signals:
    void signalCopyFile(const QString&, const QString&);
private:
    void copyFile(QString aSrcPath, QString aDstPath)
    {
        QFile::copy(aSrcPath, aDstPath);
    }
};

Of course, you must implement slot on your widget for signalCopyFile(const QString&, const QString&) and make connection. Small piece of code (for example) to start copy thread and make connection:

QStringList oldFiles;
oldfiles.append("C:/1.txt");
QStringList newFiles;
newFiles.append("C:/2.txt");
yourProgressBar.setMaximum(oldFiles.count());
yourProgressBar.setMinimum(0);
yourProgressBar.setValue(0);
CopyThread *copyThread = new CopyThread(oldFiles, newFiles, this);
connect(copyThread, &CopyThread::finished, copyThread, &QObject::deleteLater);
connect(copyThread, &CopyThread::signalCopyFile, youWidget, &YouWidget::yourSlot);
copyThread->start();

In yourSlot you can update value of your QProgressBar:

void yourSlot(const QString& oldFile, const QString& newFile)
{
    // your actions when one file was copied
    yourProgressBar->setValue(yourProgressBar.value() + 1);
}

Everything will be all right without freezes!

Upvotes: 1

Related Questions