Tkinter entendiendo mainloop

midkin:

Hasta ahora, solía terminar mis programas de Tkiter con:, ¡ tk.mainloop()o no aparecería nada! Ver ejemplo:

from Tkinter import *
import random
import time

tk = Tk()
tk.title = "Game"
tk.resizable(0,0)
tk.wm_attributes("-topmost", 1)

canvas = Canvas(tk, width=500, height=400, bd=0, highlightthickness=0)
canvas.pack()

class Ball:
    def __init__(self, canvas, color):
        self.canvas = canvas
        self.id = canvas.create_oval(10, 10, 25, 25, fill=color)
        self.canvas.move(self.id, 245, 100)
    def draw(self):
        pass

ball = Ball(canvas, "red")

tk.mainloop()

Sin embargo, cuando probé el siguiente paso en este programa (hacer que la bola se mueva por tiempo), el libro que estoy leyendo dice que haga lo siguiente. Cambie la función de dibujo a:

def draw(self):
    self.canvas.move(self.id, 0, -1)

y agregue el siguiente código a mi programa:

while 1:
    ball.draw()
    tk.update_idletasks()
    tk.update()
    time.sleep(0.01)

Pero noté que agregar este bloque de código, hacía tk.mainloop()inútil el uso , ya que todo aparecería incluso sin él.

En este momento debo mencionar que mi libro nunca habla tk.mainloop()(tal vez porque usa Python 3) pero lo aprendí buscando en la web ya que mis programas no funcionaban copiando el código del libro.

¡Así que intenté hacer lo siguiente que no funcionó!

while 1:
    ball.draw()
    tk.mainloop()
    time.sleep(0.01)

¿Que esta pasando? Lo que hace tk.mainloop()? ¿Qué hace tk.update_idletasks()y tk.update()hace y en qué se diferencia tk.mainloop()? ¿Debo usar el bucle anterior? tk.mainloop()? o ambos en mis programas?

7stud:

tk.mainloop() bloques . Lo que eso significa es que la ejecución de su programa Python se detiene allí. Puedes ver eso escribiendo:

while 1:
    ball.draw()
    tk.mainloop()
    print "hello"   #NEW CODE
    time.sleep(0.01)

Nunca verá la salida de la declaración de impresión. Como no hay bucle, la bola no se mueve.

Por otro lado, los métodos update_idletasks()y update()aquí:

while True:
    ball.draw()
    tk.update_idletasks()
    tk.update()

...no bloquees; la ejecución continúa después de que terminan esos métodos, por lo que el ciclo while se ejecuta una y otra vez, lo que hace que la bola se mueva.

Un bucle infinito que contiene las llamadas al método update_idletasks()y update()puede actuar como sustituto de la llamada tk.mainloop(). Tenga en cuenta que se puede decir que todo el ciclo while se bloquea como tk.mainloop()porque no se ejecutará nada después del ciclo while.

Sin embargo, tk.mainloop()no sustituye solo a las líneas:

tk.update_idletasks()
tk.update()

Más bien, tk.mainloop()es un sustituto del ciclo while completo:

while True:
    tk.update_idletasks()
    tk.update()

Respuesta al comentario:

Esto es lo que dicen los documentos de tcl :

actualizar idletasks

Este subcomando de actualización elimina todos los eventos inactivos actualmente programados de la cola de eventos de Tcl. Los eventos inactivos se utilizan para posponer el procesamiento hasta que “no haya nada más que hacer”, y el caso de uso típico para ellos es el rediseño de Tk y los nuevos cálculos de geometría. Al posponerlos hasta que Tk esté inactivo, las costosas operaciones de redibujado no se realizan hasta que todo, desde un grupo de eventos (por ejemplo, liberación de botón, cambio de ventana actual, etc.) se procesa a nivel de script. Esto hace que Tk parezca mucho más rápido, pero si está en medio de un procesamiento de larga duración, también puede significar que no se procesan eventos inactivos durante mucho tiempo. Al llamar a update idletasks, los redibujados debido a cambios internos de estado se procesan inmediatamente. (Los redibujos debido a eventos del sistema, por ejemplo, si el usuario los desiconifica, necesitan una actualización completa para ser procesados).

APN Como se describe en Actualización considerada dañina, el uso de la actualización para manejar los redibujos no manejados por los idletasks de actualización tiene muchos problemas. Joe English en una publicación de comp.lang.tcl describe una alternativa:

Entonces, update_idletasks()hace que se procese algún subconjunto de eventos que update()causa que se procesen.

De los documentos de actualización :

actualizar? idletasks?

El comando de actualización se utiliza para "actualizar" la aplicación al ingresar al bucle de eventos Tcl repetidamente hasta que se hayan procesado todos los eventos pendientes (incluidas las devoluciones de llamada inactivas).

Si la palabra clave idletasks se especifica como un argumento para el comando, no se procesan nuevos eventos o errores; solo se invocan devoluciones de llamada inactivas. Esto hace que las operaciones que normalmente se aplazan, como las actualizaciones de visualización y los cálculos de diseño de ventanas, se realicen inmediatamente.

KBK (12 de febrero de 2000) - Mi opinión personal es que el comando [actualizar] no es una de las mejores prácticas, y un programador debe evitarlo. Rara vez, si es que alguna vez, he visto un uso de [actualización] que no podría programarse de manera más efectiva por otros medios, generalmente el uso apropiado de devoluciones de llamada de eventos. Por cierto, esta precaución se aplica a todos los comandos Tcl (vwait y tkwait son los otros culpables comunes) que ingresan al bucle de eventos de forma recursiva, con la excepción de usar un solo [vwait] a nivel global para iniciar el bucle de eventos dentro de un shell. que no lo inicia automáticamente.

Los propósitos más comunes para los que he visto [actualización] recomendada son: 1) Mantener viva la GUI mientras se ejecuta algún cálculo de larga duración. Vea el programa Countdown para una alternativa. 2) Esperar a que se configure una ventana antes de hacer cosas como la administración de geometría en ella. La alternativa es vincular eventos como los que notifican el proceso de la geometría de una ventana. Consulte Centrado de una ventana para ver una alternativa.

¿Qué pasa con la actualización? Hay varias respuestas. Primero, tiende a complicar el código de la GUI circundante. Si trabaja los ejercicios en el programa Countdown, tendrá una idea de lo fácil que puede ser cuando cada evento se procesa en su propia devolución de llamada. En segundo lugar, es una fuente de errores insidiosos. El problema general es que la ejecución de [actualización] tiene efectos secundarios casi ilimitados; al regresar de [actualización], un script puede descubrir fácilmente que la alfombra se ha quitado debajo de él. Hay más discusión sobre este fenómeno en Update considerado dañino.

.....

¿Existe alguna posibilidad de que pueda hacer que mi programa funcione sin el ciclo while?

Sí, pero las cosas se ponen un poco complicadas. Podría pensar que algo como lo siguiente funcionaría:

class Ball:
    def __init__(self, canvas, color):
        self.canvas = canvas
        self.id = canvas.create_oval(10, 10, 25, 25, fill=color)
        self.canvas.move(self.id, 245, 100)

    def draw(self):
        while True:
           self.canvas.move(self.id, 0, -1)

ball = Ball(canvas, "red")
ball.draw()
tk.mainloop()

El problema es que ball.draw () hará que la ejecución entre en un bucle infinito en el método draw (), por lo que tk.mainloop () nunca se ejecutará y sus widgets nunca se mostrarán. En la programación de interfaz gráfica de usuario, los bucles infinitos deben evitarse a toda costa para que los widgets respondan a la entrada del usuario, por ejemplo, los clics del mouse.

Entonces, la pregunta es: ¿cómo se ejecuta algo una y otra vez sin crear realmente un bucle infinito? Tkinter tiene una respuesta para ese problema: el after()método de un widget :

from Tkinter import *
import random
import time

tk = Tk()
tk.title = "Game"
tk.resizable(0,0)
tk.wm_attributes("-topmost", 1)

canvas = Canvas(tk, width=500, height=400, bd=0, highlightthickness=0)
canvas.pack()

class Ball:
    def __init__(self, canvas, color):
        self.canvas = canvas
        self.id = canvas.create_oval(10, 10, 25, 25, fill=color)
        self.canvas.move(self.id, 245, 100)

    def draw(self):
        self.canvas.move(self.id, 0, -1)
        self.canvas.after(1, self.draw)  #(time_delay, method_to_execute)




ball = Ball(canvas, "red")
ball.draw()  #Changed per Bryan Oakley's comment
tk.mainloop()

El método after () no se bloquea (en realidad crea otro hilo de ejecución), por lo que la ejecución continúa en su programa de Python después de que se llama a after (), lo que significa que tk.mainloop () se ejecuta a continuación, por lo que sus widgets se configuran y desplegado. El método after () también permite que sus widgets sigan respondiendo a la entrada de otros usuarios. Intente ejecutar el siguiente programa y luego haga clic con el mouse en diferentes puntos del lienzo:

from Tkinter import *
import random
import time

root = Tk()
root.title = "Game"
root.resizable(0,0)
root.wm_attributes("-topmost", 1)

canvas = Canvas(root, width=500, height=400, bd=0, highlightthickness=0)
canvas.pack()

class Ball:
    def __init__(self, canvas, color):
        self.canvas = canvas
        self.id = canvas.create_oval(10, 10, 25, 25, fill=color)
        self.canvas.move(self.id, 245, 100)

        self.canvas.bind("<Button-1>", self.canvas_onclick)
        self.text_id = self.canvas.create_text(300, 200, anchor='se')
        self.canvas.itemconfig(self.text_id, text='hello')

    def canvas_onclick(self, event):
        self.canvas.itemconfig(
            self.text_id, 
            text="You clicked at ({}, {})".format(event.x, event.y)
        )

    def draw(self):
        self.canvas.move(self.id, 0, -1)
        self.canvas.after(50, self.draw)




ball = Ball(canvas, "red")
ball.draw()  #Changed per Bryan Oakley's comment.
root.mainloop()

Este artículo se recopila de Internet, indique la fuente cuando se vuelva a imprimir.

En caso de infracción, por favor [email protected] Eliminar

Editado en
0

Déjame decir algunas palabras

0Comentarios
Iniciar sesiónRevisión de participación posterior

Artículos relacionados

TOP Lista

CalienteEtiquetas

Archivo