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

LXF84:Qt/KDE

Материал из Linuxformat
(Различия между версиями)
Перейти к: навигация, поиск
(Новая: {{Цикл/Qt/KDE}} == Модули KDE == ''ЧАСТЬ 7: Модули, расширения, расширения расширений.... всKIPIте от напряжения и н...)
 
Строка 4: Строка 4:
  
 
=== Модули KDE ===
 
=== Модули KDE ===
Модули KDE (KDE plugins) представляют собой логическое разви-
+
Модули KDE (KDE plugins) представляют собой логическое развитие идеи объектно-ориентированной оболочки. Читатели этой статьи
тие идеи объектно-ориентированной оболочки. Читатели этой статьи
+
наверняка знакомы с базовыми концепциями модулей как средств расширения уже существующих приложений. Механизм модулей позволяет нам не умножать сущностей (приложений) без необходимости всякий раз, когда нам требуется новая функциональность. По самой своей
наверняка знакомы с базовыми концепциями модулей как средств рас-
+
природе модули тесно связаны с теми приложениями, чьи возможности они расширяют. Нельзя написать «просто модуль». Мы рассмотрим
ширения уже существующих приложений. Механизм модулей позволя-
+
ет нам не умножать сущностей (приложений) без необходимости вся-
+
кий раз, когда нам требуется новая функциональность. По самой своей
+
природе модули тесно связаны с теми приложениями, чьи возможнос-
+
ти они расширяют. Нельзя написать «просто модуль». Мы рассмотрим
+
 
программирование модулей для наиболее популярных приложений
 
программирование модулей для наиболее популярных приложений
 
KDE и начнем с написания модуля для компонента KParts.
 
KDE и начнем с написания модуля для компонента KParts.
Строка 17: Строка 12:
 
=== K-Части ===
 
=== K-Части ===
 
Модули KParts широко применяются, например, в браузере Konqueror.
 
Модули KParts широко применяются, например, в браузере Konqueror.
Сами компоненты KParts представляют собой еще один способ рас-
+
Сами компоненты KParts представляют собой еще один способ расширения возможностей приложения. Примером их использования
ширения возможностей приложения. Примером их использования
+
являются встроенные в Konqueror утилиты просмотра файлов различных типов. Каждый раз, когда Konqueror открывает какой-либо файл
являются встроенные в Konqueror утилиты просмотра файлов различ-
+
ных типов. Каждый раз, когда Konqueror открывает какой-либо файл
+
 
для просмотра, он загружает соответствующий компонент, который
 
для просмотра, он загружает соответствующий компонент, который
 
встраивается в интерфейс браузера и отображает содержимое файла.
 
встраивается в интерфейс браузера и отображает содержимое файла.
Строка 27: Строка 20:
 
простой дизайн и небольшие размеры основного приложения. Следует
 
простой дизайн и небольшие размеры основного приложения. Следует
 
отметить, что универсализм Konqueror иногда подвергается критике,
 
отметить, что универсализм Konqueror иногда подвергается критике,
поскольку в Unix традиционно преобладает другой подход – использо-
+
поскольку в Unix традиционно преобладает другой подход – использование большого числа независимых приложений, каждое из которых
вание большого числа независимых приложений, каждое из которых
+
 
делает что-то одно. Мы оставим в стороне вопрос о том, нужно ли
 
делает что-то одно. Мы оставим в стороне вопрос о том, нужно ли
 
писать модули, и ответим на вопрос, как это можно сделать. Модули
 
писать модули, и ответим на вопрос, как это можно сделать. Модули
 
KParts расширяют возможности компонентов KParts, то есть, с точки
 
KParts расширяют возможности компонентов KParts, то есть, с точки
зрения основной программы Konqueror, модули являются расширени-
+
зрения основной программы Konqueror, модули являются расширениями расширений. Поддержка модулей KParts возможна, естественно,
ями расширений. Поддержка модулей KParts возможна, естественно,
+
 
не только в Konqueror, но и в любом приложении, поддерживающем
 
не только в Konqueror, но и в любом приложении, поддерживающем
 
соответствующие компоненты KParts.
 
соответствующие компоненты KParts.
  
 
Возможно, на этом этапе у читателя возникает вопрос – а зачем
 
Возможно, на этом этапе у читателя возникает вопрос – а зачем
вообще нужны модули Konqueror? Необходимость в механизме моду-
+
вообще нужны модули Konqueror? Необходимость в механизме модулей была бы очевидна, если бы Konqueror был закрытым приложением, однако, при наличии исходных текстов, мы можем вносить изменения непосредственно в код компонентов KParts. Это верно, и тем не
лей была бы очевидна, если бы Konqueror был закрытым приложени-
+
ем, однако, при наличии исходных текстов, мы можем вносить изме-
+
нения непосредственно в код компонентов KParts. Это верно, и тем не
+
 
менее модули оказываются полезны и в случае открытого приложения.
 
менее модули оказываются полезны и в случае открытого приложения.
 
Главное преимущество использования модулей заключается в лучшей
 
Главное преимущество использования модулей заключается в лучшей
 
структурной организации кода. Нам легче писать и отлаживать модули,
 
структурной организации кода. Нам легче писать и отлаживать модули,
связанные с приложением с помощью строго определенного интер-
+
связанные с приложением с помощью строго определенного интерфейса, нежели вносить изменения в исходные тексты приложения,
фейса, нежели вносить изменения в исходные тексты приложения,
+
 
написанного другими программистами. К этому следует добавить и
 
написанного другими программистами. К этому следует добавить и
чисто организационный момент – для того, чтобы изменения, сделан-
+
чисто организационный момент – для того, чтобы изменения, сделанные вами в приложении, попали в его основную версию, они должны
ные вами в приложении, попали в его основную версию, они должны
+
быть одобрены другими разработчиками, тогда как в случае с модулями у вас есть возможность самостоятельно решить, какие функции
быть одобрены другими разработчиками, тогда как в случае с моду-
+
лями у вас есть возможность самостоятельно решить, какие функции
+
 
являются полезными, а какие – нет.
 
являются полезными, а какие – нет.
  
Что же представляет собой модуль KParts с точки зрения взаимо-
+
Что же представляет собой модуль KParts с точки зрения взаимодействия с приложением? Модуль – это динамическая библиотека
действия с приложением? Модуль – это динамическая библиотека
+
(файл *.so), которая загружается приложением, использующим компонент KParts. Модуль может добавлять собственные элементы управления в меню и на панель инструментов приложения. В некоторых случаях
(файл *.so), которая загружается приложением, использующим компо-
+
модули также изменяют содержимое главного окна приложения (которое в этот момент управляется соответствующим компонентом KParts).
нент KParts. Модуль может добавлять собственные элементы управле-
+
ния в меню и на панель инструментов приложения. В некоторых случаях
+
модули также изменяют содержимое главного окна приложения (кото-
+
рое в этот момент управляется соответствующим компонентом KParts).
+
  
 
В качестве примера мы напишем модуль для компонента KHTMLPart.
 
В качестве примера мы напишем модуль для компонента KHTMLPart.
 
Как нетрудно догадаться, этот компонент используется для просмотра
 
Как нетрудно догадаться, этот компонент используется для просмотра
страниц HTML. Фактически KHTMLPart представляет собой полно-
+
страниц HTML. Фактически KHTMLPart представляет собой полноценный встраиваемый HTML-браузер с поддержкой JavaScript. Класс
ценный встраиваемый HTML-браузер с поддержкой JavaScript. Класс
+
 
