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

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

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

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

GUI部品(ウィジェット)のデータ型と型変換について

GUI部品のオブジェクトとその管理

GUI部品は基本的にGtkWidget型構造体へのポインタ(「GtkWidget *」型)として宣言し、多くはgtk_[文字列]_new()のような名前の関数により新しくオブジェクトを作成する。これはGTK+の下位ライブラリの1つであるGObjectというライブラリが提供する(OSネイティブな実行形式で利用可能な)オブジェクトシステム*1における「オブジェクト」で、C言語ではGObjectライブラリの定める方法によって手動で管理する*2必要があるのだが、GTK+の簡単なプログラムではそういった作業を行うことはない。ただ、画像データのGdkPixbufなどを扱う場合に(GObjectのオブジェクトレベルでの)破棄の処理が必要なことはある(別記事で例を扱う)。GUI部品の破棄*3としてはgtk_widget_destroy()が用いられるが、g_object_ref()により参照カウンタを増やしていない限り*4はこの呼び出し時にGUI部品が破棄され、存在すればその子のGUI部品以下も同様に破棄される。
なお、GObjectライブラリはクラスの継承などもサポートしているが、C言語で記述するのは面倒(代わりにVala言語を用いると非常に楽)。

型とその変換

GTK+GUI部品のポインタ型には「GtkWidget *」型の他にGUI部品の種類(クラス)ごとの型も存在し、相互に型変換用マクロを用いて変換することができる。関数を呼び出す際にはこの型が合うようにする必要がある。マクロは単純な型変換ではなく内部的にGObjectライブラリの関数が呼ばれているのだが、そういったことを気にする必要はなく、種類ごとの名前を(「_」を区切りとして)大文字で書いたものに入れるようにすればよい。

/* ウィンドウの場合、宣言とオブジェクト作成は「GtkWidget *」型 */
GtkWidget *mainwindow = gtk_window_new (GTK_WINDOW_TOPLEVEL);
/* GtkWindowクラスの関数では引数付きマクロGTK_WINDOW()により「GtkWindow *」型へ変換する */
gtk_window_set_title (GTK_WINDOW (mainwindow), "Hello");

イベントの処理で特定のGObjectシグナルをハンドラ関数に関連付ける際にも型変換を用いる。

/* 「閉じる」ボタンが押されたらメインループを抜けるようにする例 */
g_signal_connect (G_OBJECT (mainwindow), "delete-event", G_CALLBACK (gtk_main_quit), NULL);

「GtkWidget *」型への型変換は、ハンドラ関数の引数としてGUI部品ごとの型で受け取る場合や

/*
 * ボタンがクリックされたとき("clicked"シグナル)のハンドラの引数については
 * http://library.gnome.org/devel/gtk/stable/GtkButton.html#GtkButton-clicked
 * を参照
 */
void
on_button_clicked (GtkButton *button, gpointer user_data)
{
  /* 注意:gtk_widget_xxx()という関数は実際には存在しない */
  gtk_widget_xxx (GTK_WIDGET (button));
}

GtkBuilder(ここでは詳しくは扱わない)を用いた場合などに用いる。

GtkBuilder *builder;
GtkWidget *widget;
(中略)
/* gtk_builder_get_object()は(汎用的なGObjectオブジェクトとして)「GObject *」型を返す */
widget = GTK_WIDGET (gtk_builder_get_object (builder, "widgetname"));

クラス/インターフェースの関数の呼び出し

PyGTK(Python)やGtk#(C#),Vala言語のGTK+では言語としてオブジェクト指向の機能を持っているため、あるクラスのオブジェクトについて利用可能なメンバ関数はそのクラスと親クラス,別に継承しているインターフェースのものとなる。例えば、PyGTKのgtk.FileChooserDialogクラスではgtk.Dialogクラスの系列のクラス(更にgtk.Window,gtk.Bin,...と続く)とgtk.FileChooserインターフェース(PyGTKではクラス)メンバ関数が利用可能となっている。
一方のC言語では、オブジェクトに対してメンバ関数を呼ぶという文法がなく、呼びたい関数のクラス/インターフェースに合わせた呼び出しが必要となる。例えばPyGTKでgtk.FileChooserDialogクラスの選択ダイアログを作成した場合、ファイルの場所を取り出すときに「filename = dialog.get_filename()」のようにすることができるが、C言語ではGtkFileChooserインターフェースの関数gtk_file_chooser_get_filename()を用いる。

/* C言語の場合はクラスやインターフェースの関数を用い、最初の引数に処理対象を入れる */
filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));

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

関連記事:

*1:オブジェクト指向の考え方を用いてデータを扱うための仕組み

*2:参照カウンタ(被参照数)の操作(g_object_ref():+1/g_object_ref_sink():float解除して+1(初期状態でオブジェクトが「所有」されない特別な場合のみに用いる)/g_object_unref():-1)などを行う

*3:ウィンドウや(一時的なものを含む)ダイアログなどに用いることが多い

*4:増やした場合もGUI部品としては破壊されて画面上からも消え、g_object_unref()を呼ぶまで内部的なオブジェクトのみが残る