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

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

PyGTKでツリービューの各テキストセルに対してスタイルを適用する(前半)

PyGTKのツリービューにおいて、テキストを描画するセルに対して前景色/背景色やフォント(ファミリ/スタイル/サイズ)などのスタイルを設定するには、セルを描画するオブジェクトのgtk.CellRendererTextクラスのGObjectプロパティ(関連記事)を用いて指定する。
(2010/4/4)ツリービューの中の全ての(既定の)フォントを変更するには一般的なGUI部品のフォント変更の方法としてのmodify_font()を用いるのが楽。

[gtk.TreeViewオブジェクト].modify_font(pango.FontDescription('[ファミリ名] ([スタイル...]) [サイズ]'))

コラムに属する全てのセルで共通のスタイルを指定する場合

セルレンダラのオブジェクトを先に作成しておき、GObjectプロパティを設定したものをgtk.TreeViewColumnオブジェクトの2番目のコンストラクタ引数に指定する。
セルレンダラのクラス(セルにテキストを描画する際にはgtk.CellRendererTextクラス)を継承し、__init__()の中でプロパティに値を設定すると、スタイル指定の記述をそれぞれクラス定義へ分けられるので、コードは見やすくなる。

(個別のオブジェクトにプロパティを指定する方法)
renderer = gtk.CellRendererText()
renderer.props.foreground = '#ff0000'

column = gtk.TreeViewColumn('ColumnStr',
                         renderer,
                         ...
                         )


(クラス継承を利用した方法)
class MyCellRendererText(gtk.CellRendererText):
  def __init__(self):
    gtk.CellRendererText.__init__(self)
    self.props.foreground = '#ff0000'

...

col = gtk.TreeViewColumn('ColumnStr',
                         MyCellRendererText(),
                         ...
                         )

セルごとに異なったスタイルを指定できるようにする場合

モデル(データ)側にスタイル情報を持たせることで、モデル側の各項目に異なるスタイルで表示するようにできる。
このスタイル情報を持ったモデル項目をツリービューの各コラムに関連付けるためには、gtk.TreeViewColumnオブジェクトの初期化時に

[gtk.CellRenderer系クラスのGObjectプロパティ名]=[コラム番号(モデル側の項目の番号)]

を3番目*1もしくはその後のコンストラクタ引数に指定する。これまでの記事で「text」というGObjectプロパティで表示テキストを指定してきたのと同じ要領。

(COLUMN_COL1_TEXT,
 COLUMN_COL1_FONT,
 COLUMN_COL1_FOREGROUND,
 COLUMN_COL1_BACKGROUND,
 COLUMN_COL2_TEXT,
 COLUMN_COL2_FONT,
 COLUMN_COL2_FOREGROUND,
 COLUMN_COL2_BACKGROUND,
) = range(8)
column1 = gtk.TreeViewColumn('Column1Str',
                             gtk.CellRendererText(),
                             text=COLUMN_COL1_TEXT,
                             font=COLUMN_COL1_FONT,
                             foreground=COLUMN_COL1_FOREGROUND,
                             background=COLUMN_COL1_BACKGROUND)
column2 = gtk.TreeViewColumn('Column2Str',
                             gtk.CellRendererText(),
                             ...
                             background=COLUMN_COL2_BACKGROUND)

表示する内容を含んだデータ(モデル)の側ではそれぞれのスタイル指定の内容(フォントや色を指定する文字列など)を含む項目を用意しておく必要がある。
項目の型はプロパティによって異なるが、文字列の場合は「str」,オブジェクトの場合は「object」,真偽値の場合は「bool」となる。

# text1/font1/fg1/bg1/text2/font2/fg2/bg2
liststore = gtk.ListStore(str, str, str, str, str, str, str, str)
tv = gtk.TreeView(model=liststore)

...

liststore.append(('text1', 'Serif 10', '#ff0000', '#eeeeee', 'text2', 'Sans Italic 10', '#000000', '#0000ff'))

セルレンダラクラスのGObjectプロパティの具体例

テキスト描画の前景色と背景色はそれぞれ「foreground」と「background」となり、フォントファミリは「family」、サイズは「size」や「size-points」でそれぞれ変更できるが、フォントとそのサイズについては「font」を用いて

