目次

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.jpg

 ソースコードは以下のようになっています。

  • 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.jpg

 ソースコードは以下のようになっています。

  • 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を使って、一列にコントロールを並べています。

sample_2_2_sizer.jpg

2.3 イベント処理

 コントロールを操作したときのイベント処理を追加してみましょう。sample_2_3は、テキストコントロールにテキストを入力してボタンを押すと、ウィンドウのタイトルが変更されるというサンプルアプリケーションになっています。

sample_2_3.jpg

 ソースコードは以下のようになっています。

  • 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つのウィンドウが表示されます。

  • メインウィンドウ
    wxglade_main.png
  • ツリーウィンドウ
    wxglade_tree.png
  • プロパティウィンドウ
    wxglade_prop.png

 メインウィンドウにはFrameやコントロールなど、配置可能なGUIのパーツが並んでいます。ツリーウィンドウにはGUIの階層構造が表示されます。プロパティウィンドウでは選択されているコントロールのパラメタなどを設定することができます。
 各ウィンドウの具体的な使い方などはこのあと、実際の作業を通して見ていきたいと思います。

3.3 ウィンドウを表示する

 やはり最初はウィンドウを表示するだけのアプリケーションをつくるところから始めてみましょう。

sample_3_3.png

 実行したときの見た目はsample_2_2とほとんど同じです。このアプリケーションを作成する手順を見ていきましょう。まずはwxGladeでGUIの配置情報を生成します。

 wxGladeを起動したら、まずプロパティ画面で以下の基本設定を変更します。

  • 出力形式をXRCに変更する。
  • EncodingをUTF-8に変更する。
  • 出力するファイル名を設定する。
sample_3_3_property.png

 基本設定が終わったら、さっそくwx.Frameを追加してみましょう。メイン画面の「Add a Frame」ボタンを押すと、フレームクラスを選択するダイアログが表示されるので、wxFrameを選択してOKを押しましょう。

sample_3_3_select_frame_class.png

 するとツリー画面にframe_1が追加され、新たにデザイン画面が表示されます。

sample_3_3_tree.png

 ツリー画面に追加されたframe_1を選択すると、プロパティ画面でこのFrameのプロパティを編集できるようになります。今回はName属性をframe_mainと編集しておきましょう。

sample_3_3_property_frame.png

 デザイン画面はここで定義しているウィンドウを表示したときのイメージになります。

sample_3_3_design.png

 ウィンドウタイトルなどの細かい設定はソースコードのほうで行うので、今回はこのままXRCを生成します。メイン画面のメニューから「Generate Code」を選択し、XRCファイルを出力します。出力先は先ほどプロパティ画面で設定した場所になります。

sample_3_3_generate_code.png

 出力したXRCファイルは以下のようになります。

  • sample_3_3.xrc
    <?xml version="1.0" encoding="UTF-8"?>
    <!-- generated by wxGlade 0.6.3 on Wed Nov 16 22:19:37 2011 -->
    
    <resource version="2.3.0.1">
        <object class="wxFrame" name="frame_main">
            <style>wxDEFAULT_FRAME_STYLE</style>
            <title>frame_main</title>
            <object class="wxBoxSizer">
                <orient>wxVERTICAL</orient>
            </object>
        </object>
    </resource>
    

 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を配置します。

sample_3_4.png

 コントロールの配置にwx.Sizerが必要なことは先ほど述べましたが、sample_3_3で作成したframe_mainには既にsizerが設定されています。ところが、このsizerはコントロールを縦に配置していくwx.BoxSizerです。今回はコントロールを横に配置していきたいので、ひとまずsizerをツリー画面から削除します。
 続いて、メイン画面から「Add a BoxSizer」を選択し、frame_mainのデザイン画面でsizerを追加します。この時、OrientationをHorizontalに、Slotsを2に設定しておきます。

sample_3_4_sizer.png

 先ほどと同じようにメイン画面から「Add a TextCtrl」を選択し、ウィンドウの左側にwx.TextCtrlを配置します。wx.Buttonも同様にしてウィンドウの右側に配置します。

sample_3_4_design.png

 コントロールの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のタイトルが変更されます。

sample_3_5.png

 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を少し改造して、コントロールの配置だけを変えてみましょう。

sample_3_6.png

 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
    build.py py2exe
    copy .\msvcp90.dll .\dist\
    copy .\sample_3_5.xrc .\dist\
    move .\dist .\sample_3_5-bin
    rmdir /s /q .\build
    

 build.batと同じフォルダにsample_3_5のソースファイルとXRCファイルを入れておきます。また、msvcp90.dllというDLLファイルが必要になりますので、これも同じフォルダに入れておきます。
 build.batを実行すると、sample_3_5-binフォルダの下にexeファイルが生成されます。こうして生成したアプリケーションを配布する場合は、フォルダごと配布します。配布先のPCにPythonやwxPythonがインストールされている必要はありません。


野田

添付ファイル: filewxglade_tree.png 938件 [詳細] filewxglade_prop.png 809件 [詳細] filewxglade_main.png 961件 [詳細] filesample_3_6.png 952件 [詳細] filesample_3_5.png 906件 [詳細] filesample_3_4_sizer.png 888件 [詳細] filesample_3_4_design.png 973件 [詳細] filesample_3_4.png 841件 [詳細] filesample_3_3_tree.png 854件 [詳細] filesample_3_3_select_frame_class.png 838件 [詳細] filesample_3_3_property_frame.png 907件 [詳細] filesample_3_3_property.png 887件 [詳細] filesample_3_3_generate_code.png 914件 [詳細] filesample_3_3_design.png 897件 [詳細] filesample_3_3.png 913件 [詳細] filesample_2_3.png 456件 [詳細] filesample_2_3.jpg 1008件 [詳細] filesample_2_2_sizer.jpg 878件 [詳細] filesample_2_2.jpg 980件 [詳細] filesample_2_1.jpg 1001件 [詳細]

BC::labsへの質問は、bc9-dev @ googlegroups.com までお願い致します。
トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   最終更新のRSS
Last-modified: 2011-12-12 (月) 19:11:41 (2024d)