ffmpegの処理をtkinterでGUI化（その1: プログレスバーとリアルタイム出力表示）

初投稿です．よろしくお願いします．    

＊この記事の続きは[こちら](https://qiita.com/ytakeda-paleo/items/c93ef9e1776414a0b263)


# 概要
- このようなプログレスバーを作る．
![Animation.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/736226/4938462d-6cd7-a7e1-2149-bef9b104f901.gif) 

# やりたいこと
- pythonからffmpegを使って動画を処理したい．
- GUI化したい．
## GUIの内容
- ボタンを押すと処理が開始する．
- 処理中はtkinterのプログレスバー(不確定モード）のウインドウがポップアップされる．
- 処理が完了するとプログレスバーのウインドウが自動的に閉じる．

# コード
## NGな例その１
```python
import os, subprocess
import tkinter as tk
from tkinter import ttk, messagebox

command = "ffmpeg hogehoge" # ffmpegの処理コマンド

def start():
    progress = tk.Toplevel()
    bar = ttk.Progressbar(progress,mode='indeterminate')
    bar.pack()
    bar.start()

    subprocess.run(command, shell=True)

    progress.destroy()

root = tk.Tk()
button = tk.Button(root, text = "start", command = start)
button.pack()

root.mainloop()
```
ffmpegの処理は始まるが，プログレスバーは表示されない．rootウインドウも応答なしになる．

## NGな例その２
- `subprocess.run`を非同期処理が可能な`subprocess.Popen`にするとどうなる？   
→ffmpegの処理開始と同時に`progress.destroy()`が実行されるので，プログレスバーは表示されない．そこで，同期待ちが必要．

## OKな例
```python
import os, subprocess
import tkinter as tk
from tkinter import ttk
import threading

command = "ffmpeg hogehoge" # ffmpegの処理コマンド

def start():
    progress = tk.Toplevel()
    bar = ttk.Progressbar(progress,mode='indeterminate')
    bar.pack()
    bar.start()

    def process():
        p = subprocess.Popen(command, shell=True)

        try:
            outs, errs = p.communicate()
        except subprocess.TimeoutExpired:
            pass
        else:
            p.terminate()
            progress.destroy()
    
    th1 = threading.Thread(target=process)
    th1.start()

root = tk.Tk()
button = tk.Button(root, text = "start", command = start)
button.pack()

root.mainloop()
```
### ポイント
- `subprocess.run`ではなく`subprocess.Popen`を使う．
- `threading`で別スレッドでffmpegを実行する．
- `outs, errs = p.communicate()`でffmpegの処理待ちを行う．

## 発展 
- 標準出力をプログレスバーウインドウにリアルタイム表示する（参考：[stackoverflow - FFMPEG and Pythons subprocess](https://stackoverflow.com/questions/1455532/ffmpeg-and-pythons-subprocess)）．
- 処理終了後にポップアップウインドウを表示する．
```python
import os, subprocess
import tkinter as tk
from tkinter import ttk, messagebox
import threading

command = "ffmpeg hogehoge" # ffmpegの処理コマンド

def start():
    progress = tk.Toplevel()
    progress.geometry("600x75")
    progress.title("processing...")
    info_text = tk.StringVar()
    info = tk.Label(progress, textvariable = info_text)
    info.pack(side = tk.TOP, anchor = tk.W)
    bar = ttk.Progressbar(progress,mode='indeterminate')
    bar.pack(side = tk.TOP, fill = tk.X)
    bar.start()

    def process():
        p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,shell=True,universal_newlines=True)
        
        for line in p.stdout:
            info_text.set(line)

        try:
            outs, errs = p.communicate()
        except subprocess.TimeoutExpired:
            pass
        else:
            p.terminate()
            progress.destroy()
            warnwindow = messagebox.showwarning("Done", "Animation saved!")

    th1 = threading.Thread(target=process)
    th1.start()


root = tk.Tk()
button = tk.Button(root, text = "start", command = start)
button.pack()

root.mainloop()
```

# 今後
- キャンセルボタンがあるとよい．
- 確定モードのプログレスバーと標準出力の進捗を同期させたい．


最後までお読みいただきありがとうございました．