LXF81:Qt/KDE

Материал из Linuxformat.

Перейти к: навигация, поиск
Учебник Qt/KDE

Содержание

Здравствуй, KDE!

часть 4 Сегодня мы приступаем к написанию первого настоящего приложения KDE. Андрей Боровский проведет вас по всем этапам – от ручного редактирования make-файлов до программирования в KDevelop и интернационализации.

– Форд, а что эта рыбка делает в моем ухе?

– Переводит для тебя. Это вавилонская рыбка. Если хочешь, посмотри в книге.

Дуглас Адамс, «Путеводитель по Галактике для автостопщиков»

Итак, мы приступаем к программированию KDE-приложений. В самой первой статье этой серии было сказано, что графическая среда KDE – это не просто оболочка на основе Qt. KDE добавляет к Qt новые классы, визуальные элементы и новые возможности. Из этого факта читатель может сделать вывод, что при разработке приложений всегда лучше использовать KDE, чем чистую Qt. Вывод этот неправильный. Дело не только в том, что KDE-приложения имеют, как правило, несколько больший объем, чем Qt-приложения. Гораздо важнее то, что KDE-проект утрачивает переносимость, свойственную Qt. Исходный код Qt-приложения может без всяких дополнительный модификаций компилироваться на Unix, Windows и MacOS (впрочем, для Mac OS существует своя версия KDE), тогда как в случае KDE набор поддерживаемых платформ ограничен, в основном, Unix-системами. Кросс-платформенность – важный фактор в развитии современного программного проекта, и если перенос приложения на другие платформы предполагается хотя бы теоретически, то, скорее всего, лучше отказаться от дополнительных возможностей, предоставляемых KDE.

Мы же начнем знакомство с программированием для KDE с простейшего приложения, аналогичного тому, с помощью которого мы начинали знакомиться с Qt. В системах читателей нашей серии статей все уже должно быть настроено для разработки Qt-приложений. К этим настройкам следует добавить совсем немного. Проверьте еще раз, установлены ли в вашей системе пакеты разработчика для KDE (kdelibsdevel или аналогичные). По аналогии с переменной QtDIR, нам понадобится переменная окружения KDEDIR, которая должна содержать путь к каталогу KDE, например:

export KDEDIR=/opt/kde3
Рисунок 1. KDevelop Assistant.
Рисунок 1. KDevelop Assistant.

По ходу освоения программирования KDE-приложений нам придется обращаться к различным справочным материалам. Много полезной информации можно найти на сайте http://developer.kde.org. Кроме того, в самой вашей системе в процессе установки средств разработки для KDE должен был появиться браузер справочной документации KDevelop Assistant (скорее всего, вы найдете его в группе Development|Documentation стартового меню KDE). KDevelop Assistant (рис. 1) во многом напоминает Qt Assistant. Помимо информации о программировании KDE KDevelop Assistant предоставляет доступ к полной справочной информации по Qt (а также к множеству другой информации, не относящейся непосредственно к программированию KDE).

Теперь мы можем приступить к написанию простейшего приложения. Создадим новую директорию kdeapp1. Исходный текст нашего первого приложения мы сохраним в файле kdeapp1/kdeapp1.cpp (который вы найдете на диске). Этот код заслуживает того, чтобы процитировать его полностью в тексте статьи:

#include <kcmdlineargs.h>
#include <kapplication.h>
#include <klocale.h>
#include <qlabel.h>
int main(int argc, char * argv[])
{
 KCmdLineArgs::init(argc, argv, argv[0], argv[0],"first KDE application", "1.0");
 KApplication app(argc, argv);
 QLabel label(0);
 label.setText(i18n("Hello World!"));
 app.setMainWidget(&label);
 label.show();
 return app.exec();
}

