PythonのTUIライブラリBlessedの紹介
Blessed について
Blessedは、色、キーボード入力、画面上の位置や位置情報などのエレガントなインターフェイスを提供し、端末アプリを作
るための簡単で実用的なライブラリです。
blessings をフォークして開発されたもの>で、同じAPIを提供しています。 加えて次の拡張が施されています。 Windows のサポート: 2019年12月のリリースからWindwosで動作するようになりました>。
シンプルなキーボード操作: UNICODE 入力をシステムの優先ロケールで安全にデコードし、アプリケーションキーと矢印キーをサポートされました。
24 ビットカラーのサポート: Terminal.color_rgb() と Terminal.on_color_rgb() を使用し、すべての X11カラーを番号ではなく名前 で使用することができます。 Terminal.get_location() によるカーソル位置の決定、Terminal.cbreak() またはTerminal.raw() コンテキストマネージャーによるキーアットタイム入力モード、および Terminal.inkey() による時間指定キー押下の読み出し。
制御シーケンスを含む文字列の印字可能な長さを Terminal.length() で決定できるよう>になり、組み込み関数 textwrap.wrap() と str.center() のターミナル対応版であ>る Terminal.wrap() と Terminal.center() をそれぞれ追加サポートしました。
Terminal.strip_seqs() または Terminal.strip() を使用して、シーケンスを含む文字列からシーケンスと空白を削除することができます。
terminfo
curses ライブラリが内部で使用する terminfo のマニュアルにあるCap-name 列の制御機能は、'begin underline mode' を表す 'smul' のように Terminal クラスの属性として指定す>ることができます。
terminfo のマニュアには、たくさんの興味深い機能がありますが、 これらのすべてが端末でサポートされているわけではありません。サポートされていない場合は空文字列が返されます。
例えば、'blink' はいくつかのターミナルでしか動作しませんが、あなたのターミナルではどうでしょう?
code: python
rom blessed import Terminal
term = Terminal()
print('term.blink('"Is this string blinking?"))
前述したように、blessed は blessings のフォークであり、同じAPIを提供しています。
blessings で記述されたコードは次のようにインポートを修正するだけで動作します。
code: python
# from blessings import Terminal
from blessed import Terminal
term = Terminal()
msg = f"I am {term.bold('bold')} !"
print(msg)
色
blessed では 16色だけでなく 256色をサポートしていて、色名 を指定することができます。 端末ソフトの多くが対応している色は次の8色です。
black
red
green
yellow
blue
magenta
cyan
white
on_ を前につけると背景色になります。
on_black
on_red
on_green
on_yellow
on_blue
on_magenta
on_cyan
on_white
同じ色に bright_blue のように bright_ もしくは bold_ を前につけると、
16色の残りの8色を表現します。
bright_black
bright_red
bright_green
bright_yellow
bright_blue
bright_magenta
bright_cyan
bright_white
モノクロ端末
VT220のような、カラーはサポートしていないがリバースビデオはサポートしているレガ>シー端末をターゲットにしてちょっとした配慮がされています。背景色を直接選択するのではなく、前景色を選択し、次にリバースビデオを選択するとon_background と同じ希望の背景色効果を得られるようになります。
code: python
from blessed import Terminal
term = Terminal()
print(term.on_green('This will not standout on a vt220'))
print(term.green_reverse('Though some terminals standout more than others'))
2番目のprint()は、カラー端末と緑色のモノクローム端末であるVT220の両方で、背景色緑に前景色黒で表示されます。
キーボード
組み込み関数の input()はユーザインタフェースやゲームなどでよく利用されます。
code: python
name = input("What is your name? ")
if sum(map(ord, name)) % 2:
print(f"{name}?! What a beautiful name!")
else:
print(f"How interesting, {name} you say?")
しかし、この関数にはインタラクティブなアプリケーションには向いていないという欠点があります。 この関数はリターンキーを押すまで戻らないので、何かキーを押されたタ>イミングで処理を行うといったことができません。Blessedは、コンテキスト・マネージャの cbreak() と、すべてのキーボード入力のた>めの単一の関数である inkey() でこの問題を解決します。
code: python
from blessed import Terminal
term = Terminal()
print(f"{term.home}{term.black_on_skyblue}{term.clear}")
print("press 'q' to quit.")
with term.cbreak():
val = ''
while val.lower() != 'q':
val = term.inkey(timeout=3)
if not val:
print("It sure is quiet in here ...")
elif val.is_sequence:
print("got sequence: {0}.".format((str(val), val.name, val.code)))
elif val:
print("got {0}.".format(val))
print(f'bye!{term.normal}')
cbreak() を呼び出すと、入力ストリーム上の os.read() が入力が利用可能になると
すぐに戻ることを保証する特別なモードになります。このモードを inkey() と組み合>わせると、 \0x1bOA のようなマルチバイトシーケンスを unicode を継承した Keystroke インスタンスにデコードすることができます。
inkey() が返すKeystrokeはユニコードであり、他のユニコード文字列と表示、結合、>比較することができます。また、以下のような特別な属性も持っています。
is_sequence: bool - アプリケーションキーであるかどうかのBool値
code: int - テスト用のキーコード。
name: str = アプリケーションキーの名前
キーコード
is_sequence プロパティが True の場合、code の値はキーボードの一意のアプリ>ケーションキーを表します。
code は、以下のように、cursesライブラリにあるものと重複する Terminal の属性、>または KEY_ で始まる curses の定数と比較されるかもしれません。
table: blessed のキーコード対比
Name Value Example Sequence(s)
KEY_BACKSPACE 263 ‘ \x08’ , ‘ \x7f’
KEY_BEGIN 354
KEY_BTAB 353
KEY_C1 351
KEY_C3 352
KEY_CANCEL 355
KEY_CATAB 342
KEY_CENTER 350
KEY_CLEAR 333
KEY_CLOSE 356
KEY_COMMAND 357
KEY_COPY 358
KEY_CREATE 359
KEY_CTAB 341
KEY_DELETE 330 ‘ \x1b[3~’
KEY_DL 328
KEY_DOWN 258 ‘ \x1b[B’ , ‘ \x1b[OB’
KEY_EIC 332
KEY_END 360 ‘ \x1b[F’ , ‘ \x1b[K’ , ‘ \x1b[8~’ , ‘ \x1b[OF’
KEY_ENTER 343 ‘ \n’ , ‘ \r’ , ‘ \x1bOM’
KEY_EOL 335
KEY_EOS 334
KEY_ESCAPE 361 ‘ \x1b’
KEY_F0 264
KEY_F1 265 ‘ \x1bOP’
KEY_F2 266 ‘ \x1bOQ’
KEY_F3 267 ‘ \x1bOR’
KEY_F4 268 ‘ \x1bOS’
KEY_F5 269
KEY_F6 270
KEY_F7 271
KEY_F8 272
KEY_F9 273
KEY_F10 274
KEY_F11 275
KEY_F12 276
KEY_F13 277
KEY_F14 278
KEY_F15 279
KEY_F16 280
KEY_F17 281
KEY_F18 282
KEY_F19 283
KEY_F20 284
KEY_F21 285
KEY_F22 286
KEY_F23 287
KEY_FIND 362 ‘ \x1b[1~’
KEY_HELP 363
KEY_HOME 262 ‘ \x1b[H’ , ‘ \x1b[7~’ , ‘ \x1b[OH’
KEY_IL 329
KEY_INSERT 331 ‘ \x1b[2~’
KEY_KP_0 520 ‘ \x1bOp’
KEY_KP_1 521 ‘ \x1bOq’
KEY_KP_2 522 ‘ \x1bOr’
KEY_KP_3 523 ‘ \x1bOs’
KEY_KP_4 524 ‘ \x1bOt’
KEY_KP_5 525 ‘ \x1bOu’
KEY_KP_6 526 ‘ \x1bOv’
KEY_KP_7 527 ‘ \x1bOw’
KEY_KP_8 528 ‘ \x1bOx’
KEY_KP_9 529 ‘ \x1bOy’
KEY_KP_ADD 514 ‘ \x1bOk’
KEY_KP_DECIMAL 517 ‘ \x1bOn’
KEY_KP_DIVIDE 518 ‘ \x1bOo’
KEY_KP_EQUAL 519 ‘ \x1bOX’
KEY_KP_MULTIPLY 513 ‘ \x1bOj’
KEY_KP_SEPARATOR 515 ‘ \x1bOl’
KEY_KP_SUBTRACT 516 ‘ \x1bOm’
KEY_LEFT 260 ‘ \x1b[D’ , ‘ \x1b[OD’
KEY_LL 347
KEY_MARK 364
KEY_MAX 511
KEY_MESSAGE 365
KEY_MIN 257
KEY_MOUSE 409
KEY_MOVE 366
KEY_NEXT 367
KEY_OPEN 368
KEY_OPTIONS 369
KEY_PGDOWN 338 ‘ \x1b[U’ , ‘ \x1b[6~’
KEY_PGUP 339 ‘ \x1b[V’ , ‘ \x1b[5~’
KEY_PREVIOUS 370
KEY_PRINT 346
KEY_REDO 371
KEY_REFERENCE 372
KEY_REFRESH 373
KEY_REPLACE 374
KEY_RESET 345
KEY_RESIZE 410
KEY_RESTART 375
KEY_RESUME 376
KEY_RIGHT 261 ‘ \x1b[C’ , ‘ \x1b[OC’
KEY_SAVE 377
KEY_SBEG 378
KEY_SCANCEL 379
KEY_SCOMMAND 380
KEY_SCOPY 381
KEY_SCREATE 382
KEY_SDC 383
KEY_SDL 384
KEY_SDOWN 336 ‘ \x1b[1;2B’
KEY_SELECT 385 ‘ \x1b[4~’
KEY_SEND 386
KEY_SEOL 387
KEY_SEXIT 388
KEY_SFIND 389
KEY_SHELP 390
KEY_SHOME 391
KEY_SIC 392
KEY_SLEFT 393 ‘ \x1b[1;2D’
KEY_SMESSAGE 394
KEY_SMOVE 395
KEY_SNEXT 396
KEY_SOPTIONS 397
KEY_SPREVIOUS 398
KEY_SPRINT 399
KEY_SREDO 400
KEY_SREPLACE 401
KEY_SRESET 344
KEY_SRIGHT 402 ‘ \x1b[1;2C’
KEY_SRSUME 403
KEY_SSAVE 404
KEY_SSUSPEND 405
KEY_STAB 340
KEY_SUNDO 406
KEY_SUP 337 ‘ \x1b[1;2A’
KEY_SUSPEND 407
KEY_TAB 512 ‘ \t’
KEY_UNDO 408
KEY_UP 259 ‘ \x1b[A’ , ‘ \x1b[OA’
KEY_UP_LEFT 348
KEY_UP_RIGHT 349
これらのキー操作はすべて blessed で解読できます。(デモプログラムとして、keymatrix.py があります。)
削除
通常、バックスペース は ^H (8, または 0x08) で、delete は ^? (127、または 0x7f) です。しかし、システムによっては、バックスペースのキーは実際には "delete" と表示され、送信されるが、オペレーティングシステムでの機能はバックスペースと同じように動作することもあります。Blessed は通常、ほとんどの状況で "backspace" を返します。
フルスクリーンエディタを実装する場合を除いて、 KEY_DELETE と KEY_BACKSPACE の両>方を同じ意味として受け入れ、 設定で削除モードを有効にする選択肢を提供することを>強くお勧めします。
Alt/meta
Linux の bash シェルのようなGNU readlineを使ったプログラムでは、ALT+uでカーソルの後の単語を大文字にするなど、Altコンバイネータがあります。これは、xtermの設定オプションaltSendsEscapeまたはmetaSendsEscapeによって実現されて
います。
しかし、ほとんどの端末では、このキーはオペレーティングシステムによって束縛されるか、インターナショナルキーの挿入に使用されます(ALT+u, aの組み合わせは、文字äを>挿入するために使用されます)。
このため、アプリケーションでは、altキーやmetaキーをまったく使用しないことをお勧めします。
その代わりに、Ctrlキーの組み合わせを、raw()と一緒に使うことで、Altシーケンスを通信するために端末エミュレータをカスタム設定するようユーザーに指示することを避けることができます。
それでもまだ optionall でデコードしたい場合は、ALT+z は Escape + z (または raw >形式で \x1bz) になります。これは blessings では KEY_ESCAPE と 'z' の2つのキース>トロークとして検出されます。Blessingsは現在、これらのキーの組み合わせを検出する>ためのさらなる支援を提供していません。
カーソル位置
現在のカーソル位置は、get_location() を使っていつでも知ることができます。これは、端末エミュレータが応答するアンサーバックシーケンスの一種を使用します。端末が応答しない、あるいは応答するのに時間がかかる場合があるので、timeout キーワード引数を指定して、ブロッキングタイムアウト後に座標(-1, -1)を返すことができます。
code: python
from blessed import Terminal
term = Terminal()
x, y = term.get_location()
print(f'cursor(x,y): {x, y}')
`
get_location() の戻り値は、location() の引数を反映したものである。
`python
# c22_location.py
from blessed import Terminal
term = Terminal()
with term.location(12, 12):
x, y = term.get_location()
print(f'cursor(x,y): {x, y}')
遅延があるため、ほとんどのアプリケーションでは推奨されませんが、多くのアプリケー
ションを確実に簡素化し、また、リモート端末の往復時間、おそらく帯域幅の制約を判断
するために時間を計ることができます。
カーソル移動
テキストを書く前にカーソルの位置を移動させたいだけで、戻ることを気にしない場合は、次のようにします。
code: python
# c31_alighment.py
from blessed import Terminal
term = Terminal()
print(term.home + term.clear, end='')
print(term.move_down(2) + term.move_right(20) + term.bright_red('fire!'), end='')
print(term.move_xy(20, 7) + term.bold('Direct hit!'), end='')
print(term.move_y(term.height - 3), end='')
これは、デフォルトの end='\n' の値では、カーソルが次の行の最初の列に移動するた
めです。
直接移動の機能は4つあります。
move_xy(x, y): カーソルを指定された x, y に配置する。
move_x(x): カーソルをx列に配置する
move_y(y): カーソルを y 行目に配置する
home: カーソルをホーム位置 (0, 0) に配置する
加えて、相対移動の機能おあります。
move_up() , move_up(y) : カーソルを現在の位置より1行またはy行上方に移動させる。
move_down(y): カーソルを1行またはy列のセルを現在の位置より下に移動させる。
move_left() , move_left(x): カーソルを現在の位置から1列またはx列分左に移動させる。
move_right() , move_right(x): カーソルを現在の位置から1またはx列分右に移動させる。
計測
シーケンスを含むあらゆる文字列は、length()メソッドを使って blessed によって計>測することができます。これはblessed がそれ自身の出力を測定し、右揃えし、中央揃えし、切り捨て、またはワードラップできることを意味します!
height, width プロパティは、常に現在のウィンドウのサイズを提供します。
code: python
In 2: # %load c30_height_width.py ...: from blessed import Terminal
...:
...: term = Terminal()
...:
...: term.height, term.width
...:
配列を含む文字列の印刷可能な幅の測定値をターミナルの幅と組み合わせることで、
center(), ljust(), rjust(), truncate(), wrap() メソッドは配列を含む文>字列に対してもきちんと動作します。
code: python
rom blessed import Terminal
term = Terminal()
print(term.home + term.clear)
with term.location(y=term.height // 2):
print(term.center(term.bold('press return to begin!')))
a = term.inkey()
print(a)
`
次の例では、wrap() がシーケンスを含む短い詩をワードラッピングしています。
`python
from blessed import Terminal
from blessed import Terminal term = Terminal()
quote = (
term.bold_cyan('A wise man changes his mind sometimes,')
term.cyan(' but a fool never does.')
term.bold_cyan('To change your mind is the best evidence you have one.')
)
for line in poem:
print('\n'.join(term.wrap(line, width=25, subsequent_indent=' ' * 4)))
端末のリサイズ
物的な端末では画面サイズが変わることはほとんどありませんが、端末エミュレーションソフトを使用している場合では、ウィンドウのサイズが変わることはよくかります。サイズが変わったことを検出するために、SIGWINCHシグナルのコールバックを作成します。
code: python
mport signal
from blessed import Terminal
term = Terminal()
def on_resize(sig, action):
print(f'height={term.height}, width={term.width}')
signal.signal(signal.SIGWINCH, on_resize)
# キー入力を待つ
term.inkey()
ハイパーリンク
ターミナル・エミュレータは、HTMLやfile://のような URLのようなハイパーリンクを>サポートするようになったので、クリックできるテキストのリンクを作成できます。
code: python
from blessed import Terminal
term = Terminal()
print(f"blessed {link}")
macOS では次のパターンがあります。
iterm2: cmd + Alt を押しながらリンクをクリック
標準ターミナル: cmd を押しながらリンクをダブルクリック