Programming」カテゴリーアーカイブ

GTK4: Adw.ButtonContent

Adw.ButtonContent という Widget がある。
アイコンと文字列を両方ボタンに表示させるものらしい。
何故こんなものがあるんだ?
Gtk.Button には icon-name プロパティがあるんですけど。

#!/usr/bin/env python3

import gi
gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
from gi.repository import Gtk, Adw

class Win(Gtk.ApplicationWindow):
    '''
        Adw: Sample Code
    '''
    def __init__(self, a):
        # Set Adwaita Style
        manager = Adw.StyleManager.get_default()
        manager.set_color_scheme(Adw.ColorScheme.DEFAULT)
        # init
        Gtk.ApplicationWindow.__init__(self, application=a)
        # Button
        b1 = Gtk.Button(label='普通のボタン(_A)', use_underline=True)
        #
        b2 = Gtk.Button(icon_name='edit-undo-symbolic',)
        #
        b3 = Gtk.Button(icon_name='edit-redo-symbolic', label='Label 指定すると画像が表示されない')
        #
        bc = Adw.ButtonContent(icon_name='starred-symbolic', label='_SVG 画像付きボタン', use_underline=True)
        b4 = Gtk.Button(child=bc)
        # pack
        box = Gtk.ListBox() #show_separators=True)
        box.append(b1)
        box.append(b2)
        box.append(b3)
        box.append(b4)
        # Clamp
        clamp = Adw.Clamp(maximum_size=300, tightening_threshold=200)
        clamp.set_child(box)
        self.set_child(clamp)
        self.set_default_size(400, 200)

app = Gtk.Application()
app.connect('activate', lambda a: Win(a).present())
app.run()

gtk4button

試してみたらすぐ解った。
label を指定した場合に image を表示させる手段が無いんだね。
GTK3 の時は普通にできていたような、ちと試してみよう。

#!/usr/bin/env python3

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

class Win(Gtk.ApplicationWindow):
    '''
        GTK3
    '''
    def __init__(self, a):
        # Set Adwaita Style
        # init
        Gtk.ApplicationWindow.__init__(self, application=a)
        # Button
        b1 = Gtk.Button(label='普通のボタン(_A)', use_underline=True)
        #
        im = Gtk.Image(icon_name='edit-undo-symbolic')
        b2 = Gtk.Button(label='アンドゥ', image=im, always_show_image=True)
        # pack
        box = Gtk.ListBox()
        box.insert(b1, 0)
        box.insert(b2, 1)
        # Add
        self.add(box)
        self.resize(400, 200)
        self.show_all()

app = Gtk.Application()
app.connect('activate', lambda a: Win(a))
app.run()

gtk3button

思い込みだった、普通にはできない。
always-show-image プロパティを TRUE にするという手順が必要。
いつぞやの更新でストックが廃止になったので間に合わせで機能追加って感じかな。

ちなみに GTK2 では文字列も画像なストック画像から選べだった。
OK や Open 等の限られたものしか選べなかったけど当時はそれが普通。
PyGtk は Python2 なのでサンプルコードは書かないけど。

GTK4 は極力シンプル化、表示は全部 Widget でまかなうように。
結果両方を表示するには Box に両方をセットして上に載せる手順が必要に。
それを単純化したのが ButtonContent ということみたいです。

よく見るとアイコンの位置が端になる演出もあるんだなって。
関係ないけど ListBox への Widget 挿入関数に append が追加されていた。
ListBox って本当は用途が違うけど並べたときに隙間ができるので今回使用。

ちなみに使えるアイコンは gtk4-icon-browser から。

browser

Symbolic のほうにあるアイコンはどれでも利用できます。
ということで、最後に。

jyoubitaki

今日もジョウビタキ、以降ドン曇りでロクなの撮れず。
しかしオスばかり見つかるんですけど、悪いが筆者はゲイじゃない。

GTK4: Adw.Clamp

前回まで Widget 配置を中心にするのに margin_*** を使っていた。
Nautilus の設定ウインドウを参考にしたのですけど、何か違うなって。

リサイズすると普通に伸び縮み、ある一定のサイズ以上になると固定される。
どうやら Adw.Clamp でこのような動作にできるようだ。

#!/usr/bin/env python3

import gi
gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
from gi.repository import Gtk, Adw

