Журнал LinuxFormat - перейти на главную

LXF86:GTK+

Материал из Linuxformat
Версия от 21:27, 16 декабря 2008; Yaleks (обсуждение | вклад)

(разн.) ← Предыдущая | Текущая версия (разн.) | Следующая → (разн.)
Перейти к: навигация, поиск

GTK+: первое знакомство

ЧАСТЬ 1: Прочитав нашу серию статей о Qt/KDE, вы укрепились во мнении, что о лучшем и мечтать нельзя, и уже готовы удивить мир своей разработкой? Выслушайте сначала контр-аргументы – Андрей Боровский предлагает вашему вниманию новую серию учебников о GTK+!

Линус любит KDE. Он сам неоднократно говорил об этом, не стесняясь в выражениях по адресу GNOME. Я тоже не боль- шой поклонник GNOME, а вот инструментарий GTK+ мне очень нравится. Думаю, в таком сочетании предпочтений нет ничего особенного, ведь GTK+ – это далеко не только GNOME. Даже если вы – фанатичный пользователь KDE, вы наверняка время от времени рабо- таете в редакторе Gimp, основанном на библиотеках GTK+. Собственно, аббревиатура GTK+ и расшифровывается как Gimp ToolKit – «набор инструментов для Gimp). Сегодня мы начинаем серию статей, посвященных GTK+. Большая часть приложений, которые мы напишем, будет работать независимо от среды GNOME, но мы не оставим без внимания и взаимодействие с этой популярной оболочкой. Скорее всего, вы, уважаемый читатель Linux Format, уже представляете себе, что такое GTK+. Тем не менее, следует все-таки напомнить, что GTK+ представляет собой открытый набор графических компонентов, предназначенных для создания при- ложений на платформах Linux/Unix, Win32 и MacOS. GTK+ включает в себя большое количество визуальных элементов, используемых при создании графического интерфейса приложений, а также некоторые вспомогательные невизуальные элементы. На GTK+ основаны такие приложения, как графическая среда GNOME, редактор растровой графики Gimp, текстовый редактор AbiWord, табличный процессор Gnumeric и многие-многие другие. Помимо библиотек, реализующих различные элементы графического интерфейса приложений, GTK+ снабжен вспомогательными утилитами. Glade, например, позволяет проектировать интерфейсы GTK-приложений в режиме визуального редактирования. Если вы установили пакеты разработки GTK+/GNOME, то в вашей системе наверняка также установлена программа Devhelp, которая представляет собой браузер документации программиста по GTK+, GNOME, Gimp и Evolution. Можно ли назвать GTK+ предпочтительным инструментарием раз- работчика графических приложений? Вообще говоря, если вы выби- раете набор инструментов для создания графического интерфейса нового Linux-приложения, вы не должны особенно беспокоиться о том, какую оболочку предпочитают ваши пользователи. В соответствии с идеологией свободного выбора, которая пронизывает Linux, различные платформы и компоненты системы очень хорошо уживаются между собой. Средства взаимодействия между приложениями, использующи- ми разные графические компоненты, тоже быстро совершенствуются. Для того чтобы сделать выбор между GTK+ и Qt/KDE, следует хотя бы бегло ознакомиться с возможностями и особенностями каждого инструментария. Как это нередко бывает в мире открытого ПО, в настоящее вре- мя активно используются сразу две ветки GTK+. Многие разработчи- ки приложений, воспользовавшиеся в свое время GTK+ 1.2, не видят необходимости переходить на новые версии пакета, поэтому GTK+ 1.2 все еще можно встретить во многих дистрибутивах Linux. Новые (на момент написания этой статьи) приложения используют GTK+ версий 2.x. Примеры из нашей серии статей должны работать со всеми верси- ями GTK+, начиная с 2.0, если иное не указано явно. Наши инструменты Приложения GTK+ под Linux – это, прежде всего, приложения Linux. Для создания приложений GTK+ вам понадобятся стандартные инстру- менты разработчика – GCC и automake со товарищи. Помимо этого, в вашей системе должны быть установлены пакеты GTK+-devel*, atkdevel*, pango-devel* со всеми зависимостями. Кроме того, рекоменду- ем установить пакеты libgnome-devel и glade. После установки пакетов разработчика в нашем распоряжении окажутся утилиты командной строки glib-config и pkg-config (в некоторых системах также может быть установлена утилита GTK-config). Эти утилиты выводят информацию о расположении базовых библиотек GTK+ в вашей системе. При этом утилита glib-config предназначена исключительно для вывода инфор- мации о библиотеке glib, а pkg-config выводит информацию о самых разных библиотеках. Если, например, скомандовать в окне консоли pkg-config --libs GTK+-2.0 вы увидите нечто вроде

-L/usr/X11R6/lib -L/opt/gnome/lib -lGTK-x11-2.0 -lgdk-x11-2.0 -latk-1.0 -lgdk_pixbuf-2.0
-lpangocairo-1.0 -lpango-1.0 -lcairo -lgobject-2.0 -lgmodule-2.0 -ldl -lglib-2.0
-lfreetype -lfontconfig -lXrender -lX11 -lXext -lpng12 -lz -lglitz -lm

Вывод команды представляет собой список ключей компоновщика GCC, которые необходимо указать для подключения к основанному на GTK+ 2.x приложению всех необходимых ему библиотек. Как вы, конеч- но, догадались, этот список сгенерирован не столько для того, чтобы удовлетворить наше любопытство, сколько для автоматизации работы компоновщика в процессе сборки приложений GTK+, что мы увидим ниже. Команда pkg-config --cflags GTK+-2.0 выдаст все ключи компилятора, необходимые для компиляции прило- жения GTK+ 2.x. Hello GTK+ World! Теперь, когда мы знаем, что нам нужно для того, чтобы скомпилиро- вать приложение GTK+, мы готовы написать простейшую программу. Наша первая программа (назовем ее helloworld) создает простое окно с кнопкой. Щелчок по кнопке приводит к закрытию окна и завершению работы программы. Рассмотрим исходный текст программы helloworld (этот исходный текст вы найдете на диске в файле helloworld.с).

#include <GTK/GTK.h>
static void button_clicked(GTKWidget * widget, gpointer data)
{
g_print(“Button was clicked!\n”);
}
static gboolean delete_event(GTKWidget * widget, GdkEvent * event,
gpointer data)
{
g_print(“Delete event occurred\n”);
return FALSE;
}
static void destroy(GTKWidget * widget, gpointer data)
{
g_print(“Destroy signal was sent\n”);
GTK_main_quit();
}
int main(int argc, char ** argv)
{
GTKWidget * window;
GTKWidget * button;
const gchar * title = “Hello World!;
GTK_init(&argc, &argv);
window = GTK_window_new(GTK_WINDOW_TOPLEVEL);
GTK_window_set_title(GTK_WINDOW(window), title);
GTK_container_set_border_width(GTK_CONTAINER(window), 10);
g_signal_connect(G_OBJECT(window), “delete_event”,
G_CALLBACK(delete_event), NULL);
g_signal_connect(G_OBJECT(window), “destroy”,
G_CALLBACK(destroy), NULL);
button = GTK_button_new_with_label(“Quit”);
g_signal_connect(G_OBJECT(button), “clicked”,
G_CALLBACK(button_clicked), NULL);
g_signal_connect_swapped(G_OBJECT(button), “clicked”,
G_CALLBACK(GTK_widget_destroy), G_OBJECT(window));
GTK_container_add(GTK_CONTAINER(window), button);
GTK_widget_show(button);
GTK_widget_show(window);
GTK_main();
return 0;
}

