다음 이전 차례

5. 패킹 Widget

우리가 어떤 어플을 개발하면서, 하나의 윈도에 하나 이상의 버튼을 놓으려 할 것이다. 우리의 첫번째 hello world 예제는 하나의 widget만 썼고 따라서 우리는 그것을 윈도 내부에 "pack"하기 위해 gtk_container_add 함수를 썼다. 그러나 우리가 하나의 윈도에 더많은 widget을 놓으려 할 때, 어떻게 그들이 놓일 위치를 제어해야 할까? 여기서 바로 "packing"이란 것이 등장한다.

5.1 박스 packing의 원리

대부분의 패킹은 앞서의 예제에서처럼 박스를 만드는 것으로 이루어진다. 이들은 우리의 widget을 수평 혹은 수직 방향으로 패킹해 넣을 수 있는, 보이지 않는 widget 컨테이너들이다. 수평 박스로의 패킹widget인 경우, object는 호출 하는 방식에 따라 수평으로 왼쪽에서 오른쪽으로 혹은 오른쪽에서 왼쪽으로 삽입 된다. 수직 박스에서는 반대로 수직으로 삽입된다. 우리는 원하는 효과를 내기 위해 다른 박스들의 안팎에서 어떻게라도 조합해서 사용할 수 있다.

새로운 수평박스를 만들기 위해 우리는 gtk_hbox_new()를, 그리고 수직박스를 위해서는 gtk_vbox_new()를 이용한다. gtk_box_pack_start()와 gtk_box_pack_end ()는 이런 컨테이너의 내부에 object들을 위치시키기 위해 사용한다. gtk_box_ pack_start()함수는 수직박스에서는 위에서 아래쪽으로, 그리고 수평박스에서는 왼쪽에서 오른쪽으로 패킹할 것이다. 그리고 gtk_box_pack_end()는 이와 반대 방향으로 패킹한다. 이 함수들을 이용함으로써 우리는 오른쪽 또는 왼쪽으로 widget을 정렬할 수 있고, 원하는 효과를 낼 수 있다. 우리는 대부분의 예제에서 gtk_box_pack_start()를 이용할 것이다. Object는 또다른 컨테이너거나 widget이 될 수 있다. 그리고 사실, 많은 widget들은 실제로 버튼을 포함하고 있는 widget 이지만, 우리는 보통 버튼 안의 라벨만을 이용할 것이다.

이런 함수호출로써, GTK는 우리가 widget을 놓을 위치를 알게되고 따라서 자동적으로 크기를 조절한다든지 또다른 매력적인 일들을 할 수 있게 된다. 또한 우리의 widget이 어떻게 패킹되어야 하느냐에 따른 수많은 옵션들도 있다. 우리가 상상하듯이, 이런 방식은 widget을 놓고 만드는 데 있어서 상당한 유연성 을 제공해 준다.

5.2 박스에 대해 자세히 알아보자

이런 유연성 때문에, GTK에서 박스를 패킹하는 것은 처음엔 혼란스러울지 모른다. 많은 옵션들이 있으며, 그들이 어떻게 서로 꿰어 맞춰지는지 즉시 간파 할 수는 없을 것이다. 그러나 결국, 우리는 다섯 가지의 기본적인 스타일을 가지게 된다.

Box Packing Example Image

각각의 줄은 몇 개의 버튼을 가지고 있는 하나의 수평박스(hbox)를 포함한다. 함수호출 gtk_box_pack은 이 수평박스에 각각의 버튼을 패킹하는 것을 단축한 것이다. 각각의 버튼은 이 수평박스에 같은 방법으로 패킹된다(즉, gtk_box_pack _start함수에 같은 인자를 준다는 말).

이것은 gtk_box_pack_start함수의 선언이다.

void gtk_box_pack_start (GtkBox    *box,
                         GtkWidget *child,
                         gint       expand,
                         gint       fill,
                         gint       padding);

첫번째 인자는 object를 패킹할 박스고 두번째는 그 object다. Object는 여기서 모두 버튼이 될 것이고, 따라서 우리는 박스안에 버튼들을 패킹하게 된다.

