Hi, some time ago we were very unhappy about the same issue.
Coming from Java I tried to port the "invokeLater" idea of swing to python which leads me to the following code (including dynamic Proxy objects for easier communication between the stuff in the background and the tk world) [the Background thread in this example is doing some polling, I later used an own implementation for a Queue which let me interrupt the get... but as your problem is the tk world: there is no busy waiting in this example] import Tkinter import Queue import uuid import threading import thread # ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### class BackgroundListener (threading.Thread) : def __init__(self): threading.Thread.__init__(self) self.queue = Queue.Queue() self.cancel = threading.Event() def invokeLater(self, delegate): self.queue.put(delegate) def interrupt(self) : self.cancel.set() def run(self): while not self.cancel.isSet() : try : delegate = self.queue.get(timeout=0.5) if not self.cancel.isSet() : # don't call if already finished ! delegate() except Queue.Empty : pass class TkListener : def __init__(self, tk) : self.queue = Queue.Queue() self.tk = tk self.event = "<<%s>>" % uuid.uuid1() tk.bind(self.event, self.invoke) def invokeLater(self, delegate) : self.queue.put(delegate) self.tk.event_generate(self.event, when='tail') def invoke(self, event) : try : while True : delegate = self.queue.get(block=False) delegate() except Queue.Empty : pass # ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### class Delegate : def __init__(self, real, name, args, kwargs) : self.real = real self.name = name self.args = args self.kwargs = kwargs def __call__(self) : method = getattr(self.real, self.name) apply(method, self.args, self.kwargs) class Delegator : def __init__(self, listener, real, name) : self.listener = listener self.real = real self.name = name def __call__(self, *args, **kwargs) : delegate = Delegate(self.real, self.name, args, kwargs) self.listener.invokeLater(delegate) class Proxy : def __init__(self, listener, real) : self.listener = listener self.real = real self.cache = {} def __getattr__(self, name) : try : delegator = self.cache[name] except KeyError : delegator = Delegator(self.listener, self.real, name) self.cache[name] = delegator return delegator # ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### class MyTkinterObject: def __init__(self, tk): frame = Tkinter.Frame(tk) self.quitButton = Tkinter.Button(frame, text="QUIT", fg="red", command=frame.quit) self.quitButton.pack(side=Tkinter.LEFT) self.helloButton = Tkinter.Button(frame, text="Hello") self.helloButton.pack(side=Tkinter.LEFT) self.hello2Button = Tkinter.Button(frame, text="Hello2", command=self.trigger2) self.hello2Button.pack(side=Tkinter.LEFT) frame.pack() def register(self, myBackgroundObject) : self.helloButton.bind("<Button-1>", myBackgroundObject.trigger) def trigger(self, callback) : print "%s - Front hello" % thread.get_ident() callback() def trigger2(self) : print "%s - Front hello" % thread.get_ident() class MyBackgroundObject : def __init__(self, listener): self.proxy = Proxy(listener, self) def register(self, myTkinterObject) : self.myTkinterObject = myTkinterObject def trigger(self, event) : print "%s - Back hello - %s" % (thread.get_ident(), event) self.myTkinterObject.trigger(self.proxy.callback) def callback(self) : print "%s - Back bye" % thread.get_ident() # ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### def main() : root = Tkinter.Tk() tkListener = TkListener(root) backgroundListener = BackgroundListener() myTkinterObject = MyTkinterObject(root) myBackgroundObject = MyBackgroundObject(backgroundListener) myTkinterObject.register(myBackgroundObject.proxy) myBackgroundObject.register(Proxy(tkListener, myTkinterObject)) backgroundListener.start() root.mainloop() backgroundListener.interrupt() # ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### if __name__ == "__main__": main() brgds, -- Jan Übernickel (via Andreas) 2013/11/6 Guido van Rossum <gu...@python.org>: > After many years I'm trying my hands at Tkinter again, and things have > changed... :-) > > I'm using Python 2.7 here, but it doesn't look like 3.x changes the issue. > Tcl/Tk version is 8.5, on Mac OS (10.8). > > I've got a situation where a background thread is doing I/O and wants to > wake up the Tk event loop. It seems the established wisdom (e.g. > http://code.activestate.com/recipes/82965-threads-tkinter-and-asynchronous-io/) > is to have the Tk event loop wake up every e.g. 100 msec and check a queue > or other state shared with the thread. This works, but makes me worry that > my app will be draining battery power even when no data from the bg thread > is forthcoming. > > I've tried other things: > > - createfilehandler: This says it cannot work when Tcl/Tk is threaded and it > will be removed in Python 3 anyway. > > - The bg thread can use after_idle() or after() to run a command on the Tk > object; this works, but it doesn't seem to wake up the Tk main loop -- the > command usually only runs when I cause a UI event to be generated (e.g. > selecting the window). > > What other solutions could I try? I really don't like the "after(100, > <repeat>)" solution. > > -- > --Guido van Rossum (python.org/~guido) > > _______________________________________________ > Tkinter-discuss mailing list > Tkinter-discuss@python.org > https://mail.python.org/mailman/listinfo/tkinter-discuss > _______________________________________________ Tkinter-discuss mailing list Tkinter-discuss@python.org https://mail.python.org/mailman/listinfo/tkinter-discuss