python python-3.x multithreading tkinter tcl

python - Hilos y tkinter



python-3.x multithreading (2)

La solución aquí es simple, pero difícil de descubrir:

Llame a mainWindow.quit() inmediatamente después de mainwindow.mainloop() , para que la limpieza se mainwindow.mainloop() en el mismo subproceso que creó la interfaz de usuario tk, en lugar de en el subproceso principal cuando sale Python.

He oído que los hilos en Python no son fáciles de manejar y se enredan más con tkinter.

Tengo el siguiente problema. Tengo dos clases, una para la GUI y otra para un proceso infinito. Primero, comienzo la clase GUI y luego la clase de proceso infinito. Quiero que cuando cierre la GUI, también finalice el proceso infinito y finalice el programa.

Una versión simplificada del código es la siguiente:

import time, threading from tkinter import * from tkinter import messagebox finish = False class tkinterGUI(threading.Thread): def __init__(self): threading.Thread.__init__(self) def run(self): global finish #Main Window self.mainWindow = Tk() self.mainWindow.geometry("200x200") self.mainWindow.title("My GUI Title") #Label lbCommand = Label(self.mainWindow, text="Hello world", font=("Courier New", 16)).place(x=20, y=20) #Start self.mainWindow.mainloop() #When the GUI is closed we set finish to "True" finish = True class InfiniteProcess(threading.Thread): def __init__(self): threading.Thread.__init__(self) def run(self): global finish while not finish: print("Infinite Loop") time.sleep(3) GUI = tkinterGUI() GUI.start() Process = InfiniteProcess() Process.start()

Cuando hago clic en el botón Cerrar (en la esquina superior derecha) aparece el siguiente error en la consola:

Tcl_AsyncDelete: async handler deleted by the wrong thread

No sé por qué sucede o qué significa.


Todos los comandos Tcl deben originarse desde el mismo hilo . Debido a la dependencia de tkinter de Tcl, generalmente es necesario hacer que todas las tkinter gui se originen en el mismo hilo. El problema se produce porque mainWindow se instancia en el subproceso tkinterGui , pero, debido a que mainWindow es un atributo de tkinterGui , no se destruye hasta que tkinterGui se destruye en el subproceso principal.

El problema se puede evitar al no hacer mainWindow un atributo de tkinterGui , es decir, cambiar self.mainWindow a mainWindow . Esto permite que mainWindow se destruya cuando el método de run finalice en el hilo tkinterGui . Sin embargo, a menudo puede evitar los hilos por completo utilizando mainWindow.after llama en su lugar:

import time, threading from tkinter import * from tkinter import messagebox def infinite_process(): print("Infinite Loop") mainWindow.after(3000, infinite_process) mainWindow = Tk() mainWindow.geometry("200x200") mainWindow.title("My GUI Title") lbCommand = Label(mainWindow, text="Hello world", font=("Courier New", 16)).place(x=20, y=20) mainWindow.after(3000, infinite_process) mainWindow.mainloop()

Si desea definir la GUI dentro de una clase, aún puede hacerlo:

import time, threading from tkinter import * from tkinter import messagebox class App(object): def __init__(self, master): master.geometry("200x200") master.title("My GUI Title") lbCommand = Label(master, text="Hello world", font=("Courier New", 16)).place(x=20, y=20) def tkinterGui(): global finish mainWindow = Tk() app = App(mainWindow) mainWindow.mainloop() #When the GUI is closed we set finish to "True" finish = True def InfiniteProcess(): while not finish: print("Infinite Loop") time.sleep(3) finish = False GUI = threading.Thread(target=tkinterGui) GUI.start() Process = threading.Thread(target=InfiniteProcess) Process.start() GUI.join() Process.join()

o incluso más simple, simplemente use el hilo principal para ejecutar el bucle principal de la GUI:

import time, threading from tkinter import * from tkinter import messagebox class App(object): def __init__(self, master): master.geometry("200x200") master.title("My GUI Title") lbCommand = Label(master, text="Hello world", font=("Courier New", 16)).place(x=20, y=20) def InfiniteProcess(): while not finish: print("Infinite Loop") time.sleep(3) finish = False Process = threading.Thread(target=InfiniteProcess) Process.start() mainWindow = Tk() app = App(mainWindow) mainWindow.mainloop() #When the GUI is closed we set finish to "True" finish = True Process.join()