How to clear the link of the child window stored in the parent application when the child window closes?

I am not sure how to solve the following problem:

  • I have a PyQT application that opens some child windows whenever you click a button
  • I keep a link to this popup in my application class ( self.w below)
  • If I close the popup, the self.w variable will still contain a link to the closed window
  • If somewhere in my code I call self.w.repaint() , I get a RuntimeError: underlying C/C++ object has been deleted error message. This error is due to the fact that we still have a link to a closed window stored in self.w

Question: How can I change the code below so that when the popup is closed, the self.w attribute self.w set to None automatically? What are the good and bad ways to implement this? The reason I want to set self.w to None is because I can check if this attribute is None , and if it does, I can reinitialize the popup before calling repaint() and thus avoid error messages.

 #!/usr/bin/env python #-*- coding: utf-8 -*- import sys from PyQt4.Qt import * class MyPopup(QWidget): def __init__(self): QWidget.__init__(self) def paintEvent(self, e): dc = QPainter(self) dc.drawLine(0, 0, 100, 100) dc.drawLine(100, 0, 0, 100) 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.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() 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) 
+2
source share
1 answer

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.

+1
source

All Articles