Search code examples
pythonpyqt5window

How do I remove every reference to a closed windows?


In the below example, I open new child windows from the main one. For each new window, I add a reference to it in a list to keep track on all new window (I have plenty of them in my whole software). My issue is when I have opened several windows and I have closed some of them, the references to those closed windows still appear in the list:

1 window open
[<__main__.window object at 0x000002B91B7D1798>]
2 windows open
[<__main__.window object at 0x000002B91B7D1798>, <__main__.window object at 0x000002B91B7D19D8>]
3 windows open    
[<__main__.window object at 0x000002B91B7D1798>, <__main__.window object at 0x000002B91B7D19D8>, <__main__.window object at 0x000002B91B7D1C18>]
4 windows open
[<__main__.window object at 0x000002B91B7D1798>, <__main__.window object at 0x000002B91B7D19D8>, <__main__.window object at 0x000002B91B7D1C18>, <__main__.window object at 0x000002B91B7D1E58>]

hereI closed the first two windows, so 3 windows are opened, but I still have :
[<__main__.window object at 0x000002B91B7D1798>, <__main__.window object at 0x000002B91B7D19D8>, <__main__.window object at 0x000002B91B7D1C18>, <__main__.window object at 0x000002B91B7D1E58>, <__main__.window object at 0x000002B91B8640D8>]

How can I really close the child window and don't have their reference in my list? Otherwise, they are not closed obviously.

here's the MRE

from PyQt5.QtWidgets import *
import sys



class window(QMainWindow):
    def __init__(self, parent=None ):
        super(window, self).__init__()
        self.centralWidget = QWidget()
        self.setCentralWidget(self.centralWidget)
        self.HBOX = QVBoxLayout()
        self.PB = QPushButton('open new window')
        self.PB.clicked.connect(self.new_window)
        self.HBOX.addWidget(self.PB)
        self.centralWidget.setLayout(self.HBOX)

        self.windows_list = []

    def new_window(self):
        self.windows_list.append(window(self))
        self.windows_list[-1].show()
        print(self.windows_list)

 
if __name__ == "__main__":

    app = QApplication(sys.argv)
    ex = window()
    ex.show()
    sys.exit(app.exec_())

Solution

  • Keep in mind that closing a window does not delete it (unless the Qt.WA_DeleteOnClose attribute is set, which is not by default).

    A possible solution is to override the closeEvent and send a custom signal.

    class Window(QMainWindow):
        closed = QtCore.pyqtSignal(object)
        # ...
        def new_window(self):
            new_window = Window(self)
            self.windows_list.append(new_window)
            new_window.show()
            new_window.closed.connect(self.window_list.remove)
    
        def closeEvent(self, event):
            self.closed.emit(self)
    

    Another possibility is to always set the Qt.WA_DeleteOnClose attribute and connect to the destroyed signal, but in that case you cannot rely on the signal argument (it doesn't match the actual deleted window), and a lambda with the window instance must be used instead:

    class Window(QMainWindow):
        # ...
        def new_window(self):
            new_window = Window(self)
            self.windows_list.append(new_window)
            new_window.setAttribute(Qt.WA_DeleteOnClose)
            new_window.show()
            new_window.destroyed.connect(lambda: self.windows_list.remove(new_window))
    

    Note that I capitalized the class name, as classes should not use lower cased names.