GTK4: FileChooserDialog

GTK4 で GtkFileChooserNative を何故か作れない。
理由は解らないけど何をやっても無反応。

しかたがないので GtkFileChooserDialog を使う。
Open ボタンを青くしたいんだがどうすればいいのかな?

class ComipoliWindow(Gtk.ApplicationWindow):
    def __init__(self, app):
        Gtk.ApplicationWindow.__init__(self, application=app)
        # etc...

    def open_dialog(self):
        ft = Gtk.FileFilter()
        ft.set_name('Comic Book Archive')
        ft.add_mime_type('application/x-cbz')
        ft.add_mime_type('application/vnd.comicbook+zip')
        if self.app.is_pdf:
            ft.add_mime_type('application/pdf')
        if self.app.is_unrar:
            ft.add_mime_type('application/x-cbr')
        if self.app.is_7za:
            ft.add_mime_type('application/x-cb7')
        #dialog = Gtk.FileChooserNative.new('Open', self, Gtk.FileChooserAction.OPEN, '_Open', '_Cancel')
        dialog = Gtk.FileChooserDialog(title='Open', action=Gtk.FileChooserAction.OPEN, modal=True)
        dialog.add_buttons('_Cancel', Gtk.ResponseType.CANCEL, '_Open', Gtk.ResponseType.ACCEPT)
        dialog.set_transient_for(self)
        dialog.add_filter(ft)
        # CSS CLASS
        open_button = dialog.get_widget_for_response(Gtk.ResponseType.ACCEPT)
        open_button.add_css_class('suggested-action')
        cancel_button = dialog.get_widget_for_response(Gtk.ResponseType.CANCEL)
        cancel_button.add_css_class('destructive-action')
        if len(self.archive) > 0:
            d = GLib.path_get_dirname(self.archive.path)
            dialog.set_current_folder(Gio.file_new_for_path(d))
        dialog.connect('response', self.on_open_dialog_response)
        dialog.show()

    def on_open_dialog_response(self, dialog, response_id):
        if response_id == Gtk.ResponseType.ACCEPT:
            f = dialog.get_file()
            self.set_uri(f.get_uri())
        dialog.destroy()

filechooser

add_css_class でイケた。
destructive-action は例です、本当は remove か destroy になる場合に指定します。

この add_css_class で指定する文字列なんですが。
GTK4 のほうで見つからないので GTK3 のヘルプを見る。

Gtk ? 3.0

Constants つまり定数の STYLE_*** の中身。
define されている文字列をそのまま書けば適用される。
いや小文字にしてアンダーバーをハイフンにすればいいんですけどね。
というかボタン用は上記2つしか無いんですけどね。

よく見ると title や subtitle のスタイルクラスもあるな。
ということは。

#!/usr/bin/env python3

from gi.repository import Gtk, Pango

class ComipoliHeaderBar(Gtk.HeaderBar):
    def __init__(self):
        Gtk.HeaderBar.__init__(self)
        # LR Button
        self.lr_button = Gtk.ToggleButton(label='L<-R', can_focus=False, focus_on_click=False)
        self.pack_start(self.lr_button)
        # Grid Button
        self.grid_button = Gtk.Button(icon_name='view-grid-symbolic', can_focus=False, focus_on_click=False)
        self.pack_start(self.grid_button)
        # Main Title
        self.title = Gtk.Label(label='', single_line_mode=True, ellipsize=Pango.EllipsizeMode.END)
        self.title.add_css_class('title')
        # Sub Title
        self.subtitle = Gtk.Label(label='--', single_line_mode=True, ellipsize=Pango.EllipsizeMode.END)
        self.subtitle.add_css_class('subtitle')
        # Title pack
        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, valign=Gtk.Align.CENTER)
        box.append(self.title)
        box.append(self.subtitle)
        self.set_title_widget(box)
        # Menu Button pack @ after Window.__init__

    def set_title(self, text):
        self.title.set_label(text)

    def set_subtitle(self, text):
        self.subtitle.set_label(text)

    def set_active(self, is_active):
        self.title.set_sensitive(is_active)

前回の HeaderBar に試してみたらイケた、メッチャシンプルになった。
こんな手段があったのか、まだまだ勉強しなきゃ駄目だな。

GtkFileFilter は GTK3 とまったく同じでした。
いやー野鳥が見つからないとガンガン進むな、まさかの三日連続とは...

GTK4: HeaderBar

GTK4 では GtkHeaderBar の set_title が無くなった。
というよりタイトル表示部が Widget に固定されてしまった。

いや set_title 関数は自分で定義すればいい。
後は Widget として GtkLabel を入れれば今までのようにはできる。

とはいえそれじゃ普通のラベルだから Bold 化とかしなきゃいけないな。
サブタイトルは薄く小さくしなきゃだし、CSS でやるのかな?

そういえばサブタイトルは gnome-text-editor が実装している。
どうやっているのかソースを覗いてみよう、GPL っていいですね。