Точно так же, как класс QApplication является главным классом приложения Qt, класс KApplication составляет основу KDE-приложения (заметим, что имена классов KDE начинаются с буквы K, подобно тому, как имена классов Qt начинаются с Q). Класс KApplication объявлен в заголовочном файле kapplication.h (раньше это был файл kapp.h). Класс KApplication является потомком класса QApplication (многие классы KDE наследуют классы Qt со сходной функциональностью) и класса KInstance. Конструкторов у KApplication много, и все они обладают весьма устрашающими списками параметров. К счастью для нас, многие из них имеют значения по умолчанию. Мы передаем конструктору KApplication параметры argc и argv функции main(), но прежде нам необходимо выполнить инициализацию этих параметров. Статический метод init() (точнее, один из перегруженных методов init()) класса KCmdLineArgs, объявленного в файле kcmdlineargs.h, инициализирует приложение таким образом, чтобы объект класса KApplication мог быть сконструирован правильно. В качестве параметров мы передаем методу init() значения argc, argv, имя приложения (используя argv[0]), имя программы, а также строки с описанием и версией программы. В сложных приложениях класс KCmdLineArgs позволяет организовать корректную обработку параметров командной строки приложения, в нашем же случае это просто необходимая формальность перед вызовом конструктора KAppilcation. Существует другой, устаревший способ инициализации объекта KApplication с помощью конструктора с третьим параметром – именем приложения (когда я использую конструктор с третьим параметром, компилятор предупреждает меня, что я отстал от жизни). Все, происходящее в нашей программе дальше, в точности соответствует простейшему приложению Qt, рассмотренному в первой статье этой серии. Объект label класса QLabel будет главным и единственным визуальным элементом нашего приложения. С помощью метода setMainWidget() мы указываем объекту app на главный визуальный элемент. Метод show() делает метку видимой, а метод exec() объекта app запускает цикл обработки сообщений графической системы. Функция i18n() нужна (как и следует из ее имени) для интернационализации приложения. Далее мы рассмотрим интернационализацию нашего первого приложения KDE, пока же вызов i18n() – тоже формальность.

Наша следующая задача – скомпилировать текст программы. Можно набрать команду компиляции вручную, но лучше все-таки обзавестись make-файлом. Для генерации make-файла можно воспользоваться уже знакомой нам утилитой qmake. В директории kdeapp1 дайте команду:

qmake -project -after "INCLUDEPATH += $KDEDIR/include" "LIBPATH +=$KDEDIR/lib" "LIBS = -lkdeui -lkdecore"

Эта команда создает файл проекта с дополнительными значениями переменных. К переменной INCLUDEPATH добавляется путь к заголовочным файлам KDE, к переменной LIBPATH – путь к разделяемым библиотекам KDE, а к переменной LIBS – указание связать исполнимый файл приложения с библиотеками libkdeui и libkdecore. Команда сработает правильно только в том случае, если вы установили переменную окружения KDEEDIR. Получив файл проекта, скомандуйте:

qmake

в результате чего вы получите make-файл для компиляции приложения. На диске, в директории kdeapp1, вы найдете скрипт genmake.sh, содержащий описанные выше вызовы qmake. Получив make-файл, мы можем, наконец, скомпилировать приложение и затем запустить его. Если запустить скомпилированную программу kdeapp1 с ключом --version, можно увидеть некоторые плоды использования KCmdLineArgs. На стандартный терминал будет выведена информация о версии, переданная методу KCmdLineArgs::init().

Интернационализация

Рассмотренная выше программа уже содержит некоторые элементы, необходимые для интернационализации. Мы включили в текст программы заголовочный файл klocale.h, который включает объявления необходимых классов и функций. Строковая константа, используемая в нашей программе, была «обернута» функцией i18n(). Эта функция похожа на методы tr() и trUtf8(), знакомые нам по работе с классами Qt. Функция i18n() получает в качестве параметра строку (char *) в кодировке UTF-8 и заменяет ее на строку перевода из файла ресурсов перевода. Концептуально все это очень похоже на интернационализацию Qt, но на практике существуют весьма значительные отличия.

Мы можем получить список всех строк нашего приложения, подлежащих переводу, с помощью команды:

xgettext -C -ki18n -kI18N_NOOP *.cpp
Рисунок 2. KBabel в работе.
Рисунок 2. KBabel в работе.

Если ваша система ничего не знает про xgettext, см. врезку «Где же xgettext?». Ключ –C указывает команде, что она обрабатывает исходные тексты на C/C++. Ключи -ki18n -kI18N_NOOP указывают, что нужно искать строки, обернутые i18n и I18N_NOOP (I18N_NOOP – макрос, предназначенный для упрощения перевода статического текста за пределами области доступности функции i18n(), в нашей программе он не используется). Мы приказываем утилите искать текст для перевода только в файлах с расширением .cpp. В результате выполнения команды в директории kdeapp1 должен появиться файл messages.po. Этот файл представляет собой список сообщений, предназначенных для перевода. Для обработки файлов с расширением .po используется утилита KBabel, которая в русскоязычной версии KDE именуется как «Утилита локализации приложений (редактор файлов перевода)». Внешне KBabel (рис. 2) напоминает Qt Linquist. В верхнем окне выбираем оригинальную строку, в нижней – пишем перевод.