gtk_box_pack_start() 또는 gtk_box_pack_end()에서의 expand라는 인자가 TRUE 일 때, widget은 여백공간을 가득 채우며 박스에 들어가게 될 것이다. 그리고 그것이 FALSE라면 widget은 적절히 여백을 두게 된다. 이 expand를 FALSE로 두면 우리는 widget의 좌우 정렬을 결정할 수 있다. 그렇지 않으면 그들은 박스에 가득차서 gtk_box_pack_start 또는 gtk_box_pack_end 어느 쪽을 이용하든지 같은 효과를 가지게 된다.

인자 fill은 TRUE일 때 object 자신의 여백공간을 제어한다. 그리고 FALSE라면 object 자신의 여백공간을 두지 않는다. 이것은 expand 인자가 TRUE일 때만 효과 가 있다.

새로운 박스를 만들 때는 이런 함수가 있다(수평박스).

GtkWidget * gtk_hbox_new (gint homogeneous,
                          gint spacing);

여기서의 인자 homogeneous는 박스 안의 각 object들이 같은 크기를 가지도록 제어한다(즉 수평박스일 경우엔 같은 너비, 수직박스일 경우엔 같은 높이). 이것이 세팅되면, gtk_box_pack함수의 expand 인자는 언제나 TRUE가 된다.

여기서 spacing(박스가 만들어지며 세팅됨)와 padding(요소들이 패킹되며 세팅됨)의 차이점은 무엇일까? Spacing은 object들 사이에 생겨나는 것이며 padding은 한 object의 각 방향에서 생겨나는 것이다. 이것이 그 점을 명확히 해줄 것이다.

Box Packing Example Image

여기 이 이미지를 만들어 주는 코드가 있다. 나는 여러 번 강조했으므로 이것에서 별 문제는 없으리라 믿는다. 스스로 컴파일해서 가지고 놀아 보도록 한다.

5.3 패킹에 대한 예제 프로그램

/* packbox.c */

#include "gtk/gtk.h"

void
delete_event (GtkWidget *widget, GdkEvent *event, gpointer data)
{
  gtk_main_quit ();
}

/* Button_label들로 이루어진 hbox를 만든다.  우리가 관심을 가진 변수들을
 * 위한 인자들이 이 함수로 넘겨진다.
 * 우리는 박스를 보이지 않고, 그 안에 있는 모든 것을 보일 것이다. */
GtkWidget *make_box (gint homogeneous, gint spacing,
                     gint expand, gint fill, gint padding)
{
  GtkWidget *box;
  GtkWidget *button;
  char padstr[80];
  /* 적당한 homogenous와 spacing을 가진 hbox를 만든다. */
  box = gtk_hbox_new (homogeneous, spacing);
  /* 적절히 세팅된 버튼들을 만든다. */
  button = gtk_button_new_with_label ("gtk_box_pack");
  gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
  gtk_widget_show (button);

  button = gtk_button_new_with_label ("(box,");
  gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
  gtk_widget_show (button);

  button = gtk_button_new_with_label ("button,");
  gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
  gtk_widget_show (button);
  /* expand의 값에 따르는 라벨을 가진 한 버튼을 만든다. */
  if (expand == TRUE)
    button = gtk_button_new_with_label ("TRUE,");
  else
    button = gtk_button_new_with_label ("FALSE,");

  gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
  gtk_widget_show (button);
  /* 위의 경우와 같은 버튼을 만들지만, 더 단축된 표현이다. */
  button = gtk_button_new_with_label (fill ? "TRUE," : "FALSE,");
  gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
  gtk_widget_show (button);

  sprintf (padstr, "%d);", padding);

  button = gtk_button_new_with_label (padstr);
  gtk_box_pack_start (GTK_BOX (box), button, expand, fill, padding);
  gtk_widget_show (button);

  return box;
}