src/editor-window.ui ? main ? GNOME / gnome-text-editor ? GitLab

コレを真似すればいいんだけど、attributes って何だ?
それを解っていないと定義も糞もないコピペアプリにしかならない。
調べると Pango 関連らしい、よしコードで同じになるように書いてみよう。

#!/usr/bin/env python3

from gi.repository import Gtk, Pango

class ComipoliHeaderBar(Gtk.HeaderBar):
    def __init__(self):
        Gtk.HeaderBar.__init__(self)
        # LR Button
        self.lr_button = Gtk.ToggleButton(label='L<-R', can_focus=False, focus_on_click=False)
        self.pack_start(self.lr_button)
        # Grid Button
        self.grid_button = Gtk.Button(icon_name='view-grid-symbolic', can_focus=False, focus_on_click=False)
        self.pack_start(self.grid_button)
        # Main Title
        bold = Pango.attr_weight_new(Pango.Weight.BOLD)
        alistm = Pango.AttrList()
        alistm.insert(bold)
        self.title = Gtk.Label(label='', single_line_mode=True, ellipsize=Pango.EllipsizeMode.END, attributes=alistm)
        # Sub Title
        subs = Pango.attr_scale_new(0.8222)
        suba = Pango.attr_foreground_alpha_new(32767)
        alists = Pango.AttrList()
        alists.insert(subs)
        alists.insert(suba)
        self.subtitle = Gtk.Label(label='--', single_line_mode=True, ellipsize=Pango.EllipsizeMode.END, attributes=alists)
        # Title pack
        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, valign=Gtk.Align.CENTER)
        box.append(self.title)
        box.append(self.subtitle)
        self.set_title_widget(box)
        # Menu Button pack @ after Window.__init__

    def set_title(self, text):
        self.title.set_label(text)

    def set_subtitle(self, text):
        self.subtitle.set_label(text)

header_bar

まったく同じになろうようにしてみた、滅茶苦茶面倒だった。
GtkBuilder はあんな単純表記を読んでコレをやっているのか、凄いんだな。
とにかく、attributes の指定は Pango の関数と値の省略表記ですね。

gnome-text-editor は GtkCenterBox を挟んでいるけどさ。
アレは別の用途なのでこういう場合はいらない。

そういえばタイトルが長い場合に収めるためウインドウが大きくされるのも防がないと。
ellipsize プロパティを定義すると自動でやってくれるみたい、なるほど。
後はウインドウが非アクティブになった時にタイトルを薄くすればオケ。

残りの GTK4 化は今までに書いたことの応用でイケそうだ。
何か見つけたら書いていきます。

Gdk.Cursor.new_from_name

ただ、矢印だけのカーソルは無くなってしまったようで。
とはいえ、そもそも作った本人はマウスでページめくりをしたことが無い。
ということで GTK4 版はマウスカーソルの変更は無しにする予定。

えっ夏鳥?昨日も今日もオケラでションボリしてます。

GTK4: Menu

現在 Comipoli の GTK4 化の準備をしているんだが。

まずメニュー。
GTK3 は GtkModelButton を GtkBox に入れソレを GtkPopoverMenu に。
だったけど GTK4 は GtkModelButton が廃止されている。

なので GTK4 は GMenuItem を GtkPopoverMenu に入れろとあるんだが。
GMenuItem をコードで作る方法はどこにも書いていない。

Gtk.PopoverMenu

しかたがないので XML で作る。

それから、GtkButton の image プロパティが廃止されていた。
代わりに icon-name が追加、つまり GtkImage を時前で用意しなくてもいい。

#!/usr/bin/env python3

from gi.repository import Gtk

menu_model = '''
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<menu id='comipoli-menu'>
  <section>
    <item>
      <attribute name='label' translatable='yes'>_Open</attribute>
      <attribute name='action'>app.new_file_action</attribute>
    </item>
    <item>
      <attribute name='label' translatable='yes'>_New Window</attribute>
      <attribute name='action'>app.new_window_action</attribute>
    </item>
    <item>
      <attribute name='label' translatable='yes'>_Preference</attribute>
      <attribute name='action'>app.preference_action</attribute>
    </item>
    <item>
      <attribute name='label' translatable='yes'>_Keyboard Shortcut</attribute>
      <attribute name='action'>app.shortcut_action</attribute>
    </item>
    <item>
      <attribute name='label' translatable='yes'>_About</attribute>
      <attribute name='action'>app.about_action</attribute>
    </item>
    <item>
      <attribute name='label' translatable='yes'>_Quit</attribute>
      <attribute name='action'>app.quit_action</attribute>
    </item>
  </section>
</menu>
</interface>
'''

