如何删除对已关闭 windows 的所有引用?

How do I remove every reference to a closed windows?

在下面的示例中,我从主文件打开新的 child windows。对于每个新的 window,我在列表中添加了对它的引用以跟踪所有新的 window(我的整个软件中有很多)。 我的问题是当我打开了几个 windows 并关闭了其中一些时,对那些关闭的 windows 的引用仍然出现在列表中:

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>]

我怎样才能真正关闭 child window 并且在我的列表中没有他们的参考?否则明显没有关闭。

这是 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_())

请记住,关闭 window 不会 删除它(除非设置了 Qt.WA_DeleteOnClose 属性,默认情况下未设置)。

一个可能的解决方案是覆盖 closeEvent 并发送自定义信号。

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)

另一种可能性是始终设置 Qt.WA_DeleteOnClose 属性并连接到 destroyed 信号,但在这种情况下,您 不能 依赖信号参数(它与实际删除的 window 不匹配),并且必须使用带有 window 实例的 lambda 代替:

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))

请注意,我将 class 名称大写,因为 classes 不应使用小写名称。

您可以使用 closeEvent:

发送信号
import sys

from PyQt5.QtCore import pyqtSlot, pyqtSignal
from PyQt5.QtWidgets import (
    QMainWindow, QApplication, QPushButton, QVBoxLayout, QWidget
)
from PyQt5.QtGui import QCloseEvent


class window(QMainWindow):
    closed = pyqtSignal(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):
        new_window = window(self)
        new_window.closed.connect(self.remove_window_from_list)
        self.windows_list.append(new_window)
        self.windows_list[-1].show()
        print(self.windows_list)

    @pyqtSlot(QMainWindow)
    def remove_window_from_list(self, window: QMainWindow) -> None:
        self.windows_list.remove(window)
        print(self.windows_list)

    @pyqtSlot(QCloseEvent)
    def closeEvent(self, a0: QCloseEvent) -> None:
        self.closed.emit(self)
        super().closeEvent(a0)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    ex = window()
    ex.show()
    sys.exit(app.exec_())