ループ処理の進捗を視覚的に数値(%表記など)やプログレスバー形式で表示させたい場合があります。
3rdParty製モジュールを使った本格的なプログレスバーもありますが、本記事では標準組込み関数を用いた方法についてまとめます。
ポイントは、「改行処理」、「キャリッジリターン」と「行クリア」です。
改行とキャリッジリターン
出力結果を毎回同じ位置に出力するためのポイントは、以下3つです。
- 改行処理
- print() で出力する場合は、例えばend引数=””を設定(下記具体例を参照)
- sys.stdout.write() で出力する場合は改行の設定は不要
- キャリッジリターン(CR, 行頭復帰)
- \rを出力文頭に記述し、カーソルを行の先頭に戻す
- 行クリア
- 前のタイミングよりも短い文字列を出力する場合、そのまま表示するとはみ出た分の文字列が残ってしまいます。
- その為、出力文字列の先頭に\033[K(ANSI escape code )を記述し、カーソルのある行を一旦クリアします。
- ※ANSI escape code
- \033: ESC(エスケープシーケンス)の8進数表記
- [ : CSI(Control Sequence Indicator)を表す。コマンド開始を示す
- K : EL (Erase in Line) を表す。カーソル位置から行の末尾まで削除
- ※ANSI escape code
具体例
例1) 単純に同じ位置に出力する
# 0.5s毎に数値をカウントアップ
import time, sys
for i in range(10):
print(f"\r\033[K{i}", end="")
#sys.stdout.write(f"\r\033[K{i}") #print()の代わりにsys.stdoutでもOK
time.sleep(0.5)
出力イメージ(0から9までカウントアップされます)
※見やすくするためにelse節以下に改行処理を入れています
例2) かんたんプログレスバー
# 0.5s毎に"#" 表示を更新
import time
for i in range(1, 11):
print(f"\r\033[K{'#' * i}", end="")
time.sleep(0.5)
出力イメージ(”#”が伸びていきます)
例3) プログレスバー + 進捗(%表示など)
# 0.5s毎に"#"表示と数値を更新
import time
n = 20
for i in range(n+1):
bar = '*' * i + " " * (n-i)
print(f"\r\033[K[{bar}] {i/n*100:.02f}% ({i}/{n})", end="")
time.sleep(0.5)
出力イメージ
例4) プログレスバーの先頭の形状を変えてみた
# 0.5s毎に"#"表示と数値を更新
import time
n = 20
for i in range(n+1):
bar = '='*i + ("=" if i == n else ">") + "."*(n-i) # プログレスバーの先頭の表示を工夫
print(f"\r\033[K[{bar}] {i/n*100:.02f}% ({i}/{n})", end="")
time.sleep(0.5)
出力イメージ
例5) プログレスバーに色をつけてみた
ANSIエスケープコード\033[ n m(n:カラーを表す整数値、m: SGR (Select Graphic Rendition))で色を設定できます。
元の標準出力の色に戻すには\033[39mを設定します。
# 0.5s毎に黄色の"■"表示を更新
import time
n = 20
for i in range(n+1):
bar = '■'*i + "."*(n-i)
print(f"\r\033[K[\033[33m{bar}\033[39m] {i/n*100:.02f}% ({i}/{n})", end="")
time.sleep(0.5)
出力イメージ
環境
- OS: Ubuntu20.04LTS@WSL2
- Python3.11.2
まとめ
標準出力へ同じ行に出力するポイントは以下3点です。
- 出力後に改行コードを表示させない(print()の場合はend=””と設定)
- キャリッジリターン(CR, 行頭復帰:”\r”)を出力文頭に記述する
- カーソルのある行をクリア: ”\033[K”を記述(ANSI escape code)