2013-05-30 16 views
6

Ho un widget che dovrebbe eseguire una pulizia manuale dopo che è stato distrutto (interrompere alcuni thread). Tuttavia per qualche motivo il segnale "distrutto" del widget non sta sparando. Ho fatto questo piccolo esempio che dimostra il problema.Il segnale "distrutto" del widget non viene attivato (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) 

Mi aspettavo che stampasse "Hello World!" quando la finestra principale è chiusa. Tuttavia, non stampa nulla.

risposta

5

Dopo alcuni tentativi ho scoperto che è funziona se si dichiara il doSomeDestruction al di fuori della classe. (vedere in fondo)
Ma non so perché. Come scritto nella risposta this, questo è dovuto al fatto che At the point destroyed() is emitted, the widget isn't a QWidget anymore, just a QObject (as destroyed() is emitted from ~QObject).
Ciò significa che quando la funzione viene chiamata, è già stata cancellata se la si scrive nella classe. (Guarda anche here nella mailing list qt interesse: 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: ho trovato due modi rendono davvero funzionare:

  1. aggiungere un semplice del window dopo ret = app.exec_().
  2. Impostare l'attributo WA_DeleteOnClose nella finestra principale (non il widget):
    Nella parte superiore del programma:

    from PyQt4 import QtCore 
    

    La cambiato __init__ funzione:

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

È possibile condividere il codice che stampa Hello World con me? Ho provato a modificare il codice in base alle tue istruzioni ma ancora non funziona. http://pastebin.com/ZCgteHu4 – Scintillo

+0

Questo è molto strano. L'unica differenza nel mio codice è che 'doSomeDestruction' è definito tra le due classi. Ma ora ho scoperto che funziona anche solo a volte. Anche un ['gc.collect'] (http://docs.python.org/3.3/library/gc.html#gc.collect), ma non ha avuto effetto. Spero che/qualcuno possa trovare un modo per farlo funzionare. – TobiMarg

+0

Vedere la mia risposta aggiornata. Dovrei lavorare ora. (non importa dove 'doSomeDestruction' è definito) – TobiMarg

0

Il l'istanza della classe python (o almeno il pyqt < -> qt link) non esiste al momento destroyed è emitte d. È possibile aggirare il problema rendendo il gestore destroyed un staticmethod sulla classe. In questo modo, il metodo python esiste ancora quando viene emesso il segnale 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 

Se avete bisogno di informazioni specifiche per l'istanza di classe è possibile utilizzare functools.partial e l'istanza __dict__ a passare queste informazioni al metodo di distruzione.

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

Perché non solo:' self.destroyed.connect (lambda: stampa (self .__ dict __)) '? Oppure usa una chiusura, come nell'esempio originale dei PO. (L'unico motivo per cui l'esempio non funzionava era perché i widget non venivano mai cancellati esplicitamente). – ekhumoro

+0

@ekhumoro Normalmente stai facendo operazioni molto più consistenti di una singola stampa.'lambda' supporta solo una singola istruzione. Normalmente, ci si interfaccia con una manciata di altre librerie e oggetti che devono essere ripuliti quando questo oggetto viene distrutto. Interessante, non ho nemmeno notato che l'esempio originale stava usando una chiusura e non un metodo di istanza di classe. Penso che anche una chiusura funzionerebbe. –

+0

Suppongo che avrei dovuto chiedere: qual è il vantaggio specifico di usare un 'staticmethod'? Perché non usare semplicemente * qualsiasi * funzione ordinaria (non deve essere una chiusura), al contrario di un metodo di istanza? – ekhumoro

Problemi correlati