Vala」タグアーカイブ

Gtk.Window.get_toplevels

Vala については Valadoc.org しか知らなかったけど。
GTK4 になって使わない間に Documentation サイトができていたのね。

Vala Documentation

で、よく考えたら筆者は自作した Vala Tips ページがある。
これも GTK3 のまま放置している、そろそろ書き換えしないと。

Vala (C Generator) – Paepoi

Vala 自体も C# の進化に合わせて細かく変わっていると思うけど。
筆者は 3.5 以降の C# を知らない、7 以降の Windows 買っていないし。
まあ後方互換のはずだし細かい違いは勘でなんとかなると読んでみる。
そんなことより下記のサンプルコードが気になってしまった。

Minimal App – Vala Documentation

GtkApplication や GMainLoop を使わずシグナル処理できるの?
ということで試しに PyGObject で書き換えしてみることにした。
書くまでもなく Gjs のほうが文法が近いけど独自機能が多いし。

#!/usr/bin/env python3

import gi
gi.require_version('Gtk', '4.0')
from gi.repository import Gtk, GLib

window = Gtk.Window (title = 'Minimal GTK4 App')

button = Gtk.Button(label='Click me!')
button.connect('clicked', lambda b: b.set_label('Thank you'))

window.props.child = button
window.present()

while (Gtk.Window.get_toplevels().get_n_items() > 0):
    GLib.MainContext.default().iteration (True)

vala

普通に動くんですけど、こんな手段があったのか。
閉じると終了する仕組みがある無限ループならなんでもいいのか。
まあどう考えても GApplication のほうが便利なんですけどね。
WPF なんかもそうでしょ、いや 3.5 以降はマジで知らないけど。

default の前にある @ は何だ?と思ったけど。
キーワードの default と誤認識を防止しビルド時は読み飛ばす仕組みらしい。
知らなかったわい、てか C# にこんな機能あったっけか?

Nemiver Vala

何を今頃気になった。
前記事のように C/C++ という特定言語での GUI デバッガは Nemiver で賄える。
っって Vala みたいな別言語へのジェネレーターだとどうなるんじゃ?
gdb 直使いな人も気になる所かも。

経験値が多いなら出力された C ソースへリンクと予測できる。
でもやってみなきゃ断言できないな、試すとしよう。

Gtk.Application ? gtk+-3.0

valadoc.org がしばらく見ない内に色々進化していた。
ソコらはもっと詳しい人にまかせてと。
このページにある vala ソースを gedit にこんな外部ツールで Nemiver に渡してみる。

vala_gedit

nemiver_vala

全然面白くない結果になってしまった。
Vala から使っても生成された C ソースへのリンクになるってことね。
コレだと Vala で書いているソースコードの直接的デバッグにならないんですが。

