Author mdehoon
Recipients
Date 2005-08-04.21:51:48
SpamBayes Score
Marked as misclassified
Message-id
In-reply-to
Content
This patch is a simplification of Tkinter's event loop
by moving the event loop to Python itself.

Tkinter needs an event loop to be able to handle both
Tcl/Tk events and keyboard input by the user. Usually,
an event loop looks like this:

while true:
    wait for an event (Tcl/Tk events or keyboard input)
    handle the event

Tkinter's loop is set up differently. If Tkinter is
imported, it sets the PyOS_InputHook pointer to the
EventHook function in _tkinter.c. The EventHook
function handles Tcl/Tk events only. In essence,
Tkinter's event loop looks like this:

while true:
    handle all Tcl/Tk events that need to be handled
    if keyboard input is waiting: break
    sleep for 20 milliseconds

Note that the event loop exits only if the user presses
a key.
This causes the following problems:
1) This event loop is available only for Tkinter. There
is no way to handle events other than the Tcl/Tk events.
2) If another extension module needs events to be
handled, it can do so by using a similar event loop.
However, it is not possible to handle both Tcl/Tk
events and other events, (except for keyboard input).
3) Chaining the two event loops will fail. As soon as
Tkinter's event loop starts running, it will break out
of this loop only in the case of keyboard input. So any
other events will not be processed until a key is pressed.
4) Even if Tkinter's event loop is the only event loop
that is needed, it can fail. If a Python thread is
waiting for a mutex variable to change, it cannot run
Tkinter's event loop while waiting, since Tkinter's
event loop will only return after a key is pressed (it
doesn't pay attention to the mutex variable). This is
the reason that running Tkinter in an IDLE shell will fail.
5) On some platforms (e.g. Cygwin), the event loop is
unable to check for keyboard input. Hence, one needs to
call mainloop() in order to see Tkinter's widgets.

The reason that the event loop was set up in this
peculiar way  may be due to a bug with PyOS_InputHook
in Python. If Python is compiled with readline support,
PyOS_InputHook will be called ten times per second,
while Python is waiting for keyboard input. (As shown
below, this allows us to fix this event loop problem.)
However, if Python is compiled without readline support
(notably on Windows), PyOS_InputHook is called only
once (just before a user starts entering a new command
on the keyboard). This necessitates Tkinter to run an
event loop, and not return from it until keyboard input
is ready.

If PyOS_InputHook is called ten times per second, we
can set up the event loop as follows:

Set PyOS_InputHook to Tkinter's EventHook

while true:
    call PyOS_InputHook
    handle keyboard input, if present
    sleep for 100 milliseconds

Here, the key point is that Tkinter's EventHook returns
immediately if there are no more Tcl/Tk events to be
handled. Effectively, we have moved the event loop from
Tkinter to Python itself.

Hence, there are two problems to be solved:
1) PyOS_InputHook should be called ten times per
second, regardless of whether Python is compiled with
or without readline support.
2) Tkinter's EventHook should return immediately after
handling all Tcl/Tk events.

Problem 1) is solved in patch #1049855; problem 2) is
solved in this patch. This patch is a considerable
simplication of _tkinter.c, as it removes all lines
that are no longer needed (and adds almost no code).

This patch changes the nature of the function to which
PyOS_InputHook points. Before, PyOS_InputHook is used
to start a Tcl/Tk event loop. Now, PyOS_InputHook is
called from an existing event loop to handle Tcl/Tk events.

With these two patches, Python has a functioning
PyOS_InputHook, which can be used both by Tkinter and
by other extension modules. We can handle Tcl/Tk events
by calling Tkinter's EventHook function if we're
waiting for something else other than keyboard input,
like a mutex variable. We can chain event-handling
functions from different extension modules (e.g.
Tkinter and some other module), by first calling
Tkinter's EventHook and then the other extension
module's event hook function.
Finally, on Cygwin, we can now create Tkinter widgets
without having to call mainloop. So this will now work
on Cygwin:

>>> from Tkinter import *
>>> Label()
>>> # label appears, without having to call mainloop.

This patch has been tested on WIndows and Cygwin.
History
Date User Action Args
2007-08-23 15:43:38adminlinkissue1252236 messages
2007-08-23 15:43:38admincreate