wxPythonのwxImageで画像を表示する
画像を読み込んで表示させる簡単な例です。(file open error == IOErrorの処理を入れないといけない)
利用するクラスはwx.Image。初期化で読み込む画像ファイルパスを指定します。
Imageインスタンスそのままでは表示できなくて、bitmapに変換してから初めて表示できます。
StaticBitmapで要素に画像を張り付けることができるみたいです。
import wx class myFrame(wx.Frame): def __init__(self, parent, title): wx.Frame.__init__(self, parent, title=title) image = wx.Image('test.jpg') self.bitmap = image.ConvertToBitmap() wx.StaticBitmap(self, -1, self.bitmap, (0,0), self.GetClientSize()) self.SetSize(image.GetSize()) app = wx.App(False) frame = myFrame(None, "Image Viewer") frame.Show() app.MainLoop()
デバイスコンテキストを取得してbitmapを描画する方法でも画像を表示(描画)できます。
デバイスコンテキストはwx.PaintDC(window)で得られるのですが、
"Paintイベント"のときにしか取得できないので、onPaintイベントハンドラを作成します。
import wx class myFrame(wx.Frame): def __init__(self, parent, title): wx.Frame.__init__(self, parent, title=title) self.Bind(wx.EVT_PAINT, self.OnPaint) image = wx.Image('test.jpg') self.bitmap = image.ConvertToBitmap() self.SetSize(image.GetSize()) def OnPaint(self, event=None): deviceContext = wx.PaintDC(self) deviceContext.Clear() deviceContext.SetPen(wx.Pen(wx.BLACK, 4)) deviceContext.DrawBitmap(self.bitmap, 0, 0, True) app = wx.App(False) frame = myFrame(None, "Image Viewer") frame.Show() app.MainLoop()
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()
wxPythonでGUIアプリを作る GUIコンポーネントとサイザー
GUIを作るために必要な要素(Frame,button,text,textcontrolなど)はwxWindowクラスの派生クラスとして提供されています。GUIを設計するためにこれらの要素を一つずつフォームに配置していきます。
例えばwxFrameがwxMuneBarやwxPaneを含んでいて、さらにwxPaneがwxStaticTextなどを含んでいる場合にはこんな階層構造をとることになります。
wxFrame | |||
-> | wxMuneBar | ||
-> | wxPanel | ||
-> | wxStaticText | wxTextCtrl | |
-> | wxStaticText | wxTextCtrl | |
wxPanel | |||
-> | wxStaticText | wxTextCtrl | |
-> | wxStaticText | wxTextCtrl |
要素をそのまま配置すると位置やサイズが常に固定になってしまいますが、Sizerクラスを利用すると自動的にレイアウトを整えることができます。Frameの大きさが変化しても、Sizerが各要素のサイズと位置を自動的に計算して再配置してくれます。
実際に使うにはwx.Sizerから派生したサブクラスwx.BoxSizer,wx.GridSizerなどを利用します。wx.BoxSizerは水平・垂直方向に要素を整形し、wx.GridSizerは要素を自動で格子状に配置してくれます。Sizerの中にSizerを追加して入れ子にすることもできます。
次のサンプルはwx.BoxSizerを使ったコントロール配置例です。wx.BoxSizerを宣言するときに引数としてレイアウトの方向を指定します。
import wx class MainWindow(wx.Frame): def __init__(self, parent, title): wx.Frame.__init__(self, parent, title=title, size=(400,400)) #子サイザーを用意する(水平方向に整形) self.childSizer = wx.BoxSizer(wx.HORIZONTAL) #ボタンを作成する self.startButton =wx.Button(self, -1, "Start") self.stopButton =wx.Button(self, -1, "Stop") #子サイザーにボタンを追加する self.childSizer.Add(self.startButton, 0, wx.EXPAND) self.childSizer.Add(self.stopButton, 0, wx.EXPAND) #メインサイザーを用意する(垂直方向に整形) self.sizer = wx.BoxSizer(wx.VERTICAL) #テキストボックスとラベルを作成する self.textControl = wx.TextCtrl(self, size=(300,100), style=wx.TE_MULTILINE) self.label = wx.StaticText(self, label="Label :") #サイザーに追加する self.sizer.Add(self.textControl, 0, wx.EXPAND) self.sizer.Add(self.childSizer, 0, wx.ALIGN_CENTER) self.sizer.Add(self.label, 0, wx.EXPAND) #Frameにサイザーを追加する self.SetSizer(self.sizer) #サイザーのレイアウトを行う self.SetAutoLayout(1) self.sizer.Fit(self) self.Show() app = wx.App(False) frame = MainWindow(None, "sample") app.MainLoop()
要素を登録するにはAddメソッドを使います。引数として、Sizerに追加する要素、proportion 、レイアウトを行うためのflagを指定します。
proportion が0の場合は要素のサイズを変えず、1の場合はフレームのサイズと連動して要素のサイズも変更してくれるみたいです。
flagの値を変えると、レイアウトをどのような規則で行ってくれるのかを指定することができます。(例えばwx.ALIGN_CENTERなら中央に配置するなど)
作成したサイザーを、SetSizerメソッドでFrameに追加します。その後SetAutoLayoutやFitによって自動レイアウトを行っています。