X Window SystemのHello World

第10回は、GUIのプログラミングとして、X Window System(以下X)のHello Worldを取り上げます。使用する言語はC言語(ただしQtのみC++)です。Xのプログラムでは、ツールキットと呼ばれるウィンドウ部品を集めたライブラリを用いるのが普通です。ツールキットには複数の種類が存在するため、ツールキットごとに、異なるHello Worldのプログラムを作成できます。

Athenaウィジェット(Xaw)を使う方法

Athenaウィジェットは、Xに標準で付属しているライブラリで、同じくXに標準で付属する、ツールキットイントリンシクス(Xt)の上に構築されたウィジェットです。xtermやxclockなど、Xの代表的なアプリケーションは、Xawを用いたものが多いです。

Xawを使ったHello Worldは、リスト1のとおりです。Hello Worldの文字列はプログラムに直接埋め込んでいるため、$HOME/.Xdefaultsやxrdbなどによるリソース設定は不要です。

リスト1 Xawを使ったHello World(x11_xaw.c)

#include <X11/Intrinsic.h>           ← Xtを使うために必要
#include <X11/StringDefs.h>          ← XtNlabelなどの定義
#include <X11/Xaw/Label.h>           ← labelWidgetClassの定義

int
main(int argc, char **argv)
{
  XtAppContext app_context;          ← アプリケーションコンテキストの宣言
  Widget topLevel, hello;            ← ウィジェットの宣言

  topLevel = XtVaAppInitialize(      ← トップレベルウィジェットの作成
    &app_context,                    ← アプリケーションコンテキスト
    "Hello",                         ← アプリケーションクラス名
    NULL, 0,                         ← オプションはなし
    &argc, argv,                     ← コマンドライン引数を引き継がせる
    NULL,                            ← フォールバックリソースなし
    NULL                             ← リソース指定なし
  );
  hello = XtVaCreateManagedWidget(   ← ウィジェットを作成
    "label",                         ← ウィジェット名
    labelWidgetClass,                ← ラベルウィジェットを指定
    topLevel,                        ← 親ウィジェットはtopLevel
    XtNwidth, 160, XtNheight, 80,    ← 表示幅と高さを指定
    XtNlabel, "Hello World",         ← 表示する文字列を指定
    NULL                             ← 引数リストの終了
  );
  XtRealizeWidget(topLevel);         ← ウィジェットを実際に作成する
  XtAppMainLoop(app_context);        ← メインループを実行
  return 0;
}

Xawを使ったプログラムは、実行例1のようにコンパイルします。ここでは、Xのヘッダファイルとライブラリがインストールされているディレクトリを、それぞれ-Iと-Lオプションで指定していますが、最近のLinuxでは、Xのファイルが/usr以下に直接インストールされているため、-Iと-Lオプションは省略可能です。また、リンクするライブラリは、環境によっては、-lXawからの依存ライブラリとして自動的に-lXmu -lXt -lX11を指定した場合のライブラリともリンクされるため、-lXawだけの指定でもかまいません。そのほか、Solarisの場合は、ライブラリのPATHを実行ファイルに埋め込むため、-Rオプション、または、環境変数LD_RUN_PATHの設定が必要な場合があります。

実行例1 Xawを使ったプログラムのコンパイル

$ gcc -O2 -o x11_xaw x11_xaw.c \   ← gccでコンパイル(途中改行)
    -I/usr/X11R6/include \         ← ヘッダファイルのディレクトリ指定(本文注)
    -L/usr/X11R6/lib \             ← ライブラリのディレクトリ指定(本文注)
    -lXaw -lXmu -lXt -lX11         ← リンクするライブラリを指定(本文注)

コンパイルされた実行ファイルは、実行例2のようにして実行します。すると、図1のようなウィンドウが立ち上がります。なお、このプログラムを含め、本稿のプログラムでは終了処理を省略しているため、シェル上で[Ctrl]+[C]を入力してプログラムを終了してください。

実行例2 Xawを使ったプログラムの実行

$ ./x11_xaw                  ← コンパイルされた実行バイナリファイルを実行
(ウィンドウ図1が起動)
[Ctrl]+[C]入力で終了
$                            ← シェルのプロンプトに戻る