Где же xgetext ?

Должен отметить, что с интернационализацией приложения у меня возникли неожиданные организационные сложности. В процессе интернационализации используется загадочная утилита xgettext (эта утилита извлекает из различных файлов проекта KDE сообщения, подлежащие переводу на другие языки). Пишу «загадочная», потому, что в моей системе Open SUSE 10 этой утилиты не оказалось, хотя пакет gettext, в котором она, вроде бы, должна находится, был установлен. Поиск в других, не установленных, пакетах результатов не дал, может я плохо искал, не знаю. Зато в процессе поиска обнаружилась утилита kde-xgettext, по поведению идентичная упомянутой xgettext. Поскольку все скрипты разработки KDE ориентированы на использование xgettext, я просто сделал ссылку:

ln /opt/kde3/bin/kde-xgettext /usr/bin/xgettext

Так что если в вашей системе нет утилиты xgettext, вы знаете, что делать.

Откроем файл messages.po в KBabel, выполним перевод единственной подлежащей переводу строки – “Hello World” и сохраним результат под именем kdeapp1.po. На этом работа с ресурсом перевода отнюдь не закончена, файл ресурса требуется еще и скомпилировать. Для компиляции po-файлов служит утилита msgfmt. В окне консоли командуем:

msgfmt kdeapp1.po -o kdeapp1.mo

В результате получаем файл kedapp1.mo, который и является конечным результатом нашей работы по интернационализации. Однако, если теперь мы запустим программу kdeapp1, то увидим, что ничего не изменилось, надпись Hello World! по-прежнему выводится по-английски. Дело в том, что, в отличие от Qt-приложений, программы KDE по умолчанию ищут файлы ресурсов интернационализации в определенных директориях (рабочая директория программы, к сожалению, не входит в их число). Для того, чтобы наша программа «увидела» свой ресурс интернационализации, соответствующий файл следует поместить либо в директорию $(KDEDIR)/share/locale/ru/LC_MESSAGES/ (для чего, естественно, понадобятся права root), либо в «домашнюю» директорию ~/.kde/share/locale/ru/LC_MESSAGES/ (эту директорию вам, скорее всего, придется создать вручную). Для файлов переводов на другие языки предназначены другие директории, например, для французского: $(KDEDIR)/share/locale/fr. Переместив файл kedapp1.mo в указанную директорию, можно, наконец, полюбоваться на локализованный вариант нашей программы (рис. 3).

Рисунок 3. Локализованная программа kdeapp1.
Рисунок 3. Локализованная программа kdeapp1.

Мы познакомились с интернационализацией KDE-приложения, так сказать, вручную. Проще и удобнее выполнять интернационализацию (и не только интернационализацию) в интегрированной среде разработки KDevelop, к изучению которой мы и приступим.

Почему KBabel так называется

Почему утилита для перевода файлов называется KBabel и почему ее эмблемой является рыбка, украшенная флагами разных стран? Корни названия уходят в фантастическую повесть Дугласа Адамса «Путеводитель по Галактике для автостопщиков», в которой присутствовал артефакт под названием «вавилонская рыбка». Рыбка выступала в роли универсального переводчика всего, что ее пользователь видел и слышал. Для того, чтобы рыбка начала переводить, ее нужно было вставить в ухо (см. эпиграф). Поучительную историю о жителях Вавилона, предпринявших грандиозный проект, и не позаботившихся об интернационализации, можно прочесть в Библии, в Книге Бытия.

KDevelop

Среда KDevelop – предпочтительный инструмент разработки приложений KDE, и все последующие приложения в этой серии будут созданы в KDevelop. Между прочим, в KDevelop можно программировать не только для KDE, эта среда хорошо подходит для разработки Qt-приложений. В KDevelop можно работать над созданиями GTK-приложений, приложений, использующих wxWidgets, и даже редактировать и собирать программы, написанные на таких языках программирования, как Java, Pascal, Perl, Python. KDevelop в чем-то похож на Qt Designer, однако его возможности существенно шире, ведь KDevelop – это настоящая интегрированная среда с широкими возможностями управления проектом и отладки приложений. Помимо прочего, KDevelop позволяет автоматизировать создание дистрибутивов и установку приложений. Ну и конечно, в KDevelop реализованы любимые нами функции визуального программирования. Если начинать рассказ о KDevelop с подробного описания приемов работы и возможностей среды, введение получится слишком длинным. Мы пойдем другим путем и будем осваивать KDevelop в процессе решения различных задач KDE-программирования. Начнем с интернационализации.

