wxPythonでブックマーク管理ツールを作ろう!
準備
今回作るプログラムは、あらかじめブックマークを手動でテキストファイルに書き込んでおき、それを読み込む、という形式をとります。
まずそのブックマークを書き込んだファイルを作りましょう。
- bookmark.ini
つかさのほえほえ日記 |http://d.hatena.ne.jp/tukasa1919/ はてなブックマーク |http://b.hatena.ne.jp/tukasa1919/ 2ちゃんねる サーバ負荷監視所 |http://ch2.ath.cx/ Google リーダー | http://www.google.com/reader/view/#overview-page PPx help | http://homepage1.nifty.com/toro/ppxhlp.html Twitter | http://twitter.com/ wxPythonリファレンス | http://wxwindowsjp.sourceforge.jp/docs/html/wx/wx26.htm#classref gmail | https://mail.google.com/mail 簡易AAエディタ | http://iranegi.s5.xrea.com:8080/2ch/aaedit/aaedit.php
"|"で区切っています。サイト名とurlが対になってます。これをスクリプトと同じフォルダにおくことで、ブックマークを読み込みます。
また、Migemoも使うので、それに関係するファイル(migemo.dll migemo.pyd Dict)も、同じフォルダに用意しておいてください。
では次に、このファイルを読み込むプログラムを作成します。
GUIを作る
まずはGUIを作るところからはじめましょう。
- Bookmark01.py
import wx class MyTxtFrm(wx.Frame): def __init__(self, parent, id): wx.Frame.__init__(self, parent, id) self.TxtCtr = wx.TextCtrl(self, -1) self.LBox = wx.ListBox(self, -1) 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) 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()
wxPythonでAA管理ツールをつくろう!第一回 GUIを作ると全く同じですので、詳しくはそこを参照してください。
リストを作る
次に、bookmark.iniを読み込み、サイト名とurlが対になった辞書を作り、サイト名のリストをセットします。
- Bookmark02.py
import wx class MyTxtFrm(wx.Frame): def __init__(self, parent, id): wx.Frame.__init__(self, parent, id) self.TxtCtr = wx.TextCtrl(self, -1) self.LBox = wx.ListBox(self, -1) bf = open("bookmark.ini") booklist = bf.readlines() booklist = map((lambda x: x.decode("shift-jis").strip()),booklist) booklist = map((lambda x: x.rsplit("|")),booklist) booklist = map((lambda x: tuple(x)),booklist) self.BookDict = {} for x in booklist: name = x[0].strip() url = x[1].strip() self.BookDict[name] = url self.site_list = self.BookDict.keys() self.LBox.Set(self.site_list) 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) 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()
辞書の作成
bf = open("bookmark.ini") booklist = bf.readlines() booklist = map((lambda x: x.decode("shift-jis").strip()),booklist) booklist = map((lambda x: x.rsplit("|")),booklist) booklist = map((lambda x: tuple(x)),booklist) self.BookDict = {} for x in booklist: name = x[0].strip() url = x[1].strip() self.BookDict[name] = url
まずは辞書を作るところから。"|"を区切り文字にして、サイト名とurlの対を取り出します。
次いで空の辞書self.BookDictを作り、サイト名をキーに。urlを値にして一つずつ登録します。
これで次のような辞書ができます。
{'つかさのほえほえ日記' : 'http://d.hatena.ne.jp/tukasa1919/', 'はてなブックマーク' : 'http://b.hatena.ne.jp/tukasa1919/', '2ちゃんねる サーバ負荷監視所' : 'http://ch2.ath.cx/', 'Google リーダー' : 'http://www.google.com/reader/view/#overview-page', 以下略}
リストボックスに登録
self.site_list = self.BookDict.keys() self.LBox.Set(self.site_list)
次に、この辞書からサイト名のリストを作ります。キーのリストをkeys()で作成して、それをListBoxにセットします。
これで見た目は完成です。
次に、カーソル移動とMigemoによるインクリメンタルサーチができるようにします。
カーソル移動とインクリメンタルサーチの追加(コピペ)
- Bookmark03.py
import wx,migemo,re migemo_object = None 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) self.TxtCtr = wx.TextCtrl(self, -1) self.LBox = wx.ListBox(self, -1) bf = open("bookmark.ini") booklist = bf.readlines() booklist = map((lambda x: x.decode("shift-jis").strip()),booklist) booklist = map((lambda x: x.rsplit("|")),booklist) booklist = map((lambda x: tuple(x)),booklist) self.BookDict = {} for x in booklist: name = x[0].strip() url = x[1].strip() self.BookDict[name] = url self.site_list = self.BookDict.keys() self.LBox.Set(self.site_list) 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) 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) elif key in (wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER): fr = self.LBox.GetStringSelection() webbrowser.open(self.BookDict[fr]) wx.Exit() else: event.Skip() def OnText(self,event): word = self.TxtCtr.GetValue() isearch = IncrementalSearch(word,self.BookDict.keys()) try: new_list = isearch.GetBackList() except: return False if new_list: self.LBox.Set(new_list) self.LBox.SetSelection(0) else: event.Skip() event.Skip() 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()
変更したのは以下の点です。
そして、
self.TxtCtr.Bind(wx.EVT_KEY_DOWN, self.OnKeyChar) self.TxtCtr.Bind(wx.EVT_TEXT,self.OnText)
を__ini__に付け足してます。
やってることはAA管理ツールのコードの流用です。wxPythonでAA管理ツールを作ろう!第六回 PyMigemoでインクリメンタルサーチあたりのコードから該当箇所をコピペしてます。
URLをデフォルトのブラウザで開く
いよいよラストです。
実際にブックマークをブラウザで開けるようにしましょう。変更点は、
- urlをデフォルトのブラウザで開くためにwebbrowserをインポート
- OnKeyCharのEnterの箇所
です。
- Bookmark04.py
import wx,migemo,re,webbrowser migemo_object = None 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) self.TxtCtr = wx.TextCtrl(self, -1) self.LBox = wx.ListBox(self, -1) bf = open("bookmark.ini") booklist = bf.readlines() booklist = map((lambda x: x.decode("shift-jis").strip()),booklist) booklist = map((lambda x: x.rsplit("|")),booklist) booklist = map((lambda x: tuple(x)),booklist) self.BookDict = {} for x in booklist: name = x[0].strip() url = x[1].strip() self.BookDict[name] = url self.site_list = self.BookDict.keys() self.LBox.Set(self.site_list) 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) 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) elif key in (wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER): fr = self.LBox.GetStringSelection() webbrowser.open(self.BookDict[fr]) wx.Exit() else: event.Skip() def OnText(self,event): word = self.TxtCtr.GetValue() isearch = IncrementalSearch(word,self.BookDict.keys()) try: new_list = isearch.GetBackList() except: return False if new_list: self.LBox.Set(new_list) self.LBox.SetSelection(0) else: event.Skip() event.Skip() 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()
elif key in (wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER): fr = self.LBox.GetStringSelection() webbrowser.open(self.BookDict[fr]) wx.Exit()
Enterを押したときに、サイト名に対応したurlを開くようにします。
- 選択しているサイト名をキーにして、urlを取り出し
- それをデフォルトのブラウザで開く
ということをしてるわけですね。
まとめ
要は、AA管理ツールの枠組みを使えば、
- 登録する辞書
- Enterを押した時の挙動
の二つをいじるだけで、いろんなことができるというわけです。
他に僕が思いついたのは、定型文の管理。あと、辞書をうまく作ることができれば、クリップボード履歴を監視するeClipみたいなのも作れますね。