Основы GTK+, Часть 2

Как использовать GTK+

Введение

Comments

Серия контента:

Этот контент является частью # из серии # статей: Основы GTK+, Часть 2

Следите за выходом новых статей этой серии.

Этот контент является частью серии:Основы GTK+, Часть 2

Следите за выходом новых статей этой серии.

Эта статья рассчитана на то, что Вы знакомы с основными понятиями объектно-ориентированного программирования, такими как классы, объекты, методы и наследование. Дополнительно необходимо понимание основ синтаксиса языка C.

Анатомия приложения GTK+ на языке С

Я думаю, что код лучше обсуждать на примере. В этой статье в качестве примера я использую небольшую, написанную на языке С, программу, которая называется HelloWorld. Хотя код этой программы короток и бесполезен, он показывает наиболее интересные концепции, с которыми следует познакомиться, прежде чем начинать программировать с использованием инструментария GTK+ (Листинг 1).

Листинг 1. Приложение Hello World в GTK+
#include <gtk/gtk.h>
#include <libintl.h>

#define _(x) gettext (x)
#define N_(x) (x)

#define GETTEXT_PACKAGE "gtk-hello"
#define LOCALEDIR "mo"

static char *greetings[] = { "Hello World",
			     "Witaj świecie",
			     "世界に今日は" };

static char* choose_greeting ()
{
  return greetings[g_random_int_range (0, G_N_ELEMENTS (greetings))];
}

static void cb_button_click(GtkButton *button, gpointer data)
{
  GtkWidget *label = GTK_WIDGET(data);

  g_assert(label != NULL);
  gtk_label_set_text(GTK_LABEL (label), choose_greeting());
}

static gboolean cb_delete(GtkWidget *window, gpointer data)
{
  gtk_main_quit();
  return FALSE;
}

int main (int argc, char *argv[])
{
  GtkWidget* window, *button, *label, *vbox;
  
  bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
  textdomain (GETTEXT_PACKAGE);

  gtk_init(&argc, &argv);

  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  button = gtk_button_new_with_label (_("Hello World"));
  label = gtk_label_new (choose_greeting());
  vbox = gtk_vbox_new(FALSE, 0);

  gtk_container_add(GTK_CONTAINER (window), vbox);
  gtk_container_add(GTK_CONTAINER (vbox), label);
  gtk_container_add(GTK_CONTAINER (vbox), button);

  g_signal_connect(G_OBJECT (window), "delete-event", 
		   G_CALLBACK(cb_delete), NULL);

  g_signal_connect (G_OBJECT (button), "clicked", 
		    G_CALLBACK (cb_button_click), label);

  gtk_widget_show_all(window);
  gtk_main();

  return 0;
}

Общая картина

Перед тем, как перейти к деталям, рассмотрим по порядку, что происходит, когда Вы запускаете программу HelloWorld:

  1. Происходит инициализация GTK+ и поддержки интернационализации (i18n);
  2. Создаются виджеты;
  3. Виджеты организуются в иерархию, чтобы GTK+ знал, как их отображать на экране;
  4. Подключаются два обработчика сигналов: первый – для выхода из программы по нажатию кнопки закрытия окна и второй – для изменения приветствия по нажатию кнопки на форме;
  5. Окно отображается на экране, а затем для активации главного цикла программы вызывается функция gtk_main();
  6. Главный цикл выполняется до тех пор, пока пользователь не закроет окно, и не будет вызвана функция gtk_main_quit() .

Инициализация

Следующие строки программы инициализируют GTK+ и поддержку интернационализации:

