Borrowing code from : Progress Bar Does not Render Until Job is Complete , I tried to to find way to quit/kill a Qthread while it is working, here my code, you can quit the main window while progress bar is working stopping files to be copied:
import os
import sys
import shutil
from PyQt5 import QtCore, QtWidgets
class myProgressDialog(QtWidgets.QProgressDialog):
def __init__(self, parent=None):
super(myProgressDialog, self).__init__(parent=parent)
def closeEvent(self, event):
"""Get the name of active window about to close
"""
print('cant close')
event.ignore()
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"
srcdir = "in"
dstdir = "out"
self.le_src = QtWidgets.QLineEdit(srcdir)
self.le_dst = QtWidgets.QLineEdit(dstdir)
self.button = QtWidgets.QPushButton("Copy")
# self.button.clicked.connect(self.archiveEntry)
self.button.clicked.connect(self.archiveEntry2)
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)
print('self,thread :', self.thread)
def archiveEntry2(self):
print('connected')
self.progressbar = myProgressDialog(self)
# RIMUOVO Cancel Button
self.progressbar.setCancelButton(None)
self.progressbar.hide()
self.thread = QtCore.QThread(self)
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(self.thread)
self.archiveEntry()
## Questo funziona
def closeEvent(self, event):
"""Get the name of active window about to close
"""
print('killing thread')
try:
if self.thread.isRunning():
print('killing running thread', self.thread.isRunning())
# self.thread.terminate() ## ---------> error Qt has caught an exception thrown from an event handler.
self.thread.quit() ### funziona ma non in SPYDER
except Exception as Exceptionz:
print('Exception :', Exceptionz)
try:
print('killing running thread after quit :', self.thread.isRunning())
except:
print('quitted')
event.accept()
@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)
for i in range(100000):
i = i*10
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_())
Not sure if it is the right way to kill a Qthread but seems to work.
Even if after stopping the Qthread: self.thread.quit()
I stop the copying of files
but the self.thread.isRunning()
still returns True
.
When trying to split the code and add another window using:
main.py
import os
import sys
import shutil
from PyQt5 import QtCore, QtWidgets
from mod007b_import import Windowz, MoveFileHelper, myProgressDialog
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
QtWidgets.QMainWindow.__init__(self)
self.layout = QtWidgets.QHBoxLayout()
self.lineEdit = QtWidgets.QLineEdit()
self.lineEdit.setText("Just to fill up the dialog")
self.layout.addWidget(self.lineEdit)
self.button = QtWidgets.QPushButton('pppppp')
self.layout.addWidget(self.button)
self.widget = QtWidgets.QWidget()
self.widget.setLayout(self.layout)
self.setCentralWidget(self.widget)
self.setWindowTitle('Simple')
self.button.clicked.connect(self.newWindow)
self.listz = []
def newWindow(self):
print('newwindow')
self.pippo = Windowz() ########## RIVEDERE PARENT CHILD RELATIONSHIP
self.pippo.show()
# self.listz.append(self.pippo)
pw = self.pippo.parentWidget()
print('list : ', self.listz)
print(pw)
if pw is not None:
print('self :', self)
print('pw : ', pw, pw.layout)
print('pippo :', self.pippo)
# print(' central_widget :', central_widget, type( central_widget))
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
ex = MainWindow()
# ex.setWindowTitle('Simple**************')
ex.resize(640, ex.sizeHint().height())
ex.show()
sys.exit(app.exec_())
and
mod007b_import.py
import os
import sys
import shutil
from PyQt5 import QtCore, QtWidgets
class myProgressDialog(QtWidgets.QProgressDialog):
def __init__(self, parent=None):
super(myProgressDialog, self).__init__(parent=parent)
def closeEvent(self, event):
"""Get the name of active window about to close
"""
print('cant close')
event.ignore()
class Windowz(QtWidgets.QWidget):
# class Windowz(QtWidgets.QMainWindow):
startMoveFilesSignal = QtCore.pyqtSignal(str, str)
# def __init__(self,parent=None):
# # super(Windowz, self).__init__(parent=parent)
# super(Windowz, self).__init__(parent=parent)
def __init__(self):
# super(Windowz, self).__init__(parent=parent)
super().__init__()
# srcdir = "/media/zachlab/Windows/LinuxStorage/old/embryos"
# dstdir = "/media/zachlab/Windows/LinuxStorage/old/out"
srcdir = "in"
dstdir = "out"
self.le_src = QtWidgets.QLineEdit(srcdir)
self.le_dst = QtWidgets.QLineEdit(dstdir)
self.button = QtWidgets.QPushButton("Copy")
# self.button.clicked.connect(self.archiveEntry)
self.button.clicked.connect(self.archiveEntry2)
### spostati in Main
# central_widget2 = QtWidgets.QWidget()
# self.setCentralWidget(central_widget2)
# lay = QtWidgets.QFormLayout(central_widget2)
self.lay = QtWidgets.QFormLayout(self)
self.lay.addRow("From: ", self.le_src)
self.lay.addRow("To: ", self.le_dst)
self.lay.addRow(self.button)
print('self,thread :', self.thread)
# self.show()
def archiveEntry2(self):
print('connected')
self.progressbar = myProgressDialog(self)
# RIMUOVO Cancel Button
self.progressbar.setCancelButton(None)
self.progressbar.hide()
self.thread = QtCore.QThread(self)
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(self.thread)
self.archiveEntry()
## Questo funziona
def closeEvent(self, event):
"""Get the name of active window about to close
"""
print('killing thread')
try:
if self.thread.isRunning():
print('killing running thread', self.thread.isRunning())
# self.thread.terminate() ## ---------> error Qt has caught an exception thrown from an event handler.
self.thread.quit() ### doesnt work
# self.progressbar.hide() ### hides the bar
# self.progressbar.close() ### doesnt work
try:
print('killing running thread after quit :', self.thread.isRunning())
except:
print('quitted')
except Exception as Exceptionz:
print('Exception :', Exceptionz)
event.accept()
@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)
for i in range(100000):
i = i*10
self.finished.emit()
I get a first window, pressing the 'pppppp' button it goes to a second one that is the same as the single file script above: press 'copy' button to start the copying/Qthread, but when I close this window even if the QThread seems to be stopped, progress bar doesnt disappear, I can hide the progress bar but cant close it and in any case the copying process reach completion.
Any idea what is going on ?
PS
in order to have the script working and a having a visible progress bar files need to be in a directory toghether with a 'in' folder with enough files to have a slow process.
quit
doesn't automatically stop it, nor its running function. You need to set a flag on the worker and check it periodically (for instance, at every iteration of the for loops) and eventually break or return, and connect the button to a function that will set that flag. – Wyndhamquit()
andwait()
(which blocks until the thread actually stops). – WyndhamTrue
toisRunning()
because setting the flag will not instantly stop the thread: the function needs to return first, meaning that you have to wait for the thread to get control and let the function evaluate the flag and finally return control to the main thread by actually quitting the thread. And, in any case, I don't see any reference to what you're saying in that post: if you're not overriding the QThread'srun()
(thus ignoring its event loop), a signal will be processed as expected. – Wyndhamwait()
is mandatory (otherwise it would be similar toterminate()
- which is also discouraged - if you're going to quit before waiting the correct exit of the thread). In any case, remember that QThread (similarly to python Thread) is an interface to the system thread: it causes it'srun()
(and whatever is called from it) to be executed in a separate thread. – Wyndham