Celluloid

Fedora でアプリケーション一覧、あれ?
Celluloid というインストールした覚えのない GUI アプリがあるんだけーが。
というか gnome-mpv が無いぞ。

Celluloid

gnome-mpv が知らない間に変名していたのか。
そりゃ使おうと思うまで気が付かないよ、普段鑑賞用途は自作して使っているんだから。
試しに gnome-mpv を dnf から指定してみたらリンクなのか変名版が出て来た。
つまりパッケージマネージャは変名 Update にも普通に対応しているようだ、へー。

ただ現行版は Fedora でメニューボタンを押すと落ちるんだが、アップデートを待つか。

それと初期化で動画の元サイズにリサイズせず固定にできないものか。
筆者自作のプレイヤーも以前はそうしていたけどスマホが普及した今となっては合わない。
スマホの縦動画や 4k 等々で元サイズが豊富になった現状では MPV 方式はイラッとする。

動画プレイヤーの自作をやっていて良かった、自分に必要な機能に絞ったものが手に入る。
それも不満がサイズ関連だけで良かった、サイズ固定ならむしろ簡単なんだからさ!

有名アプリだとか複数人が開発に関わっていると基本動作を変更するのは超難問だ。
Windows 日本語版が cp932 から脱却できないなのもソレ、依存していた大多数の顧客を失う。
その理由で Windows を完全に使わなくなった人は筆者を含めて 1% にも満たないと思う。
mpv 系の場合はオプションで対応できる、と思うけど筆者の知らない色々があるのかなと。

そんなこんなで。
上記リンク先から Celluloid のソースコードに辿り着けるだろう。
プログラミングに興味を持つのは現状に不満がある、ことがキッカケなのだから。

PrintScreen

突然だがデジイチを先日はじめて買った。
Nicon D3400 という型落ちだけど新品かつ望遠レンズとバッグ込みで五万ならばと。

理由はポケ GO をヤメたので別の歩き回る理由が欲しいというこで。
10 年前の IXY からの乗り換えなのでデジイチなら何でも満足できそう。
欠点は今のところ人前で出すのは結構勇気がいるということだけだ。

先日の十五夜に IXY や iPhone では話にならない満月を撮ってみた。
しかし 6000×4000 の JPEG って 6MB 以上もあるんだよね。
1920×1080 のフル HD に切り出そうと考えた。

eog で開く
Ctrl+0 で等倍にする
F11 でフルスクリーン
月を中心に動かして PrintScreen

スクロールバーも一緒に切り出し、ちょっとまて。
Fedora じゃ駄目だ、macOS でやってみよう。
macOS だと 1440×900 になるけど。

プレビューで開く
command+0 で等倍にする
command+control+F でフルスクリーン
command+shift+3 でスクリーンショット
Retina の場合 72dpi に変換

ド素人がフルオートで撮ってもそれなりなものに、流石デジイチだ。
何年かたった後自分で見て「こんなもん…」と言える人に私はなりたい。
しかし切り出し作業が面倒臭い、てかデスクトップの Fedora でやりたいし。
切り出しアプリでも作るか、その手順を考えている時が一番楽しい。

Wayland screenshot

gtk_drag_dest_add_image_targets は思っていたのと違った。
選択範囲の移動とかみたいなものかと思ったけどそれ cairo の仕事や。
結局使い道はワカランまんま、他を追記したのでそれでよしということで。

問題はそれより下の項目に多過ぎだった。
動画プレイヤーを作るは Wayland では動かないし。
仮想端末エミュレーターはバインドが以前のままで新しい API が使えないし。
スクリーン情報を得るに書いた関数は全部 deprecated だし。
スクリーンショットを保存も Wayland で動かない。
もういや!

スクリーンショットは gnome-screenshot のソースを見ればいいか。

gnome-screenshot で検索、っておいサルブンツども。
「使い方」とかの誰でも解るしょーもないページを大量生産するなよ。。。。。
Google も公式サイトを一番上にしてほしい、サルブンツのはページランクゼロでいいよ。
gnome-screenshot github で検索やりなおし。

gnome-screenshot/screenshot-utils.c at master ? GNOME/gnome-screenshot ? GitHub

Gnome-Shell の機能を DBus で呼び出しているっぽい。
DBus ならメインループがいるな。
Window を作らなければ GApplication は勝手に終了する手法は使えるかな?

#!/usr/bin/env python3