int
main (int argc, char *argv[])
{
  GtkWidget *window;
  GtkWidget *button;
  GtkWidget *box1;
  GtkWidget *box2;
  GtkWidget *separator;
  GtkWidget *label;
  GtkWidget *quitbox;
  int which;

  /* 언제나 이렇게 시작하는 것을 잊지 말 것! */
  gtk_init (&argc, &argv);

  if (argc != 2) {
    fprintf (stderr, "usage: packbox num, where num is 1, 2, 3.\n");
    /* GTK를 끝내는 부분이며, exit status는 1이다. */
    gtk_exit (1);
  }

  which = atoi (argv[1]);

    /* 우리의 윈도를 만든다. */
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

  /* Main 윈도에 destroy 시그널을 연결시켜 줘야 한다.  이것은 제대로 된
   * 동작을 위해 매우 중요한 것이다. */
  gtk_signal_connect (GTK_OBJECT (window), "delete_event",
                      GTK_SIGNAL_FUNC (delete_event), NULL);
  gtk_container_border_width (GTK_CONTAINER (window), 10);

  /* 우리는 수평박스들을 패킹해 넣을 수직박스(vbox)를 만든다.
   * 버튼이 들어있는 수평박스는 이 수직박스 안으로 순서대로 쌓인다.
   * (스택 구조를 생각하면 될 것이다.) */
  box1 = gtk_vbox_new (FALSE, 0);

  switch (which) {
  case 1:
        /* 새로운 라벨을 만든다. */
        label = gtk_label_new ("gtk_hbox_new (FALSE, 0);");

        /* 라벨들을 왼쪽으로 정렬시킨다.  이것에 대해서 widget의 속성
         * 세팅하기에서 다시 다룰 것이다. */
        gtk_misc_set_alignment (GTK_MISC (label), 0, 0);

        /* 라벨을 수직박스(vbox box1)에 패킹한다.  한 vbox에 패킹되는
         * widget들은 순서대로 다른 것들의 위쪽으로 패킹된다. */
        gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);

        /* 라벨을 보여준다. */
        gtk_widget_show (label);

        /* make_box 함수를 적절한 인자로써 호출한다. */
        box2 = make_box (FALSE, 0, FALSE, FALSE, 0);
        gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
        gtk_widget_show (box2);

        box2 = make_box (FALSE, 0, TRUE, FALSE, 0);
        gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
        gtk_widget_show (box2);

        box2 = make_box (FALSE, 0, TRUE, TRUE, 0);
        gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
        gtk_widget_show (box2);
        /* 하나의 separator를 만든다.  이들에 대해서 뒤에서 자세히
         * 다룰 것이며, 꽤나 간단한 것이다. */
        separator = gtk_hseparator_new ();

        /* separator를 vbox 안으로 패킹한다.  이들 각각의 widget은
         * vbox 안으로 패킹되므로, 수직 방향으로 쌓일 것이다. */
        gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
        gtk_widget_show (separator);

        /* 또다른 라벨을 만들어 그것을 보여준다. */
        label = gtk_label_new ("gtk_hbox_new (TRUE, 0);");
        gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
        gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
        gtk_widget_show (label);

        /* 각 인자는 homogeneous, spacing, expand, fill, padding이다. */
        box2 = make_box (TRUE, 0, TRUE, FALSE, 0);
        gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
        gtk_widget_show (box2);

        box2 = make_box (TRUE, 0, TRUE, TRUE, 0);
        gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
        gtk_widget_show (box2);

        /* 또다른 separator */
        separator = gtk_hseparator_new ();
        /* gtk_box_pack_start 의 마지막 3가지 인자들은
         * expand, fill, padding 이다. */
        gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
        gtk_widget_show (separator);

        break;

    case 2:

        /* 라벨을 새로 만든다. box1은 main()의 시작부분에서 만들어진
         * 대로 vbox이다. */
        label = gtk_label_new ("gtk_hbox_new (FALSE, 10);");
        gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
        gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
        gtk_widget_show (label);

        box2 = make_box (FALSE, 10, TRUE, FALSE, 0);
        gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
        gtk_widget_show (box2);

        box2 = make_box (FALSE, 10, TRUE, TRUE, 0);
        gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
        gtk_widget_show (box2);

        separator = gtk_hseparator_new ();
        gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
        gtk_widget_show (separator);

        label = gtk_label_new ("gtk_hbox_new (FALSE, 0);");
        gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
        gtk_box_pack_start (GTK_BOX (box1), label, FALSE, FALSE, 0);
        gtk_widget_show (label);

        box2 = make_box (FALSE, 0, TRUE, FALSE, 10);
        gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
        gtk_widget_show (box2);

        box2 = make_box (FALSE, 0, TRUE, TRUE, 10);
        gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
        gtk_widget_show (box2);

        separator = gtk_hseparator_new ();
        gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
        gtk_widget_show (separator);
        break;

    case 3:
        /* 이것은 gtk_box_pack_end()를 이용하여 widget을 오른쪽 정렬하는
         * 걸 보여준다.  먼저, 앞에서처럼 새로운 박스를 하나 만든다. */
        box2 = make_box (FALSE, 0, FALSE, FALSE, 0);
        /* 라벨을 하나 만든다. */
        label = gtk_label_new ("end");
        /* 그것을 gtk_box_pack_end()로써 패킹하므로, make_box()로 만들어진
         * hbox의 오른쪽으로 놓여지게 된다.
        gtk_box_pack_end (GTK_BOX (box2), label, FALSE, FALSE, 0);
        /* 라벨을 보인다. */
        gtk_widget_show (label);

        /* box2를 box1 안으로 패킹한다. */
        gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, FALSE, 0);
        gtk_widget_show (box2);

        /* bottom 쪽을 위한 separator. */
        separator = gtk_hseparator_new ();
        /* 이것은 400픽셀의 너비에 5픽셀의 높이(두께)로 separator를
         * 세팅한다. 이것은 우리가 만든 hbox가 또한 400픽셀의 너비이기
         * 때문이고, "end" 라벨은 hbox의 다른 라벨들과 구분될(separated)
         * 것이다.  그렇지 않으면, hbox 내부의 모든 widget들은 가능한만큼
         * 서로 빽빽히 붙어서 패킹될 것이다. */
        gtk_widget_set_usize (separator, 400, 5);
        /* main()함수의 시작부분에서 만들어진 vbox(box1)으로 separator를 
         * 패킹한다. */
        gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 5);
        gtk_widget_show (separator);
    }

    /* 또다른 hbox를 만든다.. 우리가 원하는 만큼 얼마든지 만들수 있다! */
    quitbox = gtk_hbox_new (FALSE, 0);

    /* 우리의 quit 버튼이다. */
    button = gtk_button_new_with_label ("Quit");

    /* 윈도를 파괴하기 시그널을 세팅한다.  이것은 위에서 정의된 우리의 
     * 시그널 핸들러에 의해 포착될, "destroy"시그널을 윈도로 보내준다. */
    gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
                               GTK_SIGNAL_FUNC (gtk_widget_destroy),
                               GTK_OBJECT (window));
    /* quitbox로 버튼을 패킹한다.  gtk_box_pack_start의 마지막 세 인자는
     * expand, fill, padding이다. */
    gtk_box_pack_start (GTK_BOX (quitbox), button, TRUE, FALSE, 0);
    /* vbox(box1) 안으로 quitbox를 패킹한다. */
    gtk_box_pack_start (GTK_BOX (box1), quitbox, FALSE, FALSE, 0);

    /* 우리의 모든 widget을 포함하게 된 이 vbox를, main윈도로 패킹. */
    gtk_container_add (GTK_CONTAINER (window), box1);

    /* 그리고 남아있는 모든 것을 보여준다. */
    gtk_widget_show (button);
    gtk_widget_show (quitbox);

    gtk_widget_show (box1);
    /* 마지막에 윈도를 보여줘서 모든 것이 한번에 튀어나오며 보인다. */
    gtk_widget_show (window);

    /* 당연히 우리의 gtk_main이다. */
    gtk_main ();

    /* gtk_main_quit()을 호출했다면 제어는 이곳으로 온다.  gtk_exit()를
     * 호출하면 그렇지 않다. */

    return 0;
}

