To answer your question:
How can I change the code below so that when the popup is closed, the self.w attribute is set to None automatically? What are good and bad ways to implement this?
You should try adding this to your popup method of the init class: self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
You will find it in the docs: http://qt-project.org/doc/qt-4.8/qt.html#WidgetAttribute-enum
In retrospect, if calling your self.w.repaint () raises this RuntimeError: underlying C/C++ object has been deleted , it means that the object is really deleted. self.setAttribute(QtCore.Qt.WA_DeleteOnClose) ensures that the object will be deleted when it is closed. This is the cleanest way to do this.
Gathering from your comments, you want to clear your class property that contains this widget when this close event occurs. To do this, we can simply make the pop-up window give a signal when it closes, which the main window can catch in order to make a release. You can do it like this:
#!/usr/bin/env python #-*- coding: utf-8 -*- import sys from PyQt4.Qt import * from PyQt4 import QtCore class MyPopup(QWidget): close_signal = pyqtSignal() def __init__(self): QWidget.__init__(self) self.setAttribute(QtCore.Qt.WA_DeleteOnClose) def paintEvent(self, e): dc = QPainter(self) dc.drawLine(0, 0, 100, 100) dc.drawLine(100, 0, 0, 100) def closeEvent(self, event): self.on_close() def on_close(self): """ Perform on close stuff here """ self.close_signal.emit() class MainWindow(QMainWindow): def __init__(self, *args): QMainWindow.__init__(self, *args) self.cw = QWidget(self) self.setCentralWidget(self.cw) self.btn1 = QPushButton("Click me", self.cw) self.btn1.setGeometry(QRect(0, 0, 100, 30)) self.connect(self.btn1, SIGNAL("clicked()"), self.doit) self.btn2 = QPushButton("repaint me", self.cw) self.btn2.setGeometry(QRect(100, 30, 200, 50)) self.connect(self.btn2, SIGNAL("clicked()"), self.repaintPopup) self.w = None def doit(self): print "Opening a new popup window..." self.w = MyPopup() self.w.setGeometry(QRect(100, 100, 400, 200)) self.w.show() self.w.close_signal.connect(self.on_popup_closed) def repaintPopup(self): self.w.repaint() def on_popup_closed(self): """ Cleanup the popup widget here """ print "Popup closed." self.w = None class App(QApplication): def __init__(self, *args): QApplication.__init__(self, *args) self.main = MainWindow() self.connect(self, SIGNAL("lastWindowClosed()"), self.byebye ) self.main.show() def byebye( self ): self.exit(0) def main(args): global app app = App(args) app.exec_() if __name__ == "__main__": main(sys.argv)
Here, although your popup screen is a standalone widget without any parent. That is why it appears as a window. If you want to maintain parental relationships with children, your popup should be a window (e.g. QMainWindow) with MyPopup QWidget as its center widget, and your main window should be the parent. This ensures that this popup is always considered your primary child window and allows the popup to go to the main window using the nativeParentWidget () method. To make this change, you just need to rework a little like this:
#!/usr/bin/env python #-*- coding: utf-8 -*- import sys from PyQt4.Qt import * from PyQt4 import QtCore class MyPopup(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) def paintEvent(self, e): dc = QPainter(self) dc.drawLine(0, 0, 100, 100) dc.drawLine(100, 0, 0, 100) class PopupWindow(QMainWindow): close_signal = QtCore.pyqtSignal() def __init__(self, *args): QMainWindow.__init__(self, *args) self.cw = MyPopup(parent=self) self.setCentralWidget(self.cw) self.setGeometry(QRect(100, 100, 400, 200)) self.cw.setGeometry(QRect(100, 100, 400, 200)) self.setAttribute(QtCore.Qt.WA_DeleteOnClose) print "I am the popup Window. My parent is: %s" % self.nativeParentWidget() # access the parent here def closeEvent(self, event): """ Perform on close stuff here """ self.on_close() def on_close(self): self.close_signal.emit() class MainWindow(QMainWindow): def __init__(self, *args): QMainWindow.__init__(self, *args) self.cw = QWidget(self) self.setCentralWidget(self.cw) self.btn1 = QPushButton("Click me", self.cw) self.btn1.setGeometry(QRect(0, 0, 100, 30)) self.connect(self.btn1, SIGNAL("clicked()"), self.doit) self.btn2 = QPushButton("repaint me", self.cw) self.btn2.setGeometry(QRect(100, 30, 200, 50)) self.connect(self.btn2, SIGNAL("clicked()"), self.repaintPopup) self.w = None def doit(self): print "Opening a new popup window..." self.w = PopupWindow(self) self.w.show() self.w.repaint() self.w.close_signal.connect(self.on_popup_closed) def repaintPopup(self): self.w.repaint() def on_popup_closed(self): """ Cleanup the popup widget here """ print "Popup closed." self.w = None class App(QApplication): def __init__(self, *args): QApplication.__init__(self, *args) self.main = MainWindow() self.connect(self, SIGNAL("lastWindowClosed()"), self.byebye ) self.main.show() def byebye( self ): self.exit(0) def main(args): global app app = App(args) app.exec_() if __name__ == "__main__": main(sys.argv)
You can use the sip module isdeleted() method to check if your object has been deleted. Just check if you want to.
import sip sip.isdeleted(self.w)
Check out the other methods offered by the sip module for working with swig / C / C ++ objects here: http://pyqt.sourceforge.net/Docs/sip4/python_api.html
Hope this was helpful.