среда, 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);
}

понедельник, 17 июня 2013 г.

Gtk+. События элементов управления


Продолжаем разбираться в Gtk+. Возьмем за основу наш проект, в котором мы создали главное окно и добавили элементы управления. Имя нового проекта будет Gtk_p4. Таким же образом, как было в предыдущих постах, создадим файл main.c.

Напишем в файле main.c главную функцию программы, создадим главное окно, добавим элементы управления (поле редактирование, кнопку, метку текста), разместим все эти виджеты в контейнере сетке. Ну вот, подошли к самому интересному.

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

1) Если вводимый текст пустая строка, по нажатию на кнопку покажем сообщение, что строка пуста.
2) Если длинна строки текста превышает 10-ти символом, по нажатию на кнопку покажем сообщение, что строка не должна превышать 10-ти символов.

Ну вот, пробуем написать код. Так как мы будем показывать сообщение, то в нашем проекте нам понадобится функция создания модального диалога. Данную функцию мы позаимствуем из проекта, где мы показывали сообщение по нажатию на кнопку мыши. Ниже показан код функции:

void my_modal_dialog(GtkWindow* parent, gchar* str)
{
  GtkWidget* modal_dialog = gtk_message_dialog_new(parent,
   GTK_DIALOG_DESTROY_WITH_PARENT,
   GTK_MESSAGE_INFO,
   GTK_BUTTONS_CLOSE,
   str);
  gtk_dialog_run(GTK_DIALOG(modal_dialog));
  gtk_widget_destroy(modal_dialog);
}

Так как мы должны при каждом нажатие на кнопку определять длину текста введенного в поле редактирования, то напишем для этого специальную функцию:

gint get_string_length(const gchar *str) {
  return strlen(str);
}

В самом начале кода проекта после строчки

#include <gtk/gtk.h>
подключите заголовочный файл с функциями для работы со строками

#include <string.h>

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

gpointer data[3];

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

gtk_grid_attach(GTK_GRID(grid), button, 1, 0, 1, 1);

разместите код добавления указателей в массив:

data[0] = (gpointer)label;
data[1] = (gpointer)entry;
data[2] = (gpointer)window;

Дальше разместим функцию, которая соединит кнопку с именем button и функцию с именем button_clicked, в которую мы и предадим массив указателей.

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

Ниже показан код функции с именем button_clicked:

gboolean button_clicked(GtkWidget* widget, gpointer data) {
  
  gpointer* data_t = (gpointer*)data;
  
  GtkWidget *label = (GtkWidget*)data_t[0];
  GtkWidget *entry = (GtkWidget*)data_t[1];
  GtkWidget *window = (GtkWidget*)data_t[2];

  const gchar *str = gtk_entry_get_text(GTK_ENTRY(entry));
  if (get_string_length(str) == 0) {
     my_modal_dialog(GTK_WINDOW(window), "The text is empty. Try  again.");
     gtk_widget_grab_focus(entry);
     return FALSE;
  }

  if (get_string_length(str) > 10) {
     my_modal_dialog(GTK_WINDOW(window), "The text of 10 characters. Try again.");
     gtk_entry_set_text(GTK_ENTRY(entry), "");
     gtk_widget_grab_focus(entry);
     return FALSE;
  }
  gtk_label_set_text(GTK_LABEL(label), str);
  return TRUE;
}

Рассмотрим данную функцию подробней:
1) Функция button_clicked возвращает булево значение, в качестве параметра передает указатель на виджет который вызвал данную функцию, в данном случае виджет с именем button. Второй параметр это массив указателей на виджеты.
2) Показанными ниже строчками мы получаем указатели на виджеты главного окна, метку текста и поля редактирования.

gpointer* data_t = (gpointer*)data;
GtkWidget *label = (GtkWidget*)data_t[0];
GtkWidget *entry = (GtkWidget*)data_t[1];
GtkWidget *window = (GtkWidget*)data_t[2];

3) Следующей строчкой мы получаем строку текста из поля редактирования:

const gchar *str = gtk_entry_get_text(GTK_ENTRY(entry));

4) Далее мы проверяем полученную строку пуста ли она, если пуста, то выводим сообщение, устанавливаем фокус ввода в поле редактирования и возвращаем FALSE из функции button_clicked:

