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

Vala GtkApplication

Vala という言語は C# っぽい文法で C 言語ソースコードを作る言語。
だとしたら GtkApplication はどうなるのだろう。

GtkApplication

Python で G_APPLICATION_HANDLES_OPEN を指定して GtkApplication を作成してもエラーになり Gio.File[] 引数が空配列で渡される現象(バグ?)があり困っていたけど C ソースに展開する Vala でなら使えそうだ。
使いたかったけど今までこんな状況だったので。

G_APPLICATION_HANDLES_OPEN @ C, PyGI, Seed | PaePoi

直感で書いてみても上手くいかなかったのでサンプルコードを探す。

Gtk.Application ? gtk+-3.0
GLib.Application ? gio-2.0

こんなにアッサリ、公式ドキュメントがあるというのはやはり楽だ。
GLib.Application というのもあるけど多分 GApplication のことだろう。

しかしサンプルはコードは protected ではエラーになってしまう。
public に書き換えれば問題なく動いた、override って普通はそうするし。

g_signal_connect ではなく override させるのか。
しかしクラスの概念が無い C 言語で override っていったいどう変換しているのだろう。
-C オプションで展開してみると G_APPLICATION_CLASS に代入する C のサンプルコードと似たようなコードを吐いていた、かなり無駄な関数を辿っているような気がするけど。
結果は connect と同じはずなんだけど Vala では connect だとエラーになる。
交わした約束忘れな…いや何でもない。