図1 XawのHello World

なお、OS環境の違いを吸収し、コンパイル時にgccに付加するオプションを自動化するために、xmkmfを使うことができます。xmkmfを使う場合は、リスト2のようなImakefileを、x11_xaw.cと同じカレントディレクトリに配置し、「xmkmf; make」と実行すればコンパイルが完了します。

リスト2 Xawを使ったプログラムのためのImakefile

LOCAL_LIBRARIES = XawClientLibs     ← Xawのライブラリを指定
SimpleProgramTarget(x11_xaw)        ← プログラム名を指定

GTK+を使う方法

GTK+を使ったHello Worldは、リスト3のとおりです。ウィンドウ破棄時に正常終了するために、destroyシグナルにはハンドラを設定する必要があります。

リスト3 GTK+を使ったHello World(x11_gtk.c)

#include <gtk/gtk.h>

int
main(int argc, char **argv)
{
  GtkWidget *window, *label;                    ← ウィジェット用ポインタ

  gtk_init(&argc, &argv);                       ← GTK+の初期化(引数を引継ぐ)

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL); ← トップレベルウィンドウ作成
  g_signal_connect(G_OBJECT(window), "destroy", ← destroyシグナルハンドラを
    G_CALLBACK(gtk_main_quit), NULL);           ← gtk_main_quitに設定

  label = gtk_label_new("Hello World");         ← 文字列の入ったラベルを作成
  gtk_widget_set_size_request(GTK_WIDGET(label), 160, 80);  ← ラベルサイズ設定
  gtk_container_add(GTK_CONTAINER(window), label);  ← ラベルをwindowに加える

  gtk_widget_show_all(window);                  ← すべてのウィジェットを可視化

  gtk_main();                                   ← メインループを実行
  return 0;
}

GTK+を使ったプログラムのコンパイル時に必要なgccのオプションは、pkg-configコマンドで出力できます。そこで、実行例3のように、pkg-configの出力を、シェルのコマンド置換(バッククォート)で取り込めばOKです。

実行例3 GTK+を使ったプログラムのコンパイル

$ gcc -O2 -o x11_gtk x11_gtk.c \            ← gccでコンパイル(途中改行)
    `pkg-config gtk+-2.0 --cflags --libs`   ← pkg-configでオプション指定

実行時の画面は図2のとおりです。

図2 GTK+のHello World

Qtを使う方法

Qtを使ったHello Worldは、リスト4のとおりです。ここでは、クラスの継承を行わず、また、クラスのインスタンス用メモリを、new演算子を使わずに、main()関数のスタック(auto変数領域)に直接持つ方法で記述しています。

リスト4 Qtを使ったHello World(x11_qt.cc)

#include <qapplication.h>               ← Qtを使うために必要
#include <qlabel.h>                     ← QLabelを使うために必要

int
main(int argc, char **argv)
{
  QApplication app(argc, argv);         ← Qtの初期化(コマンド引数を引継ぐ)

  QWidget window;                       ← windowというウィジェットを作成
  window.resize(160, 80);               ← windowのサイズ指定
  QLabel label("Hello World", &window); ← windowの中にラベルを作成
  label.setGeometry(50, 0, 160, 80);    ← ラベルの位置をほぼ中央に

  app.setMainWidget(&window);           ← windowを、メインウィジェットとする
  window.show();                        ← windowを可視状態にする

  return app.exec();                    ← メインループを実行
}

Qtを使ったプログラムはg++でコンパイルしますが、その際に必要なオプションは、GTK+の場合と同様にpkg-configコマンドで出力できます(実行例4)。

実行例4 Qtを使ったプログラムのコンパイル

$ g++ -O2 -o x11_qt x11_qt.cc \           ← g++でコンパイル(途中改行)
    `pkg-config qt-mt --cflags --libs`    ← pkg-configでオプション指定

コンパイルにはqmakeコマンドを使う方法もあります。その場合は、「qmake -project; qmake; make」とコマンド入力するだけでOKです。qmakeでは、作成される実行ファイルは、デフォルトではカレントディレクトリ名と同じファイル名になります。

実行時の画面は図3のとおりです。

図3 QtのHello World