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

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

PyGTKでフォント選択ボタンのフォントをテキストビューに適用

PyGTKでテキストビュー内の文字列にスタイルを適用する」ではGTK+のテキストビューにおいてテキストタグというものを用いてスタイルを変更するということを行ったが、ここではGTK+のフォント選択ボタンを用いて選択したフォントをテキストビューの選択範囲に適用してみることにする。
(2010/3/31)テキストビュー全体で用いられるフォントを指定する場合は一般的なGTK+ウィジェットのフォント変更の方法としてのmodify_font()を用いる。このとき、pango.FontDescriptionオブジェクトを用いるため、pangoモジュールのimportが必要。

import pango
...
[gtk.TextViewオブジェクト].modify_font(pango.FontDescription('[フォント名] ([スタイル...]) [サイズ]'))

メモ

フォントボタン(gtk.FontButtonオブジェクト)を押してダイアログでフォントを選択し、「OK」を押すと「font-set」シグナルが発生し、別途関連付けたハンドラで引数に受け取ったオブジェクト(もしくは直接フォントボタンのgtk.FontButtonオブジェクトを指定)のメンバ関数get_font_name()によりそのフォント名が得られるのだが、その形式が

[フォントファミリ名(スペースを含む場合あり)] [Bold/Italicなどのスタイル] [pt単位のサイズ]

となり、左のフォント名部分からたどっていくよりも一番右のサイズのほうがたどってスタイル部分まで取り出した残りをフォントファミリ名として用いるのが確実。
今回は最初に文字列オブジェクトのメンバ関数split()によりスペースでリストに分割した後でそのメンバ関数pop()を用いて最後の要素を取り出しながら処理を行い、残ったファミリ名部分はスペースで区切って連結するという方法をとったが、split()を用いずに最後のスペース文字の前後に分けるという操作を繰り返しながら解析することもできそう。
テキストタグの設定としては

# (texttagはgtk.TextTagオブジェクト, bold/italic/obliqueは真偽値)

texttag.props.family = family
texttag.props.size_points = int(size)
if bold:
  texttag.props.weight = pango.WEIGHT_BOLD
else:
  texttag.props.weight = pango.WEIGHT_NORMAL
if italic:
  texttag.props.style = pango.STYLE_ITALIC
elif oblique:
  texttag.props.style = pango.STYLE_OBLIQUE
else:
  texttag.props.style = pango.STYLE_NORMAL

のようにした。
(2010/2/25)propsメンバを用いた記述に変更
下はコード例。
[任意]ファイル名: texttagfonttest.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.props.submenu = self.__menu_file
    self.__menubar = gtk.MenuBar()
    self.__menubar.append(self.__item_file)
    # テキストビュー
    self.__texttagtable = gtk.TextTagTable()
    self.__textbuf = gtk.TextBuffer(self.__texttagtable)
    self.__textview = gtk.TextView(self.__textbuf)
    self.__textview.props.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.__fontbtn = gtk.FontButton('Sans 10')
    self.__btn_clear = gtk.Button(stock=gtk.STOCK_CLEAR)
    # レイアウト用コンテナ
    self.__hbox = gtk.HBox()
    self.__hbox.pack_start(self.__fontbtn)
    self.__hbox.pack_start(self.__btn_clear)
    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.__fontbtn.connect('font-set', self.__on_fontbtn_fontset)
    self.__btn_clear.connect('clicked', self.__on_btn_clear_clicked)
    # ウィンドウ
    self.add(self.__vbox)
    self.set_size_request(350, 300)
  def __on_fontbtn_fontset(self, widget):
    """
    フォント選択ボタンのダイアログでOKが押されたら選択範囲のフォントを変更
    """
    selection = self.__textbuf.get_selection_bounds()
    # 選択範囲がない場合はNoneが返る
    if selection:
      # 確認用端末出力
      print 'font_name: "%s"' % widget.props.font_name
      font = widget.props.font_name.split()
      # 最後の数字がpt単位のサイズ
      size = font.pop()
      # スタイルに関するフラグ
      italic = oblique = bold = False
      # サイズの1つ手前にスタイルの記述があればフラグに追加
      # フォントファミリ名には含めないようにする
      while True:
        style = font.pop()
        if style == 'Italic':
          italic = True
        elif style == 'Oblique':
          oblique = True
        elif style == 'Bold' or style == 'Semi-Bold':
          # 今回Semi-BoldはBoldと同じように扱うことにする
          bold = True
        else:
          # この時点でファミリ名の最後のトークンが入っている
          break
      # Regularなどは自動で削られるが、MediumやBookは消えない
      # (削らなくても表示に問題はない?)
      if style == 'Medium' or style == 'Book':
        style = font.pop()
      # 残っているトークンをスペースで連結して
      # 最後に先ほどのpop()で取り出した分をくっつけてファミリ名を作る
      family = ''
      for part in font:
        family += '%s ' % part
      family += style
      # 確認用端末出力
      print 'family:%s bold:%s italic:%s oblique=%s size:%d' % (family, str(bold), str(italic), str(oblique), int(size))
      # テキストタグを作成して設定
      texttag = gtk.TextTag()
      self.__texttagtable.add(texttag)
      texttag.props.family = family
      texttag.props.size_points = int(size)
      if bold:
        texttag.props.weight = pango.WEIGHT_BOLD
      else:
        texttag.props.weight = pango.WEIGHT_NORMAL
      if italic:
        texttag.props.style = pango.STYLE_ITALIC
      elif oblique:
        texttag.props.style = pango.STYLE_OBLIQUE
      else:
        texttag.props.style = pango.STYLE_NORMAL
      # 選択範囲の開始点と終了点のgtk.TextIterオブジェクトが得られる
      (iter_start, iter_end) = selection
      # テキストタグの適用
      self.__textbuf.apply_tag(texttag, iter_start, iter_end)
  def __on_btn_clear_clicked(self, widget):
    """
    クリアボタンが押されたらその範囲のテキストタグを全て取り除く
    """
    selection = self.__textbuf.get_selection_bounds()
    if selection:
      (iter_start, iter_end) = selection
      self.__textbuf.remove_all_tags(iter_start, iter_end)

class PyGTKTextTagFontTest:
  """
  テキストタグのテスト(フォントボタンを用いたフォント設定)
  """
  def main(self):
    """
    アプリケーションのメイン処理
    """
    win = MainWindow()
    win.show_all()
    gtk.main()


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

(2010/2/25)propsメンバを用いた記述に変更
テキストを選択してフォント選択ダイアログを開き、フォントを選択して「OK」を押すとその部分のフォントが変更される。クリアボタンを押すと選択範囲のスタイルがクリアされる。

関連記事:

使用したバージョン: