Pythonでコマンドラインランチャを作ろう!第三回−絞り込み編

前置き

第三回目ですね。今回から、機能面に入ります。今日やるのは、入力した文字によるリストの絞り込み!

概観

絞り込み、というのは、入力ボックスで文字を入力したら、リストでそれに一致した項目のみが残る、というやつですね。一文字うつたび、その文字列を含んでいるコマンドのみが残り、リストがだんだんと絞り込まれていくあれです。






あれ

だんだんと

減っていって

いますね・・・・w

で、これを実装するにはどうすればいいか。入力ボックスに入力された文字が変化するたびに、イベントがおこるようにすればいい。"kasa"と打ち込むとしたら、"k"でも"ka"でも"kas"でも"kasa"でも、イベントを起こせばいいんです。
そして、そのたびに入力ボックスから文字列を取得し、その文字列でリストボックスのリストから検索する。そして、一致した項目のみであらたにリストを作り、それをリストボックスにセットすればいい。だいたいこういう流れかと。
リストボックスに"t"が入力されたら、リストのそれぞれの項目から、"t"が含まれているものを検索、含まれているもののみであらたにリストを作る。"tu"が入力されたら、リストのそれぞれの項目から、"tu"が含まれているものを検索、あらたに作るというように。何回も、検索==>リストの更新を行って、絞り込みを実装するわけです。
つまり、テキストコントロールにあわせてリストボックスが更新される仕組みを作ればいい。そして、その間にリストから検索をする関数を挟む、と。

前からの変化

List = ["tukasa","konata","kagami","miwiki","yutaka","minami"]

class MyExcalibur(wx.PySimpleApp):
    
    def OnInit(self):
        Frm = wx.Frame(None, -1, "wxPython", size=(400,48),pos=(400,400))
        self.TxtCtr = wx.TextCtrl(Frm, -1)
        self.TxtCtr.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
        self.TxtCtr.Bind(wx.EVT_TEXT,self.OnText)
        self.lbFrame = wx.Frame(None, 0, "wxPython", size=(420,200),pos=(400,448),style=wx.DOUBLE_BORDER)
        self.List = List
        self.LBox = wx.ListBox(self.lbFrame, -1, choices = self.List, size=(415,200))
        Frm.Show()
        self.lbFrame.Show()
        self.TxtCtr.SetFocus()
        return 1

いちおう関係する一部だけのっけました。
リストボックスに投げるリストを、あとで扱う必要があるので、それを初期化の際に定義しておきます。
self.Listですね。
この中身は、["tukasa","konata","kagami","miwiki","yutaka","minami"]
です。
これで、たとえば入力ボックスに"ka"が入力されたら["tukasa","kagami","yutaka"]が表示されるというようになったらいいんですね。
self.TxtCtr.Bind(wx.EVT_TEXT,self.OnText)が、今回のポイントです。後で説明します。

ふたつの関数

今回は、二つの関数を作ることになります。一つは、イベントと結びつけるもの。もう一つは、リストからテキストコントロール内の語を検索する関数。
テキストコントロールでイベントが起こったら、前者の関数を呼び出す。そして、その関数のなかで入力された文字列を取得し、検索関数になげ、結果をリストとして受け取る。それを、リストボックスにセットする、という形になります。

検索関数

まず、その検索する関数を作成します。これは、検索ワードを受け取り、結果を返すというもの。

    def SearchWord(self,word):
        new_list = []
        for line in self.List:
            if word in line:
                new_list.append(line)
        return new_list

という感じです。returnのあとにあるのが、返り値です。
forで、self.Listの項目をひとつひとつとりだし、それぞれに処理をする。その処理の内容が

            if word in line:
                new_list.append(line)

