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

LXF91:GTK+

Материал из Linuxformat
Версия от 10:04, 1 декабря 2008; Crazy Rebel (обсуждение | вклад)

(разн.) ← Предыдущая | Текущая версия (разн.) | Следующая → (разн.)
Перейти к: навигация, поиск
GTK+ Разработка переносимых приложений с графическим интерфейсом пользователя

Содержание

Glade на новый лад

ЧАСТЬ 5 Описать интерфейс программы в XML и подгрузить его на лету? Совершенно необязательно покупать Visual Studio и устанавливать .NET 3.0Андрей Боровский покажет, как решить задачу средствами Glade!

Компьютеры бесполезны, ибо они дают только ответы.
Сальвадор Дали

Если бы великий художник лучше разбирался в компьютерах, он сумел бы найти в них гораздо больше недостатков. Чем занимаются большую часть времени настольные компьютеры? Они выполняют работу более примитивных устройств. Компьютер может быть видеомагнитофоном, радиоприемником, будильником и, конечно, конечно, пишущей машинкой. Ну а кроме всего прочего, любая операционная система на любой платформе предлагает программу, выполняющую функции простейшего микрокалькулятора. Непреложный факт – микрокалькулятор обязательно будет сэмулирован на любой системе, достаточно мощной доля того, чтобы его эмулировать. Нет, я, конечно, понимаю, что компьютерные проигрыватели, компьютерные будильники и компьютерные микрокалькуляторы полезны. Я и сам с удовольствием ими пользуюсь. Я лишь хочу сказать, что компьютеры все еще по-настоящему не революционизировали нашу жизнь. Ну а в ожидании революции, после которой все будет не так, как было, мы, уважаемый читатель, тоже напишем простейшую программу-калькулятор, и воспользуемся для этого, естественно, инструментарием GTK+.

В предыдущей статье мы научились создавать проекты графических приложений с помощью Glade. Используя Glade 2.x, мы сгенерировали не только описание интерфейса программы, но и заготовку ее исходного кода, включая вспомогательные функции и обработчики событий. Сегодня мы рассмотрим другой способ работы с Glade. При новом подходе Glade используется исключительно для проектирования интерфейса, а исходный код приложения пишется программистом с помощью других средств (по-видимому, этот способ работы станет единственно возможным в Glade 3.x).

Описание интерфейса, созданное Glade в формате XML, и исходный код программы связываются между собой с помощью функций библиотеки libglade. Все это немного похоже на систему XMLGUI, реализованную в KDE. Разница заключается в том, что в XMLGUI внешний XML-документ описывает лишь отдельные элементы интерфейса программы, тогда как при работе с libglade все описание интерфейса загружается из XML-файла [Это не означает, что в Qt/KDE нет технологии, аналогичной libglade. Интерфейсы, созданные в Qt Designer, также можно загружать и обрабатывать на лету, – прим. ред.].

Приступим к проектированию

Для того, чтобы понять, как работает библиотека libglade, необходимо сначала разобраться, что представляет собой проект Glade. Разбираться, как вы уже поняли, мы будем на примере программымикрокалькулятора (Рис. 1).

Рис. 1

Программа-микрокалькулятор.


Как можно заметить, наш калькулятор довольно примитивен. Поддерживаются только операции сложения и вычитания, причем над целыми числами. Отсутствует контроль переполнения при выполнении арифметических действий. При желании вы можете нарастить функциональность нашего микрокалькулятора сами – добавить поддержку чисел с плавающей точкой, тригонометрические функции, встроенный язык программирования... Нет пределов совершенству. Мы же сосредоточимся на программировании интерфейса калькулятора средствами GTK+, Glade и libglade.

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

Свойству «Имя» объекта главного окна мы присваиваем значение rootwnd. Это имя нам нужно запомнить, так как оно будет играть важную роль при взаимодействии с библиотекой libglade. В верхней части главного окна расположено текстовое поле GtkEntry, которое будет служить индикаторной панелью калькулятора. Кнопки калькулятора – это объекты GtkButton. Вот, собственно, и все, других элементов управления наш виртуальный калькулятор (как и его «железный» собрат) не предполагает.

Иерархию объектов-контейнеров и элементов управления, из которых состоит калькулятор, проще (и полезнее) показать, нежели описать. Для этого нужно всего лишь открыть окно «Дерево эл. управления» среды Glade (Рис. 2). Перед вами появится древовидный список всех визуальных элементов.

Рис. 2

Окно просмотра структуры элементов интерфейса.

Объекты-контейнеры являются внутренними узлами дерева, а элементы управления – его «листьями». Если мы теперь сохраним проект Glade под именем calculator, на диске появится файл calculator.glade. Именно этот файл содержит описание спроектированного нами графического интерфейса программы. Если мы откроем файл calculator.glade в текстовом редакторе, то увидим примерно следующее:

<?xml version=”1.0” standalone=”no”?> <!--*- mode: xml -*-->
<!DOCTYPE glade-interface SYSTEM “http://glade.gnome.org/glade-2.0.dtd”>
<glade-interface>
<widget class=”GtkWindow” id=”rootwnd”>
  <property name=”visible”>True</property>
  <property name=”title” translatable=”yes”>Калькулятор</property>
...
    <signal name=”destroy” handler=”gtk_main_quit” last_modification_
 time=”Tue, 13 Mar 2007 21:18:21 GMT”/>
    <child>
     <widget class=”GtkVBox” id=”vbox1”>
       <property name=”visible”>True</property>
       ...
       <child>
              <widget class=”GtkEntry” id=”entry1”>
                <property name=”visible”>True</property>
                 ...
       </child>
...

Таким образом, файл *.glade представляет собой XML-документ, в котором содержатся списки значений свойств каждого объекта интерфейса, а иерархия интерфейса реализована при помощи иерархии вложенных тегов.

Немного кода

Теперь, когда у нас есть описание графического интерфейса, созданное Glade, нам будет очень просто написать демонстрирующую его программу. Открою небольшой секрет – функциональность libglade используется самой средой Glade, которая должна отображать создаваемый интерфейс в отдельном окне. Исходный код программы, загружающей и отображающей графический интерфейс, описанный в файле calculator.glade, состоит примерно из десяти строк (вы найдете его на диске в файле calculator-1.c).

 #include <stdlib.h>
 #include <stdio.h>
 #include <gtk/gtk.h>
 #include <glade/glade.h>
 int main (int argc, char **argv)
 {
   GladeXML *xml;
    gtk_init(&argc, &argv);
    glade_init();
    xml = glade_xml_new(“calculator.glade, “rootwnd”, NULL);
    if (!xml) {
             g_warning(“Failed to create the interface”);
             return 1;
    }
    gtk_main();
    return 0;
 }

Обратите внимание, что теперь, помимо заголовочного файла gtk/gtk.h мы включаем в программу файл glade/glade.h. Он содержит объявления функций и типов данных библиотеки libglade, а именно она будет служить рабочей лошадкой нашей программы. Собственно программа начинается с вызова знакомой нам функции gtk_init(). Далее мы вызываем новую функцию glade_init(). Ее задача заключается в том, чтобы инициализировать систему libglade. Связывание программы с XML-файлом, описывающим ее интерфейс, выполняется функцией glade_xml_new(). Первым аргументом этой функции должен быть файл, созданный в Glade, вторым – имя корневого элемента иерархии графических объектов интерфейса, определенного в этом файле.

Третий параметр glade_xml_new() описывает домен перевода приложения (translation domain). В качестве значения этого параметра мы можем передать NULL. Функция glade_xml_new() возвращает указатель на структуру GladeXML, который мы сохраняем в переменной xml. Структура GladeXML инкапсулирует описание интерфейса, созданного Glade. Библиотека libglade экспортирует несколько функций, имена которых начинаются с префикса glade_xml_. Эти функции позволяют управлять элементами интерфейса, созданного с помощью Glade, и каждой из них в качестве одного из параметров следует передавать указатель на структуру GladeXML.

Впрочем, в версии calculator-1.c значение переменной xml еще не востребовано. Вызов glade_xml_new() приводит к загрузке файла описания интерфейса в программу, а библиотека libglade позаботится о его правильном отображении. Нам остается только запустить цикл обработки сообщений с помощью функции gtk_main(). Скомпилируем нашу программу, используя следующую команду:

 gcc calculator-1.c -o calculator `pkg-config -- cflags --libs libglade-2.0`

На этот раз в качестве аргумента утилиты pkg-config мы используем имя пакета libglade-2.0. Теперь можно запустить программу calculator. Вы, конечно, сразу заметите, что хотя программа правильно отображает все элементы пользовательского интерфейса, она ничего не делает (даже завершиться как следует не умеет). Это вполне естественно, ведь мы еще не определили ни одного обработчика сигнала.

Снова откройте (если, вдруг, вы его закрыли) проект calculator в среде Glade. Прежде всего, выделим в редакторе свойств объект rootwnd, перейдем на вкладку «Сигналы» редактора свойств и создадим обработчик сигнала destroy. В качестве процедуры обработчика из раскрывающегося списка «Обработчик» выберем функцию gtk_main_quit(). Вот так просто можно связать обработчик сигнала закрытия окна и функцию завершения работы программы. Далее выделим в редакторе форм одну из цифровых кнопок калькулятора (Рис. 3).

Рис. 3

Окно редактора формы с выделенной кнопкой.

Всем цифровым кнопкам присвоены имена вида numeric_button_x, где x – число от 0 до 9. Перейдем на вкладку «Сигналы» редактора свойств и создадим для выбранной кнопки обработчик сигнала clicked. Процедура обработчика получит имя on_numeric_button_x_clicked(). Переименуем обработчик в on_numeric_button_clicked(). При назначении обработчика каждой кнопке в поле ввода «Объект» укажем значение entry1 (Рис. 4).

Рис. 4

Окно редактора свойств в режиме создания обработчика сигнала.

Напомню, что поле ввода «Объект» позволяет задать имя объекта, указатель на который будет передан процедуре обработчика в качестве дополнительного параметра. Объект entry1 – это объект класса GtkEntry«индикаторная панель» нашего калькулятора. Вполне естественно, что обработчики событий clicked цифровых кнопок должны иметь доступ к объекту, представляющему индикаторную панель. Мы переименовали обработчик сигнала clicked в on_numeric_button_clicked(), чтобы подчеркнуть, что у нас будет один обработчик сигнала clicked для всех цифровых кнопок.

Назначим теперь обработчик on_numeric_button_clicked() сигналам clicked всех кнопок с цифрами (не забудем указать объект entry1 в качестве дополнительного параметра обработчика каждой кнопки). Кнопки +, –, = и C имеют имена operation_button_plus, operation_button_minus, operation_button_equal и operation_button_cancel соответственно. В среде Glade создадим для каждой кнопки свой обработчик события clicked (соответственно функции on_operation_button_plus_clicked(), on_operation_button_minus_clicked(), on_operation_button_equal_clicked() и on_operation_button_cancel_clicked()). Обработчикам on_operation_button_equal_clicked() и on_operation_button_cancel_clicked() в качестве дополнительного параметра должен быть передан объект entry1. Сохраним проект Glade. На этом визуальное программирование калькулятора закончено, и нам остается довершить воплощение наших идей в коде. Вариант программы с обработчиками сигналов вы найдете в файле calculator-2.c, основанном на файле calculator-1.c.

Доведем до совершенства!

