Perl/Tkを利用してのデータのビジュアル化

Perl向けの標準GUIツールキットでカスタム・グラフ表示ツールを作成する

大きなデータセットを理解するには、表現をビジュアル化することが最良な方法であることが多いです。しかし、表現のビジュアル化には、gnuplotのような標準ツールでは不十分であることが多いです。この記事では、カスタムなプロッティングやグラフ表示ツールを簡単に構築できるPerl向けの標準GUIツールキットであるPerl/Tkの使用方法について説明いたします。

Philipp Janert (janert@ieee.org), Software Project Consultant, IEEE.org

Philipp K. Janertは、ソフトウェア・プロジェクト・コンサルタントであり、サーバー・プログラマーであり、設計者でもあります。彼の興味は、ソフトウェアエンジニアのベストプラクティスの選別、確定、伝導にあります。彼はBeyondCode.orgWebサイトを保守しており、彼が投稿した記事は、IEEE Software,Linux JournalおよびO'Reilly Networkのサイトで参照することができます。彼はシアトルのワシントン大学で理論物理学の博士号を取得しました。彼の連絡先は、 janert-at-ieee.org です。



2003年 8月 27日

人間の目は、ビジュアルに表示されたデータであれば、複雑な振る舞いの認識でもパターンや傾向の把握であっても驚くほどうまく行うことができます。データセットが約12ポイントより大きい場合、グラフが有効となります。データセットが数千ポイントを超える場合、グラフは必需品になります。

単純なx-yプロットであれば、まず最初に考えられる選択肢はgnuplotです。より複雑な問題であれば、xmgraceや別のプロッティング・ツールを使用してもよいでしょう。しかし、ほとんどの単純な曲線プロッターでは、二次元データのプロッティングや、複雑なグラフ上でのきめ細かい制御には不十分です。ここでの複雑なグラフの例は、専門化したbar-and-whiskersプロット、精巧なエラー・バーを備えた時系列、カラーエンコード、密度プロット、およびその他多くの可能性を含んでいます。

ここがPerl/Tkが異彩を放っているところです。おそらく、みなさんは既にデータ操作と抽出にPerlを使用したことがあるでしょう。Perl/TkはTk GUIツール・セットにPerlバインディングを提供しており、おそらく最も使いやすいウィジェットのうちの1つなのです。

この記事では、Perl/Tkのグラフ表示の性能については細かく説明をしていますが、(チェックボックスやドロップダウン・メニューのような)実際のユーザー・インターフェース部分についてはあまりふれない事とします。

Perl/TkはGUIツールキットであるので追加機能を提供していますが、その追加機能には(優れたGDパッケージのような)Perlでの他のグラフ表示の拡張機能、すなわちアニメーションや対話型のデータ検索のような機能は存在していません。ですから、以下の例ではそのようなアプリケーションを紹介してみましょう。

Perl/Tkプログラムの構造

Perl/Tkでのプログラミングの基本概念を紹介するために、よく知られている「Hello, world」プログラムを見てみましょう。(Perl/Tkでは次のようになります)

リスト1. Perl/Tkスタイルの「Hello, world
#!/usr/bin/perl -w
use Tk;
$mw = MainWindow->new;
$mw->Label( -text => "Hello, World!" )->pack;
$mw->Button( -text => "Exit", -command => sub { exit } )->pack;
MainLoop;

このプログラムはテキストと「Exit」ボタンをもつウィンドウを新たにオープンします。ボタンのクリックによって、プログラムを終了します。(かつてプログラムがこれより簡単であったGUIツールキットはありましたか?)

use Tk宣言によりプログラムでTkモジュールを利用できるようになります。全体のTkツールセットは、Perlのオブジェクト指向の機能に非常に近いです。->を使用してオブジェクトに要求をしているメソッドが頻繁に確認出来るでしょう。

すべてのPerl/Tkアプリケーションは1つのMainWindowを持っていなければなりません。MainWindowは別々のウィンドウがオープンすることを意味する「トップレベル」のウィジェットです。ここではコンストラクターnewを使用して、そのインスタンスを作成しています。