5.4 테이블을 이용한 패킹

또다른 패킹 - 테이블을 이용한 것을 보자. 이것은 어떤 상황에서 아주 유용할 것이다.

테이블을 이용해서, 우리는 widget을 넣어둘 격자판을 만들게 된다. 그 widget 들은 우리가 설정하는대로 얼마든지 공간을 가지게 될 것이다.

물론 먼저 봐야 할 것은 gtk_table_new 함수다.

GtkWidget* gtk_table_new (gint rows,
                          gint columns,
                          gint homogeneous);

첫번째 인자는 테이블에 만들 행의 갯수고, 두번째는 당연히 열의 갯수다.

인자 homogeneous는 테이블의 박스들의 크기가 어떻게 되어야 하는지를 결정 한다. 이것이 TRUE라면 테이블의 박스들은 그 테이블에서 가장 큰 widget의 크기에 따르게 된다. 또 FALSE라면 각 박스들은 같은 행에서 가장 높은 widget의 높이를, 그리고 같은 열에서는 가장 넓은 widget의 너비를 따르게 된다.

각 행과 열은 0에서 n까지 차지하게 된다. 여기서 n은 gtk_table_new를 호출할 때 결정된 숫자다. 그래서, 만약 우리가 행=2 그리고 열=2, 이렇게 설정하게 되면 구조는 이렇게 된다.

 0          1          2
