Paepoi

Paepoi » PyGObject Tips » Gtk(PyGObject) Tips | レンダラ

Gtk(PyGObject) Tips | レンダラ

# 最終更新日 2019.09.01

2019 年現在の仕様に追記と書き換え、PyGtk 互換の削除。

GtkCellRenderer は GtkTreeView, GtkComboBox で使います。
データをどのように表示させるかを指定する Widget です。
直接使うことはありません、派生された以下を利用します。

レンダラプロパティ
GtkCellRendererTextstrtext
+----GtkCellRendererAccelstrtext
+----GtkCellRendererCombostrtext
+----GtkCellRendererSpininttext
GtkCellRendererPixbufGdkPixbuf or strpixbuf or icon-name
GtkCellRendererProgressintvalue
GtkCellRendererSpinnerinttext
GtkCellRendererToggleboolactive

GtkCellRendererText
GtkCellRendererText は名前のとおり文字列を表示するレンダラです。

基本的なものですので一般例はツリービュー、リストビューを見てもらうとしてここでは編集手段を。
editable プロパティを True にして edited シグナルを利用すれば編集可能。
データモデル (GtkTreeStore, GtkListStore) のデータを書き換える必要があります。
そのレンダラを使用したコラムはデータ追加を行ってもすべて書き換え可能になります。

edited ハンドラで PyGObject の場合は以下のようにすれば GtkTreeModel のデータを変更できる。
コメント内に devhelp の正式な手段もいれておきましたので参照ください。
#!/usr/bin/env python3

import sys, gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

class Win(Gtk.ApplicationWindow):
    '''
        レンダラ
    '''
    def __init__(self, app):
        Gtk.ApplicationWindow.__init__(self, application=app, title='Py')
        # CellRendererText
        cell = Gtk.CellRendererText(editable=True)
        # Column
        column = Gtk.TreeViewColumn()
        column.pack_start(cell, True)
        column.add_attribute(cell, 'text', 0)
        # ListStore
        store = Gtk.ListStore.new([str])
        store.append(['W クリックで'])
        store.append(['書き換え可能'])
        tree = Gtk.TreeView(model=store, headers_visible=False)
        tree.append_column(column)
        # Button
        button = Gtk.Button(label='レンダラの追加')
        # Signal
        cell.connect('edited', self.on_cell_edited, store)
        button.connect('clicked', self.on_button_clicked, store)
        # pack
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        vbox.pack_start(tree, False, False, 0)
        vbox.pack_start(button, False, False, 0)
        self.add(vbox)
        self.show_all()

    def on_cell_edited(self, renderer, path, new_text, store):
        '''
            # C Like
            it = store.get_iter_from_string(path)
            store.set_value(it, 0, new_text)
        '''
        store[path][0] = new_text
        

    def on_button_clicked(self, widget, store):
        '''
            レンダラの追加
        '''
        store.append(['追加レンダラ'])

