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

Gjs I/O Stream

以前下記で Seed, Python で Gio による読み書きをやったのを Gjs にしてみた。
Gio Streaming I/O @ Seed and PyGI | PaePoi

GCancellable 等を指定(null でいい)しないといけなかった。
他色々と Seed より Python に似ていて困惑。

/*
    Gjs Streaming I/O File Read and Write Sample
*/

const Gio = imports.gi.Gio;
const Format = imports.format;

String.prototype.format = Format.format;

let filename = "test_gjs.txt";
const lines = "Japan\n\n日本語";

// Write
let f = Gio.file_new_for_path(filename);
let fstream = f.replace(null, false, Gio.FileCreateFlags.NONE, null);
try {
    // Error, This is no Exception.
    // let dstream = new Gio.DataOutputStream(fstream);
    // g_output_stream_write: assertion 'G_IS_OUTPUT_STREAM (stream)' failed
    let dstream = new Gio.DataOutputStream({base_stream:fstream});
    dstream.put_string(lines, null);
} catch (e) {
    // Without meaning
    log("*** ERROR WRITE: " + e.message);
}
fstream.close(null);

// Read
f = Gio.file_new_for_path(filename);
fstream = f.read(null);
// Same as above
let dstream = new Gio.DataInputStream({base_stream:fstream});
while (1) {
    let [text, len] = dstream.read_line_utf8(null);
    if (text == null) break;
    print("%s(%d)".format(text, len));
    // or print(text + "(" + len + ")");
}
fstream.close(null);

/* output
Japan(5)
(0)
日本語(9)
*/

DataOutputStream 等の引数は json で指定しないと認識してくれない。
現時点では理由が解らない、GOutputStream のサブクラスと認識できないのか?
とにかく Error で困ったら json 指定にすればなんとかなるっぽい。

しかも GError 引数なのに例外にならず普通にエラー。
try ブロックを書いたけど無意味だった、これはちと困る。

おまけで let 変数のスコープは try ブロック内でも有効だと解った。
Python と同様にするには var を利用、注意しよう。

// var is OK
let f = Gio.file_new_for_path("a.txt");
try {
    // var fstream = f.replace(null, false, Gio.FileCreateFlags.NONE, null);
    let fstream = f.replace(null, false, Gio.FileCreateFlags.NONE, null);
} catch (e) {
    //
}
fstream.close(null); // ReferenceError
# Python is OK
f = Gio.file_new_for_path(filename)
try:
    fstream = f.replace("", False, Gio.FileCreateFlags.NONE, None)
except Exception as e:
    pass
fstream.close(None)

Seed にある c_new という意味不明な指定が無いのはちょっぴり嬉しいね。
それ以外は全部 Gjs のほうが面倒臭いけど。

戻り値が Python 同様に 2 つ以上である場合がある。
Python はタプルなんだけど、タプルが無い言語でもこの手があったか。
まあどちらも引数に渡された変数の値を変更できない言語だし。

どうも PyGObject の手段を参考に作っているような気がする。
いや逆かも、というか GLib 側の意向でこうなったのかな?

とにかく Python 屋なら Seed より Gjs のほうがなじみやすいという結論で。

Gjs is JavaScript

最近知ったけどガールフレンド(仮)というスマホゲームって普通にスマホのブラウザ上で動くブラウザ版ってのがあるんだね。
他にも同様なものがあるらしい、つまり HTML5 と JavaScript 製なのか。
スマホアプリは Objective-C, JAVA しかないと思っていたよ。

そういえば Google Chrome エクステンションも JavaScript だ。

ところで筆者のパソコンは Fedora で GNOME を愛用している。
何を今頃と思われそうだけど gnome-documents って Gjs 製なんだね。

gnome_documents

こんなところにも JavaScript が。
知らぬ間にあらゆるものが JavaScript になっていってる。

もう毛嫌いしている場合じゃない、普通に使えるくらいにはならないと。

ということで久々に Gjs を。
Seed プロジェクト側はヤル気が無いみたいだし。

http://ftp.gnome.org/pub/GNOME/sources/seed/
http://ftp.gnome.org/pub/GNOME/sources/gjs/

二年くらい前に少し触った頃と随分変わった気がする。
単に情報が色々出て来ただけだが、やはり日本語情報は皆無に近いけど。
とりあえず gnome-documents のコードを参考にしてウインドウを作ってみる。

const Gtk = imports.gi.Gtk;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;

const TestWindow = new Lang.Class({
    Name: 'TestWindow',
    Extends: Gtk.ApplicationWindow,

    _init: function(app) {
        this.parent({
                application: app,
                width_request: 300,
                height_request: 200,
                title: "TitleBar" });
        // or let button = Gtk.Button.new_with_label("Click!");
        let button = new Gtk.Button({ label: "Click!" });
        // or button.connect('clicked', Lang.bind(this, this._onButtonClicked));
        button.connect('clicked', function() {
            button.set_label("DDDone!");
        });
        this.add(button);
        this.show_all();
    },
    /*_onButtonClicked: function (button) {
        button.set_label("Done!");
    }*/
});

