試験運用中なLinux備忘録・旧記事

はてなダイアリーで公開していた2007年5月-2015年3月の記事を保存しています。

PyGTKでテキストビュー内の文字列にスタイルを適用する

GTK+のテキストビュー(gtk.TextViewオブジェクト)に表示されるテキストバッファ(gtk.TextBufferオブジェクト)には文字情報以外に書式などの情報を適用するタグ(gtk.TextTagオブジェクト)を含めることができる。これによりテキストビュー内の一部(もしくは全て)の文字列にスタイルを適用できる。

流れ

  1. gtk.TextTagオブジェクトを作成し、必要であれば名前も付ける(効果は後述・既定では名前は付かない)
  2. gtk.TextTagオブジェクトgtk.TextBufferオブジェクトの「テーブル」に追加(具体的には下のコード例を参照)
  3. gtk.TextBufferオブジェクトの中のスタイルを適用したい範囲(始点と終点)を示す2つのgtk.TextIterオブジェクト*1を用意
  4. gtk.TextBufferオブジェクトメンバ関数apply_tag()/remove_tag()でタグを適用/解除・あるいはタグ生成時に名前を付けた場合apply_tag_by_name()/remove_tag_by_name()を用いて名前による指定を行うこともできる

gtk.TextIterオブジェクトを用意する方法

設定できる書式など

gtk.TextTagオブジェクトの「gtk.TextTag Properties」のところに載っている項目が利用でき、メンバ関数set_property()により設定していく。

コード例

選択した範囲に対して「On」で強調され、「Off」で強調が解除される。
[任意]ファイル名: texttagtest.py

#! /usr/bin/python
# -*- encoding: utf-8 -*-

import sys
try:
  import pygtk
  pygtk.require('2.0')
except:
  pass
try:
  import pango
  import gtk
except:
  print >> sys.stderr, 'Error: PyGTK is not installed'
  sys.exit(1)


class MainWindow(gtk.Window):
  """
  メインウィンドウ
  """
  def __init__(self, *args, **kwargs):
    gtk.Window.__init__(self, *args, **kwargs)
    # ショートカットキー(アクセラレータ)
    self.__accelgroup = gtk.AccelGroup()
    self.add_accel_group(self.__accelgroup)
    # メニュー項目
    self.__item_quit = gtk.ImageMenuItem(gtk.STOCK_QUIT, self.__accelgroup)
    self.__menu_file = gtk.Menu()
    self.__menu_file.add(self.__item_quit)
    self.__item_file = gtk.MenuItem('_File')
    self.__item_file.set_submenu(self.__menu_file)
    self.__menubar = gtk.MenuBar()
    self.__menubar.append(self.__item_file)
    # テキストビュー
    self.__textbuf = gtk.TextBuffer()
    self.__textview = gtk.TextView(self.__textbuf)
    self.__textview.set_wrap_mode(gtk.WRAP_CHAR)
    self.__sw = gtk.ScrolledWindow()
    self.__sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
    self.__sw.add(self.__textview)
    # テキストタグ
    self.__texttag_strong = gtk.TextTag('strong')  # タグに名前を付けられる
    # 以下色々なスタイルを適当に設定
    self.__texttag_strong.set_properties(foreground='yellow',
                                         background='black')
    self.__texttag_strong.set_property('family', 'Serif')
    self.__texttag_strong.set_property('size-points', 16)
    self.__texttag_strong.set_property('weight', pango.WEIGHT_BOLD)
    self.__texttag_strong.set_property('underline', pango.UNDERLINE_DOUBLE)
    self.__texttag_strong.set_property('style', pango.STYLE_ITALIC)
    # 各タグはテキストバッファの「テーブル」に登録しておく必要がある
    self.__textbuf.get_tag_table().add(self.__texttag_strong)
    # ボタン
    self.__button_on = gtk.Button('On')
    self.__button_off = gtk.Button('Off')
    # レイアウト用コンテナ
    self.__hbox = gtk.HBox()
    self.__hbox.pack_start(self.__button_on)
    self.__hbox.pack_start(self.__button_off)
    self.__vbox = gtk.VBox()
    self.__vbox.pack_start(self.__menubar, expand=False, fill=False)
    self.__vbox.pack_start(self.__sw)
    self.__vbox.pack_start(self.__hbox, expand=False, fill=False)
    # シグナル
    self.connect('delete_event', gtk.main_quit)
    self.__item_quit.connect('activate', gtk.main_quit)
    self.__button_on.connect('clicked', self.__on_button_on_clicked)
    self.__button_off.connect('clicked', self.__on_button_off_clicked)
    # ウィンドウ
    self.add(self.__vbox)
    self.set_size_request(350, 300)
  def __on_button_on_clicked(self, widget):
    """
    Onボタンが押されたときに選択範囲の強調スタイルを有効にする
    """
    selection = self.__textbuf.get_selection_bounds()
    # 選択範囲がない場合はNoneが返る
    if not selection:
      print 'No selections'
      return
    # 選択範囲の開始点と終了点のgtk.TextIterオブジェクトが得られる
    (iter_start, iter_end) = selection
    # 範囲とタグを指定してタグを適用
    self.__textbuf.apply_tag(self.__texttag_strong, iter_start, iter_end)
  def __on_button_off_clicked(self, widget):
    """
    Offボタンが押されたときに選択範囲の強調スタイルをなしにする
    """
    selection = self.__textbuf.get_selection_bounds()
    if not selection:
      print 'No selections'
      return
    (iter_start, iter_end) = selection
    # タグの名前による指定(「_by_name」付きのメンバ関数)も可能
    # 内部的にこの文字列からタグオブジェクトをたどって処理される
    self.__textbuf.remove_tag_by_name('strong', iter_start, iter_end)

class PyGTKTextTagTest:
  """
  テキストタグのテスト
  """
  def main(self):
    """
    アプリケーションのメイン処理
    """
    win = MainWindow()
    win.show_all()
    gtk.main()


if __name__ == '__main__':
  app = PyGTKTextTagTest()
  app.main()

(2010/2/15)細かい部分を微調整(動作は以前と同じ)

関連記事:

参考URL:

*1:テキストバッファを操作するためのカーソル(キャレット)のようなオブジェクトで、バッファ内の位置を保持している