class App(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(self)

    def do_startup(self):
        Gtk.Application.do_startup(self)
        Win(self)

    def do_activate(self):
        self.props.active_window.present()

app = App()
app.run(sys.argv)
etc/r01.png
GtkCellRendererAccel
GtkCellRendererAccel は GNOME のキーボードショートカット設定等に使われているもの。
キーボードアクセラレータ関連に特化したウイジェットのようです。

editable プロパティを真にしてレンダラ部をクリックすると accel-edited シグナルが発行される。
このパラメータを利用して GtkAccelMap の変更を行うといったことができます。
#!/usr/bin/env python3

import sys, gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gio

class Win(Gtk.ApplicationWindow):
    '''
        レンダラ
    '''
    def __init__(self, app):
        Gtk.ApplicationWindow.__init__(self, application=app, title='Py')
        # Renderer
        cell_text = Gtk.CellRendererText()
        cell_accel = Gtk.CellRendererAccel(editable=True)
        # Signal
        cell_accel.connect('accel-edited', self.on_accel_edited)
        # Column
        column_text = Gtk.TreeViewColumn(title='動作')
        column_text.pack_start(cell_text, True)
        column_text.add_attribute(cell_text, 'text', 0)
        column_accel = Gtk.TreeViewColumn(title='キー')
        column_accel.pack_start(cell_accel, True)
        column_accel.add_attribute(cell_accel, 'text', 1)
        # ListStore
        self.store = Gtk.ListStore.new([str, str])
        self.store.append(['新規ウインドウ', 'Ctrl+N'])
        self.store.append(['終了', 'Ctrl+Q'])
        tree = Gtk.TreeView(model=self.store)
        tree.append_column(column_text)
        tree.append_column(column_accel)
        #
        self.add(tree)
        self.show_all()

    def on_accel_edited(self, accel, path_string, accel_key, accel_mods, hardware_keycode):
        '''
            以下だと '<Primary>q' みたいな文字列になる
            Primary と Control は同じ意味に扱われる
            Gtk.accelerator_name(accel_key, accel_mods)
        '''
        self.store[path_string][1] = Gtk.accelerator_get_label(accel_key, accel_mods)
        # 変更
        s = Gtk.accelerator_name(accel_key, accel_mods)
        if self.store[path_string][0] == '新規ウインドウ':
            self.props.application.set_accels_for_action('app.new_window_action', [s])
        elif self.store[path_string][0] == '終了':
            self.props.application.set_accels_for_action('app.quit_action', [s])
        

class App(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(self)

    def do_startup(self):
        Gtk.Application.do_startup(self)
        # GAction 作成
        new_window_action = Gio.SimpleAction(name='new_window_action')
        quit_action       = Gio.SimpleAction(name='quit_action')
        # 追加
        self.add_action(new_window_action)
        self.add_action(quit_action)
        # アクセラレーターの指定
        self.set_accels_for_action('app.new_window_action', ['<Control>N'])
        self.set_accels_for_action('app.quit_action', ['<Control>Q'])
        # シグナル
        new_window_action.connect('activate', self.on_new_window_action)
        quit_action.connect('activate', self.on_quit_action)
        Win(self)

    def on_new_window_action(self, action, parameter):
        Win(self)

    def on_quit_action(self, action, parameter):
        self.quit()

    def do_activate(self):
        self.props.active_window.present()

app = App()
app.run(sys.argv)
etc/r02.png
GtkCellRendererCombo
GtkCellRendererCombo はその名のとおりコンボボックスです。
GtkTreeModel に入れたデータから選択する GtkComboBox と同様に利用できます。

プロパティの editable を True にして model を指定すればドロップ選択が可能になる。
has-entry が True なら直書きもできる、text-column は指定した model のデータ位置。
又 edited シグナルも利用できるようです。
#!/usr/bin/env python3

import sys, gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

class Win(Gtk.ApplicationWindow):
    '''
        レンダラ
    '''
    def __init__(self, app):
        Gtk.ApplicationWindow.__init__(self, application=app, title='Py')
        # Data
        store_items = Gtk.ListStore.new([str])
        for item in ['純白', 'シマパン', 'Tバック', 'はいていない']:
            store_items.append([item])
        # Renderer
        cell_text = Gtk.CellRendererText()
        cell_combo = Gtk.CellRendererCombo(editable=True, model=store_items, has_entry=False, text_column=0)
        # Signal
        cell_combo.connect('changed', self.on_changed, store_items)
        # Column
        column_text = Gtk.TreeViewColumn(title='好きな下着')
        column_text.pack_start(cell_text, True)
        column_text.add_attribute(cell_text, 'text', 0)
        column_combo = Gtk.TreeViewColumn(title='選択')
        column_combo.pack_start(cell_combo, True)
        column_combo.add_attribute(cell_combo, 'text', 1)
        # ListStore
        self.store = Gtk.ListStore.new([str, str])
        tree = Gtk.TreeView(model=self.store)
        tree.append_column(column_text)
        tree.append_column(column_combo)
        # Data
        self.store.append(['JK', '選んでください'])
        self.store.append(['熟女', '選んでください'])
        #
        self.add(tree)
        self.show_all()

    def on_changed(self, combo, path_string, new_iter, model):
        self.store[path_string][1] = model.get_value(new_iter, 0)

class App(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(self)

    def do_startup(self):
        Gtk.Application.do_startup(self)
        Win(self)

    def do_activate(self):
        self.props.active_window.present()

app = App()
app.run(sys.argv)
etc/r03.png
GtkCellRendererSpin
GtkCellRendererSpin はその名のとおりスピンボタンです。
GtkAdjustment を adjustment プロパティに入れて利用します。

TreeModel で使用するデータは当然 int ですがレンダラは text なので注意しましょう。
専用のシグナルは用意されていません、edited シグナルを利用します。
#!/usr/bin/env python3

import sys, gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

class Win(Gtk.ApplicationWindow):
    '''
        レンダラ
    '''
    def __init__(self, app):
        Gtk.ApplicationWindow.__init__(self, application=app, title='Py')
        # Renderer
        cell_text = Gtk.CellRendererText()
        cell_spin = Gtk.CellRendererSpin(editable=True, adjustment=Gtk.Adjustment.new(0, 50, 2000, 1, 100, 0))
        # Signal
        cell_spin.connect('edited', self.on_cell_edited)
        # Column
        column_text = Gtk.TreeViewColumn(title='変な排気量')
        column_text.pack_start(cell_text, True)
        column_text.add_attribute(cell_text, 'text', 0)
        column_spin = Gtk.TreeViewColumn(title='cc')
        column_spin.pack_start(cell_spin, True)
        column_spin.add_attribute(cell_spin, 'text', 1)
        # ListStore
        self.store = Gtk.ListStore.new([str, int])
        tree = Gtk.TreeView(model=self.store)
        tree.append_column(column_text)
        tree.append_column(column_spin)
        # Data
        self.store.append(['CB500X', 471])
        self.store.append(['Street Triple', 675])
        #
        self.add(tree)
        self.show_all()

    def on_cell_edited(self, renderer, path, new_text):
        '''
            renderer.props.digits がゼロのままなんだが...
        '''
        self.store[path][1] = int(new_text)

class App(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(self)

    def do_startup(self):
        Gtk.Application.do_startup(self)
        Win(self)

    def do_activate(self):
        self.props.active_window.present()

app = App()
app.run(sys.argv)
etc/r04.png
GtkCellRendererPixbuf
GtkCellRendererPixbuf はその名のとおり画像を表示するレンダラです。

ストックは非推奨なので代わりに icon-name プロパティを使ってください。
自分で用意した画像を表示する場合は pixbuf プロパティ指定で GdkPixbuf を。

pixbuf-expander-closed, pixbuf-expander-open は is-expand プロパティが真の時に使う。
ツリー表示を開いた閉じたで画像を変更したい場合なんかに利用します。

GDK_TYPE_PIXBUF の GType は以下のように。
#!/usr/bin/env python3

import sys, gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GdkPixbuf

SQUARE = [
'16 16 2 1',
' 	c #000000',
'.	c #FFFFFF',
'                ',
' .............. ',
' .............. ',
' .............. ',
' .............. ',
' .............. ',
' .............. ',
' .............. ',
' .............. ',
' .............. ',
' .............. ',
' .............. ',
' .............. ',
' .............. ',
' .............. ',
'                ']

TRIANGLE = [
'16 16 2 1',
' 	c #000000',
'.	c #FFFFFF',
' ...............',
'  ..............',
' . .............',
' .. ............',
' ... ...........',
' .... ..........',
' ..... .........',
' ...... ........',
' ....... .......',
' ........ ......',
' ......... .....',
' .......... ....',
' ........... ...',
' ............ ..',
' ............. .',
'                ']

class Win(Gtk.ApplicationWindow):
    '''
        レンダラ
    '''
    def __init__(self, app):
        Gtk.ApplicationWindow.__init__(self, application=app, title='Py')
        # Renderer
        cell_1 = Gtk.CellRendererPixbuf()
        cell_2 = Gtk.CellRendererPixbuf()
        # Column
        column_1 = Gtk.TreeViewColumn(title='icon')
        column_1.pack_start(cell_1, True)
        column_1.add_attribute(cell_1, 'icon-name', 0)
        column_2 = Gtk.TreeViewColumn(title='pixbuf')
        column_2.pack_start(cell_2, True)
        column_2.add_attribute(cell_2, 'pixbuf', 1)
        # ListStore
        self.store = Gtk.ListStore.new([str, GdkPixbuf.Pixbuf.__gtype__])
        tree = Gtk.TreeView(model=self.store)
        tree.append_column(column_1)
        tree.append_column(column_2)
        # Create Pixbuf
        pixbuf1 = GdkPixbuf.Pixbuf.new_from_xpm_data(SQUARE)
        pixbuf2 = GdkPixbuf.Pixbuf.new_from_xpm_data(TRIANGLE)
        # Data
        self.store.append(['system-search-symbolic', pixbuf1])
        self.store.append(['help-about-symbolic', pixbuf2])
        #
        self.add(tree)
        self.show_all()

class App(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(self)

    def do_startup(self):
        Gtk.Application.do_startup(self)
        Win(self)

    def do_activate(self):
        self.props.active_window.present()

app = App()
app.run(sys.argv)
etc/r05.png
GtkCellRendererProgress
GtkCellRendererProgress はその名のとおりプログレスバーです。
Value プロパティに 0...100 の値で何パーセント進んだかを入れて利用します。
#!/usr/bin/env python3

import sys, gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GLib

class Win(Gtk.ApplicationWindow):
    '''
        レンダラ
    '''
    def __init__(self, app):
        Gtk.ApplicationWindow.__init__(self, application=app, title='Py')
        # Renderer
        cell = Gtk.CellRendererProgress()
        # Column
        column = Gtk.TreeViewColumn(title='progress')
        column.pack_start(cell, True)
        column.add_attribute(cell, 'value', 0)
        # ListStore
        self.store = Gtk.ListStore.new([int])
        tree = Gtk.TreeView(model=self.store)
        tree.append_column(column)
        #
        self.add(tree)
        self.show_all()
        # Timer
        it = self.store.append([0])
        self.timeout_id = GLib.timeout_add(100, self.on_timeout, it)

    def on_timeout(self, it):
        new_value = self.store[it][0] + 1
        if new_value > 100:
            return False
        self.store[it][0] = new_value
        return True

class App(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(self)

    def do_startup(self):
        Gtk.Application.do_startup(self)
        Win(self)

    def do_activate(self):
        self.props.active_window.present()

app = App()
app.run(sys.argv)
etc/r06.png
GtkCellRendererSpinner
GtkCellRendererSpinner はいわゆるウエイト表示。
昔の Windows では砂時計、今はクルクル回るアレです。

active プロパティで表示し pulse プロパティで回り具合を調節します。
#!/usr/bin/env python3

import sys, gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GLib

class Win(Gtk.ApplicationWindow):
    '''
        レンダラ
    '''
    def __init__(self, app):
        Gtk.ApplicationWindow.__init__(self, application=app, title='Py')
        # Renderer
        cell = Gtk.CellRendererSpinner(active=True, size=Gtk.IconSize.DIALOG)
        # Column
        column = Gtk.TreeViewColumn(title='spinner')
        column.pack_start(cell, True)
        column.add_attribute(cell, 'pulse', 0)
        # ListStore
        self.store = Gtk.ListStore.new([int])
        tree = Gtk.TreeView(model=self.store)
        tree.append_column(column)
        #
        self.add(tree)
        self.show_all()
        # Timer
        it = self.store.append([0])
        self.timeout_id = GLib.timeout_add(20, self.on_timeout, it)

    def on_timeout(self, it):
        new_value = self.store[it][0] + 1
        if new_value > 100:
            return False
        self.store[it][0] = new_value
        return True

class App(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(self)

    def do_startup(self):
        Gtk.Application.do_startup(self)
        Win(self)

    def do_activate(self):
        self.props.active_window.present()

app = App()
app.run(sys.argv)
etc/r07.png
GtkCellRendererToggle
GtkCellRendererToggle はその名のとおりチェックボックスです。

チェックボックスをクリックすると toggled シグナルが発生しますが表示は変わりません。
チェック状態を変更したい場合は以下のように自力で行う必要があります。
#!/usr/bin/env python3

import sys, gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GLib

class Win(Gtk.ApplicationWindow):
    '''
        レンダラ
    '''
    def __init__(self, app):
        Gtk.ApplicationWindow.__init__(self, application=app, title='Py')
        # Renderer
        cell_toggle = Gtk.CellRendererToggle()
        cell_text = Gtk.CellRendererText()
        # Signal
        cell_toggle.connect('toggled', self.on_toggled)
        # Column
        column_toggle = Gtk.TreeViewColumn(title='!')
        column_toggle.pack_start(cell_toggle, True)
        column_toggle.add_attribute(cell_toggle, 'active', 0)
        column_text = Gtk.TreeViewColumn(title='欲しいバイクを選んでね')
        column_text.pack_start(cell_text, True)
        column_text.add_attribute(cell_text, 'text', 1)
        # ListStore
        self.store = Gtk.ListStore.new([bool, str])
        for s in ['刀', 'Vストローム', '隼', 'GSX-R1000R']:
            self.store.append([False, s])
        tree = Gtk.TreeView(model=self.store)
        tree.append_column(column_toggle)
        tree.append_column(column_text)
        #
        self.add(tree)
        self.show_all()

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

class App(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(self)

    def do_startup(self):
        Gtk.Application.do_startup(self)
        Win(self)

    def do_activate(self):
        self.props.active_window.present()

app = App()
app.run(sys.argv)
etc/r08.png
Copyright(C) sasakima-nao All rights reserved 2002 --- 2020.