if (get_string_length(str) == 0) {
   my_modal_dialog(GTK_WINDOW(window), "The text is empty. Try again.");
   gtk_widget_grab_focus(entry);
   return FALSE;
}

5) Дальше проверим превышает ли введенная строка 10-ти символов, если превышает, то выводим сообщение, очищаем поле редактирования, устанавливаем фокус на поле редактирования и возвращаем FALSE из функции button_clicked:

if (get_string_length(str) > 10) {
   my_modal_dialog(GTK_WINDOW(window), "The text of 10 characters. Try again.");
   gtk_entry_set_text(GTK_ENTRY(entry), "");
   gtk_widget_grab_focus(entry);
   return FALSE;
}

6) Если все проверки пройдены успешно, то устанавливаем текст метке.

gtk_label_set_text(GTK_LABEL(label), str);

7) По окончанию работы функция button_clicked возвращает булево значение TRUE.

Ниже размещены изображения работы программы:

 
Рисунок 1. Сообщение, если поле редактирования пустое

Рисунок 2. Сообщение, если введено больше десяти символов

Рисунок 3. Правильно введенный текст
  
Ниже показан код всей программы:

#include <gtk/gtk.h>
#include <string.h>

void my_modal_dialog(GtkWindow* parent, gchar* str)
{
 GtkWidget* modal_dialog = gtk_message_dialog_new(parent, 
 GTK_DIALOG_DESTROY_WITH_PARENT,
 GTK_MESSAGE_INFO,
 GTK_BUTTONS_CLOSE,
 str);
   
 gtk_dialog_run(GTK_DIALOG(modal_dialog));
 gtk_widget_destroy(modal_dialog);
}

gint get_string_length(const gchar *str) {
 return strlen(str);
}

gboolean button_clicked(GtkWidget* widget, gpointer data) {
 
 gpointer* data_t = (gpointer*)data;
 
 GtkWidget *label = (GtkWidget*)data_t[0];
 GtkWidget *entry = (GtkWidget*)data_t[1];
 GtkWidget *window = (GtkWidget*)data_t[2];
 
 const gchar *str = gtk_entry_get_text(GTK_ENTRY(entry));
 
 if (get_string_length(str) == 0) {
  my_modal_dialog(GTK_WINDOW(window), 
"The text is empty. Try again.");
  gtk_widget_grab_focus(entry);
  return FALSE; 
 }
  
 if (get_string_length(str) > 10) {
  my_modal_dialog(GTK_WINDOW(window), 
"The text of 10 characters. Try again.");
  gtk_entry_set_text(GTK_ENTRY(entry), "");
  gtk_widget_grab_focus(entry);
  return FALSE;
 }
 
 gtk_label_set_text(GTK_LABEL(label), str);
 
 return TRUE;
}

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

 GtkWidget *window = NULL; 
 GtkWidget *grid = NULL;
 GtkWidget *entry = NULL;
 GtkWidget *button = NULL;
 GtkWidget *label = NULL;

 gpointer data[3];
 
 gtk_init(&argc, &argv);
 
 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
 gtk_window_set_title (GTK_WINDOW (window), "Gtk_p4");
 
 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);             
                  
 
 grid = gtk_grid_new();
 
 gtk_grid_set_row_spacing (GTK_GRID(grid), 10);
 
 gtk_grid_set_column_spacing (GTK_GRID(grid), 10);
 
 gtk_container_add (GTK_CONTAINER (window), grid);
 
 entry = gtk_entry_new();
 
 gtk_grid_attach(GTK_GRID(grid), entry, 0, 0, 1, 1);
 
 label = gtk_label_new("Some text");
  
 gtk_grid_attach(GTK_GRID(grid), label, 0, 1, 2, 1);
 
 button = gtk_button_new_with_label("Set text");
 
 gtk_grid_attach(GTK_GRID(grid), button, 1, 0, 1, 1);
 
 data[0] = (gpointer)label;
 data[1] = (gpointer)entry;
 data[2] = (gpointer)window;
 
 g_signal_connect(button, "clicked", G_CALLBACK(button_clicked), 
 (gpointer)data);
 
 gtk_widget_show_all(window);
 
 gtk_main();
 
 return 0;
 
}


syntax highlighted by Code2HTML, v. 0.9.1