es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Cómo cerrar correctamente una ventana de tkinter con hilos en segundo plano actualizando la interfaz gráfica.

Tengo una aplicación Tkinter que consiste en una ventana única y varios frames, cada frame es una clase. Algunos frames tienen hilos que cambian los componentes de la interfaz gráfica (por ejemplo, entry) en el contexto de recibir un mensaje desde un socket.

root = tk.Tk()
gui = MainFrame(root)   # esta clase contiene los frames
root.protocol("WM_DELETE_WINDOW", functools.partial(gui.close, root))
...
root.mainloop()

El problema es el siguiente: cuando el usuario presiona el botón “X”, se llama a la siguiente función close:

def close(self):
    self.frame1.close() # cambia la condición del bucle del hilo a False y llama a join()
    self.frame2.close() # ""
    ...
    root.destroy()

Los hilos internos se ejecutan en un bucle while (con condición) intentando actualizar los componentes de la interfaz gráfica y después de llamar a close(), se quedan atascados en una sola línea de código sin lanzar una excepción, por ejemplo: self.entry.configure(state='disabled'). Luego, la llamada a join() nunca termina.

De esta manera, el código se queda atascado y la interfaz gráfica no se cierra. Puedo llamar a root.destroy() al principio de la función y así la interfaz gráfica se cerrará, pero el código aún se está ejecutando (atascado).

¿Cómo puedo evitar este bloqueo o forzar la finalización de mis hilos?

Gracias.

Tags:  , , , ,

Answer

  1. Avatar for davy.ai

    Una solución es usar el objeto threading.Event para indicar a los hilos que dejen de ejecutarse. En cada hilo, puedes verificar periódicamente si se ha establecido el evento y salir del hilo si es así.

    Aquí tienes un ejemplo de implementación:

    1. Crea un objeto evento en la clase MainFrame:
      def __init__(self, master):
         ...
         self._stop_event = threading.Event()
      
    2. Pasa el objeto evento a cada marco que tenga un hilo:
      self.frame1 = Frame1(self, stop_event=self._stop_event)
      
    3. En el hilo, verifica periódicamente si se ha establecido el evento y sal si es así:
      def run(self):
         while not self._stop_event.is_set():
             ...
      
         # Aquí va el código de limpieza
      
    4. En el método close, establece el evento y espera a que todos los hilos salgan utilizando join:

      “`
      def close(self):
      self._stop_event.set() # Establece el evento para indicar a los hilos que dejen de ejecutarse

      # Espera a que todos los hilos salgan
      self.frame1.join()
      self.frame2.join()

      root.destroy() # Finalmente, destruye la ventana principal

    Con esta implementación, los hilos deberían salir de forma limpia cuando el usuario cierre la ventana.

Comments are closed.