目次
1. はじめに †
1.1 概要 †
Pythonは手軽に書けて、本格的なプログラミングにも使える、便利な言語です。しかし、標準モジュールとしてGUIツールキットが提供されていないために、WindowsアプリなどのGUIを持ったアプリケーションを作成するには、少し敷居が高くなってしまいます。ここでは、wxPythonというツールキットを導入することで、PythonによるGUIアプリケーションを手軽に作ってみようと思います。
動作確認に使用したバージョンは以下のとおりです。
- Python 2.6.6
- wxPython 2.8.11
1.2 対象 †
Pythonによる開発経験のある方を対象としています。Pythonプログラミングの基礎や開発環境の作り方、使い方は特に説明しません。既にPythonとwxPythonがインストールされていることを前提としています。
1.3 wxPythonとは †
wxPythonは、Pythonから利用できるGUIツールキットです。その中身は、C++で書かれたwxWidgetsというライブラリをPythonのモジュールとしてラッピングしたものです。wxWidgetsはクロスプラットフォームのライブラリですので、wxPythonもWindows、Linux、MacOSなど環境で利用することができます。
今回はWindows環境で説明していきますが、インストールなどが済んで、開発環境ができあがってしまえば、基本的には他のOS上でも同じように作業できると思います。
2. wxPythonを使ってみる †
2.1 ウィンドウを表示する †
では実際にwxPythonでGUIアプリケーションを作ってみましょう。最初に作るのはウィンドウを表示するだけのアプリケーションです。
ソースコードは以下のようになっています。
- sample_2_1.py
import wx
class SampleApp(wx.App):
def OnInit(self):
self.init_frame()
return True
def init_frame(self):
self.frm_main = wx.Frame(None)
self.frm_main.SetTitle("Hello, wxPython!")
self.frm_main.SetSize((400, 200))
self.frm_main.Show()
if __name__ == "__main__":
app = SampleApp(False)
app.MainLoop()
sample_2_1.pyは標準的なwxPythonアプリケーションの雛形です。
まず、wx.Appを継承したSampleAppクラスを定義しています。wx.AppはwxPythonプログラムに必須のアプリケーションクラスです。SampleAppではOnInit()メソッドをオーバーライドしています。このメソッドではウィンドウを作成・表示し、Trueを返します。標準的なウィンドウはwx.Frameとして用意されています。
このスクリプトを実行すると、SampleAppのオブジェクトが生成され、MainLoop()メソッドが呼ばれます。wx.AppのMainLoop()メソッドでは、内部的にOnInit()が呼び出されます。OnInit()メソッドでウィンドウが作成されたあとは、ウィンドウイベントを処理するループに入ります。ウィンドウが閉じられるまでMainLoop()はブロックします。
2.2 コントロールを配置する †
ウィンドウにコントロールをいくつか配置してみましょう。
ソースコードは以下のようになっています。
- sample_2_2.py
import wx
class SampleApp(wx.App):
def OnInit(self):
self.init_frame()
return True
def init_frame(self):
self.frm_main = wx.Frame(None)
self.sizer = wx.BoxSizer()
self.frm_main.SetSizer(self.sizer)
self.txt_title = wx.TextCtrl(self.frm_main)
self.sizer.Add(self.txt_title, 1, wx.ALIGN_CENTER_VERTICAL)
self.btn_submit = wx.Button(self.frm_main)
self.btn_submit.SetLabel("Submit")
self.sizer.Add(self.btn_submit, 0, wx.ALIGN_CENTER_VERTICAL)
self.frm_main.SetTitle("sample_2_2")
self.frm_main.SetSize((400, 200))
self.frm_main.Show()
if __name__ == "__main__":
app = SampleApp(False)
app.MainLoop()
ウィンドウにwx.TextCtrlとwx.Buttonを配置してみました。wxPythonではwx.Frameに直接、コントロールを配置することはできません。必ずwx.Sizerを使用します。wx.Sizerはコントロールのコンテナで、レイアウトマネージャとして動作します。wx.Frameにwx.Sizerをセットし、wx.Sizerの上にコントロールを配置していくイメージになります。sample_2_2ではwx.BoxSizerを使って、一列にコントロールを並べています。
2.3 イベント処理 †
コントロールを操作したときのイベント処理を追加してみましょう。sample_2_3は、テキストコントロールにテキストを入力してボタンを押すと、ウィンドウのタイトルが変更されるというサンプルアプリケーションになっています。
ソースコードは以下のようになっています。
- sample_2_3.py
import wx
class SampleApp(wx.App):
def OnInit(self):
self.init_frame()
return True
def init_frame(self):
self.frm_main = wx.Frame(None)
self.sizer = wx.BoxSizer()
self.frm_main.SetSizer(self.sizer)
self.txt_title = wx.TextCtrl(self.frm_main)
self.sizer.Add(self.txt_title, 1, wx.ALIGN_CENTER_VERTICAL)
self.btn_submit = wx.Button(self.frm_main)
self.btn_submit.SetLabel("Change Title")
self.btn_submit.Bind(wx.EVT_BUTTON, self.on_submit)
self.sizer.Add(self.btn_submit, 0, wx.ALIGN_CENTER_VERTICAL)
self.frm_main.SetTitle("sample_2_3")
self.frm_main.SetSize((400, 200))
self.frm_main.Show()
def on_submit(self, event):
self.frm_main.SetTitle(self.txt_title.GetValue())
self.txt_title.SetValue("")
if __name__ == "__main__":
app = SampleApp(False)
app.MainLoop()
基本的な構造はsample_2_2と同じで、見た目の違いはほとんどありません。SampleAppにon_submit()メソッドを追加し、39行目でwx.ButtonにBindしています。ボタンが押されるとwx.EVT_BUTTONイベントが発生し、on_submit()が呼ばれます。
3. wxGladeによるGUIの構築 †
3.1 wxGladeとは †
これまで見てきたように、テキストエディタだけでも、GUIプログラミングは可能です。ですが、やはりGUIコントロールの配置などは、VisualBasicなどのような見た目に分かりやすい開発環境で開発したくなると思います。そこで、GUIコントロールの配置を楽にできるwxGladeを使った開発をしてみます。
今回使用したのはwxGlade v0.6.3です。
wxGladeはGUIコントロールをマウスによるドラッグアンドドロップで配置することができるツールです。作成したGUIの配置データから、Pythonのソースコードを出力することも可能ですが、おすすめはXRC(XML)出力です。
ソースコードで出力した場合、イベントハンドラなどを追加するために、そのソースコードを編集したくなるのですが、GUIの配置変更などのために、wxGladeからソースコードを再出力すると追加したイベントハンドラなどは消えてしまいます。そこで、wxGladeから出力するのはXRC形式の純粋な配置情報のみにしておき、イベントハンドラなどのコードは別のソースファイルに分離することで、それぞれ個別に開発を進めることができます。
3.2 wxGladeの使い方 †
wxGladeを起動すると、3つのウィンドウが表示されます。
メインウィンドウにはFrameやコントロールなど、配置可能なGUIのパーツが並んでいます。ツリーウィンドウにはGUIの階層構造が表示されます。プロパティウィンドウでは選択されているコントロールのパラメタなどを設定することができます。
各ウィンドウの具体的な使い方などはこのあと、実際の作業を通して見ていきたいと思います。
3.3 ウィンドウを表示する †
やはり最初はウィンドウを表示するだけのアプリケーションをつくるところから始めてみましょう。
実行したときの見た目はsample_2_2とほとんど同じです。このアプリケーションを作成する手順を見ていきましょう。まずはwxGladeでGUIの配置情報を生成します。
wxGladeを起動したら、まずプロパティ画面で以下の基本設定を変更します。
- 出力形式をXRCに変更する。
- EncodingをUTF-8に変更する。
- 出力するファイル名を設定する。
基本設定が終わったら、さっそくwx.Frameを追加してみましょう。メイン画面の「Add a Frame」ボタンを押すと、フレームクラスを選択するダイアログが表示されるので、wxFrameを選択してOKを押しましょう。
するとツリー画面にframe_1が追加され、新たにデザイン画面が表示されます。
ツリー画面に追加されたframe_1を選択すると、プロパティ画面でこのFrameのプロパティを編集できるようになります。今回はName属性をframe_mainと編集しておきましょう。
デザイン画面はここで定義しているウィンドウを表示したときのイメージになります。
ウィンドウタイトルなどの細かい設定はソースコードのほうで行うので、今回はこのままXRCを生成します。メイン画面のメニューから「Generate Code」を選択し、XRCファイルを出力します。出力先は先ほどプロパティ画面で設定した場所になります。
出力したXRCファイルは以下のようになります。
sample_3_3.xrcに出力したウィンドウを表示するためのソースコードは以下のようになります。
- sample_3_3.py
import wx
from wx import xrc
XRC_FILE = "sample_3_3.xrc"
class SampleApp(wx.App):
def OnInit(self):
self.res = xrc.XmlResource(XRC_FILE)
self.init_frame()
return True
def init_frame(self):
self.frm_main = self.res.LoadFrame(None, "frame_main")
self.frm_main.SetTitle("sample_3_3")
self.frm_main.SetSize((400, 200))
self.frm_main.Show()
if __name__ == "__main__":
app = SampleApp(False)
app.MainLoop()
まず、8行目でxrc.XmlResourceとして先ほどのXRCファイルを読み込んで、リソースをオブジェクト化しています。13行目でLoadFrame()メソッドを使ってリソースからwx.Frameの情報をロードしています。ロードする際に必要なパラメタはFrameのName属性です。このようにwxGladeでGUIを構築するときに、コントロールなどのNameを分かりやすくつけておくことで、ソースコードを記述しやすくなります。
3.4 コントロールを配置する †
続いてwxGladeでコントロールを配置する手順を見てみます。sample_3_3で作成したウィンドウにwx.TextCtrlとwx.Buttonを配置します。
コントロールの配置にwx.Sizerが必要なことは先ほど述べましたが、sample_3_3で作成したframe_mainには既にsizerが設定されています。ところが、このsizerはコントロールを縦に配置していくwx.BoxSizerです。今回はコントロールを横に配置していきたいので、ひとまずsizerをツリー画面から削除します。
続いて、メイン画面から「Add a BoxSizer」を選択し、frame_mainのデザイン画面でsizerを追加します。この時、OrientationをHorizontalに、Slotsを2に設定しておきます。
先ほどと同じようにメイン画面から「Add a TextCtrl」を選択し、ウィンドウの左側にwx.TextCtrlを配置します。wx.Buttonも同様にしてウィンドウの右側に配置します。
コントロールのName属性などを設定し、sample_3_4.xrcとして出力します。
- sample_3_4.xrc
<?xml version="1.0" encoding="UTF-8"?>
<!-- generated by wxGlade 0.6.3 on Wed Nov 16 22:15:53 2011 -->
<resource version="2.3.0.1">
<object class="wxFrame" name="frame_main">
<style>wxDEFAULT_FRAME_STYLE</style>
<size>400, 300</size>
<title>main_frame</title>
<object class="wxBoxSizer">
<orient>wxHORIZONTAL</orient>
<object class="sizeritem">
<option>1</option>
<flag>wxALIGN_CENTER_VERTICAL</flag>
<object class="wxTextCtrl" name="text_input">
</object>
</object>
<object class="sizeritem">
<flag>wxALIGN_CENTER_VERTICAL</flag>
<object class="wxButton" name="button_submit">
<label>Submit</label>
</object>
</object>
</object>
</object>
</resource>
このウィンドウを表示するソースコードですが、リソースを読み込んで表示するだけですので、実はsample_3_3とほとんど変わりません。
- sample_3_4.py
import wx
from wx import xrc
XRC_FILE = "sample_3_4.xrc"
class SampleApp(wx.App):
def OnInit(self):
self.res = xrc.XmlResource(XRC_FILE)
self.init_frame()
return True
def init_frame(self):
self.frm_main = self.res.LoadFrame(None, "frame_main")
self.txt_input = xrc.XRCCTRL(self.frm_main, "text_input")
self.btn_submit = xrc.XRCCTRL(self.frm_main, "button_submit")
self.frm_main.SetTitle("sample_3_4")
self.frm_main.SetSize((400, 200))
self.frm_main.Show()
if __name__ == "__main__":
app = SampleApp(False)
app.MainLoop()
このようにアプリケーションの動作を変更せずに、コントロールの追加などをおこなうだけならば、ソースコードの変更は必要なくなるというメリットがあります。
3.5 イベント処理 †
sample_3_4にイベント処理を追加します。動作はsample_2_3と同じで、TextCtrlにテキストを入力し、Buttonを押すと、Frameのタイトルが変更されます。
GUIの配置には変更がないので、XRCファイルはsample_3_4とほとんど変わりません。ボタンのラベルが少し変わった程度です。
- sample_3_5.xrc
<?xml version="1.0" encoding="UTF-8"?>
<!-- generated by wxGlade 0.6.3 on Wed Nov 16 22:16:28 2011 -->
<resource version="2.3.0.1">
<object class="wxFrame" name="frame_main">
<style>wxDEFAULT_FRAME_STYLE</style>
<size>400, 300</size>
<title>main_frame</title>
<object class="wxBoxSizer">
<orient>wxHORIZONTAL</orient>
<object class="sizeritem">
<option>1</option>
<flag>wxALIGN_CENTER_VERTICAL</flag>
<object class="wxTextCtrl" name="text_title">
</object>
</object>
<object class="sizeritem">
<flag>wxALIGN_CENTER_VERTICAL</flag>
<object class="wxButton" name="button_submit">
<label>ChangeTitle</label>
</object>
</object>
</object>
</object>
</resource>
ソースコードは以下のようになっています。
- sample_3_5.py
import wx
from wx import xrc
XRC_FILE = "sample_3_5.xrc"
class SampleApp(wx.App):
def OnInit(self):
self.res = xrc.XmlResource(XRC_FILE)
self.init_frame()
return True
def init_frame(self):
self.frm_main = self.res.LoadFrame(None, "frame_main")
self.txt_title = xrc.XRCCTRL(self.frm_main, "text_title")
self.btn_submit = xrc.XRCCTRL(self.frm_main, "button_submit")
self.btn_submit.Bind(wx.EVT_BUTTON, self.on_submit)
self.frm_main.SetTitle("sample_3_5")
self.frm_main.SetSize((400, 200))
self.frm_main.Show()
def on_submit(self, event):
self.frm_main.SetTitle(self.txt_title.GetValue())
self.txt_title.SetValue("")
if __name__ == "__main__":
app = SampleApp(False)
app.MainLoop()
コントロールをXRCのリソースから読み込むところが違うだけで、イベント処理自体はsample_2_3と同じように、Bind()メソッドとイベントハンドラで記述できます。
XRCリソースからコントロールを読み込むにはxrc.XRCCTRL()を使います。必要なパラメタは親コントロールとName属性です。
3.6 コントロールの配置を変更する †
GUIの配置情報をXRCに分割することによって、ソースコードを変更せずにアプリケーションの見た目を変更することができるようになります。試しにsample_3_5を少し改造して、コントロールの配置だけを変えてみましょう。
wx.BoxSizerの配置方向をHORIZONTALからVERTICALに変更し、コントロールが縦方向に並ぶようにしました。また、そのままではコントロール間の隙間がなく見づらかったので、wx.TextCtrlとwx.ButtonにBorderを設定して見やすくしました。
sample_3_6のXRCファイルは以下のようになっています。
- sample_3_6.xrc
<?xml version="1.0" encoding="UTF-8"?>
<!-- generated by wxGlade 0.6.3 on Fri Nov 25 13:03:14 2011 -->
<resource version="2.3.0.1">
<object class="wxFrame" name="frame_main">
<style>wxDEFAULT_FRAME_STYLE</style>
<size>400, 300</size>
<title>main_frame</title>
<object class="wxBoxSizer">
<orient>wxVERTICAL</orient>
<object class="sizeritem">
<flag>wxALL|wxALIGN_CENTER_VERTICAL</flag>
<border>10</border>
<object class="wxTextCtrl" name="text_title">
<size>200, 22</size>
</object>
</object>
<object class="sizeritem">
<flag>wxALL|wxALIGN_CENTER_VERTICAL</flag>
<border>10</border>
<object class="wxButton" name="button_submit">
<label>ChangeTitle</label>
</object>
</object>
</object>
</object>
</resource>
ソースコードは以下のようになっています。
- sample_3_6.py
import wx
from wx import xrc
XRC_FILE = "sample_3_6.xrc"
class SampleApp(wx.App):
def OnInit(self):
self.res = xrc.XmlResource(XRC_FILE)
self.init_frame()
return True
def init_frame(self):
self.frm_main = self.res.LoadFrame(None, "frame_main")
self.txt_title = xrc.XRCCTRL(self.frm_main, "text_title")
self.btn_submit = xrc.XRCCTRL(self.frm_main, "button_submit")
self.btn_submit.Bind(wx.EVT_BUTTON, self.on_submit)
self.frm_main.SetTitle("sample_3_6")
self.frm_main.SetSize((400, 200))
self.frm_main.Show()
def on_submit(self, event):
self.frm_main.SetTitle(self.txt_title.GetValue())
self.txt_title.SetValue("")
if __name__ == "__main__":
app = SampleApp(False)
app.MainLoop()
読み込むXRCファイル名と、起動時のウィンドウタイトルだけを変更しています。
このようにGUI配置情報をXRCリソースに分割することで、画面デザインとイベント処理などを個別に開発できるようになります。また、wxGladeを使うことで視覚的に分かりやすくGUIの構築をおこなうことができます。
4. py2exeによるexeファイルへの変換 †
4.1 py2exeとは †
Pythonでウィンドウアプリケーションが作れるようになったので、もう一歩踏み込んで、このアプリケーションをexe(実行可能)ファイルにしてみます。
py2exeというツールを使うと、PythonアプリケーションをWindows exeファイルに変換することができます。exeファイルにすれば、配布先のPCにPythonをあらかじめインストールしておく必要はなくなります。
4.2 exeファイルへの変換 †
先ほどのサンプルsample_3_5をexeに変換します。まずpy2exeに必要な設定を記述します。
- build.py
from distutils.core import setup
import py2exe
py2exe_options = {
"compressed": 1,
"optimize": 2,
"bundle_files": 2}
setup(
options = {"py2exe": py2exe_options},
windows = [
{"script" : "sample_3_5.py"}],
zipfile = None)
続いてpy2exeを実行するのですが、このとき、py2exeを引数にしてbuild.pyを実行するという少し分かりづらい呼び出しかたをする必要があるので、バッチファイルなどで実行手順を作っておくと作業が楽になります。
build.batと同じフォルダにsample_3_5のソースファイルとXRCファイルを入れておきます。また、msvcp90.dllというDLLファイルが必要になりますので、これも同じフォルダに入れておきます。
build.batを実行すると、sample_3_5-binフォルダの下にexeファイルが生成されます。こうして生成したアプリケーションを配布する場合は、フォルダごと配布します。配布先のPCにPythonやwxPythonがインストールされている必要はありません。
野田