2011년 6월 13일 월요일

[번역] GTK+ Tutorial

출처 : http://zetcode.com/tutorials/gtktutorial/

* 소개
* 첫번째 프로그램
* 메뉴와 툴바
* 모양세 관리
* 이벤트
* 다이얼로그
* 위젯
* 위젯 II
* GtkTreeView 위젯
* GtkTextView 위젯
* 사용자 GTK+ 위젯

이것은 GTK+ 프로그래밍 지침서이다. GTK+ 사용 지침서는 C 프로그래밍 언어에 관한 것만 쓰여졌다. GTK+ 사용 지침서는 새내기, 혹은 중급 정도의 프로그래머에 알맞도록 작성되어 졌다. Cario graphic 사용지침서도 살펴보는 것을 꼭 잊지 말아달라.

GTK+ 소개

이 사용 지침서에 관해서

이것은 소개를 목적으로하는 GTK+ 프로그래밍 사용지침서이다. 이 사용지침서는 C 프로그래밍 언어를 대상으로 다루고 있다. 모든 내용은 리눅스 시스템 상에서 작성되고 테스트 되었다. GTK+ 프로그래밍 지침서는 초심자 및 중급 프로그래머에 적합하도록 작성되었다.

GTK+

GTK+는 그래픽 사용자 인터페이스를 작성하기 위한 라이브러리다. 이 라이브러리는 C 프로그래밍 언어에서 작성되었다. GTK+ 라이브러리는 또한 GIMP 툴킷이라고도 불린다. 원래, 이 라이브러리는 GIMP 영상 조작 프로그램을 개발할때 작성되었다. 그 이후로 GTK+는 리눅스와 BSD 유닉스에서 가장 인기있는 툴킷중 하나가 되었다. 오늘날 오픈 소스 세계의 대부분의 GUI 소프트웨어는 QT혹은 GTK+로 작성되었다. GTK+는 Object Oriented 응용 프로그램 인터페이스이다. GObject는 다양한 다른 프로그래밍 언어를 위한 language binding을 생성할 수 있게 도와 준다. language binding은 C++, Python, Perl, Java, C# 그리고 다른 많은 언어에 대해서 존재한다.

GTK+ 자체는 다음과 같은 라이브러리에 의존하고 있다.

* Glib
* Pango
* ATK
* GDK
* GdkPixbuf
* Cairo

Glib는 일반적인 사용 용도의 라이브러리이다. 이것은 데이터 타입, 문자열관련 도구, 에러 처리, 메세지 추적, 쓰레드 관련 그리고 다른 유용한 프로그래밍 특빙들을 제공한다. Pango는 국제화를 가능하게 해주는 라이브러리이다. ATK는 사용자 접근을 돕는 툴킷이다. 이 툴킷은 물리적으로 사용자가 컴퓨터를 작동할 수 있도록 돕는 도구를 제공한다. GDK는 물밑에서 작동하는 그래픽 시스템에 의해서 제공되는 저수준의 그리기와 윈도우 함수와 관련된 wrapper이다. 최근에는 대부분의 이기능은 Cairo 라이브러리로 위임되었다. GdkPixbuf 라이브러리는 영상을 읽고 pixel buffer를 조작하는 툴킷이다. Cairo는 2D 벡터 그래픽을 위한 라이브러리이다. 이것은 2.8버전 이후로 GTK+에 포함되어졌다.

Gnome과 XFce 데스크탑 환경은 GTK+ 라이브러리를 사용해서 작성되었다. SWT와 wxWidgets 또한 GTK+를 사용하는 프로그래밍 frameworks로 널리 알려져있다. GTK+를 사용하는 많은 소프트웨어 응용프로그램중에 Firefox나 Inkscape이 있다.

GTK+ 응용프로그램 컴파일하기

GTK+ 응용프로그램을 컴파일하기 위해선 pkt-config라는 유용한 도구를 사용한다. pkg-config는 설치된 라이브러리의 metadata를 되돌려준다. 간단하게 말하자면, 특정 라이브러리를 사용하고자할 때, 우리가 필요로하는 include 파일과 라이브러리파일을 알려준다. pkt-config 프로그램은  특별한 metadata 파일에서 설치된 패키지에 관한 정보를 얻어온다.

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


자 그럼 여기에 어떻게 간단한 프로그램을 컴파일 할 수 있는지 보여준다. 소스코드는 simple.c라는 하나의 파일로만 구성되어 있다.

$ pkg-config --cflags gtk+-2.0
-I/usr/include/gtk-2.0 -I/usr/lib/gtk-2.0/include -I/usr/include/atk-1.0 
-I/usr/include/cairo -I/usr/include/pango-1.0 -I/usr/include/glib-2.0 
-I/usr/lib/glib-2.0/include -I/usr/include/freetype2 -I/usr/include/libpng12  


이 리스트는 GTK+ 프로그래밍에 필요한 모든 include 파일들을 보여주고 있다.

$ pkg-config --libs gtk+-2.0
-lgtk-x11-2.0 -lgdk-x11-2.0 -latk-1.0 -lgdk_pixbuf-2.0 -lm -lpangocairo-1.0 
-lfontconfig -lXext -lXrender -lXinerama -lXi -lXrandr 
-lXcursor -lXfixes -lpango-1.0 -lcairo -lX11 -lgobject-2.0 
-lgmodule-2.0 -ldl -lglib-2.0 


그리고 이 리스트는 필요한 라이브러리들을 보여주고 있다.

참고 자료

* gtk.org
* gtkforums.com
* GTK+ / Gnome application development


GTK+를 이용한 첫번째 프로그램

GTK+ 프로그래밍 사용 지침서의 이 부분에서는 GTK+로 된 첫번째 프로그램을 작성할 것이다.

간단한 예제

아주 기본 윈도우를 갖는 간단한 예제로 시작해 보자.

#include 

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

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_widget_show(window);

  gtk_main();

  return 0;
}


이 예제는 화면에 기본 윈도우를 보여줄 것이다.

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


이것은 예제를 컴파일 하는 방법을 보여준다.

gtk_init(&argc, &argv);


여기선 GTK+ 라이브러리를 초기화 한다.

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);


GtkWindow 위젯을 생성한다. 이 윈도우의 타입은  GTK_WINDOW_TOPLEVEL이다. 최상위 윈도우는 titlebar와 경계선만 가지고 있다. 이것들은 윈도우 관리자에 의해서 관리된다.

gtk_widget_show(window);


위젯을 생성한 후 보여주는 작업이 필요한다.

gtk_main();


이 코드에서는 GTK+의 메인 루프에 진입한다. 이 시점으로부터 응용프로그램은 발생하는 이벤트를 앉아서 기다리게 된다.















그림: 간단

윈도우를 화면의 중심에 놓기

우리는 윈도우 자체를 위치시킬 수 없다면, 윈도우 관리자가 우리를 위해서 위치시켜 줄 것이다. 다음 예제는 윈도우를 중심에 위치시키는 것이다.

#include 

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

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title(GTK_WINDOW(window), "Center");
  gtk_window_set_default_size(GTK_WINDOW(window), 230, 150);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_widget_show(window);

  g_signal_connect_swapped(G_OBJECT(window), "destroy",
      G_CALLBACK(gtk_main_quit), NULL);

  gtk_main();

  return 0;
}

이 예제에서 윈도우를 중심에 위치 시키고 윈도우의 타이틀과 크기를 지정한다.

gtk_window_set_title(GTK_WINDOW(window), "Center");


gtk_window_set_title() 함수는 윈도우 타이틀을 지정할 것이다. 타이틀을 지정하지 않으면, GTK+는 소스파일의 이름을 타이틀로 사용할 것이다.

gtk_window_set_default_size(GTK_WINDOW(window), 230, 150);


이 코드는 윈도우의 크기를 230x250 픽셀로 지정한다. 주의할 것은 윈도우 관리자에 의해서 제공되는 장식물을 제외한 클라이언트 영역에 대해서만 이야기 하고 있다는 것이다.

gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);


이 코드는 윈도우를 중심에 위치시킨다.

g_signal_connect_swapped(G_OBJECT(window), "destroy",
     G_CALLBACK(gtk_main_quit), NULL);


이전의 예제에서 윈도우는 x 버튼을 클릭했을 때 완벽하게 종료되지 않았다. 명령행에서 예제를 실행시켜 보면 확인해 볼 수 있을 것이다. 윈도우는 기본적으로 destroy 시그널에 반응 하지 않는다. 그래서 우리는 destroy 시그널을 gtk_main_quit()함수에 연결시켜서 응용프로그램을 분명하게 종료해주어야 한다.

응용프로그램의 아이콘

다음 예제에서 우리는 응용프로그램의 아이콘을 살펴본다. 대부분의 윈도우 관리자는 titlebar와 taskbar 의 왼쪽 구석에 아이콘을 표현한다.

#include 

GdkPixbuf *create_pixbuf(const gchar * filename)
{
   GdkPixbuf *pixbuf;
   GError *error = NULL;
   pixbuf = gdk_pixbuf_new_from_file(filename, &error);
   if(!pixbuf) {
      fprintf(stderr, "%s\n", error->message);
      g_error_free(error);
   }

   return pixbuf;
}

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

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title(GTK_WINDOW(window), "icon");
  gtk_window_set_default_size(GTK_WINDOW(window), 230, 150);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_icon(GTK_WINDOW(window), create_pixbuf("web.png"));
  gtk_widget_show(window);

  g_signal_connect_swapped(G_OBJECT(window), "destroy",
      G_CALLBACK(gtk_main_quit), NULL);

  gtk_main();

  return 0;
}


이 코드 예제는 응용프로그램 아이콘을 보여준다.

gtk_window_set_icon(GTK_WINDOW(window), create_pixbuf("web.png"));


gtk_window_set_icon()은 윈도우를 위한 아이콘을 표출한다. create_pixbuf()는 png 영상파일로부터 GdkPixbuf를 생성한다.

pixbuf = gdk_pixbuf_new_from_file(filename, &error);


문서에 따르면 gdk_pixbuf_new_from_file() 함수는 파일에서 영상을 읽어서 새로운 pixbuf를 생성한다. 파일 형식은 자동적으로 감지한다. NULL이 리턴되면, error가 설정될 것이다.



그림: 아이콘

증가하기와 감소하기

우리는 세개의 자식 위젯을 갖는 GTK+ 프로그래밍 지침서의 첫번째 장을 예제로 마무리한다.  두개의 버튼과 하나의 라벨. 라벨을 정수의 숫자를 가지고 있을 것이다. 그리고 버튼은 이 숫자를 증가시키고 감소시킬 것이다.
#include 

gint count = 0;
char buf[5];

void increase(GtkWidget *widget, gpointer label)
{
  count++;

  sprintf(buf, "%d", count);
  gtk_label_set_text(label, buf);
}

void decrease(GtkWidget *widget, gpointer label)
{
  count--;

  sprintf(buf, "%d", count);
  gtk_label_set_text(label, buf);
}

int main(int argc, char** argv) {

  GtkWidget *label;
  GtkWidget *window;
  GtkWidget *frame;
  GtkWidget *plus;
  GtkWidget *minus;

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 250, 180);
  gtk_window_set_title(GTK_WINDOW(window), "+-");

  frame = gtk_fixed_new();
  gtk_container_add(GTK_CONTAINER(window), frame);

  plus = gtk_button_new_with_label("+");
  gtk_widget_set_size_request(plus, 80, 35);
  gtk_fixed_put(GTK_FIXED(frame), plus, 50, 20);

  minus = gtk_button_new_with_label("-");
  gtk_widget_set_size_request(minus, 80, 35);
  gtk_fixed_put(GTK_FIXED(frame), minus, 50, 80);

  label = gtk_label_new("0");
  gtk_fixed_put(GTK_FIXED(frame), label, 190, 58); 

  gtk_widget_show_all(window);

  g_signal_connect(window, "destroy",
      G_CALLBACK (gtk_main_quit), NULL);

  g_signal_connect(plus, "clicked", 
      G_CALLBACK(increase), label);

  g_signal_connect(minus, "clicked", 
      G_CALLBACK(decrease), label);

  gtk_main();

  return 0;
}


이 코드 예제는 GtkLabel에 있는 값을 증가시키거나 감소시킨다.

g_signal_connect(plus, "clicked", 
     G_CALLBACK(increase), label);


우리는 increase() 콜백함수를 plus 버튼에 연결한다. 주의할 것은 콜백함수에 매개변수로 label을 보낸다는 것이다. 콜백함수 내부에서 이 label을 가지고 작업하게 될 것이다.
count++;

 sprintf(buf, "%d", count);
 gtk_label_set_text(label, buf);


increase 콜백함수 내부에서 우리는 counter의 값을 증가시키고, 숫자 값을 textual 데이터로 만들고 label을 업데이트한다.














그림 : 증가하기 -  감소하기

이 장에서 우리는 약간의 간단한 GTK+ 프로그램을 소개했다.


GTK+에서의 메뉴와 툴바

GTK+ 프로그램 지침서의 이 장에서 우리는 메뉴와 툴바를 다루어 볼것이다.

menubar는 GUI 응용프로그램의 매우 공통적인 부분 중 하나이다. 이것은 다양한 메뉴에 위치한 명령어의 모음이다. 콘솔 응용프로그램에서는 모든 이런 비밀의 명령을 기억해야하는 반면에 여기선 대부분의 명령어의 모음을 논리적인 부분으로 다룬다. 새로운 프로그램을 습득하는데 드는 시간을 많이 만축시키주는 승인된 표준이다.

간단한 메뉴 예제

우리의 첫번째 예제에서 하나의 파일 메뉴를 갖는 menubar를 만들어 볼 것이다. 이 메뉴는 하나의 메뉴 항목만 갖는다. 이 항목을 선택하면 응용프로그램은 종료된다.

#include 


int main( int argc, char *argv[])
{

  GtkWidget *window;
  GtkWidget *vbox;

  GtkWidget *menubar;
  GtkWidget *filemenu;
  GtkWidget *file;
  GtkWidget *quit;

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 250, 200);
  gtk_window_set_title(GTK_WINDOW(window), "menu");

  vbox = gtk_vbox_new(FALSE, 0);
  gtk_container_add(GTK_CONTAINER(window), vbox);

  menubar = gtk_menu_bar_new();
  filemenu = gtk_menu_new();

  file = gtk_menu_item_new_with_label("File");
  quit = gtk_menu_item_new_with_label("Quit");

  gtk_menu_item_set_submenu(GTK_MENU_ITEM(file), filemenu);
  gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), quit);
  gtk_menu_shell_append(GTK_MENU_SHELL(menubar), file);
  gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 3);

  g_signal_connect_swapped(G_OBJECT(window), "destroy",
        G_CALLBACK(gtk_main_quit), NULL);

  g_signal_connect(G_OBJECT(quit), "activate",
        G_CALLBACK(gtk_main_quit), NULL);

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}



menubar를 생성하는 것은 약간 혼란 스럽다. menubar와 메뉴 둘다 menu shell이라 불리는 동일한 위젯에서 생성되는 것을 유념하자.  메뉴 항목들은 메뉴를 위한 하위 개념으로써만 유용하다. 그들은 서브메뉴를 구현할때도 사용되어진다.

menubar = gtk_menu_bar_new();
filemenu = gtk_menu_new();


 이 코드에서 우리는 메뉴와 menubar를 생성한다.

gtk_menu_item_set_submenu(GTK_MENU_ITEM(file), filemenu);


이 코드 라인은  파일 메뉴를 구현한다. 그 내부는 menubar가 menu shell로 되어 있다. 파일 메뉴는 또한 menu shell이기도 한다. 이것이 우리가 파일 메뉴를 서브메뉴나 subshell로 보는 이유다.

gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), quit);
 gtk_menu_shell_append(GTK_MENU_SHELL(menubar), file);


 메뉴 항목들은 gtk_menu_shell_append()함수를 호출해서 구현된다. 메뉴 항목들은 menu shell에 덧 붙여진다. 우리의 경우에서는 quit 메뉴 항목은 file 메뉴에 덧 붙여지고 file 메뉴는 menubar에 덧붙여진다.

g_signal_connect(G_OBJECT(quit), "activate",
      G_CALLBACK(gtk_main_quit), NULL);


quit 메뉴항목을 선택함으로써 응용프로그램은 종료된다.















그림: 간단한 메뉴

그림 메뉴, mnemonics과 accelerators

다음 예제에서 우리는 GTK+에서 사용할 수 있는 기능들을 좀더 탐색해 볼 것이다. Accelerators는 메뉴 항목을 할성화하기위한 키보드 단축기이다. Mnemonics는 GUI 항목을 위한 키보드 단축이다. 이 둘 모두 밑줄 쳐진 문자로 표현된다.

주의할 것은 Gnome 이 메뉴 영상을 보여주도록 설정되어 있는 않을 수도 있다는 것이다. 메뉴 영상 끄거나 켜기 위해선 gconf-editor를 실행하고 /desktop/gnome/interface/menus_have_icons로 가서 옵션을 설정해야한다.

#include 
#include 


int main( int argc, char *argv[])
{

  GtkWidget *window;
  GtkWidget *vbox;

  GtkWidget *menubar;
  GtkWidget *filemenu;
  GtkWidget *file;
  GtkWidget *new;
  GtkWidget *open;
  GtkWidget *quit;

  GtkWidget *sep;

  GtkAccelGroup *accel_group = NULL;

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 250, 200);
  gtk_window_set_title(GTK_WINDOW(window), "menu");

  vbox = gtk_vbox_new(FALSE, 0);
  gtk_container_add(GTK_CONTAINER(window), vbox);

  menubar = gtk_menu_bar_new();
  filemenu = gtk_menu_new();

  accel_group = gtk_accel_group_new();
  gtk_window_add_accel_group(GTK_WINDOW(window), accel_group);

  file = gtk_menu_item_new_with_mnemonic("_File");
  new = gtk_image_menu_item_new_from_stock(GTK_STOCK_NEW, NULL);
  open = gtk_image_menu_item_new_from_stock(GTK_STOCK_OPEN, NULL);
  sep = gtk_separator_menu_item_new();
  quit = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, accel_group);

  gtk_widget_add_accelerator(quit, "activate", accel_group, 
      GDK_q, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE); 


  gtk_menu_item_set_submenu(GTK_MENU_ITEM(file), filemenu);
  gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), new);
  gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), open);
  gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), sep);
  gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), quit);
  gtk_menu_shell_append(GTK_MENU_SHELL(menubar), file);
  gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 3);

  g_signal_connect_swapped(G_OBJECT(window), "destroy",
      G_CALLBACK(gtk_main_quit), NULL);

  g_signal_connect(G_OBJECT(quit), "activate",
      G_CALLBACK(gtk_main_quit), NULL);

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}



 이 예제는 GTK+응용 프로그램에서 어떻게 영상을 메뉴 항목에 추가하는지, accelerator를 설정하고, mnemonics를 사용하는지를 보여준다.

accel_group = gtk_accel_group_new();
gtk_window_add_accel_group(GTK_WINDOW(window), accel_group);
...
quit = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, accel_group);
gtk_widget_add_accelerator(quit, "activate", accel_group, 
    GDK_q, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE); 


 accelerator 그룹은 키보드 accelerator의 그룹이다. 일반적으로 toplevel 윈도우에 붙여서 사용한다. 여기서 Ctrl+q 라는 키보드 accelerator를 생성한다.

file = gtk_menu_item_new_with_mnemonic("_File");


mnemonic을 생성하기 위해서 우리는 gtk_menu_item_new_with_mnemonic() 함수를 호출한다. Alt-F를 눌려서 file 메뉴 항목을 선택한다.

new = gtk_image_menu_item_new_from_stock(GTK_STOCK_NEW, NULL);
open = gtk_image_menu_item_new_from_stock(GTK_STOCK_OPEN, NULL);


 여기서 우리는 두개의 영상 메뉴 항목을 생성한다. 함수의 두번째 매개변수를 NULL로 설정해서 자동적으로 accelerator를 생성한다. 우리는 GTK+의 내부 리소스로부터 메뉴항목을 위한 영상과 문자 제공한다.

sep = gtk_separator_menu_item_new();


메뉴 항목들은 수평 분리자로 분리될 수 있다. 이 방법으로 우리는 메뉴 항목을 logical 그룹에 넣을 수 있다.















그림: 메뉴 예제

메뉴 항목 체크

GtkCheckMenuItem은 체크박스를 위한 메뉴 항목이다.

#include 


void toggle_statusbar(GtkWidget *widget, gpointer statusbar) 
{
  if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) {
    gtk_widget_show(statusbar);
  } else {
    gtk_widget_hide(statusbar);
  }
}


int main( int argc, char *argv[])
{

  GtkWidget *window;
  GtkWidget *vbox;

  GtkWidget *menubar;
  GtkWidget *viewmenu;
  GtkWidget *view;
  GtkWidget *tog_stat;
  GtkWidget *statusbar;
  

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 250, 200);
  gtk_window_set_title(GTK_WINDOW(window), "view statusbar");

  vbox = gtk_vbox_new(FALSE, 0);
  gtk_container_add(GTK_CONTAINER(window), vbox);

  menubar = gtk_menu_bar_new();
  viewmenu = gtk_menu_new();

  view = gtk_menu_item_new_with_label("View");
  tog_stat = gtk_check_menu_item_new_with_label("View Statusbar");
  gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(tog_stat), TRUE);

  gtk_menu_item_set_submenu(GTK_MENU_ITEM(view), viewmenu);
  gtk_menu_shell_append(GTK_MENU_SHELL(viewmenu), tog_stat);
  gtk_menu_shell_append(GTK_MENU_SHELL(menubar), view);
  gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 3);

  statusbar = gtk_statusbar_new();
  gtk_box_pack_end(GTK_BOX(vbox), statusbar, FALSE, TRUE, 1);

  g_signal_connect_swapped(G_OBJECT(window), "destroy",
        G_CALLBACK(gtk_main_quit), NULL);

  g_signal_connect(G_OBJECT(tog_stat), "activate", 
        G_CALLBACK(toggle_statusbar), statusbar);

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}



 예제에서 우리는 체크메뉴 항목을 보여준다. 체크박스가 활성화 된다면, startusbar 위젯은 보여진다. 그렇지 않다면 statusbar는 감추어진다.

tog_stat = gtk_check_menu_item_new_with_label("View Statusbar");


gtk_check_menu_item_new_with_label()함수 호출은 새로운 체크 메뉴 항목을 생성한다.

if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) {
   gtk_widget_show(statusbar);
 } else {
   gtk_widget_hide(statusbar);
 }


메뉴 항목에서 체크박스를 활성화 하면, 우리는 statusbar 위젯을 볼 수 있을 것이다. 그렇지 않으면 statusbar는 감추어진다.















그림 : 체크 메뉴 항목

툴바

메뉴 그룹은 우리가 응용 프로그램에서 사용할 수 있는 명령을 내린다. 툴바는 아주 자주 사용하는 명령에 빠르게 접근하도록 제공한다.

#include 


int main( int argc, char *argv[])
{

  GtkWidget *window;
  GtkWidget *vbox;
  
  GtkWidget *toolbar;
  GtkToolItem *new;
  GtkToolItem *open;
  GtkToolItem *save;
  GtkToolItem *sep;
  GtkToolItem *exit;


  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 250, 200);
  gtk_window_set_title(GTK_WINDOW(window), "toolbar");

  vbox = gtk_vbox_new(FALSE, 0);
  gtk_container_add(GTK_CONTAINER(window), vbox);


  toolbar = gtk_toolbar_new();
  gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);

  gtk_container_set_border_width(GTK_CONTAINER(toolbar), 2);

  new = gtk_tool_button_new_from_stock(GTK_STOCK_NEW);
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), new, -1);

  open = gtk_tool_button_new_from_stock(GTK_STOCK_OPEN);
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), open, -1);

  save = gtk_tool_button_new_from_stock(GTK_STOCK_SAVE);
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), save, -1);

  sep = gtk_separator_tool_item_new();
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), sep, -1); 

  exit = gtk_tool_button_new_from_stock(GTK_STOCK_QUIT);
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), exit, -1);

  gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 5);

  g_signal_connect(G_OBJECT(exit), "clicked", 
        G_CALLBACK(gtk_main_quit), NULL);

  g_signal_connect_swapped(G_OBJECT(window), "destroy",
        G_CALLBACK(gtk_main_quit), NULL);

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}

이 코드 예제는 간단한 툴바 예제를 생성한다.

toolbar = gtk_toolbar_new();
  gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS)



우리는 새로운 툴바를 생성하고 툴바 버튼이 아이콘만 보여주도록 설정한다. 글자 없이 말이다.

new = gtk_tool_button_new_from_stock(GTK_STOCK_NEW);
 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), new, -1);


우리는 stock으로 부터 툴바 버튼을 생성한다. 툴바 버튼은 gtk_toolbar_insert()함수 호출에 의해서 툴바에 추가된다.
sep = gtk_separator_tool_item_new();
 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), sep, -1); 


여기는 툴바에 구분자를 넣는 것이다.
















그림: 툴바

Undo redo

다음 예제는 우리가 어떻게 툴바에서 툴바 버튼을 비활성화시키는지를 보여주고 있다. 이것은 GUI 프로그래밍에서 기본적인 동작이다. 저장 버튼을 예로 들어 보자. 문서의 모든 변화를 디스크에 저장하면, 저장 버튼을 데부분의 문서 편집기에서 비활성화 된다. 이러한 방법으로 응용프로그램은 모든 변경사항들이 이미 저장되었다는 것을 사용자에세 알려준다.

#include 
#include 


void undo_redo(GtkWidget *widget,  gpointer item) 
{
  static int count = 2;
  const char *name = gtk_widget_get_name(widget);

  if ( strcmp(name, "undo") ) {
    count++;
  } else {
    count--;
  }
 
  if (count < 0) {
     gtk_widget_set_sensitive(widget, FALSE);
     gtk_widget_set_sensitive(item, TRUE);
  } 

  if (count > 5) {
     gtk_widget_set_sensitive(widget, FALSE);
     gtk_widget_set_sensitive(item, TRUE);
  }
}


int main( int argc, char *argv[])
{

  GtkWidget *window;
  GtkWidget *vbox;

  GtkWidget *toolbar;
  GtkToolItem *undo;
  GtkToolItem *redo;
  GtkToolItem *sep;
  GtkToolItem *exit;


  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 250, 200);
  gtk_window_set_title(GTK_WINDOW(window), "undoredo");

  vbox = gtk_vbox_new(FALSE, 0);
  gtk_container_add(GTK_CONTAINER(window), vbox);


  toolbar = gtk_toolbar_new();
  gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);

  gtk_container_set_border_width(GTK_CONTAINER(toolbar), 2);

  undo = gtk_tool_button_new_from_stock(GTK_STOCK_UNDO);
  gtk_widget_set_name(GTK_WIDGET(undo), "undo");
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), undo, -1);

  redo = gtk_tool_button_new_from_stock(GTK_STOCK_REDO);
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), redo, -1);

  sep = gtk_separator_tool_item_new();
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), sep, -1); 

  exit = gtk_tool_button_new_from_stock(GTK_STOCK_QUIT);
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), exit, -1);

  gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 5);


  g_signal_connect(G_OBJECT(undo), "clicked", 
        G_CALLBACK(undo_redo), redo);

  g_signal_connect(G_OBJECT(redo), "clicked", 
        G_CALLBACK(undo_redo), undo);

  g_signal_connect(G_OBJECT(exit), "clicked", 
        G_CALLBACK(gtk_main_quit), NULL);

  g_signal_connect_swapped(G_OBJECT(window), "destroy",
        G_CALLBACK(gtk_main_quit), NULL);

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}


이 예제는 GTK+ stock 리소스에서 undo , redo 버튼을 생성한다. 버튼을 각각 몇 번 클릭해서 비활성화 시키면 버튼이 회색조으로 변한다.

if (count < 0) {
    gtk_widget_set_sensitive(widget, FALSE);
    gtk_widget_set_sensitive(item, TRUE);
 } 

 if (count > 5) {
    gtk_widget_set_sensitive(widget, FALSE);
    gtk_widget_set_sensitive(item, TRUE);
 }


 gtk_widget_set_sensitive()함수 호출은 툴바 버튼을 활성/비활성화 하는데 사용되어진다.















그림 : undo redo


GTK+ 모양새 관리

이 장에서 우리는 윈도우와 다이얼로그에서 위젯을 어떻게 배치할 것인지를 보여준다.

우리가 응용프로그램의 GUI를 디자인할 때, 사용할 위젯이 무엇이고 응용프로그램에서 어떻게 배치시켜야 하는지 결정해야한다. 위젯을 배치시키기 위해서 layout container라고 불리는 특성화된 보이지 않는 위젯을 사용한다. 이 장에서 우리는 GtkAlignment, GtkFixed, GtkVBox와 GtkTable을 언급할 것이다.

GtkFixed

GtkFixed 컨테이너는 고정된 위치와 크기로 child widget에 위치한다. 이 컨테이너는 자동 모양새관리를 수행하지는 않는다. 대부분의 응용프로그램에는 이 GtkFixed 컨테이너를 사용하지는 않는다. 하지만 이 컨테이너를 사용하는 몇가지 특수한 영역이 있다. 예를 들면, 게임같은 경우, diagram을 가지고 작업하는 특화된 응용프로그램이나 spreadsheet 응용프로그램에 있는 chart와 같이 움직일 수 있는 크기를 변경할 수 있는 요소를 갖는 작은 교육용 예제들이다.

#include 


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

  GtkWidget *button1;
  GtkWidget *button2;
  GtkWidget *button3;

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title(GTK_WINDOW(window), "GtkFixed");
  gtk_window_set_default_size(GTK_WINDOW(window), 290, 200);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);

  fixed = gtk_fixed_new();
  gtk_container_add(GTK_CONTAINER(window), fixed);

  button1 = gtk_button_new_with_label("Button");
  gtk_fixed_put(GTK_FIXED(fixed), button1, 150, 50);
  gtk_widget_set_size_request(button1, 80, 35);

  button2 = gtk_button_new_with_label("Button");
  gtk_fixed_put(GTK_FIXED(fixed), button2, 15, 15);
  gtk_widget_set_size_request(button2, 80, 35);

  button3 = gtk_button_new_with_label("Button");
  gtk_fixed_put(GTK_FIXED(fixed), button3, 100, 100);
  gtk_widget_set_size_request(button3, 80, 35);

  g_signal_connect_swapped(G_OBJECT(window), "destroy", 
      G_CALLBACK(gtk_main_quit), NULL);

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}


위의 예제에서 세 개의 버튼을 생성하고 고정된 좌표에 위치 시킨다. 응용프로그램의 창을 변경시키더라도 이 버튼들의 크기와 위치는 변함이 없다.

fixed = gtk_fixed_new();


여기서 GtkFixed 컨테이너 widget이 생성된다.

gtk_fixed_put(GTK_FIXED(fixed), button1, 150, 50);


첫 번째 버튼은 좌표 x=150, y=50에 gtk_fixed_put()함수를 이용해서 위치시킨다.


그림: GtkFixed 컨테이너

GtkVBox

GtkVBox는 vertical box container (세로로 적재하기 위한 컨테이너). 이 것은 하나의 column(하나의 행)에 child widget을 위치시킨다. GtkHBox는 이와 유사한 컨테이너이다. 이 컨테이너는 하나의 row(하나의 열)에 child widget을 위치시킨다.

#include 


int main( int argc, char *argv[])
{

  GtkWidget *window;
  GtkWidget *vbox;

  GtkWidget *settings;
  GtkWidget *accounts;
  GtkWidget *loans;
  GtkWidget *cash;
  GtkWidget *debts;

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 230, 250);
  gtk_window_set_title(GTK_WINDOW(window), "GtkVBox");
  gtk_container_set_border_width(GTK_CONTAINER(window), 5);

  vbox = gtk_vbox_new(TRUE, 1);
  gtk_container_add(GTK_CONTAINER(window), vbox);

  settings = gtk_button_new_with_label("Settings");
  accounts = gtk_button_new_with_label("Accounts");
  loans = gtk_button_new_with_label("Loans");
  cash = gtk_button_new_with_label("Cash");
  debts = gtk_button_new_with_label("Debts");

  gtk_box_pack_start(GTK_BOX(vbox), settings, TRUE, TRUE, 0);
  gtk_box_pack_start(GTK_BOX(vbox), accounts, TRUE, TRUE, 0);
  gtk_box_pack_start(GTK_BOX(vbox), loans, TRUE, TRUE, 0);
  gtk_box_pack_start(GTK_BOX(vbox), cash, TRUE, TRUE, 0);
  gtk_box_pack_start(GTK_BOX(vbox), debts, TRUE, TRUE, 0);

  g_signal_connect_swapped(G_OBJECT(window), "destroy",
        G_CALLBACK(gtk_main_quit), G_OBJECT(window));

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}


이 예제는 GtkVBox의 동작을 보여준다. 하나의 column(하나의 행)에 다섯개의 버튼을 적재 시킨다. 응용프로그램의 창을 변경시키면 child widget들도 같이 크기가 변경된다.

vbox = gtk_vbox_new(TRUE, 1);


GtkVBox가 생성된다. homogeneous 매개변수를 TRUE로 설정한다. 이 의미는 모든 버튼을 같은 크기로 만들 것이라는 것을 말한다. 그리고 widget간의 공간은 1 픽셀로 설정되었다.

gtk_box_pack_start(GTK_BOX(vbox), settings, TRUE, TRUE, 0);


우리는 이 컨테이너에 setting 버튼을 적재한다. 첫 번째 두 매개변수는 컨테이너와 child widget이다. 그리고 다음 세 개의 매개변수는 expand, fill 그리고 padding이다. 주의 할 것은 fill 매개변수는 expand 매개변수가 FALSE로 설정되면 동작하지 않는다. 이와 유사하게 컨테이너를 homegeneous 매개 변수를 TRUE로 설정해서 생성하면 expand 매개변수는 작동하지 않는다.


















그림: GtkVBox 컨테이너

GtkTable

GtkTable widget은 widget을 가로나 세로로 정렬한다.

#include 


int main( int argc, char *argv[])
{

  GtkWidget *window;

  GtkWidget *table;
  GtkWidget *button;

  char *values[16] = { "7", "8", "9", "/", 
     "4", "5", "6", "*",
     "1", "2", "3", "-",
     "0", ".", "=", "+"
  };

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 250, 180);
  gtk_window_set_title(GTK_WINDOW(window), "GtkTable");

  gtk_container_set_border_width(GTK_CONTAINER(window), 5);

  table = gtk_table_new(4, 4, TRUE);
  gtk_table_set_row_spacings(GTK_TABLE(table), 2);
  gtk_table_set_col_spacings(GTK_TABLE(table), 2);

  int i = 0;
  int j = 0;
  int pos = 0;

  for( i=0; i < 4; i++) {
    for( j=0; j < 4; j++) {
      button = gtk_button_new_with_label(values[pos]);
      gtk_table_attach_defaults(GTK_TABLE(table), button, j, j+1, i, i+1 );
      pos++;
    }
  }

  gtk_container_add(GTK_CONTAINER(window), table);

  g_signal_connect_swapped(G_OBJECT(window), "destroy",
        G_CALLBACK(gtk_main_quit), G_OBJECT(window));

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}


이 예제에서 계산기에서 볼 수 있는 버튼 모음을 생성할 것이다.

