【Python】wxPythonを使ってフォルダ転送(プログレスダイアログ、マルチスレッドの実装)

##プログレスダイアログとは？
ファイルの転送や、インストール時に出るアレです。
![computer_bar1_download.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/ec259e4b-f95e-e14b-c6a1-a9928fd59b9c.png)

wxPythonを使用して、ファイルの転送時に表示されるプログレスダイアログを作成します。

[前回](https://qiita.com/OKsaiyowa/items/3a1bfc6e885e264cf200)の記事の続きです。

##wx.ProgressDialog
`ProgressDialog`クラスを利用することでプログレスダイアログを表示できます。

クラスの引数は
	
**・title (string) –　ダイアログ左上に表示される文言**
**・message (string) –　実行時に表示される進捗メッセージ　(~％です、、、など)**
**・maximum (int) –　プログレスバーの最大値(何分割か)。デフォルトは100で、基本変えなくていいと思います。**
**・parent (wx.Window) –　ダイアログの親となるオブジェクトを指定。変えなくていいです。**
**・style (int) –　ダイアログのスタイルを変更。(後述)**

###Styleについて
ダイアログの表示に関して、オプションとして機能を追加することができます。
今回は下記機能を追加します。
**・wx.PD_AUTO_HIDE　–　プロセスが終了すると自動でダイアログが閉じる。
・wx.PD_CAN_ABORT　–　Closeボタンが表示されて、押下で中止。
・wx.PD_REMAINING_TIME　–　残り時間を表示。**

Styleに関して[公式ドキュメント](https://wxpython.org/Phoenix/docs/html/wx.GenericProgressDialog.html#styles-window-styles)に一覧があります。


```Python

        # プログレスダイアログ作成
        dlg = wx.ProgressDialog(
            title="コピー中",
            message="0/100",
            maximum=100,
            style=wx.PD_AUTO_HIDE | wx.PD_CAN_ABORT | wx.PD_REMAINING_TIME)
        
        # ダイアログ表示
        dlg.ShowModal()
```

##ProgressDialog.Update
`Update`メソッドを利用することで、プログレスバー、およびメッセージの更新を行うことができます。

```Python
        # プログレスダイアログ作成
        dlg = wx.ProgressDialog(
            title="コピー中",
            message="0/100",
            maximum=100,
            style=wx.PD_AUTO_HIDE | wx.PD_CAN_ABORT | wx.PD_REMAINING_TIME)
        
        # ダイアログ表示
        dlg.ShowModal()
        rate = 0

        # ループ処理
        while rate < 100:
            # 0.5秒待つ
            time.sleep(0.5)

            # 値の更新
            dlg.Update(value=rate, newmsg="%d/100" % rate + "%")

        dlg.Destroy()
```

##フォルダの容量比較
プログレスダイアログは用意できたので、次に用意するのはプロセスに応じて変化するプログレスバーです。

今回はフォルダの転送を目的としているので、
**・移動先のフォルダの容量
・移動するフォルダの容量**
この2つを比較します。

ドンピシャの記事[^1]があったのでまるまる使わせていただきました。

```Python
    # フォルダサイズ取得
    def get_dir_size(self, path="."):
        total = 0
        with os.scandir(path) as it:
            for entry in it:
                if entry.is_file():
                    total += entry.stat().st_size
                elif entry.is_dir():
                    total += self.get_dir_size(entry.path)
        return total
```
あとはサイズの結果を百分率で比較してあげればOKです。
先ほどのプログレスダイアログ更新の箇所に当てはめます。

```Python
    # ボタンのクリックでプログレスバー表示
    def progress(self, event):

        # プログレスダイアログ
        dlg = wx.ProgressDialog(
            title="コピー中",
            message="0/100",
            maximum=100,
            style=wx.PD_AUTO_HIDE | wx.PD_CAN_ABORT | wx.PD_REMAINING_TIME)

        dlg.ShowModal()
        rate = 0

        # ループ処理
        while rate < 100:
            # 0.5秒待つ
            time.sleep(0.5)

            # サイズから割合を算出
            rate = self.get_dir_size(
                self.old.GetLabel())/self.get_dir_size(self.old.GetLabel())*100

            # 値の更新
            dlg.Update(value=rate, newmsg="%d/100" % rate + "%")

        dlg.Destroy()
```

##マルチスレッド
"ダイアログの表示関連"と"ファイルの転送"を同一スレッド内で行うと
処理に耐えきれず`応答していません`というログが出てウィンドウがフリーズしてしまいました。

そこで、"ダイアログの表示関連"と"ファイルの転送"をスレッドで分けて行ったところ上手くいきました。

```Python
    def done(self,event):
        #別スレッドを生成する。
        thr = threading.Thread(target=self.replace_folder, args=(event,))
        thr_progress = threading.Thread(target=self.progress, args=(event,))
        thr.start()
        thr_progress.start()
```

別スレッドを作成し、実行するだけのメソッドを作りました。
これを実行ボタン押下時に呼べば複数のスレッドが作成、実行されます。




完成したダイアログはこちら。
![ProgressOption.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244071/fdf3b162-6b1b-0842-78ac-632c1cbe562c.png)


Pythonにはほとんど触れられていないのですが、今後はスクレイピングとかいう魔法も使えるように頑張りたいと思います。

[^1]: [参考リンク](https://note.nkmk.me/python-os-path-getsize/)