cursesサンプル

cursesというのは端末操作用のライブラリです。キーボードから入力を受け取ったり、画面に文字を表示したり、サブウィンドウを作ったり、カーソルを自由に移動させたりできます。もしかしたら vim っぽいものも作れるかもしれません。僕には vim は無理っぽいので、hlkj を押すと文字を表示して、カーソルを移動するだけのものを作ってみました。

#!/usr/local/python
# vim: fileencoding=utf-8

import curses

# ライブラリを初期化し、スクリーンを表すWindowObjectを返す
win = curses.initscr()

# キー入力を自動的に画面に表示しない
curses.noecho()

# Enterキーを押さずに、キー入力に直ちに反応する
curses.cbreak()

keys = (
    ("h", "Left"), 
    ("l", "Right"), 
    ("k", "Up"), 
    ("j", "Down"),
    ("c", "Clear"),
    ("q", "Quit"),
)
for y, key in enumerate(keys):
    # カーソルを座標(x, y) に移動
    win.move(y, 0)

    # カーソル位置に文字を上書き
    # 改行文字は使えないので、1行ずつ表示する
    win.addstr("%s:%s" % key)

# メインウィンドウを更新
win.refresh()

# メインウィンドウの高さと幅を取得
y, x = win.getmaxyx()

# サブウィンドウを作成
swin = win.subwin(y, x-11, 0, 10)

# サブウィンドウの枠線を描画
# 左、右、上、下、左上、右上、左下、右下
swin.border("|", "|", "-", "-", "+", "+", "+", "+")

# カーソルが移動できる範囲は
# (0, 0)〜(ウィンドウの高さ - 1, ウィンドウの幅 - 1)
# この範囲を超えた座標をmove()に渡すとエラー
min_x = 0
min_y = 0
max_y, max_x = swin.getmaxyx()
max_x -= 1
max_y -= 1

# 枠線を上書きしないように調整
min_x += 1
min_y += 1
max_x -= 1
max_y -= 1

# カーソルをサブウィンドウの座標(x, y)に移動
# サブウィンドウの左上の角が(0, 0)
x, y = min_x, min_y
swin.move(y, x)

# カーソル位置のチェックと座標変更用の無名関数
c_move = {
    "h":(lambda x, y: x > min_x, lambda x, y: (x - 1, y)),
    "l":(lambda x, y: x < max_x, lambda x, y: (x + 1, y)),
    "k":(lambda x, y: y > min_y, lambda x, y: (x, y - 1)),
    "j":(lambda x, y: y < max_y, lambda x, y: (x, y + 1)),
}

while 1:
    # 入力されたを文字で取得
    c = swin.getkey()
    
    # 入力されたキーを文字コードで取得
    # c = swin.getch()
    # c = chr(c)

    # 入力されたキーがh,l,k,j
    if c in c_move:
        # カーソル位置が端でなければ、座標を変更
        if c_move[c][0](x, y):
            x, y = c_move[c][1](x, y)

        # 入力された文字を表示した後にカーソルを移動
        # 文字を表示するとカーソルが右端に移動するので
        # 文字表示、カーソル移動の順にしている
        swin.addstr(c)
        swin.move(y, x)
    elif c == "c":
        # サブウィンドウをクリア
        swin.erase()
        # 枠線も消えるので再描画
        swin.border("|", "|", "-", "-", "+", "+", "+", "+")
        swin.move(min_x, min_y) 
    elif c == "q":
        break

    swin.refresh()

# スクリーンを閉じる
curses.endwin()