пятница, 2 августа 2013 г.

Lazarus World. Инициализация ZenGL


Внимание! Используется Lazarus 1.0.8 под управлением Fedora 19 Linux.
В первую очередь нам необходимо скачать, и установить библиотеку ZenGL. Скачать библиотеку можно с официального сайта в разделе Download (http://zengl.org/download.html). На момент написания статьи доступна версия 0.3.11. Нам необходимо скачать файл zengl-src-[номер версии].7z. Распакуйте данный архив в удобное для вас место на жестком диске. Например, у меня это каталог /home/[имя пользователя]/lazarus/zengl/zengl-src-0.3.11. Давайте посмотрим на содержимое каталога zengl-src-0.3.11. Из всех каталогов, которые нас интересуют, это каталог extra – содержит дополнительные модули, headers – содержит заголовочные модули, lib – содержит объектные файлы различных библиотек, src – исходные файлы библиотеки ZenGL, которые мы будем подключать к нашему проекту.

Переходим к подключению библиотеки ZenGL к нашему проекту в среде Lazarus. В среде Lazarus создадим новый проект. Для этого выберем пункт меню File(Файл) -> New(Создать), откроется диалог создание нового проекта. В диалоге создания нового проекта выберите раздел Project(Проект) > Program(Программа) и нажмите кнопку Ok.

Сохраните проект, например в каталоге /home/[имя пользователя]/Projects/lazarus/zengl_p1. Для этого выберите пункт меню File(Файл) > Save As(Сохранить как). Присвойте проекту имя zengl_p1.lpi. Далее в свойствах проекта, пункт меню Project(Проект) > Project Options(Параметры проекта), выберите раздел Compiler Options(Параметры компилятора) > Paths(Пути). В поле Other unit files (-Fu)(Другие модули (-Fu)) нужно указать следующие пути до модулей ZenGL:

/home/[имя пользователя]/lazarus/zengl/zengl-src-0.3.11/headers/
/home/[имя пользователя]/lazarus/zengl/zengl-src-0.3.11/extra/
/home/[имя пользователя]/lazarus/zengl/zengl-src-0.3.11/src/
/home/[имя пользователя]/lazarus/zengl/zengl-src-0.3.11/lib/zlib/$(TargetCPU)-$(TargetOS)/
/home/[имя пользователя]/lazarus/zengl/zengl-src-0.3.11/lib/zip/$(TargetCPU)-$(TargetOS)/
/home/[имя пользователя]/lazarus/zengl/zengl-src-0.3.11/lib/theora/$(TargetCPU)-$(TargetOS)/
/home/[имя пользователя]/lazarus/zengl/zengl-src-0.3.11/lib/msvcrt/$(TargetCPU)/
/home/[имя пользователя]/lazarus/zengl/zengl-src-0.3.11/lib/jpeg/$(TargetCPU)-$(TargetOS)/
/home/[имя пользователя]/lazarus/zengl/zengl-src-0.3.11/lib/ogg/$(TargetCPU)-$(TargetOS)/
Рисунок 1. Подключение модулей
В поле Include files (-Fi)(Включаемые файлы (-Fi)) нужно указать следующие пути:
/home/[имя пользователя]/lazarus/zengl/zengl-src-0.3.11/headers/
Рисунок 2. Подключение заголовочных модулей
 
Теперь перейдем к написанию кода. В только что созданном новом проекте будет присутствовать начальный код, который надо очистить и оставить только следующий код:

program zengl_p1;
begin
end.
В коде, после названия программы, напишем секцию uses со следующими подключаемыми модулями:
uses zgl_main, zgl_screen, zgl_window, zgl_timers, zgl_utils;
Ниже секции uses напишем пять процедур – заглушек:
В данной процедуре пишется код, который выполнится после создания окна и создания контекста OpenGL
procedure Init;
begin
end;
Процедура, в которой выполняется прорисовка экрана на каждой итерации главного цикла
procedure Draw;
begin
end;
Процедура, которая выполняется каждый раз перед перерисовкой экрана на каждой итерации главного цикла.
procedure Update( dt: Double );
begin
end;
Процедура, которая выполняется каждый раз через установленный интервал времени. В данном случае, каждую секунду в заголовке окна будет меняться значение FPS.
procedure Timer;
begin
 wnd_SetCaption( '01 - Initialization[ FPS: ' + u_IntToStr( zgl_Get( RENDER_FPS ) ) + ' ]' );
end;
Процедура, которая выполняется перед очисткой ресурсов, уничтожения окна и контекста OpenGL
procedure Quit;
begin
end;
Внутри главных операторных скобок необходимо написать следующий код:
Установка процедуры таймера с именем Timer с интервалом 1000 миллисекунд
timer_Add( @Timer, 1000 );
Регистрация процедуры Init
zgl_Reg( SYS_LOAD, @Init );
Регистрация процедуры Draw
zgl_Reg( SYS_DRAW, @Draw );
Регистрация процедуры Update
zgl_Reg( SYS_UPDATE, @Update );
Регистрация процедуры Quit
zgl_Reg( SYS_EXIT, @Quit );
Первоначальное значение заголовка окна
wnd_SetCaption( '01 - Initialization' );
Отобразим курсор
wnd_ShowCursor( TRUE );
Установим опции окна
scr_SetOptions( 800, 600, REFRESH_MAXIMUM, FALSE, FALSE );
Инициализация библиотеки ZenGL
zgl_Init();
Ниже показан результат работы программы.
  Рисунок 3. Результат работы программы

Код полностью:
program zengl_p1;

uses   zgl_main, zgl_screen, zgl_window, zgl_timers, zgl_utils;

procedure Init;
begin

end;

procedure Draw;
begin

end;

procedure Update( dt: Double );
begin

end;

procedure Timer;
begin
     wnd_SetCaption( '01 - Initialization[ FPS: '
+ u_IntToStr( zgl_Get( RENDER_FPS ) ) + ' ]' );
end;

procedure Quit;
begin

end;

begin
  timer_Add( @Timer, 1000 );

  zgl_Reg( SYS_LOAD, @Init );
  zgl_Reg( SYS_DRAW, @Draw );
  zgl_Reg( SYS_UPDATE, @Update );
  zgl_Reg( SYS_EXIT, @Quit );
  wnd_SetCaption( '01 - Initialization' );
  wnd_ShowCursor( TRUE );
  scr_SetOptions( 800, 600, REFRESH_MAXIMUM, FALSE, FALSE );
  zgl_Init();
end.



syntax highlighted by Code2HTML, v. 0.9.1

среда, 17 июля 2013 г.

Gtk+. Создание панели состояния


Поставим перед собой задачу реализовать небольшое приложение, в котором размещены, сверху вниз, три кнопки. Имена кнопок будут Red, Green, Blue. По - нажатию на одну из кнопок, то главное окно программы закрашивается в соответствующий цвет, либо в красный, либо зеленый, либо синий. В самом низу программы мы расположим панель состояния, в которой мы будем показывать статус программы. Другими словами, если программа ожидает действий, то программа имеет статус «Ready», иначе если курсор мыши наведен на одну из кнопок, то в статусе программы пишется название выбранной кнопки (Red, Green, Blue).

Сразу скажу, что для трех кнопок нам придется написать девять CALLBACK — функций, по три на каждую кнопку. Три CALLBACK — функции для события «clicked» (событие, когда мы нажимаем на кнопку), три CALLBACK — функции для события «enter» (событие, когда мы наводим курсор мыши на кнопку), три CALLBACK — функции для события «leave» (событие, когда мы убираем курсор мыши с кнопки).

Запустите среду программирования Geany и создайте новый c — файл с именем main. Сохраните этот файл в заранее подготовленный каталог, например в каталог gtk_p6.

Начнем с подключения заголовочного файла библиотеки GTK+:


#include <gtk/gtk.h>
  
Дальше будут идти девять CALLBACK — функций, о которых кратко было написано выше, но их мы рассмотрим позже. Перейдем к написания кода главной функции программы.
  
int main(int argc, char** argv) {
  
В самом начале главной функции нам необходимо описать указатели на будущие объекты (окно, панель состояния, вертикальный контейнер, три кнопки), массив данных необходимых нашим кнопкам, массив данных панели состояния, строка изначального состояния панели и идентификатор панели состояния.
  
GtkWidget *window = NULL;
GtkWidget *statusbar = NULL;
GtkWidget *vbox = NULL;

GtkWidget *red_button = NULL;
GtkWidget *green_button = NULL;
GtkWidget *blue_button = NULL;

gpointer button_data[1];
gpointer statusbar_data[2];

const gchar *statusbar_str = "Ready";

guint context_id;


Дальше мы создадим окно и проведем инициализацию GTK+:


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), "GTK_p6");
  
