\documentclass{howto}

\title{Python での Curses プログラミング}

\release{2.01}

\author{A.M. Kuchling, Eric S. Raymond}
\authoraddress{\email{amk@amk.ca}, \email{esr@thyrsus.com}}

\begin{document}
\maketitle

\begin{abstract}
\noindent
このドキュメントは、表示の制御に \module{curses} 拡張モジュールを使いながら
Python 2.x でのテキストモードプログラムの書き方について記述しています。

このドキュメントは \url{http://www.python.org/doc/howto} にある
Python HOWTO ページから入手可能です。
\end{abstract}

\tableofcontents

\section{curses とは何か?}

curses ライブラリは、テキストベースのターミナル向けにターミナル非依存の
スクリーン描画およびキーボード制御機構を提供します；それらターミナルには
VT100、Linux コンソール、そして xterm や rxvt といった X11 による
ターミナルエミュレータなどがあります。ディスプレイターミナルは
カーソル移動、画面スクロール、領域削除といった良くあるオペレーションを
行うために様々な制御コードをサポートしています。異なるターミナルは
大きく異なるコードをもち、しばしばそれら自身にもちょっとしたクセがあります。

X ディスプレイの世界では、``意味なくない？'' と思うかもしれません。確かに
キャラクターセル（character-cell）ディスプレイは廃れたテクノロジです、しかし
それらによってしゃれたことを成し遂げられることに価値があるような
分野は存在するのです。一つは X サーバを持たない小さなフットプリントの、
または組み込み Unix において。もう一つは OS のインストーラや
カーネル設定といったツールで、これらは X が利用可能になる前に動作できなければならないでしょう。

curses ライブラリは異なるターミナルの詳細を全て隠蔽し、そして
プログラマに、オーバーラップしない複数のウインドウを含む
抽象化されたディスプレイを提供します。ウインドウの内容は色々な方法で
変更できます -- テキストの追加、削除、それらの見た目の
変更 -- そしてcurses ライブラリは自動的に、正しい出力を得るには
どのような制御コードをターミナルに送るべきかを
割り出すのです。

curses ライブラリは元々は BSD Unix 用に書かれました；後に AT\&T による
System V バージョンの Unix が、たくさんの改善と機能を追加しました。
BSD curse はもはやメンテナンスされておらず、AT\&T インターフェースの
オープンソース実装である ncurses に取って代わられました。もし
Linux や FreeBSD といったオープンソース Unix を使っていれば、そのシステムが
ncurses を使っていることはほぼ確かでしょう。現在の商用 Unix の大部分は
System V のコードをベースにしていますので、ここで記述する関数は恐らく全て
利用可能でしょう。いくつかのプロプライエタリ Unix が持つ古いバージョンの
curses では、全てはサポートしていないかもしれません。

誰も curses モジュールの Windows ポートは作っていません。Windows 
プラットフォームにおいては、Fredrik Lundh によって書かれた Console モジュールを試してください。
Console モジュールは カーソル位置指定可能なテキスト出力、プラス
マウスおよびキーボード入力を完全にサポートしていて、
\url{http://effbot.org/efflib/console} から入手可能です。


\subsection{The Python curses module}

Python モジュールは、curses が提供する C 関数のかなりシンプルな
ラッパです；もし C による curses プログラミングに精通していれば、
その知識を Python に持ち込むことは本当に容易でしょう。
最大の違いは Python インターフェースは単純化されていることで、
\function{addstr}、\function{mvaddstr}、\function{mvwaddstr} といった
異なる C 関数を一つの \method{addstr()} メソッドに統合しています。
これは後でもっと詳しく見ていきます。

この HOWTO は単なる curses と Python によるテキストモードプログラムの
紹介です。これは curses API 完全ガイドとなることは目指していません；
これは Python ライブラリガイドの ncurses セクションや ncurses の
C のマニュアルを参照してください。しかし、これで基本的なアイデアは
分かることでしょう。

\section{curses アプリケーションの開始と終了}

何よりもまず、curses は初期化をしなければいけません。これは
\function{initscr()} を呼ぶことで行いますが、これで
ターミナルのタイプを決定し、ターミナルに必要なセットアップコードを送信し、
様々な内部データ構造を作成します。成功すると、
\function{initscr()} は画面全体を表すウインドウオブジェクトを
返します；これは、普通は対応する C の変数にあやかって \code{stdscr} と
呼ばれます。

\begin{verbatim}
import curses
stdscr = curses.initscr()
\end{verbatim}

curses アプリケーションは通常、キーを読み取り、特定の状況においてだけ
表示できるように、画面へのキーの自動エコーをオフにします。
これは \function{noecho()} 関数を呼ぶことが必要です。

\begin{verbatim}
curses.noecho()
\end{verbatim}

普通、アプリケーションは Enter キーの押下を要求することなく
キーに即座に反応する必要があります；これは通常の
バッファ入力（buffered input）モードに対して、cbreak モードと呼ばれます。

\begin{verbatim}
curses.cbreak()
\end{verbatim}

ターミナルは例えばカーソルキーや、Pege UP や Home のような
ナビゲーションキーといった特別なキーをマルチバイトの
エスケープシーケンスとして返します。アプリケーションでそのような
シーケンスを予期し、然るべく処理するように書く事も可能ですが、
curses が \constant{curses.KEY_LEFT} のような特殊な値を返すことで
代わりに行なってくれます。curses にこの仕事をさせるには、
キーパッド（keypad）モードを有効にする必要があります。

\begin{verbatim}
stdscr.keypad(1)
\end{verbatim}

curses アプリケーションの終了は開始よりもずっと簡単です。
あなたは以下を呼ぶ必要があり、

\begin{verbatim}
curses.nocbreak(); stdscr.keypad(0); curses.echo()
\end{verbatim}

そうすると curses 向きのターミナル設定が元に戻ります。それから
\function{endwin()} 関数を呼んでターミナルを元々の操作モードに
戻します。

\begin{verbatim}
curses.endwin()
\end{verbatim}

curses アプリケーションのデバッグ中に良くある問題は、アプリケーションが
死んだ際にターミナルを前の状態に戻さず、めちゃくちゃになってしまう
ことです。Python では、コードがバグだらけでキャッチされない例外が
送出された場合によく起こります。例えば、キーはもはや
タイプしてもスクリーンにはエコーされず、shell を使うことが
難しくなってしまうのです。

Python では、\module{curses.wrapper} モジュールを import することで
これらのやっかいごとを避け、デバッグを非常に容易にすることができます。これは
フックを引数に取る \function{wrapper} 関数を供給します。これは
上記で書いたような初期化を行い、カラーをサポートしていれば、
カラーも初期化します。それからあなたのフックを実行し、最後には適切に
非初期化を行います。このフックは try-catch 節の中で呼ばれ、
これは例外をキャッチし、curses の非初期化を実行し、例外を上に
渡します。ですので、例外によってターミナルが奇妙な状態におかれることは
ないでしょう。

\section{Window と Pad}

ウインドウは curses における基本的な抽象化です。ウインドウオブジェクトは
スクリーンの長方形の領域を表現していて、テキストの表示、消去、ユーザに
文字列入力をさせる等々、様々なメソッドをサポートしています。

\function{initscr()} 関数によって返される \code{stdscr} オブジェクトは
スクリーン全体をカバーするウインドウオブジェクトです。多くのプログラムでは
たった一つのウインドウだけが必要でしょうが、スクリーンをより小さな
ウインドウに分割して、個々に再描画や消去を行いたいこともあります。
\function{newwin()} 関数は与えられたサイズでウインドウを作成し、
新たなウインドウオブジェクトを返します。

\begin{verbatim}
begin_x = 20 ; begin_y = 7
height = 5 ; width = 40
win = curses.newwin(height, width, begin_y, begin_x)
\end{verbatim}

curses で使われる座標システムについて：座標は、
常に \emph{y.x} の順番で渡され、ウインドウの左上の角が
座標の (0,0) です。これは座標を扱う際の通常の規約、 \emph{x} 座標が
普通は最初にくるようなものを破っています。これはその他の多くの
コンピュータアプリケーションからの不幸な差異ではありますが、
これは、それが最初に書かれた時から curses の一部であり、
もはや変更するには遅すぎるのです。

テキストを表示もしくは消去するメソッドを呼んだ際、ディスプレイに
その効果はすぐには表れません。これは、curses が元々は
300 ボーの遅いターミナル接続を念頭に書かれたからです；
これらのターミナルでは、スクリーンの再描画に必要な時間を最小化することは
非常に重要です。これにより curses は、スクリーンに対する変更を集約し、
これらを最も効率的なやり方で表示するようにしています。例えば、
あなたのプログラムでウインドウに何らかの文字を表示し、それから
ウインドウをクリアした場合、オリジナルのテキストは決して見えることは
ないので送信する必要がありません。

ですので、curses はウインドウの再描画を、ウインドウオブジェクトの 
\function{refresh()} メソッドを使って明示的に告げることが必要です。
実際は、これは curses でのプログラミングをそれほど複雑にはしません。
大部分のプログラムは、にわかに動作した後にキー押下やその他ユーザ側の
アクション待ちで一時停止します。あなたがすべきことは、ユーザの入力待ちで
一時停止する前に \code{stdscr.refresh()} または何らかの同等のウインドウの 
\function{refresh()} メソッドを呼ぶ事で、確実に画面が再描画されているように
することです。

パッド（Pad）はウインドウの特殊なケースです；これは実際のディスプレイ画面より
大きいことがあり、いちどきに表示されるのはその一部分だけです。
pad を作成するには単に pad の高さと幅が必要ですが、pad の再描画には、
pad の一部分が表示されるであろう画面上のエリアの座標を渡す事が必要です。

\begin{verbatim}
pad = curses.newpad(100, 100)
# このループは pad を文字で埋めます；これは
# 次のセクションで説明します。
for y in range(0, 100):
    for x in range(0, 100):
        try: pad.addch(y,x, ord('a') + (x*x+y*y) % 26 )
        except curses.error: pass

# pad の一部分をスクリーンの真ん中に表示します。
pad.refresh( 0,0, 5,5, 20,75)
\end{verbatim}


\function{refresh()} 呼び出しで pad の一部分を
座標 (5,5) から座標 (20,75) に広がる領域に
表示します；表示される部分の左上のコーナーは pad の
座標 (0,0) です。このような違いはあれど、pad は通常のウインドウと
ぴったり似ていて、同じメソッドをサポートしています。

スクリーンに複数のウインドウや pad がある場合、もっと
効率的なやりかたがあり、これは更新時におけるスクリーンのちらつきを
防止します。\method{noutrefresh()} と/または各々のウインドウに対する 
\method{noutrefresh()} を使い、望むスクリーンの状態を表現する
データ構造を更新します；それから、\function{doupdate()} 関数により
物理的なスクリーンを望む状態に一度に変更します。
通常の \method{refresh()} メソッドは、その最後の動作として
\function{doupdate()} を呼んでいます。

\section{Text の表示}

C プログラマの観点からは、curses は時として曲がりくねった関数の迷路の
ように見えます、全てが微妙に異なっているのです。たとえば、
\function{addstr()} は \code{stdscr} の現在のカーソル位置に
文字列を表示しますが、\function{mvaddstr()} は文字列を表示する前に
与えられた y,x 座標にまず移動します。
\function{waddstr()} は \function{addstr()} に良く似ていますが、
デフォルトで \code{stdscr} を使う代わりに使用するウインドウを指定できます。
\function{mvwaddstr()} も然り。

幸いなことに、Python インターフェースはこれらの詳細を全て隠蔽します；
\code{stdscr} は他と同じようなウインドウオブジェクトで、
\function{addstr()} のようなメソッドは複数の引数の形を受け付けます。
通常は 4 つの異なる形態があります。

\begin{tableii}{|c|l|}{textrm}{Form}{Description}
\lineii{\var{str} または \var{ch}}{文字列 \var{str} または
文字 \var{ch} を表示します。}
\lineii{\var{str} または \var{ch}, \var{attr}}{文字列 \var{str} または
文字 \var{ch}を、 属性 \var{attr} を使用して表示します。}
\lineii{\var{y}, \var{x}, \var{str} または \var{ch}}
{ウインドウ中の位置 \var{y,x} に移動し、\var{str} 
または \var{ch} を表示します。}
\lineii{\var{y}, \var{x}, \var{str} または \var{ch}, \var{attr}}
{ウインドウ中の位置 \var{y,x} に移動し、\var{str}
または \var{ch} を、属性 \var{attr} を使用して表示します。}
\end{tableii}

属性（Attribute）により、テキストを強調した形で表示できます、例えばボールド、
下線、反転コード、または色付き。これらは次のセクションで
もっと詳しく説明します。

\function{addch()} は 長さ 1 の Python の文字列または整数のいずれかである
文字（character）を取る一方、\function{addstr()} 関数は Python の文字列を
表示すべき文字列として取ります。もし
文字列であるならば、表示される文字は 0 から 255 の間に
制限されます。SVr4 の curses は拡張文字のための定数を提供します；
これらの定数は 255 よりも大きな整数です。例えば、
\constant{ACS_PLMINUS} は +/- 記号で、\constant{ACS_ULCORNER} は
矩形の左上の角です（境界線の描画に便利です）。

ウインドウは、最後の操作でカーソルがどこに置かれたか記憶していますので、
\var{y,x} 座標を指定しないと、文字列または文字は、最後の操作が
終わった場所に表示されます。カーソルは \function{move(\var{y,x})} 
メソッドでも移動できます。いくつかのターミナルでは点滅する
カーソルを常に表示していますので、カーソルがどこか邪魔にならない場所に
位置しているようにしたいかもしれません；カーソルがどうみてもランダムな
場所で点滅していたら混乱してしまうかもしれませんね。

もしアプリケーションが点滅するカーソルを全く必要としないならば、
\function{curs_set(0)} を呼んで非表示にできます。同様に、
古い curses のバージョンとの互換性のために、\function{leaveok(\var{bool})}
関数があります。\var{bool} が true なら、curses ライブラリは
点滅するカーソルが抑制されるようにし、それが妙な場所に
残ってしまうことを心配しなくていいようになるでしょう。

\subsection{属性（Attribute）と色（Color）}

文字は色々に表示できます。テキストベースのアプリケーションにおける
ステータスラインは通常は反転された映像で表示されます；テキストビューアは
特定の単語をハイライトする必要があるかもしれません。curses は画面の
特定のセルに属性（Atribute）を指定させることでこれをサポートしています。

属性は整数で、それぞれのビットが異なる属性を表現しています。
テキストを複数の属性ビットを設定して表示させることもできますが、
curses は全ての可能な組み合わせが利用できること、または
それら全てが視覚的に識別できることは保証しません。これは使われている
ターミナルの性能に依存しますので、最もよくある属性にとどめることが
最も安全です。ここにリストします。

\begin{tableii}{|c|l|}{定数}{属性}{説明}
\lineii{A_BLINK}{点滅するテキスト}
\lineii{A_BOLD}{非常に明るい、またはボールドのテキスト}
\lineii{A_DIM}{半分ぐらい明るいテキスト}
\lineii{A_REVERSE}{反転したテキスト}
\lineii{A_STANDOUT}{可能な中のベストなハイライトモード}
\lineii{A_UNDERLINE}{下線付きテキスト}
\end{tableii}

つまり、反転した映像のステータス行をスクリーン上部に
表示するためには、
次のようにコーディングできます：

\begin{verbatim}
stdscr.addstr(0, 0, "Current mode: Typing mode",
	      curses.A_REVERSE)
stdscr.refresh()
\end{verbatim}

curses ライブラリはそれを提供するターミナル上では色づけもサポートします。
そのようなターミナルで最も良くあるものは恐らく Linux コンソールで、
カラーの xterm などもそうです。

色づけを使うには、\function{initscr()} を呼んだすぐ後に
\function{start_color()} 関数を呼んで、デフォルトのカラーセットを
初期化する必要があります（\function{curses.wrapper.wrapper()} 関数は
これを自動的に行います）。これが終わると、\function{has_colors()} 関数は
使っているターミナルが実際にカラーを表示できるならば TRUE を返します。
（AMK からの注記：curses は、カナダ/英国式のスペリング 'colour' の代わりに
アメリカ式のスペリング 'color' を使います。もし私と同じなら、
これらの関数のおかげで往生するに違いないですね。）

curses ライブラリは、前景（またはテキスト）色と背景色が含まれている
有限個の色の組み合わせ（pair）を管理しています。
\function{color_pair()} 関数で色の組み合わせに対応する属性値を
取得可能です；これは \constant{A_REVERSE} のような
その他の属性ともビット単位で OR されていますが、くり返します、
その組み合わせは全てのターミナルで動作することは保証されていません。

一行のテキストを、色の組み合わせ（pair）1 を使って表示する例です：

\begin{verbatim}
stdscr.addstr( "Pretty text", curses.color_pair(1) )
stdscr.refresh()
\end{verbatim}

前に述べたとおり、色の組み合わせは前景と背景色から成ります。
\function{start_color()} は 8 つの基本色を、カラーモードを
有効化したときに初期化します。それらは：0：黒、1：赤、
2：緑、3：黄色、4：青、5：マゼンタ、6：シアン、それに 7：白です。
curses モジュールはこれらの色それぞれに定数の名前を付けています：
\constant{curses.COLOR_BLACK}、\constant{curses.COLOR_RED}、
などなど。

\function{init_pair(\var{n, f, b})} 関数は色のペア \var{n} の
定義を前景色 {f} と 背景色 {b} に変更します。色のペア 0 は
黒地に白で固定されていて、変更はできません。

整理してみましょう。色 1 を白い背景に赤いテキストに
変更するには、こう呼べるでしょう：

\begin{verbatim}
curses.init_pair(1, curses.COLOR_RED, curses.COLOR_WHITE)
\end{verbatim}

色のペアを変更すると、既にその色のペアを使って表示されたテキストも
新しい色に変更されます。新しいテキストもこの色で表示するには
こう出来ます：

\begin{verbatim}
stdscr.addstr(0,0, "RED ALERT!", curses.color_pair(1) )
\end{verbatim}

非常にかっこいいターミナルは、実際の色定義を任意の RGB 値に
変更できます。これは色 1 を、通常は赤のところ、紫や青や、その他どんな
お好みの色にも変更できるということです。不幸にも Linux コンソールは
これをサポートせず、したがって私はこれを試す事ができないですし、
どのような例も示せません。あなたのターミナルがこれを出来るかを
チェックするには \function{can_change_color()} を呼び、これはその能力があれば 
TRUE を返します。幸運にもそのような有能なターミナルに恵まれた場合、
さらなる情報はシステムの man ページに当たって下さい。

\section{ユーザの入力}

curses ライブラリそれ自身は非常にシンプルな入力メカニズムしか提供しません。
Python の助けにより、不足しているいくらかを補うテキスト入力ウィジェットが
加わっています。

ウインドウに入力を行う最も一般的なやり方は、その \method{getch()} 
メソッドを使うことです。これは一時停止して、ユーザがキーを押すのを
待ち、\function{echo()} が前に呼ばれていればこれを表示します。
オプションとして、一時停止する前にカーソルを移動する座標を
指定することも可能です。

\method{nodelay()} メソッドで、この挙動を変更することも
可能です。\method{nodelay(1)} のあと、ウインドウに対する \method{getch()} で
ブロックしなくなり、入力が準備されていなければ ERR (-1) が
返ります。\function{halfdelay()} 関数もあり、これは \method{getch()} の
各呼び出しにタイマーをセットするのに（事実上）使われます；\function{h} の
引数として指定したミリ秒のうちに入力が得られなければ、curses は例外を送出します。

\method{getch()} メソッドは整数を返します；それが 0 から 255 の間であれば、
これは押されたキーの ASCII コードを示しています。255 より大きな値は
Page Up、Home、カーソルキー等の特別なキーです。
返されたキーは \constant{curses.KEY_PPAGE}、\constant{curses.KEY_HOME}、または
\constant{curses.KEY_LEFT} といった定数と比較ができます。通常、プログラムの
メインループは以下のようになるでしょう：

\begin{verbatim}
while 1:
    c = stdscr.getch()
    if c == ord('p'): PrintDocument()
    elif c == ord('q'): break  # while() を抜ける
    elif c == curses.KEY_HOME: x = y = 0
\end{verbatim}

\module{curses.ascii} モジュールは、整数か 1 文字の文字列を
引数に取る ASCII クラスのメンバーシップ関数を
供給します；これはより読みやすいコマンドインタプリタのテストを
書くのに便利かもしれません。これは、整数か 1 文字の文字列の引数を取って
同じ型を返す変換関数も供給します。例えば、\function{curses.ascii.ctrl()} は
その引数に対応したコントロール文字を返します。

文字列全体を取得するメソッド、\constant{getstr()} も存在します。
これはそれほど使われません、なぜならこれの機能は非常に
限られているからです；利用可能な編集キーはバックスペースと
文字列を終了する Enter キーだけです。これはオプションで
決まった数の文字に限定することも可能です。

\begin{verbatim}
curses.echo()            # 文字のエコーを有効にする
# カーソルは一番上の行にして、15 文字の文字列を取得する
s = stdscr.getstr(0,0, 15)  
\end{verbatim}

Python の \module{curses.textpad} モジュールはもう少し良いものを提供します。
これによって、ウインドウを Emacs 風のキーバインディングをサポートする
テキストボックスにすることが可能です。\class{Textbox} クラスの
様々なメソッドが、入力のバリデーションや末尾のスペースの有り/無しの
入力取得が可能な編集をサポートします。詳細は、\module{curses.textpad} の
ライブラリドキュメントを参照してください。


\section{さらなる情報}

この HOWTO は、スクリーンスクレーピングや xterm インスタンスからの
マウスイベントのキャプチャといった上級のトピックはカバーしていません。しかし、
Python ライブラリの curses モジュールのページが今や非常に完ぺきなものです。
次はこれを参照すべきでしょう。

もし、ncurses の初歩の詳しい挙動について何か疑わしければ、
ncurses であれプロプライエタリな Unix ベンダーのものであれ、あなたの 
curses 実装のマニュアルページに当たってください。マニュアルのページには
いかなるクセもドキュメントされ、利用可能な全ての関数、属性、\constant{ACS_*} 文字
の完全なリストが提供されていることでしょう。

curses の API は非常に大きいので、いくつかの関数は Python インターフェースでは
サポートされていません、これは実装が難しいからではなく、今まで
誰もそれを必要としていないからです。お好きなようにこれらを追加し、
そうしたらパッチを送ってください。さらに、ncurses にあるメニュー（menu）や
パネル（panel）もサポートしていません；これも追加なさってください。

興味深いちょっとしたプログラムを書いたなら、気軽にデモとして
提供してください。まだまだありますよ!

ncurses FAQ: \url{http://dickey.his.com/ncurses/ncurses.faq.html}

\end{document}