from gi.repository import GLib, Gio

FILENAME = f'{GLib.get_home_dir()}/screenshot_2.png'

class App(Gio.Application):
    '''
        use mainloop
        Since there is no window, it ends as it is.
    '''
    def __init__(self):
        Gio.Application.__init__(self)

    def do_startup(self):
        Gio.Application.do_startup(self)
        #
        connection = self.get_dbus_connection()
        connection.call_sync(
            'org.gnome.Shell.Screenshot',
            '/org/gnome/Shell/Screenshot',
            'org.gnome.Shell.Screenshot',
            'Screenshot',
            GLib.Variant('(bbs)', (False, True, FILENAME)),
            None, 0, -1)

    def do_activate(self):
        pass

app = App()
app.run()

できた!
って、でもコレじゃ gnome-screenshot を使えでいいじゃん。
Gnome-Shell 以外で動くのかもよく解らないし。
そもそもコレ Gdk じゃないし、うーん色々困ったことに。

UTF8_STRING

PyGObject Tips 書き直しもやっと最後の項目になった。
そのドラッグアンドドロップについてチマチマ調べている。

とりあえず文字列のドロップを追加することに、したんだけど。
ようするに Gedit 等で文字列選択してドラッグしたもののことね。
下記コメントアウトが昔書いたやり方です。

#!/usr/bin/env python3

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

class Win(Gtk.ApplicationWindow):
    '''
        TreeView
    '''
    def __init__(self, app):
        Gtk.ApplicationWindow.__init__(self, application=app, title='Py')
        # DnD
        '''
        uri = Gtk.TargetEntry.new('text/uri-list', 0, 0)
        plain = Gtk.TargetEntry.new('text/plain', 0, 0)
        self.drag_dest_set(
                Gtk.DestDefaults.MOTION |
                Gtk.DestDefaults.HIGHLIGHT |
                Gtk.DestDefaults.DROP,
                [uri, plain],
                Gdk.DragAction.COPY )
        '''
        self.drag_dest_add_uri_targets()
        self.drag_dest_add_text_targets()
        ###self.drag_dest_add_image_targets()
        #
        self.label = Gtk.Label(label='Please drop your files')
        self.add(self.label)
        self.show_all()

    def do_drag_data_received(self, context, x, y, data, info, time):
        '''
            data: GtkSelectionData
        '''
        #print(data.targets_include_text()) # All False
        name = data.get_data_type().name()
        self.props.title = name
        #if name == 'text/plain':
        if name == 'UTF8_STRING':
            s = data.get_text()
            self.label.set_text(s)
        elif name == 'text/uri-list':
            uris = data.get_uris()
            l = []
            for uri in uris:
                fn = GLib.filename_from_uri(uri)[0]
                l.append(GLib.path_get_basename(fn))
            self.label.set_text('\n'.join(l))
        else:
            self.label.set_text(name)