KHTMLPart является потомком класса KParts::ReadOnlyPart, то есть
 
KHTMLPart является потомком класса KParts::ReadOnlyPart, то есть
он предназначен для просмотра, а не для модификации открывае-
+
он предназначен для просмотра, а не для модификации открываемого файла. Наш модуль будет сохранять открытую в компоненте
мого файла. Наш модуль будет сохранять открытую в компоненте
+
 
KHTMLPart HTML-страницу в простом текстовом формате.
 
KHTMLPart HTML-страницу в простом текстовом формате.
  
 +
[[Изображение:Img 84 73 1.jpg|thumb|(Рис. 1) Создаем проект модуля KParts.]]
 
Модуль для KHTMLPart удобен в качестве примера потому, что в
 
Модуль для KHTMLPart удобен в качестве примера потому, что в
 
KDevelop есть шаблон проекта такого модуля. В окне Создать новый
 
KDevelop есть шаблон проекта такого модуля. В окне Создать новый
Строка 79: Строка 59:
 
textsaverFactory (вспомогательный класс). Оба класса реализованы в
 
textsaverFactory (вспомогательный класс). Оба класса реализованы в
 
файлах plugin_textsaver.h и plugin_textsaver.cpp. Прежде чем вносить
 
файлах plugin_textsaver.h и plugin_textsaver.cpp. Прежде чем вносить
изменения в классы, добавим в файл plugin_textsaver.cpp дополни-
+
изменения в классы, добавим в файл plugin_textsaver.cpp дополнительные заголовочные файлы:
тельные заголовочные файлы:
+
<source lang="cpp-qt">include <stdio.h>
include <stdio.h>
+
 
#include <kfiledialog.h>
 
#include <kfiledialog.h>
#include <qcstring.h>
+
#include <qcstring.h></source>
 
+
Рассмотрим сначала класс textsaverFactory, являющийся потомком класса KParts::Factory и управляющий созданием объекта класса
Рассмотрим сначала класс textsaverFactory, являющийся потом-
+
ком класса KParts::Factory и управляющий созданием объекта класса
+
 
Plugintextsaver (т.е., этот класс реализует фабрику объектов). Такой объект
 
Plugintextsaver (т.е., этот класс реализует фабрику объектов). Такой объект
 
создается методом textsaverFactory::createObject(), который сгенерирован
 
создается методом textsaverFactory::createObject(), который сгенерирован
 
автоматически. Нам вообще ничего не нужно было бы менять в классе
 
автоматически. Нам вообще ничего не нужно было бы менять в классе
 
textsaverFactory, если бы не чистый виртуальный метод createPartObject().
 
textsaverFactory, если бы не чистый виртуальный метод createPartObject().
Сам по себе он нам не нужен, но для того, чтобы наш модуль мог скомпи-
+
Сам по себе он нам не нужен, но для того, чтобы наш модуль мог скомпилироваться, мы должны создать его реализацию. Странно, что KDevelop,
лироваться, мы должны создать его реализацию. Странно, что KDevelop,
+
 
по крайней мере в версии 3.4.2, не делает этого автоматически:
 
по крайней мере в версии 3.4.2, не делает этого автоматически:
KParts::Part* createPartObject(
+
<source lang="cpp-qt">KParts::Part* createPartObject(
 
QWidget*, const char*, QObject*,
 
QWidget*, const char*, QObject*,
 
const char*, const char*, const QStringList&)
 
const char*, const char*, const QStringList&)
{return 0;}
+
{return 0;}</source>
 
Помимо прочего, разделяемая библиотека, содержащая наш
 
Помимо прочего, разделяемая библиотека, содержащая наш
 
модуль, должна экспортировать функцию, создающую объект класса
 
модуль, должна экспортировать функцию, создающую объект класса
textsaverFactory. Эта функция, сгенерированная автоматически, экс-
+
textsaverFactory. Эта функция, сгенерированная автоматически, экспортируется в формате C:
портируется в формате C:
+
<source lang="cpp-qt">extern “C”
extern “C”
+
 
{
 
{
 
void* init_libtextsaverplugin()
 
void* init_libtextsaverplugin()
Строка 109: Строка 84:
 
return new textsaverFactory;
 
return new textsaverFactory;
 
}
 
}
}
+
}</source>
 
Приложение, вызывающее функцию init_libtextsaverplugin(), знает,
 
Приложение, вызывающее функцию init_libtextsaverplugin(), знает,
 
что делать с возвращенным указателем.
 
что делать с возвращенным указателем.
Перейдем к классу Plugintextsaver. Приложение, а точнее, ком-
+
 
понент KHTMLPart, взаимодействует с нашим модулем при помощи
+
Перейдем к классу Plugintextsaver. Приложение, а точнее, компонент KHTMLPart, взаимодействует с нашим модулем при помощи
вызова трех методов этого класса – конструктора, деструктора и мето-
+
вызова трех методов этого класса – конструктора, деструктора и метода slotAction(). Метод slotAction() представляет собой слот, который
да slotAction(). Метод slotAction() представляет собой слот, который
+
 
вызывается всякий раз, когда пользователь выбирает пункт меню или
 
вызывается всякий раз, когда пользователь выбирает пункт меню или
 
нажимает кнопку, связанную с модулем. Этот метод содержит код,
 
нажимает кнопку, связанную с модулем. Этот метод содержит код,
который, собственно, и отвечает за выполнение команды. В конструк-
+
который, собственно, и отвечает за выполнение команды. В конструкторе класса Plugintextsaver создается объект-действие:
торе класса Plugintextsaver создается объект-действие:
+
<source lang="cpp-qt">Plugintextsaver::Plugintextsaver( QObject* parent, const char* name )
Plugintextsaver::Plugintextsaver( QObject* parent, const char* name )
+
 
: Plugin( parent, name )
 
: Plugin( parent, name )
 
{
 
{
Строка 126: Строка 99:
 
this, SLOT(slotAction()),
 
this, SLOT(slotAction()),
 
actionCollection(), “plugin_action” );
 
actionCollection(), “plugin_action” );
}
+
}</source>
 
Этот объект будет добавлен в коллекцию действий приложения,
 
Этот объект будет добавлен в коллекцию действий приложения,
 
вызвавшего компонент KHTMLPart, загрузивший наш модуль. Тем, кто
 
вызвавшего компонент KHTMLPart, загрузивший наш модуль. Тем, кто
не совсем понимает, о очем идет речь, рекомендуем прочитать поза-
+
не совсем понимает, о очем идет речь, рекомендуем прочитать позапрошлую статью этого цикла ([[LXF82:Qt/KDE|LXF82]]), в которой мы познакомились с
прошлую статью этого цикла (LXF82), в которой мы познакомились с
+
объектами-действиями. Мы указываем объекту-действию имя соответствующего элемента управления и слот, который должен обрабатывать
объектами-действиями. Мы указываем объекту-действию имя соответ-
+
ствующего элемента управления и слот, который должен обрабатывать
+
 
команду. Очевидно, что в общем случае мы можем зарегистрировать
 
команду. Очевидно, что в общем случае мы можем зарегистрировать
 
несколько действий для нашего модуля. Нам осталось написать только
 
несколько действий для нашего модуля. Нам осталось написать только
 
сам метод slotAction():
 
сам метод slotAction():
void Plugintextsaver::slotAction()
+
<source lang="cpp-qt">void Plugintextsaver::slotAction()
 
{
 
{
 
if ( !parent()->inherits(“KHTMLPart”) )
 
if ( !parent()->inherits(“KHTMLPart”) )
 
{
 
{
 
QString title( i18n(“HTML Only”));
 
QString title( i18n(“HTML Only”));
QString text(i18n(“Only HTML documents can be saved with this
+
QString text(i18n(“Only HTML documents can be saved with this plugin.”));
plugin.”));
+
 
KMessageBox::sorry( 0, text, title );
 
KMessageBox::sorry( 0, text, title );
 
return;
 
return;
 
}
 
}
 
KHTMLPart *part = dynamic_cast<KHTMLPart *>(parent());
 
KHTMLPart *part = dynamic_cast<KHTMLPart *>(parent());
QString FN = KFileDialog::getSaveFileName(“”, “”, 0, “Save Text File
+
QString FN = KFileDialog::getSaveFileName(“”, “”, 0, “Save Text File As”);
As”);
+
 
if (FN == “”) return;
 
if (FN == “”) return;
 
QString Cmd = “html2text > “ + FN;
 
QString Cmd = “html2text > “ + FN;
Строка 155: Строка 124:
 
fwrite((char*) Text.data(), 1, Text.length(), F);
 
fwrite((char*) Text.data(), 1, Text.length(), F);
 
pclose(F);
 
pclose(F);
}
+
}</source>
 
У модуля должна быть какая-то возможность взаимодействовать
 
У модуля должна быть какая-то возможность взаимодействовать
 
с другими элементами приложения-хозяина (этот термин не следует
 
с другими элементами приложения-хозяина (этот термин не следует
Строка 166: Строка 135:
 
KHTMLPart. На практике это излишне, поскольку модуль никогда не
 
KHTMLPart. На практике это излишне, поскольку модуль никогда не
 
будет вызван для какого-либо иного компонента. Далее мы получаем
 
будет вызван для какого-либо иного компонента. Далее мы получаем
указатель на родительский объект KHTMLPart, запрашиваем у пользо-
+
указатель на родительский объект KHTMLPart, запрашиваем у пользователя имя файла для сохранения текста (надеюсь, вы еще не забыли,
вателя имя файла для сохранения текста (надеюсь, вы еще не забыли,
+
 
что должен делать наш модуль) и, с помощью метода documentSource()
 
что должен делать наш модуль) и, с помощью метода documentSource()
 
получаем HTML-текст страницы, открытой компонентом KHTMLPart.
 
получаем HTML-текст страницы, открытой компонентом KHTMLPart.
Метод documentSource() возвращает данные в строке QString в двух-
+
Метод documentSource() возвращает данные в строке QString в двухбайтовой кодировке. Мы преобразуем полученную строку в локальную
байтовой кодировке. Мы преобразуем полученную строку в локальную
+
 
однобайтовую кодировку и сохраняем результат в переменной Text
 
однобайтовую кодировку и сохраняем результат в переменной Text
 
типа QCString. Далее, с помощью функции popen(3), мы запускаем
 
типа QCString. Далее, с помощью функции popen(3), мы запускаем
 
внешнюю утилиту html2text, преобразующую данные HTML в простой
 
внешнюю утилиту html2text, преобразующую данные HTML в простой
 
текст. Этой утилите мы передаем содержимое переменной Text и имя
 
текст. Этой утилите мы передаем содержимое переменной Text и имя
файла для сохранения результата. Применяемый нами прием – исполь-
+
файла для сохранения результата. Применяемый нами прием – использование в программе внешней утилиты для решения нашей задачи,
зование в программе внешней утилиты для решения нашей задачи,
+
 
очень широко распространен в Unix-системах.
 
очень широко распространен в Unix-системах.
Хотя мы и определили все методы классов нашего модуля, рабо-
+
 
та над ним еще не закончена. Модуль создает новый объект-действие
+
Хотя мы и определили все методы классов нашего модуля, работа над ним еще не закончена. Модуль создает новый объект-действие
 
для вызвавшего его приложения, но приложение пока не знает, как оно
 
для вызвавшего его приложения, но приложение пока не знает, как оно
 
должно (и должно ли) отображать соответствующий этому действию
 
должно (и должно ли) отображать соответствующий этому действию
 
элемент интерфейса. Наш модуль может дополнить пользовательский
 
элемент интерфейса. Наш модуль может дополнить пользовательский
интерфейс хозяйского приложения с помощью механизма XMLGUI, опи-
+
интерфейс хозяйского приложения с помощью механизма XMLGUI, описанного в прошлой статье ([[LXF83:Qt/KDE|LXF83]]). Откройте файл plugin_textsaver.rc:
санного в прошлой статье (LXF83). Откройте файл plugin_textsaver.rc:
+
<source lang="xml"><!DOCTYPE kpartgui>
<!DOCTYPE kpartgui>
+
 
<kpartplugin name=”textsaver” library=”libtextsaverplugin” version=”1”>
 
<kpartplugin name=”textsaver” library=”libtextsaverplugin” version=”1”>
 
<MenuBar>
 
<MenuBar>
Строка 196: Строка 161:
 
<Action name=”plugin_action”/>
 
<Action name=”plugin_action”/>
 
</ToolBar>
 
</ToolBar>
</kpartplugin>
+
</kpartplugin></source>
 
Те, кто читал предыдущую статью, не найдут в этом тексте ничего
 
Те, кто читал предыдущую статью, не найдут в этом тексте ничего
 
неожиданного. Мы регистрируем два элемента управления – для меню
 
неожиданного. Мы регистрируем два элемента управления – для меню
 
и панели быстрого доступа. Команда вызова нашего модуля (Save as
 
и панели быстрого доступа. Команда вызова нашего модуля (Save as
Text) будет расположена в меню Tools (Сервис), хотя, возможно, сле-
+
Text) будет расположена в меню Tools (Сервис), хотя, возможно, следовало расположить ее в меню Файл. Кнопка команды по умолчанию
довало расположить ее в меню Файл. Кнопка команды по умолчанию
+
 
будет расположена на дополнительной панели инструментов, которая
 
будет расположена на дополнительной панели инструментов, которая
 
(опять-таки, по умолчанию) не видна.
 
(опять-таки, по умолчанию) не видна.
 +
 +
[[Изображение:Img 84 74 1.jpg|thumb|(Рис. 2) Команд вызова модуля.]]
 
Нам осталось скомпилировать и установить наш модуль. Обычно
 
Нам осталось скомпилировать и установить наш модуль. Обычно
 
модули устанавливаются в общую системную директорию, например, в
 
модули устанавливаются в общую системную директорию, например, в
 
$KDEDIR/lib/kde3/, так что для установки понадобятся права root. При
 
$KDEDIR/lib/kde3/, так что для установки понадобятся права root. При
отладке модуля следует учитывать, что текущая версия модуля кэши-
+
отладке модуля следует учитывать, что текущая версия модуля кэшируется оболочкой Konqueror (и даже ldconfig не помогает), так что для
руется оболочкой Konqueror (и даже ldconfig не помогает), так что для
+
 
того, чтобы увидеть изменения, внесенные после повторной установки
 
того, чтобы увидеть изменения, внесенные после повторной установки
 
расширения, вам, возможно, придется перезапускать оболочку.
 
расширения, вам, возможно, придется перезапускать оболочку.
 +
 
Скомпилируйте и установите модуль, после чего запустите новый
 
Скомпилируйте и установите модуль, после чего запустите новый
 
экземпляр Web-браузера (но не файл-менеджера) Konqueror или
 
экземпляр Web-браузера (но не файл-менеджера) Konqueror или
откройте HTML-страницу в файл-менеджере. В меню Сервис вы уви-
+
откройте HTML-страницу в файл-менеджере. В меню Сервис вы увидите команду, вызывающую наш модуль (Рис. 2). Выше уже было сказано, что система следит за тем, чтобы наш модуль не был вызван для
дите команду, вызывающую наш модуль (Рис. 2). Выше уже было ска-
+
неподходящего компонента. Это выражается в том, что соответствующая команда меню видна тогда, когда мы открываем HTML-страницу, и
зано, что система следит за тем, чтобы наш модуль не был вызван для
+
неподходящего компонента. Это выражается в том, что соответствую-
+
щая команда меню видна тогда, когда мы открываем HTML-страницу, и
+
 
не видна в остальных случаях.
 
не видна в остальных случаях.
Если при просмотре сохраненной нашим модулем страницы в каком-
+
 
нибудь текстовом редакторе вы увидите нечто странное, то не спешите
+
Если при просмотре сохраненной нашим модулем страницы в каком-нибудь текстовом редакторе вы увидите нечто странное, то не спешите
 
ругаться. Используемая нами утилита html2text, во-первых, пытается
 
ругаться. Используемая нами утилита html2text, во-первых, пытается
 
сохранить расположение текста, соответствующее его расположению на
 
сохранить расположение текста, соответствующее его расположению на
Строка 226: Строка 189:
 
этом формате гиперссылка выделяется повторением каждой буквы ее
 
этом формате гиперссылка выделяется повторением каждой буквы ее
 
надписи по два раза).
 
надписи по два раза).
 +
 
