Pythonを使用したCursesプログラミング
- 著者
- 午前 Kuchling、EricS。 レイモンド
- リリース
- 2.03
呪いとは何ですか?
cursesライブラリは、テキストベースの端末用に端末に依存しないスクリーンペイントおよびキーボード処理機能を提供します。 このような端末には、VT100、Linuxコンソール、およびxtermやrxvtなどのX11プログラムによって提供されるシミュレートされた端末が含まれます。 ディスプレイ端末は、カーソルの移動、画面のスクロール、領域の消去などの一般的な操作を実行するためのさまざまな制御コードをサポートしています。 異なる端末は大きく異なるコードを使用し、多くの場合、独自の小さな癖があります。
Xディスプレイの世界では、「なぜわざわざ」と尋ねる人がいるかもしれません。 確かに文字セルディスプレイ端末は時代遅れの技術ですが、それを使って派手なことをできることはまだ価値があるニッチがあります。 1つは、Xサーバーを搭載していないフットプリントの小さいUnixまたは組み込みUnixです。 もう1つは、Xが使用可能になる前に実行する必要があるOSインストーラーやカーネルコンフィギュレーターなどのツール用です。
cursesライブラリは、さまざまな端末のすべての詳細を非表示にし、重複しない複数のウィンドウを含む表示の抽象化をプログラマーに提供します。 ウィンドウの内容は、テキストの追加、消去、外観の変更など、さまざまな方法で変更できます。cursesライブラリは、適切な出力を生成するために端末に送信する必要のある制御コードを自動的に判断します。
cursesライブラリは元々BSDUnix用に書かれていました。 AT&TのUnixの新しいSystem Vバージョンでは、多くの機能拡張と新機能が追加されました。 BSD cursesは維持されなくなり、AT&Tインターフェースのオープンソース実装であるncursesに置き換えられました。 LinuxやFreeBSDなどのオープンソースのUnixを使用している場合、システムはほぼ確実にncursesを使用します。 現在のほとんどの商用UnixバージョンはSystemVコードに基づいているため、ここで説明するすべての機能がおそらく利用可能になります。 ただし、一部のプロプライエタリUnixに搭載されている古いバージョンのcursesは、すべてをサポートしているわけではありません。
cursesモジュールのWindowsポートを作成した人は誰もいません。 Windowsプラットフォームでは、FredrikLundhによって作成されたコンソールモジュールを試してください。 コンソールモジュールは、カーソルアドレス可能なテキスト出力に加えて、マウスとキーボード入力の完全なサポートを提供し、 http://effbot.org/zone/console-index.htmから入手できます。
Pythoncursesモジュール
このPythonモジュールは、cursesによって提供されるC関数のかなり単純なラッパーです。 Cでのcursesプログラミングに既に精通している場合は、その知識をPythonに転送するのは非常に簡単です。 最大の違いは、Pythonインターフェースでは、addstr()
、mvaddstr()
、mvwaddstr()
などのさまざまなC関数を単一のaddstr()
メソッドにマージすることで、作業が簡単になることです。 。 これについては、後で詳しく説明します。
このHOWTOは、cursesとPythonを使用してテキストモードプログラムを作成するための簡単な紹介です。 これは、cursesAPIの完全なガイドになることを目的としたものではありません。 これについては、Pythonライブラリガイドのncursesに関するセクション、およびncursesのCマニュアルページを参照してください。 しかし、それはあなたに基本的な考えを与えるでしょう。
cursesアプリケーションの開始と終了
何かをする前に、cursesを初期化する必要があります。 これは、initscr()
関数を呼び出すことによって実行されます。この関数は、端末タイプを判別し、必要なセットアップコードを端末に送信し、さまざまな内部データ構造を作成します。 成功した場合、initscr()
は画面全体を表すウィンドウオブジェクトを返します。 これは通常、対応するC変数の名前にちなんで、stdscr
と呼ばれます。
import curses
stdscr = curses.initscr()
通常、cursアプリケーションは、キーを読み取って特定の状況でのみ表示できるようにするために、画面へのキーの自動エコーをオフにします。 これには、noecho()
関数を呼び出す必要があります。
curses.noecho()
また、アプリケーションは通常、Enterキーを押すことなく、キーに即座に反応する必要があります。 これは、通常のバッファ入力モードとは対照的に、cbreakモードと呼ばれます。
curses.cbreak()
端末は通常、カーソルキーなどの特殊キーまたはPageUpやHomeなどのナビゲーションキーをマルチバイトエスケープシーケンスとして返します。 このようなシーケンスを予期してそれに応じて処理するようにアプリケーションを作成することもできますが、cursesはそれを実行して、curses.KEY_LEFT
などの特別な値を返します。 呪いをかけて仕事をするためには、キーパッドモードを有効にする必要があります。
stdscr.keypad(1)
cursesアプリケーションの終了は、開始するよりもはるかに簡単です。 電話する必要があります
curses.nocbreak(); stdscr.keypad(0); curses.echo()
呪いに適した端末設定を逆にします。 次に、endwin()
関数を呼び出して、端末を元の動作モードに戻します。
curses.endwin()
cursesアプリケーションをデバッグする際の一般的な問題は、端末を以前の状態に復元せずに、アプリケーションが停止したときに端末を台無しにすることです。 Pythonでは、これは通常、コードにバグがあり、キャッチされない例外が発生した場合に発生します。 たとえば、キーを入力したときにキーが画面にエコーされなくなり、シェルの使用が困難になります。
Pythonでは、 curses.wrapper()関数をインポートすることで、これらの複雑さを回避し、デバッグをはるかに簡単にすることができます。 これは呼び出し可能であり、上記の初期化を実行します。また、色のサポートが存在する場合は色を初期化します。 次に、提供された呼び出し可能オブジェクトを実行し、最後に適切に初期化を解除します。 呼び出し可能オブジェクトは、例外をキャッチし、cursesの初期化解除を実行してから、例外を上方に渡すtry-catch句内で呼び出されます。 したがって、例外的に端末がおかしな状態のままになることはありません。
窓とパッド
Windowsはcursesの基本的な抽象化です。 ウィンドウオブジェクトは、画面の長方形の領域を表し、テキストの表示、消去、ユーザーによる文字列の入力などのさまざまなメソッドをサポートします。
initscr()
関数によって返されるstdscr
オブジェクトは、画面全体をカバーするウィンドウオブジェクトです。 多くのプログラムでは、この単一のウィンドウのみが必要な場合がありますが、画面を個別に再描画またはクリアするために、画面を小さなウィンドウに分割することをお勧めします。 newwin()
関数は、指定されたサイズの新しいウィンドウを作成し、新しいウィンドウオブジェクトを返します。
begin_x = 20; begin_y = 7
height = 5; width = 40
win = curses.newwin(height, width, begin_y, begin_x)
cursesで使用される座標系についての単語:座標は常に y、x の順序で渡され、ウィンドウの左上隅は座標(0,0)です。 これは、 x 座標が通常最初に来る座標を処理するための一般的な規則に違反します。 これは他のほとんどのコンピュータアプリケーションとの不幸な違いですが、最初に書かれてから呪いの一部であり、今では物事を変えるには遅すぎます。
メソッドを呼び出してテキストを表示または消去しても、効果はすぐにはディスプレイに表示されません。 これは、cursesが元々300ボーの低速端末接続を念頭に置いて作成されたためです。 これらの端末では、画面の再描画に必要な時間を最小限に抑えることが非常に重要です。 これにより、cursesは画面への変更を蓄積し、最も効率的な方法でそれらを表示できます。 たとえば、プログラムがウィンドウに一部の文字を表示してからウィンドウをクリアした場合、元の文字は表示されないため、送信する必要はありません。
したがって、cursesでは、ウィンドウオブジェクトのrefresh()
メソッドを使用して、ウィンドウを再描画するように明示的に指示する必要があります。 実際には、これは実際には呪いを伴うプログラミングをそれほど複雑にしません。 ほとんどのプログラムは活発な活動を開始し、その後、ユーザー側でのキー押下またはその他のアクションを待つために一時停止します。 他の関連ウィンドウのstdscr.refresh()
またはrefresh()
メソッドを呼び出すだけで、ユーザー入力を待つために一時停止する前に、画面が再描画されていることを確認するだけです。
パッドはウィンドウの特殊なケースです。 実際の表示画面より大きくすることができ、一度に表示されるのはその一部のみです。 パッドを作成するには、パッドの高さと幅が必要ですが、パッドを更新するには、パッドのサブセクションが表示される画面上の領域の座標を指定する必要があります。
pad = curses.newpad(100, 100)
# These loops fill the pad with letters; this is
# explained in the next section
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
# Displays a section of the pad in the middle of the screen
pad.refresh(0,0, 5,5, 20,75)
refresh()
呼び出しは、画面上で座標(5,5)から座標(20,75)まで伸びる長方形のパッドのセクションを表示します。 表示されたセクションの左上隅は、パッド上の座標(0,0)です。 その違いを除けば、パッドは通常のウィンドウとまったく同じであり、同じ方法をサポートします。
画面に複数のウィンドウとパッドがある場合は、より効率的な方法があります。これにより、更新時に画面がちらつくのを防ぐことができます。 各ウィンドウのnoutrefresh()
メソッドを使用して、画面の目的の状態を表すデータ構造を更新します。 次に、機能doupdate()
を使用して、物理画面を一度に目的の状態に一致するように変更します。 通常のrefresh()
メソッドは、最後の動作としてdoupdate()
を呼び出します。
テキストの表示
Cプログラマーの観点からは、呪いは関数のねじれた迷路のように見えることがあり、すべて微妙に異なります。 たとえば、addstr()
は、stdscr
ウィンドウの現在のカーソル位置に文字列を表示しますが、mvaddstr()
は、文字列を表示する前に、最初に指定されたy、x座標に移動します。 waddstr()
はaddstr()
と同じですが、デフォルトでstdscr
を使用する代わりに、使用するウィンドウを指定できます。 mvwaddstr()
も同様に続きます。
幸い、Pythonインターフェースはこれらすべての詳細を隠します。 stdscr
は他のオブジェクトと同様にウィンドウオブジェクトであり、addstr()
などのメソッドは複数の引数形式を受け入れます。 通常、4つの異なる形式があります。
形 | 説明 |
---|---|
str または ch | 文字列 str または文字 ch を現在の位置に表示します |
str または ch 、 attr | 現在の位置で属性 attr を使用して、文字列 str または文字 ch を表示します |
y 、 x 、 str または ch | ウィンドウ内の位置 y、x に移動し、 str または ch を表示します。 |
y 、 x 、 str または ch 、 attr | ウィンドウ内の位置 y、x に移動し、属性 attr を使用して str または ch を表示します。 |
属性を使用すると、太字、下線、逆コード、カラーなど、強調表示された形式でテキストを表示できます。 これらについては、次のサブセクションで詳しく説明します。
addstr()
関数は、表示される値としてPython文字列を取りますが、addch()
関数は、長さ1または整数のPython文字列のいずれかである文字を取ります。 文字列の場合、0〜255文字の表示に制限されます。 SVr4 cursesは、拡張文字の定数を提供します。 これらの定数は255より大きい整数です。 たとえば、ACS_PLMINUS
は+/-記号であり、ACS_ULCORNER
はボックスの左上隅です(境界線の描画に便利です)。
Windowsは、最後の操作の後にカーソルが置かれた場所を記憶しているため、 y、x 座標を省略すると、最後の操作が中断された場所に文字列または文字が表示されます。 move(y,x)
方式でカーソルを移動することもできます。 一部の端末では常に点滅するカーソルが表示されるため、カーソルが邪魔にならない場所に配置されていることを確認することをお勧めします。 明らかにランダムな場所でカーソルが点滅するのは混乱を招く可能性があります。
アプリケーションでカーソルの点滅がまったく必要ない場合は、curs_set(0)
を呼び出して非表示にすることができます。 同様に、古いcursesバージョンとの互換性のために、leaveok(bool)
関数があります。 bool がtrueの場合、cursesライブラリは点滅するカーソルを抑制しようとするため、カーソルを奇数の場所に置いたままにすることを心配する必要はありません。
属性と色
文字はさまざまな方法で表示できます。 テキストベースのアプリケーションのステータス行は、通常、リバースビデオで表示されます。 テキストビューアは、特定の単語を強調表示する必要がある場合があります。 cursesは、画面上の各セルの属性を指定できるようにすることで、これをサポートしています。
属性は整数であり、各ビットは異なる属性を表します。 複数の属性ビットが設定されたテキストを表示することはできますが、cursesは、可能なすべての組み合わせが使用可能であること、またはそれらがすべて視覚的に区別できることを保証するものではありません。 これは、使用されている端末の機能に依存するため、ここにリストされている最も一般的に使用可能な属性に固執するのが最も安全です。
属性 | 説明 |
---|---|
A_BLINK
|
点滅するテキスト |
A_BOLD
|
非常に明るいまたは太字のテキスト |
A_DIM
|
半分明るいテキスト |
A_REVERSE
|
リバースビデオテキスト |
A_STANDOUT
|
利用可能な最高のハイライトモード |
A_UNDERLINE
|
下線付きのテキスト |
したがって、画面の最上行にリバースビデオステータス行を表示するには、次のようにコーディングできます。
stdscr.addstr(0, 0, "Current mode: Typing mode",
curses.A_REVERSE)
stdscr.refresh()
cursesライブラリは、それを提供する端末の色もサポートしています。 最も一般的なそのような端末はおそらくLinuxコンソールであり、次にcolorxtermsが続きます。
色を使用するには、initscr()
を呼び出した直後にstart_color()
関数を呼び出して、デフォルトの色セットを初期化する必要があります(curses.wrapper.wrapper()
関数はこれを自動的に行います)。 それが完了すると、使用中の端末が実際に色を表示できる場合、has_colors()
関数はTRUEを返します。 (注:cursesは、カナダ式/英国式のスペル「color」ではなく、アメリカ式のスペル「color」を使用します。 英国式のつづりに慣れている場合は、これらの機能のためにつづりを間違えることを辞任する必要があります。)
cursesライブラリは、前景色(またはテキスト)の色と背景色を含む、有限数の色のペアを維持します。 color_pair()
関数を使用して、色のペアに対応する属性値を取得できます。 これは、A_REVERSE
などの他の属性とビットごとにORをとることができますが、このような組み合わせがすべての端末で機能することが保証されているわけではありません。
色のペア1を使用してテキストの行を表示する例。
stdscr.addstr("Pretty text", curses.color_pair(1))
stdscr.refresh()
前に言ったように、色のペアは前景色と背景色で構成されています。 start_color()
は、カラーモードをアクティブにすると、8つの基本色を初期化します。 それらは、0:黒、1:赤、2:緑、3:黄色、4:青、5:マゼンタ、6:シアン、7:白です。 cursesモジュールは、curses.COLOR_BLACK
、curses.COLOR_RED
などの各色の名前付き定数を定義します。
init_pair(n, f, b)
関数は、カラーペア n の定義を前景色fと背景色bに変更します。 カラーペア0は、黒地に白に配線されており、変更できません。
これらすべてをまとめましょう。 白地に色1を赤のテキストに変更するには、次のように呼び出します。
curses.init_pair(1, curses.COLOR_RED, curses.COLOR_WHITE)
色のペアを変更すると、その色のペアを使用してすでに表示されているテキストはすべて新しい色に変わります。 次の方法で、この色で新しいテキストを表示することもできます。
stdscr.addstr(0,0, "RED ALERT!", curses.color_pair(1))
非常に凝った端末は、実際の色の定義を特定のRGB値に変更できます。 これにより、通常は赤である色1を、紫や青、またはその他の好きな色に変更できます。 残念ながら、Linuxコンソールはこれをサポートしていないため、試してみることができず、例を提供することもできません。 can_change_color()
は、機能がある場合はTRUEを返します。 幸運にもそのような才能のある端末を持っている場合は、システムのマニュアルページで詳細を確認してください。
ユーザー入力
cursesライブラリ自体は、非常に単純な入力メカニズムのみを提供します。 Pythonのサポートにより、不足している部分を補うテキスト入力ウィジェットが追加されます。
ウィンドウへの入力を取得する最も一般的な方法は、そのgetch()
メソッドを使用することです。 getch()
は一時停止し、ユーザーがキーを押すのを待ち、echo()
が以前に呼び出された場合はそれを表示します。 オプションで、一時停止する前にカーソルを移動する座標を指定できます。
メソッドnodelay()
を使用してこの動作を変更することができます。 nodelay(1)
の後、ウィンドウのgetch()
は非ブロッキングになり、入力の準備ができていない場合はcurses.ERR
(値-1)を返します。 halfdelay()
関数もあり、これを使用して(事実上)各getch()
にタイマーを設定できます。 指定された遅延(10分の1秒単位で測定)内に入力が利用可能にならない場合、cursesは例外を発生させます。
getch()
メソッドは整数を返します。 0〜255の場合は、押されたキーのASCIIコードを表します。 255より大きい値は、Page Up、Home、カーソルキーなどの特殊キーです。 curses.KEY_PPAGE
、curses.KEY_HOME
、curses.KEY_LEFT
などの定数に返される値を比較できます。 通常、プログラムのメインループは次のようになります。
while 1:
c = stdscr.getch()
if c == ord('p'):
PrintDocument()
elif c == ord('q'):
break # Exit the while()
elif c == curses.KEY_HOME:
x = y = 0
curses.ascii モジュールは、整数または1文字列の引数をとるASCIIクラスメンバーシップ関数を提供します。 これらは、コマンドインタープリター用のより読みやすいテストを作成するのに役立つ場合があります。 また、整数または1文字の文字列引数を取り、同じ型を返す変換関数も提供します。 たとえば、 curses.ascii.ctrl()は、引数に対応する制御文字を返します。
文字列全体を取得するメソッドgetstr()
もあります。 機能がかなり制限されているため、あまり使用されません。 使用可能な編集キーは、バックスペースキーと文字列を終了するEnterキーのみです。 オプションで、固定文字数に制限できます。
curses.echo() # Enable echoing of characters
# Get a 15-character string, with the cursor on the top line
s = stdscr.getstr(0,0, 15)
Python curses.textpad モジュールは、より良いものを提供します。 これを使用すると、ウィンドウをEmacsのようなキーバインディングのセットをサポートするテキストボックスに変えることができます。 Textbox
クラスのさまざまなメソッドは、入力検証を使用した編集と、末尾のスペースの有無にかかわらず編集結果の収集をサポートします。 詳細については、 curses.textpad のライブラリドキュメントを参照してください。
詳細については
このHOWTOは、画面のスクレイピングやxtermインスタンスからのマウスイベントのキャプチャなど、いくつかの高度なトピックについては説明していませんでした。 しかし、cursesモジュールのPythonライブラリページはこれでかなり完成しました。 次にそれを閲覧する必要があります。
ncursesエントリポイントの詳細な動作について疑問がある場合は、ncursesであるか独自のUnixベンダーであるかにかかわらず、cursesの実装のマニュアルページを参照してください。 マニュアルページには、癖があれば文書化され、使用可能なすべての機能、属性、およびACS_*
文字の完全なリストが提供されます。
curses APIは非常に大きいため、一部の関数はPythonインターフェースでサポートされていません。これは、実装が難しいためではなく、まだ誰も必要としていないためです。 それらを自由に追加してから、パッチを送信してください。 また、ncursesに関連付けられたメニューライブラリはまだサポートされていません。 自由に追加してください。
面白い小さなプログラムを書いたら、別のデモとして気軽に投稿してください。 私たちはいつでもそれらをもっと使うことができます!
ncurses FAQ: http://invisible-island.net/ncurses/ncurses.faq.html