пятница, 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