Листинг 2. Инициализация GTK+ и поддержки интернационализации
int main (int argc, char *argv[])
{
  GtkWidget* window, *button, *label, *vbox;
  
  bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
  textdomain (GETTEXT_PACKAGE);

  gtk_init(&argc, &argv);

Объявление функции main знакомо любому программисту, знающему язык C (если Вы не знакомы с языком С, знайте что main – это функция, с которой начинается выполнение программы).

Следующая строка содержит объявление нескольких указателей с типом GtkWidget. GTK+ - это объектно-ориентированный инструментарий, по этой причине в нем используются обычные для объектно-ориентированного программирования приемы. Например, наследование используется, чтобы создать различные виды виджетов. Сам по себе язык С не является объектно-ориентированным, однако GTK+ преодолевает это ограничение путем использования нескольких хитроумных приемов и некоторых свойств языка С. Таким образом, объекты представляются указателями, а тип GtkWidget называется основным классом , от которого образуются все остальные классы. Поэтому выше я объявил переменные типа GtkWidget*.

Следующие три строки – это вызовы, которые нужно расположить в начале программы, чтобы включить в ней поддержку интернационализации интерфейса. Надо заметить, что в настоящих приложениях вам не придется в ручную объявлять LOCALEDIR и GETTEXT_PACKAGE - ваша система сама позаботится об этих объявлениях, однако в нашем примере они помогают понять, что действительно происходит в программе.

В последней строчке вызывается функция gtk_init(). Вызов этой функции обязателен, при этом ей должны быть переданы аргументы, с которыми запущена программа, а ее вызов должен осуществляться перед тем, как будет сделан вызов любой другой функции GTK+. Если вы забудете осуществить вызов этой функции, то получите несколько десятков ошибок от различных подсистем, которые просто не смогут правильно запуститься.

Создание виджетов

В следующих четырех строках осуществляется вызов различных видов функции _new():

Листинг 3. Вызов различных функций _new() functions
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  button = gtk_button_new_with_label (_("Hello World"));
  label = gtk_label_new (choose_greeting());
  vbox = gtk_vbox_new(FALSE, 0);

Как Вы можете предположить, эти функции создают новые виджеты, поэтому они называются конструкторами (constructors) объектов, представляющих собой виджеты. В языке С++ конструкторы обозначаются особым образом и вызываются с использованием специальных служебных слов языка. Однако С не является объектно-ориентированным языком, поэтому в нем нет разницы между вызовом обычной функции и конструктором. Только окончание _new() добавленное к названию функции, свидетельствует о том, что это на самом деле конструктор. Так же есть соглашение, что любой конструктор в пространстве имен gtk_* возвращает указатель на GtkWidget, поэтому, объявляя переменную такого типа, вы можете присвоить ей результат вызова конструктора.

Если вы внимательно посмотрите на несколько конструкторов, то увидите, что они вызываются с различными параметрами, набор которых зависит от типа создаваемого виджета. В частности, конструкор gtk_window_new (GTK_WINDOW_TOPLEVEL) создает новое окно типа TOPLEVEL, этот виджет представляет собой обычное окно с заголовком, кнопкой закрыть и другими добавленными оконным менеджером элементами.

Вызовы конструкторов для переменных label (метка) и button (кнопка), делают в точности то, что от них ожидается. Обратите внимание на знак подчеркивания и скобки ( _() ), окружающие строку, передаваемую в конструктор кнопки. Этот макрос вызывает подпрограмму gettext() и является основой для перевода интерфейса программы. (Чтобы получить подробную информацию по использованию gettext, посмотрите ссылки).

Загадочный конструктор gtk_vbox_new(FALSE, 0) создает вертикальный бокс (Vbox). Хотя этот виджет не отображает даже пикселя на экране, вы вскоре увидите, что он играет решающую роль при расположении элементов управления GTK+.

Определение расположения

Следующие три строки определяют расположение виджетов на экране:

    gtk_container_add(GTK_CONTAINER (window), vbox);
    gtk_container_add(GTK_CONTAINER (vbox), label);
    gtk_container_add(GTK_CONTAINER (vbox), button);

Эти строки – вызовы объектно-ориентированных методов, относящихся к типу GtkContainer. Если вы обратитесь к справочной информации по API, то увидите, что GtkContainer является потомком класса GtkWidget, и что всем его методам как первый параметр передается переменная типа GtkContainer*. Считается, что GtkContainer* - это экземпляр (instance) объекта, на которой должен действовать метод. По той причине, что все переменные объявлены с типом GtkWidget*, а компилятор C не поддерживает объектно-ориентированное наследование, необходимо убедить компилятор, что безопасно передать эти переменные функции и ждать от них значение типа GtkContainer*. Эту работу выполняет макрос GTK_CONTAINER(), в нем реализовано безопасное преобразование к типу GtkContainer. Безопасное преобразование к типу (type-safe), означает, что макрос проверяет, может ли быть выполнена необходимая операция над данным типом перед тем, как осуществить преобразование. Если макрос не может выполнить операцию, он выводит предупреждающее сообщение.

Так как в GTK+ используется модель расположения элементов box layout, нет необходимости точно указывать, где на экране будет располагаться виджет. В программе Hello World каждый вызов метода gtk_container_add() говорит программе взять первый параметр или родительский виджет и поместить в него второй параметр или дочерний виджет. Виджет VBox, используемый в нашем примере, – это вид виджета, используемый для расположения дочерних виджетов вертикально в ряд. Так что, когда вы по очереди помещаете внутрь этого виджета метку, а затем кнопку, то кнопка отображается под меткой.

Это все, что вам нужно сделать. Если вы когда-нибудь вздумаете вручную расположить и определить размеры виджетов, используйте зафиксированные позиции (absolute positioning) – модель расположения элементов, используемая в некоторых других инструментариях, например, в Win32. В GTK+ вся работа по расположению и изменению размеров виджетов, выполняется за вас автоматически.

Подключение сигналов к главному циклу

После создания и размещения виджетов наступило время добавить им немного логики. Как в большинстве графических инструментариев, в GTK+ реализована модель с управлением по событиям (event-driven). По существу, она организована вокруг главного цикла(main loop) обработки событий. Главный цикл представляет собой бесконечный цикл, включающий три этапа: проверку наличия событий, их обработку и ожидание. Когда возникает событие, объект, ассоциированный с этим событием посылает сигнал, который сообщает главному циклу о произошедшем событии. Далее главный цикл ищет сигнал во внутренней таблице, где сопоставлены сигналы и обработчики, эти сопоставления иногда называют обратными вызовами (callbacks). В конце-концов главный цикл вызывает найденный обработчик сигнала для сигнала, пришедшего от объекта.

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

Листинг 4. Регистрация обратных вызовов
  g_signal_connect(G_OBJECT (window),  "delete-event", 
		   G_CALLBACK(cb_delete), NULL);
  g_signal_connect (G_OBJECT(button), "clicked", 
		    G_CALLBACK(cb_button_click), label);

Примечательно, что в GTK+ вы подсоединяете сигналы. Так, в первой строчке функция cb_delete соединена с сигналом delete-event объекта window. Точно так же во второй строчке функция cb_button_click соединена с сигналом clicked объекта button . Обратите внимание на то, что в четвертом параметре второго вызова, передается указатель на нашу метку label, позже вы увидите, как он используется в функции cb_button_click.

Ниже представлена функция cb_delete , которая завершает приложение, когда пользователь закрывает окно.

static gboolean cb_delete(GtkWidget *window, gpointer data)
{
  gtk_main_quit();
  return FALSE;
}

Любой обратный вызов для сигнала "delete-event" обязан соответствовать определенному в API прототипу, потому этой функции передается аргумент типа GtkWidget* и указатель на неопределенный тип data (тип gpointer равноценен типу void*). В нашем случае эти аргументы не нужны, и мы их просто игнорируем. Далее в теле функции вызывается функция gtk_main_quit(), она завершает бесконечный главный цикл. В конце, функция возвращает значение логического типа, снова по той же причине, что в прототипе обратного вызова для сигнала delete-event точно определено: функция должна возвратить логический тип. Возвращаемое логическое значение определяет то, какие действия дальше предпримет GTK+. Если функция возвратит значение TRUE, событие будет считаться обработанным, и обработчик события, который по умолчанию для этого вызова удаляет виджет из оконной системы, не будет вызван. Иногда это бывает нужно, например, если надо спросить у пользователя сохранять данные или нет, возвратив TRUE, можно приостановить закрытие окна.

Ниже приведена функция cb_button_click, ее задача по нажатию кнопки изменить приветственное сообщение:

Листинг 5. Функцияcb_button_click
static void cb_button_click(GtkButton *button, gpointer data)
{
  GtkWidget *label = GTK_WIDGET(data);

  g_assert(label != NULL);
  gtk_label_set_text(GTK_LABEL (label), choose_greeting());
}

Как вы можете видеть, эта функция похожа на функцию cb_delete, за исключением того, что она ничего не возвращает и вместо аргумента GtkWidget ей передается GtkButton*. В коде функции неопределенный указатель data преобразуется в указатель на GtkLabel. Помните четвертый параметр label, на который мы обратили внимание при регистрации обратных вызовов? Теперь при каждом обратном вызове указатель data будет содержать указатель на созданную нами в начале программы метку. Вы можете использовать аргумент data везде, где нужно передать дополнительную информацию функции обратного вызова. Таким же образом, если нужно получить доступ к объекту, пославшему сигнал, можно использовать первый параметр, которому в нашем случае соответствует аргумент button.

После получения указателя на метку, макрос g_assert проверяет не получилось ли так, что наша метка указывает на значение NULL. Макрос g_assert - это специальная утилита из библиотеки Glib (библиотека полезных типов и программ, активно используемая GTK+), которая прекращает исполнение программы, если не выполняется переданное ей условие, в нашем случае программа остановится, если label будет равно NULL. Поскольку label будет равняться NULL только в случае ошибки программиста, это выражение гарантирует то, что ошибка будет отловлена до того, как программа увидит свет.

Отображение окна

После регистрации обратных вызовов функция gtk_widget_show_all() показывает окно и все его виджеты на экране (Рисунок 1).

Рисунок 1. Программа Hello World на польском и японском
Выполняющееся приложение

Активация главного цикла

После того, как все установлено и отображено на экране, фукция gtk_main() активирует главный цикл. Главный цикл входит в бесконечный цикл ожидания и обработки поступающих сигналов до тех пор, пока кто-нибудь, закрыв окно, не вызовет функцию gtk_main_quit() .

Примечание:Если у вас остались какие-нибудь вопросы по программе, посмотрите код в архиве. Он точно такой же как в статье, но каждой строчке дан подробный комментарий.

Компиляция и запуск

Чтобы скомпилировать эту программу, вам понадобится компилятор С и файлы разработчика (файлы заголовков и библиотеки) для GTK+. Информация о том как их получить приведена в ссылках.

После установки этих файлов, разархивируйте исходный код, перейдите в появившейся каталог и запустите make:

$ tar -xzf gtk_hello.tgz
$ cd gtk_hello
$ make

Примечание:Если вы используете Microsoft® Windows®, вместо запуска make откройте Microsoft Visual Studio™.NET и запустите проект "hello".

Использование GTK+ в других языках программирования

Вы можете использовать инструментарий GTK+ во множестве языков программирования. Чтобы воспользоваться им, вам понадобятся привязки. Привязки (bindings) - это специальные пакеты для определенного языка, которые представляют API GTK+ в форме, понятной этому языку.

Например, я переписал код нашего приложения HelloWorld на языки Python и C#. Чтобы запустить GTK+ c этими языками, в дополнение к системам программирования Python и Mono/.NET вам соответственно понадобятся привязки PyGTK и Gtk# (см. ссылки).

Hello World и PyGTK

В листинге 6 показан код приложения Hello World, переписанный на язык Python.

Листинг 6. Приложение Hello World в PyGTK
import pygtk
pygtk.require('2.0')
import gtk
import random

greetings = ["Hello World", "Witaj Świecie", "世界に今日は"]
def choose_greeting (greets):
    return greets[random.randint (0, len(greets) - 1)]

def cb_clicked(button, label):
    label.set_text(choose_greeting(greetings))

window = gtk.Window ()
vbox = gtk.VBox ()
button = gtk.Button("Hello World")
label = gtk.Label (choose_greeting (greetings))

window.add(vbox)
vbox.add(label)
vbox.pack_start(button, False, False)

window.connect("delete-event", lambda a,b: gtk.main_quit())
button.connect("clicked", cb_clicked, label)

window.show_all()
gtk.main()

Благодаря краткости Python, эта версия программы получилась немного короче, чем ее двойник на С. Несмотря на это, выглядит она похоже. Хотя код приложения был переписан с использованием выражений Python, интерфейс программирования (API) остался тот же самый.

Hello World и Gtk#

Код приложения Hello World с использованием привязки Gtk# занимает чуточку больше места, чем его версия на языке С, это связано с тем, что синтаксис выражений в языке С# достаточно длинный. Поэтому, я не поместил здесь полный исходный код, если нужно, его можно посмотреть в архиве. Здесь кратко рассмотрены несколько моментов при переносе приложения из языка C в C#:

  class GtkHello : Gtk.Window
  {

Вместо создания нового окна и описания его параметров, вы создаете подкласс класса Gtk.Window и помещаете весь код описания параметров в его конструктор. Этот подход не является характерной чертой Gtk#, он часто используется в С-программах, если нужно создать более одной копии окна. В любом случае, создавать подклассы в C# так просто, что имеет смысл объявить новый класс, даже для одного нового окна, тем более, что структура программ на C# требует от вас объявить хотя бы один класс.

  this.DeleteEvent += new DeleteEventHandler(DeleteCB);
  button.Clicked += new EventHandler(ButtonClickCB);

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

Листинг 7. Сигналы GTK+, переведенные в родную для C# концепцию событий
  private void DeleteCB (object o, DeleteEventArgs args)
  {
    Application.Quit ();
    args.RetVal = true;
  }

Из-за особенностей модели событий C# прототип для обработчика delete-event немного отличается. Вместо того, чтобы через функцию обратного вызова возвратить значение true, нужно передать его через аргументы args.RetVal. Пара функций gtk_main() и gtk_main_quit() заменены соответственно вызовами методов Application.Run() и Application.Quit().

Полезные инструменты

Существует несколько инструментов, способных значительно облегчить вам жизнь при разработке программ с использованием GTK+. Наиболее полезными считаются Glade, Libglade и Devhelp.

Glade and Libglade

Дизайнер интерфейсов Glade помогает графически создавать интерфейс приложения, вместо того, чтобы в исходном коде описывать в отдельности каждый элемент. Но еще более полезным является второй компонент - библиотека Libglade, она позволяет читать специальный формат XML, который Glade использует при описании интерфейса. С помощью Libglade вы можете создать интерфейс приложения из этого описания, без написания какого-либо кода вообще.

На рисунке 2 показано простое приложение, использующее Libglade для отображения нескольких виджетов.

Рисунок 2. Простое приложение, использующее Libglade
Application using libglade
Application using libglade

В листинге 8, показан полный исходный код приложения, изображенного на рисунке 2.

Листинг 8. Исходный код приложения Libglade
#include <gtk/gtk.h>
#include <glade/glade.h>

int main (int argc, char *argv[])
{
  GladeXML *ui;
  gtk_init(&argc, &argv);

  /* Read user interface description from glade file */
  ui = glade_xml_new ("glade_hello.glade", "main_window", NULL);
  /* Automatically connect signals */
  glade_xml_signal_autoconnect(ui);
  gtk_main();

  return 0;
}

Как видно, весь код занял 17 строчек, включая комментарии и пустые строки. Несмотря на то, что настоящие приложения не будут такими короткими, использование Libglade даст вам огромные преимущества при разработке программ, такие как модульность, понятность и надежность вашего кода.

Если вы хотите более подробно изучить, как построена эта программа, вы можете найти ее, также как все другие примеры из этой статьи, в приложении.

Devhelp

Браузер документации Devhelp разработан для чтения документации в формате, создаваемом программой gtk-doc (стандартной утилитой для создания документации GTK+), которая так же используется в некоторых других родственных проектах, таких как Pango и GNOME. С помощью Devhelp вы можете быстро найти функцию и посмотреть установленную по ней документацию, он помогает получать необходимую информацию намного быстрее. Devhelp – это приложение GNOME, поэтому чтобы запустить его, вам понадобится POSIX-совместимая операционная система(напр. Linux® или Sun Solaris), на которой будет работать GNOME и его библиотеки. Однако для просмотра документации вам не обязательно запускать сам GNOME.

Если вы используете другую платформу, существует множество других путей чтения документации GTK+ . В проекте Mono, есть навигатор Monodoc, который обычно поставляется вместе со справочной информацией по Gtk#. Установочные пакеты GTK+ для Windows также включают документацию в формате, пригодном для чтения средствами Windows, включая Visual Studio®. В конце концов, всегда существует возможность обратиться к документации в сети с помощью веб-обозревателя, однако использование специального инструмента всегда предпочтительнее, так как он работает быстрее и обладает специальными функциями, полезными при поиске в документации.

В следующей статье

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

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


Ресурсы для скачивания


Похожие темы

  • Оригинальная статья;
  • Загрузите Gtk#, привязку GTK+ для Microsoft .NET.
  • Получите официальные исходные коды GTK+.
  • Посетите сайт проекта PyGTK, посвященный созданию привязок GTK+ для языка Python.
  • Gazpacho редактор файлов Glade, написанный на PyGTK.
  • Установите Devhelp ориентированный на программистов, навигатор по документации для GNOME;
  • Ознакомьтесь с библиотекой GNU Gettext предназначенной для перевода интерфейса программы во время её выполнения;
  • Посетите сайи GTK+ , чтобы получить больше информации об инструментарии;
  • Страницы руководства GTK+ API необходимы каждому разработчику;
  • В книге The Official GNOME 2 Developer's Guide Матиса Варкуса (Matthias Warkus) вы найдет информацию по программированию для GNOME 2, включая программирование с GTK+.
  • Посмотрите GNOME - продвинутый рабочий стол для Linux;
  • Попробуйте Xfce, быстрый и простой в использовании рабочий стол;
  • Посетите сайт Gnomefiles, дабы ознакомиться более чем с 1000 программ использующих GTK+.
  • Посетите сайт developerWorks Opensource, чтобы получить больше информации о методах, средствах и изменениях в проектах для разработки с использованием открытых технологий и их использования с продуктами IBM.
  • Используйте инновации в Вашем следующем открытом проекте с помощью IBM trial software, доступного для загрузки и на DVD.
static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Open source
ArticleID=107291
ArticleTitle=Основы GTK+, Часть 2: Как использовать GTK+
publish-date=03302006