pygtkで遊ぼう(22) gtk.TextViewの検索機能を使う
昨日の日記でPythonの正規検索モジュールで検索した結果をgktsourceview.SourceViewのサブクラスに表示したということを書きました。
で、宿題として以下のことが残っていました。
・検索した結果を全てハイライトさせるため、検索結果が多くなると、検索結果が表示されるまでに時間がかかる。
・インクリメンタルサーチにreを使うのはちょっと。。
今日は、上記の問題を解決する方向でいろいろためしてみました。
今回変更したのは、
(1) 検索キーワード文字列に変更があった場合、reモジュールで検索を行わないように。
(2) 1の代わりにSourceViewのサブクラスに下記のメソッドを追加し、それを呼び出すように。
(3) 該当した文字列を全てハイライトせず、カーソルのある位置の文字列のみをハイライトするように。
です。
今回の修正を行った結果、かなりサクサク動くインクリメンタルサーチになりました。
(もう少し調整すればもっとよくなると思います)
少し補則です。
下記のメソッド中でself.searchIterというインスタンス変数を操作していますが、
この変数はカーソルの位置です。検索された文字にカーソルを移動させ、次の検索が行われた場合には、
そのカーソルの直前(または直後)で見付かった文字列に移動させるために使用しています。
def search_text(self, keyword, direction=1, next=False): ''' テキストバッファ中の指定された文字列を検索する。 keyword: 検索文字列 direction: 検索方向(定数) DIRECTION_NEXT(1) : 後方検索 DIRECTION_PREV(-1) : 前方検索 next: True -> 次の位置へ移動する False-> 次の位置へ移動しない ''' bf = self.get_buffer() if bf == None: return False # 現在設定さているタグを削除 if self.searchTag: bf.remove_tag(self.searchTag, bf.get_start_iter(), bf.get_end_iter()) self.searchTag = None # キーワードが指定されていない場合には処理を中断 if keyword == None or len(keyword) == 0: return False # 検索領域を取得(バッファ全体) start, end = bf.get_bounds() if self.searchIter: if next: offset = self.searchIter.get_offset() offset += direction self.searchIter = bf.get_iter_at_offset(offset) else: if direction == self.DIRECTION_NEXT: self.searchIter = start else: self.searchIter = end # 検索 searchFunc = self.searchIter.forward_search if direction < 0: searchFunc = self.searchIter.backward_search result = searchFunc(keyword, gtk.TEXT_SEARCH_TEXT_ONLY) if result == None: self.searchIter = None return False start, end = result self.searchTag = bf.create_tag(foreground='#FFFF00', underline=pango.UNDERLINE_DOUBLE) bf.apply_tag(self.searchTag, start, end) # カーソル位置が常に見えるように調整 bf.place_cursor(end) self.scroll_to_iter(end, 0.2) self.searchIter = start return True
さて、今回のお題「TextViewの検索機能」についてですが、
手順としては、
(1) TextViewからTextBufferを取得する。
(2) 取得したTextBufferからからTextIterを取得する。
(3) 取得したTextIterのメソッドforward_search(), backward_search()を使用し、イテレータの直後(または直前)の
文字列を検索する。
といった感じです。検索自体は結構簡単です。
上のソースコードが長くなってしまったのは、私の技量が足りないせいと、インクリメンタルサーチさせているから。。。だと思います。
で、めでたしめでたし。。。とはいきませんでした。
またまた新たな宿題が。。。。
今回の宿題は
・TextIterのforward_search(), backward_search()はどうやら「大文字、小文字を区別しない」という指定ができないらしい。。
ということです。
ちょっとショックでした。。。
ということで、次回はその宿題をなんとかするべく、いろいろとためしてみようと思います。