Помимо модулей KParts, браузер Konqueror поддерживает еще
 
Помимо модулей KParts, браузер Konqueror поддерживает еще
несколько типов модулей. Модули панели навигации позволяют доба-
+
несколько типов модулей. Модули панели навигации позволяют добавить новые команды на панель навигации Konqueror (эта панель по
вить новые команды на панель навигации Konqueror (эта панель по
+
 
умолчанию припаркована к левому краю окна менеджера файлов).
 
умолчанию припаркована к левому краю окна менеджера файлов).
Все, что требуется от модуля панели навигации – возвратить ука-
+
Все, что требуется от модуля панели навигации – возвратить указатель на визуальный элемент, который и будут отображен браузером при вызове соответствующей команды. Модули KFile позволяют
затель на визуальный элемент, который и будут отображен браузе-
+
ром при вызове соответствующей команды. Модули KFile позволяют
+
 
добавлять элементы в окно свойств файла (это окно можно вызвать с
 
добавлять элементы в окно свойств файла (это окно можно вызвать с
 
помощью команды Свойства контекстного меню менеджера файлов).
 
помощью команды Свойства контекстного меню менеджера файлов).
 
Обычно модули KFile используются для вывода метаданных файлов.
 
Обычно модули KFile используются для вывода метаданных файлов.
Поскольку для всех мыслимых типов файлов такие модули уже напи-
+
Поскольку для всех мыслимых типов файлов такие модули уже написаны, вам не придется писать модуль KFile, если только вы не разрабатываете собственный файловый формат.
саны, вам не придется писать модуль KFile, если только вы не разраба-
+
тываете собственный файловый формат.
+
  
 
=== Модули KIPI ===
 
=== Модули KIPI ===
 
Структура модуля обычно настолько тесно связана со структурой
 
Структура модуля обычно настолько тесно связана со структурой
 
приложения, для которого этот модуль предназначен, что изучение
 
приложения, для которого этот модуль предназначен, что изучение
программирования модулей имеет смысл только в контексте изуче-
+
программирования модулей имеет смысл только в контексте изучения разработки для конкретного приложения. Однако из этого правила есть и исключения. Примером модулей, которые могут использоваться для работы с несколькими разными приложениями, являются
ния разработки для конкретного приложения. Однако из этого прави-
+
ла есть и исключения. Примером модулей, которые могут использо-
+
ваться для работы с несколькими разными приложениями, являются
+
 
модули KIPI. KIPI (KDE Image Plugin Interface – интерфейс KDE для
 
модули KIPI. KIPI (KDE Image Plugin Interface – интерфейс KDE для
 
обработки изображений) – это проект, целью которого является
 
обработки изображений) – это проект, целью которого является
создание единого интерфейса модулей для основанных на KDE при-
+
создание единого интерфейса модулей для основанных на KDE приложений, предназначенных для просмотра и редактирования растровых изображений, – Digikam, KimDaBa (ныне KPhotoAlbum), ShowFoto
ложений, предназначенных для просмотра и редактирования растро-
+
вых изображений, – Digikam, KimDaBa (ныне KPhotoAlbum), ShowFoto
+
 
и Gwenview. Хотя «KIPI» представляет собой аббревиатуру, на сайте
 
и Gwenview. Хотя «KIPI» представляет собой аббревиатуру, на сайте
extragear.kde.org/apps/kipi/ используется написание «Kipi». Мы будем
+
http://extragear.kde.org/apps/kipi/ используется написание «Kipi». Мы будем
писать KIPI, дабы читатели понимали, что это аббревиатура, а не рус-
+
писать KIPI, дабы читатели понимали, что это аббревиатура, а не русский глагол, написанный транслитом. Модули, соответствующие требованиям KIPI, должны работать одинаково во всех перечисленных
ский глагол, написанный транслитом. Модули, соответствующие тре-
+
бованиям KIPI, должны работать одинаково во всех перечисленных
+
 
приложениях. Думаю, не нужно распространяться о том, насколько
 
приложениях. Думаю, не нужно распространяться о том, насколько
 
это упрощает жизнь программиста, стремящегося к тому, чтобы его
 
это упрощает жизнь программиста, стремящегося к тому, чтобы его
 
разработками могли пользоваться поклонники всех графических
 
разработками могли пользоваться поклонники всех графических
программ KDE. Конечно, не все модули могут иметь смысл в контекс-
+
программ KDE. Конечно, не все модули могут иметь смысл в контексте каждого приложения. Это становится очевидно, если учесть, что в
те каждого приложения. Это становится очевидно, если учесть, что в
+
 
то время как одни приложения KDE (например, Gwenview) обладают
 
то время как одни приложения KDE (например, Gwenview) обладают
исключительно функциями просмотра, то другие (Digikam) включа-
+
исключительно функциями просмотра, то другие (Digikam) включают еще и базовые функции редактирования. Если же все программы
ют еще и базовые функции редактирования. Если же все программы
+
 
будут эволюционировать в одном и том же направлении, система
 
будут эволюционировать в одном и том же направлении, система
утратит разнообразие и само наличие нескольких независимых про-
+
утратит разнообразие и само наличие нескольких независимых программ потеряет смысл.
грамм потеряет смысл.
+
 
В отличие от других интерфейсов KDE, интерфейс KIPI сравнитель-
+
В отличие от других интерфейсов KDE, интерфейс KIPI сравнительно слабо документирован. Это значит, что наилучшим источником сведений о программировании модулей KIPI являются исходные тексты
но слабо документирован. Это значит, что наилучшим источником све-
+
дений о программировании модулей KIPI являются исходные тексты
+
 
модулей, написанных другими программистами.
 
модулей, написанных другими программистами.
Информацию о модулях KIPI можно получить на сайте extragear.
+
 
kde.org. Там же можно загрузить и исходные тексты модулей (уже
+
Информацию о модулях KIPI можно получить на сайте http://extragear.kde.org. Там же можно загрузить и исходные тексты модулей (уже
 
модифицированные тексты вы можете найти на прилагаемом диске,
 
модифицированные тексты вы можете найти на прилагаемом диске,
 
в файле kipi-plugins-0.1.2m.tar.gz). Прежде чем устанавливать модули
 