0+----------+----------+
 |          |          |
1+----------+----------+
 |          |          |
2+----------+----------+

좌표계는 왼쪽 위에서 출발하는 것을 주의하자. 박스안에 widget을 놓으려면 다음 함수를 이용한다.

void gtk_table_attach (GtkTable      *table,
                       GtkWidget     *child,
                       gint           left_attach,
                       gint           right_attach,
                       gint           top_attach,
                       gint           bottom_attach,
                       gint           xoptions,
                       gint           yoptions,
                       gint           xpadding,
                       gint           ypadding);

첫번째 인자 table은 우리가 만든 테이블이고 두번째 child는 이 테이블에 놓으려고 하는 widget이다.

왼쪽과 오른쪽의 attach라는 인자들은 widget을 놓을 위치와 이용할 박스의 갯수를 결정한다. 만약 우리의 2x2 테이블의 오른쪽 아래 칸에 위치한 버튼을 원한다면, 그리고 그 요소만 가득채우기를 원한다면, left_attach=1, right_ attach=2, top_attach=1, bottom_attach=2, 이렇게 되어야 한다.

이제, 우리의 2x2 테이블의 첫번째 행 전체를 선택했다면, left_attach=0, right_attach=2, top_attach=0, bottom_attach=1 이라고 하면 된다.

인자 xoption과 yoption은 패킹 옵션을 정하기 위한 것으로, 여러가지의 옵션을 주기 위해서 OR 연산자를 쓸 수 있다.

이 옵션들은 다음과 같다.

패딩은 박스 안에서처럼, 픽셀 단위로 뚜렷한 영역을 widget 주위에 만들어 준다.

gtk_table_attach()는 수많은 옵션을 가지고 있다. 그래서 이런 shortcut이 있다.

void gtk_table_attach_defaults (GtkTable   *table,
                                GtkWidget  *widget,
                                gint        left_attach,
                                gint        right_attach,
                                gint        top_attach,
                                gint        bottom_attach);

X와 Y 옵션은 디폴트로 GTK_FILL|GTK_EXPAND, 그리고 X와 Y의 패딩은 0이다. 나머지 인자들은 이전의 함수와 같다.

또한 gtk_table_set_row_spacing()과 gtk_table_set_col_spacing()이란 함수도 있다. 이것은 주어진 행 또는 열에 대해 spacing을 설정한다.

void gtk_table_set_row_spacing (GtkTable      *table,
                                gint           row,
                                gint           spacing);
그리고
void       gtk_table_set_col_spacing  (GtkTable      *table,
                                       gint           column,
                                       gint           spacing);
어떤 열에 대해서 space는 열의 오른쪽으로, 그래고 행에 대해서는 행의 아래쪽 으로 주어진다는 것을 염두에 두자.

