본문 바로가기
오픈소스 읽기 (OLD)/리눅스 어플리케이션 만들기 - Gtk4

3-2. 간단한 어플리케이션 만들기 (Container)

by 커널패닉 2021. 4. 20.
반응형

앞선 포스트에서는 윈도우에 하나의 위젯을 배치하는 방법에 대해서 살펴보았다. 그런데 우리가 사용하는 대부분의 어플리케이션은 여러개의 위젯으로 구성되어 있다. 이처럼 윈도우에 여러개의 위젯을 배치하고 싶을 때 사용하는 위젯이 Container이다. Container 위젯들은 여러개의 자식 위젯들을 가질 수 있으며, 위젯에 따라서 다양한 유형으로 자식 위젯들을 보여준다. 이번 포스트에서는 가장 많이 사용되는 Container 위젯들인 GtkBox, GtkGrid, GtkNotebook에 대해서 다룬다.

 

3.2.1 한 줄로 줄세울때는 - Box

Box는 수평 혹은 수직으로 연속된 방향으로 위젯을 배치할 수 있게 해주는 Conatiner이다. 다음 코드와 결과물을 보면서 Box가 어떻게 사용되는지 알아보자.

// gtk-example.c

#include <gtk/gtk.h>

static void clicked(GtkButton* button, gpointer user_data) {
    gtk_button_set_label(button, "Clicked");
}

static void activate(GtkApplication* app, gpointer user_data) {
    GtkWidget *window;
    GtkWidget *box;
    GtkWidget *label;
    GtkWidget *button;

    window = gtk_application_window_new(app);
    gtk_window_set_title(GTK_WINDOW(window), "Window");
    gtk_window_set_default_size(GTK_WINDOW(window), 200, 200);

    /*
     * 컨테이너 크기에 위젯을 맞춘다.
     */
    label = gtk_label_new("I am label");
    gtk_widget_set_vexpand(label, true);
    button = gtk_button_new_with_label("I am button");
    gtk_widget_set_vexpand(button, true);
    g_signal_connect(button, "clicked", G_CALLBACK(clicked), NULL);

    /*
     * 가로 박스 혹은 세로 박스를 만든다.
     */
    int spacing = 10;
    box = gtk_box_new(GTK_ORIENTATION_VERTICAL, spacing);
    gtk_widget_set_vexpand(box, true);
    /*
     * Box에 label, button 위젯을 추가한다.
     */
    gtk_box_append(GTK_BOX(box), label);
    gtk_box_append(GTK_BOX(box), button);

    gtk_window_set_child(GTK_WINDOW(window), box);
    gtk_widget_show(window);
}

int main(int argc, char **argv) {
    GtkApplication *app;
    int status;

    app = gtk_application_new("kr.kernelpanic.gtkexample", G_APPLICATION_FLAGS_NONE);

    g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);

    status = g_application_run(G_APPLICATION(app), argc, argv);
    g_object_unref(app);

    return status;
}

위 코드는 아래 명령어로 컴파일 가능하다.

$ gcc `pkg-config --libs --cflags gtk4` gtk-example.c -o example
$ ./example

실행하면 아래와 같이 label과 box가 같이 있는 창이 생성된다.

 

3.2.2 바둑판 배열을 만들때는 - Grid

Grid는 바둑판식으로 위젯을 배치하는 컨테이너이다. Box는 가로 혹은 세로 한 방향으로만 위젯을 배치할 수 있는 반면에 Grid는 가로와 세로, 즉 2차원으로 위젯 배치가 가능하다. 아래는 Box와 Grid의 개념을 비교한 그림이다.

Grid를 사용하는 방법은 다음과 같다.

// ... <생략> ...

static void activate(GtkApplication* app, gpointer user_data) {
    GtkWidget *window;
    GtkWidget *grid;
    GtkWidget *button_1_1;
    GtkWidget *button_2_1;
    GtkWidget *button_2_2;
    GtkWidget *button_1_2;

    window = gtk_application_window_new(app);
    gtk_window_set_title(GTK_WINDOW(window), "Window");
    gtk_window_set_default_size(GTK_WINDOW(window), 200, 200);

    button_1_1 = gtk_button_new_with_label("1 X 1");
    button_1_2 = gtk_button_new_with_label("1 X 2");
    button_2_2 = gtk_button_new_with_label("2 X 2");
    button_2_1 = gtk_button_new_with_label("2 X 1");

    guint spacing = 2;
    /*
     * Grid를 생성한다.
     */
    grid = gtk_grid_new ();
    /*
     * Grid의 격차 크기의 비율을 동일하게 만든다. 예를 들어서 1 X 2 크기의 Grid는 
     * 1 X 1 크기의 Grid 보다 2배 더 크다.
     */
    gtk_grid_set_row_homogeneous(GTK_GRID(grid), true);
    gtk_grid_set_column_homogeneous(GTK_GRID(grid), true);
    gtk_grid_set_row_spacing(GTK_GRID(grid), spacing);
    gtk_grid_set_column_spacing(GTK_GRID(grid), spacing);

    /*
     * Grid에 Widget을 배치하는 함수이다. 숫자 부분은 순서대로,
     * 배치하려는 X 좌표, Y 좌표, X축으로 차지하는 Grid 개수, Y축으로 차지하는 Grid 개수를 의미한다.
     */
    gtk_grid_attach(GTK_GRID(grid), button_1_1, 0, 0, 1, 1);
    gtk_grid_attach(GTK_GRID(grid), button_1_2, 2, 1, 1, 2);
    gtk_grid_attach(GTK_GRID(grid), button_2_2, 0, 1, 2, 2);
    gtk_grid_attach(GTK_GRID(grid), button_2_1, 1, 0, 2, 1);

    gtk_window_set_child(GTK_WINDOW(window), grid);
    gtk_widget_show(window);
}

