wxPythonでAA管理ツールを作ろう!第六回 PyMigemoでインクリメンタルサーチ
あいさつ
つかさです。
今回は、PyMigemoを使います。
PyMigemoのインストール
migemo.pydと、migemo.dllをスクリプトのあるフォルダにおけば、このモジュールを使えるようになります。
また、migemo用のDictもどこかからダウンロードしましょう。これも同じフォルダにおきます。
migemoでパターンを得る
例えば、Migemoオブジェクトを作った後、query()にtukaというのを与えると
([捕遺仕疲遣柄攫束塚使冢掴閊障把支捉司掌元曹搏]|tuka|tuka|つか|付かぬ事|ツカ)
このようなパターンを得ることができます。
このパターンを正規表現に利用すれば、ローマ字でも日本語を検索できるわけです。
プログラム
以前から変更したのは大きくは二点。
- IncrementalSearchクラスを付け加えた
- OnKeyTxtを付け加えた
- AAlist07.py
import wx,os,migemo,re migemo_object = None class MyPopupWindow(wx.PopupWindow): def __init__(self, parent, style): wx.PopupWindow.__init__(self, parent, style) self.st = wx.StaticText(self, -1,pos=(10,10)) def ChangeTxt(self,message,pos): self.st.SetLabel(message) sz = self.st.GetBestSize() gps = self.GetSize() self.SetDimensions(pos[0], pos[1], sz.width + 20 , sz.height + 20) self.Show(True) class IncrementalSearch: def __init__(self,pattern,list): self.pattern = pattern self.new_list = [] self.old_list = list def GetBackList(self): global migemo_object if migemo_object==None: migemo_object = migemo.Migemo('Dict\migemo-dict') re_pattern = migemo_object.query(self.pattern) try: migemo_re_object = re.compile(re_pattern, re.IGNORECASE) except re.error: return False for x in self.old_list: if migemo_re_object.search(x): self.new_list.append(x) return self.new_list class MyTxtFrm(wx.Frame): def __init__(self, parent, id): wx.Frame.__init__(self, parent, id) aa_list = os.listdir("AAList") self.AA_Dict = {} for x in aa_list: key = os.path.splitext(x)[0].decode("shift-jis") path = os.path.join("AAlist",x) self.AA_Dict[key] = path self.win = MyPopupWindow(self, wx.SIMPLE_BORDER) self.TxtCtr = wx.TextCtrl(self, -1) self.LBox = wx.ListBox(self, -1, choices = self.AA_Dict.keys()) self.TxtCtr.Bind(wx.EVT_KEY_DOWN, self.OnKeyChar) self.TxtCtr.Bind(wx.EVT_TEXT,self.OnText) self.sizer = wx.BoxSizer(wx.VERTICAL) self.sizer.Add(self.TxtCtr, 0, wx.EXPAND) self.sizer.Add(self.LBox, 1, wx.EXPAND) self.SetSizer(self.sizer) self.SetAutoLayout(1) self.sizer.Fit(self) def OnKeyChar(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) f = open(self.AA_Dict[self.LBox.GetStringSelection()]) fr = f.read() gps = self.GetSize() gp = self.GetPosition() pos = (gp.x + gps.x, gp.y + 45) self.win.ChangeTxt(fr,pos) 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) f = open(self.AA_Dict[self.LBox.GetStringSelection()]) fr = f.read() gps = self.GetSize() gp = self.GetPosition() pos = (gp.x + gps.x, gp.y + 45) self.win.ChangeTxt(fr,pos) elif key in (wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER): c_board = wx.Clipboard() f = open(self.AA_Dict[self.LBox.GetStringSelection()]) fr = f.read() c_board.SetData(wx.TextDataObject(fr)) c_board.Flush() c_board.Close() wx.Exit() else: event.Skip() def OnText(self,event): word = self.TxtCtr.GetValue() isearch = IncrementalSearch(word,self.AA_Dict.keys()) try: new_list = isearch.GetBackList() except: return False if new_list: self.LBox.Set(new_list) self.LBox.SetSelection(0) self.ShowToolTip() else: event.Skip() event.Skip() def ShowToolTip(self): f = open(self.AA_Dict[self.LBox.GetStringSelection()]) fr = f.read() gps = self.GetSize() gp = self.GetPosition() pos = (gp.x + gps.x, gp.y + 45) self.win.ChangeTxt(fr,pos) class MyApp(wx.PySimpleApp): def OnInit(self): self.TxtFrm = MyTxtFrm(None, -1) self.TxtFrm.SetSize( (180, 200) ) self.TxtFrm.Show() self.TxtFrm.TxtCtr.SetFocus() return 1 app = MyApp() app.MainLoop()
IncrementalSearchクラス
検索文字列と調べたいリストを受け取り、検索文字列に一致したもののみのリストを返すIncrementalSearchクラスを作製しましょう。
class IncrementalSearch: def __init__(self,pattern,list): self.pattern = pattern self.new_list = [] self.old_list = list def GetBackList(self): global migemo_object if migemo_object==None: migemo_object = migemo.Migemo('Dict\migemo-dict') re_pattern = migemo_object.query(self.pattern) try: migemo_re_object = re.compile(re_pattern, re.IGNORECASE) except re.error: return False for x in self.old_list: if migemo_re_object.search(x): self.new_list.append(x) return self.new_list
__init__のところを見てください。引数として
- 検索に用いたい文字列
- その文字列で検索したいリスト
を受け取ります。
GetBackListは、受け取ったリストを受け取った文字列でmigemo検索し、それにヒットした要素のみを、新しいリストとして返す関数です。GetBackListについて説明します。
Migemoオブジェクトの作成
if migemo_object==None: migemo_object = migemo.Migemo('Dict\migemo-dict')
まず、Migemoオブジェクトがあるかどうかで分岐します。無い場合は、作製します。
一々オブジェクトを作ると、時間がかかるのでこのようにしています。
本体と結びつける
OnText関数
エディットボックスで何か入力されるたびにこのクラスを呼び出し、リストを受け取り、ListBoxにセットすればいいわけですね。
そのための関数OnTextを作製します。
def OnText(self,event): word = self.TxtCtr.GetValue() isearch = IncrementalSearch(word,self.AA_Dict.keys()) try: new_list = isearch.GetBackList() except: return False if new_list: self.LBox.Set(new_list) self.LBox.SetSelection(0) self.ShowToolTip() else: event.Skip() event.Skip()
GetValue()でエディットボックスの文字列を受け取ります。
次に、先に作ったIncrementalSearchにその文字列と、ファイル名のリストを投げます。
そして戻ってきた新しいリストをListBoxにセットし、ListBoxの一番上を選択した状態にし、ツールチップを表示します。
EVT_TEXTで結びつけ
ついで、これをエディットボックスと結びつけます。class MyTxtFrmの初期化メソッド__init__に、次の一文を付け加えます。
self.TxtCtr.Bind(wx.EVT_TEXT,self.OnText)
これで、エディットボックスに文字が入力されるたびに、OnText関数が呼び出されることになります。