バーチャルリストでリスト表示を低負荷に行う(2)

2011-09-28 - 理想のユーザ・インターフェイスを求めての続き。wxPythonの話。

VListBoxのサンプルを改良して、他で使える形にもっていく。
改良のポイントを列記すると、

  • リストはVListBoxで作成
  • リストの前後に他のコントロールを配置
  • ボタンを押したときに、リストの中の何が選ばれているかを表示

となる。

ソースコードは以下の通り。VListBoxでは、リスト(の見た目)を自分好みに作れるという点で魅力的だが、何からなにまで自分でやらないといけないので、凝ったものを作る場合には面倒。

import wx

class UserListBox(wx.VListBox):
    def __init__(self, parent, users):
        super(UserListBox, self).__init__(parent)

        self.bmp = wx.Bitmap("system-users.png",
                             wx.BITMAP_TYPE_PNG)
        self.bh = self.bmp.GetHeight()
        self.users = users

        self.SetItemCount(len(self.users))

    def OnMeasureItem(self, index):
        return self.bh + 4

    def OnDrawSeparator(self, dc, rect, index):
        oldpen = dc.GetPen()
        dc.SetPen(wx.Pen(wx.BLACK))
        dc.DrawLine(rect.x, rect.y,
                    rect.x + rect.width,
                    rect.y)
        rect.Deflate(0, 2)
        dc.SetPen(oldpen)

    def OnDrawItem(self, dc, rect, index):
        dc.DrawBitmap(self.bmp, rect.x + 2,
                      ((rect.height - self.bh) / 2) + rect.y)
        textx = rect.x + 2 + self.bh + 2
        lblrect = wx.Rect(textx, rect.y,
                          rect.width - textx,
                          rect.height)
        dc.DrawLabel(self.users[index], lblrect,
                     wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL)

class MyApp(wx.App):
    def OnInit(self):
        self.frame = MyFrame(None, title="Virtual List Box", size=(300,200))
        self.SetTopWindow(self.frame)
        self.frame.Show()
 
        return True

class MyFrame(wx.Frame):
    def __init__(self, *args, **kwargs):
        super(MyFrame, self).__init__(*args, **kwargs)
        self.panel = MyPanel(self)

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.panel, 1, wx.EXPAND)
        self.SetSizer(sizer)

class MyPanel(wx.Panel):
    def __init__(self, parent):
        super(MyPanel, self).__init__(parent)
        self.parent = parent

        users = ["Steve", "James", "Mary", "Yuya",
                 "Yukiyo", "Ankur", "Saiyam", "Vadim",
                 "Andrea", "Ana" ]

        self.lst = UserListBox(self, users)

        line = wx.StaticLine(self, -1, size=(20,-1), 
                             style=wx.LI_HORIZONTAL)

        btnsizer = wx.StdDialogButtonSizer()
        btn1 = wx.Button(self, wx.ID_OK)
        btn1.SetDefault()
        btnsizer.AddButton(btn1)

        btn2 = wx.Button(self, wx.ID_CANCEL)
        btnsizer.AddButton(btn2)
        btnsizer.Realize()

        btn1.Bind(wx.EVT_BUTTON, self.press_ok)
        btn2.Bind(wx.EVT_BUTTON, self.press_cancel)

        sizer = wx.BoxSizer(wx.VERTICAL)

        sizer.Add(wx.StaticText(self, -1, 
                  "Select one user from the below list."), 0,
                  wx.ALIGN_LEFT|wx.ALL, 5)
        sizer.Add(self.lst, 1, wx.EXPAND)
        sizer.Add(line, 0, 
            wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.TOP, 5)
        sizer.Add(btnsizer, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)

        self.SetSizer(sizer)
        sizer.Fit(self)

    def press_ok(self, evt):
        print self.lst.GetSelection()

    def press_cancel(self, evt):
        self.parent.Destroy()

if __name__ == "__main__":
    app = MyApp(False)
    app.MainLoop()

実行画面は以下の通り。


「キャンセル」ボタンでこの画面を閉じる。
「OK」ボタンで、選択したリスト項目の番号をprintする。