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

PyGObject and Gjs API Reference

PyGObject の API Reference を見つけた。
GLib, Gtk だけでなく GObject introspection 全部。

Python GI API Reference

正直かなり見難いけど。
使い道がイマイチ解からなかったライブラリもコレでバッチリ。

実は Gjs も同じライブラリなのでそのまんま適用できます。
とりあえず Xkl で試してみましょう。

#!/usr/bin/env python3

from gi.repository import Xkl

s = Xkl.get_country_name("DE")
print(s)
#=> Germany

s = Xkl.get_language_name("jpn")
print(s)
#=> Japanese

Gjs

#!/usr/bin/gjs

const Xkl = imports.gi.Xkl;

let s = Xkl.get_country_name("DE");
print(s);
//=> ドイツ

s = Xkl.get_language_name("jpn");
print(s);
//=> 日本語

コメントに解答してますが、動かしてみます。

python_setlocale

忘れていた、3.18 から require_version 必須だった!
Gjs は不要なのに、今後は解らないけど。
それより Gjs だと日本語になるんだが何が違うのだ?

#!/usr/bin/env python3

import locale
locale.setlocale(locale.LC_ALL, '')

import gi
gi.require_version('Xkl', '1.0')

from gi.repository import Xkl

s = Xkl.get_country_name("DE")
print(s)
#=> ドイツ

s = Xkl.get_language_name("jpn")
print(s)
#=> 日本語

これで Gjs と同じ結果になった。
Gjs は locale 設定を自動的にやっているってことなのね。
PyGObject しかやっていなかったら英語でしか取得できないと勘違いしていたかも。
やっててよかった JavaScript!

それより、Gjs のほうが簡単になる場合なんてあったのか。
そろそろ本格的に移行かな、Y901x を作り替えするだけだが。

Gjs new

Gjs のサンプルコードを探していると???となることが多い。
new キーワードの有無がマチマチ、パラメータが JSON か否か。

JSON イラネーだろ!と外すと動かないし、もう何がなんだか。
海外でしかサンプルコードが見つからないから英語でよく解らない。

だったけどやっと解決した。

#!/usr/bin/gjs

const Gio  = imports.gi.Gio;

/* After new @ Params ALl */

let uostream = Gio.UnixOutputStream.new(0, false);
let dostream = Gio.DataOutputStream.new(uostream);
dostream.put_string("Enter Some String :", null);

let uistream = Gio.UnixInputStream.new(1, false);
let distream = Gio.DataInputStream.new(uistream);
[txt, len] = distream.read_line_utf8(null);
print(txt);

/* Before new @ Params Json  */

let uostream2 = new Gio.UnixOutputStream({fd: 0});
let dostream2 = new Gio.DataOutputStream({base_stream: uostream2});
dostream2.put_string("Enter Some String :", null);

let uistream2 = new Gio.UnixInputStream({fd: 1});
let distream2 = new Gio.DataInputStream({base_stream: uistream2});
[txt2, len2] = distream2.read_line_utf8(null);
print(txt2);

コレだけのことだったのかYO!

どのオブジェクトも new はキーワードでもメソッドでもいい。
メソッドにしたら C 同様にパラメーターを全部埋める。
キーワードにしたら JSON で、null やゼロなら埋めなくてよし。

どっちも面倒臭い。
new も null パラメーターもいらない、としてくれるのが一番なんだけど。
ついでにセミコロン、ってソレが PyGObject だった。

GTK+/開発 – ArchWiki

GObject Introspection って今はこんなに沢山の言語から使えるのね。
GNOME 自前の Gjs でさえ情報が少ないのに他はどうするんだって感じですが。
結局 Python が一番情報が多いのは GTK2 時代と同じですね。

ところで。
let
Google Chrome, Firefox がいつのまにか let 宣言に対応しているね。
後は Safari だけだな、osascript でも使えないけど。

Mac JavaScript 日本語

osascript にて文字列のファイル保存。
いくら探しても皆アルファベットでしか保存していない。
理由はコレ。

#!/usr/bin/osascript

ObjC.import("Cocoa");

var jstr = "日本語が化ける";
var fname = "/Users/sasakima-nao/ng.txt";

app = Application.currentApplication();
app.includeStandardAdditions = true;

var refno = app.openForAccess(Path(fname), {
    writePermission: true
});
app.write(jstr, {
    to: refno
});
app.closeAccess(refno);

var cmd = "cat " + fname;
app.doShellScript(cmd);

cat_xxx

doShellScript の cat では普通に日本語処理できている。
けど普通に cat すると見事に文字化けするんだなぁこれが。
TextEdit.app で開くと問題無し、だけど別名保存しようとしてみよう。

textedit_jp

日本語(Mac OS) って何だよ?

MacJapanese – Wikipedia

コレらしい、osascript の内部文字列は古い Mac のままなのか…
AppleScript 自体は昔からあるのでそのまんま拡張しただけと憶測。
結果文字列をそのまま保存すると内部文字列のままで出力されると。

Mac 内製アプリは対応しているけど今の $LANG は UTF-8 だよ。
NSString は UTF-16 みたい、Mac も Windows と変わらないカオスだった。
Linux が一番進んでいるって笑えないYO!

とりあえず iconv で変換すればいいのかな?

UTF-8-MAC なんていう文字コードはありません | ものかの

cat ng.txt | iconv -f UTF8-MAC -t UTF8

とやってもエラー、UTF-16 から変換にすると中国語?
手段はあるだろうけどブン投げる、調べるだけ時間の無駄だ。

だって UTF-8 で出力しないと意味無いってば!

#!/usr/bin/osascript

ObjC.import("Cocoa");

var jstr = "コレなら日本語 OK です\n";
var fname = "/Users/sasakima-nao/ok.txt";

var nsstr = $.NSString.alloc.initWithString(jstr);

var data = nsstr.dataUsingEncoding($.NSUTF8StringEncoding);
var res = data.writeToFileAtomically(fname, true);
if (res) {
    app = Application.currentApplication();
    app.includeStandardAdditions = true;
    var cmd = "cat " + fname;
    app.doShellScript(cmd);
}

これでなんとかなった。
NSString に変換して UTF-8 で NSData に変換してバイナリで保存。

cat.ok

Apple が作っている言語なのに何だよこのアホ臭さ。
結論、コンソールアプリはサードパーティの言語でやったほうがいい。

でもこういうことに苦戦するのがプログラミングの楽しみでもあって。
Linux に移行した直後に pygtk と格闘していた頃も楽しかった。

Gjs pipe

今回は Gjs で標準入力とパイプ入力の振り分けをやってみよう。
ちなみに Python だと標準の os モジュールだけで可能。

#!/usr/bin/env python3

import os

if os.isatty(0):
    # stdin
    s = input("何か入力してください >")
    print("[{0}]が入力されました".format(s))
else:
    # pipe
    with os.fdopen(0) as f:
        s = f.read()[:-1]
        print("[{0}]がパイプから渡されました".format(s))

コレと同様の動作ができるものを Gjs で作りたいということで。

Gjs は当然 GLib を使う、しかし GLib に isatty 関数は無い。
そういえば Gjs は stdout で行末から入力させる簡単な手段も無い。
よく考えたら標準入力も、Python に対し Gjs は print 以外は何もない。
これらを全部 GLib で行う必要がある。

#!/usr/bin/gjs

const GLib = imports.gi.GLib;

let channel = GLib.IOChannel.unix_new(0)
if (channel.get_flags() == GLib.IOFlags.IS_READABLE) {
    /* --  Pipe -- */
    [res, str_return] = channel.read_to_end();
    // byte to string
    let s = str_return.toString();
    // Remove '\n'
    s = s.slice(0, -1);
    print("[" + s + "]がパイプから渡されました");
} else {
    /* -- stdin -- */
    // Input
    channel.write_chars("何か入力してください >", -1);
    // Flush Console
    channel.shutdown(true);
    // readline
    let in_channel = GLib.IOChannel.unix_new(1);
    [res, str_return] = in_channel.read_line();
    // byte to string
    let s = str_return.toString();
    // Remove '\n'
    s = s.slice(0, -1);
    print("[" + s + "]が入力されました");
    in_channel.shutdown(true);
}

gjs_pipe

GIOChannel だけでなんとかなった。
Gio を使う別の手段も色々あるけどコレが一番単純な手段だと思う。
正しいかどうかは解らないけど動けばいいんだよ。

g_io_channel_shutdown

でコンソール表示をフラッシュする必要に気が付くまで少し、いやかなり時間が掛かった。
コレを呼ぶまで何一つリアクションしないツンデレなコンソールに萌え…(嘘です

使っているライブラリが同じなので PyGObject で書き換えても動く。
というか PyGObject で書いて変換したのは内緒だよ。
だって PyGObject の例外表示のほうが桁違いに親切なんだもん。

しかし戻り値が配列(タプル)になるという仕様は Python と同じなのね。
これはある程度 Python の経験がある人でないと理解に苦しむと思うのですが。
現状ではそんな人しか Gjs を使うという選択はしないので問題ないけど…

それにしても中括弧やセミコロンだけでなく何もかもが面倒臭い。
うーん、やっぱり Python 屋のままなほうが幸せかも。

Mac JavaScript getenv

Linux 屋が Mac を使うと環境変数が少ないなぁと感じる。

env

そのせいか osascript で環境変数を得る方法がなかなか見つからなかった。
いくら検索してもパスを通すことばかり、Windows と変わらないじゃん。
三時間くらい探し続けてやっと見つけた。

CotEditor :: ひまつぶし雑記帖

鳶嶋工房 / AppleScript / JavaScript for Automation (JXA)

osascript って stdlib のインクルードも可能なのか。
なら stdio もイケるかも、よしやってみよう。

#!/usr/bin/osascript

ObjC.import("stdlib");
ObjC.import("stdio");

var user = $.getenv("USER");
//console.log("ユーザー名は " + user + " です");
// 戻り値が出力されるので変数に入れている
// printf って転送バイト数が戻るとは知らなかった...
// 自動改行されるので \n を省いています
var dev_null = $.printf("ユーザー名は %s です", user);

/* //ShellScript から得るという手段もある
app = Application.currentApplication();
app.includeStandardAdditions = true;
var path = app.doShellScript("echo $LANG");
console.log("$LANG は " + path + " です");
*/

getenv

なんだよ、glibc の関数が普通に使えるじゃないか。
しかし今まで何気に使っていた printf にこんな罠が。

ただ printf と console.log は混在させないほうがいい。
上記下側のコメントアウトを外してみよう。

output_pointer

出力が上下で入れ替わってしまった、ように見えますが。
単に printf と console.log で出力ポインタ管理が違う。
つまり console.log の出力は行先頭のままになる。
結果、最初の出力が console.log の出力に押し出されてこうなる。

#!/usr/bin/osascript

ObjC.import("string");

var nae = "優木苗";
console.log(nae.length); //=>3
console.log($.strlen(nae)); //=>9

おぉ日本語のバイト数も C 標準関数を使えば得られる。
Gjs で GLib 関数がフルに使えるのとほぼ同じことができるのね。
Mac で JavaScript も想像していたより便利そうです。