From guido at python.org Wed Nov 6 17:38:20 2013 From: guido at python.org (Guido van Rossum) Date: Wed, 6 Nov 2013 08:38:20 -0800 Subject: [Tkinter-discuss] Waking up the Tk event loop from a thread Message-ID: 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, )" solution. -- --Guido van Rossum (python.org/~guido) -------------- next part -------------- An HTML attachment was scrubbed... URL: From andreas.ostermann.11 at googlemail.com Wed Nov 6 18:17:01 2013 From: andreas.ostermann.11 at googlemail.com (Andreas Ostermann) Date: Wed, 6 Nov 2013 18:17:01 +0100 Subject: [Tkinter-discuss] Waking up the Tk event loop from a thread In-Reply-To: References: Message-ID: 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("", 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 : > 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, > )" solution. > > -- > --Guido van Rossum (python.org/~guido) > > _______________________________________________ > Tkinter-discuss mailing list > Tkinter-discuss at python.org > https://mail.python.org/mailman/listinfo/tkinter-discuss > From niccokunzmann at rambler.ru Wed Nov 6 18:31:49 2013 From: niccokunzmann at rambler.ru (Nicco Kunzmann) Date: Wed, 06 Nov 2013 18:31:49 +0100 Subject: [Tkinter-discuss] Waking up the Tk event loop from a thread In-Reply-To: References: Message-ID: <527A7D05.6020503@rambler.ru> 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("", 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 : >> 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, >> )" solution. >> >> -- >> --Guido van Rossum (python.org/~guido) >> >> _______________________________________________ >> Tkinter-discuss mailing list >> Tkinter-discuss at python.org >> https://mail.python.org/mailman/listinfo/tkinter-discuss >> > _______________________________________________ > Tkinter-discuss mailing list > Tkinter-discuss at python.org > https://mail.python.org/mailman/listinfo/tkinter-discuss -------------- next part -------------- An HTML attachment was scrubbed... URL: From jan.uebernickel at softdevel.de Wed Nov 6 19:06:44 2013 From: jan.uebernickel at softdevel.de (Jan Uebernickel) Date: Wed, 6 Nov 2013 19:06:44 +0100 Subject: [Tkinter-discuss] Waking up the Tk event loop from a thread In-Reply-To: <527A7D05.6020503@rambler.ru> References: <527A7D05.6020503@rambler.ru> Message-ID: Hi, afaik the after() / after_idle() calls are not thread safe! On my research leading to the code Andreas provide I found the event_generate() method the only threadsafe way to invoke the tk thread without letting it poll something. (As mentioned by Guido and seen on other pages everyone else seems to poll... why?) brgds, -- Jan 2013/11/6 Nicco Kunzmann : > 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("", 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 : > > 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, > )" solution. > > -- > --Guido van Rossum (python.org/~guido) > > _______________________________________________ > Tkinter-discuss mailing list > Tkinter-discuss at python.org > https://mail.python.org/mailman/listinfo/tkinter-discuss > > _______________________________________________ > Tkinter-discuss mailing list > Tkinter-discuss at python.org > https://mail.python.org/mailman/listinfo/tkinter-discuss > > > _______________________________________________ > Tkinter-discuss mailing list > Tkinter-discuss at python.org > https://mail.python.org/mailman/listinfo/tkinter-discuss > -- Jan ?bernickel Dipl.-Ing. (FH) Technische Informatik Dipl.-Inform. (FH) Softdevel GmbH - Don't limit your fantasies. M?ncheberg 10 24594 Hohenwestedt Tel.: +49-4871-9919-797 Fax: +49-4871-9919-798 Mobil: +49-176-6323-6068 jan.uebernickel at softdevel.de http://www.softdevel.de Registergericht / Court of Registry: Amtsgericht Kiel HRB Nr. / Commercial Register No.: 10801 KI Gesch?ftsf?hrer / Executive Director; Michael Hoppe From guido at python.org Wed Nov 6 23:02:45 2013 From: guido at python.org (Guido van Rossum) Date: Wed, 6 Nov 2013 14:02:45 -0800 Subject: [Tkinter-discuss] Waking up the Tk event loop from a thread In-Reply-To: <527A7D05.6020503@rambler.ru> References: <527A7D05.6020503@rambler.ru> Message-ID: On Wed, Nov 6, 2013 at 9:31 AM, Nicco Kunzmann wrote: > 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. > > Sadly, on Mac with Python 3 and Tcl/Tk 8.5 this exhibits exactly the problem I am complaining about -- the updates are erratic and often need to be helped along by moving the mouse or changing the focus. I'll look into Andreas' solution using event_generate(). -- --Guido van Rossum (python.org/~guido) -------------- next part -------------- An HTML attachment was scrubbed... URL: From kw at codebykevin.com Wed Nov 6 23:52:47 2013 From: kw at codebykevin.com (Kevin Walzer) Date: Wed, 06 Nov 2013 17:52:47 -0500 Subject: [Tkinter-discuss] Waking up the Tk event loop from a thread In-Reply-To: References: <527A7D05.6020503@rambler.ru> Message-ID: <527AC83F.9040904@codebykevin.com> Hello Guido, On 11/6/13, 5:02 PM, Guido van Rossum wrote: > Sadly, on Mac with Python 3 and Tcl/Tk 8.5 this exhibits exactly the > problem I am complaining about -- the updates are erratic and often need > to be helped along by moving the mouse or changing the focus. You may be seeing an issue that's specific to recent versions of Tk on the Mac. In 2009, Apple sponsored a port of Tk from the Carbon API to the Cocoa API because they had deprecated Carbon. The Cocoa port has many advantages, not the least of which is better integration with various system API's, but it has drawbacks as well. Specifically, the event loop integration between Tk and Cocoa is complicated and fragile, much more so than between Tk and Carbon (where the event loops were very similar and easy to integrate). This results in the kind of behavior you are seeing--sometimes there are issues with event loop updates, window redraw, etc., and sometimes event dispatching causes crashes. My predecessor as maintainer of Tk on the Mac, Daniel Steffen, is the author of the Cocoa port, but is now working for Apple and can't work on Tk anymore. The result is that many of the event loop bugs must remain unfixed--the code there is devilishly complex and, frankly, Daniel is the only one who really understands it. I'm not even persuaded that the impedance mismatch between the two event loops can be bridged, as Daniel already did quite a bit of bug fixing and refinement of the event loop stuff after the initial release of the Cocoa port. In most cases I'm left recommending various workarounds for the behavior, such as judicious use of update(), update_idlestasks(), after(), and so on. Exercising the event loop by moving the mouse also helps in some instances. Hope that sheds some light on what you are seeing. --Kevin -- Kevin Walzer Code by Kevin/Mobile Code by Kevin http://www.codebykevin.com http://www.wtmobilesoftware.com From nad at acm.org Thu Nov 7 00:50:33 2013 From: nad at acm.org (Ned Deily) Date: Wed, 06 Nov 2013 15:50:33 -0800 Subject: [Tkinter-discuss] Waking up the Tk event loop from a thread References: <527A7D05.6020503@rambler.ru> <527AC83F.9040904@codebykevin.com> Message-ID: In article <527AC83F.9040904 at codebykevin.com>, Kevin Walzer wrote: > On 11/6/13, 5:02 PM, Guido van Rossum wrote: > > Sadly, on Mac with Python 3 and Tcl/Tk 8.5 this exhibits exactly the > > problem I am complaining about -- the updates are erratic and often need > > to be helped along by moving the mouse or changing the focus. > > You may be seeing an issue that's specific to recent versions of Tk on > the Mac. > > In 2009, Apple sponsored a port of Tk from the Carbon API to the Cocoa > API because they had deprecated Carbon. Also, as a result of the deprecation, Apple changed its mind about providing 64-bit APIs for Carbon so the Cocoa port was needed to run native (non-X11) Tk at all in 64-bit mode, the default for executables starting with 10.6 (on capable processors). A sore point: Apple is not keeping up with the current releases of Tcl/Tk 8.5 since the Cocoa Tk was first shipped in 10.6 (and which had critical problems), thus missing out on serious bug fixes that Kevin and others have implemented and which has caused no end of headaches for Python users and for maintainers. I would advise to make sure _tkinter.so is linking with a current Tcl/Tk 8.5. For builds from source on OS X 10.6+, the easiest thing to do is install the latest 8.5 from ActiveState, which installs Tcl and Tk frameworks in /Library/Frameworks and which are searched before the Apple-supplied frameworks in /System/Library/Frameworks. The most recent version as of now is 8.5.15.1 which includes the latest fix for 10.9 Mavericks. $ otool -L build/lib.macosx-10.8-x86_64-3.4-pydebug/_tkinter.so build/lib.macosx-10.8-x86_64-3.4-pydebug/_tkinter.so: /Library/Frameworks/Tcl.framework/Versions/8.5/Tcl (compatibility version 8.5.0, current version 8.5.15) /Library/Frameworks/Tk.framework/Versions/8.5/Tk (compatibility version 8.5.0, current version 8.5.15) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 169.3.0) If you are building with a macosx SDK, you will probably have to create directories and symlinks to the real /Library/Frameworks in the SDK root. -- Ned Deily, nad at acm.org From guido at python.org Thu Nov 7 01:39:16 2013 From: guido at python.org (Guido van Rossum) Date: Wed, 6 Nov 2013 16:39:16 -0800 Subject: [Tkinter-discuss] Waking up the Tk event loop from a thread In-Reply-To: <527AC83F.9040904@codebykevin.com> References: <527A7D05.6020503@rambler.ru> <527AC83F.9040904@codebykevin.com> Message-ID: Hi Kevin, Thanks for the clarification. Fortunately using event_generate() with a virtual event works like a charm (unlike after() or after_idle()) so I am happy with the way things are now. It has been quite frustrating though to get here. :-( --Guido On Wed, Nov 6, 2013 at 2:52 PM, Kevin Walzer wrote: > Hello Guido, > > > On 11/6/13, 5:02 PM, Guido van Rossum wrote: > >> Sadly, on Mac with Python 3 and Tcl/Tk 8.5 this exhibits exactly the >> problem I am complaining about -- the updates are erratic and often need >> to be helped along by moving the mouse or changing the focus. >> > > You may be seeing an issue that's specific to recent versions of Tk on the > Mac. > > In 2009, Apple sponsored a port of Tk from the Carbon API to the Cocoa API > because they had deprecated Carbon. The Cocoa port has many advantages, not > the least of which is better integration with various system API's, but it > has drawbacks as well. Specifically, the event loop integration between Tk > and Cocoa is complicated and fragile, much more so than between Tk and > Carbon (where the event loops were very similar and easy to integrate). > > This results in the kind of behavior you are seeing--sometimes there are > issues with event loop updates, window redraw, etc., and sometimes event > dispatching causes crashes. > > My predecessor as maintainer of Tk on the Mac, Daniel Steffen, is the > author of the Cocoa port, but is now working for Apple and can't work on Tk > anymore. The result is that many of the event loop bugs must remain > unfixed--the code there is devilishly complex and, frankly, Daniel is the > only one who really understands it. I'm not even persuaded that the > impedance mismatch between the two event loops can be bridged, as Daniel > already did quite a bit of bug fixing and refinement of the event loop > stuff after the initial release of the Cocoa port. > > In most cases I'm left recommending various workarounds for the behavior, > such as judicious use of update(), update_idlestasks(), after(), and so > on. Exercising the event loop by moving the mouse also helps in some > instances. > > Hope that sheds some light on what you are seeing. > > --Kevin > > -- > Kevin Walzer > Code by Kevin/Mobile Code by Kevin > http://www.codebykevin.com > http://www.wtmobilesoftware.com > > _______________________________________________ > Tkinter-discuss mailing list > Tkinter-discuss at python.org > https://mail.python.org/mailman/listinfo/tkinter-discuss > -- --Guido van Rossum (python.org/~guido) -------------- next part -------------- An HTML attachment was scrubbed... URL: From jan.uebernickel at softdevel.de Thu Nov 7 09:33:44 2013 From: jan.uebernickel at softdevel.de (Jan Uebernickel) Date: Thu, 7 Nov 2013 09:33:44 +0100 Subject: [Tkinter-discuss] Waking up the Tk event loop from a thread In-Reply-To: References: <527A7D05.6020503@rambler.ru> <527AC83F.9040904@codebykevin.com> Message-ID: Hi, glad I could help and thx for python ;-) brgds, Jan 2013/11/7 Guido van Rossum : > [..] > Fortunately using event_generate() with a virtual event works like a charm > (unlike after() or after_idle()) so I am happy with the way things are now. > It has been quite frustrating though to get here. :-( > [..] From bha100710 at gmail.com Mon Nov 11 16:44:51 2013 From: bha100710 at gmail.com (Bhaskar Chaudhary) Date: Mon, 11 Nov 2013 21:14:51 +0530 Subject: [Tkinter-discuss] New Book on Tkinter Message-ID: Dear Friends I am very pleased to announce the publication of my new book on Tkinter titled: "Tkinter GUI Application Development Hotshot" The book has been published by Packt Publishing and is aimed at readers who want to learn GUI programming with Tkinter. The book should complement the existing documentation on Tkinter as it discusses Tkinter by developing several real world projects like Notepad, Programmable Drum Machine, Chess, Audio Player, Drawing Application, Screen Saver, Weather forecaster and several other fun projects. More details and a sample chapter from the book can be found at: http://www.packtpub.com/tkinter-gui-application-development-hotshot/book Packt Publishing has also gracefully agreed to provide limited number of complementary copies for reviews by experienced Tkinter programmers. Please write back to me if you would like to review the book. thanks & regards Bhaskar Chaudhary From carlos.zun at gmail.com Wed Nov 13 13:54:22 2013 From: carlos.zun at gmail.com (Carlos Zuniga) Date: Wed, 13 Nov 2013 07:54:22 -0500 Subject: [Tkinter-discuss] Force Frame to resize after removing all widgets. Message-ID: Hi, I have a problem with frames. If I remove a widget, it resizes to fit the remaining widgets, but if I remove all of them then the frame doesn't resize at all (in this case, the window is not resizable by the user). Here's a small example showing this: import Tkinter import ttk root = Tkinter.Tk() root.resizable(False, False) frame = ttk.Frame(root) frame.grid() #sep = ttk.Separator(frame) #sep.grid() labels = [] for i in range(5): l = ttk.Label(frame, text='label %d' % i) labels.append(l) add_all(labels) buttons = ttk.Frame(root) buttons.grid() def remove_all(labels): for i in labels: i.grid_forget() def add_all(labels): for i in labels: i.grid() b1 = ttk.Button(buttons, text='Remove All', command=lambda:remove_all(labels)) b2 = ttk.Button(buttons, text='Remove 4', command=lambda:remove_all(labels[:4])) b3 = ttk.Button(buttons, text='Add All', command=lambda:add_all(labels)) b1.grid() b2.grid() b3.grid() root.mainloop() I have a workaround by adding a Separator to the frame (uncommenting the #sep lines above), then it will always have one widget and resize every time. But I wonder if there a way to force it to resize instead? Thanks -- Carlos Z. From kw at codebykevin.com Thu Nov 14 17:03:20 2013 From: kw at codebykevin.com (Kevin Walzer) Date: Thu, 14 Nov 2013 11:03:20 -0500 Subject: [Tkinter-discuss] Get value instead of PY_VAR Message-ID: <5284F448.7050308@codebykevin.com> I'm going around in circles trying to serialize some app configuration data in my Tkinter app via pickle, and I'd appreciate some help. I set various app configurations via checkboxes and StringVar(). Sometimes the value of one of the checkboxes is lost and when I try to print the value, I get PY_VAR#2 instead. Here's a relevant snippet setting the values: self.copytext= StringVar() self.copytext.set(self.defaults['copytext']) self.whoischeck = Tile.Checkbutton(self.whoisframe, variable=self.whois, text='Display Raw Whois Output') And here's the snippet that retrieves the value: self.defaults['copytext'] = self.copytext Using this call, printing the value of self.defaults['copytext'] results in PY_VAR#2, which doesn't actually retain the value of the var I'm setting (1 or 0). It also results in a error when I try to serialize the data, because pickle can't handle this type of data: Can't pickle 'tkapp' object: I can actually get the correct value if I change one call as follows: self.defaults['copytext'] = self.copytext.get() But then, trying to pickle this value, I get this error: 'str' object has no attribute 'get' What's the right way in Tkinter to get the value of a StringVar() from a checkbutton, and then serialize the value via pickle? Sometimes I can get this to work, but I don't understand the underlying dynamics of it; the intersection of Python objects and Tcl variables gives me headaches. I am especially unclear why I get PY_VAR rather than the value in some instances, but not others. Thanks, Kevin -- Kevin Walzer Code by Kevin/Mobile Code by Kevin http://www.codebykevin.com http://www.wtmobilesoftware.com From michael.odonnell at uam.es Thu Nov 14 17:38:32 2013 From: michael.odonnell at uam.es (Michael O'Donnell) Date: Thu, 14 Nov 2013 17:38:32 +0100 Subject: [Tkinter-discuss] Get value instead of PY_VAR In-Reply-To: <5284F448.7050308@codebykevin.com> References: <5284F448.7050308@codebykevin.com> Message-ID: Hi Kevin, Try instead: self.defaults['copytext'] = self.copytext.get() the get() method returns the value of the StringVar. Mick On 14 November 2013 17:03, Kevin Walzer wrote: > I'm going around in circles trying to serialize some app configuration data > in my Tkinter app via pickle, and I'd appreciate some help. > > I set various app configurations via checkboxes and StringVar(). Sometimes > the value of one of the checkboxes is lost and when I try to print the > value, I get PY_VAR#2 instead. Here's a relevant snippet setting the values: > > self.copytext= StringVar() > self.copytext.set(self.defaults['copytext']) > > self.whoischeck = Tile.Checkbutton(self.whoisframe, > variable=self.whois, text='Display Raw Whois Output') > > And here's the snippet that retrieves the value: > > self.defaults['copytext'] = self.copytext > > Using this call, printing the value of self.defaults['copytext'] results in > PY_VAR#2, which doesn't actually retain the value of the var I'm setting (1 > or 0). It also results in a error when I try to serialize the data, because > pickle can't handle this type of data: > > Can't pickle 'tkapp' object: > > I can actually get the correct value if I change one call as follows: > > self.defaults['copytext'] = self.copytext.get() > > But then, trying to pickle this value, I get this error: > > 'str' object has no attribute 'get' > > What's the right way in Tkinter to get the value of a StringVar() from a > checkbutton, and then serialize the value via pickle? > > Sometimes I can get this to work, but I don't understand the underlying > dynamics of it; the intersection of Python objects and Tcl variables gives > me headaches. I am especially unclear why I get PY_VAR rather than the value > in some instances, but not others. > > Thanks, > Kevin > > -- > Kevin Walzer > Code by Kevin/Mobile Code by Kevin > http://www.codebykevin.com > http://www.wtmobilesoftware.com > _______________________________________________ > Tkinter-discuss mailing list > Tkinter-discuss at python.org > https://mail.python.org/mailman/listinfo/tkinter-discuss From carlos.zun at gmail.com Fri Nov 15 19:33:45 2013 From: carlos.zun at gmail.com (Carlos Zuniga) Date: Fri, 15 Nov 2013 13:33:45 -0500 Subject: [Tkinter-discuss] Get value instead of PY_VAR In-Reply-To: <5284F448.7050308@codebykevin.com> References: <5284F448.7050308@codebykevin.com> Message-ID: Hi, On Thu, Nov 14, 2013 at 11:03 AM, Kevin Walzer wrote: > > I can actually get the correct value if I change one call as follows: > > self.defaults['copytext'] = self.copytext.get() > > But then, trying to pickle this value, I get this error: > > 'str' object has no attribute 'get' > It looks like at some other place in your code you do something like self.copytext = 'foo' Instead of self.copytext.set('foo') Try checking for that. -- Carlos From rowen at uw.edu Sat Nov 16 00:29:26 2013 From: rowen at uw.edu (Russell E. Owen) Date: Fri, 15 Nov 2013 15:29:26 -0800 Subject: [Tkinter-discuss] Get value instead of PY_VAR References: <5284F448.7050308@codebykevin.com> Message-ID: In article <5284F448.7050308 at codebykevin.com>, Kevin Walzer wrote: > I'm going around in circles trying to serialize some app configuration > data in my Tkinter app via pickle, and I'd appreciate some help. > > I set various app configurations via checkboxes and StringVar(). > Sometimes the value of one of the checkboxes is lost and when I try to > print the value, I get PY_VAR#2 instead. Here's a relevant snippet > setting the values: > > self.copytext= StringVar() > self.copytext.set(self.defaults['copytext']) > > self.whoischeck = Tile.Checkbutton(self.whoisframe, > variable=self.whois, text='Display Raw Whois Output') > > And here's the snippet that retrieves the value: > > self.defaults['copytext'] = self.copytext > > Using this call, printing the value of self.defaults['copytext'] results > in PY_VAR#2, which doesn't actually retain the value of the var I'm > setting (1 or 0). It also results in a error when I try to serialize the > data, because pickle can't handle this type of data: > > Can't pickle 'tkapp' object: > > I can actually get the correct value if I change one call as follows: > > self.defaults['copytext'] = self.copytext.get() > > But then, trying to pickle this value, I get this error: > > 'str' object has no attribute 'get' > > What's the right way in Tkinter to get the value of a StringVar() from a > checkbutton, and then serialize the value via pickle? > > Sometimes I can get this to work, but I don't understand the underlying > dynamics of it; the intersection of Python objects and Tcl variables > gives me headaches. I am especially unclear why I get PY_VAR rather than > the value in some instances, but not others. StringVar and its kin are variable containers. You can pass them around as you like, but you have to be careful to always change the contained value using set and retrieve the contained value using get. (You can also assign callbacks to monitor changes, which is great.). However, apparently you can't pickle a StringVar and you want to pickle the defaults dict, so it sounds like the best thing is to save the value, not the container in the defaults dict. self.defaults["copytext"] = self.copytext.get() You then say you cannot even pickle the dict even if you do that. That suggests the value returned by get is not a python string, but some kind of wrapper class that acts like a string. If so, all you have to do is cast it: self.defaults["copytext"] = unicode(self.copytext.get()) -- Russell P.S. you might consider json instead of pickle because the file format is easy to read and edit, which can be a feature for preference files. From davnobezimeni at gmail.com Sat Nov 23 09:09:21 2013 From: davnobezimeni at gmail.com (Davnobezimeni Sasha Balagura) Date: Sat, 23 Nov 2013 10:09:21 +0200 Subject: [Tkinter-discuss] Using tkinter::ttk::treeview to implement data view Message-ID: Hello. I have to implement parcer on Python 3.3 for .txt file with special formating. There are a lot of data to display, modify, add. I decided to use tkinter::ttk:treeviev for this. And it really good one, very fast and good looking..but.. But I cant set borders between cells, like in Excel. Is it real at all for this widget? Or i've just overlooked something? Best regards, Alex -------------- next part -------------- An HTML attachment was scrubbed... URL: From josefg at gmail.com Thu Nov 28 07:10:08 2013 From: josefg at gmail.com (Josef Gabriel) Date: Wed, 27 Nov 2013 22:10:08 -0800 Subject: [Tkinter-discuss] tkinter: screenmmwidth Message-ID: I am not very familiar with the tkinter source code, please bear with me. I am using Windows7 Sp1 and python 2.7 on a laptop with no attached display. The screen specifications are 280 mm. The screen physically measures 275 mm. The screen EDID states 280 mm as well. winfo_screenmmwidth reports 361 mm. Where does tkinter extract the winfo_screenmmwidth information? I assume this is through the pywin32 api, but I don't know. I seem to have run into a dead end because I cannot view the _tkinter module. I assume the _tkinter module uses the pywin32 api, but I'm not sure where it's getting the information. I plan on using this data to calculate the pixels per centimeter (ppcm) for a gui I am writing. I have written a function to extract the extended display identification data (EDID) information from the windows registry. Is there anything wroing with this method? Thank you in advance. -------------- next part -------------- An HTML attachment was scrubbed... URL: From klappnase at web.de Thu Nov 28 19:00:05 2013 From: klappnase at web.de (Michael Lange) Date: Thu, 28 Nov 2013 19:00:05 +0100 Subject: [Tkinter-discuss] tkinter: screenmmwidth In-Reply-To: References: Message-ID: <20131128190005.1b0b7b293d418b3ddbfb53d3@web.de> Hi, On Wed, 27 Nov 2013 22:10:08 -0800 Josef Gabriel wrote: > I am not very familiar with the tkinter source code, please bear with > me. I am using Windows7 Sp1 and python 2.7 on a laptop with no attached > display. The screen specifications are 280 mm. The screen physically > measures 275 mm. The screen EDID states 280 mm as well. > winfo_screenmmwidth reports 361 mm. > > > Where does tkinter extract the winfo_screenmmwidth information? I assume > this is through the pywin32 api, but I don't know. I seem to have run > into a dead end because I cannot view the _tkinter module. I assume the > _tkinter module uses the pywin32 api, but I'm not sure where it's > getting the information. I plan on using this data to calculate the > pixels per centimeter (ppcm) for a gui I am writing. > > I have written a function to extract the extended display identification > data (EDID) information from the windows registry. Is there anything > wroing with this method? > > Thank you in advance. the screenmmwidth value in fact does not have anything particular to do with Python, but is instead the return value of the corresponding Tk function (I'm not sure how this value is calculated by Tk though). I can only guess, but if you get an incorrect value it might have something to do with a dpi value that is not correctly recognised for some reason (possibly because of bad edid data from your monitor, at least that's what I had here). You can check which dpi value Tk assumes with: dpi = root.winfo_fpixels('1i') (or dpi = root.winfo_pixels('1i') if you prefer an integer value). If you replace '1i' with '1m' you should get a mm-value. So with root.winfo_screenwidth() / root.winfo_fpixels('1m') you should be able to calculate the screenmmwidth without having to rely on the Tk routine (again, not sure if Tk doesn't do just the same ;). One thing that might be intersting for you, you can even adjust the dpi value your Tk window uses if you call: root.tk.call('tk', 'scaling', '-displayof', '.', desired_dpi/72.0) _before_ creating any widgets. I have been using this in an app where I let the users pick the value for desired_dpi from a spinbox so they can take care that a distance that is supposed to be e.g. 10 cm actually has the requested cm-size on the screen. I am not sure if taking values from the registry is a good idea, probably it depends on what you want to achieve; to me it sounds a little as if a way that works on your own system might fail on another, but in fact I don't know much about registry values ;) Best regards Michael .-.. .. ...- . .-.. --- -. --. .- -. -.. .--. .-. --- ... .--. . .-. Peace was the way. -- Kirk, "The City on the Edge of Forever", stardate unknown