2023年4月5日 星期三

ansi: 移動遊標與顯示彩色文字的 python 函式庫

256色終端機測試 想要在終端機視窗裡移動遊標、 顯示彩色的文字跟背景、 粗體/斜體/底線/閃爍等等特效, 如果在 bash 底下, 可以 用 echo 指令 列印特殊的 ANSI escape sequences。 如果在 python 底下, 則可以使用 ansi 這個套件。

先安裝套件: pip3 install ansi 然後進入 python3 測試:

import sys
from ansi.colour import fg, bg, fx
print('中文', fx.italic, '也', fx.bold, '可以', bg.blue, '有', fg.red, '特效', fx.blink, '嗎', fx.reset, '?', sep='')
sys.stdout.write('中文' + str(fx.italic) + '也' + str(fx.bold) + '可以' + str(bg.blue) + '有' + str(fg.red) + '特效' + str(fx.blink) + '嗎' + str(fx.reset) + '?')
sys.stdout.write( ''.join([ str(x) for x in ['中文', fx.italic, '也', fx.bold, '可以', bg.blue, '有', fg.red, '特效', fx.blink, '嗎', fx.reset, '?'] ] ) )

模組名稱可以叫 ansi.colour 或 ansi.color。 在文字之間穿插特效指令, 最後要記得用 fx.reset 取消所有特效指令就可以了。 特效指令的型態並不是字串; 如果要拿它跟字串相加, 就必須先做 type cast 轉型態。 print() 函數在包裝底下, 其實還是呼叫 sys.stdout.write 在做事。 所以要用 print 並且加上 sep='' 或是直接把所有參數轉成字串、 合併, 再一次呼叫後者, 叫效果都一樣。

pip3 show ansi | grep -i location 指令查出套件安裝的位置, 可以研究底下的 ansi/ 子目錄裡面的這幾個檔案:

  • cursor.py: 隱藏或移動遊標、 清除螢幕
  • colour/fx.py: 字元粗體/斜體/底線/閃爍等等特效
  • colour/fg.py: 字元前景顏色
  • colour/bg.py: 字元背景顏色

我寫了兩個小程式作為範例。 第一個範例 ctimer.py 在終端機右上角計時 (分鐘), 每隔一段時間就歸零。 如果 pip3 有安裝 playsound 套件且系統有某個聲音檔的話, 還可以打開 (提及 playsound 的那) 兩句註解, 讓它在歸零時播放鐘聲。 請特別注意 sys.stdout.flush() 這一句: 目的是叫 python3 馬上顯示, 不要把輸出 buffer 起來。

第二個範例 knight.py 是我最喜愛的 騎士走遍棋盤問題。 向前探索的時候用一種顏色; 倒退 (backtrack) 的時候用另一種顏色。 可以用 -s 指定速度 (指數效果!)、 方便觀察, 也可以用 -x 跟 -y 指定初始位置。 例如: python3 knight.py -s 3 -x 2 -y 2 因為 ansi 函式庫 清除螢幕功能目前有一個小 bug, 所以我的程式最前面手動修正它:

from ansi.sequence import sequence
cursor.erase = sequence('2J')

或者你也可以乾脆到 (先前) pip3 show ansi ... 所顯示的目錄底下直接修改 cursor.py 這個檔案, 一勞永逸。 因為我用 cursor.hide() 把遊標藏起來, 所以必須用 atexit() 這個函式預先註冊, 指定程式萬一被 ctrl-c 打斷, 就要在結束之前還原遊標。

實驗時, 如果程式沒寫好, 最後沒有印 fx.reset 或是沒有呼叫 cursor.show() 還原遊標, 那也可以在 shell 底下用 reset 指令手動還原。

最後一個例子: 下面這段程式可以顯示更細緻的前景顏色, 如文章最前面的截圖。 不過 ansi 好像沒有支援細緻背景顏色的功能。

import sys
from ansi.colour.rgb import rgb256
from ansi.colour import fx
for i in range(16):
    for j in range(16):
        sys.stdout.write(str(rgb256(i*16-1, j*16-1, 0xff)) + '#')
    sys.stdout.write('  ')
    for j in range(16):
        sys.stdout.write(str(rgb256(0xff, i*16-1, j*16-1)) + '#')
    sys.stdout.write('  ')
    for j in range(16):
        sys.stdout.write(str(rgb256(j*16-1, 0xff, i*16-1)) + '#')
    sys.stdout.write('\n')
sys.stdout.write(str(fx.reset))

不確定這個 ansi 函式庫還有沒有繼續在維護。 如果只是需要基本功能, 其實目前的版本也就夠了。 如果要比較複雜的終端機圖形介面, 也許可以研究 pytermguitextual 等等其他函式庫。

沒有留言:

張貼留言

因為垃圾留言太多,現在改為審核後才發佈,請耐心等候一兩天。