Далее создадим вертикальный контейнер в котором разместим три кнопки:
  
vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
gtk_container_add(GTK_CONTAINER(window), vbox);


Подготовим массив данных для передачи его в CALLBACK — функцию одной из трех кнопок, в зависимости какая нажата. Данные будут содержать указатель на объект окна с именем window.


button_data[0] = (gpointer)window;


Далее создаем три кнопки и свяжем событие («clicked») нажатия на кнопку с функцией — обработчиком для конкретной кнопки. Потом запакуем кнопки в вертикальный контейнер.


red_button = gtk_button_new_with_label("Red");

gtk_box_pack_start(GTK_BOX(vbox), red_button, FALSE, FALSE, 1);

g_signal_connect(red_button, "clicked", G_CALLBACK(red_button_clicked), (gpointer)button_data);

green_button = gtk_button_new_with_label("Green");

gtk_box_pack_start(GTK_BOX(vbox), green_button, FALSE, FALSE, 1);

g_signal_connect(green_button, "clicked", G_CALLBACK(green_button_clicked), (gpointer)button_data);

blue_button = gtk_button_new_with_label("Blue");

gtk_box_pack_start(GTK_BOX(vbox), blue_button, FALSE, FALSE, 1);

g_signal_connect(blue_button, "clicked", G_CALLBACK(blue_button_clicked), (gpointer)button_data);
  
