Основы GTK+ (часть 1) - Начинающим - Shelek
Что такое GTK+?


"GTK+ The GIMP Toolkit", - это написано на сайте www.gtk.org
А по руски?
"GTK+ - это мульти-платформенный инструментарий для создания графического интерфейса пользователя" - написано там же.

Библиотека очень проста в понимании и использовании. Очень удобна.

Где взять? Тут ftp://ftp.gtk.org/pub/gtk/v2.2/ (если уже не установлена) качаете gtk+-2.2.4.tar.bz2 - он весит поменьше чем gz. Возможно вам понадобится еще pango, glib, atk, tiff, libpng и pkgconfig. Все лежат рядом с GTK+.


Соберём всё в кучу.


Распакуем файлы:
$ tar xvfz filename.gz

Для файлов bz2 замените xvfz на xvfj. Собираем обычно под рутом.
# ./configure && make && make install

Если ругается на отсутствие чего либо, но вы уверенны, что устанавливали это. Тогда набираем вот такую команду:
$ export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig/

По этому адресу будут искаться конфигурационные файлы от ваших библиотек. Или скопируйте все файлы из /usr/local/lib/pkgconfig/ в /usr/lib/pkgconfig/

Скорее всего у Вас ничего не потребуют устанавливать в большинстве систем уже установлены необходимые библиотеки как и GTK+. Ну вот вроде всё в порядке, всё установлено.


Переходим непосредственно к GTK+.


Соберём просто пустое окно.



Код:
#include <gtk/gtk.h>

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 base base.c `pkg-config gtk+-2.0 -cflags` `pkg-config gtk+-2.0 -libs`

Строка GtkWidget *window объявляет указатель на виджет будущее наше окно. Далее: gtk_init (&argc, &argv) - этой функцией инициализируем некоторые параметры GTK. Она вызывается во всех приложениях GTK. В параметрах мы можем передать, например, дисплей X или перейти в режим отладки. Вот неполный список:

Код:
gtk-module
g-fatal-warnings
gtk-debug
gtk-no-debug
gdk-debug
gdk-no-debug
display
sync
name
class

Строками :
Код:
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_widget_show (window);

Создаем окно параметром GTK_WINDOW_TOPLEVEL указываем, что окно управляется менеджером окон и использовать его настройки. Этим gtk_widget_show (window) говорим, что этот виджет настроен и готов к отображению. Запуск главного цикла ожидает всяких событий gtk_main.

Привет, Мир!!



Листинг gtktest.c:
Код:
#include <gtk/gtk.h>

void hello(GtkWidget *widget, gpointer data)
{
g_print ("Hello, World!!!n");
}

gint delete_event( GtkWidget *widget, GdkEvent *event, gpointer data)
{
g_print ("delete event occurredn");

return TRUE;
}

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

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

gtk_init (&argc, &argv);

window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

g_signal_connect(G_OBJECT(window),"delete_event",
G_CALLBACK(delete_event), NULL);

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

button = gtk_button_new_with_label("Hello World!!!");

g_signal_connect(G_OBJECT(button), "clicked",
G_CALLBACK (hello), NULL);
g_signal_connect_swapped(G_OBJECT(button), "clicked",
G_CALLBACK (gtk_widget_destroy),
G_OBJECT(window));

gtk_container_set_border_width (GTK_CONTAINER (window),
10);

gtk_container_add(GTK_CONTAINER (window), button);

gtk_widget_show(button);
gtk_widget_show(window);

gtk_main();

return 0;
}

Компилируется подобно первой программе. Мы здесь использовали 6 новых библиотечных функции и 3 свои. Опишем библиотечные. Функция g_print сообщение на терминал из которого мы запустили программу работает, как printf. Функция g_signal_connect:

Код:
gulong g_signal_connect(
gpointer *object,
const gchar *name,
GCallback func,
gpointer func_data );

Привязывает к определённому объекту object вызов функции func по событию name с передачей параметра func_data. Возможны следующее варианты функции callback:

Вариант 1.
Код:
gint callback_func(
GtkWidget *widget,
GdkEvent *event,
gpointer callback_data );

