具有进度和传输速率/速度的 PyQt 复制文件夹

Posted

技术标签:

【中文标题】具有进度和传输速率/速度的 PyQt 复制文件夹【英文标题】:PyQt Copy folder with progress and transfer rate/speed 【发布时间】:2017-06-07 11:14:56 【问题描述】:

我想扩展我当前的代码,使其能够显示正在复制的文件的传输速率/速度。我正在使用 py 3.6 和 Qt 5.8 在 Windows 10 上工作。这是我的代码:

import os
import shutil
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel, QProgressBar, QFileDialog

class FileCopyProgress(QWidget):


    def __init__(self, parent=None, src=None, dest=None):
        super(FileCopyProgress, self).__init__()

        self.src = src
        self.dest = dest
        self.build_ui()

    def build_ui(self):

        hbox = QVBoxLayout()

        lbl_src = QLabel('Source: ' + self.src)
        lbl_dest = QLabel('Destination: ' + self.dest)
        self.pb = QProgressBar()

        self.pb.setMinimum(0)
        self.pb.setMaximum(100)
        self.pb.setValue(0)

        hbox.addWidget(lbl_src)
        hbox.addWidget(lbl_dest)
        hbox.addWidget(self.pb)
        self.setLayout(hbox)

        self.setWindowTitle('File copy')
        self.auto_start_timer = QTimer()
        self.auto_start_timer.singleShot(2000, lambda: self.copyFilesWithProgress(self.src, self.dest, self.progress, self.copydone))
        self.show()

    def progress(self, done, total):
        progress = int(round((done/float(total))*100))

        try:
            self.pb.setValue(progress)
        except:
            pass

        app.processEvents()

    def copydone(self):
        self.pb.setValue(100)
        self.close()

    def countfiles(self, _dir):
        files = []

        if os.path.isdir(_dir):
            for path, dirs, filenames in os.walk(_dir):
                files.extend(filenames)
        return len(files)

    def makedirs(self, dest):
        if not os.path.exists(dest):
            os.makedirs(dest)

    @pyqtSlot()
    def copyFilesWithProgress(self, src, dest, callback_progress, callback_copydone):
        numFiles = self.countfiles(src)
        if numFiles > 0:
            dest = os.path.join(dest, src.replace(BASE_DIR, '').replace('\\', ''))
            print(''.join(['Destination: ', dest]))
            self.makedirs(dest)

            numCopied = 0
            for path, dirs, filenames in os.walk(src):
                for directory in dirs:
                    destDir = path.replace(src,dest)
                    self.makedirs(os.path.join(destDir, directory))
                for sfile in filenames:
                    srcFile = os.path.join(path, sfile)
                    destFile = os.path.join(path.replace(src, dest), sfile)
                    shutil.copy(srcFile, destFile)

                    numCopied += 1
                    callback_progress(numCopied, numFiles)
            callback_copydone()




BASE_DIR = 'C:\\dev'

app = QApplication([])
FileCopyProgress(src="C:\dev\pywin32-221", dest='C:\dev\copied')

# Run the app
app.exec_()

此代码打开一个带有进度条的 gui,显示复制文件的进度。带有当前传输速率/速度(近似值)的简单标签会非常好:)

不幸的是我找不到任何例子,有人可以给我一个提示或者一个可行的例子吗?

编辑: 我做了一个翻拍,现在我有了transfer ratetime elapsedtime remaining。数据似乎是真实的。我只有一个问题:假设我有一个文件夹/文件,time remaining 为 7 秒 -> 目前它以 7 秒开始,每 1 秒更新一次。我们希望在下一步中它会显示 6 秒,但它会显示: 5 秒 3 秒 1.5 秒 1.4 秒 1.3 秒 1.2 秒 1.1 秒等

错在哪里?