Создадим панель состояния, добавим панель в конец вертикального контейнера, получим идентификатор контекста панели и добавим первоначальную строчку в панель состояния:


statusbar = gtk_statusbar_new();

gtk_box_pack_end(GTK_BOX(vbox), statusbar, FALSE, FALSE, 0);

context_id = gtk_statusbar_get_context_id(GTK_STATUSBAR(statusbar), "status");

gtk_statusbar_push(GTK_STATUSBAR(statusbar), context_id, statusbar_str);


Теперь подготовим данные о панели состояния для передачи их в функции — обработчики кнопок. Первый элемент массива это указатель на объект панели состояния, второй элемент массива это идентификатор контекста панели состояния.

statusbar_data[0] = (gpointer)statusbar;
statusbar_data[1] = GINT_TO_POINTER(context_id);


Далее нам осталось связать событие «enter» с соответствующими CALLBACK функциями — обработчиками для трех кнопок и связать событие «leave» с соответствующими CALLBACK функциями — обработчиками, тоже для трех кнопок.


g_signal_connect(red_button, "enter", G_CALLBACK(red_button_enter), (gpointer)statusbar_data);

g_signal_connect(green_button, "enter", G_CALLBACK(green_button_enter), (gpointer)statusbar_data);

g_signal_connect(blue_button, "enter", G_CALLBACK(blue_button_enter), (gpointer)statusbar_data);

g_signal_connect(red_button, "leave", G_CALLBACK(red_button_leave), (gpointer)statusbar_data);

g_signal_connect(green_button, "leave", G_CALLBACK(green_button_leave), (gpointer)statusbar_data);

g_signal_connect(blue_button, "leave", G_CALLBACK(blue_button_leave), (gpointer)statusbar_data);


Потом мы должны установить сигнал на закрытие главного окна программы, отобразить все виджеты окна, передать управление библиотеки GTK+.


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

gtk_widget_show_all(window);

gtk_main();

return 0;
}


Теперь напишем CALLBACK — функции для события «clicked» для кнопок с именем Red, Green, Blue.

 
gboolean red_button_clicked( GtkWidget *widget, gpointer data ) {


gpointer* data_t = (gpointer*)data;

GdkRGBA color = {1.0, .0, .0, 1.0};


GtkWidget *window = (GtkWidget*)data_t[0];

gtk_widget_override_background_color(window, GTK_STATE_FLAG_NORMAL, &color);


return TRUE;
}


