第13回はTcl/Tkを使ってみましょう。Tcl/Tkは、スクリプト言語のTclと、そのGUI拡張であるTkとによって構成されています。まずはTclのみを使ったHello Worldを作成し、その後Tcl/TkのGUIのHello Worldを作成していきます。
Tclのputsを使う方法
リスト1は、Tclのputsを使ったHello Worldです。このようにTclスクリプトでは、インタプリタとしてtclshを使います。putsでは、改行は自動的に付加されます。Tclの文字列は、{ }またはダブルクォートで囲みます。ダブルクォートの場合は\nなどのエスケープ文字が解釈されますが、{ }の場合はそのままの文字列と解釈されます。
リスト1 Tclのputsを使う(tcl_puts)
#!/usr/bin/tclsh ← Tclのインタプリタであるtclshを指定
puts {Hello World} ← putsで文字列を出力(改行は自動)
Tclのスクリプトは、ほかのスクリプトと同様、実行例1のように実行属性を付ければ実行できます。
実行例1 Tclスクリプトの実行
$ chmod +x tcl_puts ← Tclスクリプトに実行属性を付ける
$ ./tcl_puts ← Tclスクリプトを実行
Hello World ← 確かにHello Worldと表示される
$ ← シェルのプロンプトに戻る
putsで改行しない方法
putsは改行コードを付加しますが、-nonewlineオプションを付けると、改行の付加を抑制できます。そこで、リスト2のように、自分で改行コードを記述する方法も考えられます。ここでは文字列はダブルクォートで囲む必要があります。
リスト2 putsで改行しない方法(tcl_puts2)
#!/usr/bin/tclsh
puts -nonewline "Hello World\n" ← putsに任せず、自分で改行コードを記述する
Tclのformatを使う方法
Tclでは、printf形式の書式指定はformatを使って行います(リスト3)。formatは、sprintfに相当する動作を行い、結果の文字列を出力するため、これを[ ]で取り込んで、putsなどの引数として利用します。ここでは、format側で改行を付加し、putsの方には-nonewlineオプションを付けています。
リスト3 formatを使う方法(tcl_format)
#!/usr/bin/tclsh
puts -nonewline [format "%s\n" {Hello World}] ← formatで書式指定しputsで出力
OSのコマンドを実行する方法
TclからOSのコマンドを呼び出すには、リスト4のようにexecを使います。このexecは、シェルスクリプトのexecとは違って、OSのコマンドを呼び出したあとはTclに制御が戻ってきます。また、execの出力を標準出力に出力するには、明示的に「>@ stdout」というリダイレクトを行う必要があります。
リスト4 OSのコマンドを実行する方法(tcl_exec)
#!/usr/bin/tclsh
exec echo {Hello World} >@ stdout ← OS上のechoコマンドを呼び出す
C言語からTclライブラリを使う
Tclのライブラリは、C言語から呼び出して使うこともできます。標準出力に文字列を出力するには、リスト5のように、必要な前処理のあと、Tcl_Write()関数を使って出力します。
リスト5 C言語からTclライブラリを使う(tcl_c_write.c)
#include <tcl.h> ← Tclのヘッダが必要
int
main()
{
Tcl_Interp *interp; ← Tclのインタプリタへのポインタ
Tcl_Channel channel; ← Tclの入出力チャンネル
interp = Tcl_CreateInterp(); ← インタプリタを作成
channel = Tcl_GetStdChannel(TCL_STDOUT); ← 標準出力のチャンネルを取得
Tcl_Write(channel, "Hello World\n", 12); ← 標準出力に文字列を出力
return 0;
}
このプログラムはTclのライブラリとリンクするため、実行例1のように「-ltcl」を追加してコンパイルします。
実行例1 Tclライブラリを使ったプログラムのコンパイル
$ gcc -O2 -o tcl_c_write tcl_c_write.c -ltcl ← 「-ltcl」を追加してコンパイル
C言語からTclスクリプトを実行
Tclスクリプトの文字列を解釈して実行する、Tcl_Eval()という関数があります。このTcl_Eval()を呼び出すことによって、C言語からTclスクリプトを実行することも可能です(リスト6)。ここでは、TclスクリプトがC言語のソース中に文字列として埋め込まれています。なお、このプログラムも前項の実行例1と同様に、「-ltcl」を追加してコンパイルします。
リスト6 C言語からTclスクリプトを実行(tcl_c_eval.c)
#include <tcl.h> ← Tclのヘッダが必要
int
main()
{
Tcl_Interp *interp; ← Tclのインタプリタへのポインタ
interp = Tcl_CreateInterp(); ← インタプリタを作成
Tcl_Init(interp); ← Tclを初期化
Tcl_Eval( ← Tcl_Eval()でTclスクリプトを実行
interp, ← インタプリタを指定
"puts {Hello World}\n" ← Tclスクリプトの文字列を引数とする
);
return 0;
}
Tcl/TkのHello World
次は、TclにTkを加えた、Tcl/TkのGUIのプログラムです。 Tcl/Tkのスクリプトでは、インタプリタとして、tclshの代わりにwishを用います。Hello Worldのプログラムは、リスト7のようにlabelを使って記述します。実行すると図1のウィンドウが表示されます。
リスト7 Tcl/TkのHello World(tk_label)
#!/usr/bin/wish ← インタプリタはwish
label .hello -text {Hello World} -width 22 -height 5 ← ラベルを作成
pack .hello ← ラベルを配置
図1 Tcl/TkのHello World |
C言語からwishスクリプトを実行
Tcl/Tkのwish用スクリプトも、C言語からTcl_Eval()を呼び出すことによって実行することができます(リスト8)。ただし、Tcl単独のスクリプトを実行する場合とは違って、Tk_Init()とTk_MainLoop()の呼び出しが必要です。なお、Tcl_Eval()の引数のTcl/Tkのスクリプト文字列は、2行に分けて記述していますが、コンパイル時には1つの文字列として連結されることに注意してください。
リスト8 C言語からwishスクリプトを実行(tk_c_label.c)
#include <tcl.h> ← Tclのヘッダが必要
#include <tk.h> ← Tkのヘッダが必要
int
main()
{
Tcl_Interp *interp; ← Tclのインタプリタへのポインタ
interp = Tcl_CreateInterp(); ← インタプリタを作成
Tcl_Init(interp); ← Tclを初期化
Tk_Init(interp); ← Tkを初期化
Tcl_Eval( ← Tcl_Eval()でTcl/Tkのスクリプトを実行
interp, ← インタプリタを指定
"label .hello -text {Hello World} -width 22 -height 5\n"
"pack .hello\n" ←↑ Tcl/Tkのスクリプト文字列(2行で1つの引数)
);
Tk_MainLoop(); ← メインループを実行
return 0;
}
このプログラムは、TkとTclのライブラリとリンクするため、実行例2のように「-ltk -ltcl」を追加してコンパイルします。実行時の画面は、前掲の図1と同じです。
実行例2 Tcl/Tkライブラリを使ったプログラムのコンパイル
$ gcc -O2 -o tk_c_label tk_c_label.c -ltk -ltcl ← 「-ltk -ltcl」を追加