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

LXF109:KDE4

Материал из Linuxformat
Перейти к: навигация, поиск
KDE 4 Создаем приложения для самого современного рабочего стола в Linux.

Содержание

Сияющая Plasma

KDE4
ЧАСТЬ 2 Пожалуй, ни одна из технологий KDE 4 не привлекла к себе столько внимания, как эта. Андрей Боровский докажет, что написать собственный плазмоид совсем не сложно.

Если вы уже работали в графической среде KDE 4, то просто не могли не заметить плазмоиды. Для тех, кто все же не знает, что это такое, поясним. Плазмоиды представляют собой улучшенный и украшенный вариант апплетов, известных пользователям KDE с незапамятных времен. Свое название плазмоиды получили от системы Plasma, которая призвана существенно расширить возможности рабочего стола KDE и улучшить его внешний вид. Функциональность Plasma, в свою очередь, основана на новых возможностях библиотеки Qt 4, прежде всего – на системе Graphics View Framework (LXF105). Помимо нее, Plasma использует новые средства для работы с OpenGL, появившиеся в Qt 4. С точки зрения программиста, Plasma представляет собой API, основная часть которого реализована в библиотеке libplasma.

Все это будет подробно рассмотрено ниже. Сейчас же мы попробуем ответить на вопрос: стоит ли вам, как разработчику, изучать плазмоиды и заниматься их написанием. Излишне пояснять, что далеко не каждая программа может (или должна) быть выполнена в виде плазмоида. На сегодняшний день их написано немало (список приложений данного типа, известных команде KDE, можно найти по адресу http://techbase.kde.org/Projects/Plasma/Plasmoids), так что некоторые тенденции уже очевидны. Поскольку плазмоиды «живут» на рабочем столе или на панели задач, основная их часть предназначена для того, чтобы информировать пользователя о различных событиях в системе или за ее пределами (к этой категории относятся часы, измерители сетевого трафика, RSS-читалки и т.п.). Меньшая по численности группа плазмоидов предназначена для решения несложных, но часто востребованных задач (калькуляторы, словари, быстрый запуск программ). Встречаются, впрочем, и плазмоиды-игры, плазмоиды для просмотра комиксов и другая экзотика. С одной стороны – область применения плазмоидов ограничена. С другой, их написание – та сфера, в которой начинающий программист, одержимый потоком оригинальных идей, может проявить себя. Стоит также отметить, что идея плазмоидов понравилась не только создателям рабочего стола, и постепенно проникает и в другие области KDE-строения.

Как и все другие расширения KDE, с технической точки зрения плазмоиды представляют собой разделяемые библиотеки. Программирование плазмоидов – дело нетрудное, а вот их отладка – довольно нудный процесс. Когда мы отлаживаем обычные приложения, нам часто приходится перезапускать их; когда мы отлаживаем расширения Plasma, нам порой приходится перезапускать Плазму. Впрочем, и это нетрудно сделать – перезагружать X-сервер, по крайней мере, не придется. К тому же для отладки плазмоидов существует специальный инструмент – plasmoidviewer.

Покажите мне код!

Основой любого плазмоида является класс, наследующий Plasma::Applet или одному из его потомков. В библиотеке libplasma определены Plasma::GLApplet – специальный класс для создания плазмоидов, использующих OpenGL, и Plasma::PopupApplet – каркас для создания «всплывающих» плазмоидов. Все они происходят от QGraphicsWidget. От него же ведут свою родословную несколько классов, предназначенных для отображения данных внутри плазмоида: Plasma::Flash (вывод текста или графики в течение некоторого промежутка времени), Plasma::Icon (пиктограмма), Plasma::Meter (для отображения шкал уровня в различных форматах), Plasma::SignalPlotter (отрисовка нескольких графиков, меняющихся с течением времени) и Plasma::WebContent, который способен отображать содержимое web-страниц, заданных файлом или ссылкой.

Наш первый плазмоид распечатывает содержимое файла /proc/cpuinfo в своем окне (это не самое интересное, что можно сделать с помощью Plasma, но все не Hello World). Ниже приводится объявление класса плазмоида (полные исходные тексты вы, как всегда, найдете на диске).

  class SysInfo_1 : public Plasma::Applet {
     Q_OBJECT
  public:
     SysInfo_1(QObject *parent, const QVariantList &args);
     ~SysInfo_1();
     void paintInterface(QPainter *painter,
     const QStyleOptionGraphicsItem *option, const QRect&contentsRect);
     void init();
     private:
     Plasma::Svg m_svg;
  };
  K_EXPORT_PLASMA_APPLET(sysinfo_1, SysInfo_1)

Как видно, он содержит объявления четырех основных методов, которые обычно перекрываются в классах-потомках Plasma::Applet – конструктора, деструктора, paintInterface() и init(). Из них нам реально понадобятся только два – конструктор и paintInterface().

Макрос K_EXPORT_PLASMA_APPLET() выполняет рутинную работу по подготовке созданного нами класса к экспорту из библиотеки плазмоида. Первый его аргумент – имя библиотеки (без указания номера версии и расширения .so), второй – имя класса плазмоида. Ради удобства, а также для того, чтобы избежать некоторых неочевидных ошибок, я рекомендую использовать единую систему имен для всех файлов проекта плазмоида. Например, если целевая библиотека будет носить имя sysinfo_1.x.so, файлы с объявлением и определением класса плазмоида мы назовем sysinfo_1.h и sysinfo_1.cpp, соответственно. Файл описания плазмоида получит имя sysinfo_1.desktop.

Несколько слов о заголовочных файлах. В разных версиях libplasma, соответствующих различным версиям KDE 4, их имена могут варьироваться. В той системе, в которой я писал эти примеры, заголовочные файлы имеют вид: <plasma/applet.h>, <plasma/widgets/lineedit.h>. В вашей системе они могут выглядеть так же, а могут – и иначе, например: <Plasma/Applet>, <Plasma/Widgets/LineEdit>. CMake сам по себе тут не поможет, ведь он не станет править ваши исходные тексты [можно, правда, воспользоваться интроспекцией и условными директивами препроцессора, – прим. ред.]! Так что вам придется провести собственное исследование директории /usr/include/. Помните также, что интерфейс libplasma продолжает меняться. Примеры, подготовленные для этой статьи, используют некий общий знаменатель для всех версий API libplasma (начиная с KDE 4.0.*). Кроме того, в тексты примеров добавлены вызовы методов, определенных в более поздних версиях KDE. Эти вызовы закомментированы, и к ним даны пояснения. Если вы пользуетесь KDE 4.1 или KDE 4.2, можете раскомментировать их и сравнить результаты.

Как вы, конечно, поняли, самый интересный метод в классе SysInfo_1 – paintInterface(). Он выполняет отрисовку интерфейса плазмоида:

  void SysInfo_1::paintInterface(QPainter *p, const QStyleOptionGraphicsItem *option, const QRect &contentsRect) {
    p->setRenderHint(QPainter::SmoothPixmapTransform);
    p->setRenderHint(QPainter::Antialiasing);
    m_svg.resize((int)contentsRect.width(), (int)contentsRect.height());
    m_svg.paint(p, (int)contentsRect.left(), (int)contentsRect.top());
    QFile file("/proc/cpuinfo");
    file.open(QIODevice::ReadOnly);
    QByteArray ba = file.readAll();
    file.close();
    QString s(ba);
    p->save();
    p->setPen(Qt::white);
    p->drawText(contentsRect, Qt::AlignTop | Qt::AlignLeft, s);
    p->restore();
  }

Метод paintInterface() вызывается системой Plasma каждый раз, когда нужно перерисовать плазмоид. Помимо прочего, ему передается указатель на объект QPainter (переменная p) и ссылка на объект, обозначающий прямоугольную область вывода изображения, учитывающую отступы от края окна плазмоида (переменная contentsRect). В простейшем случае отрисовка интерфейса сводится к выводу графики с помощью объекта p.

В процессе отображения интерфейса плазмоида активно используются функции libplasma, предназначенные для работы с форматом SVG. Это неудивительно, если вспомнить, что плазмоиды можно масштабировать и вращать прямо на рабочем столе (разумеется, при работе с QPainter вы можете использовать любые графические функции, но для того, чтобы ваш плазмоид выглядел профессионально, следует предусмотреть возможность масштабирования и вращения). Помимо прочего, использование SVG позволяет настраивать внешний вид плазмоида, не внося изменений в его код, что должны высоко оценить авторы тем для оформления KDE 4. За работу с SVG в libplasma отвечает класс Plasma::Svg (мы сохраняем объект данного типа в переменной m_svg). Класс Plasma::Svg может не только преобразовывать данные из формата SVG в растровое изображение: он также «знает», где искать стандартные файлы изображений KDE, и может отрисовывать отдельные фрагменты файлов SVG, заданные своими идентификаторами. Последнее свойство очень полезно, так как позволяет хранить графические элементы сложного интерфейса приложения в одном файле, даже если они должны отображаться независимо друг от друга. Мы используем объект m_svg для отрисовки элементов окна плазмоида. Содержимое файла /proc/cpuinfo выводится на экран с помощью метода drawText() объекта QPainter. Хотя это и не обязательно, правила хорошего тона требуют вызвать метод save() объекта QPainter перед выводом собственной графики и метод restore() по окончании процесса

Метод init() выполняется системой Plasma после вызова конструктора, причем только один раз. В нем можно поместить код, инициализирующий элементы плазмоида после того, как объект уже создан. Если в ходе этого процесса возникла ошибка, вы можете вызвать метод объекта-апплета setFailedToLaunch(). Первый его аргумент имеет тип bool (значение true указывает, что в процессе инициализации плазмоида произошла ошибка). Второй параметр типа «строка» позволяет сообщить системе, что же именно стряслось. Вызов setFailedToLaunch() – вежливый способ аварийного завершения плазмоида. В тот же файл, где мы храним реализации методов его класса, необходимо добавить строчку

 #include "sysinfo_1.moc"

И что получилось?

Еще нам нужно создать файл описания плазмоида с расширением .desktop. Кстати, он требуется не только плазмоидам, но и вообще любым расширениям KDE. В .desktop- файле хранится описание расширения, а также инструкции для его загрузки системой. Наш

файл расширения (sysinfo_1.desktop) выглядит так:
[Desktop Entry]
Name="Plasmoid Demo"
Comment="Plasmoid Demo"
Type=Service
X-KDE-ServiceTypes=Plasma/Applet
X-KDE-Library=sysinfo_1
X-KDE-PluginInfo-Author=Andrei Borovsky
X-KDE-PluginInfo-Email=anb@symmetrica.net
X-KDE-PluginInfo-Name=sysinfo_1
X-KDE-PluginInfo-Version=0.1
X-KDE-PluginInfo-Website=http://symmetrica.net
X-KDE-PluginInfo-Category=Examples
X-KDE-PluginInfo-Depends=
X-KDE-PluginInfo-License=GPL
X-KDE-PluginInfo-EnabledByDefault=true

Параметру Name присваивается имя расширения, предназначенное для показа пользователю. Под ним плазмоид будет известен, например, утилите добавления виджетов на рабочий стол. Параметр Comment содержит описание. Поле Type сообщает системе, что данное расширение относится к типу «сервис». Среди других важных параметров стоит отметить X-KDE-ServiceTypes, X-KDE-Library (имя библиотеки расширения) и X-KDE-PluginInfo-Name (имя плазмоида, используемое системой). Все остальное в данном файле – «лирика».

Нам остается только создать файл инструкций для CMake (см. эту статью).

project(sysinfo_1)
find_package(KDE4 REQUIRED)
include(KDE4Defaults)
find_package(Plasma REQUIRED)
add_definitions (${QT_DEFINITIONS} ${KDE4_DEFINITIONS})
include_directories(${KDE4_INCLUDES} )
set(sysinfo_1_SRCS sysinfo_1.cpp)
kde4_add_plugin(sysinfo_1 ${sysinfo_1_SRCS})
target_link_libraries(sysinfo_1 ${PLASMA_LIBS} ${KDE4_KDEUI_LIBS})
install(TARGETS sysinfo_1 DESTINATION ${PLUGIN_INSTALL_DIR})
install(FILES sysinfo_1.desktop DESTINATION ${SERVICES_INSTALL_DIR})

Новых для нас элементов в этом файле не так уж и много. Мы загружаем пакет Plasma, без которого плазмоид, естественно, компилироваться не может. Команда kde4_add_plugin() указывает, что целью сборки является расширение KDE 4 (а не исполняемый файл, как в прошлый раз). Теперь вы можете скомандовать

cmake <project_dir>
make

В результате в каталоге проекта появится поддиректория lib, в которой будет создан файл sysinfo_1.so. Далее можно было бы перейти в режим root и скомандовать

Рис. 1 Рис. 1 Добавление плазмоида на рабочий стол.

make install

но тут нас поджидает одна неприятная неожиданность. Хотя в нашем проекте CMake и есть инструкции для генерации цели install в Make-файле, и соответствующий раздел будет создан, инсталляция плазмоида по умолчанию выполняется не туда, куда следует (этот эффект замечен и в KDE 4.0.x, и в KDE 4.1). Например, в моей системе библиотека sysinfo_1.so по умолчанию устанавливается в /usr/local/lib/kde4/, а файл sysinfo_1.desktop – в /usr/local/share/kde4/services/. Система не станет искать новый плазмоид в этих директориях, а значит, и не найдет. Правильные каталоги (для моей системы) – это /usr/lib/kde4/ и /usr/share/kde4/services/ соответственно. Чтобы заставить плазмоид работать, можно скопировать файлы .so и .desktop в указанные директории вручную, а можно попробовать запустить команду cmake с ключом -DCMAKE_INSTALL_PREFIX=/usr/lib/kde4/.

Увидеть установленный плазмоид в работе можно после перезапуска Plasma (или с помощью утилиты plasmoidviewer, о которой будет сказано ниже). Выполняется он очень просто – двумя командами из окна консоли от имени обычного пользователя:

 kquitapp plasma
 plasma

Не вздумайте закрывать окно консоли, скомандовав kquitapp plasma! В этом случае вы рискуете остаться один на один с серым экраном, и вот тогда уже для продолжения работы придется перезапускать систему. После рестарта Plasma апплет Add Widget должен «увидеть» новый плазмоид (рис. 1), а мы сможем добавить наше творение на рабочий стол и прочитать содержимое файла /proc/cpuinfo (рис. 2).

Рис. 2 Рис. 2 Плазмоид в деле.

Управляемый виджет

Большинство плазмоидов KDE 4 делают практически то же, что и наш «первый блин» – просто показывают пользователю какие-то данные. Справедливости ради надо сказать, что плазмоиды редко отображают статичную информацию, как в нашем примере: чаще данные считываются из какого-то динамического источника с регулярными интервалами. KDE 4 предоставляет специальный механизм для работы с такими источниками данных (об этом мы поговорим в следующей статье). Сейчас же мы напишем еще один простой плазмоид, но на сей раз добавим в него элементы управления, а точнее, один-единственный виджет – строку ввода. Плазмоид будет выполнять набранные в нем команды Linux (как это всегда бывает в учебных программах, функциональность не оригинальна, но реализация – поучительна). За основу в нашем примере взят плазмоид applet-runner, написанный Себастьяном Кжижковяком [Sebastian Krzyszkowiak].

Изначальный вариант плазмоида был рассчитан на KDE 4.0.x. Я добавил в него вызов некоторых методов, реализованных в более поздних версиях KDE 4, но на всякий случай закомментировал их, снабдив соответствующими пояснениями. Если вы работаете с более новой версией KDE 4 и хотите поэкспериментировать, можете раскомментировать эти вызовы.

При написании плазмоидов с элементами управления возникает соблазн воспользоваться стандартными виджетами из библиотек Qt/KDE, однако на данном этапе развития KDE 4 это может привести к краху системы. Помимо прочего, следует учесть, что для стандартного объекта-виджета трудно подобрать достойного «родителя». Главный класс плазмоида является потомком QGraphicsWidget, который не происходит от QWidget. QGraphicsWidget появился в библиотеке Qt 4.2 как компромиссное решение, которое позднее утратило свою актуальность. С самого начала разработчики Graphics View Framework понимали, что программисты захотят встраивать виджеты в графические сцены, однако механизм для этого в Qt 4.2 реализован не был (он появился в Qt 4.4). Вместо него разработчики ввели класс QGraphicsWidget, который, как и все классы-элементы графической сцены, является потомком класса QGraphicsItem, но при этом содержит ряд методов, подобных методам класса QWidget. Предполагалось, что класс QGraphicsWidget поможет программистам портировать виджеты на систему QGraphicsItem. Позднее разработчики пришли к более логичному решению – встраиванию уже готовых «настоящих» виджетов, потомков класса QWidget, но система Plasma, по крайней мере, в нынешнем своем воплощении, реализует более ранний подход.

Так или иначе, в настоящее время в апплетах для Plasma рекомендуется использовать виджеты, определенные в библиотеке libplasma: Plasma::CheckBox, Plasma::ComboBox, Plasma::GroupBox, Plasma::Label, Plasma::LineEdit, Plasma::TextEdit, Plasma::PushButton, Plasma::RadioButton. Все эти классы, в конечном счете, являются потомками класса QGrapihcsItem (а не QWidget), однако с виджетами, которые они реализуют, нельзя выполнять многие операции, допустимые при работе с объектами других классов-потомков QGrapihcsItem.

Рис. 3Рис. 3. Плазмоид с элементами управления.

Наш плазмоид использует виджет Plasma::LineEdit для ввода команд. В моей системе этот виджет объявлен в заголовочном файле <plasma/widgets/lineedit.h>, а вашей он может быть объявлен в файле <Plasma/Widgets/LineEdit>. Объект класса Plasma::LineEdit создается в методе init() главного объекта плазмоида (наконец-то он нам пригодился!):

<spurce lang=cpp>

 void PlasmaRunner::init() {
   pole = new Plasma::LineEdit(this);
   pole->setPlainText("");
   pole->setDefaultTextColor(QColor("black"));
   connect(pole, SIGNAL(editingFinished()), this, SLOT(run()));
 }

</source>

Мы работаем с Plasma::LineEdit как с обычным виджетом – создаем объект, настраиваем его внешний вид, связываем сигнал объекта-виджета со слотом. В более поздних версиях KDE 4 у класса Plasma::LineEdit появляется метод setStyleSheet(), который позволяет настраивать внешний вид с помощью стандартного описания стиля. Если вы работаете в такой версии KDE 4, можете использовать его для придания строке ввода желаемого экстерьера, мы же воздержимся ради универсальности.

Метод paintInterface() плазмоида с элементами управления интересен тем, что объединяет отображение виджета с выводом графики (в нашем случае – поясняющей подписи) с помощью объекта QPainter:

  void PlasmaRunner::paintInterface(QPainter *p,
          const QStyleOptionGraphicsItem *option, const QRect &contentsRect) {
    p->setRenderHint(QPainter::SmoothPixmapTransform);
    p->setRenderHint(QPainter::Antialiasing);
    p->save();
    p->setPen(Qt::black);
    if (formFactor() == Plasma::Planar || formFactor() == Plasma::MediaCenter) {
    p->drawText(contentsRect, Qt::AlignTop, i18n("Run command:"));
    pole->setGeometry(QRectF(0, 20, contentsRect.width(), contentsRect.height() ));
  } else {
    pole->setGeometry(QRectF(0, 5, contentsRect.width(),contentsRect.height() ));
  }
    p->restore();
  }

Остальные элементы интерактивного плазмоида (рис. 3) похожи на рассмотренные ранее. Чтобы посмотреть, как выглядит и работает плазмоид, вовсе не обязательно перезапускать Plasma. Специальная утилита plasmoidviewer позволяет загрузить собранный и установленный плазмоид в обычном окне программы KDE (рис. 4). Перед запуском программы plasmoidviewer следует вызвать программу kbuildsycoca4, которая обновит кэш KDE 4. Аргументом plasmoidviewer должно быть имя плазмоида, но не то, которое отображается в апплете Add Widgets, а то, которое указано в параметре X-KDE-PluginInfo-Name .desktop-файла.

Рис. 4Рис. 4. Утилита plasmoidviewer.

Как было сказано выше, стандартные виджеты Qt слабо интегрированы в систему плазмоидов. А можно ли использовать расширения libplasma в обычных Qt-программах вместе с системой Graphics View Framework? Теоретически – да, практически – нет, по крайней мере, пока. Объекты класса Plasma::SignalPlotter можно заставить работать в обычной программе; другие элементы libplasma либо не работают, либо приводят к краху приложения. Будем надеяться, что ситуация улучшится в будущих версиях KDE. LXF

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