// ... <생략> ...

컴파일해서 실행한 결과는 다음과 같다.

 

3.2.3 여러개의 탭을 만들때는 - Notebook

Notebook은 개인적으로 즐겨쓰는 기능인데, tutorial에는 없어서 추가해 보았다. Notebook을 사용하는 탭으로 구분되는 여러개의 페이지를 사용할 수 있다. 작성하는 코드는 다음과 같다.

// ... <생략> ...

static void activate(GtkApplication* app, gpointer user_data) {
    GtkWidget *window;
    GtkWidget *notebook;
    GtkWidget *label_1;
    GtkWidget *label_2;
    GtkWidget *label_3;
    GtkWidget *tab_label_1;
    GtkWidget *tab_label_2;
    GtkWidget *tab_label_3;

    window = gtk_application_window_new(app);
    gtk_window_set_title(GTK_WINDOW(window), "Window");
    gtk_window_set_default_size(GTK_WINDOW(window), 200, 200);

    label_1 = gtk_label_new("This is page 1");
    label_2 = gtk_label_new("This is page 2");
    label_3 = gtk_label_new("This is page 3");
    tab_label_1 = gtk_label_new("Tab 1");
    tab_label_2 = gtk_label_new("Tab 2");
    tab_label_3 = gtk_label_new("Tab 3");

    /*
     * Notebook에는 child Widget과 tab Widget이 등록된다.
     * child Widget은 해당 탭의 컨텐츠이고, tab Widget은 Tab에 표시되는 텍스트 라벨이다.
     * gtk_notebook_append_page에서 2번째 인자가 child Widget,
     * 3번째 인자가 tab Widget이다.
     */
    notebook = gtk_notebook_new ();
    gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP);
    gtk_notebook_append_page(GTK_NOTEBOOK(notebook), label_1, tab_label_1);
    gtk_notebook_append_page(GTK_NOTEBOOK(notebook), label_2, tab_label_2);
    gtk_notebook_append_page(GTK_NOTEBOOK(notebook), label_3, tab_label_3);

    gtk_window_set_child(GTK_WINDOW(window), notebook);
    gtk_widget_show(window);
}

// ... <생략> ...

이를 실행하면 다음과 같은 창일 생성된다. 상단의 탭을 클릭하면, 다른 페이지로 넘어간다.

 

3.2.4 그 외의 Container 위젯들

여기에서 소개한 Container 위젯 외에도 Gtk4는 다양한 위젯들을 제공하고 있다. 구체적으로 어떤 Container 위젯이 있는지, 그리고 사용법은 무엇인지는 아래 링크를 참조하면 도움이 될 것이다.

developer.gnome.org/gtk4/stable/LayoutContainers.html

 

Layout Containers: GTK 4 Reference Manual

 

developer.gnome.org

 

3.2.5 사족

Container는 Gtk3와 Gtk4에서 가장 크게 변한 개념 중 하나이다. Gtk3에서는 GtkBox, GtkGrid 등 위젯들의 부모가 되는 GtkContainer라는 위젯이 있었다. 따라서 자식 위젯들은 GtkContainer의 동작, 예를 들어 gtk_container_add()와 같은 API를 공유했었다.

반면 Gtk4에서는 더 이상 GtkBox, GtkGrid와 같은 Container 기능을 하는 위젯이 GtkContainer를 부모로 갖지 않는다. GtkContainer는 삭제되었고, GtkContainer가 제공하는 동작들(gtk_container_add 등)은 개별 위젯의 API로 대체되었다. 이렇게 변경된 이유는 직관적으로는 GtkContainer가 Container 위젯들의 공통된 기능을 제공하는 것이 맞는 방법처럼 보이지만, 실제로 Container 위젯들은 각기 다양한 방법으로 자식 위젯을 관리하기 때문에 현실적으로는 GtkContainer로 억지로 공통된 부분을 만드는 것이 부자연스럽기 때문이다. 

 

이와 관련된 내용에 대해서 더 깊이있는 이해를 원한다면, 아래 Gtk 공식 블로그를 읽어보는 것이 도움이 될 것이다.

blog.gtk.org/2019/03/27/layout-managers-in-gtk-4/

 

Layout managers in GTK 4 – GTK Development Blog

Containers and layout policies have been a staple of GTK’s design since the very beginning. If you wanted your widget to lay out its children according to a specific policy, you had to implement GtkContainer for handling the addition, removal, and iterat

blog.gtk.org

 

반응형