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 ページのできているところまでもアップロードして。

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

mpv: Ctrl+q @ Close all MPV Windows

Fedora で mpv を使って困ること。
Ctrl+q で複数起動したすべての mpv を終了できないこと。

だって GNOME のアプリは全部そうなっているんだもの。
そういえば Chrome が Fedora では終了できないな、狐はできるのに。
コレは GNOME アプリ同様 Ctrl+w 連続押しで代用できるのでまあよし。

macOS では command+q ですね、サードパーティすら全部。
そりゃ macOS はウインドウを全部閉じても終了できないんだから必須機能。
唯一 Finder だけが不可、ファイルマネージャは常に起動しているし。

でも Nautilus は Ctrl+q で全部のウインドウを閉じてくれる動作をする。
そんなこんなで、凄く気になるんですよ。

で、よく考えたら UNIX には killall コマンドがあった。
Linux ではコマンドと GUI の区別が無いんだから使えるかも。
ということで Lua スクリプト。

-- ~/.config/mpv/scripts/mpv_ctrl_q.lua

-- Ctrl+q @ Close all MPV Windows.
function on_close_all()
    os.execute('killall mpv')
end
mp.add_key_binding('Ctrl+q', 'close_all_func', on_close_all)

これをスクリプトディレクトリに突っ込んで。
おぉ自分を含めて全部終了してくれる、やったね。
macOS では GUI は区別されているから無理だと思う。
統一されたキーボード操作が無い某 OS はどうでもいいね。

ついでに、最近の mpv osc 設定。
シークバーは QuickTime のようにフローティングもできると知った。

# ~/.config/mpv/script-opts/osc.conf

# 動画に合わせた OSC リサイズ無効
vidscale=no

# 薄くする
boxalpha=180

# 常にトータル時間
timetotal=yes

# タイトルはファイル名 -- タイトル名
title=${filename} -- ${media-title}

# シークバーのみに、弄る時に枠外になるのを防げる
layout=slimbox
seekbarstyle=diamond
valign=0.95

kogera

てかシークバーだけにできるんだ、こりゃいいわ。
だって osc ってシークバーしか使わないよねみんな。

kogera

QuickTime も再生ボタンとか消せないかなぁって。
macOS では動画はほとんど見ないので QuickTime のままです。
ちなみに QuickTime だけで動画の切り貼りやリサイズ保存ができる。
筆者の編集範囲ならコイツだけで完了するので重宝していたりします。

GTK4: GtkRadioButton to GtkCheckButton

GTK4 は GtkRadioButton が廃止されていた。
え、GTK4 製な gnome-text-editor のメニューにはラジオボタンがあるぞ?
継承元である GtkCheckButton のドキュメントを見てみた。

Gtk.CheckButton

なるほど、GtkCheckButton に統合されたってことなんだね。
グループ化すると自動的に丸い形になるのか、よしやってみよう。

#!/usr/bin/env python3

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

class RadioWin(Gtk.ApplicationWindow):
    '''
        GTK4: GtkRadioButton
    '''
    def __init__(self, a):
        Gtk.ApplicationWindow.__init__(self, application=a)
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        root = None
        for s in ['Leica', 'SIGMA', 'Panasonic']:
            # GtkRadioButton to GtkCheckButton
            r = Gtk.RadioButton(label=s, group=root)
            #r = Gtk.CheckButton(label=s, group=root)
            r.connect('toggled', self.on_radio_toggled)
            if not root:
                root = r
                r.props.active = True
            vbox.pack_start(r, False, False, 0)
            #vbox.append(r)
        self.add(vbox)
        #self.set_child(vbox)
        self.show_all()
 
    def on_radio_toggled(self, button):
        if button.props.active:
            self.props.title = button.props.label

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

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

GTK3

#!/usr/bin/env python3

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

class RadioWin(Gtk.ApplicationWindow):
    '''
        GTK4: GtkRadioButton
    '''
    def __init__(self, a):
        Gtk.ApplicationWindow.__init__(self, application=a)
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        root = None
        for s in ['Leica', 'SIGMA', 'Panasonic']:
            # GtkRadioButton to GtkCheckButton
            #r = Gtk.RadioButton(label=s, group=root)
            r = Gtk.CheckButton(label=s, group=root)
            r.connect('toggled', self.on_radio_toggled)
            if not root:
                root = r
                r.props.active = True
            #vbox.pack_start(r, False, False, 0)
            vbox.append(r)
        #self.add(vbox)
        self.set_child(vbox)
        #self.show_all()
 
    def on_radio_toggled(self, button):
        if button.props.active:
            self.props.title = button.props.label

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

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

GTK4

RadioButton

うん唖然とするくらい同じだ、これなら GTK4 化に困らない。
そんなことより、GTK3 と GTK4 って思っていたより違うんだね。

GTK3 はデフォルトが非表示なせいか最初の toggled シグナルが発生しない。
GTK4 のウインドウのほうが少しだけ大きい、なんでだろう。
非アクティブ時のタイトルバー文字列が GTK4 だと濃いまま。
いやタイトルバーはヘッダーバーにすれば普通に薄くなるみたいですけど。

関係ないけど GtkApplication 部分はコレが一番短く書けるな。
今後のサンプルコードはコレでいこうと思う。

だいぶ GTK4 も解ってきたしまとめや自アプリの GTK4 化を。
したいんだけど Fedora 35 の PyGObject は DnD に不具合があるので。
text/uri-list の取得ができずファイルのドロップで開くことができない。
次の Fedora で修正されているはずなのでそれから本格的に。