2011年6月7日

Python + wxPython で GUI アプリケーションを作る 7 ステップ

会社で PythonwxPython を使って簡単な GUI アプリケーションを作った。
案外調べながら作ると結構色々と大変だったので大まかにまとめておく。

1. Python をインストールする
Python は歴史的な事情からどれを入れるのか迷う。
Google App Engine を使うなら未だに 2.5 系じゃないと駄目だし、3 系はサードパーティ製のものを組み合わせて使う場合若干時期尚早な感じもある。今回の場合は wxPython が 2.7 系までしかまだ配布されていなかったので、Python 2.7 を選択した。

自分の環境に合わせたインストーラをダウンロードして簡単にインストールできるので特に問題になるようなことはないと思う。今回は特に 64bit 版である必要がなかったので 32bit 版を Windows7 64bit 版にインストールした。

2. wxPython をインストールする
wxPythonwxWidgets を Python から使えるようにしたもので、OS ネイティブな部品を活用するので見た目が独特になりにくいクロスプラットフォーム GUI ライブラリとして重宝されているように思う。
しかし元々 Python で作られたライブラリではないのでメソッド名などの命名規則は基本的に Python ソースコードとしてはかなり浮いてる感じになる(実害はないけど)。

色々あるのだが、さっき 32bit 版の Python 2.7 をインストールしたのでその時点で選択肢は2つ。更に今後は unicode 版に統一されていくらしいので unicode 版を選択することにした。つまり wxPython2.8-win32-unicode-py27 を選んだ。これもインストーラが提供されているので次へ次へで上手くいくはず。

3. GUI ビルダーの XRCed を使えるようにする
GUI アプリケーションは GUI の組み立てが果てしなく面倒臭いが、wxPython をインストールした際に標準で XRCed がインストールされる。他にも自分で探せば wxGlade とか wxFormBuilder とかまあ色々あって、特に wxFormBuilder なんかは Delphi に慣れた人には扱いやすそうな代物ではあるけど取りあえず最初から入ってるヤツを使うことにした。

が、どうもそのままでは起動させられないようだ。標準のインストール先であれば以下の場所にあるファイルでコメントアウトしなければいけない箇所があった。
C:\Python27\Lib\site-packages\wx-2.8-msw-unicode\wx\tools\XRCed\plugins\xh_wxlib.pyの20行目
#self.AddStyle('FPB_DEFAULT_STYLE', fpb.FPB_DEFAULT_STYLE)
ここだけ直せば、後は C:\Python27\Scripts\xrced.bat を実行すればエディタが起動するはず。起動にちょっと時間掛かるかも。

実際にモノ作ってからまとめとしてこの記事を書いているので、実のところ今では wxFormBuilder の方がよかったかなと思っている。

4. GUI を設計する
取りあえず会社で作ったのは2つのサーバに存在するファイルを SSH 経由で見比べるソフトだったので、接続設定を入力するためのダイアログが必要だった。そのためこういう感じにした。
XRCed で GUI を作って、まず xrc ファイルを保存する。その後に Python スクリプトファイルを生成するためにメニューから [Generate Python...] を選ぶと生成に関する設定項目などが現れる。
設定次第で色々な出力の仕方が出来るようだが、取りあえず xrc ファイルの内容自体を生成するスクリプトに埋め込める設定が便利そうなのでそれを使ったりした(ちなみに wxFormBuilder の場合はそもそも XRC ファイルを経由しないでフォームが生成できたりする。これはこれでいい)。
で、スクリプトファイルが生成されるので、これを使えばすぐにプログラムが作れるところまでいける。

間違えてはいけないのは生成されたファイル自体は書き換えるべきではない
まあちょっといじれば理由はすぐにわかるとは思うけど、プログラム弄る場合にGUIを変更しようとすると、そのたびに Python のソースコードファイルを生成しなおす必要がある。
直接書き換えていたらもちろん失われるので、基本的にこのソースコードには触らずに中で定義されているクラスを継承して使うようにするべき。

GUI の設計自体も Sizer の概念が理解できるまで苦労する。しかも wxStaticText が縦方向のセンタリングができないためそこにも Sizer を入れてセンタリングしたり、案外泥臭い真似をすることになった。
ただこれを覚えずに設計すると OS によって GUI 部品のサイズが違ったりするので簡単に悲惨な状態になる。

あと、wx.stc.StyledTextCtrl のように XRCed からは挿入できないコントロールもあった。
この辺は自分で継承したクラス内でコントロールを追加するようにスクリプトを書いた。

5. 処理を書く
GUI 設計とプログラム作りはある程度並行でやることになったりすると思うのでこの順番にあまり深い意味はないけどそろそろプログラムを書く頃合い。まあ自分が作りたいもの作ればいいと思うからそんなのは深く語る必要はないね?
# -*- coding: utf-8 -*-
import wx, test_xrc

class TestDialog(test_xrc.xrcTestDialog):
    pass

def main():
    app = wx.App()
    TestDialog(None)
    app.MainLoop()
動かしてないから間違ってるところあるかも知れないけどまあ書き始めはこんな感じでしょう。

6. There is no rule six.
ルールじゃねえというツッコミは把握しているため必要ありませんことよ。

7. 配布するには
他の人がこのプログラムを使うために同じ環境構築させるのはあまりに酷な話なので、この辺は上手く exe ファイルとかにまとめられるといい。
こういうのには py2exe とか py2app とか cx_Freeze とか色々あるのだが、cx_Freeze は手元の Norton がヤバいプログラムと誤認識したので今回は使わず、py2exe と py2app で Windows 用アプリケーションと Mac OSX 用アプリケーションとして生成した(そもそもコンパイル済みのスクリプト言語はアンチウィルスソフトからは解析しづらく、誤検出されてもおかしくはないわけで……)。

Mac OSX の app 形式の場合実態はディレクトリだからファイルが散らかる心配はないのだが、Windows の exe 形式の場合は対策しないとプログラム内で使ってるだけのアイコンファイルを外に配置するハメになってしまう。
この辺の対策は真面目に考えると結構面倒なのだが、wxPython は標準のインストール先に
C:\Python27\Lib\site-packages\wx-2.8-msw-unicode\wx\tools\img2py.py
というスクリプトを用意していて、これを使うと画像ファイルを Python スクリプトの中に埋め込むことができる。こうしておけばどこの環境でも同じコードで同じリソースが読める状態に持ち込めるので便利。
(ただし完全に埋め込む形になるのでコンパイル済みのものから取り出すのは微妙な感じに)

自分の場合は Windows と Mac OSX で動かすのが取りあえずの目標だったので、Windows らしいアプローチとしてリソースから読み出す手段を選択した。詳しいやり方に関してはこの辺に詳しく書いてある。
検索すると ctypes を使わずに win32api を使ってリソースを読むための API を呼ぶ例もあったのだが、ctypes を使うもののほうがポータブルな感じなのでこれを使わせてもらった。

出来上がったアプリケーションは zip 圧縮した状態で Windows 版が 6.4MB、Mac OSX 版が 16.8MB になった。少しでかいけどまあそれが問題になるような時代でもないかな。
Clip to Evernote