2020年10月19日月曜日

wxPython 4.1のwx.ListCtrlでCheckBox

wxPythonで行の頭にCheckBoxのついたリストコントールを使いたいとき、 wx.ListCtrlにCheckListCtrlMixinを使っていた。 wxPythonの4.1で、以前のスクリプトを動かしたところこれが動かなかった。

どうやら、CheckListCtrlMixinで定義されていたCheckItem()メソッドが、 wx.ListCtrlのbuilt-ins functionになったために、 そちらが呼び出されて動作しない様子である。 新しい、wx.ListCtrlには、CheckListCtrlMixinを利用しなくても、 CheckBox付きのwx.ListCtrlが利用できるようになったらしい。

wxPythonのページに、EnableCheckBoxes(enable=True)のメソッドが説明されていた。 これを使えばよいが、これまで使いたいスクリプトをとりあえす 動かしたいということであれば、 CheckItem()をMixinのものに上書きすると動作するようになった。

def MyCheckItem(self, index, check=True):
    img_idx = self.GetItem(index).GetImage()
    if img_idx == 0 and check:
        self.SetItemImage(index, 1)
        self.OnCheckItem(index, True)
    elif img_idx == 1 and not check:
        self.SetItemImage(index, 0)
        self.OnCheckItem(index, False)

wx.ListCtrl.CheckItem = MyCheckItem
しかしながら、そのうち直さなくてはならないということで、 EnableCheckBoxes()を使ったものを書いてみた。
import wx
import wx.lib.mixins.listctrl

class CheckListCtrl(wx.ListCtrl):
    def __init__(self, panel, style):
        wx.ListCtrl.__init__(self, panel, -1, 
                style=wx.LC_REPORT,size=wx.Size(400, 300))
        self.EnableCheckBoxes(enable=True)
        self.Bind(wx.EVT_LIST_ITEM_CHECKED, self._OnCheckItem)
        self.Bind(wx.EVT_LIST_ITEM_UNCHECKED, self._OnUnCheckItem)

    def _OnCheckItem(self, evt):
        self.OnCheckItem(evt.Index, True)

    def _OnUnCheckItem(self, evt):
        self.OnCheckItem(evt.Index, False)
        
    def OnCheckItem(self, index, flag):
        print(index, flag)

class AppFrame(wx.Frame):
    def __init__(self, *args, **kwargs):

        wx.Frame.__init__(self, None ,wx.ID_ANY, 'CheckListCtrl', size=(400,300))

        self.list = CheckListCtrl(self, style=wx.LC_REPORT)

        titles = ("check", "title1", "title2")

        for index, title in enumerate(titles):
            self.list.InsertColumn(index, title, wx.LIST_FORMAT_LEFT)

        for index, title in enumerate(titles):
            self.list.Append(["name%d" % index, "value", "value" ]) 

        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add(self.list, 1, wx.EXPAND, border=0)
        self.SetSizer(self.sizer)
        self.Show()

if __name__ == "__main__":
    app = wx.App()
    win = AppFrame()
    app.MainLoop()
wx.EVT_LIST_ITEM_CHECKEDとwx.EVT_LIST_ITEM_UNCHECKEDをハンドルして、 従来のMixInを利用した場合と同じ引数で、 OnCheckItem()メソッドを呼び出すようにしてみた。