复制文件的正确方法使用GUI设置QT,避免冻结

时间:2016-08-03 14:31:29

标签: multithreading qt user-interface freeze qfile

我有一个应用程序,使用QFile::Copy(..)将文件从一个位置复制到另一个位置。复制过程在一个单独的线程中执行到一个工作对象,然而,有时 GUI冻结,我在这里读了很多关于这个的主题,但我明白这个方法(工人类)是正确的。我在其他项目中使用相同的方法在另一个线程中执行进程,如计时器,它工作得非常好,流畅。似乎这种小冻结仅在复制文件时发生。在MS Windows中,"滞后"比在Linux中更明显,在最后一个中,除非你复制一个大文件(700MB),否则不会检测到冻结,但只有当文件结束复制时,在复制期间,GUI才会响应。我在我的mainWindow (BUMain)课程中使用了这种方法:

void BUMain::initThreadSetup()
{
    thread = new QThread;
    Worker *worker = new Worker();
    worker->moveToThread(thread);

    connect(worker,SIGNAL(worker_Signal_updateProgressBar(int)),ui->progressBar,SLOT(setValue(int)),Qt::QueuedConnection);
    connect(this,SIGNAL(main_signal_copyFile(int,QStringList,QString)),worker,SLOT(worker_Slot_copyFile(int,QStringList,QString)),Qt::QueuedConnection);
    connect(worker,SIGNAL(worker_signal_keepCopying()),this,SLOT(main_slot_keepCopying()),Qt::QueuedConnection);
    connect(worker,SIGNAL(worker_signal_logInfo(QString)),gobLogViewer,SLOT(logger_slot_logInfo(QString)),Qt::QueuedConnection);

    connect(thread,SIGNAL(finished()),worker,SLOT(deleteLater()));
    connect(thread,SIGNAL(finished()),thread,SLOT(deleteLater()));
    thread->start();

}

在MainGui(BUMain)构造函数中调用此方法以启动线程。一些相关的代码:

这是worker_signal_keepCopying()发出时调用的插槽:

void BUMain::main_slot_keepCopying()
{
    giProgress ++;
    if(giProgress < giFileCounter){
        emit(main_signal_copyFile(giProgress,gobPaths,ui->toFilesTextField->text()));
    }
}

在这里,我进行计数器验证并发出一个新信号,通知工人它可以继续下一个副本。复制过程是逐个完成的。在worker.cpp文件中,您可以看到插槽worker_Slot_copyFile(int liIndex,QStringList files,QString path)实现:

worker.cpp:

#include "worker.h"
#include <QFile>
#include <QFileInfo>
#include <QStringList>
#include <QCoreApplication>

Worker::Worker(QObject *parent) :
    QObject(parent)
{

}

void Worker::worker_Slot_copyFile(int liIndex,QStringList files,QString path)
{
    QString fileName;

    fileName = QFileInfo(files.at(liIndex)).baseName()+"."+QFileInfo(files.at(liIndex)).completeSuffix();

    //If the file exist, delete it
    if (QFile::exists(path+"/"+fileName))
    {
        QFile::remove(path+"/"+fileName);
    }

    QFile lobFile(files.at(liIndex));

    if(lobFile.copy(path+"/"+fileName)){

        //Write to a logger class
        emit(worker_signal_logInfo("File: " + fileName + " copied to: " + path));

        //Update a progress bar in the main GUI
        emit(worker_Signal_updateProgressBar(liIndex+1));
    }

    //The file has been processed!, I'm ready to copy another file...
    emit(worker_signal_keepCopying());

}

worker.h:

#ifndef WORKER_H
#define WORKER_H

#include <QObject>
#include <QThread>
#include <QStringList>

class Worker : public QObject
{
    Q_OBJECT
public:
    explicit Worker(QObject *parent = 0);

signals:
    void worker_Signal_updateProgressBar(int value);
    void worker_signal_keepCopying();
    void worker_signal_logInfo(QString info);

public slots:
    void worker_Slot_copyFile(int liIndex, QStringList files, QString path);

};

#endif // WORKER_H

所以,用语言来说可以是:

确定!让我们开始复制一些文件!!记住BUMain是运行GUI的mainWindow类:

  1. BUMain将进度条最大值调整为要复制的文件总数
  2. BUMain将进度条值设置为0
  3. BUMain发出main_signal_copyFile(...)信号。这就像&#34;嘿工人!请开始复制一个新文件,我会在这里做另外的事情,当你完成复制时,请告诉我。&#34;
  4. 工作人员收到信号并调用worker_Slot_copyFile。就像&#34;嘿BUMain,我能听到你,收到订单!现在我将复制文件&#34;
  5. 工作人员完成发出worker_signal_keepCopying()的副本:&#34; BUMain我已经结束了复制文件,如果你愿意,我可以复制另一个,只需通知我。&#34;
  6. BUMain调用main_slot_keepCopying():&#34;我可以听到你的工作人员,谢谢!&#34;再次发出main_signal_copyFile(...):&#34;工作人员,我有更多要复制的文件,请复制另一个&#34;。
  7. 重复此过程,直到处理完所有文件。
  8. 有时,这个过程非常好,没有滞后或冻结,但有时却没有。请注意,这种方法不是阻止。

    我还尝试在worker类中运行for循环来复制所有文件而不通知主类,但是滞后很重,并且GUI在复制大文件(> 300MB)时变得没有响应。例如:

    void Worker::worker_Slot_copyFile(int liIndex,QStringList files,QString path)
    {
        QString fileName;
        for(int liIndex = 0; liIndex < files.length() - 1; liIndex ++){
            fileName = QFileInfo(files.at(liIndex)).baseName()+"."+QFileInfo(files.at(liIndex)).completeSuffix();
    
            if (QFile::exists(path+"/"+fileName))
            {
                QFile::remove(path+"/"+fileName);
            }
    
            QFile lobFile(files.at(liIndex));
    
            if(lobFile.copy(path+"/"+fileName)){
                emit(worker_signal_logInfo("File: " + fileName + " copied to: " + path));
                emit(worker_Signal_updateProgressBar(liIndex+1));
            }
        }
    }
    

    我希望尽可能清楚,这有点难以解释。我已将this作为参考来使用工人类方法。

    注意:我使用QT5.1.1编程和Windows 10,以及Arch Linux来部署应用程序。

    任何帮助或建议表示赞赏。在此先感谢,祝你有愉快的一天!

1 个答案:

答案 0 :(得分:1)

你的方法是正确的,如果有点冗长。 &#34;滞后&#34;很可能是由于内核对如何管理页面缓存以及驱逐部分应用程序以便为正在复制的文件中的页面腾出空间感到愚蠢。对此的唯一补救措施可能是使用特定于平台的文件复制机制,该机制建议内核您将按顺序进行读写,并且在您之后不再需要数据完成了写作。有关详细信息和一些Linux代码,请参阅this answer。在Windows上,人们希望CopyFileEx能够进行正确的咨询呼叫,Qt example is here