Pythonでコマンドラインランチャを作ろう!第十回−ListCtrlを使う

前置き

つかさです。不定期連載Pythonコマンドラインランチャを作ろう!のコーナーです。
なんか、一つのランチャを作るというよりは自動補完だなんだとかいってふらふらしてますが(=ω=;)
完成に向かっている様子が全く見られないのはきにしないでください。
今回はListCtrlを使う方法。今日の段階ではただリストコントロールを表示させるだけだけど、何回かしたらランチャっぽくなるとおもいます。だってListCtrl色々むつかしいんだもん>ω<

ListCtrlを使う理由

ListCtrlを使う理由は単純で、ListBoxだとアイコンが使えないから。
ListCtrlの特徴は、コラムとか行とかの仕組みで一連のデータを横に並べることができる。また、コラムを複数並べたり、パスの横に更新日時を表示してそれでソートをするということもできるんじゃないかな、と。
見た目で違いが分かると思います。

ListBoxをつかった、すなわち今まで作ってきたのだとこう。

ListCtrlを使うとこういうのができたりする。


今日やること

とりあえずリストコントロールを表示するところまでいこうかな、と。
ListCtrlの場合は少しややこしいので、まあゆっくりやります。

ウィンドウの表示

ListCtrlのウィンドウだけ表示するには、次のようなコードになる。

import wx

List = [
    ['tukasa','hiiragii'],
    ['kagami','hiiragii'],
    ['konata','izumi'],
    ['miwiki','takara'],
    ['yutaka','kobayakawa'],
    ['minami','iwasaki']
        ]


class MyExcalibur(wx.PySimpleApp):
    
    def OnInit(self):
        self.ListFrm = MyListFrame("Excalibur")
        self.ListFrm.Show()
        return 1

class MyListFrame(wx.Frame):
    def __init__(self, Title):
        wx.Frame.__init__(self, None, -1, Title, size=(420,200),pos=(400,448))
        self.LCtrl = MyList(self, List)


class MyList(wx.ListCtrl):
    def __init__(self, parent, List):
        wx.ListCtrl.__init__(self, parent, -1,  size=(420,200),
                style=wx.LC_REPORT | wx.LC_VIRTUAL )
        self.InsertColumn(0, "LackyStar")
        self.InsertColumn(1, "Familyname")
        self.SetColumnWidth(0,-2)
        self.SetColumnWidth(1,-2)
        self.SetItemCount(len(List))


    def OnGetItemText(self, row, col):
        return List[row][col]


app = MyExcalibur()
app.MainLoop()

説明

ListCtrlを作るときには、バーチャルを使う方法と使わない方法とがある。今回のは使う方法。こっちのほうが、リストの値を自由に変えたりできていいらしい。

上のクラスから順に、App、Frame、ListCtrl。最初の二つはあとの便宜のために用意してあるだけで、今までやってきたことから意味は分かるはず。
重要なのはListCtrlです。

class MyList(wx.ListCtrl):
    def __init__(self, parent, List):
        wx.ListCtrl.__init__(self, parent, -1,  size=(420,200),
                style=wx.LC_REPORT | wx.LC_VIRTUAL )
        self.InsertColumn(0, "LackyStar")
        self.InsertColumn(1, "Familyname")
        self.SetColumnWidth(0,-2)
        self.SetColumnWidth(1,-2)
        self.SetItemCount(len(List))

まず、最初のwx.ListCtrl.__init__です。注意するのは、styleのところ。style=wx.LC_REPORT | wx.LC_VIRTUAL とやることで、バーチャルリストになります。
次に、InsertColumnでコラムを作成。ListCtrlというのは、行と列からなるリストを作るものです。だから、これを作る必要がある。コラムというのは縦の列のことですね。これは、LackyStarという名前のコラムを作っている。最初の"0"という数字がコラムを左から数えた番号です。二番目のがタイトル。そして同様に、二つ目のコラムを作ると。
次に、SetColumnWidthでそのコラムの幅を指定しています。最初の0が左から数えた番号で、次のが幅の数値。-2にすると、勝手に調節してくれるみたいです。
次のSetItemCountですが、アイテムの数を数えることで、そのアイテムをセットするという仕組みになっています。ここで数えることで、表の中身に関する関数、OnGetItemTextなどが実行され、表にデータがのります。ちょっと分かりにくいですよね。リストを更新するときにも、この仕組みを使います。

とりあえず整理すると、この初期化のところでは、表の形式をどうするかを設定するわけです。どういうコラムを設定し、それぞれどういう大きさにするか、ということなどですね。

OnGetItemText

では、その表の内容はどうするかというと、このクラスの他の関数で設定することになる。
その関数というのはあらかじめ決まっています。OnGetItemTextとかOnGetItemAttrのように、最初にOnGetItemというのが目印。

    def OnGetItemText(self, row, col):
        return List[row][col]

これは、表にテキストを載せる関数。引数として、rowとcolをとり、returnでListを返しています。
Listは

    ['tukasa','hiiragii'],
    ['kagami','hiiragii'],
    ['konata','izumi'],
    ['miwiki','takara'],
    ['yutaka','kobayakawa'],
    ['minami','iwasaki']

となっていて、さっき__init__で作った表に対応していると見なせる。
この場合だと、List[0][0]というのは、"tukasa"を指す。あらかじめListを、表に対応する形式にしておくわけです。それぞれのマスに何を表示できるかが、rowとcolで場合分けすることで決定できるわけです。
返り値としてrow colとの二つで何を返すかが決定したのなら、リストの項目をどう埋めるかも決められる訳だ。行と列からなる表なわけだから、行の番号と列の番号が分かればいいんですね。ややこしいから、実際にリストの文字を色々変えたりreturnで返す値を色々いじったりしてみたほうが実感としてわかりやすいかもしれません。
イメージとしては、あらかじめ空白の表を作り、そこにいろいろなデータを上乗せしていく、という感じでしょうか。今回扱っているのはテキストだけですが、各項目の色だとかアイコンだとかも、同じような仕組みです。行と列とを引数とし、各々の場合にどうするかを決められることで何にでも対応できるわけですね。