Рисунок 4. Мастер создания проекта.
Рисунок 4. Мастер создания проекта.

Запустите KDevelop. В меню запуска Development|Integrated Environment вы можете найти несколько вариантов запуска KDevelop, нам подойдет любой. В меню Проект выберите команду Новый проект... и в открывшемся окне Создать новый проект выберите пункт Simple Designer based KDE Application (рис. 4). Для того, чтобы получить доступ к этому пункту, нужно установить флажок Показать все шаблоны проекта. В поле ввода Имя приложения введите kdeapp2.

Пройдите по всем оставшимся окнам мастера создания проекта, и в результате у вас должен появиться новый проект простого KDE-приложения с окном и кнопкой в нем. Соберите проект с помощью команды Сборка|Собрать проект. Если сборка пройдет благополучно, вы сможете запустить приложение (например, с помощью команды Сборка > Выполнить программу) и увидите окно с кнопкой (рис. 5).

Рисунок 5. Программа kdeapp2.
Рисунок 5. Программа kdeapp2.

Мы создали и скомпилировали простое приложение KDE, теперь займемся его интернационализацией и локализацией. В левой части главного окна KDevelop вертикально расположены переключатели вкладок. Перейдите на вкладку Группы файлов, откройте группу Sources и в этой группе выберите файл kdeapp2widget.cpp. В окне редактора кода добавьте в файл строку

#include <klocale.h>

В этом же файле найдите строку

label->setText("Hello World!" );

Эта строка содержит единственный в нашем приложении текст, подлежащий интернационализации явным образом. Замените ее на:

label->setText( i18n("Hello World!") );

Далее нам следует внести некоторые изменения в проект приложения. Корневая директория нашего проекта – kdeapp2. Перейдите в нее директорию и дайте команду:

make -f admin/Makefile.common package-messages

В результате выполнения этой команды в проекте происходит несколько важных изменений. Некоторые из них не заметны (такие как изменения в make-файлах), другие бросаются в глаза. В корневой директории проекта появляется директория po, содержащая файл kdeapp2.pot. Этот файл представляет собой шаблон для всех будущих файлов перевода. Фактически его содержимое аналогично содержимому файла .po, создаваемого утилитой xgettext. Теперь перейдем в KDevelop. Выбираем команду меню Проект|Добавить перевод.... В открывшемся окне выбираем русский язык (ru). Эта команда создает в директории po полезные make-файлы. Она также создает бесполезный для нас файл ru.po нулевого размера. Откройте теперь файл kdeapp2.pot в KBabel, и переведите все (или какие хотите) строки английского текста (обратите внимание, что в список строк для перевода автоматически были добавлены название кнопки Click me! и некоторые другие строки из проекта). Сохраните результат перевода под именем ru.po (заменив уже существующий файл). Теперь пересоберите проект в KDevelop. Для того чтобы интернационализация заработала, новое приложение нужно установить в системе. Запустите KDevelop, откройте проект kdeapp2 и выберите команду меню Сборка|Установить (или Сборка|Установить (с правами root)) Теперь приложение kdeapp2 будет говорить по-русски (рис. 6).

Рисунок 6. Русифицированная программа kdeapp2.
Рисунок 6. Русифицированная программа kdeapp2.

Перейдите в директорию $(KDEDIR)/share/locale/ru/LC_MESSAGES/. В этой директории появился новый файл – kdeapp2.mo, содержащий ресурсы перевода для приложения kdeapp2. Заметим, что так уж совпало, что этот файл содержит перевод строки из приложения kdeapp1 (Hello World!), в связи с чем можно использовать один трюк. Удалите из текущей директории файл kdeapp1.mo (если, конечно, вы поместили его сюда). В исходном тексте приложения kdeapp1 добавьте строку (она должна быть первой в теле функции main()):

KLocale::setMainCatalogue("kdeapp2");

