Python + PySide + QtDesigner で 実行ファイルを作るまで

# はじめに
Python初心者がGUIつきのEXEファイルを作るまでの流れです。
もう既にごまんと記事がありそうな内容ですが、私的覚え書きとして残します :blush: 
OSはWindows10です。

# 環境作り
## 既存 Python のアンインストール
昔~~Mayaで使おうとしてやらなかった時に~~入れたpythonが既にPCにインストールされていたので、削除します。
普通にアプリケーションのアンインストールでいけます。
## Pythonを公式ページからインストール
[公式ページ](https://www.python.org/downloads/)からインストーラをダウンロードしてインストールします。
MayaのPythonが2.7.11らしいので、今回はそれに合わせました。
## パスを通す
環境変数にPythonのパスを通します。
Winキー → 「システム環境変数の編集」（**「環境変数の編集」ではない！**） → 環境変数 → 「システム環境変数」の「Path」を選択して「編集」
`C:\python27` と `C:\python27\scripts` を新規追加します。

## PySideをコマンドプロンプトからインストール
Winキー → 「cmd」　で開くコマンドプロンプトを使って `PySide`をインストールします。

```shell
python -m pip install -U PySide
```

pipをアップグレードしてください的なログが出たら指示通りにアップグレードします。

---

# QtDesignerを使ってGUI作成
`QtDesigner` を使うとGUI上でGUIを作れます。正直コードでUI作るのは ~~嫌い~~ ~~ストレスが溜まる~~ ~~HTMLで書きたい~~ 苦手なのでとても助かります :blush:
`C:\Python27\Lib\site-packages\PySide\designer.exe` にインストールされています。面倒なのでショートカットを作っておくのをオススメします。
`QtDesigner` の細かい使い方は省きますが、あとから段組みを変えるのは苦手なようなので、必要な要素は先にリストアップしておくと良いです。

![QtDesigner01](https://qiita-image-store.s3.amazonaws.com/0/308883/b94de20d-52d6-3239-ce68-74a135a2212b.png)
左のWidgetBoxから適当に要素を出して、
![QtDesigner02](https://qiita-image-store.s3.amazonaws.com/0/308883/457a2045-50e1-8975-ac01-328dafb3eb4e.png)
子レイアウト（今回はHorizontalLayout）をつけてから
![QtDesigner03](https://qiita-image-store.s3.amazonaws.com/0/308883/4c959faf-f596-6e15-4775-9eb4332c4a86.png)
一番親のレイアウトを右クリックで整える（今回はVerticalLayout）と
![QtDesigner04](https://qiita-image-store.s3.amazonaws.com/0/308883/8aa2021b-9a0c-771f-a0bd-ec9ae246c6ae.png)
さくっとできます。
buttonやlineEditなど、機能実装に使うパーツには右中段の `Property Editor` で `objectName` をつけておきましょう。
![QtDesigner05](https://qiita-image-store.s3.amazonaws.com/0/308883/ad33c551-4f65-4c85-2f75-cab6f2ffbc12.png)
機能はあとからコードで実装しますが、閉じるボタンは楽なので `QtDesigner` でつけちゃいます。
`Edit Signals/Slots` モードに切り替えてボタンをWindow内にドラッグし、
![QtDesigner06](https://qiita-image-store.s3.amazonaws.com/0/308883/f4f5b2bb-6af6-e865-3ada-612cac6d664e.png)
`clicked()` と `close()` を選んで完了です。

保存すると `*.ui` ファイルが出来上がります。

##pyside-uic で *.ui を *.py に変換
コマンドプロンプトで `*.ui` を `*.py` に変換します。

```shell
pyside-uic.exe ****.ui -o ****.py
```

毎回コマンドプロンプト叩くのは面倒くさいので、バッチファイルを作ると楽です。

```shell:pyside-ui2py.bat
@echo off
@setlocal enabledelayedexpansion

REM ドラッグ＆ドロップされたファイルをforでまわす
for %%A in (%*) do (
    REM 拡張子が.uiだったら処理続行
    if "%%~xA"==".ui" (
        REM 変数OutputFileにアウトプットしたいフルパスをセットする
        REM %%~dA = ドライブ名, %%~pA = パス, %%~nA = ファイル名
        set OutputFile="%%~dA%%~pA%%~nA.py"
        REM %hoge%とすると中身がなくなるので遅延環境変数 !hoge! を使う
        echo Convert to !OutputFile!
        C:\Python27\Scripts\pyside-uic.exe %%A -o !OutputFile!
    )
)
pause
```

下記記事を参考にさせていただきました。
[pyside-uicを使ったuiファイルをpyファイルに変換するバッチ - redglasses67 Jimdoページ](https://redglasses67.jimdo.com/2016/09/17/bat-pyside-ui2py/)

---

# 実装
## UIパーツに機能を実装
`QDialog` を継承したClassを作成し、init内でUIパーツに関数を紐づけます。

```python:template.py
# -*- coding: utf-8 -*-
from PySide.QtGui import *
from ui import Ui_Form

class MainUi(QDialog):
    def __init__(self, parent=None):
        super(MainUi, self).__init__(parent)
        self.ui = Ui_Form()
        self.ui.setupUi(self)

        # 実行ボタンに関数を紐づけ
        self.ui.submitButton.clicked.connect(self.submit)

        # 引き数付きで紐づけ(ラムダ)
        self.ui.toolButton.clicked.connect(lambda: self.calc(message))

    def submit(self):
        pass

    def calc(self, message):
        pass
```
（2行目は `*.ui` から変換した `*.py` の中身を参考に変えてください。）

## 文字化けする。。。
![文字化け](https://qiita-image-store.s3.amazonaws.com/0/308883/ec024d32-16e4-adb1-296e-08e0a53d91e5.png)
実装中、 `QMessageBox` などで文字を表示しようとすると文字化けしてしまいました。。 :sob:
どうやら、 `QString` と言う独自のClassで文字列を扱ってる？っぽいのが影響しているっぽい :thinking: 
変換をかけてあげる必要があります。

```python:encodeSample.py
STRING.decode("utf-8")
# または
unicode(STRING, "utf-8")
```

2時間くらいハマってしまった。。

---

# exe化
## pyinstaller をインストール
コマンドプロンプトで `pyinstaller` をインストールします。

```shell
pip install pyinstaller
```

## pyinstaller でビルドする
```shell
pyinstaller ****.py --onefile --clean --noconsole
```
**コマンドを実行する前に、 `cd` で対象の `.py` ファイルのある階層まで移動してから実行しましょう！**
当たり前の事かもしれませんが、これに気づかず、実行してもエラーを吐くだけのexeを量産してしまいました。。

|option|役割|
|:--|:--|
|--onefile|1つのexeファイルのみビルドする|
|--clean|ビルド前にクリーン|
|--noconsole|実行時にコンソールウィンドウを出なくする|

## なんか重い
作ったのは簡単なファイル整理ツールでしたが、14MBくらいになりました。重い :thinking: 
でも今回はスルーです :innocent: 

---

# おわりに
無事実行ファイルを作ることができました :grinning:
今まではMayaとかUnityとか、ソフトの中でスクリプトを書いていたので、はじめて実行ファイルでツールが出来たので嬉しいです :blush: