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

bracket

明けましておめでとうございます。

と、いうことで。

括弧 – Wikipedia
マジですか!えぇ…

( = パーレン (paren) # 小括弧
[ = ブラケット (bracket) # 大括弧
{ = ブレース (brace) # 中括弧

が 2017 年初っ端な現在の常識なの?

上から順に「括弧、角括弧、中括弧」と 15 年間読んでいや筆者は…
グローバルにも程があるだった、どの宗教にも合致しネェや!
いったい何の影響でこんなヒネクレたのかはシラネ、これが今の自分。

元旦に更新した ShellScript Tips も中括弧って表現を多用した。
だってさ、ついさっきまでソレが当然だと思っていたんだモン!

と、いうことで。
今年もよろしくお願いします。

GSubprocess and Python subprocess

GSubprocess: GIO Reference Manual
あれ、こんなのあったんだ。

これを使えば Gjs からでも Python の subprocess と同様なことができるかも。
subprocess を使って筆者が作っている Comipoli beta9 で一部を書き換えしてみた。

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

    def iter_7z(self):
        result, output, error, status = GLib.spawn_command_line_sync('7za l -slt "{0}"'.format(self.cbzpath))
        lines = output.decode("utf-8").split("\n")
        for line in lines:
            if line.startswith('Path'):
                name = line[7:]
                ext = os.path.splitext(name)[1].lower()
                if ext == ".jpg" or ext == ".jpeg" or ext == ".png":
                    #with subprocess.Popen(["7za", "x", "-so", self.cbzpath, name], stdout=subprocess.PIPE) as proc:
                    #    data = proc.stdout.read()
                    #    yield self.data_to_pixbuf(data)
                    sp = Gio.Subprocess.new(["7za", "x", "-so", self.cbzpath, name], Gio.SubprocessFlags.STDOUT_PIPE)
                    stream = sp.get_stdout_pipe()
                    p = GdkPixbuf.Pixbuf.new_from_stream(stream)
                    if p.get_height() > 1080:
                        rate = p.get_width() / p.get_height()
                        p = GdkPixbuf.Pixbuf.scale_simple(p, 1080 * rate, 1080, GdkPixbuf.InterpType.BILINEAR)
                    stream.close()
                    yield p

    def iter_zip(self):
        with zipfile.ZipFile(self.cbzpath) as o:
            l = o.namelist()
            l.sort()
            self.datas.clear()
            for name in l:
                ext = os.path.splitext(name)[1].lower()
                if ext == ".jpg" or ext == ".jpeg" or ext == ".png":
                    data = o.read(name)
                    yield self.data_to_pixbuf(data)

    def data_to_pixbuf(self, data):
        stream = Gio.MemoryInputStream.new_from_data(data)
        p = GdkPixbuf.Pixbuf.new_from_stream(stream)
        if p.get_height() > 1080:
            rate = p.get_width() / p.get_height()
            p = GdkPixbuf.Pixbuf.scale_simple(p, 1080 * rate, 1080, GdkPixbuf.InterpType.BILINEAR)
        stream.close()
        return p

動くじゃん。
しかもほとんど同じ!

ただ何故か g_subprocess_newv の引数が new メソッドに割り当てされている。
Gjs リファレンス同様に Property 割り当てで作ったほうが違和感が少ないかと。
PyGObject では JSON をイコールにするだけ。

// Gjs
let sp = new Gio.Subprocess({
    argv: ["7za", "x", "-so", self.cbzpath, name],
    flags: Gio.SubprocessFlags.STDOUT_PIPE
});

たぶんこれでいい。

一旦バイナリを取り出しせずに直接ストリームにできるからこのほうが効率いいかも。
でもそう書き換えると CBZ の処理側を作り替えしなきゃいけないけどどうしよう?

GLib, Gio はまだまだ知らないことがイッパイあると思い知る今日この頃です。

gnome-autoar

Nautilus 3.22 は 7z 形式の圧縮と展開をサポートした。
でも p7zip パッケージは入っていない、あれ?

もしかして自前対応なのだろうか。
というかあのダイアログは file-roller ではないし、あれは何?
これはソースコードを見てみるしかないな。

https://github.com/GNOME/nautilus/blob/master/src/nautilus-file-operations.c

凄く長いけど今現在で 8782 行目
autoar_compressor_new という関数をやっと見つける。

正体はコイツか!

gnome-autoar Reference Manual: gnome-autoar Reference Manual

マニュアルも普通に見つかった。
よし早速使ってみよう。

#!/usr/bin/gjs

const GnomeAutoar = imports.gi.GnomeAutoar;
const Gio = imports.gi.Gio;

let infiles = [Gio.File.new_for_path("blog.php")];
let outfile = Gio.File.new_for_path("test.7z");

let ar = new GnomeAutoar.Compressor({
    source_files: infiles,
    output_file: outfile,
    format: 4,
    filter:1,
    create_top_level_directory: false
});
ar.start(null);

//=> JS ERROR: Error: Cannot convert non-null JS value to G_POINTER

あれ?

GnomeAutoar.Compressor – Classes – GnomeAutoar 0.1

source-files の定義が int ってなんだよ…
Gjs や PyGObject からは現行では使えないのかな?
もう少し調べる。

file-roller command

忘れるところだった。
以前のように形式自由でアーカイブ可能な Nautilus Script を作らないと!

筆者は「新しくなったことを全否定する老人は死ね」な人間である。
でも今回は否定させて、だってデフォルトの機能では tar.gz が無いんだ。

7z なんて Kivy がソレで配布されていて mac でスゲー困ったし使いたくない。
GNOME はあの3形式以外は使ってほしく無いのかもだが流石に無理。
サルな人はすぐ昔のバージョンに戻そうとする、筆者は作る。

でも多数のアーカイブ形式に対応するって大変そう。
と思うけど実はもうソレをやっているものがあるんです。
file-roller はコマンドとして使えます。

file_roller

–add-to オプションで拡張子を指定すれば自動判別でアーカイブしてくれる。
ということでこんなシェルスクリプトを、拡張子のとうりな形式になります。

#!/bin/sh

path=`pwd`
name=${path##*/}
file-roller --add-to="${name}.tar.gz" "$@"

で。
Nautilus Script にコピーして送ってみる。

targz

これだけでで半角スペース及び日本語に対応。
以前と同じく親ディレクトリ名でアーカイブされる。

コレを Gjs なんかを使って GUI を以前みたく作って実行させればいい。
と思ったけどなんかコレで充分な気がしてきた。
Comipoli もやんなきゃいけないし、後は気が向いたら。

追記

下記だけで以前のダイアログが出せる、何を今頃だけど。

#!/bin/sh
file-roller --add "$@"

PyGObject Evince

遅ればせながら gir で evince を使う方法がやっと解った。
以下のファイル形式がこの手段で開けるようです。
ようするに Evince で開けるファイルということですけど。

PDF, CBR/CBZ, PostScript, TIFF, XPS, Djvu

#!/usr/bin/env python3

import gi
gi.require_version("EvinceDocument", "3.0")
gi.require_version("EvinceView", "3.0")
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, EvinceDocument, EvinceView

URI = "file:///home/sasakima-nao/doc/pdfs/gero-map.pdf"
#URI = "file:///home/sasakima-nao/doc/pdfs/postscript.ps"
#URI = "file:///home/sasakima-nao/doc/pdfs/kokomin.tiff"
#URI = "file:///home/sasakima-nao/doc/pdfs/erohon.cbz"

EvinceDocument.init()

doc = EvinceDocument.Document.factory_get_document(URI)
model = EvinceView.DocumentModel.new_with_document(doc)
view = EvinceView.View()
view.set_model(model)

scroll = Gtk.ScrolledWindow()
scroll.add(view)

win = Gtk.Window()
win.add(scroll)
win.connect("delete-event", Gtk.main_quit)
win.resize(600, 600)
win.show_all()

Gtk.main()

URI は各自の手持ちに書き換えてね。

init は必須、呼ばないとエラーになる。
EvinceDocument はファイル形式につては自動判別してくれるようです。
EvinceView は GtkScrolledWindow 以外には乗せることができないので注意。
マルチページ分はスクロールすると表示される。

Djvu なんて知らなかったし今後も普及するとは思えないけど。
CBR はもちろん unrar 必須です、HONDA のバイクじゃ(以下略

TIFF はマルチページを作る手段を知らないのでそれは試していない。
アーカイブして拡張子を変えるだけの CBR/CBZ のほうが使い勝手がいいしね。

ま、実際のところ PDF 以外での利用はほぼ無いと思う。