table = gtk_table_new(4, 4, TRUE);


4개의 열과 4개의 행을 갖는 새로운 GtkTable widget을 생성한다.

gtk_table_set_row_spacings(GTK_TABLE(table), 2);
 gtk_table_set_col_spacings(GTK_TABLE(table), 2);


우리는 모든 열과 모든 행 사이에 어떤 공간을 설정한다.

for( i=0; i < 4; i++) {
   for( j=0; j < 4; j++) {
     button = gtk_button_new_with_label(values[pos]);
     gtk_table_attach_defaults(GTK_TABLE(table), button, j, j+1, i, i+1 );
     pos++;
   }
 }


이 코드는 16개의 버튼을 생성하고 이 컨테이너에 위치 시킬 것이다.














그림: GtkTable 컨테이너

GtkAlignment

GtkAlignment 컨테이너는 child widget의 크기와 정렬을 제어한다.

#include 


int main( int argc, char *argv[])
{

  GtkWidget *window;
  GtkWidget *ok;
  GtkWidget *close;

  GtkWidget *vbox;
  GtkWidget *hbox;
  GtkWidget *halign;
  GtkWidget *valign;

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 350, 200);
  gtk_window_set_title(GTK_WINDOW(window), "GtkAlignment");
  gtk_container_set_border_width(GTK_CONTAINER(window), 10);

  vbox = gtk_vbox_new(FALSE, 5);

  valign = gtk_alignment_new(0, 1, 0, 0);
  gtk_container_add(GTK_CONTAINER(vbox), valign);
  gtk_container_add(GTK_CONTAINER(window), vbox);

  hbox = gtk_hbox_new(TRUE, 3);

  ok = gtk_button_new_with_label("OK");
  gtk_widget_set_size_request(ok, 70, 30);
  gtk_container_add(GTK_CONTAINER(hbox), ok);
  close = gtk_button_new_with_label("Close");
  gtk_container_add(GTK_CONTAINER(hbox), close);

  halign = gtk_alignment_new(1, 0, 0, 0);
  gtk_container_add(GTK_CONTAINER(halign), hbox);

  gtk_box_pack_start(GTK_BOX(vbox), halign, FALSE, FALSE, 0);

  g_signal_connect_swapped(G_OBJECT(window), "destroy",
        G_CALLBACK(gtk_main_quit), G_OBJECT(window));

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}


이 코드 예제에서는 창의 우측 하단에 두 개의 버튼을 위치시킨다. 이를 위해서 하나의 horizontal box와 하나의 vertical box 그리고 두 개의 alignment 컨테이너를 사용한다.

valign = gtk_alignment_new(0, 1, 0, 0);


이 것은 child widget을 하단에 위치 시킬 것이다.

gtk_container_add(GTK_CONTAINER(vbox), valign);


여기선 vertical box에 alignment widget을 위치시킨다.

hbox = gtk_hbox_new(TRUE, 3);

 ok = gtk_button_new_with_label("OK");
 gtk_widget_set_size_request(ok, 70, 30);
 gtk_container_add(GTK_CONTAINER(hbox), ok);
 close = gtk_button_new_with_label("Close");
 gtk_container_add(GTK_CONTAINER(hbox), close);


horizontal box를 생성하고 두 개의 버튼을 그 안에 위치 시킨다.

halign = gtk_alignment_new(1, 0, 0, 0);
 gtk_container_add(GTK_CONTAINER(halign), hbox);

 gtk_box_pack_start(GTK_BOX(vbox), halign, FALSE, FALSE, 0);


이 것은 우측에 child widget을 위치 시킬 alignment 컨테이너를 생성할 것이다. horizontal box를 이 alignment 컨테이너에 추가하고 이 alignment 컨테이너는 vertical box에 적재한다. 우리는 alignment 컨테이너는 단 하나의 child widget밖에 갖을 수 없다는 것을 명심해야한다. 그래서 여기서 우리가 여러 개의 box를 사용해야하는 이유가 그것 때문인 것이다.













그림: GtkAlignment 컨테이너

Windows

다음은 좀 더 확장된 예제를 생성하게 될 것이다. JDeveloper IDE에서 찾아 볼 수 있는 윈도우를 살펴볼 것이다.

















그림: JDeveloper에서의 Window dialog

이 다이얼로그는 JDeveloper 응용프로그램에서 열려 있는 모든 윈도우들을 좀더 정확히 이야기 하자면 Tab들을 보여준다.

#include 


int main( int argc, char *argv[])
{

  GtkWidget *window;
  GtkWidget *table;

  GtkWidget *title;
  GtkWidget *activate;
  GtkWidget *halign;
  GtkWidget *halign2;

  GtkWidget *valign;
  GtkWidget *close;
  GtkWidget *wins;

  GtkWidget *help;
  GtkWidget *ok;

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_widget_set_size_request (window, 300, 250);
  gtk_window_set_resizable(GTK_WINDOW(window), FALSE);

  gtk_window_set_title(GTK_WINDOW(window), "Windows");

  gtk_container_set_border_width(GTK_CONTAINER(window), 15);

  table = gtk_table_new(8, 4, FALSE);
  gtk_table_set_col_spacings(GTK_TABLE(table), 3);

  title = gtk_label_new("Windows");
  halign = gtk_alignment_new(0, 0, 0, 0);
  gtk_container_add(GTK_CONTAINER(halign), title);
  gtk_table_attach(GTK_TABLE(table), halign, 0, 1, 0, 1, 
      GTK_FILL, GTK_FILL, 0, 0);

  wins = gtk_text_view_new();
  gtk_text_view_set_editable(GTK_TEXT_VIEW(wins), FALSE);
  gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(wins), FALSE);
  gtk_table_attach(GTK_TABLE(table), wins, 0, 2, 1, 3,
      GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 1, 1);

  activate = gtk_button_new_with_label("Activate");
  gtk_widget_set_size_request(activate, 50, 30);
  gtk_table_attach(GTK_TABLE(table), activate, 3, 4, 1, 2, 
      GTK_FILL, GTK_SHRINK, 1, 1);

  valign = gtk_alignment_new(0, 0, 0, 0);
  close = gtk_button_new_with_label("Close");
 
  gtk_widget_set_size_request(close, 70, 30);
  gtk_container_add(GTK_CONTAINER(valign), close);
  gtk_table_set_row_spacing(GTK_TABLE(table), 1, 3);
  gtk_table_attach(GTK_TABLE(table), valign, 3, 4, 2, 3, 
      GTK_FILL, GTK_FILL | GTK_EXPAND, 1, 1);

  halign2 = gtk_alignment_new(0, 1, 0, 0);
  help = gtk_button_new_with_label("Help");
  gtk_container_add(GTK_CONTAINER(halign2), help);
  gtk_widget_set_size_request(help, 70, 30);
  gtk_table_set_row_spacing(GTK_TABLE(table), 3, 6);
  gtk_table_attach(GTK_TABLE(table), halign2, 0, 1, 4, 5, 
      GTK_FILL, GTK_FILL, 0, 0);

  ok = gtk_button_new_with_label("OK");
  gtk_widget_set_size_request(ok, 70, 30);
  gtk_table_attach(GTK_TABLE(table), ok, 3, 4, 4, 5, 
      GTK_FILL, GTK_FILL, 0, 0);

  gtk_container_add(GTK_CONTAINER(window), table);

  g_signal_connect_swapped(G_OBJECT(window), "destroy",
        G_CALLBACK(gtk_main_quit), G_OBJECT(window));

  gtk_widget_show_all(window);
  gtk_main();

  return 0;
}