gboolean green_button_clicked( GtkWidget *widget, gpointer data ) {


gpointer* data_t = (gpointer*)data;

GdkRGBA color = {.0, 1.0, .0, 1.0};


GtkWidget *window = (GtkWidget*)data_t[0];

gtk_widget_override_background_color(window, GTK_STATE_FLAG_NORMAL, &color);


return TRUE;
}


gboolean blue_button_clicked( GtkWidget *widget, gpointer data ) {


gpointer* data_t = (gpointer*)data;

GdkRGBA color = {.0, .0, 1.0, 1.0};


GtkWidget *window = (GtkWidget*)data_t[0];

gtk_widget_override_background_color(window, GTK_STATE_FLAG_NORMAL, &color);


return TRUE;
}


В данных функциях, в зависимости от нажатой кнопки, установка необходимого цвета, получение переданных данных в CALLBACK — функцию и установка заднего фона главного окна в цвет (Красный, Зеленый, Синий).

Теперь напишем CALLBACK — функции для события «enter» для кнопок с именем Red, Green, Blue.


gboolean red_button_enter( GtkWidget *widget, gpointer data ) {


gpointer* data_t = (gpointer*)data;

GtkWidget *statusbar = (GtkWidget*)data_t[0];

guint context_id = GPOINTER_TO_INT(data_t[1]);

gtk_statusbar_push(GTK_STATUSBAR(statusbar), context_id, "Red");
return TRUE;
}


gboolean green_button_enter( GtkWidget *widget, gpointer data ) {


gpointer* data_t = (gpointer*)data;

GtkWidget *statusbar = (GtkWidget*)data_t[0];

guint context_id = GPOINTER_TO_INT(data_t[1]);

gtk_statusbar_push(GTK_STATUSBAR(statusbar), context_id, "Green");
return TRUE;
}


gboolean blue_button_enter( GtkWidget *widget, gpointer data ) {


gpointer* data_t = (gpointer*)data;

GtkWidget *statusbar = (GtkWidget*)data_t[0];

guint context_id = GPOINTER_TO_INT(data_t[1]);

gtk_statusbar_push(GTK_STATUSBAR(statusbar), context_id, "Blue");
return TRUE;
}


В данных функциях, в зависимости от того на какую кнопку наведен курсор мыши, мы в панели состояния размещаем строку с именем кнопки.

Последнее, мы напишем CALLBACK — функции для события «leave» для кнопок с именем Red, Green, Blue.
  
gboolean red_button_leave( GtkWidget *widget, gpointer data ) {


gpointer* data_t = (gpointer*)data;

GtkWidget *statusbar = (GtkWidget*)data_t[0];

guint context_id = GPOINTER_TO_INT(data_t[1]);

gtk_statusbar_push(GTK_STATUSBAR(statusbar), context_id, "Ready");
return TRUE;
}


gboolean green_button_leave( GtkWidget *widget, gpointer data ) {


gpointer* data_t = (gpointer*)data;

GtkWidget *statusbar = (GtkWidget*)data_t[0];

guint context_id = GPOINTER_TO_INT(data_t[1]);

gtk_statusbar_push(GTK_STATUSBAR(statusbar), context_id, "Ready");
return TRUE;
}


gboolean blue_button_leave( GtkWidget *widget, gpointer data ) {


gpointer* data_t = (gpointer*)data;

GtkWidget *statusbar = (GtkWidget*)data_t[0];

guint context_id = GPOINTER_TO_INT(data_t[1]);

gtk_statusbar_push(GTK_STATUSBAR(statusbar), context_id, "Ready");
return TRUE;
}


В данных функциях, когда мы убираем курсор мыши с кнопки, то, в зависимости от кнопки, надпись панели состояния меняется с названия кнопки на строку «Ready».

 
Рисунок 1. Результат работы программы
  
Код полностью:
#include <gtk/gtk.h>

/*
 * Some button clicked 
 * 
 */

gboolean red_button_clicked( GtkWidget *widget, gpointer data ) {

 gpointer* data_t = (gpointer*)data;
 
 GdkRGBA color = {1.0, .0, .0, 1.0};

 GtkWidget *window = (GtkWidget*)data_t[0];
 
 gtk_widget_override_background_color(window,  
GTK_STATE_FLAG_NORMAL, &color);

 return TRUE;
}

gboolean green_button_clicked( GtkWidget *widget, gpointer data ) {

 gpointer* data_t = (gpointer*)data;
 
 GdkRGBA color = {.0, 1.0, .0, 1.0};

 GtkWidget *window = (GtkWidget*)data_t[0];
 
 gtk_widget_override_background_color(window,  
GTK_STATE_FLAG_NORMAL, &color);

 return TRUE;
}

gboolean blue_button_clicked( GtkWidget *widget, gpointer data ) {

 gpointer* data_t = (gpointer*)data;
 
 GdkRGBA color = {.0, .0, 1.0, 1.0};

 GtkWidget *window = (GtkWidget*)data_t[0];
 
 gtk_widget_override_background_color(window
GTK_STATE_FLAG_NORMAL, &color);

 return TRUE;
}

/*
 * Some button entered
 * 
 */

gboolean red_button_enter( GtkWidget *widget, gpointer data ) {

 gpointer* data_t = (gpointer*)data;
 
 GtkWidget *statusbar = (GtkWidget*)data_t[0];
 
 guint context_id = GPOINTER_TO_INT(data_t[1]); 
 
 gtk_statusbar_push(GTK_STATUSBAR(statusbar), context_id, "Red");
 return TRUE;
}

gboolean green_button_enter( GtkWidget *widget, gpointer data ) {

 gpointer* data_t = (gpointer*)data;
 
 GtkWidget *statusbar = (GtkWidget*)data_t[0];
 
 guint context_id = GPOINTER_TO_INT(data_t[1]); 
 
 gtk_statusbar_push(GTK_STATUSBAR(statusbar), context_id, "Green");
 return TRUE;
}

gboolean blue_button_enter( GtkWidget *widget, gpointer data ) {

 gpointer* data_t = (gpointer*)data;
 
 GtkWidget *statusbar = (GtkWidget*)data_t[0];
 
 guint context_id = GPOINTER_TO_INT(data_t[1]); 
 
 gtk_statusbar_push(GTK_STATUSBAR(statusbar), context_id, "Blue");
 return TRUE;
}

/*
 * Some button leaved 
 * 
 */

gboolean red_button_leave( GtkWidget *widget, gpointer data ) {

 gpointer* data_t = (gpointer*)data;
 
 GtkWidget *statusbar = (GtkWidget*)data_t[0];
 
 guint context_id = GPOINTER_TO_INT(data_t[1]); 
 
 gtk_statusbar_push(GTK_STATUSBAR(statusbar), context_id, "Ready");
 return TRUE;
}

gboolean green_button_leave( GtkWidget *widget, gpointer data ) {

 gpointer* data_t = (gpointer*)data;
 
 GtkWidget *statusbar = (GtkWidget*)data_t[0];
 
 guint context_id = GPOINTER_TO_INT(data_t[1]); 
 
 gtk_statusbar_push(GTK_STATUSBAR(statusbar), context_id, "Ready");
 return TRUE;
}

gboolean blue_button_leave( GtkWidget *widget, gpointer data ) {

 gpointer* data_t = (gpointer*)data;
 
 GtkWidget *statusbar = (GtkWidget*)data_t[0];
 
 guint context_id = GPOINTER_TO_INT(data_t[1]); 
 
 gtk_statusbar_push(GTK_STATUSBAR(statusbar), context_id, "Ready");
 return TRUE;
}

