2010-08-17 17 views
7

Da quello che ho capito di twistato, non dovrebbe essere bloccato nulla nel thread del reattore. Tutte le attività di blocco devono essere delegate ad altri thread, per richiamare i callback nel thread del reattore quando hanno finito.twisted + gtk: dovrei eseguire le cose della GUI nei thread o nel thread del reattore?

Questo vale anche per le cose con gtk? Ad esempio, desidero visualizzare un messaggio "connessione non riuscita" se la connessione ... non è riuscita. Faccio:

def connectionFailed(self, reason): 
    dlg = gtk.MessageDialog(type=gtk.MESSAGE_ERROR, 
         buttons=gtk.BUTTONS_CLOSE, 
         message_format="Could not connect to server:\n%s" % (
          reason.getErrorMessage())) 
    dlg.run() 

o:

def connectionFailed(self, reason): 
    dlg = gtk.MessageDialog(type=gtk.MESSAGE_ERROR, 
         buttons=gtk.BUTTONS_CLOSE, 
         message_format="Could not connect to server:\n%s" % (
          reason.getErrorMessage())) 
    reactor.callInThread(dlg.run) 

o:

def connectionFailed(self, reason): 
    def bloogedy(): 
     dlg = gtk.MessageDialog(type=gtk.MESSAGE_ERROR, 
          buttons=gtk.BUTTONS_CLOSE, 
          message_format="Could not connect to server:\n%s" % (
           reason.getErrorMessage())) 
     dlg.run() 
    reactor.callInThread(bloogedy) 

?

MODIFICA: Ooh ok, gli ultimi due sono davvero incasinati. quindi immagino che la risposta sia la prima. Quindi la mia domanda è: perché? Sembra che questo bloccherebbe il filo del reattore.

risposta

10

Il problema in realtà non ha nulla a che fare con i thread e le GUI. È necessario utilizzare Twisted e GTK sempre dallo stesso thread: non è necessario fare diversamente.

Il tuo problema è che stai usando gtk.Dialog.run(). Questa è un'API che non dovresti mai usare, ritorta o no. Esegue un ciclo principale di rientro, che fa bloccare il gestore di eventi corrente, ma consente ad altri gestori di eventi di eseguire un livello in pila. GTK ha un eccellente supporto per i loop principali per i rientranti, ma Twisted non lo fa (e va bene perché, come ho detto, non dovresti usarli).

MessageDialog.run non funzionerà in un thread, in ogni caso, quindi non si dispone di tale opzione. Causerà un comportamento imprevedibile che può causare un comportamento strano o crash della tua applicazione. GTK ha un supporto eccellente per i thread, ma ci sono cose che non dovresti mai fare con un thread perché non ha alcun senso, e questo è uno di questi.

Se si ha a che fare con codice che non sta eseguendo alcuna elaborazione, ma si desidera solo attendere che qualcosa accada (come aspettare che un utente preme un pulsante in una finestra di dialogo) è necessario utilizzare le funzioni che restituiscono Deferred s , non discussioni. In questo caso, gtk.Dialog emetterà un segnale nel punto in cui viene risposto: "response". È possibile utilizzare questo per collegare una funzione molto semplice che visualizza il messaggio con una finestra di dialogo e restituisce un Deferred quando è completo. Ecco un esempio:

def showMessage(text): 
    mdlg = gtk.MessageDialog(type=gtk.MESSAGE_INFO, 
          buttons=gtk.BUTTONS_CLOSE, 
          message_format=text) 
    result = Deferred() 
    def response(dialog, response_id): 
     mdlg.destroy() 
     result.callback(response_id) 
     return False 
    mdlg.connect("response", response) 
    mdlg.show_all() 
    return result 
+0

ah che ha più senso. Avevo il sospetto che avesse qualcosa a che fare con 'run()'. i differiti sono sicuramente la strada da percorrere. – Claudiu

0

Dalla mia esperienza con Gtk +, l'opzione migliore è eseguire GUI in un thread separato. Puoi comminuire con il thread GUI eseguendo le funzioni nel loop principale di Gtk + (tramite la funzione idle_add). Non conosco il reattore, ma dai tuoi esempi sembra che sia possibile lo stesso modo di comunicare con la GUI.

Ad esempio, come questo (mi dispiace, non ho provato il codice):

def connectionFailed(self, reason): 
    def frob(): 
     dlg = gtk.MessageDialog(type=gtk.MESSAGE_ERROR, 
          buttons=gtk.BUTTONS_CLOSE, 
          message_format="Could not connect to server:\n%s" % (
           reason.getErrorMessage())) 
     dlg.run() 
    gobject.idle_add(frob) 

(oltre a questo codice, Gtk.main dovrà essere eseguito su un proprio filo)

Questo eseguirà la funzione frob in thread Gtk + e non bloccherà il thread del reattore.

Questo avvierà Gtk + in thread separato:

import threading 
import pygtk 
pygtk.require('2.0') 
import gtk 
import gobject 
def gtk_thread(): 
    gtk.main() 
threading.Thread(target=gtk_thread) 

Se avete bisogno di alcune interazioni GUI complesse, dovrete programmare utilizzando continuation-passing style

Edit: Esempio aggiunto di correre Gtk + in separato thread

+0

come si esegue la gui in una discussione diversa? Ho appena fatto 'gtk2reactor.install()', e ho usato quel reattore. Ho incontrato enormi problemi quando ho provato a usare twisted + gtk senza quello. – Claudiu

+0

Ho modificato il messaggio per includere il codice che avvia Gtk + nella thread separata –

0

Anche se non è raccomandato e non supportato, con Twisted 10.x sembra che il seguente codice renderà possibile continuare a utilizzare Gtk.main()/dialog.run()

gobject.idle_add(lambda *x: reactor.runUntilCurrent()) 
    reactor.startRunning()  
    dialog.run() 
Problemi correlati