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

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

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

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

メインループを用いた例2

下の例を実行すると

  • button text
  • change label

の2つのボタンとその上に「default label」の文字列を含んだウィンドウが表示され

上のボタンを押すと

** (gtkhello2:xxxxx): DEBUG: user_data: 12345

のように端末に表示され、下のボタンを押すと上部の文字列が「Hello, Work !!」に変わる。

[任意]ファイル名: gtkhello2.c

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

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

#define UNUSED_VARIABLE(x) ((void) (x))  /* コンパイラの警告対策 */

/*
 * 上のボタンが押されたときに呼ばれるハンドラ
 * 整数のユーザデータを受け取って処理するテスト
 * swapped指定しない場合はイベントの発生したオブジェクトが最初の引数になる
 */
void
on_button1_clicked (GtkButton *button, gpointer user_data)
{
  /*
   * ユーザデータはgpointer型なので、整数を型変換して渡したときは
   * GPOINTER_TO_INT(), GPOINTER_TO_UINT(), GPOINTER_TO_SIZE()により型変換して値を得る
   */
  g_debug ("user_data: %d", GPOINTER_TO_INT (user_data));
  UNUSED_VARIABLE (button);
}

/*
 * 下のボタンが押されたときに呼ばれるハンドラ
 * swapped指定で対象をラベルにしているため、引数はそのラベルのみ
 */
void
on_button2_clicked (GtkLabel *label)
{
  gtk_label_set_text (label, "Hello, Work !!");
}

int
main (int argc, char **argv)
{
  GtkWidget *mainwindow, *label, *button1, *button2, *vbox;
  gtk_init (&argc, &argv);
  mainwindow = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  label = gtk_label_new ("default label");
  /* ボタン */
  button1 = gtk_button_new_with_label ("button text");
  button2 = gtk_button_new_with_label ("change label");
  /* 垂直ボックス(縦方向分割) */
  vbox = gtk_vbox_new (FALSE, 0);
  /*
   * pack_start()は先頭から,pack_end()は末尾から追加
   * 3番目の引数は部品がスペースをフルに使うか(expand)
   * 4番目の引数はexpandする場合の余白の取り方に影響(fill)
   */
  gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
  gtk_box_pack_start (GTK_BOX (vbox), button1, TRUE, TRUE, 0);
  gtk_box_pack_start (GTK_BOX (vbox), button2, TRUE, TRUE, 0);
  /* ウィンドウ内のスペースは垂直ボックスに割り当てる */
  gtk_container_add (GTK_CONTAINER (mainwindow), vbox);
  /* ここからは前回と同様 */
  gtk_widget_show_all (mainwindow);
  gtk_window_set_title (GTK_WINDOW (mainwindow), "Hello");
  gtk_widget_set_size_request (mainwindow, 100, 30);
  gtk_window_resize (GTK_WINDOW (mainwindow), 200, 150);
  g_signal_connect (G_OBJECT (mainwindow), "delete-event", G_CALLBACK (gtk_main_quit), NULL);
  /*
   * ボタンを押したときのハンドラを指定
   * ハンドラへの追加データ(ユーザデータ)に整数値を渡したいときには
   * GINT_TO_POINTER()マクロを用いる
   * GUINT_TO_POINTER()やGSIZE_TO_POINTER()もある
   */
  g_signal_connect (G_OBJECT (button1), "clicked", G_CALLBACK (on_button1_clicked), GINT_TO_POINTER (12345));
  /*
   * g_signal_connect_object()はハンドラを呼ぶときに
   * 4番目の引数のGObjectオブジェクトの参照カウンタを
   * 一時的に増やし、そのオブジェクトが確実に生存するようにする
   *
   * 最後の引数にG_CONNECT_SWAPPEDを指定すると
   * g_signal_connect_swapped()のオブジェクト向けバージョンとなる
   * これは4番目の引数をハンドラの1番目の引数にする効果がある
   * (引数が1つのgtk_xxx_xxx()系関数を接続するのに便利)
   *
   * 最後の引数のフラグ(GConnectFlags)を全てなしにする場合は「0」を指定する
   * (有効なフラグはG_CONNECT_AFTER, G_CONNECT_SWAPPEDの2つ)
   * G_CONNECT_AFTERはハンドラを(そのシグナルの)既定のハンドラの後に呼ぶための指定で
   * g_signal_connect_after()のオブジェクト向けバージョンとなる
   *
   * 4番目の引数のGObjectオブジェクトを後で破棄する場合
   * 1番目の引数のオブジェクトを同時に破棄するか、この関数の戻り値(ID)を用いて
   * 「接続が残っている場合にのみ切断する」ようにしないと接続だけ残ってしまうバグがある?
   * http://library.gnome.org/devel/gobject/stable/gobject-Signals.html#g-signal-connect-object
   */
  g_signal_connect_object (G_OBJECT (button2), "clicked", G_CALLBACK (on_button2_clicked), G_OBJECT (label), G_CONNECT_SWAPPED);
  gtk_main ();
  return EXIT_SUCCESS;
}

g_signal_connect_swapped()を用いるかg_signal_connect_object()の最後の引数にG_CONNECT_SWAPPEDを含む場合、ハンドラの最初の引数が4番目の引数のものとなる。これを利用すると、GUI部品に対する処理を行う関数の呼び出しを自作ハンドラなしでその場で記述できる場合がある(引数がそのオブジェクト1つだけのもの・GtkWidgetクラスに多い)。

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

関連記事: