😀

wxPythonのGUIデザイナーwxFormBuilderを使ってみる

2022/12/24に公開

PythonでGUIアプリを作るとき、ライブラリの選択がとても難しいです。
私の希望としては下記なのですが、

  • 無料版があること
  • GPL系のライセンスではないこと
  • コーディングでレイアウトを書くのはやはり面倒なので、Visual Studioのようにグラフィカルに書きたい
    特に最後のGUIデザイナーについてはQt以外は良いものがあまりなく、あってもしばらく更新されていなかったり、情報がほとんどなかったりするので、
    これだというものがあまりありません。

色々調べた結果、タイトル通りライブラリはwxPython。GUIデザイナーはwxFormBuilderが1番良さそうですので、
とりあえず使ってみます。

インストール

まず、wxPythonをインストールします。

pip install wxPython

wxFormBuilderのインストールは下記GithubのReleaseページからインストーラーをダウンロードします。
現時点ではv3.9.0でした。
https://github.com/wxFormBuilder/wxFormBuilder/releases

wxFormBuilderでGUI部分を作る

wxFormBuilderを起動し、はじめはプロジェクトの設定をします。
code_generationをPythonに、fileをMyAppとします。

次に、Formsタブ、Frameボタンを押して、プロジェクトにFrameを追加します。

FrameにwxBoxSizerを追加し、それにwxStaticTextとwxButtonを追加します。

ボタンが押された時のイベントを追加します。
wxButtonのEventタブ内にあるOnButtonClickをダブルクリックすると、イベント名が自動登録されます。

これでGUIの設定は終わりです。
最後に、GUIのコードを生成します。

  • まずはプロジェクトを保存します
  • FileタブのGenerate Codeをクリックします
  • ToolsタブのGenerate Inherited Classをクリックします(ウィンドウが出てくると思うので、MyForm1にチェックを入れてOKボタンを押してください)

これでwxFormBuilderでやる作業は終わりです。

ロジック部分を作る

MyApp.pyとMyProject1MyForm1.pyというファイルが生成されていると思います。
MyAppはFrameの所設定が記載されたファイルです。GUIのレイアウトもここに記載されています。

# -*- coding: utf-8 -*-

###########################################################################
## Python code generated with wxFormBuilder (version Oct 26 2018)
## http://www.wxformbuilder.org/
##
## PLEASE DO *NOT* EDIT THIS FILE!
###########################################################################

import wx
import wx.xrc

###########################################################################
## Class MyFrame1
###########################################################################

class MyFrame1 ( wx.Frame ):

	def __init__( self, parent ):
		wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = wx.EmptyString, pos = wx.DefaultPosition, size = wx.Size( 500,300 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL )

		self.SetSizeHints( wx.DefaultSize, wx.DefaultSize )

		bSizer1 = wx.BoxSizer( wx.VERTICAL )

		self.m_staticText1 = wx.StaticText( self, wx.ID_ANY, u"MyLabel", wx.DefaultPosition, wx.DefaultSize, 0 )
		self.m_staticText1.Wrap( -1 )

		bSizer1.Add( self.m_staticText1, 0, wx.ALL, 5 )

		self.m_button1 = wx.Button( self, wx.ID_ANY, u"MyButton", wx.DefaultPosition, wx.DefaultSize, 0 )
		bSizer1.Add( self.m_button1, 0, wx.ALL, 5 )


		self.SetSizer( bSizer1 )
		self.Layout()

		self.Centre( wx.BOTH )

		# Connect Events
		self.m_button1.Bind( wx.EVT_BUTTON, self.m_button1OnButtonClick )

	def __del__( self ):
		pass


	# Virtual event handlers, overide them in your derived class
	def m_button1OnButtonClick( self, event ):
		event.Skip()

次に、MyProject1MyFrame1はイベントなどが登録されています。
MyProject1MyFrame1はMyFrameを継承しているので、ボタンをクリックされたときにm_button1OnButtonClickが呼ばれます。

ネットで情報を見ると、MyAppではなく、このファイルにロジック部分を追加することが多いようです。
その理由としては、wxFormBuilderでの編集を反映させるときにはGenerate Codeを押して設定をコードに反映させる必要があるのですが、
その際にMyAppファイルが上書き保存されてしまうため、ハンドコードで書いた部分がすべて消えてしまうからです。
MyProject1MyFrame1はGenerate Inherited Classをクリックしない限り上書きされません。

ですが、イベントの追加や名前変更を行った際にはGenerate Inherited Classをするひつようがあるので、結局MyProject1MyFrame1も上書き保存されてしまいます。
wxFormBuilderでの変更の程度の問題です。
なので、MyProject1MyFrame1にもコードを書きたくはないです。
これの対応などを次にやっていきます。

"""Subclass of MyFrame1, which is generated by wxFormBuilder."""

import wx
import MyApp

# Implementing MyFrame1
class MyProject1MyFrame1( MyApp.MyFrame1 ):
	def __init__( self, parent ):
		MyApp.MyFrame1.__init__( self, parent )

	# Handlers for MyFrame1 events.
	def m_button1OnButtonClick( self, event ):
		# TODO: Implement m_button1OnButtonClick
		pass

次に新しく2つのファイルを追加します。
まずはメイン関数があるmain.pyです。

#! env python
# -*- coding: utf-8 -*-

import os
import sys
import wx
from MyProject1MyFrame1Child import MyProject1MyFrame1Child

if __name__ == '__main__':
	app = wx.App(False)
	frame = MyProject1MyFrame1Child(None)
	frame.Show(True)
	app.MainLoop()

次にMyProject1MyFrame1にコードを書かないように新たにファイルを作ります。
MyProject1MyFrame1Child.pyという名前で次を保存します。

MyProject1MyFrame1にコードを書くと上書きで消されてしまうので、
MyProject1MyFrame1を継承したMyProject1MyFrame1Childを作成し、initとm_button1OnButtonClickをオーバーライドします。
ついでに、ロジックの確認のため、ボタンが押された時のイベント内でstaticTextの文字を変更するようにします。

import wx
import MyApp
from MyProject1MyFrame1 import MyProject1MyFrame1

class MyProject1MyFrame1Child( MyProject1MyFrame1 ):
	def __init__( self, parent ):
		MyProject1MyFrame1.__init__( self, parent )

	# Handlers for MyFrame1 events.
	def m_button1OnButtonClick( self, event ):
		# TODO: Implement m_button1OnButtonClick
		self.m_staticText1.SetLabel('Hello wxPython!!')

これでコーディングも終わりです。
最後に実行してみます。

python main.py


ボタンを押すと、、、文字の内容が変化しました。

こんな感じです。
MyProject1MyFrame1Childを自分で作ったりと面倒なこともありますが、
Visual StudioのFormアプリケーション並みの機能を求めるのは厳しいと思うので、
今回のようにwxFormBuilderで作成するファイルと、ハンドコーディングするファイルを明確に分離した方が、良いと思います。
(そのためにwxFormBuilder側もForm部とイベント部でファイルを分けているんだと思います。)

上記の方法でも、やり方さえ慣れてしまえばスムーズにいくと思うので、
これからPythonでGUIアプリを作るときにはこんな感じでやっていきたいと思います。

GitHubで編集を提案

Discussion