class FileCopyProgress(QWidget):

    def __init__(self, parent=None, src=None, dest=None):
        super(FileCopyProgress, self).__init__()

        self.src = src
        self.dest = dest
        self.rate = "0"
        self.total_time = "0 s"
        self.time_elapsed = "0 s"
        self.time_remaining = "0 s"
        self.build_ui()

    def build_ui(self):

        hbox = QVBoxLayout()

        lbl_src = QLabel('Source: ' + self.src)
        lbl_dest = QLabel('Destination: ' + self.dest)
        self.pb = QProgressBar()
        self.lbl_rate = QLabel('Transfer rate: ' + self.rate)
        self.lbl_time_elapsed = QLabel('Time Elapsed: ' + self.time_elapsed)
        self.lbl_time_remaining = QLabel('Time Remaining: ' + self.time_remaining)

        self.pb.setMinimum(0)
        self.pb.setMaximum(100)
        self.pb.setValue(0)

        hbox.addWidget(lbl_src)
        hbox.addWidget(lbl_dest)
        hbox.addWidget(self.pb)
        hbox.addWidget(self.lbl_rate)
        hbox.addWidget(self.lbl_time_elapsed)
        hbox.addWidget(self.lbl_time_remaining)
        self.setLayout(hbox)

        self.setWindowTitle('File copy')

        self.auto_start_timer = QTimer()
        self.auto_start_timer.singleShot(100, lambda: self.copy_files_with_progress(self.src, self.dest, self.progress, self.copy_done))

        self.copy_timer = QTimer()
        self.copy_timer.timeout.connect(lambda: self.process_informations())
        self.copy_timer.start(1000)
        self.show()

    @pyqtSlot()
    def process_informations(self):

        time_elapsed_raw = time.clock() - self.start_time
        self.time_elapsed = ':.2f s'.format(time_elapsed_raw)
        self.lbl_time_elapsed.setText('Time Elapsed: ' + self.time_elapsed)

        # example - Total: 100 Bytes, bisher kopiert 12 Bytes/s
        time_remaining_raw = self._totalSize/self._copied
        self.time_remaining = ':.2f s'.format(time_remaining_raw) if time_remaining_raw < 60. else ':.2f min'.format(time_remaining_raw)
        self.lbl_time_remaining.setText('Time Remaining: ' + self.time_remaining)

        rate_raw = (self._copied - self._copied_tmp)/1024/1024
        self.rate = ':.2f MB/s'.format(rate_raw)
        self.lbl_rate.setText('Transfer rate: ' + self.rate)

        self._copied_tmp = self._copied

    def progress(self):
        self._progress = (self._copied/self._totalSize)*100

        try:
            self.pb.setValue(self._progress)
        except:
            pass

        app.processEvents()

    def get_total_size(self, src):
        return sum( os.path.getsize(os.path.join(dirpath,filename)) for dirpath, dirnames, filenames in os.walk(src) for filename in filenames ) # total size of files in bytes

    def copy_done(self):
        self.pb.setValue(100)
        print("done")
        self.close()

    def make_dirs(self, dest):
        if not os.path.exists(dest):
            os.makedirs(dest)

    @pyqtSlot()
    def copy_files_with_progress(self, src, dst, callback_progress, callback_copydone, length=16*1024*1024):
        self._copied = 0
        self._copied_tmp = 0
        self._totalSize = self.get_total_size(src)

        print(''.join(['Pre Dst: ', dst]))

        dst = os.path.join(dst, src.replace(BASE_DIR, '').replace('\\', ''))

        print(''.join(['Src: ', src]))
        print(''.join(['Dst: ', dst]))
        self.make_dirs(dst)

        self.start_time = time.clock()
        for path, dirs, filenames in os.walk(src):
            for directory in dirs:
                destDir = path.replace(src, dst)
                self.make_dirs(os.path.join(destDir, directory))
            for sfile in filenames:
                srcFile = os.path.join(path, sfile)
                destFile = os.path.join(dst, sfile)
#                     destFile = os.path.join(path.replace(src, dst), sfile)

                with open(srcFile, 'rb') as fsrc:
                    with open(destFile, 'wb') as fdst:
                        while 1:
                            buf = fsrc.read(length)
                            if not buf:
                                break
                            fdst.write(buf)
                            self._copied += len(buf)
                            callback_progress()         
        try:
            self.copy_timer.stop()
        except:
            print('Error: could not stop QTimer')

        callback_copydone()

【问题讨论】:

一种简单的方法是只计算文件数并以此方式显示进度,或者计算总大小并使用文件大小来显示进度。我猜如果您不自己打开并复制文件,您将无法轻松显示单个大文件的进度.. 我已经统计了文件,见方法def countfiles。进度很好,我只想显示传输速度,例如15 MB /s 要显示整体传输速度,您可以计算到要复制的文件的总文件大小(我想如果您已经计算过它们并且可以访问循环)并将其除以使用的时间!?或者如果您只考虑文件的最后一分钟等,则显示当前速度,这取决于文件大小和内容,如果您无法访问单个文件的副本,则很难获得更精确的信息? 我重新考虑了理论部分:我只需要在特定的时间间隔内读取复制的文件大小 -> 每 1 分钟检查一次复制的文件并计算大小,例如1 分钟内 50 MB (50 MB / 60s = 0.83 MB/s) 还是我错过了什么?但是间隔呢,我该怎么做呢?? 【参考方案1】:

这都是关于逻辑的,请按照以下方式思考:

    您有所有文件的总大小,假设您有 100 个破折号:[----- ... -----] 现在您将获得开始传输文件的开始时间。 选择一些间隔,例如 2 秒。 因此,在 2 秒后查看您已经传输了多少文件,换句话说,您在新目录中已有多少文件。假设您转移了 26 个破折号。 计算结果为 26 次/2 秒 = 13 次/秒。 再深入一点,我们在 2 秒内下载了 26% 的内容,因为总量为每秒 100 或 13%。 更进一步,我们还可以计算时间预测。 总时间 = 100%/13% = 7.6 秒 经过的时间 = 2 秒 剩余时间 = 7.6 - 2 = 5.6 秒

我想你明白了……

注意:如果您当然选择 2 秒,您只需要在每 2 秒内继续验证和重新制作所有这些计算,并将信息更新给用户。如果您想更精确或向用户显示更频繁更新的信息,只需将该间隔缩短到 0.5 秒,并将计算时间设置为毫秒。更新信息的频率取决于您,这只是对如何进行数学计算的概述。 ")

【讨论】:

以上是关于具有进度和传输速率/速度的 PyQt 复制文件夹的主要内容,如果未能解决你的问题,请参考以下文章

4-10 Linux 中的文件同步传输 --- rsync

上行速率和下行速率

如果局域网的传输速率为100MBPS,那么传输25MBYTE的数据,理论上需要的时间是多少?怎么计算?

串口的最大传输速率到底是多少

数据传输速率和磁盘传输速率是相同还是不同?

哪种无线协议的传输速率最高