I am encountering this problem QThread: Destroyed while thread is still running when I close the NextWindow QDialog.
The code I wrote is
import sys
from PyQt5 import QtWidgets as qtw
from PyQt5 import QtGui as qtg
from PyQt5 import QtCore as qtc
from pathlib import Path
from hashlib import sha1
def recursive_hashes(path):
"""Generate name and SHA1 hash of all files under the given path"""
if path.is_file():
sha1_obj = sha1()
try:
with open(path, 'rb') as handle:
while True:
data = handle.read(8192)
if not data:
break
sha1_obj.update(data)
sha1_hash = sha1_obj.hexdigest()
except PermissionError:
sha1_hash = 'Permission Denied'
yield (str(path), sha1_hash)
elif path.is_dir():
try:
for child in path.iterdir():
yield from recursive_hashes(child)
except PermissionError:
yield(str(path), 'Permission Denied')
else:
yield (str(path), 'Not a file or dir')
class Worker(qtc.QObject):
hashed = qtc.pyqtSignal(str, str)
@qtc.pyqtSlot(str)
def hash_directory(self, root):
hash_gen = recursive_hashes(Path(root))
for path, sha1_hash in hash_gen:
self.hashed.emit(path, sha1_hash)
class NextWindow(qtw.QDialog):
hash_requested = qtc.pyqtSignal(str)
def __init__(self):
"""MainWindow constructor."""
super().__init__()
layout = qtw.QFormLayout()
self.setLayout(layout)
self.file_root = qtw.QLineEdit(returnPressed=self.start_hashing)
self.go_button = qtw.QPushButton('Start Hashing', clicked=self.start_hashing)
self.results = qtw.QTableWidget(0, 2)
self.results.setHorizontalHeaderLabels(['Name', 'SHA1-sum'])
self.results.horizontalHeader().setSectionResizeMode(qtw.QHeaderView.Stretch)
self.results.setSizePolicy(qtw.QSizePolicy.Expanding, qtw.QSizePolicy.Expanding)
layout.addRow(qtw.QLabel('SHA1 Recursive Hasher'))
layout.addRow('File Root', self.file_root)
layout.addRow('', self.go_button)
layout.addRow(self.results)
# Create a worker object and a thread
self.worker = Worker()
self.worker_thread = qtc.QThread(self)
self.worker.hashed.connect(self.add_hash_to_table)
self.hash_requested.connect(self.worker.hash_directory)
# Assign the worker to the thread and start the thread
self.worker.moveToThread(self.worker_thread)
self.worker_thread.start()
# Connect signals & slots AFTER moving the object to the thread
# End main UI code
self.show()
def add_hash_to_table(self, name, sha1_sum):
"""Add the given name and sha1 sum to the table"""
row = self.results.rowCount()
self.results.insertRow(row)
self.results.setItem(row, 0, qtw.QTableWidgetItem(name))
self.results.setItem(row, 1, qtw.QTableWidgetItem(sha1_sum))
print(name, sha1_sum)
def start_hashing(self):
"""Prepare the GUI and emit the hash_requested signal"""
# Clear the table
while self.results.rowCount() > 0:
self.results.removeRow(0)
# Get the file root and validate it
file_root = self.file_root.text()
if not Path(file_root).exists():
qtw.QMessageBox.critical(self, 'Invalid Path', 'The specified file root does not exist.')
return
# Emit the signal
self.hash_requested.emit(file_root)
#self.worker.hash_directory(file_root)
class MainWindow(qtw.QDialog):
def __init__(self):
super().__init__()
layout = qtw.QFormLayout()
self.next_button = qtw.QPushButton('Next', clicked=self.next_button)
layout.addWidget(self.next_button)
self.setLayout(layout)
def next_button(self):
dialog = NextWindow()
dialog.exec_()
if __name__ == '__main__':
app = qtw.QApplication(sys.argv)
mw = MainWindow()
mw.show()
sys.exit(app.exec())
I have checked reference answers in PyQt5 - QThread: Destroyed while thread is still running and QThread: Destroyed while thread is still running.
The answers are basically didnt assign instance to the QThread like
self.thread = QThread(parent=self) While I did so but the issue persisted.
The debug message of QThread: Destroyed while thread is still running can be ubiquitous but the cause of the reason can be not always the same. For this case the thread has not been ended well when you close the dialog even you include the self as
self.worker_thread = qtc.QThread(self)
In this case you should reject the Qthread as mentioned in the question Terminating QThread gracefully on QDialog reject()
So you should add one more line in your code
self.worker_thread.start()
self.rejected.connect(self.thread.terminate)