pythonでネタ進捗バーを作ってみた。

# 動機のお話

簡単にいうと、進捗バーを作成したくなりました。
tqdmというパッケージを使えば簡単にできるっぽいのですが、それでは面白みがありません。
せっかくアスキーアートというものがあるのだから、暇な時間はそれを映したいなと思い、いろいろ試すことにしました。

## 必要な知識
まず、進捗バーを表示する際に知っておくべきことがあります。
それは、表示カーソルの動かし方です。
以下のコードを見てください。

```python:sample1.py
import time
for i in range(10):
    time.sleep(1)
    print("\r{}".format(i),end="")
```
これを実行すると、表示される数字が上書きされます。
注目すべきは二つ。
<dl>
    <dt>1. end=""</dt>
    <dd>これはprint関数のオプションにある、終端文字の指定で、デフォルトは"\n"(改行文字)です。
        そのため、これを""(文字なし)に変更すると、行を下に動かさずに出力できるようになります。</dd>
    <dt>2. "\r"</dt>
    <dd>こちらはエスケープシーケンスというもので特殊なコマンドを意味し、入力カーソルを行の先頭に移すことができます。<dd>
</dl>
つまり、end=""をprint関数に入れることで、入力後の改行をなくし、"\r"を入れることで、カーソルを前に持っていき、前の表示文字を強制的に上書きしてしまう、というやり方です。

これを応用すれば、次のようにかけます。
もし使用したいのであれば、stepの数を変更するだけで、大体なんとかなる。はず。

```python:sample2.py
import time
progress_bar = "\r[{0}{1}] {2}%"
step = 100
for i in range(step):
    time.sleep(1)
    bar = "#" * (i//10+1)
    nobar = " " * ((step-i)//10)
    print(progress_bar.format(bar, nobar,  round((i+1) / step * 100., 2)), end="")
```
ここまでが基本のお話。

次に、エスケープシーケンスについて、もう少し詳しく触れます。

# エスケープシーケンスで入力箇所を動かす。
エスケープシーケンスとは、ターミナルの制御を可能とする文字で、\033から始まる文字列で表します。
これを利用すれば色の変更などもできるようだが、今回はカーソルを動かすことに焦点を当てます。
使用するシーケンスは以下の通りです。これさえ使いこなせれば、いかようにもターミナル内の文字を操作できるでしょう。

