GTK+」タグアーカイブ

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 ではモーダルにできないのでやはり必要ですね。
今後メッセージはこれでいこう。

GTK4: GridView

GTK4 は TreeView の他に IconView も廃止なんだね。
GlidView が後継のようで、Nautilus 43 のもコレっぽい。

Gtk.GridView

GtkDemo にサンプルが無い、自力で試してみよう。

#!/usr/bin/env python3

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

class Items(GObject.Object):
    def __init__(self, items):
        self.items = items
        super(Items, self).__init__()

class TestWindow(Gtk.ApplicationWindow):
    def __init__(self, app):
        # Set Adwaita Style
        manager = Adw.StyleManager.get_default()
        manager.set_color_scheme(Adw.ColorScheme.DEFAULT)
        # init
        Gtk.ApplicationWindow.__init__(self, application=app, title='GridView')
        #
        model = Gio.ListStore()
        model.append(Items(['OM SYSTEM', 'pen']))
        model.append(Items(['LUMIX', 'GH6']))
        #
        #sel_model = Gtk.SingleSelection(model=model)
        sel_model = Gtk.MultiSelection(model=model)
        #
        factory1 = Gtk.SignalListItemFactory()
        factory1.connect('setup', self.on_listitem_setup)
        factory1.connect('bind', self.on_listitem_bind1)
        #
        listnview = Gtk.GridView(model=sel_model, factory=factory1)
        listnview.connect('activate', self.on_columnview_activate)
        #
        self.set_child(listnview)
        self.set_default_size(200, 200)

    def on_listitem_setup(self, factory, item):
        label = Gtk.Label(label='oman')
        item.set_child(label)

    def on_listitem_bind1(self, factory, item):
        l = item.get_item()
        label = item.get_child()
        label.props.label = l.items[0]

    def on_columnview_activate(self, list, pos):
        print(1)

