試験運用中なLinux備忘録・旧記事

はてなダイアリーで公開していた2007年5月-2015年3月の記事を保存しています。

C言語でGTK+ 2を扱う上での幾つかのメモ(第4回)

C言語でGTK+ 2を扱う上での幾つかのメモ(第3回)」の続き。

メインループを用いた簡単な例

下の例を実行すると、ラベル(文字列を表示するGUI部品)のみを含むウィンドウを表示し、これを閉じると終了する。
[任意]ファイル名: gtkhello.c

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

/*
 * gcc -O2 -Wall -Wextra $(pkg-config --cflags --libs gtk+-2.0) gtkhello.c -o gtkhello
 */

int
main (int argc, char **argv)
{
  /* GUI部品 */
  GtkWidget *mainwindow, *label;
  /* GTK+の初期化 */
  gtk_init (&argc, &argv);
  /*
   * 新しいウィンドウを生成
   * 通常のウィンドウはGTK_WINDOW_TOPLEVELを引数に指定
   * ポップアップするメニューやツールチップのようなものに限りGTK_WINDOW_POPUP
   * GTK_WINDOW_POPUPではウィンドウ装飾が付かないが
   * 通常のウィンドウでは *そのためにGTK_WINDOW_POPUPを指定してはいけない* 代わりに
   * 「GTK_WINDOW_TOPLEVEL指定かつgtk_window_set_decorated()でFALSE指定」をする
   *
   * なお、Vala言語のGtk.WindowクラスではGtk.WindowType.TOPLEVELの指定は初期値なので不要
   */
  mainwindow = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  /* 新しいラベルウィジェットを生成 */
  label = gtk_label_new ("Hello, Work !!");
  /*
   * ウィンドウにラベルを入れる
   * この入れ方ではウィンドウにラベルを1つだけしか入れられない(メニューバーも入らない)
   * 複雑にレイアウトしたい場合はGtkVBox(縦分割)やGtkHBox(横分割)などを用いる
   *
   * これはGtkContainerクラスの関数なので引数は「GtkContainer *」型となり
   * 宣言したGtkWidget型構造体へのポインタはGTK_CONTAINER()マクロで型変換する必要がある
   * 型変換マクロは「GTK_XXX」の形で色々とあり、部品ごとの型で宣言したり
   * ハンドラの引数として入る場合に「GtkWidget *」型で関数に渡すような場面では
   * GTK_WIDGET()マクロを用いて型変換する
   *
   * 操作を行う関数では1番目の引数に対象のGUI部品を指定する
   */
  gtk_container_add (GTK_CONTAINER (mainwindow), label);
  /*
   * 子のGUI部品を含め、指定したGUI部品以下を全て表示される状態にする
   * GtkWidgetクラスの関数なので型変換はしない
   * 単体版gtk_widget_show()や逆のgtk_widget_hide(),
   * 逆の全体(子を含む)版のgtk_widget_hide_all()もある
   */
  gtk_widget_show_all (mainwindow);
  /*
   * ウィンドウのタイトルを変更
   * GtkWindowクラスの関数なのでGTK_WINDOW()で型変換する
   */
  gtk_window_set_title (GTK_WINDOW (mainwindow), "Hello");
  /* GUI部品の最小サイズを指定 */
  gtk_widget_set_size_request (mainwindow, 100, 30);
  /* ウィンドウに対してサイズの変更をする(GtkWindowクラス) */
  gtk_window_resize (GTK_WINDOW (mainwindow), 200, 150);
  /*
   * 閉じるボタンが押されたときにメインループを抜けてgtk_main()の先に進むようにする
   * 1番目の引数が処理対象(GObjectオブジェクト)の場所
   * 2番目がGObjectシグナル名
   * 3番目がハンドラの場所
   * 4番目は追加のデータ(任意/なければNULL)
   * 追加データの種類や使い方によっては
   * g_signal_connect_object()やg_signal_connect_swapped()などの
   * 別の関数でシグナル接続することもある
   */
  g_signal_connect (G_OBJECT (mainwindow), "delete-event", G_CALLBACK (gtk_main_quit), NULL);
  /* gtk_main_quit()が呼ばれるまでメインのループが回り続ける */
  gtk_main ();
   /* 終了 */
  return EXIT_SUCCESS;
}


GLibの提供する基本データ型について

C言語のGLibライブラリではint型の代わりにgint型,char型の代わりにgchar型などといった独自のデータ型を用いる。これは移植性を考えてGLibが用意したものとなっており、真偽値のgbooleanという型*1なども新しく追加されている。ただし、標準Cライブラリの関数を用いる際に引数に型変換なしでGLibの提供する型の変数を用いたり、main()関数の戻り値の型をgint型などにしたりはしないようにする。GNU/Linuxに限定すれば、例えばgintとintは偶然(内部的に)同じ型となっており、実用上大きな問題にはならないかもしれないが、OSの仕様で決まっている型をわざわざ別の型にはしない。
(2010/7/1)補足として、gintやgcharなどの型はそれぞれリファレンスによると「Corresponds to the standard C [C言語の標準の型の名前] type. 」つまりそれぞれのC言語の標準の型に相当するものだとしており、内部的にも環境を問わず「typedef int gint;」のようになっているので、同じものとして扱っても動作上の問題はない(GLib上のgint型は各OSごとのint型と内部的に等しい、と言える)。OSに依存しない固定bit数を要求するときにはgint64などのbit数の保証されているものを用いる。gint64型などの内部的な定義は環境ごとに異なり/usr/lib(64)/glib-2.0/include/glibconfig.hに記述されている。
提供されている型に関する詳細は

も参照。また、型ごとの値の上限/下限も

のマクロで確認できる。
なお、関連する項目として、基本データ型以外に、よく用いられる連結リスト(リスト構造)のようなものはデータ型(単方向 双方向)が標準で利用可能で、かつこれを扱う上で便利な関数も用意されている。
(2010/8/23)「GLib 2が用意している基本的なデータ型について(ページ1/3)」も参照。


(「C言語でGTK+ 2を扱う上での幾つかのメモ(第5回)」に続く)

関連記事:

*1:TRUE(真)かFALSE(偽)のどちらかをとり、大文字で記述する