2011-12-30 43 views
35

Todos los tutoriales de tkinter que he visto dicen que se debe llamar al tkinter.mainloop para que se dibujen las ventanas y se procesen los eventos, y siempre llaman a esta función, incluso en los programas de hello world. Sin embargo, cuando pruebo esto en el shell interactivo, las ventanas se dibujan correctamente sin tener que llamar a mainloop. This example de incrustación de gráficos matplotlib en tkinter produce una aplicación relativamente compleja, con botones para panoramizar, ampliar y redimensionar un gráfico dentro de una ventana tkinter, y de nuevo, todo esto funciona si elimina la llamada a mainloop y ejecuta el código en el intérprete interactivo. Por supuesto, si ejecuto el script (con mainloop eliminado) fuera del shell interactivo, el programa finaliza demasiado rápido para ver qué sucede, pero si agrego una llamada al input para mantener el programa abierto todo funciona correctamente (estoy ejecutando python 3.2.2 en Linux).¿Cuándo debo llamar a mainloop en una aplicación Tkinter?

¿Qué hace exactamente mainloop y cuándo es necesario llamarlo?

EDIT: para aclarar, si abro la terminal de GNOME y el tipo

$python3 
>>> import tkinter 
>>> root = tkinter.Tk() 

aparece inmediatamente una ventana sin tener que llamar mainloop, y la funcionalidad tkinter más complejo parece funcionar tan bien (por ejemplo, agregando botones a la ventana). En IDLE, se necesita una llamada a mainloop. Tenía entendido que no se debería extraer nada, y no se deberían procesar eventos hasta que se llame a mainloop.

Respuesta

38

La respuesta a su pregunta principal es, debe llamar a mainloop una vez y solo una vez, cuando esté listo para que se ejecute su aplicación.

mainloop no es mucho más que un bucle infinito que se ve más o menos como esto (esos no son los nombres reales de los métodos, los nombres sirven meramente para ilustrar el punto):

while True: 
    event=wait_for_event() 
    event.process() 
    if main_window_has_been_destroyed(): 
     break 

En este contexto , "evento" hace referencia tanto a las interacciones del usuario (clics del mouse, pulsaciones de teclas, etc.) como a las solicitudes del kit de herramientas o al administrador del sistema operativo/ventanas para dibujar o volver a dibujar un widget. Si ese bucle no se está ejecutando, los eventos no se procesan. Si los eventos no se procesan, nada aparecerá en la pantalla y tu programa probablemente salga a menos que tengas tu propio bucle infinito en ejecución.

Entonces, ¿por qué no necesita llamar esto de forma interactiva? Eso es solo una conveniencia, porque de lo contrario sería imposible ingresar cualquier comando una vez que llame al mainloop desde mainloop hasta que se destruya la ventana principal.

7

Compare un programa con una GUI interactiva con un programa que calcule el centésimo número de Fibonacci. Todo el último programa tiene que pasar por una serie de pasos en orden, de arriba abajo. El conjunto de pasos y su secuencia se pueden conocer de antemano, y se mantendrá constante sin importar cuántas veces ejecutes el programa.

Pero el programa GUI es diferente: en un momento dado, tiene que ser capaz de manejar todo tipo de diferentes tipos de eventos e interacciones. Este requisito a menudo se implementa utilizando una construcción de programación llamada un ciclo de eventos. Un bucle de evento es la estructura de control central de un programa. Espera a que ocurra un evento y luego envía el controlador apropiado.

No mencionaste qué shell interactivo estás usando, pero supongo que es INACTIVO. IDLE en sí mismo es un programa Tkinter, y ya tiene un bucle de eventos. Así que posiblemente el código de Tkinter que estás escribiendo en el shell esté vinculado al bucle de eventos de IDLE.

+0

Lo siento, debería haber mencionado: Estoy utilizando el shell pitón estándar (no de reposo) en la terminal de GNOME (que está aparentemente escrito en C). Por lo que puedo ver, nada aparte de mi propio código debería estar haciendo algo que afecte a tkinter. – James

+1

Acabo de probar lo mismo en IDLE, y no obtengo el mismo comportamiento: no aparece ninguna ventana hasta que llamo a mainloop. – James

-3

He decidido que, en lugar de hacer una llamada directamente a mainloop en cualquier lugar de mi script, simplemente lo agregaré como parte de atexit, es decir, cuando el intérprete de Python decida que es hora de comenzar a cerrar, es va a entrar en el pasillo principal de Tk. Esto entonces le impide terminar el cierre secuencia hasta que el usuario realmente le dice a Tk para dejar de fumar (es decir, con el comando-Q en un Mac, o haciendo clic en la X roja en Windows.)

from Tkinter import Tk 
root = Tk() 

import atexit 
atexit.register(root.mainloop) 

Parece que hay no es necesario llamar al mainloop desde una línea de comando del sistema. El intérprete de Python continuará ejecutándose sin él, porque está esperando su entrada (hasta que ejecute exit()).

+1

_ "Me parece que lo único que hace mainloop es evitar que su aplicación se cierre automáticamente" _: es una evaluación incorrecta. Gestiona la cola de eventos, que es mucho más que evitar que la aplicación salga. Además, ni siquiera estás haciendo lo que crees que estás haciendo: en realidad estás llamando a 'mainloop()' inmediatamente en lugar de a la hora de salida. –

+1

@BryanOakley - Vaya, arreglé esa llamada accidental a 'mainloop()' - ahora solo está registrada, como debería ser. De todos modos, creo que eres incorrecto. En su respuesta, usted dice que el hecho de que no necesita llamar a mainloop en un interactivo interactivo es "solo una conveniencia". ¿Cómo? Si es algo conveniente, entonces algo, ya sea el intérprete interactivo o el módulo 'Tkinter', debe contener un código especial que lo permita. No creo que ese código exista. No hay documentación que diga que sí, tampoco. La carga está en ti para demostrar que Mainloop es mágico como dices. – ArtOfWarfare

+0

Nunca escuché de atexit y estoy contento de haber encontrado esta respuesta, ya que me permite llamar a una aplicación 'Tk()' durante la prueba unitaria sin tener que cerrar manualmente la aplicación. –

-3

como sigue:

from tkinter import * 

tk = Tk() 
canvas = Canvas(tk, width=500, height=500) 
canvas.pack() 
canvas.create_line(0, 0, 500, 500) 

mainloop() 
Cuestiones relacionadas