[GTK+] 描画する色を指定する

線を描画するの続き。


前回のプログラムでは、線の色として、そのウィジェットの前景色として指定されていたものをそのまま使っていた。しかし、描画する色を明示的に指定したい場面も多々ある。そこで今回は描画色を指定する方法について。

点を打つときに使う関数 gdk_draw_point() の仕様は以下のとおりだった。

void                gdk_draw_point        (GdkDrawable *drawable,
                                                         GdkGC *gc,
                                                         gint x,
                                                         gint y);

第2引数の gc はグラフィックコンテキストというもので、描画のスタイルについていろいろな指定ができるオブジェクト。前回は、GtkDrawingArea ウィジェットに設定されていた値を取り出してきて指定していたが、もちろん新たにGdkGCオブジェクトを生成して、そこで値を指定して gdk_draw_point に渡すこともできる。

前回から変更した関数のみ示す。

void draw_point(
    GtkWidget *area,
    gint x,
    gint y,
    guint16 red,
    guint16 green,
    guint16 blue ) 
{
    GdkGC  *gc;
    GdkColor color;

    color.red   = red;
    color.green = green;
    color.blue  = blue;

    gc = gdk_gc_new(area->window);
    gdk_gc_set_rgb_fg_color(gc, &color);

    gdk_draw_point(area->window,
                   gc,
                   x, y);

    g_object_unref(gc);
}

static gboolean
expose_event(GtkWidget *area, GdkEventExpose *event, gpointer data)
{
    gint x;

    for (x = 0; x < 200; x++)
        draw_point(area, x, x, x * 300, 0, 65535);

    return TRUE;
}
    GdkGC  *gc;
    ....
    gc = gdk_gc_new(area->window);

点を打つための関数 gdk_draw_point() の第2引数に渡す GdkGC オブジェクトを生成し、ポインタを gc に格納する。なお、順番が前後するが、生成した GdkGC オブジェクトは使用後に開放する必要があるため、関数の最後で

    g_object_unref(gc);

としている。

    GdkColor color;

    color.red   = red;
    color.green = green;
    color.blue  = blue;

    ....
    gdk_gc_set_rgb_fg_color(gc, &color);

GdkColor 構造体の各要素にRGB値を設定している。GdkColor 構造体は以下のような構造となっている。

typedef struct {
  guint32 pixel;
  guint16 red;
  guint16 green;
  guint16 blue;
} GdkColor;

red, green, blue に各RGB値の配合度合いを 0 〜 65535 の整数値で指定する。

そして、gc に対して前景色の設定を行うため、gdk_gc_set_rgb_fg_color() という関数を呼び出す。このとき、今生成した GdkColor 構造体のポインタを引数として渡す。

これで、独自のグラフィックコンテキストの準備が出来た。あとは gdk_draw_point() にこの gc を渡せば、指定した色で点を打つことができる。



ソース全体

#include <gtk/gtk.h>

static void destroy(GtkWidget *window, gpointer data)
{
    gtk_main_quit();
}

void draw_point(
    GtkWidget *area, 
    gint x, 
    gint y, 
    guint16 red, 
    guint16 green, 
    guint16 blue) 
{
    GdkGC  *gc;
    GdkColor color;

    color.red   = red;
    color.green = green;
    color.blue  = blue;

    gc = gdk_gc_new(area->window);
    gdk_gc_set_rgb_fg_color(gc, &color);

    gdk_draw_point(area->window, 
                   gc,
                   x, y);

    g_object_unref(gc);
}

static gboolean 
expose_event(GtkWidget *area, GdkEventExpose *event, gpointer data)
{
    gint x;
    
    for (x = 0; x < 200; x++)
        draw_point(area, x, x, x * 300, 0, 65535);

    return TRUE;
}