이 코드는 어떻게 GTK+에서 유사한 윈도우를 생성할 수 있는지 보여준다.

table = gtk_table_new(8, 4, FALSE);


table 컨테이너 widget을 사용한다.

title = gtk_label_new("Windows");
 halign = gtk_alignment_new(0, 0, 0, 0);
 gtk_container_add(GTK_CONTAINER(halign), title);
 gtk_table_attach(GTK_TABLE(table), halign, 0, 1, 0, 1, 
     GTK_FILL, GTK_FILL, 0, 0);


이 코드는 왼쪽으로 정렬된 label을 생성한다. 이 label은 GtkTable 컨테이너의 첫 번째 열에 위치한다.

wins = gtk_text_view_new();
 gtk_text_view_set_editable(GTK_TEXT_VIEW(wins), FALSE);
 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(wins), FALSE);
 gtk_table_attach(GTK_TABLE(table), wins, 0, 2, 1, 3, 
     GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 1, 1);


text view widget은 두 개의 열과 두 개의 행을 차지하게 된다. 이 widget은 편집 불가능하게 만들고 cursor는 숨긴다.

valign = gtk_alignment_new(0, 0, 0, 0);
 close = gtk_button_new_with_label("Close");
 
 gtk_widget_set_size_request(close, 70, 30);
 gtk_container_add(GTK_CONTAINER(valign), close);
 gtk_table_set_row_spacing(GTK_TABLE(table), 1, 3);
 gtk_table_attach(GTK_TABLE(table), valign, 3, 4, 2, 3, 
     GTK_FILL, GTK_FILL | GTK_EXPAND, 1, 1);


close button은 text view widget 옆의 4번째 column에 배치시킨다. (숫자 매기기는 0부터 시작한다.)  이 button은 alignment widget에 추가한다. 맨 위쪽에 위치 시킬 수 있도록 하기위해서,


















그림: windows

GTK+ events and signals


GTK+ 프로그래밍 도움말의 이번 부분에서는 GTK+ 라이브러리의 event system에 대해서 이야기 할 것이다. GTK+ 라이브러리는 event 기반으로 운용되는 system이다. 모든 GUI 응용프로그램은 event로 운용된다. 응용프로그램은 새롭게 생성되는 event들에 대해서 계속적으로 검사하는 main loop를 시작한다. 만약 아무런 event가 없다면, 응용프로그램은 대기 하면서 아무런 동작도 하지 않는다. GTK+에서 event는 X server로부터의 message이다. event가 widget에 도달할때 signal을 방출하면서 이 event에 대해서 반응한다. GTK+ 프로그래머는 signal에 특정 callback함수를 연결할 수 있다. 이 callback 함수는 signal에 반응하는 handler 함수이다.

#include 

void button_clicked(GtkWidget *widget, gpointer data)
{
  g_print("clicked\n");
}

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

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title(GTK_WINDOW(window), "GtkButton");
  gtk_window_set_default_size(GTK_WINDOW(window), 230, 150);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);

  fixed = gtk_fixed_new();
  gtk_container_add(GTK_CONTAINER(window), fixed);

  button = gtk_button_new_with_label("Click");
  gtk_fixed_put(GTK_FIXED(fixed), button, 50, 50);
  gtk_widget_set_size_request(button, 80, 35);

  g_signal_connect(G_OBJECT(button), "clicked", 
      G_CALLBACK(button_clicked), NULL);

  g_signal_connect(G_OBJECT(window), "destroy", 
      G_CALLBACK(gtk_main_quit), NULL);

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}


이 응용프로그램에서 두 개의 signal을 다룬다. clicked signal과 destroy signal.

g_signal_connect(G_OBJECT(button), "clicked", 
     G_CALLBACK(button_clicked), NULL);


g_signal_connect() 함수는 clicked signal을 button_clicked() callback 함수에 연결한다.

void button_clicked(GtkWidget *widget, gpointer data)
 {
   g_print("clicked\n");
 }


이 callback 함수는 "clicked" 문자를 console에 출력할 것이다.  callback 함수의 첫 번째 매개변수는 signal을 방출하는 object이다. 이 경우에는 Click button이 된다. 두 번째 매개변수는 선택사항이다. 어떤 데이터라도 callback 함수에 전달 할 수 있을 것이다. 이 경우에는 어떤 데이터도 전달하지 않는다. g_signal_connect() 함수에서 NULL 매개변수를 전달한다.

g_signal_connect(G_OBJECT(window), "destroy", 
      G_CALLBACK(gtk_main_quit), NULL);


titlebar의 오른쪽 구석에 위치한 x button을 누르거나 Alt+F4를 누르면, destroy signal을 방출한다. 그리고 응용프로그램을 종료하는 gtk_main_quit() 함수를 호출한다.

Moving window

다음 예제는 어떻게 window의 move event에 반응하는지를 보여준다.

#include 

void frame_callback(GtkWindow *window, 
      GdkEvent *event, gpointer data)
{
   int x, y;
   char buf[10];
   x = event->configure.x;
   y = event->configure.y;
   sprintf(buf, "%d, %d", x, y);
   gtk_window_set_title(window, buf);
}


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

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 230, 150);
  gtk_window_set_title(GTK_WINDOW(window), "Simple");
  gtk_widget_add_events(GTK_WIDGET(window), GDK_CONFIGURE);

  g_signal_connect_swapped(G_OBJECT(window), "destroy",
        G_CALLBACK(gtk_main_quit), G_OBJECT(window));

  g_signal_connect(G_OBJECT(window), "configure-event",
        G_CALLBACK(frame_callback), NULL);

  gtk_widget_show(window);
  gtk_main();

  return 0;
}


위의 예제에서는 titlebar에 윈도우의 윗쪽 왼쪽 구석의 현재 위치를 보여준다.

gtk_widget_add_events(GTK_WIDGET(window), GDK_CONFIGURE);


widget의 event mask는 어떤 종류의 event가 특정 widget에서 처리될 것인지 결정한다. 어떤 event는 사전에 설정되어 있고, 그렇지 않은 종류의 event는 사후에 event mask에 추가되어져야 한다. gtk_widget_add_events() 함수는 GDK_CONFIGURE의 event 타입을 mask에 추가한다. GDK_CONFIGURE의 event type은  모든 크기, 위치, 스택 순서 event를 차지한다.

g_signal_connect(G_OBJECT(window), "configure-event",
     G_CALLBACK(frame_callback), NULL);


configure-event는 크기, 위치와 스텍의 순서 event들을 제공한다.

void frame_callback(GtkWindow *window, 
     GdkEvent *event, gpointer data)
 {
   int x, y;
   char buf[10];
   x = event->configure.x;
   y = event->configure.y;
   sprintf(buf, "%d, %d", x, y);
   gtk_window_set_title(window, buf);
 }


