Cómo ejecutar una función en el fondo de tkinter

Soy nuevo en la progtwigción GUI y quiero escribir un progtwig Python con tkinter. Todo lo que quiero que haga es ejecutar una función simple en segundo plano que se puede influenciar a través de la GUI.

La función cuenta desde 0 hasta el infinito hasta que se presiona un botón. Al menos eso es lo que quiero que haga. Pero no tengo idea de cómo puedo ejecutar esta función en segundo plano, porque mainloop () de tkinter tiene control todo el tiempo. Y si inicio la función en un bucle sin fin, mainloop () no se puede ejecutar y la GUI está muerta.

Me gustaría devolver el control al mainloop () después de cada ciclo, pero ¿cómo puedo recuperar el control del mainloop () a la función runapp sin un evento activado por el usuario?

Aquí hay un código de ejemplo que mata la GUI:

from Tkinter import * class App: def __init__(self, master): frame = Frame(master) frame.pack() self.button = Button(frame, text="START", command=self.runapp) self.button.pack(side=LEFT) self.hi_there = Button(frame, text="RESTART", command=self.restart) self.hi_there.pack(side=LEFT) self.runapp() def restart(self): print "Now we are restarting..." def runapp(self): counter = 0 while (1): counter =+ 1 time.sleep(0.1) 

Encontrará la respuesta en esta otra pregunta que Tkinter bloquea python cuando Icon está cargado y tk.mainloop en un hilo .

En pocas palabras, necesita tener dos hilos, uno para tkinter y otro para la tarea de fondo.

La progtwigción basada en eventos es conceptualmente simple. Solo imagine que al final de su archivo de progtwig hay un simple bucle infinito:

 while :   

Por lo tanto, todo lo que necesita hacer para ejecutar una pequeña tarea continuamente es dividirla en pedazos pequeños y colocarlos en la cola de eventos. Cada vez que pase por el bucle, la siguiente iteración de su cálculo se realizará automáticamente.

Puede colocar objetos en la cola de eventos con el método after . Entonces, cree un método que incremente el número, luego se reprograme para ejecutarse unos milisegundos más tarde. Se vería algo así como:

 def add_one(self): self.counter += 1 self.after(1000, self.add_one) 

Lo anterior actualizará el contador una vez por segundo. Cuando su progtwig se inicializa, lo llama una vez y, a partir de ese momento, se vuelve a llamar una y otra vez, etc.

Este método solo funciona si puede dividir su gran problema (en su caso, “contar para siempre”) en pequeños pasos (“agregar uno”). Si está haciendo algo como una consulta de base de datos lenta o un gran cálculo, esta técnica no funcionará necesariamente.

No tengo suficiente reputación para comentar la respuesta de Bryan Oakley (que en mi progtwig me pareció muy efectiva), por lo que agregaré mi experiencia aquí. Descubrí que, dependiendo de cuánto self.after ejecución de la función de fondo y de la precisión con la que desee que se establezca el intervalo de tiempo, puede ser mejor ponerse ” self.after llamada al comienzo de la función recurrente. En el ejemplo de Bryan, eso parecería

 def add_one(self): self.after(1000, self.add_one) self.counter += 1 

Si lo hace de esta manera, se garantiza que el intervalo de tiempo se respete exactamente, lo que anulará cualquier desviación de intervalo que pueda ocurrir si su función lleva mucho tiempo.

Intente comprender este ejemplo: la actualización del reloj en backgroud y la actualización de la GUI (no se necesitan 2 subprocesos).

 # use Tkinter to show a digital clock # tested with Python24 vegaseat 10sep2006 from Tkinter import * import time root = Tk() time1 = '' clock = Label(root, font=('times', 20, 'bold'), bg='green') clock.pack(fill=BOTH, expand=1) def tick(): global time1 # get the current local time from the PC time2 = time.strftime('%H:%M:%S') # if time string has changed, update it if time2 != time1: time1 = time2 clock.config(text=time2) # calls itself every 200 milliseconds # to update the time display as needed # could use >200 ms, but display gets jerky clock.after(200, tick) tick() root.mainloop( ) 

créditos: enlace al sitio