L'Isola di Niente
L'Isola di Niente » PyGObject Tips » ツリービュー、リストビュー

ツリービュー、リストビュー

GTK+ ではツリービューとリストビューは同一のものです。
データモデルに GtkTreeStore, GtkListStore のどちらを使うかで確定されます。

また Windows でよく利用されている LISTBOX 相当の Widget は GTK2 時に廃止されました。
ちょっとした LISTBOX を利用したいという場合でも GtkTreeView を使う必要があります。

GtkTreeView

GtkTreeView を利用するには以下のウイジェットを予め作成する必要があります。

GtkTreeViewColumn 表示領域
GtkCellRenderer etc. 表示部分
GtkTreeModel (GtkTreeStore, GtkListStore) データモデル

モデルにデータ(文字列、アイコン等)を挿入するとコラムのレンダラ部に描写される。
ツリービュー自体はコントローラの役目というわけです。
コラムのヘッダ文字列はコラムで、表示非表示はビューで指定みたいな感じです。

以下はツリーとリストの簡単な例、PyGtk 互換でも書けますがココでは C 言語同様の書き方で
モデルが違うだけでツリービューにもリストビューにもなるのが解ると思います。
#!/usr/bin/env python3

from gi.repository import Gtk

YAMAHA = ("JOG", "Vino", "VOX", "BW'S")
HONDA = ("Today", "Zoomer", "giorno", "Dio")

class TreeWin(Gtk.Window):
    """
        単純なツリー
    """
    def __init__(self):
        Gtk.Window.__init__(self)
        # レンダラに文字列を描写したい場合は GtkCellRendererText
        cell = Gtk.CellRendererText.new()
        # コラムを作成しレンダラを挿入
        column = Gtk.TreeViewColumn.new()
        column.pack_start(cell, True)
        # コラムに何を表示するか指定
        column.add_attribute(cell, "text", 0)
        # コラムのタイトル
        column.set_title("50cc")
        # モデルを作成しデータを入れていく
        store = Gtk.TreeStore.new([str])
        it_yamaha = store.append(None, ["YAMAHA"])
        for s in YAMAHA:
            store.append(it_yamaha, [s])
        it_honda = store.append(None, ["HONDA"])
        for s in HONDA:
            store.append(it_honda, [s])
        # モデルを指定してツリービューを作成
        tree = Gtk.TreeView.new_with_model(store)
        # モデルを表示するコラムを指定
        tree.append_column(column)
        # いつもの処理
        self.add(tree)
        self.resize(100, 200)
        self.connect("delete-event", Gtk.main_quit)
        self.show_all()

TreeWin()
Gtk.main()

#!/usr/bin/env python3

from gi.repository import Gtk

GEN2 = ("PCX", "Address", "CYGNUS-X")

class TreeWin(Gtk.Window):
    """
        単純なリスト
    """
    def __init__(self):
        Gtk.Window.__init__(self)
        # レンダラに文字列を描写したい場合は GtkCellRendererText
        cell = Gtk.CellRendererText.new()
        # コラムを作成しレンダラを挿入
        column = Gtk.TreeViewColumn.new()
        column.pack_start(cell, True)
        # コラムに何を表示するか指定
        column.add_attribute(cell, "text", 0)
        # コラムのタイトル
        column.set_title("125cc")
        # モデルを作成しデータを入れていく
        store = Gtk.ListStore.new([str])
        for s in GEN2:
            store.append([s])
        # モデルを指定してツリービューを作成
        tree = Gtk.TreeView.new_with_model(store)
        # モデルを表示するコラムを指定
        tree.append_column(column)
        # いつもの処理
        self.add(tree)
        self.resize(100, 200)
        self.connect("delete-event", Gtk.main_quit)
        self.show_all()

TreeWin()
Gtk.main()

img/gtk_treeview.png

GtkTreeStore

GtkTreeStore をデータモデルに指定するとツリービューを作成できます。

データモデルは new の引数にリストで str 等の型を指定して作成します。
ツリーに表示する文字列は GtkTreeViewColumn で何番目データかを指定するので後ほど。

