Pregunta twisted + gtk: ¿debería ejecutar cosas GUI en hilos, o en el hilo del reactor?


Por lo que entiendo acerca de retorcido, no debe bloquearse nada que se ejecute en el hilo del reactor. Todas las actividades de bloqueo se deben delegar a otros hilos, para devolver las devoluciones de llamada al hilo del reactor cuando estén listas.

¿Esto también se aplica a las cosas gtk? Por ejemplo, quiero mostrar un mensaje de "conexión fallida" si la conexión ... falló. Qué hago?

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)

?

EDITAR: Ooh está bien, los últimos dos realmente mal. así que supongo que la respuesta es la primera. Entonces mi pregunta es: ¿por qué? Parece que esto bloquearía el hilo del reactor.


7
2017-08-17 16:29


origen


Respuestas:


Su problema en realidad no tiene nada que ver con los hilos y las GUI. Siempre debe usar Twisted y GTK del mismo hilo: no es necesario que haga lo contrario.

Tu problema es que estás usando gtk.Dialog.run(). Esta es una API que nunca debes usar, Twisted o no. Ejecuta un bucle principal reentrante, lo que hace que el controlador de eventos actual se bloquee, pero permite que otros manejadores de eventos ejecuten una capa en la pila. GTK tiene un excelente soporte para los bucles principales reentrantes, pero Twisted no (y eso está bien porque como dije, no deberías usarlos).

MessageDialog.run no funcionará en un hilo, de todos modos, por lo que en realidad no tiene esa opción. Causará un comportamiento impredecible que puede hacer que su aplicación se comporte de forma extraña o se bloquee. GTK tiene un buen soporte para los hilos, pero hay cosas que nunca se supone que debes hacer con un hilo porque no tiene ningún sentido, y esta es una de ellas.

Si está tratando con código que no está procesando, pero solo quiere esperar a que suceda algo (como esperar que un usuario presione un botón en un diálogo) debe usar funciones que regresen Deferreds, no hilos. Como sucede, gtk.Dialogs emitirá una señal en el punto donde se responde: "response". Puede usar esto para conectar una función muy simple que muestra su mensaje con un diálogo y devuelve un Deferred cuando está completo. Aquí hay un ejemplo:

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

10
2017-08-18 07:10



Desde mi experiencia con Gtk +, la mejor opción es ejecutar GUI en un hilo separado. Puede reducir con el hilo de la GUI ejecutando funciones en el bucle principal Gtk + (mediante idle_add función). No sé sobre reactor, pero de sus ejemplos parece que es posible la misma forma de comunicación de GUI.

Por ejemplo, así (lo siento, no he probado el código):

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)

(además de este código, gtk.main tendrá que ejecutarse en su propio hilo)

Esto ejecutará la función frob en hilo Gtk + y no bloqueará la rosca del reactor.

Esto iniciará Gtk + en un hilo separado:

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

Si necesita algunas interacciones de GUI complejas, tendrá que programar el uso estilo de continuación de paso

Editar: ejemplo agregado de ejecutar Gtk + en un hilo separado


0
2017-08-17 17:45



Aunque no se recomienda y no es compatible, con Twisted 10.x parece que el siguiente código permitirá seguir usando gtk.main () / dialog.run ()

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

0
2017-07-26 15:40