Subscribed unsubscribe Subscribe Subscribe

ふつうのGUIプログラミング

プログラミング

研究室でGUIプログラミングは面倒という話が出ていた.以前Gladeで作った簡単なサンプルがあったのでメモしておく.対象はGTK+かつGlade.べたにGUIを書いていくのは面倒なのでGladeみたいなのは使った方がよいと思う.

Gladeファイル

GladeとかGazpachoで適当なGladeファイルを作る.ここではGazpachoでつくる.機能らしい機能はないけど,それぞれのボタンは

  • Helloボタン: 標準出力にhelloを表示するふつうのボタン
  • Toggle meボタン: Helloのトグル版.単にtoggledと表示
  • Quitボタン: そのまんま.終了する.

というものを想定している.

作成画面はこんな感じ.


Gladeファイルにはロジックは書かない(書けない)が,コールバック関数の指定はできる.これを指定しておくと後々楽になる.ふつうのボタンはclickedのところ,トグルボタンにはtoggledのところに書いておく.


ここまで来たら,Gladeは終了.GladeTest.gladeなど適当な名前で保存しておく.

<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
<!DOCTYPE glade-interface SYSTEM "http://gazpacho.sicem.biz/gazpacho-0.1.dtd">
<glade-interface>
    <widget class="GtkWindow" id="window1">
        <property name="title" context="yes" translatable="yes">GladeTestApp</property>
        <property name="visible">True</property>
        <child>
            <widget class="GtkHBox" id="hbox1">
                <property name="visible">True</property>
                <child>
                    <widget class="GtkButton" id="button1">
                        <property name="border_width">10</property>
                        <property name="label" context="yes" translatable="yes">Hello</property>
                        <property name="use_underline">True</property>
                        <property name="visible">True</property>
                        <signal handler="hello_cb" name="clicked" after="False"/>
                    </widget>
                </child>
                <child>
                    <widget class="GtkToggleButton" id="togglebutton1">
                        <property name="border_width">10</property>
                        <property name="label" context="yes" translatable="yes">Toggle me!</property>
                        <property name="use_underline">True</property>
                        <property name="visible">True</property>
                        <signal handler="toggled_cb" name="toggled" after="False"/>
                    </widget>
                    <packing>
                        <property name="position">1</property>
                    </packing>
                </child>
                <child>
                    <widget class="GtkButton" id="button2">
                        <property name="border_width">10</property>
                        <property name="label" context="yes" translatable="yes">Quit</property>
                        <property name="use_underline">True</property>
                        <property name="visible">True</property>
                        <signal handler="quit_cb" name="clicked" after="False"/>
                    </widget>
                    <packing>
                        <property name="position">2</property>
                    </packing>
                </child>
            </widget>
        </child>
    </widget>
</glade-interface>

Cの場合 (GladeTest.c)

このファイルをCで使う場合は

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

void hello_cb(GtkButton *button, gpointer user_data);
void toggle_cb(GtkToggleButton *togglebutton, gpointer user_data);
void quit_cb(GtkButton *button, gpointer user_data);

int main(int argc, char *argv[])
{
	GladeXML *xml;
	gtk_init(&argc, &argv);
	xml = glade_xml_new("GladeTest.glade", NULL, NULL);
	glade_xml_signal_autoconnect(xml);
	gtk_main();
	return 0;
}

void hello_cb(GtkButton *button, gpointer user_data)
{
	g_print("hello");
}

void toggle_cb(GtkToggleButton *togglebutton, gpointer user_data)
{
	g_print("toggled");
}

void quit_cb(GtkButton *button, gpointer user_data)
{
	g_print("quit");
}

こんな感じでOK.Gladeファイル側にコールバック関数を指定したお陰でシグナルとにコールバック関数を接続するのが

	glade_xml_signal_autoconnect(xml);

の一行でできてしまう.

コンパイルはpkg-configを使うと楽ちん.

$ gcc `pkg-config --cflags --libs gtk+-2.0 libglade-2.0` GladeTest.c

後は実行するだけ.

C#の場合 (GladeTest.cs)

コールバック関数をCamelCaseで書かなかったのでこの例ではちょっと気持ち悪い.

using System;
using Gtk;
using Glade;

class GladeTestWindow {
	static void hello_cb(object o, EventArgs e)
	{
		Console.WriteLine("hello");
	}
	static void toggled_cb(object o, EventArgs e)
	{
		Console.WriteLine("toggled");
	}
	static void quit_cb(object o, EventArgs e)
	{
		Console.WriteLine("quit");
		Application.Quit();
	}
	public GladeTestWindow()
	{
		Glade.XML gui = new Glade.XML("GladeTest.glade", "window1", "");
		gui.Autoconnect(this);
	}
}

class GladeTest {
	static void Main(string[] args)
	{
		Application.Init();
		GladeTestWindow w = new GladeTestWindow();
		Application.Run();
	}
}

コンパイルと実行は

$ mcs GladeTest.cs -pkg:gtk-sharp-2.0 -pkg:glade-sharp-2.0
$ mono GladeTest.exe

Python (GladeTest.py)

#!/usr/bin/env python

import pygtk
pygtk.require('2.0')
import gtk
import gtk.glade
import inspect

class GladeTestApp(object):
	def __init__(self):
		xml = gtk.glade.XML('GladeTest.glade')
		xml.signal_autoconnect(dict(inspect.getmembers(self, inspect.ismethod)))
		[setattr(self, gtk.glade.get_widget_name(x), x) for x in xml.get_widget_prefix('')]
		self.window1.connect('destroy', lambda w: gtk.main_quit())
		self.window1.show_all()
	
	def hello_cb(self, arg):
		print 'Hello'
	
	def toggled_cb(self, arg):
		print 'Toggled'

	def quit_cb(self, arg):
		print 'Quit'
		gtk.main_quit()

	def main(self):
		gtk.main()


if __name__ == '__main__':
	GladeTestApp().main()

Ruby (GladeTest.rb)

#!/usr/bin/env ruby

require 'libglade2'

class GladeTestApp
   def initialize
      GladeXML.new("GladeTest.glade") {|handler|
         method(handler)
      }.get_widget_prefix('').each {|widget|
         instance_variable_set('@' + widget.name, widget)
      }
      @window1.signal_connect('destroy') { Gtk.main_quit }
   end

   def hello_cb(widget)
      puts "Hello"
   end

   def toggled_cb(widget)
      puts "Toggled"
   end

   def quit_cb(widget)
      puts "Quit"
      Gtk.main_quit()
   end
end

if __FILE__ == $0
   Gtk.init
   GladeTestApp.new
   Gtk.main
end

Haskell (GladeTest.hs)

これはなかったのでやらない.

まとめ

動作確認はしてないし以前動いたような気がする程度の信頼性なので,駄目かもしれない.それから,こういうトイプログラムは簡単でGUI簡単じゃんとか思うんだけど,TreeViewとか画面遷移するやつとかに遭遇すると面倒になってくる.

また,Firefoxで見ると画像がGladeファイルにもろに被ってしまっているが,気にしない.

追記

Cのコードはquitしてないしdestroyも接続してない.だいぶあとで直す.