GtkBuilderとPython(pygtk) JavaScript(Seed) C#(Vala) C そして Mono gtkmm gjs

libgladeからGtkBuilderへの移行の時期が近づいてますが、対応状況がバラバラなので、ある程度の割り切りが必要になりそうです。
当然C言語では問題ないですが、C++(gtkmm)は対応が中途半端みたいです。pygtkは問題なく動作中。C#JavaScriptはValaとSeedに軍配があがりました。MonoはGladeのみの対応ですし、gjsはドキュメントが見つからない。。。

ということで、いつものプログラムを書いたんだけど、今回からボタンではなくメニューにしてみました。

GtkBuilder用のuiファイルは以下の通り'hello.ui'。glade-3はLANG=Cで起動しないと、uiに日本語が入ってしまうので、今後の課題です。

<?xml version="1.0"?>
<interface>
  <requires lib="gtk+" version="2.16"/>
  <!-- interface-naming-policy project-wide -->
  <object class="GtkWindow" id="window1">
    <child>
      <object class="GtkVBox" id="vbox1">
        <property name="visible">True</property>
        <property name="orientation">vertical</property>
        <child>
          <object class="GtkMenuBar" id="menubar1">
            <property name="visible">True</property>
            <child>
              <object class="GtkMenuItem" id="menuitem1">
                <property name="visible">True</property>
                <property name="label" translatable="yes">&#x30A2;&#x30D7;&#x30EA;&#x30B1;&#x30FC;&#x30B7;&#x30E7;&#x30F3;(_A)</property>
                <property name="use_underline">True</property>
                <child type="submenu">
                  <object class="GtkMenu" id="menu1">
                    <property name="visible">True</property>
                    <child>
                      <object class="GtkImageMenuItem" id="imagemenuitem1">
                        <property name="label">gtk-quit</property>
                        <property name="visible">True</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                        <signal name="activate" handler="on_imagemenuitem1_activate"/>
                      </object>
                    </child>
                  </object>
                </child>
              </object>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkLabel" id="label1">
            <property name="visible">True</property>
            <property name="xalign">0.4699999988079071</property>
            <property name="label" translatable="yes">Hello</property>
            <attributes>
              <attribute name="size" value="100000"/>
            </attributes>
          </object>
          <packing>
            <property name="position">1</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
</interface>

それではCから

#include <gtk/gtk.h>
#include <stdlib.h>

G_MODULE_EXPORT void
on_imagemenuitem1_activate(GtkAction *action)
{
	gtk_main_quit();
}

int
main (int argc, char* argv[])
{
	GtkWidget *window;
	GtkBuilder *builder;
	GError *err = NULL;

	gtk_init(&argc, &argv);
	builder = gtk_builder_new();
	gtk_builder_add_from_file(builder, "hello.ui", &err);
	if (err)
	{
		g_error ("ERROR: %s\n", err->message);
		return EXIT_FAILURE;
	}
	gtk_builder_connect_signals(builder, NULL);
	window = GTK_WIDGET(gtk_builder_get_object(builder, "window1"));
	gtk_widget_show_all (window);
	gtk_main();
}

EXIT_FAILUREのためだけにstdlib.hをincludeしてます。シグナル名と関数名を同じにすると、gtk_builder_connect_signals()で自動的に割り当てられるのが便利です。

次は、vala

using Gtk;

public void on_imagemenuitem1_activate(Menu source) {
	Gtk.main_quit ();
}

public class Hello
{
	static void main(string [] args) {
		new Hello(args);
	}

	public Hello(string [] args) {
		Gtk.init (ref args);
		try {
			var builder = new Builder();
			builder.add_from_file("hello.ui");
			builder.connect_signals(null);
			var window = builder.get_object ("window1") as Window;
			window.show_all();
			Gtk.main();
		}
		catch (Error e) {
			stderr.printf ("Could not load UI: %s\n", e.message);
		}
	}
}

こちらは、メソッドをpublicにする必要はありますが、builder.connect_signals()できるのはCと同じです。

pygtkも同様に

#!/usr/bin/env python

import gtk

class hello:
	def __init__(self):
		self.builder = gtk.Builder()
		self.builder.add_from_file('hello.ui')
		self.builder.connect_signals(self)
		self.builder.get_object('window1').show_all()
	def on_imagemenuitem1_activate(self, data):
		gtk.main_quit()

if __name__ == "__main__":
	app = hello()
	gtk.main()

self.builder.connect_signals(self)のように非常にすっきりとした書き方になります。

残念ながらJavaScriptのSeedはオブジェクトとして渡す必要があります。しかし、JSON形式で書くので問題ないかな?

#! /usr/bin/env seed

const Gtk = imports.gi.Gtk;
const GtkBuilder = imports.gtkbuilder;

Gtk.init(Seed.argv);

builder = new Gtk.Builder();
builder.add_from_file("hello.ui");
builder.connect_signals({on_imagemenuitem1_activate:function(){Seed.quit();}});

win = builder.get_object("window1");
win.show_all();
Gtk.main();

Seedが素晴らしいのは、builderをSeed独自に対応しているところ、こうなるとGjsは使い難いですね。

上記のソースの問題は、window の destory シグナルを処理していないところです。右上のバッテンをクリックすると window は閉じるけどプロセスは死なないのです。。。

hideするか、main_quit()につなげるか、どちらかが必要です。一般的なアプリを作るなら、別途関数を作るべきでしょうね。