class Win(Gtk.ApplicationWindow):
    '''
        Adw: Sample Code
    '''
    def __init__(self, a):
        # Set Adwaita Style
        manager = Adw.StyleManager.get_default()
        manager.set_color_scheme(Adw.ColorScheme.DEFAULT)
        # init
        Gtk.ApplicationWindow.__init__(self, application=a)
        # Round Button
        pills = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        pills.append(Gtk.Button(label='pill', css_classes=['pill']))
        pills.append(Gtk.Button(label='circular', css_classes=['circular']))
        # Button and Entry
        buttons = [
            Gtk.Button(label='nomal'),
            Gtk.Button(label='flat', css_classes=['flat']),
            Gtk.Button(label='suggested', css_classes=['suggested-action']),
            Gtk.Button(label='destructive', css_classes=['destructive-action']),
            pills,
            Gtk.Entry(placeholder_text='success', css_classes=['success']),
            Gtk.Entry(placeholder_text='warning', css_classes=['warning']),
            Gtk.Entry(placeholder_text='error', css_classes=['error'])
        ]
        # pack
        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) #, margin_start=48, margin_end=48)
        for b in buttons:
            box.append(b)
        # Clamp
        clamp = Adw.Clamp(maximum_size=300, tightening_threshold=200)
        clamp.set_child(box)
        self.set_child(clamp)
        self.set_default_size(400, 400)

app = Gtk.Application()
app.connect('activate', lambda a: Win(a).present())
app.run()

前回のを書き換え。
margin 指定をヤメて Adw.Clamp を利用にしてみたのでリサイズしてみよう。
なるほど、中心配置はコレを使ったほうがいいな。

tightening-threshold はしきい値ということだけど。
指定値以下にはならないと思って縮めてみたら違った。

clamp

正直指定しても意味ない、コンテンツ最小サイズまで縮められる。
多分内部的な値なのだろう、てか最小サイズプロパティが欲しいかも。

GTK4: css-classes Property

GTK4 の Widget には css-classes というプロパティがある。
定義された文字列を入れれば css が適用されるみたいなんだが。

Adw ? 1: Style Classes

なんで Adw のほうに書いてあるんだよ。
とにかくボタンとエントリーを着色してみる。

#!/usr/bin/env python3

import gi
gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
from gi.repository import Gtk, Adw

class Win(Gtk.ApplicationWindow):
    '''
        Adw: Sample Code
    '''
    def __init__(self, a):
        # Set Adwaita Style
        manager = Adw.StyleManager.get_default()
        manager.set_color_scheme(Adw.ColorScheme.DEFAULT)
        # init
        Gtk.ApplicationWindow.__init__(self, application=a)
        # Round Button
        pills = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
        pills.append(Gtk.Button(label='pill', css_classes=['pill']))
        pills.append(Gtk.Button(label='circular', css_classes=['circular']))
        # Button and Entry
        buttons = [
            Gtk.Button(label='nomal'),
            Gtk.Button(label='flat', css_classes=['flat']),
            Gtk.Button(label='suggested', css_classes=['suggested-action']),
            Gtk.Button(label='destructive', css_classes=['destructive-action']),
            pills,
            Gtk.Entry(placeholder_text='success', css_classes=['success']),
            Gtk.Entry(placeholder_text='warning', css_classes=['warning']),
            Gtk.Entry(placeholder_text='error', css_classes=['error'])
        ]
        # pack
        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, margin_start=48, margin_end=48)
        for b in buttons:
            box.append(b)
        self.set_child(box)
        self.set_default_size(400, 400)

app = Gtk.Application()
app.connect('activate', lambda a: Win(a).present())
app.run()

css

css は自分で定義もできますけどまだ調べていない。
てかこれだけあれば十分だと思うけど。

GTK4: Adw.ComboRow

GTK+ 4.10 にて Gtk.ComboBox は廃止になる。
Adw に ComboRow というものがあるね。

Adw.ComboRow

ちょっとまて、コレ Nautilus 43 の設定にあるのそのまんまじゃん。
しかも Adw.PreferencesGroup というソレっぽいものもあるという。

それよりも、GtkStringList という GListStore 派生 class があったのかい!
文字列のみで使うなら当然 ListView や GridView にも使えます。
そんなこんなで。

#!/usr/bin/env python3

import gi
gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
from gi.repository import Gtk, Adw

class Win(Gtk.ApplicationWindow):
    '''
        Adw: Sample Code
    '''
    def __init__(self, a):
        # Set Adwaita Style
        manager = Adw.StyleManager.get_default()
        manager.set_color_scheme(Adw.ColorScheme.DEFAULT)
        # init
        Gtk.ApplicationWindow.__init__(self, application=a)
        # GListStore
        slist = Gtk.StringList()
        slist.append('OM-1')
        slist.append('GH6')
        rlist = Gtk.StringList()
        rlist.append('PRO レンズ')
        rlist.append('パナライカ')
        # combo
        self.r1 = Adw.ComboRow(model=slist, title='カメラ')
        self.r2 = Adw.ComboRow(model=rlist, title='レンズ')
        # Group
        group = Adw.PreferencesGroup()
        group.add(self.r1)
        group.add(self.r2)
        # Button
        button = Gtk.Button(label='確認', margin_top=12, margin_bottom=12)
        button.connect('clicked', self.on_button_clicked)
        # pack
        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, margin_start=48, margin_end=48)
        box.append(Gtk.Label(label='欲しい', halign=Gtk.Align.START, margin_top=12, margin_bottom=12))
        box.append(group)
        box.append(button)
        self.set_child(box)
        self.set_default_size(400, 300)

    def on_button_clicked(self, button):
        '''
            値の取り出し
        '''
        s1 = self.r1.get_selected_item().get_string()
        s2 = self.r2.get_selected_item().get_string()
        self.set_title(f'{s1}, {s2}')