Прежде всего нам нужно написать функции-обработчики сигналов:

 void on_numeric_button_clicked(gpointer user_data, GtkButton *button)
 {
   int i = atoi(gtk_button_get_label(button));
   if (mode == COPY_MODE) {
     bgvalue = fgvalue;
     fgvalue = 0;
     mode = INPUT_MODE;
   }
   fgvalue = fgvalue*10 + i;
   sprintf(screen,%li\0”, fgvalue);
   gtk_entry_set_text(GTK_ENTRY(user_data), screen);
 }
 void on_operation_button_plus_clicked(GtkButton *button)
 {
   mode = COPY_MODE;
   op_state = OP_PLUS;
 }
 void on_operation_button_minus_clicked(GtkButton *button)
 {
   mode = COPY_MODE;
   op_state = OP_MINUS;
 }
 void on_operation_button_equal_clicked(gpointer user_data, GtkButton *button)
 {
   switch (op_state) {
     case OP_PLUS:
      fgvalue += bgvalue;
      break;
     case OP_MINUS:
      fgvalue = bgvalue - fgvalue;
     default:;
   }
   sprintf(screen,%li\0”, fgvalue);
   gtk_entry_set_text(GTK_ENTRY(user_data), screen);
   mode = COPY_MODE;
 }
 void on_operation_button_cancel_clicked(gpointer user_data, GtkButton *button)
 {
   bgvalue = fgvalue = 0;
   mode = COPY_MODE;
   op_state = OP_NONE;
   sprintf(screen,%li\0”, fgvalue);
   gtk_entry_set_text(GTK_ENTRY(user_data), screen);
 }

Необходимо указать одну особенность взаимодействия программы libglade с обработчиками сигналов. Если при связывании сигнала с обработчиком указывается дополнительный параметр, этот параметр будет первым аргументом функции-обработчика. Вторым аргументом будет указатель на объект-источник сигнала. Например, в обработчике on_numeric_button_clicked() первый аргумент указывает на объект entry1, а второй аргумент – на объект numeric_button_x, для которого вызван обработчик. Если же при связывании обработчика с сигналом дополнительные параметры не указываются, первым (и единственным) аргументом функции-обработчика будет указатель на объект-источник сигнала. Например, в обработчике on_operation_button_plus_clicked() первым аргументом является указатель на объект operation_button_plus, для которого вызван обработчик.

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

    В обработчике on_numeric_button_clicked() нам необходимо полу-

чить цифру, соответствующую нажатой кнопке. Мы не можем переда- вать цифру в дополнительном параметре обработчика, поскольку этот параметр уже занят объектом entry1. Вместо этого мы просто считыва- ем цифру, являющуюся меткой нажатой кнопки, с помощью функции gtk_button_get_label(). Текст элемента ввода entry1 устанавливается с помощью функции gtk_entry_set_text().

    Хотя мы и создали обработчики всех сигналов, но если бы мы ском-

пилировали программу на данном этапе, ее элементы управления все равно ничего бы не делали. Для того чтобы наладить в программе обра- ботку сигналов, недостаточно написать их обработчики. Необходимо решить еще одну проблему, которая в явном или неявном виде возни- кает во всех средах визуального программирования. В описании интер- фейса программы мы указали, например, что обработчиком события clicked для кнопки operation_button_minus является нечто по имени on_operation_button_minus_clicked(). На этапе визуального программи- рования строка on_operation_button_minus_clicked() не является име- нем функции, поскольку никакой функции еще нет. Фактически мы просто добавили в описание интерфейса программы имя обработчика сигнала. Если бы связывание сигнала и обработчика выполнялось на этапе компоновки программы (как это происходит в «настоящих» IDE), компоновщик нашел бы функцию on_operation_button_minus_clicked() и связал бы ее с сигналом. Но в GTK+ связывание сигналов и обработ- чиков происходит во время выполнения программы. Каким же образом программа находит функцию, соответствующую имени обработчика?

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