PythonでGUI(WxFormBuilder)

<img width="1038" alt="スクリーンショット 2018-02-16 15.01.02.png" src="https://qiita-image-store.s3.amazonaws.com/0/76058/4b4069a8-6204-2442-1917-4cfb6c1a2b9e.png">

PythonのGUI作成ツールは色々ありますが、ライセンス的に使いやすいWxPythonを使うことが多いです。
しかしコードを直接記述する方法は時間が非常にかかるためオススメ出来ません。
WxFormBuilderは、そんなコードの状態をわかりやすく編集可能でフリーで使用出来るためオススメします。

## マルチプラットフォーム

Windows、MacOS、Linuxでそれぞれ動作するマルチプラットフォームRADツールです。
今回はMac版を使用してますが、他のプラットフォームでも操作はほとんど変わらないと思います。

コードのエディット機能はついてませんので、他のエディタと併用して使用するのが基本になります。

## ダウンロードとインストール

GitHubにあるリリース済みのものを選択してください。

> 公式ページの[GitHub Releases](https://github.com/wxFormBuilder/wxFormBuilder/releases)をクリックしてプラットフォームに合わせたアプリケーションをダウンロード。
> https://github.com/wxFormBuilder/wxFormBuilder

<img width="551" alt="スクリーンショット 2018-02-16 13.15.42.png" src="https://qiita-image-store.s3.amazonaws.com/0/76058/0b60b858-d5e9-c097-a049-52abe15b842e.png">

## 基本的な使い方

起動すると、下のような画面になると思います。（環境により若干の差はありますが）

<img width="834" alt="スクリーンショット 2018-02-16 13.27.57.png" src="https://qiita-image-store.s3.amazonaws.com/0/76058/831bffb1-9c0a-3541-8e4a-95751d10757f.png">

### フォームを貼り付ける

とりあえず、基盤となるフォームを貼り付けましょう。Component Paletteの「Forms」というタブを選択して現れたアイコン群の一番左をクリックしましょう。

<img width="835" alt="スクリーンショット_2018-02-16_13_29_362.png" src="https://qiita-image-store.s3.amazonaws.com/0/76058/64f68384-11b9-b420-ddb1-7ce313b030bd.png">

### Sizer

フォームを貼り付けてもそのままではボタンなどは配置出来ません。Sizerを貼り付けましょう。
Sizerは画面のどこにどのようにものを貼り付けるのかを決めるためのオブジェクトで、
Wxはその仕様上、Visual Stuido等のように自由にオブジェクトを貼り付けたりが出来ません。
Wordのような感覚に近いかもしれませんが、とにかくSizerだらけになって結構ゴチャゴチャします。

<img width="937" alt="スクリーンショット_2018-02-16_13_37_16.png" src="https://qiita-image-store.s3.amazonaws.com/0/76058/9693fd33-0091-ed4f-41f9-1464e93deb8b.png">

試しにもう一つSizerをいれてみます。
Sizerの中にSizerが入ってる状態にしてください。

<img width="188" alt="スクリーンショット 2018-02-16 13.43.02.png" src="https://qiita-image-store.s3.amazonaws.com/0/76058/aef9e0e8-12ad-1e61-27c7-a2db6f9cc735.png">

そして、内側のSizerにボタンを３つ入れてみましょう。
Component PaletteのCommonの一番左側にボタンツールがありますので３回クリックしてみましょう。
ボタンが縦に３つ並んだかと思います。

<img width="935" alt="スクリーンショット 2018-02-16 13.44.08.png" src="https://qiita-image-store.s3.amazonaws.com/0/76058/406853f7-b35b-3303-8c8e-a4bfe7d057ef.png">

これを横並びにしてみます。

ObjectTreeから「bSizer2」をクリックして右側のPropertiesタブにあるorientの項目をwxHORIZONTALに変更しましょう。
ボタンが横並びになったかと思います。
このようにSizerを並べたりSizerの属性を変更させたりすることでレイアウトを指定するのが基本になります。

<img width="937" alt="スクリーンショット_2018-02-16_13_45_392.png" src="https://qiita-image-store.s3.amazonaws.com/0/76058/e4dd17a0-9949-b105-4840-e94c35ee9288.png">

### イベントを設定

ObjectTreeかEditorからボタンを選択してEventsタブを選択し、OnButtonClickの文字をダブルクリックするか、直接イベント名を記述するとイベントが生成されます。
イベントの名前は自由に変えられますが、今回は変更無しにします。

<img width="934" alt="スクリーンショット_2018-02-16_13_49_512.png" src="https://qiita-image-store.s3.amazonaws.com/0/76058/82cbb75b-04a1-558d-20ad-cb843238df7c.png">

### 一旦保存

適当なところに保存しておきましょう。

## 出力

### ファイルについて

WxFormBuilderからPythonのファイルを書き出すのですが、その際に出力するのは２つのクラスファイルになります。

#### 親フレームクラス

親フレームクラスはWxFormBuilderで作ったレイアウトそのものを保存しているクラスです。
WxFormBuilderで何かしら変更があった場合、このクラスの内容は全て上書きされます。
したがって、このクラスに何かを手書きするのはやめましょう。

#### 子フレームクラス

子フレームクラスは親クラスを継承したクラスです。
WxFormBuilderで書き出すのは多くの場合最初の1回だけになりますので、このクラス内にボタンの動作や他の内容を記述します。
WxFormBuilderで変更があっても、ボタンの消去等の操作が入らない限り、このクラスは書き換えること無くそのまま使えます。

### 使う言語を指定

WxはPython以外の言語にも対応しています。
標準はC++となっているので、これをPythonに変更します。
ObjectTreeから「MyProject1」を選択して、ObjectPropertiesから code_generationという項目を見つけて、そこの三角をクリックして展開し、C++のチェックを外してPythonにチェックを入れます。

<img width="1038" alt="スクリーンショット_2018-02-16_14_07_382.png" src="https://qiita-image-store.s3.amazonaws.com/0/76058/001446a6-1e89-abc5-da58-c58043a9e267.png">

### 親フォームクラスのファイル名

ObjectTreeから「MyProject1」を選択して、ObjectPropertiesから、fileの項目に適当な名前を付けておきます。
「MyClass」と付けておきました。

### 親フレームクラスを書き出し

メニューからFile->GenerateCodeを選択すると、保存したWxFormBuilderと同じ階層にファイルが生成されます。
WxFormBuilderで何かしら変更したときは、この方法で上書きしていきます。

<img width="352" alt="スクリーンショット 2018-02-16 14.05.04.png" src="https://qiita-image-store.s3.amazonaws.com/0/76058/8728df75-59c3-6ee8-df49-e68a0671bd65.png">

### 子フレームクラスを書き出し

子フレームクラスを書き出しは何か理由が無い限りは１回だけ行います。

メニューからTools->Generate Inherited Classを選択します。

<img width="226" alt="スクリーンショット 2018-02-16 14.11.47.png" src="https://qiita-image-store.s3.amazonaws.com/0/76058/024a8495-35be-dc15-539b-21b37e8556eb.png">

適当に名前を付けて、MyFrame1にチェックを入れたらOK

<img width="496" alt="スクリーンショット 2018-02-16 14.15.18.png" src="https://qiita-image-store.s3.amazonaws.com/0/76058/1055b0b4-da8d-df2e-4a26-7924a9dff61e.png">

出力されたファイルの「MyProject1MyFrame1.py」が子クラスですので、ここの内容を変えていきましょう。
今回はボタン１を押したら**ボタンが押された**とボタン表示が変わるだけの単純なスクリプトにしました。

```python:MyProject1MyFrame1.py
....

	# Handlers for MyFrame1 events.
	def m_button1OnButtonClick( self, event ):
		# TODO: Implement m_button1OnButtonClick
		# 下を追記
		self.m_button1.SetLabel('ボタンが押された')
		pass

.....
```

## 仮想環境上で試す

windowsならコマンドプロンプト、macならターミナルで下記を実行

```batch:commandline
conda create -n wxenv python=3.6.3 
# Anacondaのセットで作成する場合
# conda create -n wxenv python=3.6.3 anaconda
# 作った環境を確認するときは
# conda info -e
# conda list -n wxenv
# 作った仮想環境を削除する時は
# conda remove -n wxenv --all

# Windowsでアクティブ化
activate wxenv
# 非アクティブ化
# deactivate

# Macでアクティブ化
source activate wxenv
# 非アクティブ化
# source deactivate

# WxPythonをインストール
pip install wxpython
```

## ファイルを作成

作ったクラスを正しく表示させるために、同じディレクトリに下記が記述されたmain.pyを作り、pythonでこのファイルを実行させるようにします。

```python:main.py
#! env python
# -*- coding: utf-8 -*-

import os
import sys
import wx
from MyProject1MyFrame1 import MyProject1MyFrame1

if __name__ == '__main__':
    app = wx.App(False)
    frame = MyProject1MyFrame1(None)
    frame.Show(True)
    app.MainLoop()

```

## ファイルを実行

```batch:commandline
# cd (main.pyファイルを保存した場所)
cd /Users/user/Desktop/test

# windows
python main.py

# Mac(Mac版はGUIが伴う場合pythonではなくpythonwを使用する)
pythonw main.py
```

<img width="392" alt="スクリーンショット 2018-02-16 15.36.22.png" src="https://qiita-image-store.s3.amazonaws.com/0/76058/cbd691b6-fccc-cbb8-2927-3eddcf4af227.png">

## 最後に

Sizerは分かりにくくて制御しにくいですが自分の思ったようになるまで、とにかく色々試してみることが大事だと思います。

## WxPythonとPyInstaller

[以前にWxPythonとPyInstallerに関して記事にしています。](https://qiita.com/mm_sys/items/a7690dface9727704143)