В приведенном ниже примере я открываю новые дочерние окна из основного. Для каждого нового окна я добавляю ссылку на него в список, чтобы отслеживать все новые окна (у меня их много во всем моем программном обеспечении). Моя проблема заключается в том, что когда я открыл несколько окон и закрыл некоторые из них, ссылки на эти закрытые окна все еще отображаются в списке:
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>]
Как я могу действительно закрыть дочернее окно и не иметь их ссылки в моем списке? В противном случае они явно не закрыты.
вот МРЭ
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_())
Имейте в виду, что закрытие окна не удаляет его (если не установлен атрибут 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
и подключаться к сигналу уничтоженного, но в этом случае вы не можете полагаться на аргумент сигнала (он не соответствует фактическому удаленному окну), и лямбда с экземпляром окна должна вместо этого использовать:
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.
@ymmx закрытие виджета технически равносильно его скрытию: по умолчанию виджет не удаляется, а просто скрывается. Фактические различия заключаются в том, что событие закрытия может быть проигнорировано (не позволяя пользователю закрыть окно) и что, когда виджет верхнего уровня (окно) закрывается, Qt проверяет, видимо ли еще другое окно, и если нет, он автоматически закрывает окно. приложение (если quitOnLastWindowClosed() имеет значение True, по умолчанию). Кроме того, если не установлен WA_DeleteOnClose
, закрытие виджета просто скрывает его. ->
-> Пока приложение QApplication активно, оно хранит список всех виджетов и виджетов верхнего уровня (окна), которые все еще существуют, даже если они скрыты (включая те, которые были закрыты). На самом деле, даже если у вас больше нет ссылки на окно, вы все равно можете получить его из списка, возвращаемого QApplication.topLevelWidgets()
(или QApplication.allWidgets()
, если это не окно). Виджет фактически уничтожается только при вызове deleteLater()
(что происходит, если WA_DeleteOnClose
установлен).
Хорошо, я понимаю. Спасибо вам за разъяснение.
Извините, я должен уточнить. Если не существует абсолютно никакой ссылки на виджет (включая отсутствие существующего родителя Qt), виджет фактически уничтожается: это происходит из-за того, что объект подвергается сборке мусора, который вызывает метод __del__
виджета, который, в свою очередь, вызывает deleteLater()
на стороне Qt, если виджет не имеет родителя QObject (или QWidget). Вот почему виджеты без родителей, созданные в функции, мгновенно удаляются при возврате функции, если не создается ссылка на python.
Вы можете отправить сигнал с помощью 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` в коде.
Это подсказка типа, говорящая вам, что метод должен вернуть. Это не влияет на код запуска.
Хороший. Оно работает. Я не уверен, что понимаю, что вы подразумеваете под «закрытие окна не удаляет его». Даже если они больше не ссылаются на это окно, объект не удаляется? Я имею в виду, что если ссылки на это окно не существует, у нас больше не будет к нему доступа, верно?