次に、MainWindow内の子ウィジェットとして、ラベルとボタンオブジェクトを作成しています。それぞれのウィジェット(MainWindowのようなトップレベルのウィジェットを除いて)は、1つの親ウィジェットに対する子ウィジェットでなければなりません。

ここでは、ラベルとボタンそれぞれでウィジェットに対するパラメーターをいくつか指定して、perl/Tkのジオメトリー・マネージャーのうちの1つであるpackを呼んでいます。ジオメトリー・マネージャーの役割は、親ウィンドウ内の子ウィジェットを並べて配置することです。packだけでなく、gridplaceなどもあります。これらはウィジェットがレイアウトされる方法によりきめ細かなコントロールを提供しています。

(packのようなマネージャー機能へのパラメーターと同様に)ウィジェットへのパラメーターも、それらの名前を使用して指定されます。Perl/Tkでは通常ダッシュ(-)から始めます。=>演算子が左にあるbareword(ベアワード)を引用文字列として扱うので、パラメーター名をクォートで囲む必要がない(したがって、一般に省略される)ことに注意してください。-commandパラメーターはコールバック、つまり、このウィジェットがユーザー・イベント(例えばこの場合はボタンがユーザーにクリックされる)を受け取った際に呼ばれる関数への参照を定義するために使用されます。ここではコールバックは重要でないので、全体のインライン関数を匿名サブルーチンとして定義しました。以下で、スタンドアロンのサブルーチンがコールバックとして登録される場合を見てみましょう。

ウィジェットの設定はこれで完璧です。プログラムはユーザー・イベントを受け取ることができます。最終行はMainLoopを呼んでいます。MainLoopはユーザー・イベントを受け取り、イベントを生成することができるウィジェットごとに登録されている適切なコールバックにユーザー・イベントをディスパッチします。

これだけのことです。初心者がよく行う間違いは、packの呼出しやMainLoopの呼出しを忘れることだということを留意しておいてください。プログラムが実行中の時、アプリケーション・ウィンドウが表示できないなら、おそらく先ほどの呼び出しのどちらかもしくは両方を省略していることでしょう。また、新しいウィンドウの実際のサイズや配置は、プログラムではなく(Gnome、KDE、IceWMのような)ウィンドウ・マネージャによってコントロールされていることも覚えておいてください。


Canvasにおいて

グラフィカルな出力を生成したい場合は、TkがCanvasウィジェットを提供してくれます。Canvasウィジェットがインスタンス化されると、いくつかの標準グラフィカル・オブジェクト(行、円、長方形など)を作成することができます。次のプログラムを使って説明いたします。

リスト2. Canvasウィジェットのインスタンス化およびオブジェクトの配置
#!/usr/bin/perl -w
use Tk;
my ( $size, $step ) = ( 200, 10 );
# Create MainWindow and configure:
my $mw = MainWindow->new;
$mw->configure( -width=>$size, -height=>$size );
$mw->resizable( 0, 0 ); # not resizable in any direction
# Create and configure the canvas:
my $canvas = $mw->Canvas( -cursor=>"crosshair", -background=>"white",
              -width=>$size, -height=>$size )->pack;
# Place objects on canvas:
$canvas->createRectangle( $step, $step, $size-$step, $size-$step, -fill=>"red" );
for( my $i=$step; $i<$size-$step; $i+=$step ) {
  my $val = 255*$i/$size;
  my $color = sprintf( "#%02x%02x%02x", $val, $val, $val );
  $canvas->createRectangle( $i, $i, $i+$step, $i+$step, -fill=>$color );
}
MainLoop;

ここでは、上のようにMainWindowを作成して、幅と高さを$sizeピクセルに設定しました。トップレベルのウィンドウ・サイズを修正するためには、resizable($in_x_direction、$in_y_direction)メソッドが使用されます。このメソッドは、ウィジェットがx方向あるいはy方向にサイズ変更することができるかどうかを決定する2つのブール引数をとります。今回の例では、サイズ変更は完全に禁止しています。