app = Gtk.Application()
app.connect('activate', lambda a: Win(a).present())
app.run()

combo

予想以上に簡単だった、margin は勘で割り当てした適当な値です。
変更を即座に適用させたい場合は activated シグナルを処理。
いや GTK+ アプリの設定は基本「適用」ボタンなんて無い文化ですし。

値の取り出しはもちろん selected プロパティの UINT 値を利用する手段もある。
そのほうが設定保存等で都合がいいけど GSettings 的には文字列のほうが良さげ。

しかし GTK+ も Adw のおかげで型がいっぱい増えてなんというか。
UI の統一を狙うなら 4.10 の新規 Widget は Adw にまかせたほうがとか。

GTK4: Adw.MessageDialog

GTK+ 4.10 にて Gtk.MessageDialog は廃止になる。
変わりに Gtk.AlertDialog を、とドキュメントには書いている。
しかし、Adw 1.2 には Adw.MessageDialog が追加されている。

Adw.MessageDialog

どちらを使えと?
まあ現状 AlertDialog は使えないしコッチを試してみよう。

Gtk のは GtkDialog ベースだったが Adw のは GtkWindow ベース。
DialogFlags や ButtonsType のような単純明快な定数も用意されていない。
heading にメッセージを body に詳細を、ということみたい。
GtkWindow ベースなので parent を NULL にすれば単体でも使える。

#!/usr/bin/env python3

import gi
gi.require_version('Adw', '1')
from gi.repository import Adw

class Win(Adw.MessageDialog):
    def __init__(self, a):
        Adw.MessageDialog.__init__(self, application=a)
        # Create
        self.set_heading('Message')
        self.set_body('body')
        self.add_response('ok', 'OK')

app = Adw.Application()
app.connect('activate', lambda a: Win(a).present())
app.run()

adw_message

親ウインドウが無いというメッセージは一応出るようです。
ついでに app の最短表記を思いついたので今後はコレで。
これだけじゃ使い道が解らないので普通なサンプルコード。

#!/usr/bin/env python3

import gi
gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
from gi.repository import Gtk, Adw

class Win(Gtk.ApplicationWindow):
    '''
        Adw: Sample Code
    '''
    def __init__(self, a):
        # Set Adwaita Style
        manager = Adw.StyleManager.get_default()
        manager.set_color_scheme(Adw.ColorScheme.DEFAULT)
        # init
        Gtk.ApplicationWindow.__init__(self, application=a)
        # Button
        button = Gtk.Button(label='This is Button\nShow Message Dialog')
        button.connect('clicked', self.on_button_clicked)
        # Toast
        self.toast = Adw.ToastOverlay(child=button)
        # pack
        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        box.append(self.toast)
        self.set_child(box)
        self.set_default_size(600, 300)

    def on_button_clicked(self, button):
        dlg = Adw.MessageDialog.new(self, '何が撮りたい?', '選んでください')
        dlg.add_response('bird', '野鳥(_b)')
        dlg.add_response('portrait', 'ポートレート(_p)')
        dlg.add_response('train', '電車(_d)')
        dlg.connect('response', self.on_message_response)
        dlg.present()

    def on_message_response(self, dlg, response):
        match response:
            case 'bird':
                toast = Adw.Toast(title='貴方は変態です')
            case 'portrait':
                toast = Adw.Toast(title='貴方はスケベです')
            case 'train':
                toast = Adw.Toast(title='貴方は頭がおかしいです')
            case 'close':
                toast = Adw.Toast(title='Esc 押さないで')
            case _:
                toast = Adw.Toast(title='不明なレスポンス')
        self.toast.add_toast(toast)

app = Gtk.Application()
app.connect('activate', lambda a: Win(a).present())
app.run()

adw_message

定数が無いおかげでむしろ簡単になったって感じですね。
Alt キーのニーモニックも普通に使えるようです。
Python の match 文と相性がいいのも嬉しいです。
ついでに、ウインドウを小さくするとボタンが縦並びになったりする。

adw_message

なんだよ凄く便利じゃないの。
Adw.Toast ではモーダルにできないのでやはり必要ですね。
今後メッセージはこれでいこう。