[Tkinter-discuss] implementation change to simplify Tkinter event loop and threading issues

Mark Roseman mark at markroseman.com
Wed Jun 6 14:56:02 EDT 2018


I’m not sure if this is the right place to discuss internal implementation details, so apologies in advance if not…

Ivan and I have been going back and forth regarding some of the subtleties of how Tkinter and Tcl/Tk interact, which can come up particularly with regards to threads, but also in other ways. This is in the context of a reference doc update he’s proposed. It’s gotten me to pop my head into the code (_tkinter.c) several times. 

It’s a classic case of “who is in charge” when you try to merge two different event models with two different sets of constraints on them. To simplify, the Tkinter mainloop code is more or less in charge, calling out to Tcl to process one event (or return back if no events show up quickly enough). In the times when it’s not in the middle of a call to Tcl to process an event, it can also call into Tcl directly to run a command (e.g. turn the canvas item pink). Adding threads to the mix is another layer but still amounts to calling into Tcl in the right way at the right time. Tkinter needs to do a lot of clever things with locks, timeouts, etc. to make some of the magic happen.

It strikes me there’s another way to do this that might both simplify the Tkinter code and make it more robust, even in some of the weird and wacky ways that people are using it (despite being warned it’s not a good idea!).

Tcl’s event handling mechanism was updated in Tcl 7.5 (1996) at the time it was being moved from Unix-only to Mac and Windows. It explicitly provides mechanisms to deal with integrating with other event loops, and situations where either Tcl’s event loop is in charge (but can handle generated events from external code) or vice versa. The documentation can be found at http://www.tcl.tk/man/tcl8.6/TclLib/Notifier.htm

Tkinter can potentially make use of these facilities. Rather than the juggling act that Tkinter’s mainloop does now, it can just run Tcl’s event loop. Tkinter would also register itself with Tcl as an event source. This is a way that it can provide new “events” to the Tcl event loop. In this case, the “events” are really Tkinter calls that we want to send on to Tcl/Tk. Registering as an event source essentially means that Tcl will repeatedly call into Tkinter to ask “got anything new for me?”. if no Tkinter commands are pending, then the answer is no. If there is a Python command pending, an “event” can be added to the Tcl event queue. The event handler for that event would then call back into Tkinter, get the Tcl command, execute it, and pass the result back to Tkinter. 

The best part of this, at least as far as stability goes, is that Tcl controls the timing of the calls into Tkinter, so that it will only make them when it’s “safe” to do so, from a thread it expects to do so from, etc. Given that Tcl has the stricter restrictions on what can be called when and from where, this seems like a win. On the Tkinter side, it never has to worry about if it’s safe to call into Tcl.

(The one exception to this is probably application startup, i.e. before we hit mainloop, where Tkinter would just call directly into Tcl. It may be a reasonable restriction to say that you have to make Tkinter calls from the originating thread if they’re made before you hit mainloop).

To me this appears like it should be feasible. Any thoughts?

Mark





More information about the Tkinter-discuss mailing list