で、そこで、その項目に検索語が含まれるかどうかを判断するわけです。
if word in line:
がそれですね。もし、検索語が含まれていたなら、new_list.appendでそれをnew_listに追加していくと。new_listは最初に、new_list = []という形で空のリストとして定義しているので、ここには検索語に一致した項目がたまっていくという仕組みです。
で、結果をreturnで返す、と。たとえば、wordが"tu"だったら、["tukasa"]が。"ka"だったら["tukasa","kagami","yutaka"]が返ってくるわけです。
もっと詳しく言うと、最初にself.Listから"tukasa"が取り出される。そして、その中に検索語が含まれているかどうかがif word in line:で調べられる。ヒットしたら、back_listへの追加が実行される、と。で、同じことが順にkonata kagami miwiki yutaka minami で行われるわけですね。
これで、もとのリストから、検索語に一致したもののみのリストが作成できる訳です☆ミ

もうひとつの関数

あとは、検索語をテキストコントロールが変化する度にとりだす仕組みが必要。もうひとつの関数ですね。
これには、wx.EVT_TEXTを使います。これは、ずばりテキストコントロールが変化する度にイベントを起こすやつです。
で、前回と同様にOnInitのところでBindします

self.TxtCtr.Bind(wx.EVT_TEXT,self.OnText)

そして、このイベントが起きたら、テキストコントロール内の文字列を、上で作った検索関数SearchWordに投げ、その結果をリストボックスにセットする関数をつくればいい。

    def OnText(self,event):
        get_word = self.TxtCtr.GetValue()
        get_list = self.SearchWord(get_word)
        self.LBox.Set(get_list)
        event.Skip()

self.TxtCtr.GetValue()というのが、テキストコントロール上の文字列ですね。
それを、検索関数に投げ、その結果を取得。そして、それをリストボックスにセットということをしています。

まとめ

これで、入力ボックスに文字が入力される度に、それに連動してリストボックスのリストが絞り込まれるという仕組みを作りました。
なぜかわからないけど、順調にいっている気がしてます。このまま、最後までいって本当にコマンドラインランチャが完成したらいいですね(*´・ω・)(・ω・`*)ネー
では今日はここまで☆(ゝω・)vキャピ

サンプルその4

import wx

List = ["tukasa","konata","kagami","miwiki","yutaka","minami"]

class MyExcalibur(wx.PySimpleApp):
    
    def OnInit(self):
        Frm = wx.Frame(None, -1, "wxPython", size=(400,48),pos=(400,400))
        self.TxtCtr = wx.TextCtrl(Frm, -1)
        self.TxtCtr.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
        self.TxtCtr.Bind(wx.EVT_TEXT,self.OnText)
        self.lbFrame = wx.Frame(None, 0, "wxPython", size=(420,200),pos=(400,448),style=wx.DOUBLE_BORDER)
        self.List = List
        self.LBox = wx.ListBox(self.lbFrame, -1, choices = self.List, size=(415,200))
        Frm.Show()
        self.lbFrame.Show()
        self.TxtCtr.SetFocus()
        return 1


    def OnKeyDown(self,event):
        key = event.GetKeyCode()
        if key ==  wx.WXK_ESCAPE:
            wx.Exit()
        elif key == wx.WXK_UP:
            count = self.LBox.GetCount()
            next = self.LBox.GetSelection() - 1
            if next >=  0:
                self.LBox.SetSelection(next)
            else: self.LBox.SetSelection(count - 1)
        elif key == wx.WXK_DOWN:
            count = self.LBox.GetCount()
            next = self.LBox.GetSelection() + 1
            if next < count:
                self.LBox.SetSelection(next)
            else: self.LBox.SetSelection(0)
        else: event.Skip()

    def OnText(self,event):
        get_word = self.TxtCtr.GetValue()
        get_list = self.SearchWord(get_word)
        self.LBox.Set(get_list)
        event.Skip()

    def SearchWord(self,word):
        new_list = []
        for line in self.List:
            if word in line:
                new_list.append(line)
        return new_list


app = MyExcalibur()
app.MainLoop()