모든 행과 열에 대한 일관된 spacing은 다음 두 함수를 사용한다.

void gtk_table_set_row_spacings (GtkTable *table,
                                 gint      spacing);

void gtk_table_set_col_spacings (GtkTable  *table,
                                 gint       spacing);

이 두 함수는 마지막 행과 마지막 열에 대해서는 spacing을 하지 않는다는 것을 기억하라.

5.5 테이블 패킹 예제

여기서 우리는 2x2 테이블 안에 세개의 버튼이 있는 윈도를 하나 만든다. 처음 두 버튼은 윗행에 놓이고 세번째 quit 버튼은 두열을 차지하면서 아랫행에 놓인다. 그러므로 다음 그림처럼 보이게 된다.

Table Packing Example Image

소스 코드는 이렇다.

/* table.c */
#include <gtk/gtk.h>

/* 우리의 callback.
 * 이 함수로 넘겨지는 데이타는 stdout으로 출력된다. */
void callback (GtkWidget *widget, gpointer data)
{
    g_print ("Hello again - %s was pressed\n", (char *) data);
}

/* 이 callback 프로그램을 종료한다 */
void delete_event (GtkWidget *widget, gpointer data)
{
    gtk_main_quit ();
}

int main (int argc, char *argv[])
{
    GtkWidget *window;
    GtkWidget *button;
    GtkWidget *table;

    gtk_init (&argc, &argv);

    /* 새로운 윈도를 만든다. */
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

    /* 윈도의 제목을 정한다. */
    gtk_window_set_title (GTK_WINDOW (window), "Table");

    /* GTK를 곧장 종료시키는 delete_event 핸들러를 정한다. */
    gtk_signal_connect (GTK_OBJECT (window), "delete_event",
                        GTK_SIGNAL_FUNC (delete_event), NULL);

    /* 윈도의 border width를 정한다. */
    gtk_container_border_width (GTK_CONTAINER (window), 20);

    /* 2x2의 테이블을 만든다. */
    table = gtk_table_new (2, 2, TRUE);

    /* 테이블을 윈도에 놓는다. */
    gtk_container_add (GTK_CONTAINER (window), table);

    /* 첫 버튼을 만든다. */
    button = gtk_button_new_with_label ("button 1");

    /* 버튼이 눌리면 "button 1"을 인수로 해서 "callback" 함수를
     * 부른다. */
    gtk_signal_connect (GTK_OBJECT (button), "clicked",
              GTK_SIGNAL_FUNC (callback), (gpointer) "button 1");


    /* 첫 버튼을 테이블 왼쪽 제일 위에 놓는다. */
    gtk_table_attach_defaults (GTK_TABLE(table), button, 0, 1, 0, 1);

    gtk_widget_show (button);

    /* 두번째 버튼을 만든다. */

    button = gtk_button_new_with_label ("button 2");

    /* 버튼이 눌리면 "button 2"을 인수로 해서 "callback" 함수를 
     * 부른다. */
    gtk_signal_connect (GTK_OBJECT (button), "clicked",
              GTK_SIGNAL_FUNC (callback), (gpointer) "button 2");
    /* 두번째 버튼을 테이블 오른쪽 제일 위에 놓는다. */
    gtk_table_attach_defaults (GTK_TABLE(table), button, 1, 2, 0, 1);

    gtk_widget_show (button);

    /* "Quit" 버튼을 만든다. */
    button = gtk_button_new_with_label ("Quit");

    /* 버튼이 눌리면 "delete_event" 함수를 호출해서
     * 프로그램을 끝낸다. */
    gtk_signal_connect (GTK_OBJECT (button), "clicked",
                        GTK_SIGNAL_FUNC (delete_event), NULL);

    /* "Quit" 버튼을 테이블의 아랫행의  두열에 놓는다. */ 
    gtk_table_attach_defaults (GTK_TABLE(table), button, 0, 2, 1, 2);

    gtk_widget_show (button);

    gtk_widget_show (table);
    gtk_widget_show (window);

    gtk_main ();

    return 0;
}


다음 이전 차례