В эту функцию передается указатель на объект для которого произошло событие, само событие и какие-то данные. Если функция возвращает TRUE значит событие обработано, дальнейшая обработка не нужна, если возвращает FALSE управление передается в следующую привязанную функцию. Теперь понят почему при попытке закрыть окно крестиком в заголовке наша программа продолжает работать, как не в чем не бывало лишь сообщив нам выводом сообщения в терминал, что событие было. Функция delete_event возвращая TRUE останавливает обработку события, если заменить TRUE на FALSE окно спокойно закроется.

Вариант 2.
Код:
void callback_func(
GtkWidget *widget,
gpointer callback_data );

Передается тоже всё тоже самое, но без события. Функция ничего не возвращает. Обработка не прекращается.

Вариатн 3.
Код:
void callback_func( GtkObject *object );

Передается только объект. Функция ничего не возвращает. Обработка не прекращается. Но для привязки функции к событию используется g_signal_connect_swapped:

Код:
gulong g_signal_connect_swapped(
gpointer *object,
const gchar *name,
GCallback func,
gpointer *slot_object );

Параметр slot_object передает в функцию указатель на объект. Обычно используется для вызова функций GTK.

Строка

Код:
button = gtk_button_new_with_label("Hello World!!!")

создает экземпляр кнопки с надписью.

Строки

Код:
gtk_container_set_border_width (GTK_CONTAINER (window),
10);
gtk_container_add(GTK_CONTAINER (window), button);

задают обводку окна шириной 10 пикселей иначе кнопка будет в притирку к краям окна и "засовывают" кнопку внутрь окна. Далее показываем кнопку, окно и запускаем цикл.

Небольшой список событий:

Код:
event
button_press_event
button_release_event
scroll_event
motion_notify_event
delete_event
destroy_event
expose_event
key_press_event
key_release_event
enter_notify_event
leave_notify_event
configure_event
focus_in_event
focus_out_event
map_event
unmap_event
property_notify_event
selection_clear_event
selection_request_event
selection_notify_event
proximity_in_event
proximity_out_event
visibility_notify_event
client_event
no_expose_event
window_state_event

Возможные значения структуры GtkEvent:
Код:
GDK_NOTHING
GDK_DELETE
GDK_DESTROY
GDK_EXPOSE
GDK_MOTION_NOTIFY
GDK_BUTTON_PRESS
GDK_2BUTTON_PRESS
GDK_3BUTTON_PRESS
GDK_BUTTON_RELEASE
GDK_KEY_PRESS
GDK_KEY_RELEASE
GDK_ENTER_NOTIFY
GDK_LEAVE_NOTIFY
GDK_FOCUS_CHANGE
GDK_CONFIGURE
GDK_MAP
GDK_UNMAP
GDK_PROPERTY_NOTIFY
GDK_SELECTION_CLEAR
GDK_SELECTION_REQUEST
GDK_SELECTION_NOTIFY
GDK_PROXIMITY_IN
GDK_PROXIMITY_OUT
GDK_DRAG_ENTER
GDK_DRAG_LEAVE
GDK_DRAG_MOTION
GDK_DRAG_STATUS
GDK_DROP_START
GDK_DROP_FINISHED
GDK_CLIENT_EVENT
GDK_VISIBILITY_NOTIFY
GDK_NO_EXPOSE
GDK_SCROLL
GDK_WINDOW_STATE
GDK_SETTING


Упаковка виджетов.


Есть два основных способа компоновки виджетов 1-й с помощью коробок(box), 2-й с помощью таблиц.

Коробочная версия.




Листинг gtktest2.c:
Код:
#include <gtk/gtk.h>

void hello(GtkWidget *widget, gpointer data)
{
g_print ("Quit na fig ot sel!!!n");
}

gint delete_event( GtkWidget *widget, GdkEvent *event, gpointer data)
{
g_print ("delete event occurredn");
return FALSE;
}

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

