LXF84:Qt/KDE
|
|
|
- Метамодернизм в позднем творчестве В.Г. Сорокина
- ЛитРПГ - последняя отрыжка постмодерна
- "Ричард 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» представляет собой аббревиатуру, на сайте extragear.kde.org/apps/kipi/ используется написание «Kipi». Мы будем писать KIPI, дабы читатели понимали, что это аббревиатура, а не рус- ский глагол, написанный транслитом. Модули, соответствующие тре- бованиям KIPI, должны работать одинаково во всех перечисленных приложениях. Думаю, не нужно распространяться о том, насколько это упрощает жизнь программиста, стремящегося к тому, чтобы его разработками могли пользоваться поклонники всех графических программ KDE. Конечно, не все модули могут иметь смысл в контекс- те каждого приложения. Это становится очевидно, если учесть, что в то время как одни приложения KDE (например, Gwenview) обладают исключительно функциями просмотра, то другие (Digikam) включа- ют еще и базовые функции редактирования. Если же все программы будут эволюционировать в одном и том же направлении, система утратит разнообразие и само наличие нескольких независимых про- грамм потеряет смысл. В отличие от других интерфейсов KDE, интерфейс KIPI сравнитель- но слабо документирован. Это значит, что наилучшим источником све- дений о программировании модулей KIPI являются исходные тексты модулей, написанных другими программистами. Информацию о модулях KIPI можно получить на сайте 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. В следующей, заключительной части серии мы узнаем, как создавать собственные компоненты (и модули для них).