//public override void open(File[] files, int n_file, string hint) {
public override void open(File[] files, string hint) {

上のように書きたい所だが配列数は length メソッドで調べろらしい。
C# っぽくしたかったのだろうか、よくワカラン。

とにかく GtkApplication は G_APPLICATION_HANDLES_OPEN 指定で問題なく使えるようだ。
アプリ開発者なら涙がちょちょぎれるくらい嬉しい機能なのに日本語で検索してもウチしか引っかからないって何だよ…
どんだけ嬉しいかサンプルコードを書いてみる。

using Gtk;

public class TestWin : Window {

    private Notebook note;
    private TextView[] view;

    public TestWin ( Gtk.Application app ) {
        this.set_application(app);
        this.set_title("TestWin");
        note = new Notebook();
        this.add ( note );
        this.resize ( 320, 240 );
        this.show_all();
    }
    public void CreateNew () {
        var tab_label = new Label("new.txt");
        var text_view = new TextView();
        view += text_view;
        note.append_page(text_view, tab_label);
        this.show_all();
    }
    public void CreateTab ( File[] files ) {
        foreach ( var file in files ) {
            var tab_label = new Label( file.get_basename () );
            var text_view = new TextView ();
            view += text_view;
            note.append_page(text_view, tab_label);
        }
        this.show_all();
    }
}

public class App : Gtk.Application {

    private TestWin win = null;

    public App () {
        Object (application_id:"apps.test.helloworld", flags:ApplicationFlags.HANDLES_OPEN );
    }
    public override void activate () {
        if ( win == null ) {
            win = new TestWin( this );
        }
        win.CreateNew ();
    }
    public override void open ( File[] files, string hint ) {
        if ( win == null ) {
            win = new TestWin( this );
        }
        win.CreateTab ( files );
    }
}

public class Main {
    public static int main ( string[] args ) {
        Gtk.init ( ref args );
        var app = new App ();
        app.run ( args );
        return 0;
    }
}

vala_gtkapplication.tar.gz

完璧な多重起動防止アプリがこんなにアッサリ作れる。
自力でやるなら DBus 通信処理が必要だがマジでこれだけだ。

libunique なんて便利そうなライブラリも一応あるけど。

LibUnique – GNOME Live!

とにかく GtkApplication を使えば DBus 処理も必要なく別個でライブラリを入れてもらう必要もなく、あの面倒極まりない多重起動防止処理が完結できる。
GtkApplication が管理してくれるので delete-event の処理も必要無い。

と解っていたけど PyGI からでは使えなかったんだよね。
んーやっぱり Vala をメイン言語にするかなと、C は面倒くさいし。

Vala GTK+ (GError)

前回の GTK+ コードでコンパイル時に WARNING が出ていた件が解決した。
valac はオプションに -C を入れるとコンパイル前に C 言語に変換されたソースコードを吐き出す。
コレを利用してどのように変換されたかを調べる。

private void on_clicked(Button button) {
    var f = File.new_for_path("gtk.txt");
    var fstream = f.replace(null, false, FileCreateFlags.NONE);
# valac -C --pkg gtk+-3.0 hoge.vala
static void win_on_clicked (Win* self, GtkButton* button) {
	GFile* _tmp0_ = NULL;
	GFile* f;
	GFileOutputStream* _tmp2_ = NULL;
	GFileOutputStream* fstream;

	GError * _inner_error_ = NULL;
	g_return_if_fail (self != NULL);
	g_return_if_fail (button != NULL);
	_tmp0_ = g_file_new_for_path ("gtk.txt");
	f = _tmp0_;
	_tmp1_ = f;
	_tmp2_ = g_file_replace (_tmp1_, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &_inner_error_);
	fstream = _tmp2_;
	if (_inner_error_ != NULL) {
		_g_object_unref0 (f);
		g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code);
		g_clear_error (&_inner_error_);
		return;
    }

どうやら GError にてチェックを行う関数はすべて確認コードを生成するようである。
ところが Vala で書く場合は GError を ref で入れることができない。
このような場合 try,catch 文にすれば GError が渡ってくるようだ。

private void on_clicked(Button button) {

    File f;
    FileOutputStream fstream;
    DataOutputStream dstream;
    
    f = File.new_for_path("gtk.txt");
    try {
        fstream = f.replace(null, false, FileCreateFlags.NONE);
    } catch (Error e) {
        stderr.printf ("Error: %s\n", e.message);
        return;
    }
    dstream = new DataOutputStream(fstream);
    try {
        dstream.put_string(entry.get_text());
    } catch (IOError e) {
         stderr.printf ("Error: %s\n", e.message);
         return;
    }
}

ついでに解ったけど FileOutputStream は関数を抜けると勝手に close される。

エラーなんて絶対に起こらないし厳密すぎだしこんなの面倒くさいよ!
という場合は以下みたいな超手抜きコードでイケる。

private void on_clicked() {
    try {
        var f = File.new_for_path("gtk.txt");
        var fstream = f.replace(null, false, FileCreateFlags.NONE);
        var dstream = new DataOutputStream(fstream);
        dstream.put_string(entry.get_text());
    } catch {
    }
}

前回のコードの on_clicked 関数をこう書き換えれば確認できる。
これで WARNING とはおさらば。

それとせっかく GTK+ を使うのに printf は無いだろう。
やはりココはエラーメッセージをダイアログで出したい。
C# からの乗り換えでは MessageBox.Show() を使いたい。
ということで簡易なものを作ってみた。

using Gtk;

public class MessageBox {
    public static ResponseType Show (string text) {
        var dlg = new MessageDialog(
                null,
                DialogFlags.MODAL,
                MessageType.WARNING,
                ButtonsType.OK,
                text );
        dlg.set_title("TitleBar");  
        var res = dlg.run();
        dlg.destroy();
        return (ResponseType)res;
    }
}

public static int main (string[] args) {
    Gtk.init(ref args);   
    MessageBox.Show("MessageBox.Show Test");
    return 0;
}

やはり PyGI で書くのとほとんど変わらない。
var と new と中括弧とセミコロン、他少々書くことが増えただけ。
ちなみに Python ではこんな感じ。

#!/usr/bin/env python
#-*- coding:utf-8 -*-

from gi.repository import Gtk

class MessageBox:
    @staticmethod
    def Show (text):
        dlg = Gtk.MessageDialog(
                None,
                Gtk.DialogFlags.MODAL,
                Gtk.MessageType.WARNING,
                Gtk.ButtonsType.OK,
                text );
        dlg.set_title("TitleBar")
        res = dlg.run()
        dlg.destroy()
        return res

if __name__ == '__main__':  
    MessageBox.Show("MessageBox.Show Test");

まあコンパイルするわけだから実行速度的には Python を圧倒するわけだ。
こんな PyGI で書いているのとほとんど同じ感覚で C 言語で作られたのと同様な実効速度を持つアプリケーションが生成できるなのならば、それはとっても嬉しいなって。

Vala and GtkBox

そういえば現行 Ubuntu にはデフォルトでは mono は入らない。
覚書ページを書き換えしないといけないですね。
ぶっちゃけ現在は C# なんてまったく使っていないので Python に導く感じで書いたつもりだが、現在なら Vala のほうがいいかなと。

Windows しか使えなかった小僧が Linux に来てプログラミングに興味をもつキッカケだけなら C# は最適だ、続ける奴は今すぐ死ねだが。
ということで Vala を少し。

環境は anjuta 入れれば勝手に揃う(手抜き
anjuta は使い方が解らないので無視(つかイラネ

Tutorial
Vala/Tutorial – GNOME Live!

C# との違い
Vala/ValaForCSharpProgrammers – GNOME Live!

C# が少し解るなら上記だけ見れば大体解る。
インタラクティブシェルは無いけど vala コマンドにソースファイルを渡せばコンパイルせずに実行できる。
valac ならコンパイル、あれ?以外と便利かも。
しかし stdout するにも GLib のお世話にならないといけない。

てか多様性ばかり強調して肝心なことが書いていないような。
最小限のコードは以下のようだ。

Vala

using GLib;

void main () {
    stdout.printf ("最小限\n");
}

C

#include <stdio.h>

main () {
    printf ("最小限\n");
}

C# と違い全部を class に入れる必要は無いし引数の args もオプション。
ただ C と違うのはエントリポイントの戻り値は必ず指定の必要あり。
WriteLine や Python の print ではないので \n を最後に書かないと改行しない。
まあ C と同じということですね。

そういえば vala の文字列ってどう考えても ascii ですよね?
mono(C#) は当然 UCS-2 で、ということは…

C#

using System;

class Foo {
    [STAThread]
    public static void Main(string[] args) {
        string homura = "ほむら";
        foreach (var s in homura) {
            Console.WriteLine(s);
        }
    }
}

Vala

using GLib;

int main (string[] args)
{
    string homura = "ほむら\n";
    stdout.printf(homura);
    //`string' does not have an `iterator' method
    //foreach (var s in homura) {}
    for (var i=0; i<3; i++) {
        stdout.printf ("%c\n", homura[i]);
    }
    return 0;
}

foreach できないし gchar 扱いなので添字だと 1byte 単位になる。
やっぱりこうなるよな、+= で合体できたりスライスも可能なのだけどバイト単位であるのを意識する必要があって C 言語で書くのと変わらない。

こんなことばかりやっても私らしくないからとっとと GTK+ を。
前回 Python3 で書いた奴と同じものを Vala で書いてみる。
ということでテキトに書いてコンパイルするとこんなエラーが。

マジで!

GtkVBox
GtkVBox has been deprecated. You can use GtkBox instead, which is a very quick and easy change.

本当だった、PyGI では何の警告も出ないのに。
代わりに GtkBox を使ってということか。
Gtk.Orientation.VERTICAL, Gtk.Orientation.HORIZONTAL
とスキマの数値を引数に作成すれば同じように使えるみたい。

gi ばかり使っていると気がつかない、C や Vala をたまにはやろう。

関数なんかは gi のバインディングと同じ書き方でいいみたい。
new で作成するので関数に new_ の部分がある所は全部消して。
シグナルのハンドラに引数を書くとエラーになるのは何故…
FileOutputStream 等が何故か GLib のメソッドになっている…

コンパイルで warning が出るけど問題なく動いたコード。

using Gtk;
using GLib;
 
public class Win : Window {
    private Entry entry;
    public Win () {
        this.set_title("たいとるばぁ");
        this.destroy.connect (Gtk.main_quit);
        entry = new Gtk.Entry();
        var button = new Gtk.Button.with_label("保存");
        button.clicked.connect(on_clicked);
        var vbox = new Gtk.Box(Orientation.VERTICAL, 0);
        vbox.pack_start(entry, false, false, 0);
        vbox.pack_start(button, false, false, 0);
        this.add(vbox);
        this.show_all();
    }
 
    private void on_clicked() {
        var f = File.new_for_path("gtk.txt");
        var fstream = f.replace(null, false, FileCreateFlags.NONE);
        var dstream = new DataOutputStream(fstream);
        dstream.put_string(entry.get_text());
        fstream.close();
    }
 
    public static int main (string[] args)
    {
        Gtk.init(ref args);
        new Win();
        Gtk.main();
        return 0;
    }
}
valac --pkg gtk+-3.0 t2.vala

まだよく解っていない、もうちょっとやってみる。
とにかく GTK3 で GtkVBox で書いていた部分は今後変更することにしよう。

Linux Python 3

そろそろ Python を 3 に移行しようと思う。
はて、日本語はどうなるのだろう?
Linux ではまったく使っていなかったから解らない。

Gio Tips – L’Isola di Niente

多分組み込みの open() 関数はツジツマを合わせていると思う。
GIO での読み書きはどうなるやら、実験。

# Write and Length Check
s = '日本語'
f = open('test.txt', 'w')
f.write('{0}\n{1}'.format(len(s), s))
f.close()

# g_file_load_contents
from gi.repository import Gio
gfile = Gio.File.new_for_path('test.txt')
res = gfile.load_contents(None)
print(res)

# Streaming I/O
fstream = gfile.read(None)
dstream = Gio.DataInputStream.new(fstream)
print(dstream.read_line_utf8(None))
print(dstream.read_line_utf8(None))
fstream.close(None)

うん、文字列はシッカリ gunichar(UCS-4) だね。
やはり組み込み関数は仕事キッチリか。

g_file_load_contents だと、、、バイナリ扱いになってしまった。
読み込んでも UTF-8 のままで保持するということみたい。
文字列の読み書きに利用するなということなのだろう。

Streaming I/O では文字列自体は UCS-4 化するみたいだけど文字列長は UTF-8 バイナリでカウントしている。
試しに dstream.read_Line(None) に書き換えたら予想通り文字列がバイナリに。
GFileOutputStream は UCS-4 の文字列を普通に UTF-8 として書き込みできた。

うーん、やはり文字列が UCS-4 だと扱い辛いぞ。

GTK+ の GtkEntry の文字列はどうなるんだろう?

#!/usr/bin/env python3
#-*- coding:utf-8 -*-
 
from gi.repository import Gtk
 
class Win(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self)
        self.set_title('たいとるばぁ')
        self.connect('delete-event', Gtk.main_quit)
        self.entry = Gtk.Entry()
        button = Gtk.Button('保存')
        button.connect('clicked', self.on_clicked)
        vbox = Gtk.VBox()
        vbox.pack_start(self.entry, False, False, 0)
        vbox.pack_start(button, False, False, 0)
        self.add(vbox)
        self.show_all()
 
    def on_clicked(self, widget, data=None):
        f = open('gtk.txt', 'w')
        f.write(self.entry.get_text())
        f.close()
 
if __name__ == '__main__':
    w = Win()
    Gtk.main()

タイトルバー等の文字列も GtkEntry とのやりとりも UCS-4 の内部文字列のままでイケてしまうようである。
GIO で文字列を扱う場合以外は Python2 時と同様に使えると思ってよさそう。
なんて思っていたら落とし穴がありそうだが…

もう少し検証したいけど Boxes 上の Ubuntu では辛い。
「クリップボードを共有」を ON にしているのに何故かホストと共有できなくて面倒くさいぞコノヤロウ。
Fedora に入れたいけどデフォルトで使えるコードというポリシーに反するし。
18 にはデフォルトで入るみたいだが β すらまだ出ていない。

ちなみに本サイトのメール送信フォームを利用して文字列のやりとりというバカなことをやっていたりする、サイトを作っておくと便利だよ。

Ubuntu 12.10 Programming

せっかく Ubuntu 12.10 を仮想で導入したのでプログラミング環境等をチェック。

やったぜ、mono という巨大なゴミがデフォルトで入らなくなった!
と思ったけど 12.04 から既に無かったのか、気がつきもしなかった。
もしかして valac が…あるわけないか。

なんと Python 3 がデフォルトで入るようだ。
LXDE に変更したから入ったのかもしれないけど。

Python 3 からは pygtk はやっぱり使えません。
その他 Python 2 で提供されていた静的バインディングも当然全滅。
gi による動的バインディングなら問題ない。
一応書くと Python 2 も PyGtk も当然あるよと。

gcc はあるけど g++ は無いのは以前と同じ。
Gjs も Seed も無い、Ubuntu は JavaScript に消極的か。

Fedora なんかは Python に依存する体制から脱却しようとしているみたいだけど結局戻りそうな予感。
今の Linux では GLib 等の動的バインドに対応できるなら言語なんて何でもいいんだけど Python は標準ライブラリが絶妙だからね。

GTK+ のバージョンは…あれ? Nautilus や Totem のバージョンが 3.4 だ。
Gedit と eog は 3.6 って何だこれ。
まあつまり GTK+ は 3.6 であるということか、何故アプリはバラバラ?
Ubuntu って最新環境を最速で提供が売りではなかったのかな。

おかげで Nautilus にメニューバーがあるけどw
Nautilus 3.6 はかなり大胆に変更されているし、アプリケーションメニューに対応できなかっただけかもしれない。
GNOME は今後このメニュー主体になるわけで、GNOME を捨てたくせに GNOME の成果物に依存している Ubuntu の今後はいかに。

Gnome Files (aka Nautilus) 3.6 (3.5.92) Preview | Worldwide Edition – YouTube

しかし LXDE の設定には「ルックアンドフィールの設定」なんてのがあるけど Ubuntu ではまったく適用されない。
GTK2 はクリアルックス固定だし GTK3 に至っては Windows95 みたいだ。

元の Ubuntu が GTK2 と GTK3 を同じ見た目にするために標準を無視して特殊なことをしているからだろうけどカッチョワルイ。
Lubuntu ならこの辺りをなんとかしているのだろうか。
仮想だからいいけどホスト OS でこの環境を使いたくないないな。

というか LXDE 自体が GTK2 であるわけで。
この環境で GTK3 アプリを作ろうと思う人はいないだろうな。

GTK3 に移行したい人は素直に GNOME 3 を使いましょうということでしょう。
良さげならホスト化しようと思ったけどやっぱり次も Fedora にしよう。