[ファミリ名] ([BoldやItalicなどのスタイル...]) [pt単位のサイズ]

という書式で一括で変更できる。これらはgtk.CellRendererTextクラスのGObjectプロパティなので、テキスト以外を描画するセルレンダラでは使用できない。それぞれのセルレンダラのクラス(もしくは親クラスのgtk.CellRendererクラス)のGObjectプロパティを用いて表示を制御する。
gtk.CellRendererクラスの「cell-background-gdk」プロパティはgtk.gdk.Colorオブジェクトの形で背景色が指定でき、テキスト以外のセルでも利用できるが、(テキストではないので)前景色版はない。

[任意のセルレンダラオブジェクト].props.background_gdk = gtk.gdk.Color('#ff0000')

同一コラムに属するセルのスタイルを同じに指定した例

PyGTKでツリービューにリストのデータ(ListStore)を表示(簡単な例)」の例のフォントを全て「'Serif Bold Italic 10'」に、Family nameの背景色を「#ffcccc」に、Given nameの前景色を「#0000ff」にしたものとなる。
以前と同様、この中で使用した名前は架空のものであり、実在の個人名や団体名などと一致するものがあったとしても関係はない。
[任意]ファイル名: treeviewliststoretestwithstyle.py

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

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


class CellRendererTextWithFont(gtk.CellRendererText):
  """
  フォントを指定したテキストセルレンダラ
  """
  def __init__(self):
    gtk.CellRendererText.__init__(self)
    self.props.font = 'Serif Bold Italic 10'

class CellRendererFamily(CellRendererTextWithFont):
  """
  Family name用セルレンダラ
  """
  def __init__(self):
    CellRendererTextWithFont.__init__(self)
    self.props.background = '#ffcccc'
    # background_gdkプロパティはテキスト以外のセルレンダラの背景色にも使える
#    self.props.background_gdk = gtk.gdk.Color('#ffcccc')

class CellRendererGiven(CellRendererTextWithFont):
  """
  Given name用セルレンダラ
  """
  def __init__(self):
    CellRendererTextWithFont.__init__(self)
    self.props.foreground = '#0000ff'

class TreeViewWithColumn(gtk.TreeView):
  """
  コラムを含んだツリービュー
  """
  # コラム内の項目番号(連番)をrange()で作成する
  (
    COLUMN_NUM,
    COLUMN_FAMILY,
    COLUMN_GIVEN,
  ) = range(3)  # 実際の値は上から順に0,1,2が入る
  def __init__(self, *args, **kwargs):
    gtk.TreeView.__init__(self, *args, **kwargs)  # 必須
    # コラムの設定
    # ListStore/TreeStore上の項目とTreeView上に表示する項目・その位置とを
    # 関連付ける(コラム自体の設定としてのデータを持つこともできる)
    # 今回はセルレンダラはgtk.CellRendererTextを生成と同時に渡している
    # また、設定も表示するテキストを「text=」で指定しているだけ
    # セルのスタイルはgtk.CellRendererTextクラスのGObjectプロパティを用いて
    # gtk.TreeViewColumnオブジェクトのコンストラクタ引数として設定する
    # http://library.gnome.org/devel/pygtk/stable/class-gtkcellrenderertext.html
    # 「gtk.CellRendererText Properties」を参照
    self.__col_num = gtk.TreeViewColumn('No.',
                                        CellRendererTextWithFont(),
                                        text=TreeViewWithColumn.COLUMN_NUM)
    self.__col_family = gtk.TreeViewColumn('Family name',
                                           CellRendererFamily(),
                                           text=TreeViewWithColumn.COLUMN_FAMILY)
    self.__col_given = gtk.TreeViewColumn('Given name',
                                          CellRendererGiven(),
                                          text=TreeViewWithColumn.COLUMN_GIVEN)

    # コラムを追加
    self.append_column(self.__col_num)
    self.append_column(self.__col_family)
    self.append_column(self.__col_given)

