Skip to content

Tkinter on Windows cannot use pipe for stdin #140146

@mdehoon

Description

@mdehoon

Bug report

Bug description:

When using tkinter in an interactive Python session, the EventHook function in Modules/_tkinter.c runs the Tcl/Tk event loop, and exits once data become available on stdin (i.e., when the user types in the next Python command).

The EventHook function uses the Tcl_CreateFileHandler function to monitor for activity on stdin.
As was already noticed in the original commit (of 23 May 1998; 7bf1564), this does not work on Windows, as Tcl_CreateFileHandler Unix-only (https://www.tcl-lang.org/man/tcl8.6/TclLib/CrtFileHdlr.htm).

Instead, for Windows the _kbhit function of Microsoft Windows (https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/kbhit?view=msvc-170) is used to check if a key has been pressed (commit of 13 June 1998; ad4db17).

As _kbhit checks for a physical key stroke on a console, it does not work if stdin is redirected to a pipe.

The script below shows an example. On Unix and Mac, running this script as python script.py causes 'hello' to be printed immediately. On Windows, the script starts running but does not print anything to screen and does not exit until the user presses some key on the keyboard.

Though most users won't need to use a pipe for stdin, being able to pipe to stdin is important as it will let you test tkinter interactive usage from a script (for example as part of the cpython test suite).

It's fairly straightforward to fix this bug by replacing Tcl_CreateFileHandler by Tcl_CreateChannelHandler, and using Tcl_GetStdChannel(TCL_STDIN) to get a channel for stdin.

import sys
import subprocess
proc = subprocess.Popen([sys.executable, "-i"],
                         stdin=subprocess.PIPE,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE)
_ = proc.stdin.write(b"import tkinter\n")
_ = proc.stdin.write(b"interpreter = tkinter.Tcl()\n")
_ = proc.stdin.write(b"print('hello')\n")
_ = proc.stdin.write(b"quit()\n")
stdout, stderr = proc.communicate()
stdout = stdout.decode()
stderr = stderr.decode()
print(stdout)

CPython versions tested on:

CPython main branch

Operating systems tested on:

Windows, macOS, Linux

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions