LoginSignup
34

More than 5 years have passed since last update.

Pythonでキー入力を検出する(Enter無しで)

Last updated at Posted at 2016-09-07

[追記]
tty版

input()とか、sys.stdin.read()とかを普通に使うと、

  1. 文字を入力する

  2. エンターを押す

の2ステップが必要となる。

エンターを押さなくても、キーを入力した瞬間に何かして欲しい。
と思って調べてみたんだけどなかなか見つからなかったのでメモ.

macとubuntuでは動作確認できてます。
windowsでは動きませんが、msvcrtモジュールがあると同じことが簡単にできるようです。

import sys
import termios

#標準入力のファイルディスクリプタを取得
fd = sys.stdin.fileno()

#fdの端末属性をゲットする
#oldとnewには同じものが入る。
#newに変更を加えて、適応する
#oldは、後で元に戻すため
old = termios.tcgetattr(fd)
new = termios.tcgetattr(fd)

#new[3]はlflags
#ICANON(カノニカルモードのフラグ)を外す
new[3] &= ~termios.ICANON
#ECHO(入力された文字を表示するか否かのフラグ)を外す
new[3] &= ~termios.ECHO


try:
    # 書き換えたnewをfdに適応する
    termios.tcsetattr(fd, termios.TCSANOW, new)
    # キーボードから入力を受ける。
    # lfalgsが書き換えられているので、エンターを押さなくても次に進む。echoもしない
    ch = sys.stdin.read(1)

finally:
    # fdの属性を元に戻す
    # 具体的にはICANONとECHOが元に戻る
    termios.tcsetattr(fd, termios.TCSANOW, old)

print(ch)

ファイルディスクリプタ

そもそも、入出力はファイルディスクリプタという識別子を用いて管理されている。
http://qiita.com/toshihirock/items/78286fccf07dbe6df38f

ファイルディスクリプタをOSと各プログラム間でやりとりすることで、標準の入出力なのか、あるいはエラー出力なのか、はたまたファイルからの入出力なのかってのをお互いに判断する。

termios

ファイルディスクリプタ使って実際にOSとデータのやりとりをする窓口になってるのが、termiosってやつ。
termiosは、プログラムとOS間で、どうやってデータをやりとりするかって情報を持ってる。
https://linuxjm.osdn.jp/html/LDP_man-pages/man3/termios.3.html

その情報の中に、エンターを押すまで入力を待つか、キーが押されたらすぐに入力とするかを決めるフラグ(カノニカルモード,ICANON)もある。
それをいじればキー入力の検出ができるって寸法。

ちなみに、上の例では入力した文字を表示するかどうかを決めるフラグ(ECHO)もいじってる。

参考

termiosについて。カノニカルモードとかも詳しい。
https://linuxjm.osdn.jp/html/LDP_man-pages/man3/termios.3.html

pythonにおけるtermios
http://docs.python.jp/2/library/termios.html#module-termios

ttyというtermiosより高レベルで似たようなことができる。というかこっちのほうが簡単。Unixでしか使えない。
http://docs.python.jp/2/library/tty.html#module-tty

ttyを利用していい感じのモジュールにしてる
http://code.activestate.com/recipes/134892/

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
34