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 <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

_______________________________________________
Tkinter-discuss mailing list
Tkinter-discuss@python.org
https://mail.python.org/mailman/listinfo/tkinter-discuss

Reply via email to