Paepoi

Paepoi » PyGObject Tips » Gtk(PyGObject) Tips | CSS

Gtk(PyGObject) Tips | CSS

# 最終更新日 2019.08.25

2019 年現在の仕様に追記と書き換え。

GtkCssProvider
GTK+ では Widget で利用するフォントや色等は CSS を使って指定します。
GtkCssProvider というものを使います。
基本的には Widget の name プロパティを設定、CSS に # の接頭子で指定できます。
複数の Widget に同じ name プロパティを指定しても問題ありません。
#!/usr/bin/env python3

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

BUTTON_CSS = '''
/* name property で指定 */
#MyButton {
    color: red;
}
#MyButton:active {
    color: blue;
}'''
ENTRY_CSS = '''
#MyEntry {
    font-style: italic;
}'''

class Win(Gtk.ApplicationWindow):
    '''
        name は重複してもいい
    '''
    def __init__(self, app):
        Gtk.ApplicationWindow.__init__(self, application=app, title='Py', resizable=False)
        #
        button = Gtk.Button(label='ボタンを押すと青色になる', name='MyButton')
        # GtkCssProvider
        provider = Gtk.CssProvider()
        # 文字列から CSS を読み込む、Python3 は UCS-4 なので要変換
        provider.load_from_data(BUTTON_CSS.encode('utf-8'))
        #provider.load_from_path('style.css') # ファイルから読み込む場合
        # GtkStyleContext
        context = button.get_style_context()
        context.add_provider(provider, Gtk.STYLE_PROVIDER_PRIORITY_USER)
        #
        entry = Gtk.Entry(name='MyEntry')
        entry.props.buffer.props.text = 'SUZUKI'
        # 同様に
        provider2 = Gtk.CssProvider()
        provider2.load_from_data(ENTRY_CSS.encode('utf-8'))
        context = entry.get_style_context()
        context.add_provider(provider2, Gtk.STYLE_PROVIDER_PRIORITY_USER)
        # pack
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        vbox.pack_start(button, False, False, 0)
        vbox.pack_start(entry, False, False, 0)
        #
        self.add(vbox)
        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/css1.png
CSS の一括指定
上記の手段だと Widget 毎に登録する必要があり複数指定したい場合は面倒です。
アプリ全体(GdkScreen 全体)に一括登録するには以下のようにします。
動かすと上記と同様に表示されることが確認できます。
#!/usr/bin/env python3

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

APP_CSS = '''
/* 一つの CSS で指定 */
#MyButton {
    color: red;
}
#MyButton:active {
    color: blue;
}
#MyEntry {
    font-style: italic;
}'''

class Win(Gtk.ApplicationWindow):
    '''
        name は重複してもいい
    '''
    def __init__(self, app):
        Gtk.ApplicationWindow.__init__(self, application=app, title='Py', resizable=False)
        #
        button = Gtk.Button(label='ボタンを押すと青色になる', name='MyButton')
        # GtkCssProvider
        provider = Gtk.CssProvider()
        provider.load_from_data(APP_CSS.encode('utf-8'))
        # GtkStyleContext
        context = self.get_style_context()
        context.add_provider_for_screen(
            self.get_screen(),
            provider,
            Gtk.STYLE_PROVIDER_PRIORITY_USER)
        #
        entry = Gtk.Entry(name='MyEntry')
        entry.props.buffer.props.text = 'SUZUKI'
        # pack
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        vbox.pack_start(button, False, False, 0)
        vbox.pack_start(entry, False, False, 0)
        #
        self.add(vbox)
        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)

Widget のデフォルトを変更
CSS 定義に Widget 名から 'Gtk' を外し全部小文字にすれば名前を付ける必要もありません。
ただし GTK+ ではタイトルバーを含め文字列表示は全部 GtkLabel みたいな感じです。
ボタンも全部 GtkButton 派生です、これらに影響するのでこの方法を使う場合は慎重に。
下記でも動かすと [閉じる] ボタンが赤くなってしまうのが確認できます。
#!/usr/bin/env python3

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

APP_CSS = '''
/* Widget すべてで指定 */
button {
    color: red;
}
button:active {
    color: blue;
}
entry {
    font-style: italic;
}'''

class Win(Gtk.ApplicationWindow):
    '''
        閉じるボタンまで赤くなる...
    '''
    def __init__(self, app):
        Gtk.ApplicationWindow.__init__(self, application=app, title='Py', resizable=False)
        #
        button = Gtk.Button(label='ボタンを押すと青色になる')
        # GtkCssProvider
        provider = Gtk.CssProvider()
        provider.load_from_data(APP_CSS.encode('utf-8'))
        # GtkStyleContext
        context = self.get_style_context()
        context.add_provider_for_screen(
            self.get_screen(),
            provider,
            Gtk.STYLE_PROVIDER_PRIORITY_USER)
        #
        entry = Gtk.Entry()
        entry.props.buffer.props.text = 'SUZUKI'
        # pack
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        vbox.pack_start(button, False, False, 0)
        vbox.pack_start(entry, False, False, 0)
        #
        self.add(vbox)
        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/css2.png
利用できるスタイル
GTK+ CSS Overview: GTK+ 3 Reference Manual
GTK+ CSS Properties: GTK+ 3 Reference Manual

上記公式にサンプルがいくつか載っています、少し解り辛いですが。
スタイルの他にアニメーションやキーバインドの設定までできるようです。
色々試して自力で発掘しましょう、Linux はそんなもんです。
#!/usr/bin/env python3

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

APP_CSS = '''
/**
 * グラデーションサンプルのコピペ
 */
#MyButton {
    background-image: radial-gradient(ellipse at center, yellow 0%, green 100%);
}
#MyButton:active {
    color: blue;
}
/**
 * キーバインドサンプルのコピペ
 * 公式サンプルにはセミコロンが付いているけど非標準エラーになる
 */
@binding-set binding-set1 {
    bind "<alt>Left" { "move-cursor" (visual-positions, -3, 0) };
}
@binding-set binding-set2 {
    bind "<alt>Right" { "move-cursor" (visual-positions, 3, 0) };
}
#MyEntry {
    font: 10pt Sans;
    -gtk-key-bindings: binding-set1, binding-set2;
}'''

class Win(Gtk.ApplicationWindow):
    '''
        Entry で Alt+矢印キーで 3 つづつカーソルが移動する
    '''
    def __init__(self, app):
        Gtk.ApplicationWindow.__init__(self, application=app, title='Py', resizable=False)
        #
        button = Gtk.Button(label='ボタンを押すと青色になる', name='MyButton')
        # GtkCssProvider
        provider = Gtk.CssProvider()
        provider.load_from_data(APP_CSS.encode('utf-8'))
        # GtkStyleContext
        context = self.get_style_context()
        context.add_provider_for_screen(
            self.get_screen(),
            provider,
            Gtk.STYLE_PROVIDER_PRIORITY_USER)
        #
        entry = Gtk.Entry(name='MyEntry')
        entry.props.buffer.props.text = 'SUZUKI'
        # pack
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        vbox.pack_start(button, False, False, 0)
        vbox.pack_start(entry, False, False, 0)
        #
        self.add(vbox)
        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/css3.png
Copyright(C) sasakima-nao All rights reserved 2002 --- 2020.