Look on the Bright Side of Things

Anderson's Blog - since 2005

wxPythonでのウィジェット動的配置

アプリのメニュー画面とか入力画面とかで、ボタンやチェックボックスなどを別途定義してある項目内容に応じて変動させたい場合がある。

たとえば、何らかの商品に区分をつけて入力させる場合に、新しい区分を後でデータベース上に追加したら、それが入力画面に出てくるとか。

プログラムを起動させるメニュー画面で登録プログラムを追加・削除したりとかで、いちいちプログラム内にハードコードしてあるものをいじらずに、JSONなどの定義ファイルから読み込んで表示させるとか。

それをwxPythonでやってみたのが下記のコード。
もとにしたコードはコレ。
python - Dynamically populating widgets, how to then access them? - Stack Overflow

import wx
import math


def btn_click(event):
    # eventで渡されるidでウィジェットオブジェクトを指定
    btn_obj = wx.FindWindowById(event.GetId())
    wx.MessageBox("ボタンラベル:" + btn_obj.Label + "\nリスト名:" + button_list[button_id.index(event.GetId())])


# ボタンの数・内容定義
cont = []
for i in range(20):
    cont.append(["ボタン" + str(i + 1), "button " + str(i + 1)])

application = wx.App()
frame = wx.Frame(None, wx.ID_ANY, 'テスト', size=(600, 500))

panel = wx.Panel(frame, wx.ID_ANY)
panel.SetBackgroundColour('#AFAFAF')

base = wx.BoxSizer(wx.VERTICAL)
title = wx.StaticText(panel, wx.ID_ANY, 'ボタンたくさん', style=wx.TE_CENTER)
tfont = wx.Font(14,
                wx.FONTFAMILY_DEFAULT,
                wx.FONTSTYLE_NORMAL,
                wx.FONTWEIGHT_BOLD)
title.SetFont(tfont)
base.Add(title, flag=wx.SHAPED | wx.ALIGN_CENTER | wx.ALL, border=10)

pfont = wx.Font(13,
                wx.FONTFAMILY_DEFAULT,
                wx.FONTSTYLE_NORMAL,
                wx.FONTWEIGHT_NORMAL)

# 列数 指定及びボタン数に応じて行数rowsを変動させる
gridcol = 3
rownum = lambda x : int(math.ceil(len(cont) / x))
buttons = wx.GridSizer(rows=rownum(gridcol), cols=gridcol, gap=(0, 0))

# ボタンの動的配置
button_id = []
button_list = []
for ls in cont:
    button = wx.Button(panel, wx.ID_ANY, ls[0])
    button.SetFont(pfont)
    button.Bind(wx.EVT_BUTTON, btn_click)
    buttons.Add(button, proportion=1, flag=wx.SHAPED |
                wx.ALIGN_CENTER | wx.ALL, border=5)

    # wigetのidと対応するpgnameを記憶
    button_id.append(button.GetId())
    button_list.append(ls[1])

base.Add(buttons, proportion=1, flag=wx.SHAPED | wx.ALIGN_CENTER_HORIZONTAL |
         wx.ALIGN_TOP | wx.ALL, border=10)


panel.SetSizer(base)
panel.Layout()
base.Fit(panel)
frame.Centre(wx.BOTH)
frame.Show()
application.MainLoop()

思ってたよりは簡単にできたかなと。
ここんとこ、ずっとwxFormBuilder使っててイチから画面のプログラムを書いておらず、いい復習になった。