【Python】進捗・プログレスバーを表示する(print、sys.stdout)

Python文字列

プログラムの処理の進捗を数値(%表記など)やプログレスバー形式で表示させたい場合があります。
3rdParty製モジュールを使った本格的なプログレスバーもありますが、本記事では標準組込み関数を用いた方法についてまとめます。
ポイントは、「改行処理」、「キャリッジリターン」と「行クリア」です。

改行とキャリッジリターン

出力結果を毎回同じ位置に出力するためのポイントは、以下3つです。

  1. 改行処理
    • print() で出力する場合は(例えば)end引数=“”に設定
    • print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)

      引数objectsを標準出力(file=sys.stdout)に出力し、最後に引数endを表示します。(デフォルト設定は改行end=’\n’

    • sys.stdout.write() で出力する場合は改行の設定無し
  2. キャリッジリターン(CR, 行頭復帰)
  3. “\r”を出力文頭に記述

  4. カーソルのある行をクリア
  5. 前のタイミングよりも短い文字列を出力する場合、そのまま表示するとはみ出た分の文字列が残ってしまいます。
    こんな時は、出力文字列の先頭に“\033[K”ANSI escape code )を記述するとカーソルのある行を一旦クリアすることができます

    ※ANSI escape code

    • 033: ESC(エスケープシーケンス)の8進数表記
    • [ : CSI(Control Sequence Indicator)を表す。コマンド開始を示す
    • K : EL (Erase in Line) を表す。カーソル位置から行の末尾まで削除

具体例

例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までカウントアップされます)

#6

例2) かんたんプログレスバー 

# 0.5s毎に"#" 表示を更新
import time

for i in range(10):
    print(f"\r\033[K{'#' * i}", end="")
    time.sleep(0.5)

出力イメージ(”#”が伸びていきます)

######

例3) プログレスバー + 進捗(%表示など)

# 0.5s毎に"#"表示と数値を更新
import time

n = 20
for i in range(n):
    bar = '*' * i + " " * (n-i-1)
    print(f"\r\033[K[{bar}] {i/n*100:.02f}% ({i}/{n})", end="")
    time.sleep(0.5)

出力イメージ

[*************      ] 65.00% (13/20)

例4) プログレスバーの先頭の形状を変えてみた

# 0.5s毎に"#"表示と数値を更新
import time

n = 20
for i in range(n):
    bar = '='*i + ("=" if i == n-1 else ">")  + "."*(n-i-1)    # プログレスバーの先頭の表示を工夫
    print(f"\r\033[K[{bar}] {i/n*100:.02f}% ({i}/{n})", end="")
    time.sleep(0.5)

出力イメージ

[>...................] 0.00% (0/20)
    ↓ 
[============>.......] 60.00% (12/20)
    ↓ 
[====================] 95.00% (19/20)

例5) プログレスバーに色をつけてみた

ANSIエスケープコード“\033[ n m”(n:カラーを表す整数値、m: SGR (Select Graphic Rendition))で色を設定できます。
元の標準出力の色に戻すには\033[39mを設定します。

# 0.5s毎に黄色の"■"表示を更新
import time

n = 20
for i in range(n):
    bar = '■'*i + "."*(n-i-1)
	print(f"\r\033[K[\033[33m{bar}\033[39m] {i/n*100:.02f}% ({i}/{n})", end="")
    time.sleep(0.5)

出力イメージ

[■■■■■■■■■■■■.......] 60.00% (12/20)

環境

  • OS: Ubuntu20.04LTS@WSL2
  • Python3.9.7

まとめ

標準出力へ同じ行に出力するポイントは以下3点です。

  • 出力後に改行コードを表示させない(print()の場合はend=””と設定)
  • キャリッジリターン(CR, 行頭復帰:”\r”)を出力文頭に記述する
  • カーソルのある行をクリア: ”\033[K”を記述(ANSI escape code)

参考書籍

書籍でもう少し詳しく学びたい場合はこちらもどうぞ。筆者もかなり参考にさせてもらっています!

シェアする
ひびきをフォローする
Hbk project