2013-05-30 7 views
6

У меня есть виджет, который должен был бы выполнить ручную очистку после его уничтожения (остановить некоторые потоки). Однако по какой-то причине «разрушенный» сигнал виджета не срабатывает. Я сделал этот небольшой пример, демонстрирующий проблему.Не удалось запустить «разрушенный» сигнал виджета (PyQT)

import sys 
from PyQt4 import QtGui 

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

     def doSomeDestruction(): 
      print('Hello World!') 

     self.destroyed.connect(doSomeDestruction) 


class MyWindow(QtGui.QMainWindow): 
    def __init__(self): 
     super(MyWindow, self).__init__() 

     self.widget = MyWidget(self) 

app = QtGui.QApplication(sys.argv) 
window = MyWindow() 
window.show() 
ret = app.exec_() 
sys.exit(ret) 

Я ожидал, что он напечатает "Hello World!" когда главное окно закрыто. Однако он ничего не печатает.

ответ

5

После нескольких попыток я узнал, что он работает, если вы объявите doSomeDestruction вне класса. (см. Внизу)
Но я не знаю почему. Как написано в this Ответ, это потому, что At the point destroyed() is emitted, the widget isn't a QWidget anymore, just a QObject (as destroyed() is emitted from ~QObject).
Это означает, что когда ваша функция будет вызвана, она уже удалена, если вы напишете ее в классе. (Смотрите также here в списке рассылки кварты беспроцентного: Ok , I am sorry for stupid question. The signal is emitted, but the slot is not called for the obvious reason, that is because object is already deleted. )

EDIT: Я нашел два способа сделать это действительно работает:

  1. добавить простой del window после ret = app.exec_().
  2. Установить атрибут WA_DeleteOnClose в главном окне (а не виджет):
    В верхней части программы:

    from PyQt4 import QtCore 
    

    Измененное __init__ функции:

    class MyWindow(QtGui.QMainWindow): 
        def __init__(self): 
         super(MyWindow, self).__init__() 
         self.setAttribute(QtCore.Qt.WA_DeleteOnClose, True) 
         self.widget = MyWidget(self) 
    
+0

Можете ли вы поделиться кодом, который печатает Hello World со мной? Я попытался отредактировать код в соответствии с вашими инструкциями, но он все еще не работает. http://pastebin.com/ZCgteHu4 – Scintillo

+0

Это очень странно. Единственное отличие в моем коде состоит в том, что 'doSomeDestruction' определяется между двумя классами. Но теперь я обнаружил, что он работает и только иногда. Также ['gc.collect'] (http://docs.python.org/3.3/library/gc.html#gc.collect), но это не повлияло. Я надеюсь, что мы/кто-то найдем способ заставить его работать. – TobiMarg

+0

См. Мой обновленный ответ. Я должен сейчас работать. (независимо от того, где определен 'doSomeDestruction') – TobiMarg

0

экземпляр класса python (или, по крайней мере, pyqt < -> qt link) не существует к моменту destroyed is emitte д. Вы можете обойти это, сделав обработчик destroyedstaticmethod на классе. Таким образом, метод python будет существовать, когда выдается сигнал destroyed.

class MyWidget(QWidget): 

    def __init__(self, parent): 
     super(MyWidget, self).__init__(parent) 
     self.destroyed.connect(MyWidget._on_destroyed) 

    @staticmethod 
    def _on_destroyed(): 
     # Do stuff here 
     pass 

Если вам нужна информация, относящаяся к экземпляру класса вы можете использовать functools.partial и экземпляр __dict__ передать эту информацию к способу уничтожения.

from functools import partial 

class MyWidget(QWidget): 

    def __init__(self, parent, arg1, arg2): 
     super(MyWidget, self).__init__(parent) 
     self.arg1 = arg1 
     self.arg2 = arg2 
     self.destroyed.connect(partial(MyWidget._on_destroyed, self.__dict__)) 

    @staticmethod 
    def _on_destroyed(d): 
     print d['arg1'] 
+0

Почему не просто:' self.destroyed.connect (lambda: печать (само .__ ДИКТ __)) '? Или используйте закрытие, как в исходном примере OPs. (Единственная причина, по которой этот пример не срабатывал, заключалась в том, что виджеты никогда не были явно удалены). – ekhumoro

+0

@ekhumoro Вы обычно делаете гораздо более существенные операции, чем просто один отпечаток.'lambda' поддерживает только одно заявление. Как правило, вы должны взаимодействовать с несколькими другими библиотеками и объектами, которые необходимо очистить, когда этот объект будет уничтожен. Интересно, что я даже не заметил, что в исходном примере используется закрытие, а не метод экземпляра класса. Я думаю, что закрытие тоже сработает. –

+0

Полагаю, я должен был спросить: в чем особенное преимущество использования 'staticmethod'? Почему бы просто не использовать * любую * обычную функцию (не обязательно закрывать), в отличие от метода экземпляра? – ekhumoro

Смежные вопросы