QWidget не удаляется с родительским окном

#python #qt #pyqt

#python #qt #pyqt

Вопрос:

Используя приведенный ниже код, __del__ метод моего виджета предварительного просмотра никогда не вызывается. Если я раскомментирую строку « del window «, это произойдет. Почему?

 #!/usr/bin/env python

from PyQt4 import QtGui

class Preview(QtGui.QWidget):
  def __init__(self, parent):
    QtGui.QWidget.__init__(self, parent)

  def __del__(self):
    print("Deleting Preview")

class PreviewWindow(QtGui.QMainWindow):
  def __init__(self):
    QtGui.QMainWindow.__init__(self)

    self.widget = Preview(self)
    self.setCentralWidget(self.widget)

  def __del__(self):
    print("Deleting PreviewWindow")

if __name__ == "__main__":
  app = QtGui.QApplication(["Dimension Preview"])
  window = PreviewWindow()
  window.show()
  app.exec()
  # del window
  

Комментарии:

1. У меня это работает на Fedora Linux 14 (PyQt 4.8.3, Python 2.7). Однако мне пришлось изменить app.exec() на app.exec_().

2.@xioxox. Вы уверены, что вызываются оба __del__ метода?

3. @xioxox. Для меня, используя pyqt-4.8.6, вызывается только один __del__ метод; но с pyqt-4.8.5, оба делают. Интересно, есть ли ошибка в последней версии?

Ответ №1:

Если у QObject подкласса есть родительский элемент, Qt удалит его при удалении родительского элемента. С другой стороны, если у QObject подкласса нет родителя, он (в конечном итоге) будет удален python.

Надеюсь, этот пример несколько прояснит ситуацию:

 from PyQt4 import QtGui

class Widget(QtGui.QWidget):
    def __init__(self, parent):
        QtGui.QWidget.__init__(self, parent)
        self.destroyed.connect(self.handleDestroyed)

    def __del__(self):
        print ('__del__:', self)

    def handleDestroyed(self, source):
        print ('destroyed:', source)

class Foo(Widget):
    def __init__(self, parent):
        Widget.__init__(self, parent)

class Bar(Widget):
    def __init__(self, parent):
        Widget.__init__(self, parent)

class Window(Widget):
    def __init__(self, parent=None):
        Widget.__init__(self, parent)
        self.foo = Foo(self)
        self.bar = Bar(None)

if __name__ == "__main__":

    app = QtGui.QApplication([__file__, '-widgetcount'])
    window = Window()
    window.show()
    app.exec_()
  

Какие результаты:

 __del__: <__main__.Window object at 0x88f514c>
destroyed: <__main__.Foo object at 0x88f5194>
__del__: <__main__.Bar object at 0x88f51dc>
Widgets left: 0    Max widgets: 4 
  

Редактировать

Если подумать, то, похоже, в некоторых версиях PyQt4 может быть ошибка (или, по крайней мере, разница в поведении).

В качестве возможного обходного пути кажется, что создание двух имен python для основного виджета, а затем явное удаление каждого из них может помочь гарантировать, что обе стороны объекта C и python будут уничтожены.

Если в приведенный выше скрипт добавлена следующая строка:

 tmp = window; del tmp, window
  

Затем вывод становится:

 __del__: <__main__.Window object at 0x8d3a14c>
__del__: <__main__.Foo object at 0x8d3a194>
__del__: <__main__.Bar object at 0x8d3a1dc>
Widgets left: 0    Max widgets: 4
  

Комментарии:

1. Значит __del__ , ожидаемое поведение в этом случае никогда не вызывается? Это приводит к утечкам в моем приложении. И использование parent=None не поможет, если вы это сделаете self.setCentralWidget(self.widget) .

2. @TavianBarnes. Я отредактировал свой ответ и добавил возможное решение вашей проблемы.

3. Для записи это был ответ: riverbankcomputing.com/pipermail/pyqt/2011-November/030656.html