JavaScript」タグアーカイブ

GIOChannel @ Gjs, PyGObject

全体的に内容が古くなった Tips ページの更新を昨年末から地味に。
次は我がサイトの最大コンテンツである PyGObject のページ。

blog ではやったけどまとめていない GIOChannel, GSubprocess, GRegex 等々。
ただ Gjs でやってるのよね、メイン言語が今は JavaScript ですし。
書き換えするより Gjs の新規ページを作ったほうがいいかなと。

まあどちらでも基本的には変わらないし。
と思っていたけど…

GNOME 3.22 時の記事です、GIOChannel でファイルの読み書きを。

Gjs

#!/usr/bin/gjs
 
const GLib = imports.gi.GLib;
 
let s = "abcdefg\nあいうえお\n3行目";
 
let channel = GLib.IOChannel.new_file("output_js.txt", "w");
channel.write_chars(s, -1);
channel.shutdown(true);
 
let channel2 = GLib.IOChannel.new_file("output_js.txt", "r");
let [status, str_return] = channel2.read_to_end();
print(str_return);
channel2.shutdown(true);

PyGObject

#!/usr/bin/env python3

from gi.repository import GLib

s = "abcdefg\nあいうえお\n3行目"

channel = GLib.IOChannel.new_file("output_py.txt", "w")
#channel.write_chars(s, -1) # TypeError: Item 0: Must be number, not str
channel.write(s) # deprecated
channel.shutdown(True)
 
channel2 = GLib.IOChannel.new_file("output_py.txt", "r")
status, str_return = channel2.read_to_end()
print(str_return.decode("utf-8"))
channel2.shutdown(True)

で同様になる。

PyGObject は g_io_channel_write_chars が使えない。
g_io_channel_write は既に非推奨、これは困る。

それより read での挙動が違うんですけど。
Gjs は JavaScript 文字列で戻るけど PyGObject はデコードが必要。
write はそのまま UTF-8 で書き出しなのにチグハグです。

同じライブラリを使っているはずなのに。
やはり Gjs でまとめ直したほうが無難っぽい。

GRegex

正規表現に実は今まで興味が無かった。
実際に知らなくても特に何も困らなかった。

でもパターンの書き方を何かの言語で覚えてしまえば他で使い回せる。
他の言語はもとより grep コマンドでさえ、と今頃知った。
これは勉強しといたほうが良いかも。

でも使う手段は言語によって違うんだよなぁ。
いっそ GLib で使えればどんな言語でも gir で同じ手段でイケるのに。

Perl-compatible regular expressions: GLib Reference Manual

って普通にあるじゃん、perl と同じってことね。
Gjs でコイツと JavaScript の RegExp を比べてみよう。

正規表現 – JavaScript | MDN

画像の拡張子が最後に付いているかを大小文字区別なく探すコード。

#!/usr/bin/gjs

const GLib = imports.gi.GLib;

const ARRAY = ["001.jpg", "002.JPEG", "003.Png", "004.gif", "jpeg.gif"];
const PATTERN = "\.(jpe?g|png)$";

// JavaScript RegExp
ARRAY.forEach(function(s) {
    let re = new RegExp(PATTERN, "i");
    print(re.test(s));
});

print("----------");

// GLib Regex
ARRAY.forEach(function(s) {
    print(GLib.Regex.match_simple(PATTERN, s, GLib.RegexCompileFlags.CASELESS, 0));
});

/* @ Python
import re
for s in ARRAY:
    res = True if re.search(PATTERN, s, re.I) else False
    print(res)
*/

/* output
true
true
true
false
false
----------
true
true
true
false
false
*/

うん、見事に同じパターンが使えるのね。
コレで Vala だろうが何だろうが同様に使える。

Python は真偽値を戻さず mach Object 自体が True って変。
そりゃ Python なんだから変だと言われても何を今更と思えるのが凄い。

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 からは現行では使えないのかな?
もう少し調べる。

Safari 10

遅ればせながら MacBook Air を macOS Sierra にしました。
2011 モデルですが何も問題なくアップグレードできました。

五年前のマシンでも Siri は使えます、マジで iPhone と同じ。
サブマシンなので詳しいことはもっとヘビーな人のサイトで。

さて Safari 10 で我がサイトはキチンと表示できるか確認だ。
うん問題ないみたい、Mac の情報はほとんど無いサイトだけど。
あら、Safari 10 がついに let に対応してくれたようで。

safari_let

iPhone のアップデートはまだ様子見だけど多分同じだろう。
これで主用ブラウザすべてで let を使っても大丈夫になった。
いや、macOS や iOS はアップグレードしない人も結構いるんだけど。

つか、osascript の JavaScript もやはり let 対応になっている。
これで Gjs と同じように変数定義できる、やったね。

osascript_let

普通に文字列保存すると古い Mac の文字コードになるのは変わっていない。
ソコを一番変えてほしいんだけど、UTF-8 以外は全部絶滅してくれよ。
まあ少数派の意見なんでしょうけどね。

追記
iOS 版 Safari 10 もやはり同様でした。