Vala という超マイナーな言語で書ける人という時点で(以下略
現状ならソレでいいけどさ。
未来なんて誰も断言できないから。
スマートフォンがココまで普及すると予測できた人なんているのか?

Here Document

ヒアドキュメントが便利なのでもう少し調べる。
Y901x のインストールスクリプトで使っているから経験はあるのだが。

ヒアドキュメント – Wikipedia

シェルスクリプトはバッククォーテーションでコマンドも使えるのか。

#!/bin/sh

func() {
    echo バッククオートで関数も使える
}
shstr=シェルスクリプト
hdstr=ヒアドキュメント

cat << __EOS__
${shstr}で$hdstrは今更だけど
最初の改行は無視される、最後は echo や cat が改行する
エスケープ文字 \\ は有効
記号は\$とバッククオートを除いて <'"{[+=~ と普通に使える
`func`
__EOS__

自作関数でもいいようだ。

# output
シェルスクリプトでヒアドキュメントは今更だけど
最初の改行は無視される、最後は echo や cat が改行する
エスケープ文字 \ は有効
記号は$とバッククオートを除いて <'"{[+=~ と普通に使える
バッククオートで関数も使える

これはもしかして PHP でも関数が使えるかも。

<?php

function func() { return "関数"; }
$plstr = "Perl";
$shstr = "シェルスクリプト";

echo <<< __EOS__
PHP は${shstr}や $plstr 同様に利用できる
最初と最後の改行は無視されるので最後は一行開けるとよし
エスケープ文字 \\ は有効
記号は\$を除いて <'"{[+=~` と普通に使える
`func` って PHP にこんな機能はもともと無いよ!

__EOS__;
/* output
PHP はシェルスクリプトや Perl 同様に利用できる
最初と最後の改行は無視されるので最後は一行開けるとよし
エスケープ文字 \ は有効
記号は$を除いて <'"{[+=~` と普通に使える
`func` って PHP にこんな機能はもともと無いよ!
*/
?>

んなわけないか。
ところで PHP のヒアドキュメントは最後を改行しなくて最初戸惑った。
よく考えたら echo, cat, print() が改行していただけだった。
PHP の echo は改行しないもんね。

Perl や Lua って Fedora に最初から入っているけど使ったことが無いな。
余程のことがないかぎり今後も使うことは無いと思うけど。
GNOME は以前 JavaScript を押していたけど今はどうなんだろう?

// Gjs
// imports /usr/share/gjs-1.0/format.js

const Format = imports.format;

String.prototype.format = Format.format;

let jstr = "\
JavaScript はエスケープで強引な改行しか手段が無い\n\
更に%s機能は無い\n\
しかし %s ならこんなことができる\n\
%%s, %%d, %%x, %%f のみ";

print(jstr.format("文字列フォーマット", "Gjs"));
/*
JavaScript はエスケープで強引な改行しか手段が無い
更に文字列フォーマット機能は無い
しかし Gjs ならこんなことができる
%s, %d, %x, %f のみ
*/

これだものな。
面倒臭くなってブン投げたのは筆者だけではないと思う。

#include <stdio.h>

#define CSTR "\
%s は\n\
説明不要だよね\n"

int
main (int argc, char ** argv) {
	printf (CSTR, "C 言語");
	return 0;
}
/*
C 言語 は
説明不要だよね
*/

C のほうが簡単なんて洒落にもならん。
プラス記号で成形するのって最初は分かり易いのでいいと思うが。

#!/usr/bin/env python3

DOCSTR = """{0} はお馴染 {1}
最初と最後の改行も有効だけど print が最後を改行する
{2}
{1} は \\ エスケープが有効"""

def func():
    return "関数は format で実行すれば文字列さ"

print(DOCSTR.format("Python", "docstring", func()))

''' output
Python はお馴染 docstring
最初と最後の改行も有効だけど print が最後を改行する
関数は format で実行すれば文字列さ
docstring は \ エスケープが有効
'''

あぁ楽チン、やっぱりコレだよコレ!

しかしやっぱりヒアドキュメントに変数を直書きできたほうが便利。
それには $ 記号を変数に利用する言語しか無理なんだろうな。
と思っていました。

// Vala

const string DOCSTR = """Python と同じ
%s も使える、でも ''' は使えない
何故か \\ エスケープ\nは使えない
""";

string func() { return "無理"; }

int main (string[] args) {
    string one = "Vala";
    string tow = "ヒアドキュメント";
    stdout.printf(@"$one は実は@\"\"を使って$towもどきが利用できる
ですが$${one}みたいなブレース表記はダメみたい
最初と最後の改行も有効、エスケープ文字 \\ は有効
記号は$$と\"を除いて <'{[+=~` と普通に使える
`func` は当然不可能\n\n");
    //
    stdout.printf(DOCSTR, "docstring");
    return 0;
}
/* output
Vala は実は@""を使ってヒアドキュメントもどきが利用できる
ですが${one}みたいなブレース表記はダメみたい
最初と最後の改行も有効、エスケープ文字 \ は有効
記号は$と"を除いて <'{[+=~` と普通に使える
`func` は当然不可能

Python と同じ
docstring も使える、でも ''' は使えない
何故か \\ エスケープ\nは使えない
*/

Vala はやってくれました。
おかげで解った、滅茶苦茶使い辛いということを。
これなら docstring 方式のほうがいいや。

最後に、Gedit の色分けってスゴすぎる!

gedit

まさかこの Vala の $ 変数を見分けて色分けするとは思わなかった。
PHP 部分と HTML 部分をしっかり見分けるとかは知っていたが。
どの言語でもヒアドキュメント内でマズい記号を打つと即座に色が変わる。
これがデフォルトエディタって GNOME 恐るべしだよ。

Vala etc…

昔書いた Vala コードをもう少しマシなサンプルコードに。
引数で指定したテキストファイルをシングルインスタンスで読み込むように。
多重起動防止処理に startup シグナルを利用してみたけど

startup_error

チェーンアップに失敗って何よ、Google 翻訳役に立たネェ。
一応多重起動防止処理は問題なく行なわれるけど気持ち悪い。
とにかく何かがマズいのだろうと色々検索して以下を見つける。

ApplicationWindow

protected override void startup () {
    base.startup ();
    //etc...
}

startup ハンドラで base.startup を呼ぶ必要があるようです。
何故だかよく解らないけどそういうもんだと納得しておこう。
とにかくそんなこんなでこんなコードを書いてみた。

using Gtk;

/**
 * Single Instance Window
 * valac --pkg gtk+-3.0 hoge.vala
 */
public class TextReader : Window {

    private Notebook _note;

    public TextReader () {
        // Property Set
        Object ( title: "Hoge", default_width: 500, default_height: 400 );
        _note = new Notebook ();
        this.add ( _note );
        this.show_all ();
    }
    public void create_tab ( File? file = null ) {
        if ( file == null ) {
            new_contents ( "New Document", "" );
        } else {
            try {
                var fstream = file.read();
                var dstream = new DataInputStream(fstream);
                var sb = new StringBuilder();
                string line;
                while ( (line = dstream.read_line_utf8(null)) != null ) {
                    sb.append( line );
                    sb.append_c( '\n' );
                }
                new_contents ( file.get_basename(), sb.str );
            } catch ( IOError e ) {
                 messagebox ( "IOError", e.message );
                 return;
            } catch ( Error e ) {
                messagebox ( "Error", e.message );
                return;
            } 
        }
    }
    private void new_contents ( string title, string text ) {
        var view = new TextView ();
        view.show ();
        var sw = new ScrolledWindow ( null, null );
        sw.add ( view );
        sw.show ();
        var label = new Label ( title );
        label.show ();
        if ( text != "" ) {
            var buf = view.get_buffer ();
            buf.set_text ( text );
        }
        var n = _note.append_page(sw, label);
        _note.set_current_page (n);
    }
    private void messagebox ( string title, string s ) {
        var dlg = new MessageDialog( this, DialogFlags.MODAL,
                    MessageType.WARNING, ButtonsType.OK, s );
        dlg.set_title ( title ) ;
        dlg.run ();
        dlg.destroy ();
    }
}

public class App : Gtk.Application {

    private TextReader _win;

    public App () {
        Object (application_id:"apps.test.textreader", flags:ApplicationFlags.HANDLES_OPEN );
    }
    protected override void startup () {
        base.startup ();
        _win = new TextReader ();
        add_window( _win );
    }
    protected override void activate () {
        _win.create_tab ();
        _win.present();
    }
    protected override void open ( File[] files, string hint ) {
        foreach ( var file in files ) {
            _win.create_tab ( file );
        }
        _win.present();
    }
    public static int main ( string[] args ) {
        Gtk.init ( ref args );
        var app = new App ();
        return app.run ( args );
    }
}

なかなか面白いことをみつけた。

public class ClassName : Window {
    public ClassName () {
        Object ( title: "Hoge", default_width: 500, default_height: 400 );

Python

class ClassName (Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self, title="Hoge", default_width=500, default_height=400):

Vala もこんな感じでプロパティセットが可能なんだね。
base ではなく Object なのか、何か凄く変です。
Python のほうが変という人のほうが多いという事実は置いておいて。

それより関数のパラメータをオブジェクトか NULL かで振り分けたい場合。
C ならポインタなのでそのまんま NULL かどうかを見分けできる、Python も同様。
型指定が厳密な C# の場合はオーバーロードを利用すると思います。

Vala は C に変換されるからそのままと思いきや C# と同じく型指定に厳密。
以下のようにすると見事にコンパイルエラーになってしまう。

public void create_tab ( File file ) {
    if ( file == null ) {
//etc...

// Compile Error
_win.create_tab (null);

最初は何故か全然解らず困ったけど多分 C# に合わせたということだろう。
しかし Vala はオーバーロードができないぞ、何か別の手段があるはずだ。
valadoc.org の cancellable 引数等の説明と同じように書いてみたらイケた。

public void create_tab ( File? file = null ) {
    if ( file == null ) {
//etc...

// OK
_win.create_tab ();

コレも凄く変です。
Python のほうが(以下略

後 GtkScrolledWindow は引数が必要だとか PyGI とは微妙に違うのね。
なんだかんだで勉強になった、実践的なコードを書くと色々気が付くものだ。
とにかく筆者は var と new と中括弧とセミコロンが面倒臭いです。

g_type_init

Vala の覚書ページを書き換えようとテスト。

init_error

なんだそりゃwww
警告が出るだけでビルドは問題なく行われるようだけど。

調べてみると GLib 2.36 からマジで g_type_init を廃止したようだ。
Fedora 19 の GLib はたしかに 2.36 だった。
Python ばかり使っているとこういうの気が付かないよね。

GIO入門 – ふとしの日記

#include <gio/gio.h>

/* gcc no_init.c `pkg-config --cflags --libs gio-2.0` */

gint
main (gint argc, gchar* argv[])
{
	GFile *gf;
	/* GLib >= 2.36 
	g_type_init ();*/
	gf = g_file_new_for_path ("sample.txt");
	return 0;
}

g_type_init は本当に不要になっているよふとしさん。
しかし Vala の場合はジェネレータが勝手に書き足すのだが…

[vala] codegen: Do not call g_type_init when targeting GLib >= 2.36

対策版は出ているのか、って Oct 2012 だから一年前かよ。
Fedora 19 パッケージの 0.20.1 は April 2013 のはずなのだが。

Lubuntu 13.10 の GLib は 2.38。
valac のパッケージを Synaptic で調べると同じ 0.20.1 か。
インストールしてみたけど、やはり症状は同じであった。

本家からソースを落として自力 make するか修正を待つか、うーん。
Vala のバージョンと GLib, GTK+ のバージョンは関係ってあるのか?
そのあたりがよく解らないから自力ビルドは避けたいのだが。

ビルドは可能なんだからこのままでもいいか、一番安全だし。

しかし `g_type_init vala` で検索しても日本語がまったく出てこない。
つまり日本人で Vala を使っている人はいないってことなのかも…