в файле kipi-plugins-0.1.2m.tar.gz). Прежде чем устанавливать модули
 
необходимо установить библиотеку libkipi (ее исходные тексты тоже
 
необходимо установить библиотеку libkipi (ее исходные тексты тоже
 
есть на диске, в файле libkipi-0.1.4.tar.bz2).
 
есть на диске, в файле libkipi-0.1.4.tar.bz2).
 +
 
Рассмотрим структуру модуля KIPI на примере модуля HelloWorld,
 
Рассмотрим структуру модуля KIPI на примере модуля HelloWorld,
который, как следует из названия, как раз и должен служить приме-
+
который, как следует из названия, как раз и должен служить примером для начинающих модулеписателей (исходные тексты этого модуля
ром для начинающих модулеписателей (исходные тексты этого модуля
+
 
находятся в директории kipi-plugins-0.1.2/kipi-plugins/helloworld/ файла
 
находятся в директории kipi-plugins-0.1.2/kipi-plugins/helloworld/ файла
 
kipi-plugins-0.1.2m.tar.gz). Как вы, наверное, догадались, модуль KIPI
 
kipi-plugins-0.1.2m.tar.gz). Как вы, наверное, догадались, модуль KIPI
Строка 287: Строка 235:
 
Определение соответствующего класса Plugin_HelloWorld мы найдем в
 
Определение соответствующего класса Plugin_HelloWorld мы найдем в
 
файлах plugin_helloworld.cpp и plugin_helloworld.h.
 
файлах plugin_helloworld.cpp и plugin_helloworld.h.
 +
 
Пробуем разобраться в том, что делает каждый метод этого класса.
 
Пробуем разобраться в том, что делает каждый метод этого класса.
 
Конструктор Plugin_HelloWorld вызывает конструктор базового класса
 
Конструктор Plugin_HelloWorld вызывает конструктор базового класса
Строка 292: Строка 241:
 
модуль загружен. Отметим здесь, что поскольку модули подключаются
 
модуль загружен. Отметим здесь, что поскольку модули подключаются
 
динамически, вывод такого сообщения полезен не только в процессе
 
динамически, вывод такого сообщения полезен не только в процессе
отладки модуля. Рассмотрим метод setup(), который, как можно дога-
+
отладки модуля. Рассмотрим метод setup(), который, как можно догадаться из названия, будет вызван приложением-хозяином модуля сразу после создания объекта.:
даться из названия, будет вызван приложением-хозяином модуля сра-
+
<source lang="cpp-qt">void Plugin_HelloWorld::setup( QWidget* widget )
зу после создания объекта.:
+
void Plugin_HelloWorld::setup( QWidget* widget )
+
 
{
 
{
 
KIPI::Plugin::setup( widget );
 
KIPI::Plugin::setup( widget );
Строка 313: Строка 260:
 
return;
 
return;
 
}
 
}
}
+
}</source>
 
Этот метод вызывает одноименный метод базового класса и
 
Этот метод вызывает одноименный метод базового класса и
создает объект-действие (таких объектов может быть зарегистриро-
+
создает объект-действие (таких объектов может быть зарегистрировано несколько). В методе setup() модуль получает также указатель
вано несколько). В методе setup() модуль получает также указатель
+
на объект класса KIPI::Interface, который реализует интерфейс взаимодействия между модулем и программой-хозяином.
на объект класса KIPI::Interface, который реализует интерфейс взаимо-
+
 
действия между модулем и программой-хозяином.
+
 
Далее в классе Plugin_HelloWorld определен метод slotActivate(),
 
Далее в классе Plugin_HelloWorld определен метод slotActivate(),
который и выполняет всю работу модуля (именно этот слот был ука-
+
который и выполняет всю работу модуля (именно этот слот был указан при создании объекта-действия). Метод slotActivate() мы пока
зан при создании объекта-действия). Метод slotActivate() мы пока
+
пропустим и рассмотрим последний метод класса Plugin_HelloWorld::category():
пропустим и рассмотрим последний метод класса Plugin_HelloWorld::
+
<source lang="cpp-qt">KIPI::Category Plugin_HelloWorld::category( KAction* action ) const
category():
+
KIPI::Category Plugin_HelloWorld::category( KAction* action ) const
+
 
{
 
{
 
if ( action == m_actionHelloWorld )
 
if ( action == m_actionHelloWorld )
Строка 331: Строка 275:
 
identification” << endl;
 
identification” << endl;
 
return KIPI::IMAGESPLUGIN; // no warning from compiler, please
 
return KIPI::IMAGESPLUGIN; // no warning from compiler, please
}
+
}</source>
 
С помощью этого метода программа-хозяин может выяснить, к
 
С помощью этого метода программа-хозяин может выяснить, к
какой категории должен принадлежать элемент управления, кото-
+
какой категории должен принадлежать элемент управления, который она создает для данного объекта-действия. Модуль HelloWorld
рый она создает для данного объекта-действия. Модуль HelloWorld
+
 
возвращает значение KIPI::IMAGESPLUGIN, которое указывает, что
 
возвращает значение KIPI::IMAGESPLUGIN, которое указывает, что
соответствующий элемент управления должен относиться к катего-
+
соответствующий элемент управления должен относиться к категории Images.
рии Images.
+
 
Для того чтобы написать свой модуль KIPI, нам, фактически, необхо-
+
Для того чтобы написать свой модуль KIPI, нам, фактически, необходимо только переписать метод slotActivate() модуля HelloWorld. В архиве
димо только переписать метод slotActivate() модуля HelloWorld. В архиве
+
 
kipi-plugins-0.1.2m.tar.gz вы найдете модуль mykipi, который и был создан
 
kipi-plugins-0.1.2m.tar.gz вы найдете модуль mykipi, который и был создан
 
на основе модуля HelloWorld (поскольку заготовки проекта KDevelop для
 
на основе модуля HelloWorld (поскольку заготовки проекта KDevelop для
такого модуля в KDevelop нет, его проще распространять как часть паке-
+
такого модуля в KDevelop нет, его проще распространять как часть пакета kipi-plugins). Все, что отличает модуль mykipi от модуля-прародителя,
та kipi-plugins). Все, что отличает модуль mykipi от модуля-прародителя,
+
 
сосредоточено в методе slotActivate() класса Plugin_MyKIPI:
 
сосредоточено в методе slotActivate() класса Plugin_MyKIPI:
void Plugin_MyKIPI::slotActivate()
+
<source lang="cpp-qt">void Plugin_MyKIPI::slotActivate()
 
{
 
{
 
kdDebug( 51000 ) << “Plugin_MyKIPI slot activated” << endl;
 
kdDebug( 51000 ) << “Plugin_MyKIPI slot activated” << endl;
Строка 358: Строка 299:
 
KMessageBox::information(0, S, “Selected Images”);
 
KMessageBox::information(0, S, “Selected Images”);
 
}
 
}
}
+
}</source>
 
Метод начинает работу с вывода диагностического сообщения.
 
Метод начинает работу с вывода диагностического сообщения.
Затем мы создаем объект класса KIPI::ImageCollection, который содер-
+
Затем мы создаем объект класса KIPI::ImageCollection, который содержит ссылки на объекты, представляющие изображения, выбранные
жит ссылки на объекты, представляющие изображения, выбранные
+
 
пользователем в окне графической программы (ваш разум еще не
 
пользователем в окне графической программы (ваш разум еще не
кипит от этих модулей KIPI?). Если метод currentSelection() вернул зна-
+
кипит от этих модулей KIPI?). Если метод currentSelection() вернул значение NULL, значит, пользователь не выбрал ни одного изображения
чение NULL, значит, пользователь не выбрал ни одного изображения
+
 
и тогда нашему модулю просто нечего делать. Впрочем, вызов модуля
 
и тогда нашему модулю просто нечего делать. Впрочем, вызов модуля
 
при отсутствии выбранных изображений невозможен, как невозможен
 
при отсутствии выбранных изображений невозможен, как невозможен
 
вызов модуля KParts для неподходящего компонента. Как и положено
 
вызов модуля KParts для неподходящего компонента. Как и положено
 
всякому учебному примеру, модуль mykipi не делает ничего полезного.
 
всякому учебному примеру, модуль mykipi не делает ничего полезного.
С помощью метода selection.images() мы получаем список URL выде-
+
С помощью метода selection.images() мы получаем список URL выделенных изображений (не забывайте, что мы имеем дело с программами, предназначенными, в основном, для просмотра и редактирования
ленных изображений (не забывайте, что мы имеем дело с программа-
+
ми, предназначенными, в основном, для просмотра и редактирования
+
 
существующих графических файлов). Далее мы преобразуем список
 
существующих графических файлов). Далее мы преобразуем список
URL в QStringList и затем QString, и выводим его содержимое в диа-
+
URL в QStringList и затем QString, и выводим его содержимое в диалоговом окне.
логовом окне.
+
  
 
Когда я писал, что у нашего модуля есть только один класс, я
 
Когда я писал, что у нашего модуля есть только один класс, я

Версия 21:07, 14 декабря 2008

Содержание

Модули KDE

ЧАСТЬ 7: Модули, расширения, расширения расширений.... всKIPIте от напряжения и нетерпения под присмотром Андрея Боровского!

Модули KDE

Модули KDE (KDE plugins) представляют собой логическое развитие идеи объектно-ориентированной оболочки. Читатели этой статьи наверняка знакомы с базовыми концепциями модулей как средств расширения уже существующих приложений. Механизм модулей позволяет нам не умножать сущностей (приложений) без необходимости всякий раз, когда нам требуется новая функциональность. По самой своей природе модули тесно связаны с теми приложениями, чьи возможности они расширяют. Нельзя написать «просто модуль». Мы рассмотрим программирование модулей для наиболее популярных приложений KDE и начнем с написания модуля для компонента KParts.

K-Части

Модули KParts широко применяются, например, в браузере Konqueror. Сами компоненты KParts представляют собой еще один способ расширения возможностей приложения. Примером их использования являются встроенные в Konqueror утилиты просмотра файлов различных типов. Каждый раз, когда Konqueror открывает какой-либо файл для просмотра, он загружает соответствующий компонент, который встраивается в интерфейс браузера и отображает содержимое файла. Благодаря динамическим компонентам KParts, Konqueror выполняет функцию универсального просмотрщика файлов, сохраняя при этом простой дизайн и небольшие размеры основного приложения. Следует отметить, что универсализм Konqueror иногда подвергается критике, поскольку в Unix традиционно преобладает другой подход – использование большого числа независимых приложений, каждое из которых делает что-то одно. Мы оставим в стороне вопрос о том, нужно ли писать модули, и ответим на вопрос, как это можно сделать. Модули KParts расширяют возможности компонентов KParts, то есть, с точки зрения основной программы Konqueror, модули являются расширениями расширений. Поддержка модулей KParts возможна, естественно, не только в Konqueror, но и в любом приложении, поддерживающем соответствующие компоненты KParts.

Возможно, на этом этапе у читателя возникает вопрос – а зачем вообще нужны модули Konqueror? Необходимость в механизме модулей была бы очевидна, если бы Konqueror был закрытым приложением, однако, при наличии исходных текстов, мы можем вносить изменения непосредственно в код компонентов KParts. Это верно, и тем не менее модули оказываются полезны и в случае открытого приложения. Главное преимущество использования модулей заключается в лучшей структурной организации кода. Нам легче писать и отлаживать модули, связанные с приложением с помощью строго определенного интерфейса, нежели вносить изменения в исходные тексты приложения, написанного другими программистами. К этому следует добавить и чисто организационный момент – для того, чтобы изменения, сделанные вами в приложении, попали в его основную версию, они должны быть одобрены другими разработчиками, тогда как в случае с модулями у вас есть возможность самостоятельно решить, какие функции являются полезными, а какие – нет.

Что же представляет собой модуль KParts с точки зрения взаимодействия с приложением? Модуль – это динамическая библиотека (файл *.so), которая загружается приложением, использующим компонент KParts. Модуль может добавлять собственные элементы управления в меню и на панель инструментов приложения. В некоторых случаях модули также изменяют содержимое главного окна приложения (которое в этот момент управляется соответствующим компонентом KParts).

В качестве примера мы напишем модуль для компонента KHTMLPart. Как нетрудно догадаться, этот компонент используется для просмотра страниц HTML. Фактически KHTMLPart представляет собой полноценный встраиваемый HTML-браузер с поддержкой JavaScript. Класс KHTMLPart является потомком класса KParts::ReadOnlyPart, то есть он предназначен для просмотра, а не для модификации открываемого файла. Наш модуль будет сохранять открытую в компоненте KHTMLPart HTML-страницу в простом текстовом формате.

(thumbnail)
(Рис. 1) Создаем проект модуля KParts.

Модуль для KHTMLPart удобен в качестве примера потому, что в KDevelop есть шаблон проекта такого модуля. В окне Создать новый проект (Рис. 1) мы выбираем пункт KHTMLPart Plugin. Назовем наш проект «textsaver» (полные исходные тексты модуля вы найдете на диске, в файле textsaver.tar.gz). Наш модуль, как таковой, состоит только из двух классов – Plugintextsaver (основной класс модуля) и textsaverFactory (вспомогательный класс). Оба класса реализованы в файлах plugin_textsaver.h и plugin_textsaver.cpp. Прежде чем вносить изменения в классы, добавим в файл plugin_textsaver.cpp дополнительные заголовочные файлы:

include <stdio.h>
#include <kfiledialog.h>
#include <qcstring.h>

Рассмотрим сначала класс textsaverFactory, являющийся потомком класса KParts::Factory и управляющий созданием объекта класса Plugintextsaver (т.е., этот класс реализует фабрику объектов). Такой объект создается методом textsaverFactory::createObject(), который сгенерирован автоматически. Нам вообще ничего не нужно было бы менять в классе textsaverFactory, если бы не чистый виртуальный метод createPartObject(). Сам по себе он нам не нужен, но для того, чтобы наш модуль мог скомпилироваться, мы должны создать его реализацию. Странно, что KDevelop, по крайней мере в версии 3.4.2, не делает этого автоматически:

KParts::Part* createPartObject(
QWidget*, const char*, QObject*,
const char*, const char*, const QStringList&)
{return 0;}

Помимо прочего, разделяемая библиотека, содержащая наш модуль, должна экспортировать функцию, создающую объект класса textsaverFactory. Эта функция, сгенерированная автоматически, экспортируется в формате C:

extern “C”
{
void* init_libtextsaverplugin()
{
KGlobal::locale()->insertCatalogue(“textsaver”);
return new textsaverFactory;
}
}

Приложение, вызывающее функцию init_libtextsaverplugin(), знает, что делать с возвращенным указателем.

