问题
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.
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
example code:
#!/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_())
回答1:
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_())
来源:https://stackoverflow.com/questions/53751909/progress-bar-does-not-render-until-job-is-complete