Programming」カテゴリーアーカイブ

exit_status

さて前回やった cbr ファイルの展開を我がアプリに実装するのだが。
アレは完全なる unrar コマンド依存である。
つまり、使う側のマシンに unrar が入っているかどうかを調べなければいけない。

ライブラリやコマンドが入っていないマシンなら華麗に受け流すようにするべき。
かなり重要なコトなのにそういう Tips を書く人が少ないんだよなぁ。
まあソレは置いておいて。

16.16. ctypes ? Pythonのための外部関数ライブラリ ? Python 3.5.2 ドキュメント

ctypes.util.find_library(name)

でライブラリの有無を調べられるってことなんだけどコレって意味あるのか?
そもそも Python では使う機会が少ないしどうでもいいか。

いや、今回のはライブラリじゃなくてコマンドの有無。

Bashでコマンドの存在チェックはwhichよりhashの方が良いかも→いやtypeが最強 – Qiita

そうか type コマンドを使うのか。
ってソレよく考えたら自分で書いていた。
シェルスクリプトの覚書色々/コマンドの存在を確認してから実行

標準出力ではなく終了ステータスがゼロ(正常終了)かどうかを調べる。

g_spawn_command_line_sync なら第四引数がゼロになるかを調べればいい。
PyGObject だと戻り値タプルの四番目になるので

#!/usr/bin/env python3

from gi.repository import GLib

is_unrar = GLib.spawn_command_line_sync("type unrar")[3] == 0

if is_unrar:
    print("unrar is in your machine")
else:
    print("unrar not found...")

# test
print(GLib.spawn_command_line_sync("type unko")[3] == 0)

標準出力の値は勝手に捨ててくれる。

これで振り分けは大丈夫そうだ。
みなさん、ko というアーカイブは絶対に作ってはいけません。

ところで小牧駅周辺がカイロスの巣っぽくなっているのは何故だ?
用事ついでに歩いたらやたらイッパイ出たんだけーが。

Read application/x-cbr

やっと application/x-cbr を展開する方法が解った。
コマンドから取得するしかないんだから unrar のヘルプを見る。

unrar_help

引数無しで unrar と打つとヘルプが出るのね、知らなかった。
えっと、オプションの使い方がよくわかんないんですけど。

つーことで file-roller のソースを落として見る。
fr-command-rar.c を見つける。

<Commands> はハイフン無し、ファイル名の前にハイフン 2 つ。
なるほど、jjs と同じなんだね。

ファイルに展開はコイツで解ったけどメモリ上に展開させる手段がワカンネ。
ええい最後の手段だ、mcomix のソースを見る、筆者のも GPL だからいいよね。
見ないままココまでやってきたけど筆者の技術ではもう無理だ。
ああ Python2 だ、今となっては懐かしい。

unrar p -inul -@ -- test.cbr 111.jpg > a.jpg

なんだ、p オプションにするだけだった。
上記でメモリ展開されたファイルをリダイレクトするとファイルにもなる。

つまり最初にファイル名のリストを取得して一つずつ抜き出せばいいのね。
よしこんだけ理解すればなんとかなるぞ。

#!/usr/bin/env python3

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

class CbrTest(Gtk.ApplicationWindow):
    def __init__(self, app):
        Gtk.ApplicationWindow.__init__(self, application=app)
        self.datas = []
        self.pixbuf = []
        # add
        button = Gtk.Button(label="show CBR")
        button.connect("clicked", self.on_button_clicked)
        self.fbox = Gtk.FlowBox(valign=Gtk.Align.START, max_children_per_line=6, min_children_per_line=6)
        sc = Gtk.ScrolledWindow()
        sc.add(self.fbox)
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
        vbox.pack_start(button, False, False, 0)
        vbox.pack_start(sc, True, True, 0)
        self.add(vbox)
        self.resize(600, 300)
        self.show_all()

    def on_button_clicked(self, button):
        # listup filename
        result, output, error, status = GLib.spawn_command_line_sync("unrar vt -p- -- test.cbr")
        lines = output.decode("utf-8").split("\n")
        for line in lines:
            s = line.lstrip()
            if s.startswith('Name: '):
                name = s[6:]
                ext = os.path.splitext(name)[1].lower()
                if ext == ".jpg" or ext == ".jpeg" or ext == ".png":
                    # error: output is gchar*
                    #result, output, error, status = GLib.spawn_command_line_sync("unrar p -inul -@ -- test.cbr {0}".format(name))
                    with subprocess.Popen(["unrar", "p", "-inul", "-@", "--", "test.cbr", name], stdout=subprocess.PIPE) as proc:
                        output = proc.stdout.read()
                        self.append_data(output)

    def append_data(self, data):
        try:
            stream = Gio.MemoryInputStream.new_from_data(data)
            p = GdkPixbuf.Pixbuf.new_from_stream(stream)
            minp = p.scale_simple(80, 100, GdkPixbuf.InterpType.BILINEAR)
            image = Gtk.Image(pixbuf=minp)
            image.show()
            self.fbox.add(image)
        except Exception as e:
            print(e)
        return False

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

    def do_activate(self):
        CbrTest(self)

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

おぉ。

naekore

なんとかなったけど GLib.spawn_command_line_sync には困った。
output に \0 が含まれているとソコでバイナリが途切れてエラーになる。
そりゃまあ C での定義は gchar* だし当然かもしれないけど。
しかたがないので subprocess を使う、Gjs だとお手上げかも。

ClutterActor on the GtkWidget

今頃気が付いた、sushi は cbr/cbz を表示できる!