int main(int argc, char** argv) {
 
 GtkWidget *window = NULL;
 GtkWidget *statusbar = NULL;
 GtkWidget *vbox = NULL;
 
 GtkWidget *red_button = NULL;
 GtkWidget *green_button = NULL;
 GtkWidget *blue_button = NULL;
 
 gpointer button_data[1];
 gpointer statusbar_data[2];
 
 const gchar *statusbar_str = "Ready";
 
 guint context_id;
 
 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), "GTK_p6");
 
 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
 
 gtk_container_add(GTK_CONTAINER(window), vbox);
 
 button_data[0] = (gpointer)window;
 
 red_button = gtk_button_new_with_label("Red");
 
 gtk_box_pack_start(GTK_BOX(vbox), red_button, FALSE, FALSE, 1);  
 
 g_signal_connect(red_button, "clicked",  
G_CALLBACK(red_button_clicked), (gpointer)button_data);
 
 green_button = gtk_button_new_with_label("Green");
 
 gtk_box_pack_start(GTK_BOX(vbox), green_button, FALSE, FALSE, 1); 
 
 g_signal_connect(green_button, "clicked"
G_CALLBACK(green_button_clicked), (gpointer)button_data);
 
 blue_button = gtk_button_new_with_label("Blue");
 
 gtk_box_pack_start(GTK_BOX(vbox), blue_button, FALSE, FALSE, 1);
 
 g_signal_connect(blue_button, "clicked",  
G_CALLBACK(blue_button_clicked), (gpointer)button_data);  

 statusbar = gtk_statusbar_new();
 
 gtk_box_pack_end(GTK_BOX(vbox), statusbar, FALSE, FALSE, 0);
 
 context_id = gtk_statusbar_get_context_id(GTK_STATUSBAR(statusbar),
 "status");
 
 gtk_statusbar_push(GTK_STATUSBAR(statusbar), context_id,  
statusbar_str);
 
 statusbar_data[0] = (gpointer)statusbar;
 statusbar_data[1] = GINT_TO_POINTER(context_id);
 
 g_signal_connect(red_button, "enter",  
G_CALLBACK(red_button_enter), (gpointer)statusbar_data);
 
 g_signal_connect(green_button, "enter",  
G_CALLBACK(green_button_enter), (gpointer)statusbar_data);
 
 g_signal_connect(blue_button, "enter",  
G_CALLBACK(blue_button_enter), (gpointer)statusbar_data);
 
 g_signal_connect(red_button, "leave",  
G_CALLBACK(red_button_leave), (gpointer)statusbar_data);
 
 g_signal_connect(green_button, "leave"
G_CALLBACK(green_button_leave), (gpointer)statusbar_data);
 
 g_signal_connect(blue_button, "leave"
G_CALLBACK(blue_button_leave), (gpointer)statusbar_data);
 
 g_signal_connect(window, "destroy",  
G_CALLBACK(gtk_main_quit), NULL);
 
 gtk_widget_show_all(window);
 
 gtk_main();
 
 return 0;

}


syntax highlighted by Code2HTML, v. 0.9.1

пятница, 21 июня 2013 г.

Gtk+. Перемещение элементов управления


Поставим для себя задачу реализовать программу, которая будет представлять из себя главное окно с кнопкой. По нажатию на кнопку она перемещается в новую позицию, если кнопка нажата из новой позиции, то кнопка возвращается на свое исходное место.

Разобьем наши дальнейшие действия:
1) Создадим главное окно. Здесь все как обычно, подключаем заголовочный файл gtk:

#include <gtk/gtk.h>

Потом создадим главную функцию программы:

int main(int argc, char **argv) {
return 0;
}

В теле главной функции определим указатель на главное окно:
GtkWidget *window;

Проведем инициализацию gtk:
gtk_init(&argc, &argv);

Создадим окно:
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

Присвоим заголовок окну:
gtk_window_set_title (GTK_WINDOW (window), "Gtk_p5");

Разместим в центре рабочего стола:
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);

Установим размер окна:
gtk_window_set_default_size(GTK_WINDOW(window), 200, 200);

Установим границу окна:
gtk_container_set_border_width (GTK_CONTAINER (window), 10);

Установим сигнал закрытия окна:
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);

Отобразим все виджеты окна:
gtk_widget_show_all(window);

Передадим управление библиотеки GTK:
gtk_main();

Необходимый минимум для создания главного окна мы рассмотрели. Пойдем дальше и добавим кнопку.
Чтобы добавить кнопку в окно, мы должны создать контейнер и разместить его в главном окне. Для позиционирования элементов управления в определенных координатах существует контейнер GtkFixed.

