Pythonで `light-progress` を使って進捗(プログレスバー)を表示

Pythonで進捗表示といえば[tqdm](https://github.com/tqdm/tqdm)が有名だと思いますが、今回は[light-progress](https://github.com/itkr/light-progress)というライブラリを紹介したいと思います。

## 特徴

`light-progress` は依存ライブラリを必要とせず軽量なライブラリを目指しており、Python2でも3でも動くようになっています。LinuxやMacではカラー表示に対応しており進行中・完了・エラー終了でそれぞれカラーが変わります。自分の環境では Jupyter Notebook でも動きました。

ただ `light_progress` というようにモジュール名に `_` が含まれているのがイケてなかったり関数名が微妙だったりというのは気になっています。ロードマップにはコマンドラインだけでなくGUIのためのツールやWindowsでのカラー表示があります。

<img width="830" alt="スクリーンショット 2018-12-04 20.08.13.png" src="https://qiita-image-store.s3.amazonaws.com/0/14918/dc0f6a8a-09d9-70ee-2029-b8dc82cf6885.png">

## インストール

```bash
pip install light-progress
```

## 使い方

```python
from time import sleep
from light_progress.commandline import ProgressBar
```

### 1. 愚直な使い方

`start()` で進捗管理を開始し、 `forward()` もしくは `update(i)` 進捗を進めます。最後に `finish()` を呼び出して終了します。これは最も直接的な使い方ですがかなり不便です。

```python
n = 42
progress_bar = ProgressBar(n)
progress_bar.start()

for item in range(n):
    sleep(0.01)
    progress_bar.forward()

progress_bar.finish()
```

### 2. 1よりちょっと手間を省く

`start()` や `finish()` を自分で呼び出すのは面倒なので `with` 文を使って省略できます。

```python
n = 42
with ProgressBar(n) as progress_bar:
    for item in range(n):
        sleep(0.01)
        progress_bar.forward()
```

### 3. イテレーションをライブラリに移譲

2も面倒なのでイテレーションをライブラリに移譲できます。行いたい処理は引数で渡します。

```python
ProgressBar.iteration(range(42), lambda item: sleep(0.01))
```

### 4. ジェネレータを使う

3はコードを短くできますが、引数に関数や `lambda` を入れるのがあまり直感的ではありません。ジェネレータ関数を使ってイテレーション自体は移譲せずに `forward()` など面倒な処理だけをライブラリ側に委ねます。これが最もシンプルで使いやすい形だと思われます。

```python
for item in ProgressBar.generation(range(42)):
    sleep(0.01)
```

## 表示のカスタマイズ

表示は `widget` を使ってカスタマイズすることができます。各 `widget` や文字列をリストにして `widgets` として渡すと適応できます。

```python
from light_progress import widget
```

```python
widgets = [widget.Bar(bar='=', tip='-'),
           widget.Percentage(),
           widget.Num()]

ProgressBar.iteration(
    range(42), lambda item: sleep(0.01), widgets=widgets)

# [===============-...............] 50% (21/42)
```

```python
widgets = [widget.Percentage(),
           widget.Num(),
           'loading...',
           widget.Bar(bar='#', tip='>')]

ProgressBar.iteration(
    range(42), lambda item: sleep(0.01), widgets=widgets)

# 50% (21/42) loading... [###############>...............]
```

### 独自widget

`progress_bar.widget.Widget` を継承してクラスを作り、 `get_str` を実装すれば独自のwidgetが作成できます。 `get_str` の `context` には `progress_bar.core.Progress` を継承したクラスのインスタンス( `progress_bar.commandline.ProgressBar` など)が入ってくるので、それらのプロパティを表示項目に使用できます。


## 表示フォーマット

`widget` で表示をカスタマイズできますが、`format_str` に `'{}'` の入った文字列を渡すことで各widget間や配置もカスタマイズできます。なお `widgets` の項目数と `format_str` に含まれる `'{}'` の数は一致している必要があります。

```python
format_str = '{} {} ({})'

widgets = [widget.Bar(), widget.Percentage(), widget.Num()]
ProgressBar.iteration(
    range(100),
    lambda item: sleep(0.01),
    widgets=widgets,
    format_str=format_str)

# [███████████████████████████████] 100% (100/100)
```

```python
format_str = '{} *** {} *** ({})'

widgets = [widget.Bar(), widget.Percentage(), widget.Num()]
ProgressBar.iteration(
    range(100),
    lambda item: sleep(0.01),
    widgets=widgets,
    format_str=format_str)

# [███████████████████████████████] *** 100% *** (100/100)
```

## くるくる回るやつ

`ProgressBar` だけでなく　`Loading` くるくる回る表示もできます。

```python
from light_progress.commandline import Loading
```

```python
Loading.iteration(range(100), lambda item: sleep(0.01))
```

## 余談

ちなみに[light-progress](https://github.com/itkr/light-progress)は[tqdm](https://github.com/tqdm/tqdm)というものがあると知らなかったときに、自分の別プロジェクトで進捗管理していた部分を独立させてPyPIに公開したものです。せっかくなので育てて行きたいと思います。