callback 함수는 세 개의 매개변수를 가진다. 그 object는 특정 signal, GdkEvent와 추가적인 data를 생성한다. 여기서는 그저 x, y 좌표를 결정하고 title에 출력한다.












그림: Move event

The enter signal

다음 예제는 enter signal에 어떻게 반응해야하는지를 보여준다. 마우스 위치가 widget위 영역에 들어 가면 enter signal은 발생된다.

#include 


void enter_button(GtkWidget *widget, gpointer data) 
{ 
  GdkColor color;
  color.red = 27000;
  color.green = 30325;
  color.blue = 34181;
  gtk_widget_modify_bg(widget, GTK_STATE_PRELIGHT, &color);
}


int main( int argc, char *argv[])
{

  GtkWidget *window;
  GtkWidget *fixed;
  GtkWidget *button;

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 230, 150);
  gtk_window_set_title(GTK_WINDOW(window), "enter signal");

  fixed = gtk_fixed_new();
  gtk_container_add(GTK_CONTAINER(window), fixed);

  button = gtk_button_new_with_label("Button");
  gtk_widget_set_size_request(button, 80, 35);
  gtk_fixed_put(GTK_FIXED(fixed), button, 50, 50);

  g_signal_connect(G_OBJECT(button), "enter", 
      G_CALLBACK(enter_button), NULL);

  g_signal_connect_swapped(G_OBJECT(window), "destroy",
      G_CALLBACK(gtk_main_quit), NULL);

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}


여기서 button widget위에 마우스 포인터를 위치시키면 button widget위 배경색을 변경될 것이다.

g_signal_connect(G_OBJECT(button), "enter", 
    G_CALLBACK(enter_button), NULL);


여기서, enter signal이 발생하면 enter_button() 사용자 함수가 호출된다.

GdkColor color;
 color.red = 27000;
 color.green = 30325;
 color.blue = 34181;
 gtk_widget_modify_bg(widget, GTK_STATE_PRELIGHT, &color);


callback함수 내부에서 gtk_widget_modify_bg()함수를 호출해서 button의 배경을 변경한다.

Disconnectiong a callback

signal에 연결된 callback함수를 연결해제 할 수 도 있다. 다음 예제 코드는 이런 케이스를 보여준다.

#include 


int handler_id;

void button_clicked(GtkWidget *widget, gpointer data) 
{ 
  g_print("clicked\n");
}

void toogle_signal(GtkWidget *widget, gpointer window)
{
  if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
     handler_id = g_signal_connect(G_OBJECT(window), "clicked", 
           G_CALLBACK(button_clicked), NULL);
  } else {
     g_signal_handler_disconnect(window, handler_id);
  }
}


int main( int argc, char *argv[])
{

  GtkWidget *window;
  GtkWidget *fixed;
  GtkWidget *button;
  GtkWidget *check;


  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 250, 150);
  gtk_window_set_title(GTK_WINDOW(window), "Disconnect");

  fixed = gtk_fixed_new();
  gtk_container_add(GTK_CONTAINER(window), fixed);

  button = gtk_button_new_with_label("Click");
  gtk_widget_set_size_request(button, 80, 30);
  gtk_fixed_put(GTK_FIXED(fixed), button, 30, 50);

  check = gtk_check_button_new_with_label("Connect");
  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), TRUE);
  gtk_fixed_put(GTK_FIXED(fixed), check, 130, 50);

  handler_id = g_signal_connect(G_OBJECT(button), "clicked", 
        G_CALLBACK(button_clicked), NULL);

  g_signal_connect(G_OBJECT(check), "clicked",
        G_CALLBACK(toogle_signal), (gpointer) button);

  g_signal_connect_swapped(G_OBJECT(window), "destroy",
        G_CALLBACK(gtk_main_quit), NULL);

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}



이 코드 예제에선 버튼과 체크 박스를 가지고 있다. 체크박스는 button의 clicked signal에 해당하는 callback함수에 연결되거나 그렇지 않게 되어 있다.

handler_id = g_signal_connect(G_OBJECT(button), "clicked", 
     G_CALLBACK(button_clicked), NULL);


g_signal_connect() 함수는 callback 함수를 구분 짓는 handler id를 되돌려 준다.

if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
    handler_id = g_signal_connect(G_OBJECT(window), "clicked", 
          G_CALLBACK(button_clicked), NULL);
 } else {
    g_signal_handler_disconnect(window, handler_id);
 }


이 코드는 체크박스의 상태를 결정한다. 체크가 선택되거나 그렇지 않을 경우의 callback함수를 연결시켜준다.












그림: Disconnect

Drag and Drop 예제

다음 예제에서 흥미로운 것을 보여 줄 것이다. 경계가 없는 윈도우와 그런 윈도우를 어떻게 움직이고 드레그 할 수 있는지를 보여줄 것이다.

#include 

gboolean on_button_press (GtkWidget* widget,
  GdkEventButton * event, GdkWindowEdge edge)
{
  if (event->type == GDK_BUTTON_PRESS)
  {
    if (event->button == 1) {
      gtk_window_begin_move_drag(GTK_WINDOW(gtk_widget_get_toplevel(widget)),
          event->button,
   event->x_root,
   event->y_root,
   event->time);
    }
  }

  return FALSE;
}


int main( int argc, char *argv[])
{

  GtkWidget *window;

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 230, 150);
  gtk_window_set_title(GTK_WINDOW(window), "Drag & drop");
  gtk_window_set_decorated(GTK_WINDOW (window), FALSE);
  gtk_widget_add_events(window, GDK_BUTTON_PRESS_MASK);

  g_signal_connect(G_OBJECT(window), "button-press-event",
      G_CALLBACK(on_button_press), NULL);

  g_signal_connect_swapped(G_OBJECT(window), "destroy",
        G_CALLBACK(gtk_main_quit), G_OBJECT(window));

  gtk_widget_show(window);

  gtk_main();

  return 0;
}


이 예제는 경계가 없는 윈도우의 drag와 drop을 보여준다.

gtk_window_set_decorated(GTK_WINDOW (window), FALSE);


윈도우의 기타 요소들을 움직인다. 이것은 윈도우가 경계와 titlebar를 가지지 안을 것이라는것을 의미한다.

g_signal_connect(G_OBJECT(window), "button-press-event",
      G_CALLBACK(on_button_press), NULL);


이것은 윈도우의 button-press-event 시그날을 연결시켜준다.

gboolean on_button_press (GtkWidget* widget,
  GdkEventButton * event, GdkWindowEdge edge)
{
  if (event->type == GDK_BUTTON_PRESS)
  {
    if (event->button == 1) {
      gtk_window_begin_move_drag(GTK_WINDOW(gtk_widget_get_toplevel(widget)),
          event->button,
   event->x_root,
   event->y_root,
   event->time);
    }
  }

  return FALSE;
}


on_button_press() 함수의 내부에서는 drag와 drop의 동작을 구현한다. 마우스의 우측 버튼이 눌렸는지 않은지를 검사한다. 그리고 나서 gtk_window_begin_move_drag() 함수를 호출한다.

A timer example

다음 예제는 timer 예제를 보여준다. Timer는 어떤 반복적인 작업을 가지고 있을 때 사용된다.  그건 clock이거나 count down 이거나 visual 효과거나 animation일 수 있다.

#include 
#include 
#include 

댓글 1개: