среда, 22 мая 2013 г.

Gtk+. События окна на нажатие мыши


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

Первым делом нам необходимо подсоединить, определенную нами GCallback функцию с именем on_button_press, к сигналу button-press-event для объекта нашего окна с именем window.

g_signal_connect(window, "button-press-event", G_CALLBACK(on_button_press), NULL);

Данную функцию нужно разместить сразу после строчки:

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


Таким образом, когда мы нажимаем кнопкой мыши в окне, то возникает сигнал button-press-event и вызывает определенную нами функцию on_button_press. Ниже показана реализация функции on_button_press:

gboolean on_button_press (GtkWidget* widget, GdkEventButton * event)   
{  
   if (event->type == GDK_BUTTON_PRESS)
   {  
      if (event->button == 1)
      {
           g_print("Left button pressed\n");
           my_modal_dialog(GTK_WINDOW(widget), "Left button pressed\n");  
      }
      if (event->button == 2)  
      {
           g_print("Middle button pressed\n");
           my_modal_dialog(GTK_WINDOW(widget), "Middle button pressed\n");
       }
      if (event->button == 3)
      {  
           g_print("Right button pressed\n");
           my_modal_dialog(GTK_WINDOW(widget), "Right button pressed\n"); 
      } 
   }
return TRUE;  
}


Давайте, вкратце, рассмотрим функцию on_button_press. Данная функция имеет два параметра, первый параметр это указатель на объект по которому нажали мышкой (GtkWidget* widget), а второй это событие которое произошло, в данном случае нажата кнопка мыши (GdkEventButton * event). Внутри функции мы проверим тип события event->type (т.е. GDK_BUTTON_PRESS — нажата ли кнопка, GDK_BUTTON_RELEASE — отпущена ли кнопка, и другое). Далее мы проверяем какая кнопка мыши нажата event->button (1 — Левая кнопка мыши, 2 — Средняя кнопка мыши, 3 — Правая кнопка мыши), и в зависимости от того какая клавиша мыши нажата, то возникает модальный диалог с необходимым сообщением (например, my_modal_dialog(GTK_WINDOW(widget), "Left button pressed\n"), если нажата левая кнопка мыши). По окончанию работы функция on_button_press возвращает TRUE.
Теперь напишем функцию модального диалога void my_modal_dialog(GtkWindow* parent, gchar* str). Это функция имеет два параметра, первый это указатель на окно — родитель модального диалога (GtkWindow* parent), второй параметр, это строка, которую будет содержать наш модальный диалог (gchar* str).

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);  
}


Внутри функции мы создаем новое диалоговое окно, это простой диалог с иконкой и пользовательским сообщением. Функция, которая создает новый диалог с сообщением, показана ниже:


GtkWidget* modal_dialog = gtk_message_dialog_new(parent, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, str);

1) GtkWidget* modal_dialog указатель на виджет — диалог.
2) Теперь пройдемся по параметрам gtk_message_dialog_new
  • Первый параметр это указатель на окно — родитель, имя parent
  • Второй параметр, это флаги диалога, например флаг GTK_DIALOG_DESTROY_WITH_PARENT, который уничтожает диалог вместе с окном - родителем.
  • Третий параметр, это тип сообщения, например GTK_MESSAGE_INFO — информативное сообщение.
  • Четвертый параметр, это тип кнопки диалога, например GTK_BUTTONS_CLOSE — кнопка с надписью «Close».
  • Пятый параметр, это строка сообщения.

Дальше нам необходимо запустить наш, только что созданный, диалог. Сделать это можно следующей строчкой:

gtk_dialog_run(GTK_DIALOG(modal_dialog));

Данной функций мы отображаем диалог и все его виджеты, блокируем главный цикл обработки сообщений, до тех пор, пока диалог не будет уничтожен или пока пользователь не ответит на диалог, например нажав на кнопку «Close». Подробнее по функции gtk_dialog_run можно прочитать на официальном сайте.

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

gtk_widget_destroy(modal_dialog);

Теперь реализуем возможность нашего приложения закрываться по нажатию на клавишу «Esc» и выдавать диалоговое сообщение, если нажата любая другая клавиша.

Присоединим, определенную нами GCallback функцию с именем on_key_press, к сигналу key_press_event для объекта нашего окна с именем window.

g_signal_connect(window, "key-press-event", G_CALLBACK(on_key_press), NULL);

Данную функцию нужно разместить сразу после строчки:

g_signal_connect(window, "button-press-event", G_CALLBACK(on_button_press), NULL);

Таким образом, когда мы нажимаем любую клавишу в окне, то возникает сигнал key-press-event и вызывает определенную нами функцию on_key_press. Ниже показана реализация функции on_key_press:

gboolean on_key_press(GtkWidget *widget, GdkEventKey *event)  
{  
g_printerr("%s\n", gdk_keyval_name (event->keyval));
g_printerr("%d\n", event->keyval);

if (event->type == GDK_KEY_PRESS)
{  
   switch (event->keyval)  
   {  
      case GDK_KEY_Escape:
                 gtk_widget_destroy(widget);
                 break;  
      default:  
                 my_modal_dialog(GTK_WINDOW(widget), "Any Key pressed"); 
              break;
   }  
}
return TRUE;  
}


Перед тем как мы разберем функцию on_key_press, нам необходимо добавить еще один заголовочный файл gdk/gdkkeysyms.h, в котором определены все значения клавиш клавиатуры. Для этого разместите строчку: #include <gdk/gdkkeysyms.h> после строчки #include <gtk/gtk.h>.

Внутри функции on_key_press мы проверяем тип события нажатия event->type, например GDK_KEY_PRESS — нажата ли клавиша. Потом проверяем нажата ли клавиша «Esc» или любая другая клавиша, то уничтожаем главное окно программы или показываем диалог с сообщением, что нажата любая клавиша, соответственно.

И напоследок, добавим нашему контейнеру — сетке пробелы, по строчкам и по колонкам сетки. Этим мы сделаем так, чтобы между виджетами были интервалы. Разместите следующие две строчки после функции создания новой сетки grid = gtk_grid_new():

gtk_grid_set_row_spacing (GTK_GRID(grid), 10);

gtk_grid_set_column_spacing (GTK_GRID(grid), 10);


Рисунок 1. Пример события на нажатие кнопки мыши

Рисунок 2. Пример события на нажатия клавиши клавиатуры

#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.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);
}

gboolean
on_key_press(GtkWidget *widget,
          GdkEventKey *event)
{
    g_printerr("%s\n",
               gdk_keyval_name (event->keyval));
               
    g_printerr("%d\n", event->keyval);
    
    
    if (event->type == GDK_KEY_PRESS) {

  switch (event->keyval)
  {
   case GDK_KEY_Escape:
    gtk_widget_destroy(widget);
    break;
   default:
    my_modal_dialog(GTK_WINDOW(widget),
                                                "Any Key pressed");
    break;
  }
 
 }
 
    return TRUE;
}

gboolean on_button_press (GtkWidget* widget,
  GdkEventButton * event)
{
  if (event->type == GDK_BUTTON_PRESS)
  {
    if (event->button == 1) {
  g_print("Left button pressed\n");
  my_modal_dialog(GTK_WINDOW(widget),  
                                "Left button pressed\n");
    }
    if (event->button == 2) {
  g_print("Middle button pressed\n");
  my_modal_dialog(GTK_WINDOW(widget),  
                                "Middle button pressed\n");
 }
 if (event->button == 3) {
  g_print("Right button pressed\n");
  my_modal_dialog(GTK_WINDOW(widget),  
                                "Right button pressed\n");
 }
  }

  return TRUE;
}

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

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

 
 gtk_init(&argc, &argv);
 
 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
 gtk_window_set_title (GTK_WINDOW (window), "Gtk_p3");
 
 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);
 
 g_signal_connect(window, "button-press-event",
      G_CALLBACK(on_button_press), NULL);
 
 g_signal_connect(window, "key-press-event",  
      G_CALLBACK(on_key_press), 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);

 gtk_widget_show_all(window);
 
 gtk_main();
 
 return 0;
 
}


syntax highlighted by Code2HTML, v. 0.9.1

пятница, 10 мая 2013 г.

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


Изучение GTK+ мы продолжим с присвоения заголовка окну и добавления виджетов. За основу возьмем наш прошлый проект, в котором мы создали простое окно GTK+. 

1) Добавим в код нашего проекта строчку которой мы присвоим заголовок нашему окну. Эту строчку нужно поместить где-нибудь после window = gtk_window_new(GTK_WINDOW_TOPLEVEL):

gtk_window_set_title (GTK_WINDOW (window), "Gtk_p2");

2) Теперь разместим наше окно в центре рабочего стола. Для этого после строчки gtk_window_set_title (GTK_WINDOW (window), "Gtk_p2") мы разместим строчку:

gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);

3) Теперь зададим размер окна, размер окна будет 200 на 200 пикселей. Для этого после строчки gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER) нужно добавить строчку:

gtk_window_set_default_size(GTK_WINDOW(window), 200, 200);

4) Следующей строчкой мы установим ширину границы нашего окна в 10 пикселей:

gtk_container_set_border_width (GTK_CONTAINER (window), 10);

разместите ее после строчки:

gtk_window_set_default_size(GTK_WINDOW(window), 200, 200);

5) Так как в главном окне программы мы разместим больше чем один виджет, то для позиционирования виджетов и установки их размеров нам необходимо воспользоваться контейнером в который мы и упакуем все элементы. В GTK+ существует большое число контейнеров в которые мы можем размещать наши виджеты. В нашей программе мы воспользуемся контейнером, который называется GtkGrid. Данный контейнер позволяет размещать виджеты в ряд или в колонку. Следующей строчкой мы создадим контейнер:

grid = gtk_grid_new();

где grid это указатель на наш контейнер — сетка, так что после определения указателя на наше окно GtkWidget *window = NULL, разместите определение GtkWidget *grid = NULL.
  
6) Чтобы упаковать контейнер в наше окно, то необходимо воспользоваться функцией:

gtk_container_add (GTK_CONTAINER (window), grid);
  
7) После строчки кода, где мы определили указатель на контейнер-сетку GtkWidget *grid = NULL, нам необходимо определить указатели на три виджета, которые мы в дальнейшем разместим в окне нашей программы.
  
1. Указатель на поле редактирования: GtkWidget *entry = NULL;
2. Указатель на кнопку: GtkWidget *button = NULL;
3. Указатель на метку: GtkWidget *label = NULL;

8) Далее нам необходимо создать объект поля редактирования и прикрепить его к нашему контейнеру — сетки, разместить его в параметрах сетки.

а) Создание поля редактирования: 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);

Функция gtk_grid_attach размещает виджет в контейнере — сетка. Сетка состоит из клеток. Каждый виджет в сетке может заполнить одну или несколько клеток по вертикали и по горизонтали.

9) Ну и последнее, так как в нашей программе не один виджет, а несколько, то для показа окна, со всеми виджетами в нем, нам необходимо заменить функцию gtk_widget_show(window); на gtk_widget_show_all(window);.

Теперь, давайте, соберем наш проект. Результатом нашей работы будет окно с размещенными на нем элементами управления (смотрите рисунок 1).

 
Рисунок 1. Окно с элементами управления

 
Ниже размещен весь код программы:
#include <gtk/gtk.h>

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

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

 
 gtk_init(&argc, &argv);
 
 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
 gtk_window_set_title (GTK_WINDOW (window), "Gtk_p2");
 
 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_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);

 gtk_widget_show_all(window);
 
 gtk_main();
 
 return 0;
 
}


syntax highlighted by Code2HTML, v. 0.9.1

понедельник, 6 мая 2013 г.

Gtk+. Минимальный проект


Для создания минимального проекта используется среда программирования Geany 1.2.3, под управлением Fedora 18 Linux и KDE 4.10.2.

1. Подготовка среды Geany

Чтобы приступить к написанию кода нам необходимо настроить среду Geany на компиляцию и сборку проекта. Наш проект это один файл main.c. Настройку среды начнем с знакомства с программой pkg-config. Например, если мы хотим скомпилировать GTK+ приложение, мы должны показать компилятору, где искать GTK+ заголовочные файлы и библиотеки, то на помощь к нам придет программа pkg-config. Откройте терминал Konsole и введите следующие две команды:

1) Первая команда отобразит все GTK+ заголовочные файлы
> pkg-config --cflags gtk+-3.0

Результат выполнения:
-pthread -I/usr/include/gtk-3.0 -I/usr/include/atk-1.0 -I/usr/include/at-spi2-atk/2.0 -I/usr/include/pango-1.0 -I/usr/include/gio-unix-2.0/ -I/usr/include/cairo -I/usr/include/gdk-pixbuf-2.0 -I/usr/include/glib-2.0 -I/usr/lib64/glib-2.0/include -I/usr/include/harfbuzz -I/usr/include/freetype2 -I/usr/include/pixman-1 -I/usr/include/libpng15 -I/usr/include/libdrm

2) Вторая команда отобразить все GTK+ библиотеки
> pkg-config --libs gtk+-3.0

Результат выполнения:
-lgtk-3 -lgdk-3 -latk-1.0 -lgio-2.0 -lpangocairo-1.0 -lgdk_pixbuf-2.0 -lcairo-gobject -lpango-1.0 -lcairo -lgobject-2.0 -lglib-2.0

Следующим шагом будет создание файла main.c. Откройте среду Geany и выберите пункт меню File > New, этим действием мы создадим безымянный файл проекта. Сохраните файл проекта под именем main.c, для этого выберите пункт меню File > Save As.