class ComipoliMenuButton(Gtk.MenuButton):
    def __init__(self):
        ''' GTK3
        # MenuIten
        menu_open  = Gtk.ModelButton(active=True, action_name='app.new_file_action', text='_Open', use_markup=True)
        menu_new   = Gtk.ModelButton(active=True, action_name='app.new_window_action', text='_New Window', use_markup=True)
        menu_pref  = Gtk.ModelButton(active=True, action_name='app.preference_action', text='_Preference', use_markup=True)
        menu_kbd   = Gtk.ModelButton(active=True, action_name='app.shortcut_action', text='_Keyboard Shortcut', use_markup=True)
        menu_about = Gtk.ModelButton(active=True, action_name='app.about_action', text='_About', use_markup=True)
        menu_quit  = Gtk.ModelButton(active=True, action_name='app.quit_action', text='_Quit', use_markup=True)
        # Box
        vbox = Gtk.Box(visible=True, margin=10, orientation=Gtk.Orientation.VERTICAL)
        vbox.pack_start(menu_open, False, False, 0)
        vbox.pack_start(Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL), False, False, 0)
        vbox.pack_start(menu_new, False, False, 0)
        vbox.pack_start(menu_pref, False, False, 0)
        vbox.pack_start(menu_kbd, False, False, 0)
        vbox.pack_start(menu_about, False, False, 0)
        vbox.pack_start(Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL), False, False, 0)
        vbox.pack_start(menu_quit, False, False, 0)
        # PopoverMenu
        self.popover = Gtk.PopoverMenu()
        self.popover.add(vbox)
        '''
        builder = Gtk.Builder.new_from_string(menu_model, len(menu_model))
        model = builder.get_object('comipoli-menu')
        self.popover = Gtk.PopoverMenu.new_from_model(model)
        ''' GTK3
        #image = Gtk.Image(icon_name='open-menu-symbolic')
        #Gtk.MenuButton.__init__(self, can_focus=False, focus_on_click=False, image=image, visible=True, popover=self.popover)
        '''
        Gtk.MenuButton.__init__(self, can_focus=False, focus_on_click=False, icon_name='open-menu-symbolic', visible=True, popover=self.popover)

    def do_toggled(self):
        if self.props.active:
            self.popover.show_all()
        else:
            self.popover.hide()

丸ごと作り替えだった。

menu_button

そうそう、GtkShortcutsWindow の pack_start が廃止。
かつ GtkBox のサブクラスに、なので append で配置する。
orientation=Gtk.Orientation.HORIZONTAL 指定を忘れずに。
最初縦に配置されてしばらく悩んでしまった。
そんなことより。

shortcut

GtkShortcutsWindow を表示すると上記エラーを吐く。
もちろんサーチバーは出ない、これバグだよな。
Fedora 36 では修正されているのかな?

まだ色々とあるけど解決していないのでまた今度。

Flower

今日は市民四季の森へ、夏鳥を求めて。

enaga

エナガ、夏鳥じゃないけどなんか久々に見た。
おちょぼ口がカワイイよね。

kogera

コゲラ、夏鳥じゃないけどおなかが撮れたのは初めてだ。
いつも横向きばかりだったのでなんか新鮮。

後はホーホケキョとか色々な鳥が鳴いていたけど見えない!
夏鳥は?まあ何も撮れなかったよりはマシか。
そんなことより。

bara

薔薇がもう咲いていることに驚いた。
三月後半に梅を撮りに来た時は土に枝が刺さっていただけだったのに。

lib

リビングストンデージーっていう花らしい。
最初造花かと思った、なんだこの蛇の目傘みたいな花弁は。

花もいいな、今年は花メインでいこうかな。
歳をとると花が好きになるのは自然なことなんだなって。

というかカメラ二台体制にして本当に良かった。
レンズ交換しなくてもいいなら気軽にコイツもってなるのよね。

bash 5.1

Fedora 35 の bash は 5.1.8 である。
何を今更だが 5.1 で大文字小文字変換に新たな手段が追加されていた。
U u L 指定らしいんだけど、どう指定すればいいのかはドコにも書いていない。

How to convert a string to lower case in Bash? – Stack Overflow

やっと見つけた、fedorqui さんありがとう。

#!/bin/sh

pen='olympus PEN'

echo ${pen@U}
#=> OLYMPUS PEN

echo ${pen@L}
#=> olympus pen

echo ${pen@u}
#=> Olympus PEN

結局ブレースが必要なのか。
zsh みたく $pen:U とはできなかったのだろうか。
u は先頭文字以外を無視するだけだし使い道が無さそう。

#!/bin/sh

pen='olympus PEN'

echo ${pen^^}
#=> OLYMPUS PEN

echo ${pen,,}
#=> olympus pen

echo ${pen~~}
#=> OLYMPUS pen

今までのとタイプ数が変わっていないし、反転指定は無いし。
いやまあ、こんな手段もありますよってことで。

ということでコレを Tips ページに追記して。
こないだ調べたシェル関連も忘れないうちに追記して。
ついでに GTK4 ページのできているところまでもアップロードして。

よくみたら前の更新は昨年の今日だった、放置しすぎだ!
そんなこんなで、一年ぶりに本サイト更新です。