class MainWindow(gtk.Window):
  """
  メインウィンドウ
  """
  # 直接ウィンドウとは関係ないが、データは便宜上ここに用意しておくことにする
  __data = \
  [
    (1, 'Tanaka', 'Ichiro'),
    (2, 'Yamana', 'Hanako'),
    (3, 'Urashima', 'Saburo'),
    (4, 'Kurusu', 'Santa'),
    (5, 'Handa', 'Fuuta'),
    (6, 'Umeno', 'Tsubomi'),
    (7, 'Yoshi', 'Yaruzo'),
    (8, 'Kawai', 'Nuko'),
    (9, 'Hoshi', 'Kintaro'),
    (10, 'Shirayuki', 'Himeko'),
    (11, 'Ashigaka', 'Yui'),
    (12, 'Ageyanagi', 'Masako'),
    (13, 'Torino', 'Kenta'),
    (14, 'Kubota', 'Mochio'),
    (15, 'Kuroi', 'Sora'),
    (16, 'Hirai', 'Shin'),
    (17, 'Akai', 'Midori'),
    (18, 'Nakano', 'Anko'),
    (19, 'Imai', 'Takeo'),
    (20, 'Kouno', 'Torio'),
    (21, 'Yoshino', 'Yasu'),
    (22, 'Komatsu', 'Taro'),
    (23, 'Kondo', 'Musashi'),
    (24, 'Ono', 'Ken'),
    (25, 'Mochida', 'Usuichi'),
    (26, 'Mochida', 'Kineko'),
    (27, 'Honma', 'Kayo'),
    (28, 'Matsuno', 'Sarunosuke'),
    (29, 'Nishi', 'Minami'),
    (30, 'Usui', 'Hikaru'),
    (31, 'Sato', 'Toshio'),
    (32, 'Doi', 'Tsubasa'),
    (33, 'Ishimaru', 'Denko'),
    (34, 'Usami', 'Mimi'),
    (35, 'Hattori', 'Shinobu'),
    (36, 'Kago', 'Yuri'),
    (37, 'Takeda', 'Ingen'),
    (38, 'Kai', 'Dankichi'),
    (39, 'Okusa', 'Ben'),
    (40, 'Hara', 'Tatsuo'),
    (41, 'Mizuno', 'Shizuku'),
    (42, 'Baba', 'Nana'),
  ]
  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)
    # ツリービュー
    # model(ListStore)はツリービューに連動させるリストデータ
    # 初期化時に型を項目ごとに指定する
    # 使用できる型は下のURLを参照・GObjectの各形式も使用可能
    # http://www.pygtk.org/pygtk2tutorial/sec-TreeModelInterface.html#sec-CreatingTreeStoreAndListStore
    # 今回は「[整数] + [文字列1] + [文字列2]」の形
    # データとの結び付けは初期化時もしくはプロパティmodelかset_model()で行う
    self.__treeview = TreeViewWithColumn(model=gtk.ListStore(int, str, str))
    self.__treeview.props.rules_hint = True  # 背景色のシマシマを付ける
    # ツリービュー向けスクロールウィンドウ
    self.__sw = gtk.ScrolledWindow()
    self.__sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
    self.__sw.add(self.__treeview)  # 子にツリービューを指定してスクロール可能にする
    # レイアウト用コンテナ
    self.__vbox = gtk.VBox()
    self.__vbox.pack_start(self.__menubar, expand=False, fill=False)
    self.__vbox.pack_start(self.__sw)
    # シグナル
    self.connect('delete_event', gtk.main_quit)
    self.__item_quit.connect('activate', gtk.main_quit)
    # データ追加
    for rec in self.__data:
      # 結び付けられたモデル(gtk.ListStore)のメンバ関数append()に*タプル*を渡す
      self.__treeview.props.model.append(rec)
    # ウィンドウ
    self.add(self.__vbox)
    self.set_size_request(300, 300)

class PyGTKTreeViewListStoreTestWithStyle:
  """
  リストを用いたツリービューのテスト・スタイル指定付き
  """
  def main(self):
    """
    アプリケーションのメイン処理
    """
    win = MainWindow()
    win.show_all()
    gtk.main()


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


このように、背景色を指定するとセルの端にすき間ができる場合があるが、これを詰める方法はあり、後半で扱う。

また、同じコラムの中でセルごとに異なったスタイルを指定できるようにした例についても後半で扱う。

(「PyGTKでツリービューの各テキストセルに対してスタイルを適用する(後半)」に続く)

関連記事:

参考URL:

使用したバージョン:

*1:1番目はコラム文字列,2番目はセルレンダラオブジェクト