\033[nA ...カーソルをn行だけ上に移動
\033[nB ...カーソルをn行だけ下に移動
\033[nC ...カーソルをn行だけ右に移動
\033[nD ...カーソルをn行だけ左に移動

ただし、Windowsではエスケープシーケンスはデフォルトでは使用できないので、設定を変える必要があります。下に参考になりそうなリンクを掲載しておきました。
また、Jupyterなどの環境はターミナルとは異なるので、上にあげたようなエスケープシーケンスは使用できないようです。


ということで、これらを用いて、動くAAを作ってみましょう。


# 完成品はこちら
## ダンスを披露するAAを進捗バーの下に表示
コードは以下の通り

``` python:dancing_progress_bar.py
import time

progress_bar = "\r[{0}{1}] {2}%"
size = 1000
#顔文字がずれているのは自分で修正してください。
dance1 = """\
   ♪　∧,＿∧　　♪              
　　 ( ´･ω･) ))	               
　(( (　つ　ヽ、　　　♪	            
　　　〉 とノ　)))	             
　　（__ノ^(＿)	            
"""
dance2 = """\
　 　　∧＿,∧　♪	           
  (( (･ω･` )                  
♪　  /　⊂　 ) ))　♪	           	
　　 (((ヽつ〈                 
　　 (＿)^ヽ__）            
"""

dance3 = """\
　　　　 ∧,＿∧　♪            
　　((　(　　　 )             
♪　　  /　　　 )　))　♪	     
　 (( (  (　 〈	           
　　　 (＿)^ヽ__）           
"""

dance4 = """\
♪　   ∧,＿∧	                  
　　 (　　　　) ))	              
　(( (　　 　ヽ、　　　♪           
　　　〉 　　ノ )　))         
　　（__ノ^(＿)				
"""

dance5 = """\
　　　 ∧＿∧　♪           
　　　(´･ω･`)　　♪        	
　　　（ つ　つ	           
　((　（⌒ __)　))          
　　　　し' っ	             
"""

dance6 = """\
　　♪　∧＿∧					
　　　 ∩´･ω･`)				
　　　　ヽ　 ⊂ノ			
　　　(( （　 ⌒)　　))		
　　 　　　c し'			
"""

dance7 = """\
♪                      
　　　  ∧＿∧ ♪           
.　((o(・ω・` )(ｏ))     
　　　/ 　　　/         
　　　し―-Ｊ            
"""

dance8 = """\
♪                     
　　　 ∧＿∧              
　((ｏ(´・ω・)o))       
　　　 ヽ　　 ヽ ♪     	  
　　　　 し―-Ｊ	    	    
"""
dance_list = [dance1,dance2,dance3,dance4,dance5,dance6,dance7,dance8]

for i in range(size):
	time.sleep(0.05)
	progress = int ((i+1)/100)
	maxprog = int(size/100)
	bar = "=" * progress
	nobar = " " * (maxprog - progress)
	dancenum = (i//25)%8
	if i%25==0:
		print(progress_bar.format(bar, nobar,  round((i+1) / size * 100., 2)))
		print(dance_list[dancenum])
		print("\r\33[7A", end = "")
	else:
		print(progress_bar.format(bar, nobar,  round((i+1) / size * 100., 2)), end="")
	if i == size-1:
		for j in range(6):
			print(" "*40)
		print("\r\33[5A",end="")
```
見て察したとは思いますが、上の8個のAAをひたすら繰り返す進捗バーです。
証拠隠滅のため、進捗100%になった時にAAは消えるように調整しています。
証拠隠滅なんてする必要がない！と思った方は、下の4行を削除すればそのままになるはずです。

せっかくなので、もう一つ用意しておきました。
## 待機中にお茶を勧めてくるAA
```python:progress_bar_ocha.py
ocha = []

ocha.append("""\

                       _______
　　　　　　 ∧__∧　　   /__　o、 |、
　　　　  （´・ω・)    　　| ・　＼ﾉ
　　　　　旦　　ｏ)        | ・  |
　　　　￣￣￣￣￣￣￣￣￣￣￣
""")
ocha.append("""\

　　　　　　ｼﾞｬｰ 　 　 　　＿＿____
　　　　  　 ∧__∧　   　/__　o、 |、
　　　　　 (´・ω・) ﾉ  .ii | ・ ＼ﾉ
　　　　　（　ｏ       　旦 | ・　|
　　　　￣￣￣￣￣￣￣￣￣￣￣
""")
ocha.append("""\
　　　　　あ、きみもお茶のむ？
　　　　　　　　　 　 　　＿＿____
　　　　  　 ∧__∧　 　　/__　o、 |、
　　　　　（´・ω・ ）　　　 | ・　＼ﾉ
　　　　　（　ｏ旦o 　　 　 | ・　|
　　　　￣￣￣￣￣￣￣￣￣￣￣
""")

for i in range(3):
	bar = "#" * (i+1)
	nobar = " " * (2-i)
	print("\r[{0}{1}] {2}/3".format(bar, nobar, i+1))
	print(ocha[i])
	time.sleep(5)
	if i != 2:
    		print("\r\33[8A",end="")
	if i == 2:
		time.sleep(2)
		print("\r\33[7A",end="")
		for i in range(6):
    			print(" "*40)
		print("\r\33[6A",end="")
```


# まとめ

今回、AA進捗バーを作ってみて、エスケープシーケンスの良い勉強になりました。これを応用すれば文字の色なども変えられることも分かったし、以外と学びは多かったです。
これはPython以外でも活用できるようなので、今後仕事でCなどを使う際にも活用していきたいです。

# 参考サイト
[Qiita:Pythonで進捗表示したい！](https://qiita.com/kuroitu/items/f18acf87269f4267e8c1 "Pythonで進捗表示したい！")
[2chのかわいいAA/顔文字まとめ](https://2ch-aa.blogspot.com/2017/05/512.html)
[Windowsでエスケープシーケンスを有効化する方法](http://blog.livedoor.jp/pirosoft/archives/8735780.html "pirosoftのブログ")