データを append する時の第一引数は GtkTreeIter で第二引数は作成時に指定した型をリストで渡します。
第一引数を None にするとルートになり、GtkTreeIter にするとその子になる。
append の戻り値は GtkTreeIter でその戻り値を利用します。

クリック等で選択を行うと GtkSelection の changed シグナルが発行される。
このシグナルを利用してデータモデルの値を取得したり変更したりが可能です。

主に devhelp 等のようにツリーで何か選択すると別ペインにデータを表示したい場合に利用します。
以下はそのサンプル。
#!/usr/bin/env python3

from gi.repository import Gtk

class TreeWin(Gtk.Window):
    """
        TreeView Sample Code
    """
    def __init__(self):
        Gtk.Window.__init__(self)
        # Column
        cell = Gtk.CellRendererText.new()
        column = Gtk.TreeViewColumn.new()
        column.pack_start(cell, True)
        column.add_attribute(cell, "text", 0)
        # TreeStore
        store = Gtk.TreeStore.new([str, str])
        tree = Gtk.TreeView.new_with_model(store)
        tree.append_column(column)
        # Hide Header
        tree.set_headers_visible(False)
        # Signal
        selection = tree.get_selection()
        selection.connect("changed", self.selection_changeed)
        #
        # Data
        it0 = store.append(None, ["new 250cc", "何か選択しやがれ"])
        store.append(it0, ["CBR250R", "単気筒www"])
        store.append(it0, ["GSR250", "重すぎwww"])
        store.append(it0, ["Z250", "バッタwww"])
        it1 = store.append(None, ["2st 250cc", "..."])
        store.append(it1, ["NSR250R", "サビサビじゃんwww"])
        store.append(it1, ["TZR250R", "古過ぎイラネwww"])
        # pack
        self.label = Gtk.Label()
        vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0)
        vbox.pack_start(tree, True, True, 0)
        vbox.pack_start(self.label, False, False, 0)
        # self
        self.add(vbox)
        self.set_title("TreeStore")
        self.resize(200, 200)
        self.connect("delete-event", Gtk.main_quit)
        self.show_all()

    def selection_changeed(self, widget, data=None):
        """
            Params @ widget: GtkTreeSelection
        """
        model, it = widget.get_selected()
        if it:
            self.label.set_text(model.get_value(it, 1))

TreeWin()
Gtk.main()


img/gtk_treestore.png

GtkListStore

GtkListStore をデータモデルに指定するとリストビューを作成できます。

データモデル作成は GtkTreeStore と同じで new の引数に型リストにして指定します。
append は Tree のような親子関係が無いので作成時に指定した型をリストで渡すだけです。

コラムを表示したい数だけ作成し add_attribute の第三引数で何番目データを表示するかを指定。
主にファイルマネージャの詳細表示のように表示したい場合に利用します。
以下はそのサンプル。
#!/usr/bin/env python3

from gi.repository import Gtk

class TreeWin(Gtk.Window):
    """
        ListView Sample Code
    """
    def __init__(self):
        Gtk.Window.__init__(self)
        # Column Name
        cell_first = Gtk.CellRendererText.new()
        column_first = Gtk.TreeViewColumn.new()
        column_first.pack_start(cell_first, True)         # CellRenderer
        column_first.add_attribute(cell_first, "text", 0) # Property and column position on the model
        column_first.set_title("Motor Cycle")             # Title
        # Column Description
        cell_second = Gtk.CellRendererText.new()
        column_second = Gtk.TreeViewColumn.new()
        column_second.pack_start(cell_second, True)
        column_second.add_attribute(cell_second, "text", 1)
        column_second.set_title("Description")
        # ListStore
        store = Gtk.ListStore.new([str, str])
        tree = Gtk.TreeView.new_with_model(store)
        tree.append_column(column_first)
        tree.append_column(column_second)
        #
        # Data
        store.append(["SR400", "古臭いwww"])
        store.append(["CB400SF", "教習車www"])
        store.append(["GSR400", "変態www"])
        # self
        self.add(tree)
        self.set_title("ListStore")
        self.connect("delete-event", Gtk.main_quit)
        self.show_all()

