月別アーカイブ: 2017年1月

Python __getitem__

自作 comicbook archive ビューアがまだまだ気に入らない。
ページが多い場合にとにかく遅い、理由は解っているけど。
全ページをメモリに展開し更にリサイズして配列に格納していたらそりゃ遅い。

マルチスレッド化とか色々試したけど Python じゃ無理だ。
サムネイル表示時にもう一度リサイズするとかなんか無駄なことしているし。

ページめくり高速化の為に全部読み込んでいたけど都度読み込みのほうが良さげ。
ということでクラスを作ったけど今まで配列から取り出ししていたんだよなぁ。
全部の処理を関数に書き換えするのが面倒臭いし間違えるのが怖い。

いや、コイツは Python で作っている。
__getitem__ を使えばいいじゃないか。

#!/usr/bin/env python3

from gi.repository import GLib, Gio, GdkPixbuf
import os, zipfile

class ComipoliArchive:
    def __init__(self):
        self.status = 0
        self.namelist = []

    def new_cba(self, uri, is_unrar, is_7za):
        self.arc = Gio.file_new_for_uri(uri)
        self.path = self.arc.get_path()
        ext = os.path.splitext(self.path)[1].lower()
        if ext == ".cbz":
            with zipfile.ZipFile(self.path) as o:
                l = o.namelist()
                l.sort()
                for name in l:
                    if GLib.Regex.match_simple("\.(jpe?g|png|gif)$", name, GLib.RegexCompileFlags.CASELESS, 0):
                        self.namelist.append(name)
        elif is_unrar and ext == ".cbr":
            self.status = 1
            result, output, error, status = GLib.spawn_command_line_sync('unrar vt -p- -- "{0}"'.format(self.path))
            lines = output.decode("utf-8").split("\n")
            for line in lines:
                s = line.lstrip()
                if s.startswith('Name: '):
                    name = s[6:]
                    if GLib.Regex.match_simple("\.(jpe?g|png|gif)$", name, GLib.RegexCompileFlags.CASELESS, 0):
                        self.namelist.append(name)
        elif is_7za and ext == ".cb7":
            self.status = 2
            result, output, error, status = GLib.spawn_command_line_sync('7za l -slt "{0}"'.format(self.path))
            lines = output.decode("utf-8").split("\n")
            for line in lines:
                if line.startswith('Path'):
                    name = line[7:]
                    if GLib.Regex.match_simple("\.(jpe?g|png|gif)$", name, GLib.RegexCompileFlags.CASELESS, 0):
                        self.namelist.append(name)
        else:
            return 1
        return 0

    def __getitem__(self, num):
        stream = None
        if (self.status == 0):
            with zipfile.ZipFile(self.path) as o:
                data = o.read(self.namelist[num])
                stream = Gio.MemoryInputStream.new_from_data(data)
        elif (self.status == 1):
            sp = Gio.Subprocess.new(["unrar", "p", "-inul", "-@", "--", self.path, self.namelist[num]], Gio.SubprocessFlags.STDOUT_PIPE)
            stream = sp.get_stdout_pipe()
        elif (self.status == 2):
            sp = Gio.Subprocess.new(["7za", "x", "-so", self.path, self.namelist[num]], Gio.SubprocessFlags.STDOUT_PIPE)
            stream = sp.get_stdout_pipe()
        p = GdkPixbuf.Pixbuf.new_from_stream(stream)
        stream.close()
        return p

    def __len__(self):
        return len(self.namelist)

イケるやん!

これで今まで配列から GdkPixbuf を取り出ししていたように都度展開だ!
この機能はどんな時に使うんだ?と思っていたけどこういう場合なのね、ふむふむ。
yield なんていらなかったんや!まあ勉強になったしイイけど。

どうでもいいけど beta11 は arcive と誤字していた、次で直す。

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 でまとめ直したほうが無難っぽい。

JXA and JavaScriptCore stdin

JavaScriptCore は readline が使えるのか。
MacのJavaScript Coreで標準入出力 – Qiita

こういう即時実行を使っている人は何か理由があるのだろうか?
よく見かけるけど無意味だと思うが、まあそれはいいとして。

#!/System/Library/Frameworks/JavaScriptCore.framework/Versions/A/Resources/jsc

print("May I ask your name? ");
let s = this.readline();
print("Hello " + decodeURIComponent(escape(s)));

// ex: ft=js

動くんだね。
戻り値は当然 UTF-8 のままなので変換しないと日本語は化ける。

JXA でも同様かと思ったけどできないや、そもそも print 関数すら無い。
JavaScriptCore とはまったくの別物?イマイチ解っていない。
コッチは Cocoa を使えってことなんでしょうか。

ということで久々に JXA をやってみたので更新。
JXA 版はコッチで。
JavaScript Tips – L’Isola di Niente

JavaScript 文字列と NSString と UTF-8 文字列な NSData が混在。
やっと相互変換を全部やれたけどもう少しなんとかならないものか。
てか情報が少なすぎ、そんな筆者もメインは Fedora てか GNOME だけど。

関係ないけど jjs の -scripting オプション。
シバンに書き込んだら jjs への引渡しでも適用された、そういうもの?

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 なんだから変だと言われても何を今更と思えるのが凄い。

bracket

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

と、いうことで。

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

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

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

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

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

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