Hello Guido van Rossum,
when your source code looks like this:
from tkinter import Tk, Label
from threading import Thread
import time
def thread():
for i in range(10):
time.sleep(1)
t.after(0, lambda: l.configure(text = str(i)))
t = Tk()
l = Label(t)
l.pack()
th = Thread(target = thread)
th.start()
t.mainloop()
then it works under Windows.
Thank you for Python,
Nicco Kunzmann
Am 06.11.2013 18:17, schrieb Andreas Ostermann:
> 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 <[email protected]>:
>> 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
>> [email protected]
>> https://mail.python.org/mailman/listinfo/tkinter-discuss
>>
> _______________________________________________
> Tkinter-discuss mailing list
> [email protected]
> https://mail.python.org/mailman/listinfo/tkinter-discuss
_______________________________________________
Tkinter-discuss mailing list
[email protected]
https://mail.python.org/mailman/listinfo/tkinter-discuss