wxPythonでGUIアプリを作る 〜XRCでGUI設計
XRC
wxPythonでGUIアプリを作る GUIコンポーネントとサイザー - white wheelsのメモの続き。
GUIを作るには各コンポーネントをサイザーの上に載せて設計するというのが前回の話です。
簡単なGUIならまだしも、複雑なインターフェースを作ろうとすると、コードを手打ちで作っていくスタイルではあまりにも作業量が多くなってしまいます。
GUI要素の配置に関する情報をコードのロジックから分離して、データ構造として取り出しておくことができれば非常に使いやすいと思います。実はwxPythonではGUIアプリのレイアウトの情報をXMLファイルに分離してしまうXRCという手法があります。
例えば、次のようなGUIを考えてみます。
wx.Frame
wx.BoxSizer
wx.TextCtr |
---|
wx.Button |
import wx class MainWindow(wx.Frame): def __init__(self, parent, title): wx.Frame.__init__(self, parent, title=title) #textboxとbuttonを作成する self.control = wx.TextCtrl(self, style=wx.TE_MULTILINE) self.button = wx.Button(self, -1, "OK") #sizerに登録する self.sizer = wx.BoxSizer(wx.VERTICAL) self.sizer.Add(self.control, 0, wx.EXPAND) self.sizer.Add(self.button, 0, wx.EXPAND) #sizerをframeにを追加する self.SetSizer(self.sizer) self.SetAutoLayout(1) self.sizer.Fit(self) self.Show() app = wx.App(False) frame = MainWindow(None, "Sample") app.MainLoop()
XRC(xmlファイル)にレイアウトの情報を分離したコードが次の例です。gui.xrcはTestXRC.pyと同じディレクトリに置きます。
gui.xrc
<?xml version="1.0" ?> <resource> <object class="wxFrame" name="myFrame"> <object class="wxBoxSizer"> <orient>wxVERTICAL</orient> <object class="sizeritem"> <object class="wxTextCtrl"/> </object> <object class="sizeritem"> <object class="wxButton" name="myButton"> <label>OK</label> </object> </object> </object> </object> </resource>
TestXRC.py
import wx from wx import xrc class MyApp(wx.App): def OnInit(self): self.res = xrc.XmlResource('gui.xrc') self.init_frame() return True def init_frame(self): self.frame = self.res.LoadFrame(None, 'myFrame') self.frame.Show() if __name__ == '__main__': app = MyApp(False) app.MainLoop()
from wx import xrcを忘れないようにしましょう。
最初の例と違ってwx.Appをクラス化していて、初期化用のメソッドの中でFrameを作成しています。
そこでgui.xrcを読み込んでいることが分かります。要素のレイアウトはxmlの構造から自動的に作成してくれます。
結局GUI設計をするには次の2点の作業を行うとよいことが分かります。
イベント処理の追加
先ほど作成したGUIにイベント処理を追加してみます。ボタンを押すとOnButtonPressedが呼び出されるようにしてみます。
するべきことは、ボタンの要素を取得することと、ボタンとイベントを結びつけることです。
xmlノードから特定の要素を取得するにはxrc.XRCCTRLを使います。例えばname="myButton"の要素を取り出すには、
button = xrc.XRCCTRL(self.frame, 'myButton')
という処理を行います。
イベントと要素を結びつける処理は前回のようにFrameクラスのBindメソッドを使います。
self.frame.Bind(wx.EVT_BUTTON, self.OnButtonPressed, button)
結局コードとしては下のようになりました。
TestXRC.py
class MyApp(wx.App): def OnInit(self): self.res = xrc.XmlResource('gui.xrc') self.init_frame() return True def init_frame(self): self.frame = self.res.LoadFrame(None, 'myFrame') button = xrc.XRCCTRL(self.frame, 'myButton') self.frame.Bind(wx.EVT_BUTTON, self.OnButtonPressed, button) self.frame.Show() def OnButtonPressed(self, evt): print "submit" if __name__ == '__main__': app = MyApp(False) app.MainLoop()
XRCedを使ったGUI設計
GUIのレイアウトに対応したxrcファイルを手書きで作るのはかなり大変な作業です。
それを読み込むコードも作成するのが面倒な場合が多いと思います。
そこでビジュアルにxrcを作成できるツールがいくつかあります。
- XRCed
- wxGlade
- wxFormBuilder
- ....他いろいろ
XRCedはxmlのツリーを作成する機能があります。wxPythonに標準で付属されているため、wxPythonをインストールすれば使うことができます。
そこでこのツールを使ってGUI設計をしてみることにします。
wxPython>XRC Resource Editorを起動します。
GUI要素の配置
XRCedは要素選択と、レイアウトのツリー構造&要素のプロパティ設定の2つのウインドウから成っています。
Frameの作成
WindowsウインドウでwxFrameを選択します。name欄をmyFrameにしておきます。
Sizerの作成
SizersウインドウでwxBoxSizerを選択します。
TextControlの配置
wxBoxSizerを選択した状態で、ControlsウインドウのwxTextCtrlを選択します。
SizerにwxTextCtrlが追加されました。
Buttonの配置
同じくwxBoxSizerを選択した状態で、ControlsウインドウのwxButtonを選択します。
nameをmyButtonにしておきます。また、labelをOKにします。
GUIアプリの作成
あとは、アプリ側のコードを実装すれば終わりですが、上で紹介したTestXRC.pyをそのまま使っても大丈夫です。
XRCedに付属しているpythonモジュール生成機能を使うのが良いと思います。
モジュール生成はFile>Generate Pythonを選択してください。次のようなコードが自動生成されます。
gui_xrc.py
# This file was automatically generated by pywxrc. # -*- coding: UTF-8 -*- import wx import wx.xrc as xrc __res = None def get_resources(): """ This function provides access to the XML resources in this module.""" global __res if __res == None: __init_resources() return __res class xrcmyFrame(wx.Frame): #!XRCED:begin-block:xrcmyFrame.PreCreate def PreCreate(self, pre): """ This function is called during the class's initialization. Override it for custom setup before the window is created usually to set additional window styles using SetWindowStyle() and SetExtraStyle(). """ pass #!XRCED:end-block:xrcmyFrame.PreCreate def __init__(self, parent): # Two stage creation (see http://wiki.wxpython.org/index.cgi/TwoStageCreation) pre = wx.PreFrame() self.PreCreate(pre) get_resources().LoadOnFrame(pre, parent, "myFrame") self.PostCreate(pre) # Define variables for the controls, bind event handlers # ------------------------ Resource data ---------------------- def __init_resources(): global __res __res = xrc.EmptyXmlResource() __res.Load('gui.xrc')
今度はFrameをクラス化していることに注意してください。
ここにイベントハンドラ(ボタンが押された時の処理)、イベントとのBind処理、
アプリの実行コード(別のモジュールにする方がいいかも)を書けば終了です。
import wx import wx.xrc as xrc __res = None def get_resources(): """ This function provides access to the XML resources in this module.""" global __res if __res == None: __init_resources() return __res class xrcmyFrame(wx.Frame): #!XRCED:begin-block:xrcmyFrame.PreCreate def PreCreate(self, pre): """ This function is called during the class's initialization. Override it for custom setup before the window is created usually to set additional window styles using SetWindowStyle() and SetExtraStyle(). """ pass #!XRCED:end-block:xrcmyFrame.PreCreate def __init__(self, parent): # Two stage creation (see http://wiki.wxpython.org/index.cgi/TwoStageCreation) pre = wx.PreFrame() self.PreCreate(pre) get_resources().LoadOnFrame(pre, parent, "myFrame") self.PostCreate(pre) # Define variables for the controls, bind event handlers #ボタンをXRCから取得する button = xrc.XRCCTRL(self, 'myButton')#追加 #ボタンイベントとハンドラを結びつける self.Bind(wx.EVT_BUTTON, self.OnButtonPressed, button)#追加 self.Show()#追加 #ボタンイベントハンドラ def OnButtonPressed(self, evt):#追加 print "submit"#追加 # ------------------------ Resource data ---------------------- def __init_resources(): global __res __res = xrc.EmptyXmlResource() __res.Load('gui.xrc') #ここから追加:アプリの起動処理 app = wx.App(False) frame = xrcmyFrame(None) app.MainLoop()