I have a widget which would have to do some manual cleanup after it's destroyed (stop some threads). However for some reason the "destroyed" signal of the widget is not firing. I have made this small example that demonstrates the problem.
import sys
from PyQt4 import QtGui
class MyWidget(QtGui.QWidget):
def __init__(self, parent):
super(MyWidget, self).__init__(parent)
def doSomeDestruction():
print('Hello World!')
self.destroyed.connect(doSomeDestruction)
class MyWindow(QtGui.QMainWindow):
def __init__(self):
super(MyWindow, self).__init__()
self.widget = MyWidget(self)
app = QtGui.QApplication(sys.argv)
window = MyWindow()
window.show()
ret = app.exec_()
sys.exit(ret)
I expected it to print "Hello World!" when the main window is closed. However, it doesn't print anything.
The python class instance (or at least the pyqt<->qt link) doesn't exist by the time destroyed
is emitted. You can work around this by making the destroyed
handler a staticmethod
on the class. This way, the python method will still exist when the destroyed
signal is emitted.
class MyWidget(QWidget):
def __init__(self, parent):
super(MyWidget, self).__init__(parent)
self.destroyed.connect(MyWidget._on_destroyed)
@staticmethod
def _on_destroyed():
# Do stuff here
pass
If you need information specific to the class instance you can use functools.partial
and the instance __dict__
to pass that info to the destruction method.
from functools import partial
class MyWidget(QWidget):
def __init__(self, parent, arg1, arg2):
super(MyWidget, self).__init__(parent)
self.arg1 = arg1
self.arg2 = arg2
self.destroyed.connect(partial(MyWidget._on_destroyed, self.__dict__))
@staticmethod
def _on_destroyed(d):
print d['arg1']
After a few tries I found out that it works if you declare the (see at the bottom)doSomeDestruction
outside the class.But I don't know why. As written in this answer, this is because At the point destroyed() is emitted, the widget isn't a QWidget anymore, just a QObject (as destroyed() is emitted from ~QObject)
.
This means when your function would be called it is already deleted if you write it in the class. (look also here in the qt-interest mailing list: Ok , I am sorry for stupid question. The signal is emitted, but the slot is not called for the obvious reason, that is because object is already deleted.
)
EDIT: I've found two ways make it really work:
del window
after ret = app.exec_()
.Set the WA_DeleteOnClose
attribute in the main window (not the widget):
At the top of the program:
from PyQt4 import QtCore
The changed __init__
function:
class MyWindow(QtGui.QMainWindow):
def __init__(self):
super(MyWindow, self).__init__()
self.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
self.widget = MyWidget(self)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With