PythonでGUIプログラミングをするのは結構面倒です。標準のTkinterは比較的簡単ですが、やはりレイアウトを作り込むのが面倒なのは変わりません。ここではwxPythonのレイアウトを自動コーディングしてくれるwxFormBuilderを紹介します。
こんにちは。wat(@watlablog)です。ここでは面倒なGUIレイアウトコードを自動生成してくれるwxFormBuilderを使ってみます!
本記事のモチベーション
GUIプログラミングに時間をかけたくない
GUI(Graphical User Interface)プログラムとは、ユーザが画面上でマウス等を使って簡単に操作できるよう配慮したインターフェースを使って、ウィンドウ上にボタンやプルダウンメニュー等を配置したプログラムを意味します。
当ブログのほとんどの記事はCUI(Character User Interface)で完結しています。自分で使うことを前提とした多くの技術計算コードはそれで問題ないのですが、他人に使ってもらう時はGUIプログラムの方が説明ドキュメントを少なくできるため一定の需要があります。
このブログでも「Python初心者がGUI作成にTkinterを使う3つの理由」という記事で、Python標準ライブラリであるTkinterを紹介し、簡単なGUIを組むことができるようになりました。
しかし日本語ドキュメントの多いTkinterを使ったとしても、GUIレイアウトを思い通りに作るのには結構時間がかかります。
プログラムのメインは目的の処理を行う部分なので、GUIで見栄えを良くする作業に多くの工数を使いたくないというのが1つ目のモチベーションです。
色々調べてみたところ、wxFormBuilderというソフトはGUIを使ってwxPython用のコードを自動生成してくれるとのこと。
今回は調査目的でちょっと使ってみた内容を紹介します!
wxPythonについてちょっと知りたくなった
単純にwxPythonについて知りたくなったというのもモチベーションの1つです。
wxPythonは標準ライブラリであるTkinterと比べて設置できるウィジェット(ステータスバーとか)が多いことや、見た目がTkinterよりもちょっとモダンという特徴があります。
まずは公式ドキュメントをご覧ください。
ライセンスもwxWindowsライセンスで商用利用ができるとのこと(詳しくは公式のLicenseページをご確認ください)。
さらに、最近ではPython3系に対応(Phoenixバージョン)、pip installに対応し環境も改善しています。
ちょっと前はMacでpip installするだけだとコード実行時にエラーが出ていた問題も今回はなかったので、開発も進んでいるのではないでしょうか。
またwxPythonはwxWidgetsというクロスプラットフォームなGUIライブラリをPythonで使えるようにしたライブラリです。
検索の仕方によってはC++用のページがヒットする可能性もあるのでご注意を。
pip install
ライブラリ本体はpipで簡単にインストール可能です。
1 2 |
pip3 install wxPython # Mac python -m pip install wxPython # Windows |
公式チュートリアルをお試し
百聞は一見にしかずということで、上記公式ページのHello Worldコードを試してみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
#!/usr/bin/env python """ Hello World, but with more meat. """ import wx class HelloFrame(wx.Frame): """ A Frame that says Hello World """ def __init__(self, *args, **kw): # ensure the parent's __init__ is called super(HelloFrame, self).__init__(*args, **kw) # create a panel in the frame pnl = wx.Panel(self) # put some text with a larger bold font on it st = wx.StaticText(pnl, label="Hello World!") font = st.GetFont() font.PointSize += 10 font = font.Bold() st.SetFont(font) # and create a sizer to manage the layout of child widgets sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(st, wx.SizerFlags().Border(wx.TOP|wx.LEFT, 25)) pnl.SetSizer(sizer) # create a menu bar self.makeMenuBar() # and a status bar self.CreateStatusBar() self.SetStatusText("Welcome to wxPython!") def makeMenuBar(self): """ A menu bar is composed of menus, which are composed of menu items. This method builds a set of menus and binds handlers to be called when the menu item is selected. """ # Make a file menu with Hello and Exit items fileMenu = wx.Menu() # The "\t..." syntax defines an accelerator key that also triggers # the same event helloItem = fileMenu.Append(-1, "&Hello...\tCtrl-H", "Help string shown in status bar for this menu item") fileMenu.AppendSeparator() # When using a stock ID we don't need to specify the menu item's # label exitItem = fileMenu.Append(wx.ID_EXIT) # Now a help menu for the about item helpMenu = wx.Menu() aboutItem = helpMenu.Append(wx.ID_ABOUT) # Make the menu bar and add the two menus to it. The '&' defines # that the next letter is the "mnemonic" for the menu item. On the # platforms that support it those letters are underlined and can be # triggered from the keyboard. menuBar = wx.MenuBar() menuBar.Append(fileMenu, "&File") menuBar.Append(helpMenu, "&Help") # Give the menu bar to the frame self.SetMenuBar(menuBar) # Finally, associate a handler function with the EVT_MENU event for # each of the menu items. That means that when that menu item is # activated then the associated handler function will be called. self.Bind(wx.EVT_MENU, self.OnHello, helloItem) self.Bind(wx.EVT_MENU, self.OnExit, exitItem) self.Bind(wx.EVT_MENU, self.OnAbout, aboutItem) def OnExit(self, event): """Close the frame, terminating the application.""" self.Close(True) def OnHello(self, event): """Say hello to the user.""" wx.MessageBox("Hello again from wxPython") def OnAbout(self, event): """Display an About Dialog""" wx.MessageBox("This is a wxPython Hello World sample", "About Hello World 2", wx.OK|wx.ICON_INFORMATION) if __name__ == '__main__': # When this module is run (not imported) then create the app, the # frame, show it, and start the event loop. app = wx.App() frm = HelloFrame(None, title='Hello World 2') frm.Show() app.MainLoop() |
以下のようなウィンドウが表示されます。上のメニュー欄からPython→Aboutを選択すると、OnAboutメソッドが実行される等、わかりやすいチュートリアルとなっていました。
ちゃんとdocStringも書いてある。
Class形式、かつ各メソッドにeventが引数設定されています。これがイベントドリブンな書き方…というのでしょうか?
wxFormBuilderのインストール方法
wxFormBuilderについて
早速GUIコードを自動生成してくれるというwxFormBuilderを使ってみましょう。
wxFormBuilderはPythonだけでなく、C++やJavaといったその他のプログラミング言語へのコード生成も行うソフトウェアです。
公式はおそらく以下のGitHubと思いますので、詳細はこちらをご確認ください。
公式(GitHub):https://github.com/wxFormBuilder
インストール環境
ここではインストール方法について説明してみますが、今回は以下のMac環境にインストールして使ってみます。
Mac | OS | macOS Catalina 10.15.7 |
---|---|---|
CPU | 1.4[GHz] | |
メモリ | 8[GB] |
Python環境
本記事のPython環境は以下です。wxPythonは2022年5月時点で4.1.1がpip installされました。
Python | Python 3.9.6 |
---|---|
PyCharm (IDE) | PyCharm CE 2020.1 |
wxPython | 4.1.1 |
GitHubからインストーラをダウンロードする
ソフトウェアは以下のGitHubページからダウンロードします。
公式:https://github.com/wxFormBuilder/wxFormBuilder/releases
Assets欄にインストーラがあります。自分の環境に合ったディストリビューションを選びましょう。ここでは「wxFormBuilder-3.10.1-macos-10.15-x86_64-bundle.zip」を選びました。
インストールする
Macの場合はzipを解凍後、.appファイルをアプリケーションへドラッグ&ドロップすることでLaunchpadにインストール可能です。ここに登録しておけばDockへの追加もできるので使いやすくなります。
インストール方法は以上…超簡単だったので、あえて説明しなくてもよかったかも知れません。
wxFormBuilderの使い方
出力言語設定
wxFormBuilderを起動すると以下の画面が表示されます。
左ツリーのMyProject1が選択された状態で右のPropertiesをクリックします。
次にcode_generationを展開し、Pythonにのみチェックを付けます(C++はチェックを外します)。
このチェックによりPythonのwxWidgetコードが出力されるようになります。
出力ファイル設定
Propertiesのfile欄が空欄になっているので、ここに出力ファイル名を記入します。ここでは「Application」と記入しましたが、ここで設定した名前で親フレームクラスが記載されたApplication.pyが出力されるようになります。
レイアウトを作り込む
ここからはレイアウトを作り込んでいきます。今回は動作確認のために単一のフレームにボタンとテキストを配置させてみます。
FrameとSizerの追加
wxPythonの基本はFrameの作成→Sizerの作成→ボタン等のウィジェットの作成…という順で行います。
wxFormBuilderでFrameを追加するには、FormsタブをクリックしてFrameをクリックするだけです。
SizerはLayoutタブから選定します。今回はwxBoxSizerを選びました。
ちなみにSizerの中にウィジェットを配置することで、フレームのサイズ変更等に適切に追従させることが可能となります。
Buttonの追加
ボタンはCommonタブから選択します。今回はwxButtonを選びました。
テキストの追加
wxStaticTextのような静的テキストもCommonタブの中にあります。
イベントを設定する
次にボタンがクリックされた時のイベントを設定します。
ツリーの中からイベントを設定するButtonを選択し、右側のEventタブを選択します。OnButtonClickをダブルクリックすることで、隣のセルにイベント名が登録されます。
ここで登録されたイベントに後で動作をコーディングします。
見栄えを整える
ここではあまりいじりませんが、フレームのサイズがマウスで変更できるのはもちろんのこと、中央揃えや幅調整といった操作もソフト上で可能です。最初は慣れが必要だと思いますが、楽…なのかな?
以下は操作例の動画です。
プロジェクトを保存する
File→Save As...でプロジェクトを保存します。このプロジェクトを保存したディレクトリにコードが出力されるので、場所は選びましょう。
親フレームクラスのコード(.py)を出力する
File→Generate Codeにより親フレームクラスのコードを出力します。
子フレームクラスのコード(.py)を出力する
Tools→Generate Inherited Classにより子フレームクラスのコードを出力します。
以下のウィンドウが表示されるので、MyFrame1にチェックを付けてからOKをクリックします。
プロジェクトファイルの場所に以下のファイルが出来上がります。ひとまずこれでwxFormBuilderの操作は終了です。
Application.pyの中身
ここで一度各Pythonファイルの中身を見てみましょう。
まずはApplication.pyです。
このファイルは「PLEASE DO *NOT* EDIT...」と書いてある通り、編集しないでおきます。スタイル系の記述がクラス形式で書いてあります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# -*- coding: utf-8 -*- ########################################################################### ## Python code generated with wxFormBuilder (version 3.10.1-0-g8feb16b) ## 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( 522,322 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL ) self.SetSizeHints( wx.DefaultSize, wx.DefaultSize ) bSizer1 = wx.BoxSizer( wx.VERTICAL ) 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.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.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, override them in your derived class def m_button1OnButtonClick( self, event ): event.Skip() |
MyProject1MyFrame1.pyの中身
こちらのファイルを編集します。作ったばかりの状態は設定したイベントの中身がpassになっており、コメントで# TODOと書いてあります。
ちなみにこのファイルのクラスは先ほどのApplication.pyをimport、親フレームのクラスを継承して書かれています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
"""Subclass of MyFrame1, which is generated by wxFormBuilder.""" import wx import Application # Implementing MyFrame1 class MyProject1MyFrame1( Application.MyFrame1 ): def __init__( self, parent ): Application.MyFrame1.__init__( self, parent ) # Handlers for MyFrame1 events. def m_button1OnButtonClick( self, event ): # TODO: Implement m_button1OnButtonClick pass |
Pythonコードを完成させて動作させる
子フレームクラスにイベント時の動作を記述する
子フレームクラスが書いてあるMyProject1MyFrame1.pyについて、passとなっていた部分にイベント発生時(ここではボタンがクリックされた時)の動作を記述します。
以下のコードは「self.m_staticText1.SetLabelText()」で静的テキストの文字を変更してみました。Application.pyの中身を見れば操作対象のメソッド名を確認できます。
さらに、PyCharm等のIDEを使っていれば、コード入力サポートが付いているので特にメソッド名を覚えていなくても割とカンでなんとか書けてしまいます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
"""Subclass of MyFrame1, which is generated by wxFormBuilder.""" import wx import Application # Implementing MyFrame1 class MyProject1MyFrame1( Application.MyFrame1 ): def __init__( self, parent ): Application.MyFrame1.__init__( self, parent ) # Handlers for MyFrame1 events. def m_button1OnButtonClick( self, event ): # TODO: Implement m_button1OnButtonClick # pass self.m_staticText1.SetLabelText('Clicked!') |
main.pyを作って実行する
wxPythonのチュートリアルを思い出しながら、main.pyと新しいPythonファイルを作成して実行できるようにすれば完成です。
1 2 3 4 5 6 7 8 |
import wx from MyProject1MyFrame1 import MyProject1MyFrame1 if __name__ == '__main__': app = wx.App() frame = MyProject1MyFrame1(None) frame.Show() app.MainLoop() |
実行結果の例
なんともしょぼいプログラムですが、最初は最低限の内容で型を把握することが大事だと思います。
まとめ
今回は自動でGUIプログラムを作ってくれるwxFormBuilderを使ってみました。
このページではwxPythonやwxFormBuilderの簡単な紹介、公式ドキュメントの紹介、インストール方法や使用例を紹介しました。
wxPythonにはまだ慣れていませんが、GUIベースでGUIコードを書けるのは楽なのかも知れません。
まだ複雑なレイアウトに挑戦していないので、本当に使いやすいかどうかはこれから判定を下すとして、少なくともwxPythonのコードを書く時のお手本にはなりそうです。
GUIにはあまり時間をかけたくないという人にとって、一つの選択肢になり得るか…今後に期待。
GUIはこだわり始めると時間が溶けそうですが、簡単に作れるようになりたいですね!
Twitterでも関連情報をつぶやいているので、wat(@watlablog)のフォローお待ちしています!