Теперь в настройках проекта мы пропишем необходимые команды компиляции и сборки. Выберите пункт меню Build > Set Build Commands, откроется окно установки команд компиляции и сборки (смотрите рисунок 1). В поле с именем Compile нужно написать следующую команду:

gcc `pkg-config --cflags gtk+-3.0` -c "%f"

где %f , это параметр, вместо которого среда Geany подставит имя нашего файла main.c, gcc, это название компилятора который скомпилирует наш файл. Опция -c, это опция говорит компилятору, что файл нужно только скомпилировать. Последнее, это pkg-config --cflags gtk+-3.0, заголовочные файлы библиотеки GTK+. На этом этапе мы будем получать только объектный файл, в нашем случае main.o. Об опциях компилятора gcc читаем на сайте Opennet.ru. О параметрах среды Geany читаем на сайте Geany.org.

В поле с именем Build нужно написать следующую команду:

gcc `pkg-config --cflags --libs gtk+-3.0` -o "%e" "%f"

Выше указаная строчка сборки проекта не работает у меня Ubuntu 15.04 и Geany 1.24.1 и поэтому вы должны получить ряд сообщение при сборки проекта:

undefined reference to `gtk_init'  
undefined reference to `gtk_window_new'  
undefined reference to `gtk_main_quit'  
undefined reference to `g_signal_connect_data' 
undefined reference to `gtk_widget_show' 
undefined reference to `gtk_main'

так что ее нужно заменить на

gcc "%f" `pkg-config --cflags --libs gtk+-3.0` -o "%e" 

указав исходный файл проекта перед указанием необходимых библиотек для сборки проекта (ссылка по теме: http://stackoverflow.com/questions/11546877/compiling-and-linking-gtk-3-with-c-project-on-ubuntu).

где %f, параметр среды Geany, который мы разобрали выше по тексту, %e, тоже подстановка в командном пути, среда Geany подставит имя файла без пути и расширения, то есть подставится main. Опция -o, говорит компилятору, что весь вывод нужно поместить в файл main. Последнее, это pkg-config --cflags --libs gtk+-3.0, подстановка заголовочных файлов и библиотек GTK+. На этом этапе мы получим исполняемый файл GTK+ приложения.

Рисунок 1. Установка команд компиляции и сборки в среде Geany


Дополнение к посту:

Если pkg-config сообщает, что пакет gtk+-3.0 не найден, то нам необходимо установить пакет для разработчика, который называется gtk3-devel для Fedora 20. В Ubuntu 15.04 пакет называется libgtk-3-dev.

Если выдается сообщение, что gcc не найден, то нам необходимо его установить при помощи программы Apper.


Так, теперь окно, установки параметров сборки проекта в среде Geany, можно закрыть. Переходим к написанию кода.

2. Написание кода GTK+ приложения

В файле main.c, который мы создали и сохранили, нам необходимо написать следующие:

#include <gtk/gtk.h>

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

 GtkWidget *window = NULL;
 
 gtk_init(&argc, &argv);
 
 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
 g_signal_connect(window, 
                  "destroy",
                  G_CALLBACK(gtk_main_quit),
                  NULL);
 gtk_widget_show(window);
 
 gtk_main();
 
 return 0;
 
}


syntax highlighted by Code2HTML, v. 0.9.1


1) Строчкой #include <gtk/gtk.h> мы подключаем заголовочный файл библиотеки GTK+.
2) Строчкой int main(int argc, char **argv) { } главная функция, где { }- открывающая и закрывающая фигурные скобки главной функции программы.
3) Внутри главной функции программы, строчкой GtkWidget *window = NULL, мы определили будущий указатель на GTK объект, которой будет являться главным окном программы. Все GTK объекты, будь то окно или кнопка (другими словами «виджеты») , должны быть определены как указатель на базовый класс GtkWidget.
4) Строчкой gtk_init(&argc, &argv), мы инициализируем GTK+.
5) Строчкой window = gtk_window_new(GTK_WINDOW_TOPLEVEL), мы выделяем память под GTK объект, в нашем случае обычное окно (тип окна: GTK_WINDOW_TOPLEVEL).
6) Строчка g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL), когда окно уничтожается, например по нажатию на кнопку закрытия окна, то вызывается функция gtk_main_quit, которая прервет главный цикл обработки сообщений начатый функцией gtk_main
7) Строчкой gtk_widget_show(window), мы покажем наше окно.
8) Строчкой gtk_main(), мы передаем управление над программой библиотеке GTK.

Теперь нам осталось только построить проект. Для этого выберите пункт меню Build > Build или нажмите клавишу F9.

Ниже, на рисунке 2, можно посмотреть результат нашей работы.
Рисунок 2. Минимальное GTK+ приложение