次のステップではCanvasウィジェットを作成し、全てのMainWindowを塗りつぶしています。(背景色を「白」にセットすることで)キャンバスを取り除いて、マウスがキャンバス上にある時は、マウス・カーソルを十字線に変更しました。(X11には標準マウス・カーソルの形が78あります。それらの名前は標準的なインストールをされた場合、/usr/X11/include/X11/ ディレクトリーにあるヘッダーファイルcursorfont.hで参照することができます)

packの呼出しに注目してください。packはMainWindowにおいてインスタンス化されたCanvasオブジェクトを目に見えるようにしています。

「# Place objects on canvas」部分で、キャンバスに項目を設ける準備をしています。ここでは小さな灰色の矩形の列が対角線上に走っている、大きな赤い矩形を作成しています。(両方の軸が出会う)座標の起点は左上角にあるので、右に向かうX軸と下に向かうY軸を使って、Canvasオブジェクトが標準の「グラフィックス座標」を使用することに注目してください。

図1.キャンバスに配置された矩形のオブジェクト
図1.キャンバスに配置された矩形のオブジェクト

このコードでは、Perl/Tkにおいて色を指定する2つの方法を説明します。まず1つ目は、(通常/usr/X11/lib/X11/にある)rgb.txtファイルに「red」あるいは「PapayaWhip」のように、あらかじめ定義されている色を使用する方法です。2つ目は、"#"から始まる文字列に、2桁の16進数でそれぞれRGB(赤/緑/青)の値を指定する方法です。1桁の16進数は左側に0が埋め込まなければならないことに注意してください。(この例のように)3つの値がすべて等しいと、灰色に見えます。

(他の形と同様に)矩形にも、塗りつぶす色とアウトライン色の両方があります。ここではアウトライン色を指定していないため、色は自動的に黒になります。この境界をなくすためには、-outlineプロパティを塗りつぶす色と同じ色に設定してください。境界を広げるには、-widthプロパティを使用してピクセルで幅を指定して ください。

最後に、矩形、行、円のようなグラフィカルな要素はウィジェットではないことに注意してください!createRectangleのような機能はオブジェクトを返すのではなく、それぞれのグラフィック要素が識別可能となるID(実際は数値)を返します。グラフィックス要素を移動するか、修正するか、削除するためには、Canvasオブジェクトを含むそれぞれのメンバー関数にパラメーターとしてこのIDが渡されます。例えば、赤い正方形を削除するには、$canvas->delete( $id )を使用します。この$idcreateRectangleの最初の呼出しの戻り値です。


ユーザー対話

ある意味では、(Tkのような)GUIツールキットの優れた点はユーザー対話を可能にしていることです。言いかえれば、ツールキットは、マウスクリックやキープレスのようなあらゆる「イベント」へのアプリケーションの応答を簡単にしなければなりません。

Perl/Tkのほとんどのウィジェットは-command属性を必要とします。-commandは既に言及したように、ウィジェットがユーザーによって起動されると呼びだされるサブルーチン(「コールバック」)を登録することができます。(コールバックが匿名のサブルーチンであった)リスト1で、既にこの例を見てきました。:-command => sub { exit }もし代わりに、(sub method_name{ ... }を使用して定義された)任意のサブルーチンを登録したければ、次のようにこのサブルーチンへの参照を登録しなければなりません。:-command=>\&method_name

Canvasウィジェットは-command属性を必要としないという点で他と異なります。Canvasをユーザー対話に応答させるためには、Tk::bind関数を使用してコールバックを明示的にイベントに結び付ける必要があります。(Canvasウィジェットは継承されたbind関数を隠す独自のbind関数を定義しているので、ネーム・スペースTk::を明示的に記す必要があることに注意してください。)

bind関数は2つの引数をとります:1つは応答用のイベント・シーケンスであり、もう1つはコールバックとそのパラメーターです。イベント・シーケンスは、<Motion>あるいは<Shift-Button-3>のような鍵括弧で囲まれた文字列です。(Tkが応答するイベントは似ていますが、X11ウィンドウ・システムに定義される一連のイベントが完全に等しいわけではありません。イベントと修飾子の完全なリストのためにTk::bindリファレンスを参照してください。)

bind関数の第2の引数は起動されるコールバックです。ここでは、既に匿名のサブルーチンや指定されたサブルーチンへの参照を使用する方法を見てきました。コールバックがパラメーターを必要とする場合、次のように、まずサブルーチン参照をもった匿名のリストとしてコールバックを指定し、それからリスト・エントリーとしてパラメーターを指定します。:bind( "<Motion>", [ \&method_name, parameter1, parameter2 ] )。匿名のリスト参照の構成の記述は、(括弧ではなく)大括弧であること注意してください。

bindを使用して割り当てられたコールバックへの最初の引数は、常にイベントを生成したウィジェットへの参照です。そのあとは、ユーザー定義のパラメーターが続くだけです。

最後の特異性としては、Ev()機能がコールバックを起動したイベントに関する詳細を検索し使用することを可能にしています。例えば、イベントが生じた場所の($canvas の起点に関しての)座標は、Ev('x')およびEv('y')によって利用可能です。

従って、2番めの例プログラムのMainLoopへの呼出しの前に次のような行を追加します

$canvas->Tk::bind( "<Button-1>", [ sub { print "$_[1] $_[2]\n"; }, Ev('x'), Ev('y') ] );

キャンバス・ウィンドウ上の左のマウスボタンをクリックする場合はいつも、プログラムは、コンソールへキャンバス座標を出力します。(パラメータ・リストで、最初のエントリ、$_[0]を無視することに注意してください。これは、以前言及した起動ウィジェット、この場合は$canvasへの参照です。)


グラフィックの例

グラフ表示およびデータ・ビジュアル化の重要な例として、2つの変数、 xおよび yが次の方程式に直面すると想像してください

f(x, y) = cos(2a) cos(4b) + cos(5a) cos(3b) + cos(7a) cos(1b)

(pi = 3.1415... およびsqrt()は平方根を表示しています。)

a = pi (x - sqrt(3.0)y )b = pi ( 3x + sqrt(3.0)y )

方程式の研究からでは、この機能の振る舞いの概念を単純に理解するのは非常に難しいです。更に、多くの一次元のプロット(言いかえれば、ある値に決まっているyとxだけの関数として方程式を図示すること)でさえ、基礎をなす構造を明らかにしていないでしょうから、振る舞いは非常に複雑です。しかしながら、この関数の単純な二次元の密度プロッからは、オリジナルの方程式に含まれるシンプルで美しい対称性との感覚を得ることができます。

図2. 二次元の密度プロット
図2. 二次元の密度プロット

この記事の参考文献からこのプロットを行なうプログラムをダウンロードすることができます。このプログラムは非常に長いのでここで一つ一つの説明することはできませんが、先の議論をふまえれば簡単に理解できるはずです。実際のプロッティングとユーザー対話を扱うプログラムの部分が非常に小さいことに気付いてください。プログラムの大半は、様々なGUIウィジェットの作成や設定、そして関数の座標からスクリーン座標までの変換に費やされています。これは、典型的プロッティング・プログラムです(もしプロットされる関数をユーザーが入力出来るようにするならば、プログラムに入力妥当性検査が必要となるためにコードは長くなるでしょう)


作業の保存

スクリーン上のデータを見ることと、データをディスクに保存すること(そして印刷すること)は、全く別の問題です。Perl/Tkの機能と柔軟性を考えると、ビットマップ・ファイルへグラフィックスを保存する標準モジュールがないことにはかなり驚かれるかもしれません。

$canvas->postscript( -file=>"file_name.ps" )を呼ぶことで、PostScriptファイルへCanvasのコンテンツを保存することができます。これは、Canvasに実際に表示されたコンテンツだけをキャプチャーします。したがって、実際Canvasオブジェクトはスクリーンに描写されなければなりません。そうでなければ、出力ファイルは空になります。update()関数は、全てのイベントが処理されるまで待つためと描写を強制的に行うために(任意のウィジェットで)使用することができます。

