2009-07-29 12 views
5

Este es el caso de la prueba ...Tkinter bloquea Python cuando un icono es cargado y tk.mainloop está en un hilo

import Tkinter as tk 
import thread 
from time import sleep 

if __name__ == '__main__': 
    t = tk.Tk() 
    thread.start_new_thread(t.mainloop,()) 
    # t.iconbitmap('icon.ico') 

    b = tk.Button(text='test', command=exit) 
    b.grid(row=0) 

    while 1: 
     sleep(1) 

Este código funciona. Descomenta la línea t.iconbitmap y se bloquea. Vuelva a organizarlo de la forma que desee; se bloqueará

¿Cómo evito que tk.mainloop bloquee el GIL cuando hay un icono presente?

El objetivo es win32 y Python 2.6.2.

Respuesta

16

Creo que no debe ejecutar el bucle principal en un hilo diferente. AFAIK, el bucle principal debería ejecutarse en el mismo hilo que creó el widget.

Los kits de herramientas de GUI con los que estoy familiarizado (Tkinter, .NET Windows Forms) son de esa manera: Puede manipular la GUI desde un único subproceso.

En Linux, el código lanza una excepción:

 
self.tk.mainloop(n) 
RuntimeError: Calling Tcl from different appartment 

una de las siguientes funcionará (no hay temas adicionales):

if __name__ == '__main__': 
    t = tk.Tk() 
    t.iconbitmap('icon.ico') 

    b = tk.Button(text='test', command=exit) 
    b.grid(row=0) 

    t.mainloop() 

Con rosca adicional:

def threadmain(): 
    t = tk.Tk() 
    t.iconbitmap('icon.ico') 
    b = tk.Button(text='test', command=exit) 
    b.grid(row=0) 
    t.mainloop() 


if __name__ == '__main__': 
    thread.start_new_thread(threadmain,()) 

    while 1: 
     sleep(1) 

Si necesita comunicarse con tkinter desde fuera del hilo de tkinter, sugiero que configure un temporizador que verifique la cola de trabajo.

Aquí se muestra un ejemplo:

import Tkinter as tk 
import thread 
from time import sleep 
import Queue 

request_queue = Queue.Queue() 
result_queue = Queue.Queue() 

def submit_to_tkinter(callable, *args, **kwargs): 
    request_queue.put((callable, args, kwargs)) 
    return result_queue.get() 

t = None 
def threadmain(): 
    global t 

    def timertick(): 
     try: 
      callable, args, kwargs = request_queue.get_nowait() 
     except Queue.Empty: 
      pass 
     else: 
      print "something in queue" 
      retval = callable(*args, **kwargs) 
      result_queue.put(retval) 

     t.after(500, timertick) 

    t = tk.Tk() 
    t.configure(width=640, height=480) 
    b = tk.Button(text='test', name='button', command=exit) 
    b.place(x=0, y=0) 
    timertick() 
    t.mainloop() 

def foo(): 
    t.title("Hello world") 

def bar(button_text): 
    t.children["button"].configure(text=button_text) 

def get_button_text(): 
    return t.children["button"]["text"] 

if __name__ == '__main__': 
    thread.start_new_thread(threadmain,()) 

    trigger = 0 
    while 1: 
     trigger += 1 

     if trigger == 3: 
      submit_to_tkinter(foo) 

     if trigger == 5: 
      submit_to_tkinter(bar, "changed") 

     if trigger == 7: 
      print submit_to_tkinter(get_button_text) 

     sleep(1) 
+2

Bueno usted ha golpeado el clavo en la cabeza, funciona ... pero ha sufrido de no proporcionar suficiente información. Mi razonamiento es que quiero poder hacer cosas para tkinter donde el ciclo while es ... Siendo un poco nuevo en SO, ¿debería aceptar su respuesta y hacer otra pregunta más detallada? – burito

+2

Hola, he actualizado mi respuesta con una sugerencia y un ejemplo de código para eso. El ciclo while ahora llama a unos pocos métodos en el subproceso tkinter, utilizando colas de solicitud/respuesta. – codeape

+2

BTW, para el código de producción, sugiero que encapsule la ventana, el hilo y las colas de Tkinter en una clase. Esto para evitar los globales que tenemos ahora: request_queue, response_queue y t. También necesita algún tipo de manejo de errores alrededor de los invocables (* args, ** kwargs). – codeape

Cuestiones relacionadas