GtkWidget * make_box(
gboolean homogeneous,
gint spacing,
gboolean expand,
gboolean fill,
guint padding)
{
GtkWidget *box;
GtkWidget *button;

box = gtk_hbox_new (homogeneous, spacing);

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

gtk_widget_show (button);

button = gtk_button_new_with_label ("Second");
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 *label;
GtkWidget *ver_box;
GtkWidget *hor_box;

gtk_init (&argc, &argv);

window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

g_signal_connect(G_OBJECT(window), "delete_event",
G_CALLBACK (delete_event), NULL);

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

button = gtk_button_new_with_label("Quit!");

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

g_signal_connect(G_OBJECT(button), "clicked",
G_CALLBACK (gtk_main_quit),
G_OBJECT(window));

ver_box = gtk_vbox_new(FALSE, 0);

label = gtk_label_new ("First box");
gtk_misc_set_alignment (GTK_MISC (label), 0, 0);

gtk_box_pack_start (GTK_BOX (ver_box), label,
FALSE, FALSE, 20);

gtk_widget_show (label);

hor_box = make_box (TRUE, 0, FALSE, TRUE, 20);
gtk_box_pack_start (GTK_BOX (ver_box), hor_box,
FALSE, FALSE, 0);

gtk_widget_show (hor_box);

hor_box = gtk_hbox_new(FALSE, 0);
gtk_box_pack_start (GTK_BOX(hor_box), button,
TRUE, TRUE, 20);

gtk_box_pack_start (GTK_BOX(ver_box), hor_box,
FALSE, FALSE, 20);

gtk_container_add(GTK_CONTAINER (window), ver_box);

gtk_widget_show(button);
gtk_widget_show(hor_box);
gtk_widget_show(ver_box);
gtk_widget_show(window);

gtk_main();

return 0;
}

Суть метода заключается в использовании двух видов контейнеров (коробок) вертикального и горизонтально. Комбинируются они в каком угодно порядке и сочетании т.е. в один контейнер помещать другой пару кнопок и метку например. Горизонтальный контейнер помещает в себя виджеты по горизонтали в ряд. Вертикальный в столбик. Теперь рассмотрим код.

Функция make_box:
Код:
GtkWidget * make_box(
gboolean homogeneous,
gint spacing,
gboolean expand,
gboolean fill,
guint padding)
{
GtkWidget *box;
GtkWidget *button;

box = gtk_hbox_new (homogeneous, spacing);

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

gtk_widget_show (button);

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

gtk_widget_show (button);

return box;
}

Функция gtk_hbox_new (homogeneous, spacing) создает горизонтальную коробку.

Код:
GtkWidget *gtk_hbox_new (
gboolean homogeneous,
gint spacing );

Homogeneous указывает, что инкапсулируемые виджеты будут выравниваться по высоте коробки, если коробка вертикальная то по ширине, а spacing промежуток между виджетами. Для вертикальных коробок используется функция gtk_vbox_new (homogeneous, spacing). gtk_box_pack_start (GTK_BOX(box), button, expand, fill, padding) добавляет виджет в коробку в прямом порядке т.е. вошел первым отображается первым слева, вошел вторым - вторым слева. В противовес функция gtk_box_pack_end добавляет виджеты в обратном порядке.

Код:
void gtk_box_pack_start(
GtkBox *box,
GtkWidget *child,
gboolean expand,
gboolean fill,
guint padding);

Параметры box, child, expand, fill и padding указывают соответственно коробку, инкапсулируемый виджет, увеличить размер коробки, если виджет "не влезает" в коробку, заполнить свободное пространство виджетом и последние отступ от виджета в пикселах. После завершения манипуляций с конкретным виджетом мы его отображаем. На этом с коробками можно закончить код достаточно прозрачен.

Табличная упаковка.




Листинг gtktest3.c:
Код:
#include <gtk/gtk.h>

void hello(GtkWidget *widget, gpointer data)
{
g_print ("%s was pressedn", (char *) data);
}

gint delete_event(
GtkWidget *widget,
GdkEvent *event,
gpointer data)
{
g_print ("delete event occurredn");
return FALSE;
}

void destroy(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);

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

gtk_container_set_border_width (GTK_CONTAINER (window),
20);

table = gtk_table_new (2, 2, TRUE);

gtk_container_add (GTK_CONTAINER (window), table);

