¿Cómo ejecuta su propio código junto con el bucle de eventos de Tkinter?

Resuelto Allan S asked hace 15 años • 5 respuestas

Mi hermano pequeño recién se está iniciando en la programación y, para su proyecto de feria de ciencias, está haciendo una simulación de una bandada de pájaros en el cielo. Ha escrito la mayor parte de su código y funciona muy bien, pero los pájaros necesitan moverse en todo momento .

Tkinter, sin embargo, acapara el tiempo de su propio bucle de eventos, por lo que su código no se ejecuta. Se root.mainloop()ejecuta, se ejecuta y sigue ejecutándose, y lo único que ejecuta son los controladores de eventos.

¿Hay alguna manera de ejecutar su código junto con el bucle principal (sin subprocesos múltiples, es confuso y debe mantenerse simple) y, de ser así, cuál es?

En este momento, se le ocurrió un truco feo, vinculando su move()función a <b1-motion>, de modo que mientras mantenga presionado el botón y mueva el mouse, funcione. Pero tiene que haber una manera mejor.

Allan S avatar Jan 20 '09 03:01 Allan S
Aceptado

Utilice el aftermétodo en el Tkobjeto:

from tkinter import *

root = Tk()

def task():
    print("hello")
    root.after(2000, task)  # reschedule event in 2 seconds

root.after(2000, task)
root.mainloop()

Aquí está la declaración y la documentación del aftermétodo:

def after(self, ms, func=None, *args):
    """Call function once after given time.

    MS specifies the time in milliseconds. FUNC gives the
    function which shall be called. Additional parameters
    are given as parameters to the function call.  Return
    identifier to cancel scheduling with after_cancel."""
Dave Ray avatar Jan 19 '2009 20:01 Dave Ray

La solución publicada por Bjorn genera un mensaje "RuntimeError: llamando a Tcl desde otro apartamento" en mi computadora (RedHat Enterprise 5, Python 2.6.1). Es posible que Bjorn no haya recibido este mensaje ya que, según un lugar que revisé , el mal manejo de los subprocesos con Tkinter es impredecible y depende de la plataforma.

El problema parece ser que app.start()cuenta como una referencia a Tk, ya que la aplicación contiene elementos Tk. Solucioné esto reemplazándolo app.start()con un self.start()interior __init__. También lo hice para que todas las referencias de Tk estén dentro de la función que llamamainloop() o dentro de funciones que son llamadas por la función que llama mainloop()(esto aparentemente es crítico para evitar el error de "apartamento diferente").

Finalmente, agregué un controlador de protocolo con una devolución de llamada, ya que sin esto el programa sale con un error cuando el usuario cierra la ventana Tk.

El código revisado es el siguiente:

# Run tkinter code in another thread

import tkinter as tk
import threading

class App(threading.Thread):

    def __init__(self):
        threading.Thread.__init__(self)
        self.start()

    def callback(self):
        self.root.quit()

    def run(self):
        self.root = tk.Tk()
        self.root.protocol("WM_DELETE_WINDOW", self.callback)

        label = tk.Label(self.root, text="Hello World")
        label.pack()

        self.root.mainloop()


app = App()
print('Now we can continue running code while mainloop runs!')

for i in range(100000):
    print(i)
Kevin avatar Dec 02 '2009 18:12 Kevin