int main(int argc, char *argv[]) 
{
    GtkWidget *window, *area;
    
    gtk_init(&argc, &argv);

    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window), "Drawing Line");
    gtk_widget_set_size_request(window, 200, 200);
    g_signal_connect(G_OBJECT(window), "destroy", 
                     G_CALLBACK(destroy), NULL);

    area = gtk_drawing_area_new();
    g_signal_connect(G_OBJECT(area), "expose_event", 
                     G_CALLBACK(expose_event), NULL);

    gtk_container_add(GTK_CONTAINER(window), area);
    gtk_widget_show_all(window);
    gtk_main();

    return 0;
}

パフォーマンスを最大化するために必要なこと

Time Power を引き続き読んでいる。

最大のパフォーマンスを得るための12個の実証済みの原則 (Twelve Proven Principles for Peak Performance) という項目があった。とりあえず気になったものをば。

時間を上手く管理するため、自分を「工場」に見立てる

工場の生産活動には3つのフェーズがある。

  • 資源投入(input)
  • 生産(production activities)
  • 成果物(output)

で、自分の活動を工場に見立てたあと、どこに意識の重点をおくかが運命の分かれ道となる。普通の人は、一番はじめの input に注目し、何を自分の中に取り入れたら良いかということばかり気にしてしまう。どんな知識や経験を身につけるか、とか。
しかし効率的に行動できる人は、一番最後の output に意識を集中する。つまり、何かを行うことで自分が何を生み出せるか、何を成し遂げられるかという点を重視し、それに向かって行動する。
逆に、output に意識を集中させれば、行動を効率化できる。

自分に与えられたすべての時間は「今」、この瞬間だけである。

今、この1分、この1時間、この1日…を上手く活用することを考える。
昔読んだ「アルケミスト」という小説に、「過去のことも未来のことも忘れてしまいなさい。今という時間を大切にしなさい」といった事が書いてあった(文章正確でなくてスマソ)。未来は自分の意思で変えられるってよく言われるけど、今という時間はその未来よりはるかに自由に変えられる。自分の思い通りにできる。だったら、未来のことをあれこれ考えるより、今を精一杯生きることが大切なんだと思う。
まぁ計画性は必要だけど。

[GTK+] 線を描画する (解説編)

さて昨日の線描画プログラムの解説をば。長いよ。w

 28     GtkWidget *window, *area;
...
 38     area = gtk_drawing_area_new();

GtkDrawingArea ウィジェットを生成する。
GtkDrawingArea というのは GtkWidget の派生オブジェクトなのだが、他の派生オブジェクト(ボタンやラベルなど)と違い、特に用途が決められているわけではない。このことは、 GtkDrawingArea が提供する関数がただ一つ、このオブジェクトを生成するための gtk_drawing_area_new() だけだということからも伺い知れる。リファレンスマニュアルの GtkDrawingArea に対する説明には「カスタムユーザインタフェースを生成するために使われる」とある。
今回はこのGtkDrawingArea ウィジェットをウィンドウの上に貼り付けて、その上に描画することにしたい。

 39     g_signal_connect(G_OBJECT(area), "expose_event",
 40                      G_CALLBACK(expose_event), NULL);

つい先ほど生成した GtkDrawingArea ウィジェット(=area)が expose_event シグナルを受け取ったときに、コールバック関数 expose_event (15-24行目) をコールするようにする。

expose_event シグナルとは何ぞや、ということになるが、その前にまず GTK+の描画モデルについて理解する必要があると思うので、はじめにそちらについて説明したい。


例えばウィンドウのサイズを変更したり、背面にあったウィンドウを最前面に持ってきたりしたときのことを考えてみよう。ウィンドウサイズを拡大した場合、その拡げられた部分にはこれまで何も描画されていないため、新たに描画を行う必要がある。また背面にあったウィンドウを前面に持ってくるケースでは、背面に隠れていた部分を新たに描画する必要がある。
これら新たに描画される部分を含めて、ウィンドウシステム側であらかじめ描画内容をメモリに格納しておき、必要なときに必要な箇所を自動的に再描画してくれたりしたら、プログラマ的には楽だろう。
が、実際にはそうなっていない。おそらく「どう再描画するか」については、アプリケーション制作者に委ねられるべきという考え方からだと思う。そこでウィンドウシステム側は、自動的に再描画はせずに、再描画が必要なウィジェットに対して expose_event というシグナルを送信し、再描画の必要があることをアプリケーション側に知らせるようになっている。

繰り返しになるが、上記の g_signal_connect では、ウィジェット area が expose_event シグナルを受け取ったときに実行される関数を登録している。

 42     gtk_container_add(GTK_CONTAINER(window), area);

そして、その area を、トップレベルウィンドウ(window)に追加している。


次に、このコールバック関数 expose_event の内容をば。

 15 static gboolean
 16 expose_event(GtkWidget *area, 
                                  GdkEventExpose *event, 
                                  gpointer data)
 17 {
 18     gint x;
 19 
 20     for (x = 0; x < 200; x++)
 21         draw_point(area, x, x);
 22 
 23     return TRUE;
 24 }

expose_event シグナルを受け取ったときのコールバック関数は以下の形式となる。(GTK+ Reference Manual の GtkWidget の Signals 参照)

gboolean user_function (GtkWidget *widget, 
                                               GdkEventExpose *event, 
                                               gpointer user_data)

で、ここでやっているのは、

  • (0, 0) から (200, 200) に向かって斜めの線を引く

これだけである。
ちなみに draw_point というのは以下のとおりで、

  8 void draw_point(GtkWidget *area, gint x, gint y)
  9 {
 10     gdk_draw_point(area->window,
 11                    area->style->fg_gc[ GTK_WIDGET_STATE(area) ],
 12                    x, y);
 13 }

gdk_draw_point という関数を使って、指定された座標に点を打っている。
gdk_draw_point の関数仕様は以下のようになっている。

void gdk_draw_point(GdkDrawable *drawable, 
                                        GdkGC *gc, 
                                        gint x, 
                                        gint y)


注:以下の説明文で GdkWindow と GtkWindow という非常に良く似ているが全く別物の名前が出てきますw


第1引数で指定するのは描画対象。ただし、ウィジェットそのものを指定するのではなく、GdkDrawable * 型のものを指定する。描画対象を GtkWidget 型で持っている場合は、その中に window というプロパティがある(型は GdkWindow*)ので、それを使って

widget->window

のように指定すればよい。

ちなみに window という言葉の意味がややこしいのだが、ここで言う window は GdkWindow *型であり、単に「画面上に存在する矩形の領域」を意味する。GtkWindow、つまり複数ウィジェットをまとめる最上位のウィジェットという意味のウィンドウとは別物だ。(一文字違いだから余計ややこしいw)

window はGtkWidget のプロパティの一つとして含まれているので、GtkWidget を継承したオブジェクト、たとえば GtkButton や GtkEntry などもすべてこの window を持っていることになる。
で、GdkWindow は GdkDrawable を継承したオブジェクトなので、gdk_draw_point の第1引数の描画対象として指定することが可能だ。


次に第2引数の描画スタイルの指定部分について。

 11                    area->style->fg_gc[ GTK_WIDGET_STATE(area) ],

内側の GTK_WIDGET_STATE(area) から見ていく。
GTK_WIDGET_STATE() はマクロで、以下のように定義されている。

#define GTK_WIDGET_STATE(wid)		  (GTK_WIDGET (wid)->state)

state というのは何かというと、特定ウィジェットの「状態」を表す値であって、GtkStateType で定義された5つの値のいずれかをとる。

typedef enum
{
  GTK_STATE_NORMAL,
  GTK_STATE_ACTIVE,
  GTK_STATE_PRELIGHT,
  GTK_STATE_SELECTED,
  GTK_STATE_INSENSITIVE
} GtkStateType;

これらの意味するところは Reference Manual を参照していただくとしてここでは割愛。ただ、これが GTK_WIDGET(area)->state の値であり、配列 fg_gc[] の添字である。

 11                    area->style->fg_gc[ GTK_WIDGET_STATE(area) ],

さて今度は area->style の意味を。
GtkWidget のプロパティの中に style という GtkStyle * 型の値がある。この GtkStyle 構造体は、ウィジェットの見せ方に関するさまざまな情報を格納している(色など)。

さらに GtkStyle の中を見ていく。

typedef struct {
  ...(中略)
  GdkGC *fg_gc[5];
  ...(中略)
} GtkStyle;

かなり前後を略してあるが、この中に fg_gc というGdkGC * 型の配列が定義されている。

GdkGC というのは GDK で使用される Graphic Context(GC)。描画を行うさいのさまざまな情報、たとえば色や線の太さなどを保持している。
で、fg_gc は、GdkGC へのポインタを5つ保持している。5つというのは、先に出てきた GtkStateType の数と同じであり、つまり、各 State における前景描画時の情報をそれぞれ保持しているということになる。


かなり話がややこしくなってきたが、結局

 11                    area->style->fg_gc[ GTK_WIDGET_STATE(area) ],

これは、

ウィジェットに定義されている、現在のウィジェットの状況における描画コンテクスト

を得ることになる。そして、

 10     gdk_draw_point(area->window,
 11                    area->style->fg_gc[ GTK_WIDGET_STATE(area) ],
 12                    x, y);

これは「ウィジェット area の window 領域に対して、現在の状況における描画コンテクストを使って、座標(x, y)に点を打つ」ということになる。


ふぅ、ずいぶん長くなってしまったww

(参考)

[GTK+] 線を描画する

とりあえずウィンドウ内に線を引いてみた。
時間がないので説明は明日以降で(ぉぃ

  1 #include <gtk/gtk.h>
  2 
  3 static void destroy(GtkWidget *window, gpointer data)
  4 {
  5     gtk_main_quit();
  6 }
  7 
  8 void draw_point(GtkWidget *area, gint x, gint y)
  9 {
 10     gdk_draw_point(area->window,
 11                    area->style->fg_gc[ GTK_WIDGET_STATE(area) ],
 12                    x, y);
 13 }
 14 
 15 static gboolean
 16 expose_event(GtkWidget *area, GdkEventExpose *event, gpointer data)
 17 {
 18     gint x;
 19 
 20     for (x = 0; x < 200; x++)
 21         draw_point(area, x, x);
 22 
 23     return TRUE;
 24 }
 25 
 26 int main(int argc, char *argv[])
 27 {
 28     GtkWidget *window, *area;
 29 
 30     gtk_init(&argc, &argv);
 31 
 32     window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 33     gtk_window_set_title(GTK_WINDOW(window), "Drawing Line");
 34     gtk_widget_set_size_request(window, 200, 200);
 35     g_signal_connect(G_OBJECT(window), "destroy",
 36                      G_CALLBACK(destroy), NULL);
 37 
 38     area = gtk_drawing_area_new();
 39     g_signal_connect(G_OBJECT(area), "expose_event",
 40                      G_CALLBACK(expose_event), NULL);
 41 
 42     gtk_container_add(GTK_CONTAINER(window), area);
 43     gtk_widget_show_all(window);
 44     gtk_main();
 45 
 46     return 0;
 47 }

描画するときのキモは expose_event を g_signal_connect するところかなぁ。


あ、あと、線を引く関数はちゃんとあるよ、というツッコミはなしの方向でww

スキルアップをより効果的に行うための6つの方法

Time Power: A Proven System for Getting More Done in Less Time Than You Ever Thought Possible という本をSafariで読んでる。この中で、効果と効率性を高めるために自己をプログラムする方法というのがいくつか紹介されていた。



注:以下は、同書で紹介されていた内容をもとに自分の感想をまとめたものであり、必ずしも同書の主旨とは一致しない。

前向きな言葉で自分を励ます(Use Positive Self-Talk)

例えば「自分は時間管理の達人だ! 自分は時間管理の達人だ!」などと、自分に何度も言い聞かせる。そうするとその言葉が自分の潜在意識に染み込んで、いざ行動するときに現れるようになる。

高度に効率化した存在として自分を見る(Visualize Yourself as Hightly Efficient)

いわゆるイメトレ。自分が何かを上手くやっている姿を繰り返し想像する。そうすると、いざやってみたときに実際上手くいく。

上手くいっている人の役を演じる(Act the Part)

まずはとりあえず演じてみる。演技であっても、それを繰り返していれば、いつのまにか自分の体に染み込んでいる。

目標に対するベンチマークをとる(Benchmark Against the Best)

まず、自分が理想とする人を一人見つける。過去の時代に生きた人でもよい。そして、ある状況において、その理想の人だったらどのように行動するかを想像し、その後自分で実際に行動してみる。おそらく全く同じには行動できないと思うので、その差異を認識して、次はもっと上手くやって理想に近づけるようにする。

先生を目指す(Become a Teacher)

自分が成長したいと思っている分野について、1年後に教える立場になっていることを想像してスキルアップしていく。自分が成長するだけでなく、他人を成長させるためにはどのように教えたらよいかを考えると、スキルアップは早い。
効率的な方法の一つとして、自分が学んだらすぐにその内容を他人とシェアするという方法がある。他人に説明するという行為は大変な集中力を必要とするし、それだけ自分自身の勉強にもなる。

他人のお手本になる(Be a Role Model for Others)

自分自身が上手く行動するだけでなく、それが他人にも良い影響を与えるように意識してみる。他人の目を気にすることで余計意識を集中することになるから、自分自身をより高めることができる。


特に最後2つの、他人に影響を与えるという視点は重要かなぁと。自分を高めるだけだと一人よがりな評価になりがちだけど、他人を意識することで自分を客観的に見れそうだし。

GTK+ でプログラミング:基本形

とりあえず、GTK+ で何かを作るときに基本形となるものをば。これを拡張していけば大抵のものは作れるかと。w

ちなみに、GTK+ の関数仕様については、DevHelp というマニュアルを使って調べるのが便利。メニュー Application → Programming の中に入っていると思う。入ってなかったらアプリケーション追加でインストールしておく。

まずプログラム全体はこんな感じ。

  1 #include <gtk/gtk.h>
  2 
  3 static void destroy(GtkWidget *window, gpointer data)
  4 {
  5     /*  メインループを終了  */
  6     gtk_main_quit();
  7 }
  8 
  9 int main(int argc, char *argv[])
 10 {
 11     GtkWidget *window, *label;
 12 
 13     /*  GTK+ の初期化  */
 14     gtk_init(&argc, &argv);
 15 
 16     /*  ウィンドウ(GtkWindow) の作成  */
 17     window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 18 
 19     /*  ウィンドウの初期設定  */
 20     gtk_window_set_title(GTK_WINDOW(window), "Hello");  //  title
 21     gtk_widget_set_size_request(window, 200, 100);      //  window size
 22 
 23     /*  ウィンドウが消去されたときのコールバック関数を設定  */
 24     g_signal_connect(G_OBJECT(window), "destroy",
 25                      G_CALLBACK(destroy), NULL);
 26 
 27     /*  ラベル(GtkLabel) の作成  */
 28     label = gtk_label_new("Hello, Mirin!");
 29 
 30     /*  ラベルをウィンドウに追加  */
 31     gtk_container_add(GTK_CONTAINER(window), label);
 32 
 33     /*  ウィンドウを表示  */
 34     gtk_widget_show_all(window);
 35 
 36     /*  メインループ  */
 37     gtk_main();
 38 
 39     return 0;
 40 }


以下、それぞれの箇所について解説。

  1 #include <gtk/gtk.h>

GTK+ で開発を行うときに最低限必要なヘッダファイルはこれ。

  9 int main(int argc, char *argv[])
 10 {
 11     GtkWidget *window, *label;

ウィンドウやラベルなど、ウィジェットを格納するための変数を宣言。
ここでは2つの変数を GtkWidget *型で宣言しているが、実際には window には GtkWindow 型のオブジェクトが、また label には GtkLabel 型のオブジェクトが格納される。この2つの型は GtkWidget を継承したものであり、宣言時にはGtkWidget 型を使うとよい。

 13     /*  GTK+ の初期化  */
 14     gtk_init(&argc, &argv);

GTK+を初期化する。
関数 gtk_init() は、GTK+ 関連のすべての操作を行う前に呼び出す必要がある。
引数は main 関数で渡されたものを何も考えずそのままアドレス渡しすればよい。

 16     /*  ウィンドウ(GtkWindow) の作成  */
 17     window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

まずウィンドウオブジェクトを作成する。
引数で GTK_WINDOW_TOPLEVEL と指定しているが、これはウィンドウのタイプ。ここで指定できるのは、以下のいずれか。(GtkWindowType で定義されている)

  • GTK_WINDOW_TOPLEVEL

いわゆる「普通のウィンドウ」。WindowManagerが提供するいろいろな機能の恩恵をうけられる(ウィンドウのリサイズや、ショートカットキーの使用など)。特別な理由がない限りはこちらを使う。

  • GTK_WINDOW_POPUP

特殊な場合にのみ使われる。これを指定したウィンドウは、WindowManager の提供する一切の機能が使えなくなる。
用途としては、ウィンドウの上に表示されるメニューやツールチップなどがある。

 19     /*  ウィンドウの設定  */
 20     gtk_window_set_title(GTK_WINDOW(window), "Hello");  //  title
 21     gtk_widget_set_size_request(window, 200, 100);      //  window size

ウィンドウに対していくつかの設定を行う。
まず、gtk_window_set_title() を使って、ウィンドウのタイトルを設定している。
ここで問題になるのが、第1引数の指定方法。関数仕様によると、第1引数では GtkWindow * を指定することになっている。しかし window という変数は GtkWidget * 型で宣言されている。
したがって、この window はキャストしてあげる必要がある。GtkWidget * 型を GtkWindow *型に変換するには、GTK_WINDOW() 関数を使う。
ちなみに GTK+でのプログラミングでは、この手のキャストは頻繁に使われる。他にも GTK_CONTAINER などいろいろあるので、第一引数で指定すべき型に応じて使い分ける。

次に、gtk_widget_set_size_request() で、そのウィンドウに対してサイズを設定している。第1引数は GtkWidget *型なので、ここではキャストの必要はない。

 23     /*  ウィンドウが消去されたときのコールバック関数を設定  */
 24     g_signal_connect(G_OBJECT(window), "destroy",
 25                      G_CALLBACK(destroy), NULL);

ウィンドウに対してなんらかのアクションが行われたときのコールバック関数を設定する。ここでは、ウィンドウ右上の×ボタンが押されるなど、ウィンドウを閉じる操作が行われたときに、destroy というコールバック関数を呼び出すように指定している。
なお、第4引数は、コールバック関数に渡す引数。ここでは NULL を指定しているため、何も渡されない。


次に、文字を表示するためのラベルオブジェクトを作成する。

 27     /*  ラベル(GtkLabel) の作成  */
 28     label = gtk_label_new("Hello, Mirin!");

引数に、ラベル上に表示させたい文字をそのまま指定する。

 30     /*  ラベルをウィンドウに追加  */
 31     gtk_container_add(GTK_CONTAINER(window), label);

そしてこのラベルを、先ほど生成したウィンドウに貼り付ける。

 33     /*  ウィンドウを表示  */
 34     gtk_widget_show_all(window);

ウィンドウの準備がすべて整ったので、ここで表示を行う。

 36     /*  メインループ  */
 37     gtk_main();

そしてメインループへと入る。
gtk_main() によるメインループが続いている間は、ウィンドウに対していろいろな操作を行える。このループを抜けるのは、次に出てくる gtk_main_quit() が呼ばれた時。


24行目で設定したコールバック関数 destroy() は、ウィンドウを閉じようとしたときに呼ばれる。

  3 static void destroy(GtkWidget *window, gpointer data)
  4 {
  5     /*  メインループを終了  */
  6     gtk_main_quit();
  7 }

gtk_main_quit() がここで呼ばれている。つまり、ウィンドウ右上の×印が押されるなどしてウィンドウが閉じられ
るときに、gtk_main() のループが終了する。


以上の内容を hello.c として保存してコンパイル

gcc -Wall -o hello hello.c `pkg-config --cflags --libs gtk+-2.0`


そして実行。

> ./hello

ウィンドウが現れ、その中に文字列が表示される。
リサイズや移動も自由にできる。右上の×印で終了。


参考: Foundations of GTK+ Development

Test::Continuous

継続的テスト(Continuous Testing)というのに興味を引かれて使ってみる。

上記の紹介ページによると、Continuous Testing を取り入れることによって、締め切りまでに作業完了できる確率が3倍になるとのこと。なんて素敵なんだ!w
で、これを Perl で実現したのが Test::Continuous とのこと。


さっそく install して使ってみる。


今回テストで使用するファイルの構成。

lib/MyTest.pm
t/01test.t


まずは test されるモジュールの作成。ここは簡単に足し算の関数を。

lib/MyTest.pm

package MyTest;

use strict;
use warnings;

sub add {
    my ($x, $y) = @_;

    my $z = $x + $y;
    return $z;
}

1;


それと、このモジュールをテストするためのスクリプト

t/01test.t

use strict;
use warnings;

use lib qw(lib);
use MyTest;

use Test::More tests => 2;

is(MyTest::add(1, 2), 3, '1 + 2');
is(MyTest::add(-3, 5), 2, '-3 + 5');


ここまでは普段どおり。
ここで Test::Continuous の runtests を起動させる。

perl -MTest::Continuous -e runtests


起動すると、Ctrl+C で中断するまで動き続ける。screen で画面を2分割して、片方の画面で runtests の出力を確認しながら、もう一つの画面で lib/MyTest.pm を編集していくと使いやすい。


たとえば、lib/MyTest.pm の、関数 add を以下のように変更して保存すると、runtests が新たな出力を吐き出す。

sub add {
    my ($x, $y) = @_;

    my $z = $x + $y;
    return $x + $y;        #  ここを変更
}

(出力結果)

[MSG]:/home/egawata/perl/Test-Continuous/lib/.MyTest.pm.swp was changed.
[MSG]:/home/egawata/perl/Test-Continuous/lib/MyTest.pm was changed.
ALL PASSED

今回の変更を行っても t/01test.t のテストは正常に通るので、上記のとおり ALL PASSED となる。


もし関数の記述を誤り、テストに通らない状態にしてしまうと、runtests は以下のような出力を行う。

sub add {
    my ($x, $y) = @_;

    my $z = $x - $y;

    return $z;
}

(実行結果)

[MSG]:/home/egawata/perl/Test-Continuous/lib/.MyTest.pm.swp was changed.
2 planned, only 0 passed.
Failed test(s) in t/01test.t: 1 2
 Non-zero exit status: t/01test.t
/home/egawata/perl/Test-Continuous/t/01test.t:
#   Failed test '1 + 2'
#   at /home/egawata/perl/Test-Continuous/t/01test.t line 9.
#          got: '-1'
#     expected: '3'
#   Failed test '-3 + 5'
#   at /home/egawata/perl/Test-Continuous/t/01test.t line 10.
#          got: '-8'
#     expected: '2'
# Looks like you failed 2 tests of 2.

以上のように、モジュールに変更を加えて保存した段階で、修正した内容が正しいかどうかテストしてくれる点は便利でやりやすいかも。