button = gtk_button_new_with_label ("button 1");
g_signal_connect (G_OBJECT (button), "clicked",
G_CALLBACK (hello), (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");
g_signal_connect (G_OBJECT (button), "clicked",
G_CALLBACK (hello), (gpointer) "button 2");
gtk_table_attach_defaults (GTK_TABLE (table), button,
1, 2, 0, 1);
gtk_widget_show(button);

button = gtk_button_new_with_label("Quit!");

g_signal_connect(G_OBJECT(button), "clicked",
G_CALLBACK (hello), (gpointer) "Quit button");
g_signal_connect(G_OBJECT(button), "clicked",
G_CALLBACK (gtk_main_quit),
G_OBJECT(window));

gtk_table_attach (GTK_TABLE (table), button,
0, 2, 1, 2,
GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND,
20, 40);

gtk_widget_show(button);

gtk_table_set_col_spacing (GTK_TABLE(table), 0, 10);

gtk_widget_show(table);
gtk_widget_show(window);

gtk_main();

return 0;
}

Упаковка таблицами похожа на разбивку HTML.
Сначала мы создаем таблицу нужного размера x*y в данном случае 2*2.

Код:
GtkWidget *gtk_table_new(
guint rows,
guint columns,
gboolean homogeneous );

Указывается количество строк, столбцов и выравнивание под один размер. В дальнейшем используем для задания координат в таблице следующую систему для таблицы 2*2:

Код:
_0__________1__________2
0+----------+----------+
_|__________|__________|
1+----------+----------+
_|__________|__________|
2+----------+----------+

Для добавления виджета в таблицу используется функция:

Код:
void gtk_table_attach(
GtkTable *table,
GtkWidget *child,
guint left_attach,
guint right_attach,
guint top_attach,
guint bottom_attach,
GtkAttachOptions xoptions,
GtkAttachOptions yoptions,
guint xpadding,
guint ypadding );

Параметры table и child - таблица и добавляемый виджет соответственно. Далее left_attach и top_attach указывают верхний левый угол в таблице для помещения виджета в соответсвии с приведенной таблицей и параметры right_attach и bottom_attach нижний правый угол т.е., если хотите можете занять несколько клеток таблицы. Дополнительные опции при размещении виджета по горизонтали и вертикали - xoptions и yoptions. Могут принимать значения - GTK_FILL, GTK_SHRINK и GTK_EXPAND - заполнить клетки виджетом, обрезать виджет, если не помещается и расширить клетки таблици, если не помещается соответственно. Xpadding и ypadding отступы от виджета. Есть урезанный вариант этой функции:

Код:
void gtk_table_attach_defaults(
GtkTable *table,
GtkWidget *widget,
guint left_attach,
guint right_attach,
guint top_attach,
guint bottom_attach );

Отрезанные параметры принимают следующие значения по умолчанию:

Код:
xoptions = yoptions = GTK_FILL | GTK_EXPAND
xpadding = ypadding = 0

Так же для форматирования используются:

Код:
void gtk_table_set_row_spacing(
GtkTable *table,
guint row,
guint spacing);

И

Код:
void gtk_table_set_col_spacing (
GtkTable *table,
guint column,
guint spacing);

Первая указывает расстояние между строкой row и строкой row + 1. Вторая тоже для столбцов. В случае, если надо установить расстояние для всех столбцов или строк используется:

Код:
void gtk_table_set_row_spacings(
GtkTable *table,
guint spacing);

И

Код:
void gtk_table_set_col_spacings(
GtkTable *table,
guint spacing);

Они не устанавливают spacing только для последнего столбца строки.
Т.к. и коробки и таблицы являются виджетами, то они комбинируются, как Вам нравится. Для компиляции всего этого можно использовать такой Makefile:

Код:
CC = gcc

CFLAGS = -Wall -g


All:
$(CC) $(CFLAGS) -o gtktest gtktest.c `pkg-config gtk+-2.0 --cflags` `pkg-config gtk+-2.0 --libs`
$(CC) $(CFLAGS) -o gtktest2 gtktest2.c `pkg-config gtk+-2.0 --cflags` `pkg-config gtk+-2.0 --libs`
$(CC) $(CFLAGS) -o gtktest3 gtktest3.c `pkg-config gtk+-2.0 --cflags` `pkg-config gtk+-2.0 --libs`

clean:
rm -f gtktest gtktest2 gtktest2


Автор: LogRus
Information
  • Posted on 31.01.2010 21:39
  • Просмотры: 2921