Posterior a consultar especialistas en el tema, programadores de deferentes ramas y maestros hemos dado con la respuesta al dilema y la compartimos en este post.
Solución:
No puede iniciar un while True:
bucle en el mismo subproceso en el que está operando el bucle de eventos de Tkinter. Si lo hace, bloqueará el bucle de Tkinter y hará que el programa se congele.
Para una solución simple, podrías usar Tk.after
para ejecutar un proceso en segundo plano cada segundo más o menos. A continuación se muestra un script para demostrar:
from Tkinter import *
running = True # Global flag
def scanning():
if running: # Only do this if the Stop button has not been clicked
print "hello"
# After 1 second, call scanning again (create a recursive loop)
root.after(1000, scanning)
def start():
"""Enable scanning by setting the global flag to True."""
global running
running = True
def stop():
"""Stop scanning by setting the global flag to False."""
global running
running = False
root = Tk()
root.title("Title")
root.geometry("500x500")
app = Frame(root)
app.grid()
start = Button(app, text="Start Scan", command=start)
stop = Button(app, text="Stop", command=stop)
start.grid()
stop.grid()
root.after(1000, scanning) # After 1 second, call scanning
root.mainloop()
Por supuesto, es posible que desee refactorizar este código en una clase y tener running
frijol attribute de eso Además, si su programa se vuelve algo complejo, sería beneficioso buscar en Python. threading
módulo para que su scanning
La función se puede ejecutar en un subproceso separado.
Aquí hay una solución diferente, con las siguientes ventajas:
-
No requiere la creación manual de subprocesos separados
-
No se usa
Tk.after
llamadas En su lugar, se conserva el estilo original del código con un bucle continuo. La principal ventaja de esto es que no tiene que especificar manualmente una cantidad de milisegundos que determina la frecuencia con la que se ejecuta su código dentro del ciclo, simplemente se ejecuta con la frecuencia que su hardware lo permita.
Nota: Solo he probado esto con pitón 3no con python 2. Supongo que lo mismo debería funcionar también en python 2, simplemente no estoy seguro al 100%.
Para el código de la interfaz de usuario y la lógica de inicio/detención, usaré principalmente el mismo código que en la respuesta de iCodez. Una diferencia importante es que asumo que siempre tendremos un ciclo en ejecución, pero dentro de ese ciclo decidiremos qué hacer en función de los botones que se hayan presionado recientemente:
from tkinter import *
running = True # Global flag
idx = 0 # loop index
def start():
"""Enable scanning by setting the global flag to True."""
global running
running = True
def stop():
"""Stop scanning by setting the global flag to False."""
global running
running = False
root = Tk()
root.title("Title")
root.geometry("500x500")
app = Frame(root)
app.grid()
start = Button(app, text="Start Scan", command=start)
stop = Button(app, text="Stop", command=stop)
start.grid()
stop.grid()
while True:
if idx % 500 == 0:
root.update()
if running:
print("hello")
idx += 1
En este código, no llamamos root.mainloop()
para tener la GUI de tkinter continuamente actualizándose. En su lugar, lo actualizamos manualmente cada cierto tiempo (en este caso, cada 500 iteraciones de bucle).
Teóricamente, esto significa que es posible que no detengamos instantáneamente el bucle tan pronto como presionemos el botón Detener. Por ejemplo, si en el momento exacto en que presionamos el botón Detener, estamos en la iteración 501, este código continuará en bucle hasta que se alcance la iteración 1000. Por lo tanto, la desventaja de este código es que, en teoría, tenemos una GUI ligeramente menos receptiva (pero no se notará si el código dentro de su bucle es rápido). A cambio, obtenemos el código dentro del ciclo para que se ejecute casi tan rápido como sea posible (solo con sobrecarga a veces de una GUI update()
call), y haz que se ejecute dentro del hilo principal.
Reseñas y valoraciones
Eres capaz de animar nuestro estudio poniendo un comentario o puntuándolo te damos la bienvenida.