別の方法は、バイト-トリプルとして任意のファイル・ハンドルに各スクリーン・ピクセルに対するRGBの値を明示的に記述することです。いくつかのグラフィックス・プログラムは、このように生成されたrgbファイルを扱うことができます。おそらく、グラフィックス・プログラムの中で最も強力なものはImageMagickパッケージの変換ユーティリティでしょう。これはrgbファイルを一般に使用されたグラフィックス・ファイル形式のうちの任意の1つに変形することができます。

最後の方法として、表示されたウィンドウからグラフィックス・ファイルを得る最も簡単な方法は、スクリーンショットをとることです。ImageMagickパッケージのインポート・ツールはかなり柔軟で、多くのファイル形式を生成することができます。

参考文献

役に立つ機能やその容易さにも関わらず、残念なことにPerl/Tkはいくぶん沈滞した技術となっています。そして、広範囲な最新情報を見つけるのが非常に難しい技術でもあります。以下に著者の推薦リストをあげておきます。

  • 図2の密度プロットを作成するためには、hexa-plot.plをダウンロードしてください。
  • 公式のPerl/Tkホームページで多くの情報を見つけることができます。FAQは少し古いかもしれませんがまだ十分役に立つものです。
  • Stephen O. Lidie と Nancy WalshによるMastering Perl/Tk (O'Reilly & Associates、2002年)は、読みやすいイントロダクションを提供しています。Nancy Walshによる第2弾のLearning Perl/Tk (O'Reilly & Associates、1999年)はかなり拡張されています。しかし、それは特に組織化されているわけではないので、標準ウィジェットのすべてをカバーしているわけでは限りません。
  • おそらく、Perl/Tkの最も最新で広範囲のドキュメンテーションは、マニュアル・ページです。概観および単純で複雑なウィジェットの広範囲なリストに関しては、man Tk (orperldoc Tk)で調べてください。
  • 著者がこの記事で触れていない1つの問題は色選択です。理由は、Perl/TkがRGBでの色指定(およびシンボル名)だけをサポートしているためです。ほとんどの人々にとって、RGBの値は特に理解しやすいものではありません。また、HSV(色調/彩度/値=明るさ)カラー・ーモデルに基づいた色選択のほうが、はるかに簡単です。しかし、異なる色空間の変換は複雑な意味合いを持ちます。Color FAQColour Space Conversions FAQなどを確認してみてください。単純であるものの役に立つ概観については、Introduction to Colorを考慮してください。
  • コマンドラインからImageMagickを使用する方法については、「Graphics from the command line」(developerWorks、2003年7月)を参照してください。
  • ImageMagickのホームページは、ImageMagickをダウンロードしたり、様々なプログラミング・インタフェースについてのより多くの情報を得ることができます。
  • TIFFイメージを操作する必要がある場合は、developerWorksの2部シリーズでlibtiffと呼ばれているTIFFのANSI C実装を使用する方法を知ってください。Part 1はTIFFの落とし穴について説明し、白黒画像でlibtiffライブラリーを使用する方法を紹介しています。Part 2はグレースケール画像やカラー画像でlibtiffを扱う方法を紹介しています。
  • 文字およびグラフィカルなPerlユーザ・インターフェースはCPANでリストされます。
  • CPANでGD.pm (GDグラフィックス・ライブラリーへのPerlインタフェース)に関してより多くの情報を見つけてください。
  • SFGraphは、大きな高解像度の画像を圧縮して見るためのIBM alphaWorksフレームワークです。SFGraphのエンコーダーは、PGM、PPM、PNM、JPEG、GIF、RGBの色で大きな画像およびグレースケール・フォーマットを圧縮するためにウェーブレット技術を使用しています。
  • developerWorksに掲載されている他のLinux参考文献もご覧ください。
  • developerWorksの他のオープン・ソース関連記事もご覧ください。

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Linux, Open source
ArticleID=228125
ArticleTitle=Perl/Tkを利用してのデータのビジュアル化
publish-date=08272003