进度条在作业完成之前不会呈现 [英] Progress Bar Does not Render Until Job is Complete

查看:58
本文介绍了进度条在作业完成之前不会呈现的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试制作一个用于复制大文件的进度条.但是,当前对话框窗口会变黑,直到该过程完成.我现在明白我可能必须学习如何使用胎面并将数据传回 GUI.但是我仍然不明白为什么窗口无法完全渲染.我会理解窗口是否因为 moveFilesWithProgress 函数正在运行而没有响应.但在该函数中,我正在更新进度条值.我什至尝试添加 QtGui.QGuiApplication.processEvents() ,希望它能在继续迭代之前更新 gui.

I am trying to make a progress bar for copying large files. However, currently the Dialog window goes black until the process is finished. I now understand I probably have to learn how to use treads and pass the data back into the GUI. But I still don't understand why the window fails to render completely. I'd understand if the window was unresponsive because the moveFilesWithProgress function is running. But within that function I am updating the progress bar value. I even tried adding QtGui.QGuiApplication.processEvents() in the hopes that it will update the gui before continuing to iterate.

如果没有多线程进程,我将不胜感激任何可能解决此问题的帮助,但如果不可能,我将非常感谢如何使用 Qthread 的简化示例.我发现了一个相关的问题,但是我有很多事情要理解这个例子.这里是链接

I would appreciate any help to possibly fix this without a multi threaded process but if it is not possible I would greatly appreciate a dumbed down example of how to use Qthread. I found a related question but there was to much going on for me to understand the example. here is the link

#!/usr/bin/env python
import os
import sys
import shutil
from PyQt5.QtWidgets import (QApplication, QDialog, QLineEdit, QMainWindow,
                            QPushButton, QProgressBar)
import PyQt5.QtGui as QtGui

class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.button = QPushButton("Copy", self)
        self.button.clicked.connect(self.archiveEntry)
        self.show()

    def archiveEntry(self):
        self.p = ProgressBar()
        self.p.show()
        Dir = "/media/zachlab/Windows/LinuxStorage/old/embryos"
        outDir = "/media/zachlab/Windows/LinuxStorage/old/out"
        run = self.p.moveFilesWithProgress(Dir, outDir)
        # if run:
        #    self.p.close


class ProgressBar(QDialog):
    def __init__(self):
        super().__init__()
        self.pbar = QProgressBar(self)

    def timerEvent(self, e):
        if self.step >= 100:
            self.timer.stop()
            self.btn.setText('Finished')
            return
        self.pbar.setValue(self.step)

    def update(self, value):
        self.pbar.setValue(value)
        QtGui.QGuiApplication.processEvents()

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

    def countFiles(self, directory):
        files = []
        if os.path.isdir(directory):
            for path, dirs, filenames in os.walk(directory):
                files.extend(filenames)
        return len(files)

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

    def moveFilesWithProgress(self, src, dest):
        numFiles = self.countFiles(src)
        if os.path.exists(dest):
            return 0
        if numFiles > 0:
            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
                    self.calculateAndUpdate(numCopied, numFiles)
                    self.show()
            return 1
        else:
            return 0


if __name__ == "__main__":
    app = QApplication(sys.argv)
    ex = MainWindow()
    sys.exit(app.exec_())

推荐答案

重任务不应该在主线程上运行,你应该使用线程.在这种情况下,我已经替换了您的逻辑,因此有一个类负责复制通过进度、错误等迹象通知的文件.该类的一个对象将存在于另一个通过信号通知 GUI 的线程中,在此如果我将使用 QProgressDialog 来显示进度.

Heavy tasks should not run on the main thread, you should use a thread. In this case I have replaced your logic, so there is a class that is in charge of copying the files informing through signs of progress, error, etc. An object of that class will live in another thread notifying the GUI through signals, in this case I will use QProgressDialog to show the progress.

#!/usr/bin/env python
import os
import sys
import shutil
import threading
from PyQt5 import QtCore, QtGui, QtWidgets

class MainWindow(QtWidgets.QMainWindow):
    startMoveFilesSignal = QtCore.pyqtSignal(str, str)

    def __init__(self):
        super(MainWindow, self).__init__()
        srcdir = "/media/zachlab/Windows/LinuxStorage/old/embryos"
        dstdir = "/media/zachlab/Windows/LinuxStorage/old/out"
        self.le_src = QtWidgets.QLineEdit(srcdir)
        self.le_dst = QtWidgets.QLineEdit(dstdir)
        self.button = QtWidgets.QPushButton("Copy")
        self.button.clicked.connect(self.archiveEntry)

        central_widget = QtWidgets.QWidget()
        self.setCentralWidget(central_widget)
        lay = QtWidgets.QFormLayout(central_widget)
        lay.addRow("From: ", self.le_src)
        lay.addRow("To: ", self.le_dst)
        lay.addRow(self.button)

        self.progressbar = QtWidgets.QProgressDialog(self)
        self.progressbar.hide()

        thread = QtCore.QThread(self)
        thread.start()
        self.helper = MoveFileHelper()
        self.startMoveFilesSignal.connect(self.helper.moveFilesWithProgress)
        self.helper.progressChanged.connect(self.progressbar.setValue)
        self.helper.finished.connect(self.on_finished)
        self.helper.started.connect(self.progressbar.show)
        self.helper.errorOccurred.connect(self.on_errorOcurred)
        self.helper.moveToThread(thread)

    @QtCore.pyqtSlot()
    def archiveEntry(self):
        self.startMoveFilesSignal.emit(self.le_src.text(), self.le_dst.text())
        self.progressbar.hide()

    @QtCore.pyqtSlot()
    def on_finished(self):
        self.button.setText('Finished')

    @QtCore.pyqtSlot(str)
    def on_errorOcurred(self, msg):
        QtWidgets.QMessageBox.critical(self, "Error Ocurred", msg)

class MoveFileHelper(QtCore.QObject):
    progressChanged = QtCore.pyqtSignal(int)
    started = QtCore.pyqtSignal()
    finished = QtCore.pyqtSignal()
    errorOccurred = QtCore.pyqtSignal(str)

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

    @staticmethod
    def countFiles(directory):
        count = 0
        if os.path.isdir(directory):
            for path, dirs, filenames in os.walk(directory):
                count += len(filenames)
        return count

    @staticmethod
    def makedirs(dest):
        if not os.path.exists(dest):
            os.makedirs(dest)

    @QtCore.pyqtSlot(str, str)
    def moveFilesWithProgress(self, src, dest):
        numFiles = MoveFileHelper.countFiles(src)
        if os.path.exists(dest):
            self.errorOccurred.emit("Dest exist")
            return 
        if numFiles > 0:
            self.started.emit()
            MoveFileHelper.makedirs(dest)
            numCopied = 0
            for path, dirs, filenames in os.walk(src):
                for directory in dirs:
                    destDir = path.replace(src, dest)
                    MoveFileHelper.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
                    self.calculateAndUpdate(numCopied, numFiles)
            self.finished.emit()

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    ex = MainWindow()
    ex.resize(640, ex.sizeHint().height())
    ex.show()
    sys.exit(app.exec_())

这篇关于进度条在作业完成之前不会呈现的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