TreeWin()
Gtk.main()


img/gtk_liststore.png

GtkTreeViewColumn

GtkTreeViewColumn は表示領域及びモデルデータとレンダラの橋渡しを行います。
タイトル、レンダラのパック、そしてモデルのどの位置のデータをどのプロパティと結びつけるかを指定して利用します。

上記サンプルでは個別に指定していますが以下の関数を利用すれば一度に指定できます。
gtk_tree_view_column_new_with_attributes()
ただし PyGI ではこの関数は Gtk.TreeViewColumn() にバインドされています。
またこの場合プロパティとの結び付けは下記サンプルのように指定、又 NULL ターミネーターも不要。
以下は上記サンプルコードを書き替えたもの、同じものが作られるのが確認できる。
#!/usr/bin/env python3

from gi.repository import Gtk

class TreeWin(Gtk.Window):
    """
        gtk_tree_view_column_new_with_attributes()
    """
    def __init__(self):
        Gtk.Window.__init__(self)
        # Column Name
        cell_first = Gtk.CellRendererText.new()
        column_first = Gtk.TreeViewColumn( #.new_with_attributes(
                "Motor Cycle", # Title
                cell_first,    # CellRenderer
                text=0 )       # Property and column position on the model
        # Column Description
        cell_second = Gtk.CellRendererText.new()
        column_second = Gtk.TreeViewColumn(
                "Description",
                cell_second,
                text=1 )
        # ListStore
        store = Gtk.ListStore.new([str, str])
        tree = Gtk.TreeView.new_with_model(store)
        tree.append_column(column_first)
        tree.append_column(column_second)
        #
        # Data
        store.append(["SR400", "古臭いwww"])
        store.append(["CB400SF", "教習車www"])
        store.append(["GSR400", "変態www"])
        # self
        self.add(tree)
        self.set_title("ListStore")
        self.connect("delete-event", Gtk.main_quit)
        self.show_all()

TreeWin()
Gtk.main()

又、行毎に表示状態を変更させたい場合はコラムのシグナルを利用します。
gtk_tree_view_column_set_cell_data_func ()
関数で指定した GtkTreeCellDataFunc の引数に詳細が送られてきますので都度処理。

以下はチェックボックス状態で sensitive プロパティを切り替えする処理の例。
#!/usr/bin/env python3

from gi.repository import Gtk

class TreeWin(Gtk.Window):
    """
        set_cell_data_func
    """
    def __init__(self):
        Gtk.Window.__init__(self)
        # Column Toggle
        cell_toggle = Gtk.CellRendererToggle.new()
        column_toggle = Gtk.TreeViewColumn("Show", cell_toggle, active=0)
        # Column Text
        cell_text = Gtk.CellRendererText.new()
        column_text = Gtk.TreeViewColumn("text", cell_text, text=1)
        # Signal
        cell_toggle.connect("toggled", self.on_toggled)
        column_text.set_cell_data_func(cell_text, self.on_cell_func)
        # ListStore
        self.store = Gtk.ListStore.new([bool, str])
        tree = Gtk.TreeView.new_with_model(self.store)
        tree.append_column(column_toggle)
        tree.append_column(column_text)
        #
        # Data
        for i in range(10):
            self.store.append([i%2==0, "aaaaaaaaaa"])
        # self
        self.add(tree)
        self.set_title("Test")
        self.connect("delete-event", Gtk.main_quit)
        self.show_all()

    def on_toggled(self, widget, path, data=None):
        self.store[path][0] = not self.store[path][0]

    def on_cell_func(self, column, cell, model, it, data=None):
        cell.props.sensitive = model.get_value(it, 0)

TreeWin()
Gtk.main()

img/gtk_treecelldatafunc.png
Copyright(C) sasakima-nao All rights reserved 2002 --- 2017.