Перейдем к классу Plugintextsaver. Приложение, а точнее, компонент KHTMLPart, взаимодействует с нашим модулем при помощи вызова трех методов этого класса – конструктора, деструктора и метода slotAction(). Метод slotAction() представляет собой слот, который вызывается всякий раз, когда пользователь выбирает пункт меню или нажимает кнопку, связанную с модулем. Этот метод содержит код, который, собственно, и отвечает за выполнение команды. В конструкторе класса Plugintextsaver создается объект-действие:

Plugintextsaver::Plugintextsaver( QObject* parent, const char* name )
: Plugin( parent, name )
{
(void) new KAction( i18n(“Save as &Text”), “textsaver”, 0,
this, SLOT(slotAction()),
actionCollection(), “plugin_action” );
}

Этот объект будет добавлен в коллекцию действий приложения, вызвавшего компонент KHTMLPart, загрузивший наш модуль. Тем, кто не совсем понимает, о очем идет речь, рекомендуем прочитать позапрошлую статью этого цикла (LXF82), в которой мы познакомились с объектами-действиями. Мы указываем объекту-действию имя соответствующего элемента управления и слот, который должен обрабатывать команду. Очевидно, что в общем случае мы можем зарегистрировать несколько действий для нашего модуля. Нам осталось написать только сам метод slotAction():

void Plugintextsaver::slotAction()
{
if ( !parent()->inherits(“KHTMLPart”) )
{
QString title( i18n(“HTML Only”));
QString text(i18n(“Only HTML documents can be saved with this plugin.”));
KMessageBox::sorry( 0, text, title );
return;
}
KHTMLPart *part = dynamic_cast<KHTMLPart *>(parent());
QString FN = KFileDialog::getSaveFileName(“”, “”, 0, “Save Text File As”);
if (FN == “”) return;
QString Cmd = “html2text >+ FN;
QCString Text = part->documentSource().local8Bit();
FILE * F = popen(Cmd.ascii(), “w”);
fwrite((char*) Text.data(), 1, Text.length(), F);
pclose(F);
}

У модуля должна быть какая-то возможность взаимодействовать с другими элементами приложения-хозяина (этот термин не следует понимать в том смысле, что наш модуль паразитирует на браузере Konqueror). Такая возможность у модуля есть, благодаря тому, что конструктору объекта модуля был передан указатель на вызвавший его объект класса KTHMLPart. Доступ к этому указателю можно получить с помощью метода parent(). Первый блок кода в методе slotAction() проверяет, действительно ли объект parent() принадлежит классу KHTMLPart. На практике это излишне, поскольку модуль никогда не будет вызван для какого-либо иного компонента. Далее мы получаем указатель на родительский объект KHTMLPart, запрашиваем у пользователя имя файла для сохранения текста (надеюсь, вы еще не забыли, что должен делать наш модуль) и, с помощью метода documentSource() получаем HTML-текст страницы, открытой компонентом KHTMLPart. Метод documentSource() возвращает данные в строке QString в двухбайтовой кодировке. Мы преобразуем полученную строку в локальную однобайтовую кодировку и сохраняем результат в переменной Text типа QCString. Далее, с помощью функции popen(3), мы запускаем внешнюю утилиту html2text, преобразующую данные HTML в простой текст. Этой утилите мы передаем содержимое переменной Text и имя файла для сохранения результата. Применяемый нами прием – использование в программе внешней утилиты для решения нашей задачи, очень широко распространен в Unix-системах.

Хотя мы и определили все методы классов нашего модуля, работа над ним еще не закончена. Модуль создает новый объект-действие для вызвавшего его приложения, но приложение пока не знает, как оно должно (и должно ли) отображать соответствующий этому действию элемент интерфейса. Наш модуль может дополнить пользовательский интерфейс хозяйского приложения с помощью механизма XMLGUI, описанного в прошлой статье (LXF83). Откройте файл plugin_textsaver.rc:

<!DOCTYPE kpartgui>
<kpartplugin name=”textsaver” library=”libtextsaverplugin” version=”1”>
<MenuBar>
<Menu name=”tools”><Text>&amp;Tools</Text>
<Action name=”plugin_action”/>
</Menu>
</MenuBar>
<ToolBar name=”extraToolBar”>
<Action name=”plugin_action”/>
</ToolBar>
</kpartplugin>

Те, кто читал предыдущую статью, не найдут в этом тексте ничего неожиданного. Мы регистрируем два элемента управления – для меню и панели быстрого доступа. Команда вызова нашего модуля (Save as Text) будет расположена в меню Tools (Сервис), хотя, возможно, следовало расположить ее в меню Файл. Кнопка команды по умолчанию будет расположена на дополнительной панели инструментов, которая (опять-таки, по умолчанию) не видна.

(thumbnail)
(Рис. 2) Команд вызова модуля.

Нам осталось скомпилировать и установить наш модуль. Обычно модули устанавливаются в общую системную директорию, например, в $KDEDIR/lib/kde3/, так что для установки понадобятся права root. При отладке модуля следует учитывать, что текущая версия модуля кэшируется оболочкой Konqueror (и даже ldconfig не помогает), так что для того, чтобы увидеть изменения, внесенные после повторной установки расширения, вам, возможно, придется перезапускать оболочку.

Скомпилируйте и установите модуль, после чего запустите новый экземпляр Web-браузера (но не файл-менеджера) Konqueror или откройте HTML-страницу в файл-менеджере. В меню Сервис вы увидите команду, вызывающую наш модуль (Рис. 2). Выше уже было сказано, что система следит за тем, чтобы наш модуль не был вызван для неподходящего компонента. Это выражается в том, что соответствующая команда меню видна тогда, когда мы открываем HTML-страницу, и не видна в остальных случаях.

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

Помимо модулей KParts, браузер Konqueror поддерживает еще несколько типов модулей. Модули панели навигации позволяют добавить новые команды на панель навигации Konqueror (эта панель по умолчанию припаркована к левому краю окна менеджера файлов). Все, что требуется от модуля панели навигации – возвратить указатель на визуальный элемент, который и будут отображен браузером при вызове соответствующей команды. Модули KFile позволяют добавлять элементы в окно свойств файла (это окно можно вызвать с помощью команды Свойства контекстного меню менеджера файлов). Обычно модули KFile используются для вывода метаданных файлов. Поскольку для всех мыслимых типов файлов такие модули уже написаны, вам не придется писать модуль KFile, если только вы не разрабатываете собственный файловый формат.

Модули KIPI

Структура модуля обычно настолько тесно связана со структурой приложения, для которого этот модуль предназначен, что изучение программирования модулей имеет смысл только в контексте изучения разработки для конкретного приложения. Однако из этого правила есть и исключения. Примером модулей, которые могут использоваться для работы с несколькими разными приложениями, являются модули KIPI. KIPI (KDE Image Plugin Interface – интерфейс KDE для обработки изображений) – это проект, целью которого является создание единого интерфейса модулей для основанных на KDE приложений, предназначенных для просмотра и редактирования растровых изображений, – Digikam, KimDaBa (ныне KPhotoAlbum), ShowFoto и Gwenview. Хотя «KIPI» представляет собой аббревиатуру, на сайте http://extragear.kde.org/apps/kipi/ используется написание «Kipi». Мы будем писать KIPI, дабы читатели понимали, что это аббревиатура, а не русский глагол, написанный транслитом. Модули, соответствующие требованиям KIPI, должны работать одинаково во всех перечисленных приложениях. Думаю, не нужно распространяться о том, насколько это упрощает жизнь программиста, стремящегося к тому, чтобы его разработками могли пользоваться поклонники всех графических программ KDE. Конечно, не все модули могут иметь смысл в контексте каждого приложения. Это становится очевидно, если учесть, что в то время как одни приложения KDE (например, Gwenview) обладают исключительно функциями просмотра, то другие (Digikam) включают еще и базовые функции редактирования. Если же все программы будут эволюционировать в одном и том же направлении, система утратит разнообразие и само наличие нескольких независимых программ потеряет смысл.

