PyGObject HANDLES_COMMAND_LINE

前回 *.desktop に追記で ApplicationMenu だった位置に項目追加の件。
あの後アップデート通知から適用して再起動をしたら表示されるようになった。
なんだコレ、*.desktop 項目ってメモリキャッシュでもしているのだろうか?
よくわからないけど前回のリンク先の方法のみで項目追加は可能だと解った。

それより肝心な –new-window オプションの適用手段はなんとかしたい。
GSettings にフラグを用意して転送先 GApplication から参照では強引すぎる。
他に手段があるはず、Nautilus や Gedit はそんな変なことしていないけどメニューにあるし。
ダメだ、筆者の経験値では他の手段を思いつかない。。。。。

素直に Gedit のソースコードを参考にすることにする。
Gedit の About を開いてリンクをクリックすればホームページが開くのでそこから落とす。
当然 C 言語、Python でアプリを作れる人は C でも作れるけど面倒だから(以下略

GtkApplikation の flags に G_APPLICATION_HANDLES_OPEN だけでなく
G_APPLICATION_HANDLES_COMMAND_LINE を OR 演算している、あれ?
この指定って CUI アプリを作る時に使うと思っていた、思い込みだったようだ。

command-line シグナルとのセットのようだ、–new-window をコチラで処理している。
なるほど、–version の場合は転送前の GApplication にて処理。
–new-window は転送先の GApplication で処理ってことね。
handle-local-options と command-line シグナルってそういうことみたい。
こんな便利なものを GApplication は提供していたとは。

ただ同様に G_APPLICATION_HANDLES_COMMAND_LINE を OR で色々試してみたんだけど。。。。。
どうやっても open シグナルが発行されない、これじゃファイル名を得られない。
devhelp には G_APPLICATION_HANDLES_OPEN 指定なら残りの引数で open シグナルとあるんだが。
Google 翻訳なので完全に正しいとはいえないんだけど、とにかく困った。

よく見ると Gedit もファイル引数は独自で処理しているみたいだし。
あきらめて command-line ハンドラ内で処理するしかないみたい。
get_arguments で引数は全部取得できる、存在するファイルかどうかを自前で調べ
g_application_open を発行という手もあるけど二度手間だよね。

経緯を全部書いたら長くなってしまった。
ということをふまえて早速サンプルコードを書いてみる。

#!/usr/bin/env python3

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

class Win(Gtk.ApplicationWindow):
    def __init__(self, app):
        Gtk.ApplicationWindow.__init__(self, application=app, title='Chikubi')
        self.resize(150, 60)
        self.show_all()

class App(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(
            self,
            application_id='bura.oppai.chikubi',
            flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE)
            # nonsense
            #flags=Gio.ApplicationFlags.HANDLES_OPEN | Gio.ApplicationFlags.HANDLES_COMMAND_LINE)
        # add --version, -v option
        self.add_main_option('version', b'v', GLib.OptionFlags.NONE, GLib.OptionArg.NONE, 'Show Chikubi Version', None)
        # add --new-window, -n option
        self.add_main_option('new-window', b'n', GLib.OptionFlags.NONE, GLib.OptionArg.NONE, 'New Chikubi Window', None)

    def do_command_line(self, command_line):
        # --new-window Option Handler
        options = command_line.get_options_dict()
        if options.lookup_value('new-window', GLib.VariantType.new('b')):
            w = Win(self)
        # get filename
        arg = command_line.get_arguments()[1:]
        for s in arg:
            # Option ignored
            if not s.startswith('-'):
                # URI? (file:///...)
                if '//' in s:
                    f = Gio.file_new_for_uri(s)
                else:
                    f = Gio.file_new_for_path(s)
                if f.query_exists():
                    self.props.active_window.set_title(s)
                    break
        return 0

    def do_handle_local_options(self, options):
        # --version Option Handler
        if options.lookup_value('version', GLib.VariantType.new('b')):
            print('Chikubi 0.0.1')
            return 0
        return -1

    def do_startup(self):
        Gtk.Application.do_startup(self)
        Win(self)

''' needless
    def do_activate(self):
        self.props.active_window.present()

    def do_open(self, files, n_file, hint):
        self.props.active_window.present()
'''

if __name__ == '__main__':
    app = App()
    app.run(sys.argv)

狙ったとおりになった。

*.desktop で %U 指定だと file:///… の URI で渡ってくるので注意ね。
パス名は二連スラッシュに絶対できないはずだから振り分けはコレでイケると思う。
もう少し自前 debug してから自アプリは更新します。