Объявим указатель на наш будущий контейнер, его нужно разместить в начале главной функции программы, напишем следующую строчку кода:

GtkWidget *fixed;

после строчки

GtkWidget *button;

а указатель на кнопку после объявления указателя на главное окно:

GtkWidget *window;

Далее определим массив указателей который мы передадим в функцию обработки нажатия на виджет кнопки.

gpointer data[3];
Теперь ниже, там где мы подключили Gtk заголовочный файл #include <gtk/gtk.h>, объявим две структуры:

1) Одна структура описывает размер кнопки

struct button_size {
gint width;
gint height;
};

2) Вторая структура описывает позицию кнопки

struct button_position {
gint x;
gint y;
};

После объявления структур, ниже объявим булеву переменную которая будет «переключать» положение кнопки:

gboolean f_switch = FALSE;

В главной функции программы, там где мы объявили массив указателей

gpointer data[3];

ниже мы должный объявить переменную на структуру с именем button_size и две переменные на структуру с именем button_position и присвоить всем этим структурам значения:

struct button_size bs = { 80, 35 };
struct button_position bp1 = { 55, 10 };
struct button_position bp2 = { 55, 135 };

Ниже, после этих строчек, объявим два указателя на структуру типа button_position:

struct button_position *p_bp1;
struct button_position *p_bp2;

Теперь давайте создадим контейнер, после строчки:

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

напишем следующую строчку:

fixed = gtk_fixed_new();

Функция gtk_fixed_new создаст для нас контейнер GtkFixed.

Следующей строчкой мы разместим только что созданный контейнер в главном окне программы . Кстати, главное окно — это тоже виджет — контейнер для других контейнеров или виджетов.

gtk_container_add(GTK_CONTAINER(window), fixed);

Дальше создадим кнопку с надписью «Move»

button = gtk_button_new_with_label("Move");

Теперь подготовим массив указателей, с именем data, для дальнейшей передачи его в функцию — обработчик нажатия кнопки.

Присвоим указателям на структуру button_position адреса переменных типа button_position:

p_bp1 = &bp1;
p_bp2 = &bp2;

Присвоим каждому элементу массива типа gpointer преобразованные указатели (тип gpointer) на структуру типа button_position (с именами p_bp1 и p_bp2) и преобразованный указатель (тип gpointer) на контейнер GtkFixed (с именем fixed).
data[0] = (gpointer)p_bp1;
data[1] = (gpointer)p_bp2;
data[2] = (gpointer)fixed;

Далее напишем сигнал который свяжет событие («clicked») нажатия на кнопку (с именем button) с функцией — обработчиком (с именем button_clicked).

g_signal_connect(button, "clicked", G_CALLBACK(button_clicked), (gpointer)data);

Далее добавим нашу кнопку (с именем button) в контейнер GtkFixed (с именем fixed) в определенную позицию (55, 10) в пикселях:

gtk_fixed_put(GTK_FIXED(fixed), button, bp1.x, bp1.y);

Установим размер кнопки в (85, 35) пикселях:

gtk_widget_set_size_request(button, bs.width, bs.height);

Теперь рассмотрим функцию — обработчик для нашей кнопки:

gboolean button_clicked( GtkWidget *widget, gpointer data ) {
gpointer* data_t = (gpointer*)data;
struct button_position *p_bp1 = (struct button_position*)data_t[0];
struct button_position *p_bp2 = (struct button_position*)data_t[1];
GtkWidget *fixed = (GtkWidget*)data_t[2];
if (!f_switch) {
g_print("%d\n%d\n", p_bp2->x, p_bp2->y);
gtk_fixed_move(GTK_FIXED(fixed), widget, p_bp2->x, p_bp2->y);
f_switch = TRUE;
}
else {
g_print("%d\n%d\n", p_bp1->x, p_bp1->y);
gtk_fixed_move(GTK_FIXED(fixed), widget, p_bp1->x, p_bp1->y);
f_switch = FALSE;
}
return TRUE;
}

1) В качестве параметров функция передает указатель на виджет (GtkWidget *widget), в данном случае виджет — кнопка (с именем button), который вызвал функцию — обработчик, и указатель на данные (с именем data). Функция возвращает булево значение.