const Application = new Lang.Class({
    Name: 'Application',
    Extends: Gtk.Application,

    _init: function() {
        // TaskBar Title
        GLib.set_application_name("Test");
        // property
        this.parent({
                application_id: 'apps.test.Test',
                flags: Gio.ApplicationFlags.FLAGS_NONE });
    },
    // override activate signal
    vfunc_activate: function() {
        new TestWindow(this);
    }
});
let application = new Application();
application.run(ARGV);

GtkApplication は Gjs からも使えるようになったみたい。
というか gnome-documents が普通に使っていた。

class みたいに扱う時 Seed は GType、Gjs は Lang.Class。
Name 指定は必須、継承元は Extends にて指定、コンストラクタが _init ね。
なんとも奇妙だが JavaScript 仕様に class は無いので慣れるしかない。

プロパティセットは Python が __init__ で Gjs が parent にて可能。
普通にドットから代入でもいいけど。

# Python
self.props.title = "new Title"
// Gjs
this.title = "New Title";

シグナルハンドラは Vala のように無名関数が利用できる。
connect でもいいんだけど何故こんなに面倒な書き方なんだろう。
Python でも使えるといい気がするけどインデントが台無しになるお。

オーバーライドは Python が do_ で Gjs が vfunc_ のようだ。
無駄な connect を書かずにすむのは嬉しいね。

const Gtk = imports.gi.Gtk;
const Lang = imports.lang;

const TestWindow = new Lang.Class({
    Name: 'TestWindow',
    Extends: Gtk.Window,

    _init: function() {
        this.parent({ title: "TitleBar" });
        this.show_all();
    },
    vfunc_delete_event: function() {
        Gtk.main_quit();
    }
});
// Initialize the gtk
Gtk.init(null, 0);
new TestWindow();
Gtk.main();

GtkApplication 使用時は不要だけど Gtk.main では init() 必須。
というより Python だと init 呼び出し不要なのが変ということだが。

Gtk.Button.new_with_label が使えたのは何故か不思議な感じ。
そういえば JavaScript は何でもアリだったな。

こんなことをやっても Web やスマホアプリを作りたい時の参考にならない気も。
とはいえまったく文法を知らないよりはマシだからもう少し続けるかも。

g_list_foreach

STL(C++) の std::list と GLib の GList で foreach してみた。
C と C++ の微妙な違いがあってなかなか興味深い。

C++, STL

#include <iostream>
#include <list>
#include <algorithm>

void
show_data(std::string s) {
    std::cout << s << std::endl;
}

int
main (int argc, char **argv) {

    std::list<std::string> l;
    std::string s;

    for (int i=0; i<3; i++) {
        std::cout << "input " << i << " : ";
        std::cin >> s;
        l.push_back(s);
    }
    // for_each @ algorithm
    std::for_each(l.begin(), l.end(), show_data);
    return 0;
}

GLib

#include <glib.h>

void 
show_data(gpointer data ,gpointer user_data) {
    g_printf("%s\n",data);
}

void 
delete_data(gpointer data ,gpointer user_data) {
    g_free(data);
    data = NULL;
}

int
main (int argc, char **argv) {

    GList* list = NULL;
    gchar s[256];
    gint i;

    for (i=0; i<3; i++) {
        g_printf("input %d : ", i);
        scanf("%s", s);
        //list = g_list_append (list, s); // All Last Data
        list = g_list_append (list, g_strdup(s));
    }
    /* foreach */
    g_list_foreach(list, show_data, NULL);
    /* free */
    g_list_foreach(list, delete_data, NULL);
    g_list_free( list );
    return 0;
}

ビルド結果は同じものが作られるはず。

glist

std::for_each, g_list_foreach 共に関数ポインタを使っている。
GLib はソックリにしようと思ったのでしょうか?
STL は関数オブジェクトが使えるとかもろもろは置いておいて。

ところで for 文等を使わずにズラズラ並べるなら当然下記でもいい。
スタックのメモリ領域なので当然破棄処理も不要。
実際のアプリケーションでこんなコードになることはまずありえないですが。

list = g_list_append (list, "YAMAHA");
list = g_list_append (list, "HONDA");

for 文でコレをやると当然だけど全部同じ s ポインタを参照になる。
なのでデータを拾う毎にヒープに領域を作ってそのポインタを入れていく必要あり。
当然破棄も必須、C++ の std::string は全自動でやってくれているだけだし。

ただ知ってのとおり std::list は整数による添字アクセスができない。
というか何をするにもループ必須で正直使い勝手がよくない。
GList は一応こんなことができるようです。

gchar* c;
/* 3 番目を表示 */
c = (gchar*)g_list_nth_data(list, 2);
g_printf("%s\n", c);
/* 3 番目を削除 */
c = (gchar*)g_list_nth_data(list, 2);
list = g_list_remove(list, c);