class TestApplication(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(self, application_id='org.omsystem.om1')

    def do_activate(self):
        w = TestWindow(self)
        w.present()

app = TestApplication()
app.run(sys.argv)

grid

ちょっと驚いた、という不正確な日本語での表現にしておくけどナルホドなぁ。
完全に GTK4 の ListView と同様の手順、いや当然狙ってそうしたのだろう。
Box を使ってレイアウトすれば Nautilus 43 の見た目にはできるね。

ついでに。
init 直後に書いた Adw 用途の命令って GTK4 全部に影響するのね。
上記のまま全体の DarkMode にしてみたら。

grid

あぁ macOS や iOS の DarkMode は開発者に丸投げだったもんな。
GNOME はこうしたんですね、いや勘違いかもしれないけれど。

GTK4: ListView and ColumnView

前回は少し勘違いをしてました。
GtkListView を GtkListBox だと思い込んでいた、反省。

ListView は GTK4 で追加された新規のリストビューでした。
ただし 1 カラム限定、それいったい何に使うんだろう?は置いておいて。
複数カラムにするには ColumnView を使う、ということらしい。

Gtk.ListView

Gtk.ColumnView

ListView のほうにはサンプルコードがある。
でも create_application_list って関数何よ、これじゃよくワカンネェよ。
model は GListModel 派生なので Gio.ListStore にすればいいようです。

ただ ListStore には GObject.Object 派生しか append できないみたいなんですが。
GObject.TYPE_STRING 等を指定しても例外、丁度いい class も見つからない。
PyGObject の制限なのかどうかは知らないけど文字列のリストを入れたいんですけど。
ということでちょっぴり手抜きな Item class を作ってサンプルコードを書いてみた。

#!/usr/bin/env python3

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

class Items(GObject.Object):
    def __init__(self, items):
        self.items = items
        super(Items, self).__init__()

class TestWindow(Gtk.ApplicationWindow):
    def __init__(self, app):
        Gtk.ApplicationWindow.__init__(self, application=app, title='ListView')
        #
        model = Gio.ListStore()
        model.append(Items(['OM SYSTEM', 'pen']))
        model.append(Items(['LUMIX', 'GH6']))
        #
        sel_model = Gtk.SingleSelection(model=model)
        #
        factory1 = Gtk.SignalListItemFactory()
        factory1.connect('setup', self.on_listitem_setup)
        factory1.connect('bind', self.on_listitem_bind1)
        #
        listnview = Gtk.ListView(model=sel_model, factory=factory1)
        listnview.connect('activate', self.on_columnview_activate)
        #
        self.set_child(listnview)

    def on_listitem_setup(self, factory, item):
        label = Gtk.Label(label='sexxx')
        item.set_child(label)

    def on_listitem_bind1(self, factory, item):
        l = item.get_item()
        label = item.get_child()
        label.props.label = l.items[0]

    def on_columnview_activate(self, list, pos):
        print(1)

class TestApplication(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(self, application_id='org.omsystem.pen')

    def do_activate(self):
        w = TestWindow(self)
        w.present()

app = TestApplication()
app.run(sys.argv)

表示はレンダラではなく Widget を使うようになりました。

リストビューらしい複数カラム表示にするなら ColumnView を使う。
ほとんど同じ手段です、ListView は単なるベースクラスなのかな。
カラムの数だけ ColumnViewColumn を追加して使います。

#!/usr/bin/env python3

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

class Items(GObject.Object):
    def __init__(self, items):
        self.items = items
        super(Items, self).__init__()

class TestWindow(Gtk.ApplicationWindow):
    def __init__(self, app):
        Gtk.ApplicationWindow.__init__(self, application=app, title='ColumnView')
        #
        model = Gio.ListStore()
        model.append(Items(['OM SYSTEM', 'pen']))
        model.append(Items(['LUMIX', 'GH6']))
        #
        sel_model = Gtk.SingleSelection(model=model)
        #
        factory1 = Gtk.SignalListItemFactory()
        factory1.connect('setup', self.on_listitem_setup)
        factory1.connect('bind', self.on_listitem_bind1)
        #
        factory2 = Gtk.SignalListItemFactory()
        factory2.connect('setup', self.on_listitem_setup)
        factory2.connect('bind', self.on_listitem_bind2)
        #
        columnview = Gtk.ColumnView(model=sel_model)
        columnview.connect('activate', self.on_columnview_activate)
        #
        column1 = Gtk.ColumnViewColumn(title='Camera', factory=factory1)
        columnview.append_column(column1)
        column2 = Gtk.ColumnViewColumn(title='Model', factory=factory2)
        columnview.append_column(column2)
        #
        self.set_child(columnview)

    def on_listitem_setup(self, factory, item):
        label = Gtk.Label(label='sexxx')
        item.set_child(label)

    def on_listitem_bind1(self, factory, item):
        l = item.get_item()
        label = item.get_child()
        label.props.label = l.items[0]

    def on_listitem_bind2(self, factory, item):
        l = item.get_item()
        label = item.get_child()
        label.props.label = l.items[1]

    def on_columnview_activate(self, list, pos):
        print(1)

class TestApplication(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(self, application_id='org.omsystem.om1')

    def do_activate(self):
        w = TestWindow(self)
        w.present()

app = TestApplication()
app.run(sys.argv)

で。

column

解り辛いですよね、でも表示が Widget なので GTK3 以前よりはマシ。
チェックボックスや画像を入れたくなっても Widget の知識でやれますんで。
以前はレンダラという専用のものを使う必要がありました。

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

しかし Nautilus 43 みたくモダンな選択表示にするにはどうすればいいんだろう?
メッセージが Adw.ToastOverlay になったので Adw だと思ったけど無いんだよな。
それは今後の課題ということで、最後に。

jyoubitaki

本日はジョウビタキが綺麗に撮れました。

GTK4: GtkInscription

そういえば GTK4 に GtkInscription が追加されるんだっけ。
Gtk.Inscription

特殊用途に使う Widget なので通常は GtkLabel を使ってくれ。
ということらしいけど、つまり文字列がはみ出すラベルってことね。

#!/usr/bin/env python3

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

class TestWindow(Gtk.ApplicationWindow):
    def __init__(self, app):
        try:
            Gtk.ApplicationWindow.__init__(self, application=app)
            # Label and Inscription
            label = Gtk.Label(label='文字列に合わせて拡大されます')
            inscription = Gtk.Inscription(text='文字列に合わせて拡大されません、はみ出します')
            # pack
            vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
            vbox.append(label)
            vbox.append(inscription)
            self.set_child(vbox)
        except Exception as e:
            print(e, file=sys.stderr)
            app.quit()

class TestApplication(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(self, application_id='org.omsystem.pen')

    def do_activate(self):
        w = TestWindow(self)
        w.present()

app = TestApplication()
app.run(sys.argv)

inscription

なんだそれだけだったのか。
カラム部分とかコンテナが拡大されたら困る場所に使うってことですね。

ところで、GTK4 のドキュメントが親切になっている。
deprecated つまり廃止予定を明確にしてくれています。

Gtk ? 4.0

TreeView がやっと廃止、マウス必須な時代遅れ UI ですからね。
TreeStore だけでなく ListStore も廃止、リストビュー表示もできなくなる。
一応、GtkListView は昔ながらのリストビューではなくスマホの設定画面みたいな UI です。
あれ、Nautilus 43 のリストビュー表示は?
と思うかもだけどアレは ColumnView なんですね、いや今知ったんだけど。

伴ってあの解り辛いレンダラ関連が全面廃止。
あぁ、それで Inscription という Widget が必要になったということか。

ダイアログ関連を全面見直し。
いやそれ何故 GTK4 の最初でやらなかったのか。

ComboBox も廃止。いや困るだろと思ったけど Popover があったわ。
Nautilus 43 の設定もそうなっていた、はよ移行しろってことですね。

StyleContext も廃止。
CSS の適用は今後どうするんだろう?調べなきゃ。

GTK4 の方向性が見えてきましたね。
次は GtkColumnView あたりを調べてみるかな。

GTK4: TextView

Fedora 36 には adwaita-1-demo が最初から入っていると以前書いた。
GTK Demo(gtk4-demo) は gnome-softwere から入手できるので入れてみる。

demo

なんか勝手に 4 ヶも入ってしまったけどまあいい。
こっちはソースが直接見られるので解りやすいですね。

demo

TextView は文字列中に Widget を入れることができるのか。
よし PyGObject でやってみよう。

#!/usr/bin/env python3

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

class Win(Gtk.ApplicationWindow):
    '''
        GTK4: TextView in Button
    '''
    def __init__(self, a):
        Gtk.ApplicationWindow.__init__(self, application=a)
        # Button
        button = Gtk.Button(label='Button')
        button.connect('clicked', self.on_button_clicked)
        # TextView
        self.view = Gtk.TextView()
        buf = self.view.get_buffer()
        it = buf.get_start_iter()
        buf.insert(it, 'This')
        anchor = buf.create_child_anchor(it)
        self.view.add_child_at_anchor(button, anchor)
        buf.insert(it, 'click!')
        # pack
        sw = Gtk.ScrolledWindow()
        sw.set_child(self.view)
        self.set_child(sw)
        self.set_default_size(400, 300)

    def on_button_clicked(self, button):
        with open(__file__) as f:
            s = f.read()
            self.view.props.buffer.set_text(s)

def app_activate(a):
    w = Win(a)
    w.present()

app = Gtk.Application()
app.connect('activate', app_activate)
app.run()

demo

マジでできた、必要性は疑問だけど。
他 tag の利用方法なんかもコレで解るね。
GTK4 はこんな感じで勉強すればいいと思います。

ところで SVG エディタなんだけど。
Vectornator という Mac のアプリを使うことにしました。
M1 Mac なのに Raw 現像専用じゃもったいない。