LXF84:Qt/KDE
Yaleks (обсуждение | вклад) (Новая: {{Цикл/Qt/KDE}} == Модули KDE == ''ЧАСТЬ 7: Модули, расширения, расширения расширений.... всKIPIте от напряжения и н...) |
Yaleks (обсуждение | вклад) |
||
Строка 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 утилиты просмотра файлов | + | |
− | + | ||
для просмотра, он загружает соответствующий компонент, который | для просмотра, он загружает соответствующий компонент, который | ||
встраивается в интерфейс браузера и отображает содержимое файла. | встраивается в интерфейс браузера и отображает содержимое файла. | ||
Строка 27: | Строка 20: | ||
простой дизайн и небольшие размеры основного приложения. Следует | простой дизайн и небольшие размеры основного приложения. Следует | ||
отметить, что универсализм Konqueror иногда подвергается критике, | отметить, что универсализм Konqueror иногда подвергается критике, | ||
− | поскольку в Unix традиционно преобладает другой подход – | + | поскольку в Unix традиционно преобладает другой подход – использование большого числа независимых приложений, каждое из которых |
− | + | ||
делает что-то одно. Мы оставим в стороне вопрос о том, нужно ли | делает что-то одно. Мы оставим в стороне вопрос о том, нужно ли | ||
писать модули, и ответим на вопрос, как это можно сделать. Модули | писать модули, и ответим на вопрос, как это можно сделать. Модули | ||
KParts расширяют возможности компонентов KParts, то есть, с точки | KParts расширяют возможности компонентов KParts, то есть, с точки | ||
− | зрения основной программы Konqueror, модули являются | + | зрения основной программы Konqueror, модули являются расширениями расширений. Поддержка модулей KParts возможна, естественно, |
− | + | ||
не только в Konqueror, но и в любом приложении, поддерживающем | не только в Konqueror, но и в любом приложении, поддерживающем | ||
соответствующие компоненты KParts. | соответствующие компоненты KParts. | ||
Возможно, на этом этапе у читателя возникает вопрос – а зачем | Возможно, на этом этапе у читателя возникает вопрос – а зачем | ||
− | вообще нужны модули Konqueror? Необходимость в механизме | + | вообще нужны модули Konqueror? Необходимость в механизме модулей была бы очевидна, если бы Konqueror был закрытым приложением, однако, при наличии исходных текстов, мы можем вносить изменения непосредственно в код компонентов KParts. Это верно, и тем не |
− | + | ||
− | + | ||
− | + | ||
менее модули оказываются полезны и в случае открытого приложения. | менее модули оказываются полезны и в случае открытого приложения. | ||
Главное преимущество использования модулей заключается в лучшей | Главное преимущество использования модулей заключается в лучшей | ||
структурной организации кода. Нам легче писать и отлаживать модули, | структурной организации кода. Нам легче писать и отлаживать модули, | ||
− | связанные с приложением с помощью строго определенного | + | связанные с приложением с помощью строго определенного интерфейса, нежели вносить изменения в исходные тексты приложения, |
− | + | ||
написанного другими программистами. К этому следует добавить и | написанного другими программистами. К этому следует добавить и | ||
− | чисто организационный момент – для того, чтобы изменения, | + | чисто организационный момент – для того, чтобы изменения, сделанные вами в приложении, попали в его основную версию, они должны |
− | + | быть одобрены другими разработчиками, тогда как в случае с модулями у вас есть возможность самостоятельно решить, какие функции | |
− | быть одобрены другими разработчиками, тогда как в случае с | + | |
− | + | ||
являются полезными, а какие – нет. | являются полезными, а какие – нет. | ||
− | Что же представляет собой модуль KParts с точки зрения | + | Что же представляет собой модуль KParts с точки зрения взаимодействия с приложением? Модуль – это динамическая библиотека |
− | + | (файл *.so), которая загружается приложением, использующим компонент KParts. Модуль может добавлять собственные элементы управления в меню и на панель инструментов приложения. В некоторых случаях | |
− | (файл *.so), которая загружается приложением, использующим | + | модули также изменяют содержимое главного окна приложения (которое в этот момент управляется соответствующим компонентом KParts). |
− | + | ||
− | + | ||
− | модули также изменяют содержимое главного окна приложения ( | + | |
− | + | ||
В качестве примера мы напишем модуль для компонента KHTMLPart. | В качестве примера мы напишем модуль для компонента KHTMLPart. | ||
Как нетрудно догадаться, этот компонент используется для просмотра | Как нетрудно догадаться, этот компонент используется для просмотра | ||
− | страниц HTML. Фактически KHTMLPart представляет собой | + | страниц HTML. Фактически KHTMLPart представляет собой полноценный встраиваемый 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, являющийся | + | |
− | + | ||
Plugintextsaver (т.е., этот класс реализует фабрику объектов). Такой объект | Plugintextsaver (т.е., этот класс реализует фабрику объектов). Такой объект | ||
создается методом textsaverFactory::createObject(), который сгенерирован | создается методом textsaverFactory::createObject(), который сгенерирован | ||
автоматически. Нам вообще ничего не нужно было бы менять в классе | автоматически. Нам вообще ничего не нужно было бы менять в классе | ||
textsaverFactory, если бы не чистый виртуальный метод createPartObject(). | textsaverFactory, если бы не чистый виртуальный метод createPartObject(). | ||
− | Сам по себе он нам не нужен, но для того, чтобы наш модуль мог | + | Сам по себе он нам не нужен, но для того, чтобы наш модуль мог скомпилироваться, мы должны создать его реализацию. Странно, что 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: |
− | + | <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. Приложение, а точнее, | + | |
− | + | Перейдем к классу Plugintextsaver. Приложение, а точнее, компонент KHTMLPart, взаимодействует с нашим модулем при помощи | |
− | вызова трех методов этого класса – конструктора, деструктора и | + | вызова трех методов этого класса – конструктора, деструктора и метода slotAction(). Метод slotAction() представляет собой слот, который |
− | + | ||
вызывается всякий раз, когда пользователь выбирает пункт меню или | вызывается всякий раз, когда пользователь выбирает пункт меню или | ||
нажимает кнопку, связанную с модулем. Этот метод содержит код, | нажимает кнопку, связанную с модулем. Этот метод содержит код, | ||
− | который, собственно, и отвечает за выполнение команды. В | + | который, собственно, и отвечает за выполнение команды. В конструкторе класса 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]]), в которой мы познакомились с |
− | + | объектами-действиями. Мы указываем объекту-действию имя соответствующего элемента управления и слот, который должен обрабатывать | |
− | объектами-действиями. Мы указываем объекту-действию имя | + | |
− | + | ||
команду. Очевидно, что в общем случае мы можем зарегистрировать | команду. Очевидно, что в общем случае мы можем зарегистрировать | ||
несколько действий для нашего модуля. Нам осталось написать только | несколько действий для нашего модуля. Нам осталось написать только | ||
сам метод 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: |
− | + | <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 не помогает), так что для |
− | + | ||
того, чтобы увидеть изменения, внесенные после повторной установки | того, чтобы увидеть изменения, внесенные после повторной установки | ||
расширения, вам, возможно, придется перезапускать оболочку. | расширения, вам, возможно, придется перезапускать оболочку. | ||
+ | |||
Скомпилируйте и установите модуль, после чего запустите новый | Скомпилируйте и установите модуль, после чего запустите новый | ||
экземпляр Web-браузера (но не файл-менеджера) Konqueror или | экземпляр Web-браузера (но не файл-менеджера) Konqueror или | ||
− | откройте HTML-страницу в файл-менеджере. В меню Сервис вы | + | откройте HTML-страницу в файл-менеджере. В меню Сервис вы увидите команду, вызывающую наш модуль (Рис. 2). Выше уже было сказано, что система следит за тем, чтобы наш модуль не был вызван для |
− | + | неподходящего компонента. Это выражается в том, что соответствующая команда меню видна тогда, когда мы открываем HTML-страницу, и | |
− | + | ||
− | неподходящего компонента. Это выражается в том, что | + | |
− | + | ||
не видна в остальных случаях. | не видна в остальных случаях. | ||
− | Если при просмотре сохраненной нашим модулем страницы в каком- | + | |
− | нибудь текстовом редакторе вы увидите нечто странное, то не спешите | + | Если при просмотре сохраненной нашим модулем страницы в каком-нибудь текстовом редакторе вы увидите нечто странное, то не спешите |
ругаться. Используемая нами утилита html2text, во-первых, пытается | ругаться. Используемая нами утилита html2text, во-первых, пытается | ||
сохранить расположение текста, соответствующее его расположению на | сохранить расположение текста, соответствующее его расположению на | ||
Строка 226: | Строка 189: | ||
этом формате гиперссылка выделяется повторением каждой буквы ее | этом формате гиперссылка выделяется повторением каждой буквы ее | ||
надписи по два раза). | надписи по два раза). | ||
+ | |||
Помимо модулей KParts, браузер Konqueror поддерживает еще | Помимо модулей KParts, браузер Konqueror поддерживает еще | ||
− | несколько типов модулей. Модули панели навигации позволяют | + | несколько типов модулей. Модули панели навигации позволяют добавить новые команды на панель навигации Konqueror (эта панель по |
− | + | ||
умолчанию припаркована к левому краю окна менеджера файлов). | умолчанию припаркована к левому краю окна менеджера файлов). | ||
− | Все, что требуется от модуля панели навигации – возвратить | + | Все, что требуется от модуля панели навигации – возвратить указатель на визуальный элемент, который и будут отображен браузером при вызове соответствующей команды. Модули 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 |
− | + | ||
− | + | ||
и Gwenview. Хотя «KIPI» представляет собой аббревиатуру, на сайте | и Gwenview. Хотя «KIPI» представляет собой аббревиатуру, на сайте | ||
− | extragear.kde.org/apps/kipi/ используется написание «Kipi». Мы будем | + | http://extragear.kde.org/apps/kipi/ используется написание «Kipi». Мы будем |
− | писать KIPI, дабы читатели понимали, что это аббревиатура, а не | + | писать KIPI, дабы читатели понимали, что это аббревиатура, а не русский глагол, написанный транслитом. Модули, соответствующие требованиям KIPI, должны работать одинаково во всех перечисленных |
− | + | ||
− | + | ||
приложениях. Думаю, не нужно распространяться о том, насколько | приложениях. Думаю, не нужно распространяться о том, насколько | ||
это упрощает жизнь программиста, стремящегося к тому, чтобы его | это упрощает жизнь программиста, стремящегося к тому, чтобы его | ||
разработками могли пользоваться поклонники всех графических | разработками могли пользоваться поклонники всех графических | ||
− | программ KDE. Конечно, не все модули могут иметь смысл в | + | программ KDE. Конечно, не все модули могут иметь смысл в контексте каждого приложения. Это становится очевидно, если учесть, что в |
− | + | ||
то время как одни приложения KDE (например, Gwenview) обладают | то время как одни приложения KDE (например, Gwenview) обладают | ||
− | исключительно функциями просмотра, то другие (Digikam) | + | исключительно функциями просмотра, то другие (Digikam) включают еще и базовые функции редактирования. Если же все программы |
− | + | ||
будут эволюционировать в одном и том же направлении, система | будут эволюционировать в одном и том же направлении, система | ||
− | утратит разнообразие и само наличие нескольких независимых | + | утратит разнообразие и само наличие нескольких независимых программ потеряет смысл. |
− | + | ||
− | В отличие от других интерфейсов KDE, интерфейс KIPI | + | В отличие от других интерфейсов KDE, интерфейс 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() модуль получает также указатель |
− | + | на объект класса KIPI::Interface, который реализует интерфейс взаимодействия между модулем и программой-хозяином. | |
− | на объект класса KIPI::Interface, который реализует интерфейс | + | |
− | + | ||
Далее в классе Plugin_HelloWorld определен метод slotActivate(), | Далее в классе Plugin_HelloWorld определен метод 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 |
− | + | ||
возвращает значение KIPI::IMAGESPLUGIN, которое указывает, что | возвращает значение KIPI::IMAGESPLUGIN, которое указывает, что | ||
− | соответствующий элемент управления должен относиться к | + | соответствующий элемент управления должен относиться к категории Images. |
− | + | ||
− | Для того чтобы написать свой модуль KIPI, нам, фактически, | + | Для того чтобы написать свой модуль KIPI, нам, фактически, необходимо только переписать метод 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 от модуля-прародителя, |
− | + | ||
сосредоточено в методе 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, значит, пользователь не выбрал ни одного изображения |
− | + | ||
и тогда нашему модулю просто нечего делать. Впрочем, вызов модуля | и тогда нашему модулю просто нечего делать. Впрочем, вызов модуля | ||
при отсутствии выбранных изображений невозможен, как невозможен | при отсутствии выбранных изображений невозможен, как невозможен | ||
вызов модуля KParts для неподходящего компонента. Как и положено | вызов модуля KParts для неподходящего компонента. Как и положено | ||
всякому учебному примеру, модуль mykipi не делает ничего полезного. | всякому учебному примеру, модуль mykipi не делает ничего полезного. | ||
− | С помощью метода selection.images() мы получаем список URL | + | С помощью метода selection.images() мы получаем список URL выделенных изображений (не забывайте, что мы имеем дело с программами, предназначенными, в основном, для просмотра и редактирования |
− | + | ||
− | + | ||
существующих графических файлов). Далее мы преобразуем список | существующих графических файлов). Далее мы преобразуем список | ||
− | URL в QStringList и затем QString, и выводим его содержимое в | + | URL в QStringList и затем QString, и выводим его содержимое в диалоговом окне. |
− | + | ||
Когда я писал, что у нашего модуля есть только один класс, я | Когда я писал, что у нашего модуля есть только один класс, я |
Версия 21:07, 14 декабря 2008
|
|
|
- Метамодернизм в позднем творчестве В.Г. Сорокина
- ЛитРПГ - последняя отрыжка постмодерна
- "Ричард III и семиотика"
- 3D-визуализация обложки Ridero создаем обложку книги при работе над самиздатом.
- Архитектура метамодерна - говоря о современном искусстве, невозможно не поговорить об архитектуре. В данной статье будет отмечено несколько интересных принципов, характерных для построек "новой волны", столь притягательных и скандальных.
- Литература
- Метамодерн
- Рокер-Прометей против изначального зла в «Песне про советскую милицию» Вени Дркина, Автор: Нина Ищенко, к.ф.н, член Союза Писателей ЛНР - перепубликация из журнала "Топос".
- Как избавиться от комаров? Лучшие типы ловушек.
- Что делать если роблокс вылетает на windows
- Что делать, если ребенок смотрит порно?
- Почему собака прыгает на людей при встрече?
- Какое масло лить в Задний дифференциал (мост) Visco diff 38434AA050
- О чем может рассказать хвост вашей кошки?
- Верветки
- Отчетность бюджетных учреждений при закупках по Закону № 223-ФЗ
- Срок исковой давности как правильно рассчитать
- Дмитрий Патрушев минсельхоз будет ли преемником Путина
- Кто такой Владислав Поздняков? Что такое "Мужское Государство" и почему его признали экстремистским в России?
- Как правильно выбрать машинное масло в Димитровграде?
- Как стать богатым и знаменитым в России?
- Почему фильм "Пипец" (Kick-Ass) стал популярен по всему миру?
- Как стать мудрецом?
- Как правильно установить FreeBSD
- Как стать таким как Путин?
- Где лучше жить - в Димитровграде или в Ульяновске?
- Почему город Димитровград так называется?
- Что такое метамодерн?
- ВАЖНО! Временное ограничение движения автотранспортных средств в Димитровграде
- Тарифы на электроэнергию для майнеров предложено повысить
Содержание |
Модули 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-страницу в простом текстовом формате.
Модуль для 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>&Tools</Text> <Action name=”plugin_action”/> </Menu> </MenuBar> <ToolBar name=”extraToolBar”> <Action name=”plugin_action”/> </ToolBar> </kpartplugin>
Те, кто читал предыдущую статью, не найдут в этом тексте ничего неожиданного. Мы регистрируем два элемента управления – для меню и панели быстрого доступа. Команда вызова нашего модуля (Save as Text) будет расположена в меню Tools (Сервис), хотя, возможно, следовало расположить ее в меню Файл. Кнопка команды по умолчанию будет расположена на дополнительной панели инструментов, которая (опять-таки, по умолчанию) не видна.
Нам осталось скомпилировать и установить наш модуль. Обычно модули устанавливаются в общую системную директорию, например, в $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
Файл kipiplugin_mykipi.desktop cодержит сведения о типе модуля (ServiceTypes=KIPI/Plugin), а также имя модуля и комментарий.
Теперь мы можем скомпилировать и установить модуль (естественный путь – make install). Запустим программу Gwenview и в меню Модули, в группе Изображения (Images) мы увидим команду Sample KIPI Plugin, которая будет работать так, как мы и ожидаем (Рис. 3).
В этой статье мы рассмотрели, среди прочих, модули для компонентов KParts. В следующей, заключительной части серии мы узнаем, как создавать собственные компоненты (и модули для них).