【问题标题】:How do I remove every reference to a closed windows?如何删除对已关闭窗口的所有引用?
【发布时间】:2020-12-18 12:11:57
【问题描述】:

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

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

我怎样才能真正关闭子窗口并且在我的列表中没有它们的引用?否则,它们显然不会关闭。

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

【问题讨论】:

    标签: python pyqt5 window


    【解决方案1】:

    请记住,关闭窗口不会将其删除(除非设置了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),而必须使用带有窗口实例的 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))
    

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

    【讨论】:

    • 不错。有用。不过,我不确定“关闭窗口不会删除它”是什么意思。即使他们不再引用此窗口,对象也不会被删除?我的意思是,如果不存在对该窗口的引用,我们将无法再访问它,对吧?
    • @ymmx 关闭一个小部件在技术上与隐藏它相同:默认情况下,该小部件被删除,只是隐藏。实际的区别是可以忽略关闭事件(阻止用户关闭窗口),并且当顶级窗口小部件(窗口)关闭时,Qt 检查其他窗口是否仍然可见,如果不可见,它会自动退出应用程序(如果 quitOnLastWindowClosed() 为 True,则为默认值)。除此之外,除非设置了WA_DeleteOnClose,否则关闭小部件只会隐藏它。 ->
    • -> 只要 QApplication 处于活动状态,它就会保留 所有 小部件和仍然存在的顶级小部件(窗口)的列表,即使它们被隐藏(包括那些已经关闭)。事实上,即使您不再有对该窗口的引用,您仍然可以从QApplication.topLevelWidgets()(或QApplication.allWidgets(),如果它不是窗口)返回的列表中获取它。只有在调用 deleteLater() 时才会真正销毁小部件(如果设置了 WA_DeleteOnClose,则会发生这种情况)。
    • 好的,我明白了。谢谢你的解释。
    • 对不起,我最好澄清一下。如果绝对 no 对小部件的引用存在(包括不存在现有的 Qt 父级),则小部件实际上被销毁:这是由于对象被垃圾收集,它调用了小部件的 __del__ 方法,该方法反过来在 Qt 端调用deleteLater() if 小部件没有 QObject(或 QWidget)父级。这就是为什么如果没有创建 python 引用,则在函数中创建的没有父级的小部件会在函数返回时立即删除。
    【解决方案2】:

    您可以使用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_())
    

    【讨论】:

    • 它也有效。即使我不理解代码中的`-> None`。
    • type hinting 告诉你方法预期返回什么。它对运行代码没有影响。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-04-01
    • 2021-04-08
    • 2014-06-20
    • 1970-01-01
    • 1970-01-01
    • 2023-02-01
    • 1970-01-01
    相关资源
    最近更新 更多