本日は
- TerminalMenus の実装を調べてたらこういうことできそうだなという物で作ったネタです.TerminalMenus 自体はJuilaのREPL上で対話操作を可能にしその結果に応じて処理を分岐させることができるツールとしてJuliaの標準に組み込まれています.今回は実装を眺めててえられた知見から得られたコードを紹介します.
できたこと
下記のGIFをごらんください
ターミナル上に出力される文字(の背後)に色をつけたものを逐次プリントしているものになります.
実装
下記のコードをそのまま実行します.
$ julia --color=yes partypeople.jl
--color
オプションを有効にしておかないと背景に色が修飾されないので寂しいアウトプットになります.
#=
julia --color=yes partypeople.jl
=#
using Crayons
using Crayons.Box
using ColorTypes
const SPACE = " " # one white space
BG_COLORS=[
#BLACK_BG ,
RED_BG ,
GREEN_BG ,
YELLOW_BG ,
BLUE_BG ,
MAGENTA_BG ,
CYAN_BG ,
LIGHT_GRAY_BG ,
DEFAULT_BG ,
DARK_GRAY_BG ,
LIGHT_RED_BG ,
LIGHT_GREEN_BG ,
LIGHT_YELLOW_BG ,
LIGHT_BLUE_BG ,
LIGHT_MAGENTA_BG ,
LIGHT_CYAN_BG ,
WHITE_BG ,
]
function draw(c,H,W; init::Bool=false)
H,W = 5,10
buf = IOBuffer()
if !init
print(buf, "\x1b[999D\x1b[$(H)A") #rollback H-times
end
for i in 1:H
msg = repeat("$(rand(1:9))",W)
println(buf, c, msg, Crayon(reset=true))
end
print(buf |> take! |> String)
end
function clean(H)
buf = IOBuffer()
for i in 1:H+1
print(buf, "\x1b[2K") # clear line
print(buf, "\x1b[999D\x1b[$(1)A") # rollback
end
print(buf |> take! |> String)
end
function partypeople()
H,W=5,10
draw(BLACK_BG,H,W,init=true)
for c in BG_COLORS
draw(c,H,W)
sleep(0.1)
end
clean(H)
end
partypeople()
やっていること
Crayon で色付け
draw
関数で色付きの文字を出力します.for ループの中を見ると msg
に格納した文字列を println
している様子がわかります.
色を付けるために Crayons.jl
で定義されているCrayon構造体を msg
の前後に挟んでいます.実体はプリントの制御をする \e[31m
(赤色)や \e[0m
(元に戻す)のような命令を挟んでいるだけです.
例えば下図のように出力に使う色を制御させることができます.
繰り返しプリントするために
プリントしたら元に戻す
print(buf, "\x1b[999D\x1b[$(H)A")
をするとプリントをする場所をH行分戻してくれます.これをすることで出力がダーーーーっと流れるのを防いでいます.
普通 println("Hello") とするとプリントした結果の次の行にカーソルが出ますね.続けて println("World")
とすると
Hello
World
のように前の出力である Hello
が残っています.カーソルを1行戻して World
を書くと Hello
が消えて World
”だけ”が残るという仕組みです.
Step 1 | Step 2 | Step 3
Hello | > Hello | World
World> | (上に持っていく) |
実際に動かしてもらった方が理解が進むと思います.
後片付け
clean
関数で draw
をした結果を消していきます.print(buf, "\x1b[2K")
をして行を消してカーソルを1行上に持っていくという操作をプリントした行数繰り返すということをしています.
まとめ
- 以上の動作を高速にすることで色付きの文字がパッパと入れ替わるように見せることができました.
- TerminnalMenu の実装から面白いところを取ってきてJuliaのお遊びコードを書きました.TerminalMenus の中では waitKey という関数が中で動いており,ユーザーのキーの入力に合わせてアクションをしその中で IOBuffer を制御
させてターミナルでの対話操作を可能にしています.頑張ったらJuliaだけで簡単なエディタ作れそうですね.お暇な方是非どうぞ.
おまけ
おつきさんがグルグル回転します.
function moonbar()
try
print("\x1b[?25l") # hide cursor
while true
for 🌝 ∈ '🌑':'🌘'
sleep(0.1)
print(🌝)
print('\r')
end
end
finally
print("\x1b[?25h") # unhide cursor
end
end
moonbar()
これも同様で出力する場所をコントロールしています.Nowloading...って感じのシチュエーションで使ってみてください.