class App(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(self)

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

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

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

えぇ。。。
gtk_drag_dest_add_text_targets を指定するだけだった。
てか UTF8_STRING という ContentType があったんだ、知らなかった。
新しいのかと思ったら GTK+2.6 からみたい、何故知らなかったんだ俺!

gtk_selection_data_targets_include_text は何をやっても False だ。
上記手段で判別はできるけど、この関数っていったい何なんだろう?

gtk_drag_dest_add_image_targets は簡単に試す手段が無かった。
だいたい使い方は解るので Tips ページを作る時に。
今週こそ終わらせなきゃ、おかげで mac 関連が完全に止まっているし。
macOS がバージョンアップする前にさわっておきたい。

PyGObject: Set Child Property

GtkPopoverMenu の submenu 続き。
Child Properties にある submenu にコードでアクセスする手段が解った。

gtk_container_child_set_property って関数があるヤン!
devhelp で GtkContainer の所を見るだけで解決してしまった。

それと GtkPopoverMenu の Description を見ると
親メニューに戻るボタンを最初に入れる、inverted, centered を使え。
そしてメインメニュー名は ‘main’ です、とある。

何だ、全部 devhelp に書いてあった。

英語が読めない?それは読もうとしないだけだ。
筆者だって Google 翻訳が無かったらこんなことワカランわい。

おまけで。
GtkPopoverMenu の use-markup は勝手に True にセットされるようだ。
visible も show_all を使うなら特に設定は不要、今まで無駄を書いていた。
メニューボタンも GNOME アプリは GtkMenuButton を使っているみたい。

そんなこんなで。
前回のサンプルコードをコードだけで書き直し。
プラス、メインメニューに戻るメニューの追加。
プラス、GtkMenuButton に変更及び F10 キーへの対応。

#!/usr/bin/env python3

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

class Win(Gtk.ApplicationWindow):
    '''
        GtkPopoverMenu submenu Sample
        Do not use GtkBuilder
    '''
    def __init__(self, app):
        Gtk.ApplicationWindow.__init__(self, application=app, title='Py')
        # First Contents
        menu_open  = Gtk.ModelButton(action_name='app.new_window_action', text='_New Window')
        menu_new   = Gtk.ModelButton(menu_name = 'tool_page', text='_Tool')
        menu_quit  = Gtk.ModelButton(action_name='app.quit_action', text='_Quit Application')
        #
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, margin=10)
        vbox.pack_start(menu_open, False, False, 0)
        vbox.pack_start(menu_new, False, False, 0)
        vbox.pack_start(Gtk.Separator(), False, False, 0)
        vbox.pack_start(menu_quit, False, False, 0)
        # Second Contents
        menu_back   = Gtk.ModelButton(menu_name = 'main', inverted=True, centered=True, text='_Tool')
        menu_set    = Gtk.ModelButton(action_name='app.set_title_action', text='_Set Titlebar Text')
        menu_append = Gtk.ModelButton(action_name='app.append_title_action', text='_Append Titlebar Text')
        #
        tool_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, margin=10)
        tool_box.pack_start(menu_back, False, False, 0)
        tool_box.pack_start(menu_set, False, False, 0)
        tool_box.pack_start(menu_append, False, False, 0)
        # GtkPopoverMenu
        self.popovermenu = Gtk.PopoverMenu()
        self.popovermenu.add(vbox)
        self.popovermenu.add(tool_box)
        self.popovermenu.child_set_property(tool_box, 'submenu', 'tool_page')
        # MenuButton
        open_image = Gtk.Image(icon_name='open-menu-symbolic', icon_size=Gtk.IconSize.MENU)
        self.menu_button = Gtk.MenuButton(image=open_image, popover=self.popovermenu)
        self.menu_button.connect('toggled', self.on_menu_button_toggled)
        # GtkHeaderBar
        hbar = Gtk.HeaderBar(show_close_button=True)
        hbar.pack_end(self.menu_button)
        self.set_titlebar(hbar)
        # F10
        accelgroup = Gtk.AccelGroup()
        self.add_accel_group(accelgroup)
        accelgroup.connect(Gdk.KEY_F10, 0, Gtk.AccelFlags.VISIBLE, self.on_f10)
        # self
        self.resize(240, 200)
        self.show_all()

    def on_f10(self, group, acceleratable, keyval, modifier):
        self.menu_button.clicked()

    def on_menu_button_toggled(self, button):
        if button.props.active:
            self.popovermenu.show_all()

class App(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(self)

    def do_startup(self):
        Gtk.Application.do_startup(self)
        # GAction
        new_window_action = Gio.SimpleAction(name='new_window_action')
        set_title_action = Gio.SimpleAction(name='set_title_action')
        append_title_action = Gio.SimpleAction(name='append_title_action')
        quit_action = Gio.SimpleAction(name='quit_action')
        # Add
        self.add_action(new_window_action)
        self.add_action(set_title_action)
        self.add_action(append_title_action)
        self.add_action(quit_action)
        # Keyboard Shortecut
        self.set_accels_for_action('app.new_window_action', ['N'])
        self.set_accels_for_action('app.set_title_action', ['S'])
        self.set_accels_for_action('app.append_title_action', ['A'])
        self.set_accels_for_action('app.quit_action', ['Q'])
        # Signal
        new_window_action.connect('activate', self.on_new_window_action)
        set_title_action.connect('activate', self.on_set_title_action)
        append_title_action.connect('activate', self.on_append_title_action)
        quit_action.connect('activate', self.on_quit_action)
        # Window
        Win(self)

    def on_set_title_action(self, action, parameter):
        self.props.active_window.props.title = 'Hello'

    def on_append_title_action(self, action, parameter):
        self.props.active_window.props.title += 'Hello'

    def on_new_window_action(self, action, parameter):
        Win(self)

    def on_quit_action(self, action, parameter):
        self.quit()

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

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

なんとか GNOME 標準アプリに近づいてきた。