STL を少し知っているなら直感的に書くことが可能、かつちょっぴり便利。
でも class は使えないからデストラクタで破棄とかはできないよってか。
むりやり std::list と同じにしようとしたのではないようで面白い。

Mini GtkButton

Gedit のステータスバーにあるみたいなちっちゃいボタンが使いたい。

gedit_button

タッチパネルを考えると時代に逆行ですけど。
主に確認用として表示させボタンは補助目的としてならアリかと。
実際 Gedit でもこのボタンを使っている人は少ないだろうし。

普通に GtkButton を作ると当然隙間が多く小さくはならない。

#!/usr/bin/env python3

from gi.repository import Gtk

class Win(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self)
        self.connect("delete-event", Gtk.main_quit)
        vbox = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 0)
        for s in "ABC":
            button = Gtk.Button.new_with_label(s*10)
            vbox.pack_start(button, False, False, 0)
        self.add(vbox)
        self.show_all()

Win()
Gtk.main()

どうやっているか調べると CSS を使っていた。
Style プロパティの border と padding の値を全部ゼロにすればいいらしい。
ようするに HTML の CSS と同様。

GtkButton はコレしか使わないなら直接 CSS 指定すればいい。
しかしこういう部品は GtkButton のサブクラスを作ったほうが使い易い。
ので gtype を指定した class を作ってこんな感じにすると便利。

#!/usr/bin/env python3

from gi.repository import Gtk

style = """
MiniButton {
    -GtkButton-default-border : 0;
    -GtkButton-default-outside-border : 0;
    -GtkButton-inner-border: 0;
    -GtkWidget-focus-line-width : 0;
    -GtkWidget-focus-padding : 0;
    padding: 0;
}"""

class MiniButton(Gtk.Button):
    __gtype_name__ = "MiniButton"
    def __init__(self, label):
        Gtk.Button.__init__(self, label=label)
        # css
        provider = Gtk.CssProvider()
        provider.load_from_data(style.encode("utf-8"))
        context = self.get_style_context()
        context.add_provider(provider, Gtk.STYLE_PROVIDER_PRIORITY_USER)
        #self.set_relief(Gtk.ReliefStyle.NONE)

class MiniButtonWin(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self)
        self.connect("delete-event", Gtk.main_quit)
        vbox = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 0)
        for s in "ABC":
            button = MiniButton(s*10)
            vbox.pack_start(button, False, False, 0)
        self.add(vbox)
        self.show_all()

MiniButtonWin()
Gtk.main()

minibutton

こんなにちっちゃくなりました。
css の指定で Widget のサイズも変わるんだね、これは面白い。

GtkHeaderBar and GStreamer

なんということだ。
GtkHeaderBar を set_titlebar したら GStreamer 再生画面が表示できない。
だから Totem 3.10 も使っていなかったのか、OpenGL の干渉?
やはり Widget を表示できるはずがない部分に描写するのは色々影響が出るよね。

動画プレイヤー(GStreamer フロントエンド)を作る – L’Isola di Niente
筆者が書いた上記に下記を追記すれば解ると思う。
今現在は GNOME 3.10 の人しか試せないけど。

hbar = Gtk.HeaderBar()
hbar.set_show_close_button(True)
self.set_titlebar(hbar)

逃げ道で GtkBox に pack_start なら問題なく GStreamer は使える。

hbar_gst

なんとかなる手段が解るまでしばらくはコレで進めていこう。
ClutterGst なら大丈夫なのかな?
しかし Clutter では Ubuntu の人は Clutter を入れてくれになってしまうのがチト。

Ubuntu 14.04 LTS: Pictures of First Set of Proposed New Icons | Unixmen

Ubuntu 14.04 LTS は GTK+ が 3.10 になり GtkHeaderBar はタイトルバーの下でお茶を濁すみたい。
予想通りだけど本当に魅力の無いディストリになってしまったなぁ、そのうち Mageia にも負けるぞ。
ま、Ubuntu がコレなら pack_start でいいかなと思ったり。

で、どうしても GtkHeaderBar にしたいのは理由があって。

Initiatives/GnomeGoals/HeaderBars – GNOME Wiki!

GNOME は全部 GtkHeaderBar に切り換えるつもりマンマンですし。

gEdit 3.12 Migrates to HIG 3! | woGue

GNOME 3.12 はついに Gedit さえもメニューバーがなくなる。
Totem もゴロッと変わるようだ、やばい浦島太郎になってしまう。

更に今日気が付いたけど Gtk 3.10 以降は GtkUIManager が非推奨になってしまった。
GtkActionGroup, GtkRadioAction, GtkToggleAction も非推奨に。
思いっきりコレらを利用している自アプリは変更せざるをえなくなってしまった。

こんなのは Linux では毎度のことでして。
それを楽しめないフヌケは使うなというスタンスに痺れるZE!

つか覚書ページで上記をお勧めしている部分を全書き換えしなきゃ!
チップスページを書くための実験用と化している我がアプリ達であった。