2) Далее мы получаем указатели на структуру типа button_position и указатель на контейнер GtkFixed:

gpointer* data_t = (gpointer*)data;
struct button_position *p_bp1 = (struct button_position*)data_t[0];
struct button_position *p_bp2 = (struct button_position*)data_t[1];
GtkWidget *fixed = (GtkWidget*)data_t[2];

3) Дальше мы проверяем значение переменной f_switch, если она равна FALSE, то мы перемещаем кнопку на новую позицию и меняем значение f_switch на TRUE, иначе если f_switch равна TRUE, то мы перемещаем кнопку в изначальное положение и меняем f_switch на FALSE.

if (!f_switch) {
g_print("%d\n%d\n", p_bp2->x, p_bp2->y);
gtk_fixed_move(GTK_FIXED(fixed), widget, p_bp2->x, p_bp2->y);
f_switch = TRUE;
}
else {
g_print("%d\n%d\n", p_bp1->x, p_bp1->y);
gtk_fixed_move(GTK_FIXED(fixed), widget, p_bp1->x, p_bp1->y);
f_switch = FALSE;
}

Таким образом, нажимая на кнопку, мы перемещаем кнопку на другую позицию.

 
Рисунок 1. Изначальное положение кнопки

Рисунок 2. Измененное положение кнопки

 
Код полностью:

#include <gtk/gtk.h>

struct button_size {
  gint width;
  gint height;
 };

struct button_position {
  gint x;
  gint y;
 };
 
gboolean f_switch = FALSE;

gboolean button_clicked( GtkWidget *widget, gpointer data ) {
  
gpointer* data_t = (gpointer*)data;
struct button_position  
                *p_bp1 = (struct button_position*)data_t[0]; 
struct button_position 
                *p_bp2 = (struct button_position*)data_t[1];
GtkWidget *fixed = (GtkWidget*)data_t[2];
  
if (!f_switch) {
 g_print("%d\n%d\n", p_bp2->x, p_bp2->y); 
 gtk_fixed_move(GTK_FIXED(fixed), widget, p_bp2->x, p_bp2->y);
 f_switch = TRUE;
}
else {
 g_print("%d\n%d\n", p_bp1->x, p_bp1->y);
 gtk_fixed_move(GTK_FIXED(fixed), widget, p_bp1->x, p_bp1->y);
 f_switch = FALSE;
}
  
return TRUE;
}

int main( int argc, char **argv )
{
 GtkWidget *window;
 GtkWidget *button;
 GtkWidget *fixed;
    
 gpointer data[3];
    
 struct button_size bs = { 80, 35 };
 struct button_position bp1 = { 55, 10 };
 struct button_position bp2 = { 55, 135 };
    
 struct button_position *p_bp1;
 struct button_position *p_bp2;
   
 gtk_init (&argc, &argv);
    
 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    
 gtk_window_set_title (GTK_WINDOW (window), "Gtk_p5");
 
 gtk_window_set_position(GTK_WINDOW(window)
                        GTK_WIN_POS_CENTER);
 
 gtk_window_set_default_size(GTK_WINDOW(window), 200, 200);
 
 gtk_container_set_border_width (GTK_CONTAINER (window), 10);
 
 g_signal_connect(window, "destroy"
         G_CALLBACK(gtk_main_quit), NULL);
    
 fixed = gtk_fixed_new();
 
 gtk_container_add(GTK_CONTAINER(window), fixed);
 
 button = gtk_button_new_with_label("Move");
 
 p_bp1 = &bp1;
 p_bp2 = &bp2;
 
 data[0] = (gpointer)p_bp1;
 data[1] = (gpointer)p_bp2;
 data[2] = (gpointer)fixed;
 
 g_signal_connect(button, "clicked",  
            G_CALLBACK(button_clicked), (gpointer)data); 
 
 gtk_fixed_put(GTK_FIXED(fixed), button, bp1.x, bp1.y);
 
 gtk_widget_set_size_request(button, bs.width, bs.height); 
 
 gtk_widget_show_all(window);
 
 gtk_main();   
    
 return(0);
}