カテゴリー別アーカイブ: Programming

GNOME 3.26 gjs class

おまたせ、gjs の ES6 class でアプリを作る方法がやっと解ったよ。
PyGObject みたく親切に例外を吐くなんてしてくれないので厳しい。

注意点、constructor はマジでコンストラクタです。
Python の __init__ 等と違う、動的言語でまさかこの仕様だとは。
細かい説明はしないけど、つまり super() 以外は何もできない。
継承で色々行う場合は下記のように別関数にする等の必要あり。

/*
const MyWindow = new Lang.Class({
    Name: 'MyWindow',
    Extends: Gtk.ApplicationWindow,

    _init: function(app) {
        this.parent({application: app});
        this.createContents();
        // etc...
*/
var MyWindow = GObject.registerClass({
    GTypeName: "MyWindow",
}, class MyWindow extends Gtk.ApplicationWindow {
    constructor(props={}) {
        super(props);
    }
    create() {
        this.createContents();
        // etc...

更に constructor の引数はベースへの property を JSON でに限定。
他を入れるとエラーになるのでとにかくこう書く以外に手段は無いみたい。

override で継承元の関数を呼ぶには super からドットで呼び出す。
Lang.Class ではないので this.parent() は使えない。

var MyApplication = GObject.registerClass({
    GTypeName: "MyApplication"
}, class MyApplication extends Gtk.Application {
    constructor(props={}) {
        super(props);
    }
    vfunc_startup() {
        //this.parent();
        super.vfunc_startup();

コレはすぐ気が付くか。

それ以外は今までのコードをそのまんま使えるようだ。
var にする必要があるのは外部から使う class だけでいいみたい。

本当は物凄い遠回りをしたんだけど以上であるようだ。

ということで、もう少し実験してから Y901x を更新する。
ぶっちゃけ動画プレイヤーは gnome-mpv でイイんだけど
Y901x は gjs のサンプルコードがメインの仕事だったことを忘れる所だった。

GNOME 3.26 gjs p2

散々探してやっと見つけた。

Inventing GObject ES6 classes | The Mad Scientist Review

なんかイッパイ手段があるみたい。
一番簡単そうな手段で試してみる。

/* btn.js */

const Gtk = imports.gi.Gtk;
const GObject = imports.gi.GObject;

var Btn = GObject.registerClass({
    GTypeName: "BtnClass"
}, class Btn extends Gtk.Button {
    constructor(props={}) {
        super(props);
    }
    // function
    set_button_label(txt) { this.label = txt; }
    // getter
    get prop() { return this.label }
});

#!/usr/bin/gjs

imports.gi.versions.Gtk = "3.0";
imports.searchPath.unshift('.');

const Gtk = imports.gi.Gtk;
const Btn = imports.btn;

class Test extends Gtk.Window {
    constructor() {
        super();
        this.connect("hide", ()=> {
            Gtk.main_quit();
        });
        this.ll = "カワイイ";
        var btn = new Btn.Btn({label: "睦ちゃん"});
        btn.connect("clicked", (widget)=> {
            // getter
            var s = widget.prop;
            // function
            widget.set_button_label(`${s} ${this.ll}`);
        });
        this.add(btn);
        this.show_all();
    }
}

Gtk.init(null);
let test = new Test();
Gtk.main();

で動かしてみる。

何も警告は出なくなった、思いっきり const を使っているのに。
これで安心してコード分割ができるぞい。
ただ、ちょっぴり面倒臭い。

関数定義や getter/setter は検索して一番解りやすかったページが以下。

JavaScriptにもクラスがやってきた!JavaScriptの新しいclass構文をマスターしよう | HTML5Experts.jp

コピペして console.log を print に書き換えてと。
うん gjs 1.50.2 で問題なく動くんだね。
だけど gir で継承すると…

#!/usr/bin/gjs

imports.gi.versions.Gtk = "3.0";

const Gtk = imports.gi.Gtk;
Gtk.init(null);

//* gir @ Error
class Btn extends Gtk.Button {
    constructor() { super(); }
    put() { print("func test"); }
}
//*/

/* no gir @ OK!
class Btn {
    constructor() {}
    put() { print("func test"); }
}
*/
var btn = new Btn();
btn.put();

何でや!

getter/setter も同様だった、理由が解らない。
上記のように GObject.registerClass をするしかないっぽい。
class が使えるようになって面倒になるとは思わなかったよ。

GNOME 3.26 gjs

GNOME 3.26 の目玉、gjs が ES6 フル対応になった。
これでで class が使えるぞ、Lang.Class の奇妙なコードとはおさらばじゃ。
んと、その前に。

#!/usr/bin/gjs

// 'gjs a.js 苗ちゃん'

const GLib = imports.gi.GLib;

for (let val of ARGV) {
    print(val);
    //let s = decodeURIComponent(escape(val));
    //print(s);
}

変わっていないや、まあいいか。
ClutterImage のセットが激遅なのもそのまんまだね。
ClutterImage PyGObject/Gjs | PaePoi

それはそれとして新機能だ。
GtkWindow を作ってみよう、やっぱり GUI だよね。

#!/usr/bin/gjs

imports.gi.versions.Gtk = "3.0";

const Gtk = imports.gi.Gtk;

class Test extends Gtk.Window {
    constructor() {
        super();
        this.connect("hide", ()=> {
            Gtk.main_quit();
        });
        this.show_all();
    }
}

Gtk.init(null);
let test = new Test();
Gtk.main();

これでウインドウを作るだけの最小限コードのようだ。

imports は PyGObject と同様にバージョン指定が無いと警告が出るようになった。
アロー関数を使えば Lang.bind を使わずにあの this の糞仕様を回避できる。
他の説明はいらないよね。

次はコード分割してみよう。

#!/usr/bin/gjs

imports.gi.versions.Gtk = "3.0";
imports.searchPath.unshift('.');

const Gtk = imports.gi.Gtk;
const Btn = imports.btn;

class Test extends Gtk.Window {
    constructor() {
        super();
        this.connect("hide", ()=> {
            Gtk.main_quit();
        });
        this.btn = new Btn.Btn("睦ちゃん");
        this.add(this.btn);
        this.show_all();
    }
}

Gtk.init(null);
let test = new Test();
Gtk.main();

var Gtk = imports.gi.Gtk;

class Btn extends Gtk.Button {
    constructor(lb) {
        super({
            label: lb
        });
    }
}

えーーーーー
let も const も定義していないお!
Java と違って同一ソースに複数の class が書けるのでこうすればいいけど。

#!/usr/bin/gjs

imports.gi.versions.Gtk = "3.0";

const Gtk = imports.gi.Gtk;

class Test extends Gtk.Window {
    constructor() {
        super();
        this.connect("hide", ()=> {
            Gtk.main_quit();
        });
        this.btn = new Btn("睦ちゃん");
        this.add(this.btn);
        this.show_all();
    }
}

class Btn extends Gtk.Button {
    constructor(lb) {
        super({
            label: lb
        });
    }
}

Gtk.init(null);
let test = new Test();
Gtk.main();

コードが長くなると…

モジュールとして継承 class は作ってはいけないってことかな?
まだ試したばかりだし他の方法はあるかもしれないけど。
なんか一気にテンションが下がった、PyGObject に戻るかなぁ。。。

GTK+ Cancel Long Keypress

GTK+ にてキーの長押しを判別する手段を発見。
GTK+ はキーの長押しを認識できないようで | PaePoi
こんなことを書いて 8 年もたってしまった、遅すぎるぞ俺!

何かキーを長押しした状態で event.time の値を見ると同一だった。
つまり長押しは最初に押した時間のまま延々とイベントが飛んでくるようだ。
とっととサンプルコードを書いたほうが解りやすいということで。

#!/usr/bin/env python3

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

class CancelLongKeypressWindow(Gtk.Window):
    """
        Wayland Only
    """
    def __init__(self):
        Gtk.Window.__init__(self)
        self.connect("hide", Gtk.main_quit)
        self.connect("key-press-event", self.on_key_press_event)
        self.long_keypress = 0
        self.show()

    def on_key_press_event(self, widget, event):
        #
        # Cancel Long Keypress
        #
        if self.long_keypress == event.time:
            return False
        self.long_keypress = event.time
        #
        print("Press!")
        return False

CancelLongKeypressWindow()
Gtk.main()

これでうっかり長押ししてしまっても早送りみたいな状態を避けられる。
逆に長押しされているのを判別するということも可能、こんなに簡単だった。
Comipoli で使うのと 8 年前の記事に合わせるため Python にしました。
これで我が Comipoli で早送りされてしまうのを防げるぞと。

そういえばあの頃は Ubuntu を使っていたなぁ。
今度から GNOME に戻るんだっけ、良さげなら又使うかな?
って GNOME に Dock を付けるという余計なことを又してる、ヤメた。
筆者は MacBook Air でも Dock はまったく使わないんですけど。
第493回 Ubuntu 17.10の変更点:Ubuntu Weekly Recipe|gihyo.jp … 技術評論社

追記
Wayland 環境だけのようです。
X.org でログインしたら同様にならなかった。

surrogate-pair

JavaScript における文字コードと「文字数」の数え方 | blog.jxck.io

そうだった、Python3 ばかり使っていたのでサロゲートペアのことを忘れていた。
GLib が扱う Unicode も Linux,macOS 版 Python3 の文字列も UCS-4 なんだよね。
Windows は捨てたので Windows 版 Python3 は知らん、ってどうでもいいか。

ES6 では for of を使って Python3 と同様な処理ができるようだ。
もちろん Node.js, JavaScriptCore も同様の結果でした。

UTF-16 はこの件があるし UTF-8 との変換も面倒だしetc…
何故いまだに Unicode の主流なんだろうね。
メモリ効率うんぬんならスクリプト言語や仮想マシンの時点で、って話だし。

おっと、一番下を見逃す所だった。

Family: Man, Woman, Girl, Boy Emoji

Emoji にはこんな文字をくっつけた一文字があったのか!
コピペしたら WordPress で投稿できないぞ、つかイッパイあるなぁ。

Emoji Version 2.0 List

Python3 でも駄目だ、バイナリを見れば当然だけど。
こんな文字まで考慮しなきゃいけない時代になってしまったのか。