Эта строка заставит программу kdeapp1 использовать файлы ресурсов (каталоги сообщений) программы kdeapp2. Перекомпилируйте и запустите программу kdeapp1. Вы увидите, что строка “Hello World!” переведена правильно (то есть также, как и в программе kdeapp2). Возможность использовать «чужие» каталоги переводов при программировании KDE-приложений чрезвычайно полезна. Речь идет не только об экономии места на диске. Используя в своем приложении ресурсы другого приложения KDE, переведенного на многие языки, вы «бесплатно» получаете перевод своего приложения на языки, которых можете и не знать. Если сверх стандартных надписей ваше приложение содержит специфический текст, не встречающийся в других программах, ничто не мешает вам использовать несколько каталогов (других приложений и ваш собственный). В нашем примере мы использовали класс KLocale, который управляет различными региональными настройками программы. Метод setMainCatalogue(), устанавливающий основной каталог переводов приложения – статический, но у каждого приложения есть свой объект класса KLocale. Указатель на этот объект можно получить с помощью статического метода KGlobal::locale(). Например:

KLocale * locale = KGlobal::locale();
locale->setLanguage(QString("ru"));
locale->setMainCatalogue("kdeapp2");

Вы не должны уничтожать объект locale явным образом. Касс KGlobal объединяет множество методов, позволяющих получить указатели на различные глобальные объекты вашего приложения (эти объекты существуют в единственном экземпляре, создаются и уничтожаются автоматически). Следует помнить, что большая часть глобальных объектов будет создана после вызова конструктора KApplication. Например, если вызвать KGlobal::locale() до инициализации объекта класса KApplication, метод вернет NULL.

Чтобы убедиться в том, какую мощь предоставляет нам возможность использования каталогов других приложений, создадим еще одно стандартное приложение в KDevelop. В окне Создать новый проект в группе KDE выберите пункт Application Framework (проект приложения со стандартной строкой меню и панелью быстрого доступа). В поле ввода Имя приложения введите kdeapp3. После завершения мастера создания проекта соберите и запустите новое приложение. Вы увидите англоязычный интерфейс меню и пояснений к кнопкам быстрого доступа. Нас сейчас не интересует логика работы этого приложения, мы займемся только его интернационализацией. В среде KDevelop перейдите на вкладку Группы файлов, откройте группу Sources и в этой группе выберите файл main.cpp (главный файл приложения). В начале функции main() добавьте строку:

KLocale::setMainCatalogue("konqueror");

Теперь снова скомпилируйте и запустите приложение и… (о чудо!) весь интерфейс приложения теперь переведен на русский язык! В функции main() мы указали, что наше приложение должно использовать каталог переводов приложения konqueror, а konqueror уж точно содержит перевод всех надписей интерфейса простого приложения KDE Application Framework.

Функция i18n() и макрос I18N_NOOP

Функция i18n() позволяет избежать неоднозначностей, возникающих при переводе интерфейсов с английского языка. Допустим, что англоязычный интерфейс содержит две строки “Build” в двух разных смыслах – «собрать» и «сборка». Если мы будем писать в тексте программы просто

i18n("Build")

то в файл перевода будет включена только одна строка “Build”, для которой можно будет указать, соответственно, только один вариант перевода. Но мы можем вызывать i18n и иначе. Там, где подразумевается «собрать», мы напишем:

i18n("verb", "Build")

Строка “verb” (англ. «глагол») здесь является произвольной (это просто комментарий), можно было бы написать что-нибудь другое. На месте варианта «сборка» мы напишем:

i18n("noun", "Build")

в результате в файле перевода появятся две строки “Build” с пояснениями “verb” и “noun”, для каждого из используемых значений слова Build.

Макрос I18N_NOOP сам не выполняет никакой работы по переводу строки. Макрос только помечает строку для извлечения утилитой xgettext при построении заготовки файла перевода (этот макрос иногда используется для маркировки текста, расположенного до инициализации объекта KApplication, когда функция i18n() еще не может быть вызвана). Для подстановки перевода в программе все равно придется вызывать i18n().

Мы начали знакомство с KDevelop с интернационализации приложений, но интернационализацией его возможности отнюдь не ограничиваются! В следующей статье мы узнаем, как создавать в KDevelop KDE-приложения различных типов.

Личные инструменты
  • Купить электронную версию
  • Подписаться на бумажную версию