Прежде чем мы пройдем нашу программу шаг за шагом, необходи- мо дать некоторые пояснения общего характера. В графических интер- фейсах, реализованных на объектно-ориентированных языках, все визуальные элементы представляются классами. В интерфейсе GTK+, реализованном на языке C, визуальным элементам соответствуют структуры данных. Эти структуры (мы будем называть их объектами) группируются в иерархии, которые соответствуют отношениям объ- ектов интерфейса. Следует отметить, что понятие «иерархии» здесь достаточно условное, отношения объектов GTK+ не следует путать с иерархическими отношениями классов в объектно-ориентированных языках. Корнем иерархии объектов GTK+ является абстрактный объект GObject. Ниже в иерархической лестнице расположен объект GTKObject, потомком которого является объект GtkWidget, который, в свою оче- редь, служит корнем иерархии всех визуальных элементов (виджетов). Здесь уместно сказать несколько слов и о формировании идентифи- каторов в GTK+. Как и во многих интерфейсах Unix, имена функций, типов данных, констант и макросов в GTK+ начинаются с префик- са, указывающего на имя библиотеки, которая экспортирует данный идентификатор. Имена функций, экспортируемых библиотекой GTK (libGTK), начинаются с префикса gtk_, имена типов данных из этой биб- лиотеки предваряются префиксом GTK, а имена констант и макросов имеют префикс GTK_. Имена функций, типов и констант с макросами, экспортируемых библиотекой GLib (libglib), начинаются с префиксов g_, g и G_, соответственно. Текст нашей программы helloworld начинается с определения трех статических функций. Эти функции представляют собой обработчики сигналов GTK+. Как и все современные многооконные графические системы, GTK+ базируется на событийно-управляемой архитектуре. Когда в графической системе происходит нечто, связанное с одним из окон приложения (щелчок мышью, нажатие на клавишу, сокрытие окном другого приложения – то есть возникает необходимость перери- совки), данному окну посылается сообщение. GTK+ преобразует сооб- щение оконной системы в сигнал и вызывает функцию-обработчик этого сигнала. В качестве аргументов функции-обработчику переда- ются данные об объекте-источнике и параметрах события. Механизм сигналов абстрагирован от механизма сообщений низкоуровневой графической подсистемы и отражает скорее структуру GTK+, нежели структуру системы низкого уровня. Источником сигналов, связанных с визуальным элементом управления, в GTK+ считается сам визуальный элемент. Для обработки сигналов GTK+ использует функции обратно- го вызова (callback). Qt, кстати, тоже использует функции обратного вызова, скрытые под надстройкой сигналов и слотов. Похоже, что прогрессивное человечество, по крайней мере, та его часть, которая пишет на C и C++, ничего лучшего пока не придумало. Обработчики сигналов представляют собой обычные функции C. Например, если мы создадим функцию-обработчик button_clicked и свяжем ее с сигналом clicked визуального элемента-кнопки, обработчик button_clicked будет вызываться в ответ на щелчок мышью по кнопке. Мы можем связать один обработчик с несколькими сигналами и назначить одному сиг- налу несколько обработчиков. Первым параметром функции обра- ботчика должен быть указатель на объект-источник сигнала, вторым параметром – указатель на произвольную структуру данных, которую программист может связать с данным сигналом. Помимо сигналов и GTK+ определены события, которые соответствуют событиям низко- уровневой системы X Window. По сути, события – это те же сигналы, но функции-обработчики событий отличаются от обработчиков обычных сигналов списком параметров. Имена событий имеют окончание _event. В нашей программе функция button_clicked() является обработчиком сигнала, а функция delete_event() – обработчиком события. Вы можете видеть, что списки параметров этих функций различаются. Теперь мы можем более подробно описать каждую функцию обратного вызова, определенную в нашей программе. Функция button_ clicked – это обработчик сигнала clicked кнопки приложения. Функция delete_event обрабатывает событие delete_event, а функция destroy представляет собой обработчик сигнала destroy. Событие delete_event генерируется системой X Window в случае, если пользователь пытается закрыть окно приложения. Сигнал destroy всегда посылается приложе- нию GTK+ во время завершения его работы. Действия, выполняемые функцией main() нашей программы, можно разделить на шесть ста- дий: создание и настройка главного окна, назначение обработчиков сигналов и событий окна, создание кнопки, назначение обработчиков сигнала clicked кнопки, расположение кнопки в главном окне, отобра- жение окна и кнопки. Работа программы начинается с вызова функции gtk_init(). Как сле- дует из названия, gtk_init() инициализирует приложение (устанавливает значения параметров GTK+) и обрабатывает специальные аргументы командной строки, которые могут быть переданы приложению GTK+. Далее мы создаем главное окно приложения с помощью функции gtk_window_new(). Единственный параметр функции указывает, что мы создаем обычное главное окно. Другой возможный параметр – GTK_WINDOW_POPUP позволяет создать всплывающее окно (popup window). Функция gtk_window_new() возвращает указатель на струк- туру GTKWidget, соответствующую созданному окну. Этот указатель мы сохраняем в переменной window. Функция gtk_window_set_title() устанавливает надпись в заголовке окна. Первым аргументом функции должен быть идентификатор окна, вторым аргументом – текст заголов- ка. Функция gtk_window_set_title() ожидает, что первым аргументом будет значение типа GTKWindow * (указатель на объект-окно). Однако, поскольку переменная window имеет тип указатель на GTKWidget, мы выполняем приведение типа с помощью макроса GTK_WINDOW. Приведение типов в данном случае необязательно, и мы выполняем его только для того, чтобы избавиться от предупреждений, выдава- емых компилятором. Главное окно приложения, как и многие другие объекты GTK+, представляет собой контейнер. Объекты-контейнеры могут содержать произвольное количество дочерних визуальных эле- ментов. При этом контейнеры обычно управляют расположением и размером дочерних виджетов, чем очень облегчают жизнь програм- миста (более подробное знакомство с контейнерами состоится в одной из следующих статей серии). Функция gtk_container_set_border_width() устанавливает ширину границы контейнера. Для главного окна «шири- на границы» означает расстояние от края дочернего элемента до края окна. Функция g_signal_connect() связывает сигнал объекта с обработ- чиком. Первый параметр функции – объект-источник сигнала. Мы приводим идентификатор объекта к типу GObject с помощью макро- са G_OBJECT. Второй параметр функции – строка с именем сигнала. Третий параметр – это указатель на функцию-обработчик (мы при- водим его значение к типу GCallback). Последний параметр функции g_signal_connect() представляет собой указатель на произвольную структуру данных, которую мы можем передать обработчику события. Для большинства обработчиков событий мы будем оставлять это зна- чение равным null. Мы назначаем обработчики двум другим сигналам окна – delete_event и destroy (для удобства имена функций-обработчи- ков выбраны аналогичными именам сигнала). Теперь нам необходимо создать кнопку, для чего мы пользуемся функцией gtk_button_new_with_label(). С помощью этой функции, чье имя говорит само за себя, мы создаем кнопку и указываем надпись на ней. Единственный параметр функции gtk_button_new_with_label() – это текст надписи на кнопке. Возвращаемое функцией значение пред- ставляет собой указатель на объект GTKWidget, соответствующий созданной кнопке. Почему функции, создающие окно и кнопку, возвра- щают указатели на объекты GTKWidget, а не на соответствующие этим визуальным элементам объекты GTKWindow и GTKButton (оба типа определены в библиотеке GTK)? Вероятно, это сделано для того, что- бы избавить нас от лишних операций приведения типов. Ведь большая часть функций, работающих с визуальными элементами, принимает в качестве параметра именно указатель на GTKWidget. Кнопка может быть источником нескольких сигналов, важнейший из которых, сигнал clicked, оповещает приложение о том, что по кнопке щелкнули мышью (или выполнили аналогичное действие с помощью клавиатуры). Мы назначаем сигналу clicked кнопки button два обра- ботчика событий – определенный нами обработчик button_clicked() и функцию gtk_widget_destroy(), которая завершает работу программы, посылая при этом сигнал destroy. Любопытно отметить, что функция gtk_widget_destroy(), вообще говоря, не является обработчиком сиг- нала. Возможность использовать «простые» функции GTK+ в качест- ве обработчиков сигналов представляет собой полезную особенность API GTK+, с которой мы еще встретимся не один раз. Для назначения обработчика gtk_widget_destroy() мы используем функцию g_signal_ connect_swapped(). Эта функция аналогична функции g_signal_connect (), за исключением того, что при вызове обработчика параметры пере- даются ему в другом порядке (подробнее об использовании g_signal_ connect_swapped() мы поговорим в следующих статьях). Функция gtk_container_add() добавляет дочерний элемент в контей- нер. Первый параметр функции – это указатель на объект-контейнер, вторым параметром должен быть указатель на добавляемый объект. Мы используем функцию gtk_container_add() для того, чтобы размес- тить кнопку в окне. И кнопка, и окно примут при этом разумные разме- ры (определяемые длиной надписи на кнопке и шириной границы окна- контейнера). Наконец, мы выполняем два вызова gtk_widget_show(), делающих визуальные элементы интерфейса (кнопку и окно) види- мыми. Последняя функция, которую мы вызываем явным образом – gtk_main(), она запускает цикл обработки сообщений. Теперь наша программа сможет реагировать на сигналы, посылаемые визуальными элементами управления. Нам осталось рассмотреть обработчики сигналов. Обработчик button_clicked() распечатывает в окне терминала сообщение о том, что кнопка была нажата. Для этого используется функция g_print(), кото- рая работает аналогично функции printf(). Сигнал-событие delete_event посылается программе, как уже говорилось, при попытке закрыть окно приложения. Обработчик этого события может отменить завершение работы приложения (для этого, как ни странно, он должен вернуть значение true, а не false). Сигнал destroy сообщает приложению, что его работа будет завершена и не допускает отмены завершения. Если вы закрываете окно приложения, щелкая по кнопке в заголовке окна, приложение сначала получит сигнал delete_event, а затем, если обра- ботчик этого сигнала вернет значение FALSE, сигнал destroy. Функция gtk_widget_destroy() (связанная в нашем примере с кнопкой button) посылает только сигнал destroy, но не delete_event. Хотя получение сигнала destroy в нашей программе свидетельствует об уничтожении главного визуального элемента (и его дочерних элементов), само по себе это уничтожение не приводит к выходу из цикла обработки сооб- щений. Выход из цикла нужно выполнить явным образом с помощью функции gtk_main_quit(). Для того, чтобы скомпилировать наш пример, мы воспользуемся уже известной нам утилитой pkg-config. Команда компиляции может выглядеть так:

gcc -Wall helloworld.c -o helloworld `pkg-config --cflags GTK+-2.0` `pkg-config --libs GTK+-2.0`
(thumbnail)
Рисунок 1. Наше первое приложение GTK+.

Мы вставляем в командную строку ключи, которые выдает нам ути- лита pkg-config. Теперь мы можем, наконец, полюбоваться творением наших рук (рис. 1).

Приступая к изучению нового интерфейса, очень приятно быстро получить первую работающую программу. Еще приятнее понимать, как это программа работает. Углубленному изучению механизма сигналов и взаимодействия визуальных компонентов GTK+ будет посвящена следующая статья.

Персональные инструменты
купить
подписаться
Яндекс.Метрика