あ、sushi は nautilus で Space キーにてプレビューする拡張。
macOS のプレビューと同じものね、Fedora には最初から入っている。

でも巨大ファイルだと最初の表示が遅いなぁ。
CPU 全コアを使って展開しているけど全部読み込むまで表示されないっぽい。
マルチスレッドのほうがいいというわけではないのね。
Evince と同じなので多分 gir で Evince を使って…

そうか、gir で Evince を使えばいいのか!
読み込みの工夫で初回表示を早くできれば我がアプリに使えるかも。
sushi は Gjs だ、早速コードを見てみよう。

gedit /usr/share/sushi/js/viewers/evince.js

gtkclutter

うぅ、思ったより解析に時間が掛りそうだ。
いやまてよ、この下のほうのコードはまさか。
おまけで ClutterActor 上に GTK+ 部品を乗せる方法が解ったぞ。

#class ComipoliGoButton(Clutter.Actor):
class ComipoliGoButton(GtkClutter.Actor):
    def __init__(self, right):
        '''Clutter.Actor.__init__(self)
        if right:
            pixbuf = GdkPixbuf.Pixbuf.new_from_xpm_data(ICON_RIGHT)
        else:
            pixbuf = GdkPixbuf.Pixbuf.new_from_xpm_data(ICON_LEFT)
        image = Clutter.Image()
        image.set_data(
            pixbuf.get_pixels(),
            Cogl.PixelFormat.RGBA_8888,
            pixbuf.get_width(),
            pixbuf.get_height(),
            pixbuf.get_rowstride()
        )
        self.set_content(image)'''
        GtkClutter.Actor.__init__(self)
        if right:
            self.icon = Gtk.Button.new_from_icon_name("go-next-symbolic", Gtk.IconSize.MENU)
        else:
            self.icon = Gtk.Button.new_from_icon_name("go-previous-symbolic", Gtk.IconSize.MENU)
        #self.set_content(self.icon)
        self.props.contents = self.icon
        self.icon.show()
        #self.connect("enter-event", self.on_enter_event)
        #self.connect("leave-event", self.on_leave_event)
        #self.props.opacity = 0x00
        self.set_reactive(True)

うんうん。

gtkclutter2

ClutterActor 継承をヤメにして GtkClutterActor にする。
set_content だと例外になるけど property なら何故かセット可能。
上に GtkWidget があるせいで enter-event が動作しないので変える。
コレだけで普通に GtkWidget と相互の上下関係が作れる、やったね。

よし久々に comipoli の更新でもするか。
でも今日は「名駅周辺でポケボール集め」という大切な大切な用事が…
我が町でポケ GO をやっているとすぐ無くなっちゃうのは痛い。

JXA Rename

以前 JXA で Finder から選択項目を得られればどうにでもできる。
なんて書いたけどそのどうにかする方法を書いていない。
ということで選択ファイルを全部小文字にする手段でも。

Macでファイル名を小文字から大文字に一発で変換する ? 静岡県湖西市南台のECサイト制作会社

いや、これじゃカレントディレクトリ全部になっちゃう。
Terminal.app で開き直すのも面倒だし。

【Mac】Automatorを使って画像のファイル名を連番付きに変更する方法 | コトノバ

Automator にはこんなのがあるのか。
指定がごちゃごちゃして余計に面倒臭いと思うんですけど。

JXA で拡張ならこんなに簡単。

#!/usr/bin/osascript

let selections = Application("Finder").selection();
selections.forEach(function(item) {
    // Get Property
    let s = item.name();
    // Set Property
    item.name = s.toLowerCase();
});

後は小文字にしたいファイルを選択して。

lowercase

JXA のプロパティは括弧で getter、括弧無しなら setter になるようだ。
何か変だけどそういうもんだと思うしかない。
それとヘルプで r/o となっているところはリードオンリー。

finder_help

文字列変換は JavaScript そのまんま。
ただし alert, prompt とかは Application から作る。

Batch File Rename Script ? GitHub

cookbook はわざと難解に書いているとしか思えないのは気のせい?

shell

ブックマークの整理をしていて素晴らしいページを発見。
って、つまり一年以上前にブックマークして華麗に忘れていたんだけど。

スマートな紳士のためのシェルスクリプト – @IT

この人は経験値があって話が上手いな、真似したい。
筆者が自分のことを筆者と書いているのはそういう人達の真似だったり…
でも一番素敵と思ったのは (2) で駄目と書かれた

if type logger > /dev/null 2>&1; then
    LOGGER="logger -s -p user.notice -t dhclient"
else
    LOGGER=echo
fi

で。

いや、絶対パスに入っているかどうかはディストリビューション側の仕事で。
サードパーティ側はコマンドが使えるかどうか「だけ」が重要ですんで。

いやまて…
コレってインタラクティブシェルだとどうなる?
面白そうなので早速。

dev_null

インタラクティブシェルでもやはり何も出力されない。
>>> の PS2 変数さえ無視される、打ち込んだキー以外は完全破棄のようだ。
だけど exit() 等の関数は普通に通用されてしまう、面白い。
なので、gedit や gnome-terminal は問題なく起動できてしまう。

でも eog はどうやっても駄目、あぁなるほど。
eog って GNOME Project の中でもかなり特殊と感じていたのはコレか。
もの凄く優秀なアプリなんだけど GNOME の思想と何か違う、みたいな。
それが何かはまだ解らないけど、まあそれは別の話で。

あぁそういう手があったのか、みたいな。
mac でも当然使えるしシェルスクリプト覚書に追記しようかと。