В отличие от других интерфейсов KDE, интерфейс KIPI сравнительно слабо документирован. Это значит, что наилучшим источником сведений о программировании модулей KIPI являются исходные тексты модулей, написанных другими программистами.

Информацию о модулях KIPI можно получить на сайте http://extragear.kde.org. Там же можно загрузить и исходные тексты модулей (уже модифицированные тексты вы можете найти на прилагаемом диске, в файле kipi-plugins-0.1.2m.tar.gz). Прежде чем устанавливать модули необходимо установить библиотеку libkipi (ее исходные тексты тоже есть на диске, в файле libkipi-0.1.4.tar.bz2).

Рассмотрим структуру модуля KIPI на примере модуля HelloWorld, который, как следует из названия, как раз и должен служить примером для начинающих модулеписателей (исходные тексты этого модуля находятся в директории kipi-plugins-0.1.2/kipi-plugins/helloworld/ файла kipi-plugins-0.1.2m.tar.gz). Как вы, наверное, догадались, модуль KIPI представляет собой разделяемую библиотеку. Явным образом эта библиотека экспортирует один-единственный класс – класс модуля. Определение соответствующего класса Plugin_HelloWorld мы найдем в файлах plugin_helloworld.cpp и plugin_helloworld.h.

Пробуем разобраться в том, что делает каждый метод этого класса. Конструктор Plugin_HelloWorld вызывает конструктор базового класса всех классов модулей KIPI KIPI::Plugin и выводит сообщение о том, что модуль загружен. Отметим здесь, что поскольку модули подключаются динамически, вывод такого сообщения полезен не только в процессе отладки модуля. Рассмотрим метод setup(), который, как можно догадаться из названия, будет вызван приложением-хозяином модуля сразу после создания объекта.:

void Plugin_HelloWorld::setup( QWidget* widget )
{
KIPI::Plugin::setup( widget );
// this is our action shown in the menubar/toolbar of the mainwindow
m_actionHelloWorld = new KAction (i18n(“Hello World...”),
“misc”,
0, // do never set shortcuts from plugins.
this,
SLOT(slotActivate()),
actionCollection(),
“helloworld”);
addAction( m_actionHelloWorld );
m_interface = dynamic_cast< KIPI::Interface* >( parent() );
if ( !m_interface )
{
kdError( 51000 ) << “Kipi interface is null!<< endl;
return;
}
}

Этот метод вызывает одноименный метод базового класса и создает объект-действие (таких объектов может быть зарегистрировано несколько). В методе setup() модуль получает также указатель на объект класса KIPI::Interface, который реализует интерфейс взаимодействия между модулем и программой-хозяином.

Далее в классе Plugin_HelloWorld определен метод slotActivate(), который и выполняет всю работу модуля (именно этот слот был указан при создании объекта-действия). Метод slotActivate() мы пока пропустим и рассмотрим последний метод класса Plugin_HelloWorld::category():

KIPI::Category Plugin_HelloWorld::category( KAction* action ) const
{
if ( action == m_actionHelloWorld )
return KIPI::IMAGESPLUGIN;
kdWarning( 51000 ) << “Unrecognized action for plugin category
identification” << endl;
return KIPI::IMAGESPLUGIN; // no warning from compiler, please
}

С помощью этого метода программа-хозяин может выяснить, к какой категории должен принадлежать элемент управления, который она создает для данного объекта-действия. Модуль HelloWorld возвращает значение KIPI::IMAGESPLUGIN, которое указывает, что соответствующий элемент управления должен относиться к категории Images.

Для того чтобы написать свой модуль KIPI, нам, фактически, необходимо только переписать метод slotActivate() модуля HelloWorld. В архиве kipi-plugins-0.1.2m.tar.gz вы найдете модуль mykipi, который и был создан на основе модуля HelloWorld (поскольку заготовки проекта KDevelop для такого модуля в KDevelop нет, его проще распространять как часть пакета kipi-plugins). Все, что отличает модуль mykipi от модуля-прародителя, сосредоточено в методе slotActivate() класса Plugin_MyKIPI:

void Plugin_MyKIPI::slotActivate()
{
kdDebug( 51000 ) << “Plugin_MyKIPI slot activated” << endl;
KIPI::ImageCollection selection = m_interface->currentSelection();
if ( !selection.isValid() ) {
kdDebug( 51000) << “No Selection!<< endl;
}
else {
KURL::List images = selection.images();
QStringList SL = images.toStringList();
QString S = SL.join(“\n”);
KMessageBox::information(0, S, “Selected Images”);
}
}

Метод начинает работу с вывода диагностического сообщения. Затем мы создаем объект класса KIPI::ImageCollection, который содержит ссылки на объекты, представляющие изображения, выбранные пользователем в окне графической программы (ваш разум еще не кипит от этих модулей KIPI?). Если метод currentSelection() вернул значение NULL, значит, пользователь не выбрал ни одного изображения и тогда нашему модулю просто нечего делать. Впрочем, вызов модуля при отсутствии выбранных изображений невозможен, как невозможен вызов модуля KParts для неподходящего компонента. Как и положено всякому учебному примеру, модуль mykipi не делает ничего полезного. С помощью метода selection.images() мы получаем список URL выделенных изображений (не забывайте, что мы имеем дело с программами, предназначенными, в основном, для просмотра и редактирования существующих графических файлов). Далее мы преобразуем список URL в QStringList и затем QString, и выводим его содержимое в диалоговом окне.

Когда я писал, что у нашего модуля есть только один класс, я немного упростил ситуацию. На самом деле, модуль реализует еще один класс, а именно класс Factory, управляющий созданием основного объекта класса модуля, однако генерация этого класса выполняется автоматически с помощью определения типа и вызова макроса:

typedef KGenericFactory<Plugin_MyKIPI> Factory;
K_EXPORT_COMPONENT_FACTORY( kipiplugin_mykipi,Factory("kipiplugin_mykipi"));

Прежде, чем мы установим наш модуль, нам следует отредактировать файл kipiplugin_mykipi.desktop. Ниже приводится его отредактированный текст (структура файлов *.desktop похожа на структуру файлов *.ini Windows, и это не случайное совпадение):

[Desktop Entry]
Encoding=UTF-8
Name=MyKIPI
Name[cs]=My KIPI
Comment=Sample KIPI Plugin
Type=Service
ServiceTypes=KIPI/Plugin
X-KDE-Library=kipiplugin_mykipi
author=Andrei Borovsky, borovsky@tochka.ru
(thumbnail)
(Рис. 3) Модуль mykipi в работе.

Файл kipiplugin_mykipi.desktop cодержит сведения о типе модуля (ServiceTypes=KIPI/Plugin), а также имя модуля и комментарий.

Теперь мы можем скомпилировать и установить модуль (естественный путь – make install). Запустим программу Gwenview и в меню Модули, в группе Изображения (Images) мы увидим команду Sample KIPI Plugin, которая будет работать так, как мы и ожидаем (Рис. 3).

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

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