imageНачиная с версии 4.x, на смену проверенных с годами, но морально устаревших (по этому поводу, конечно, мнения могут и разделиться) KDesktop, Kicker и SuperKaramba в KDE пришел Plasma Desktop. Этот фреймворк представляет рабочий стол (по крайней мере, его видимую часть), как совокупность виджетов или плазмоидов. Обзаведясь в один прекрасный момент некоторым количеством свободного времени, я решил немного разобраться с Plasma SDK, реализовав функцию, которой на тот момент мне действительно не хватало в составе моего рабочего стола. А функция эта — возможность без привлечения дополнительных приложений отправлять записи в LiveJournal. То есть этакий миниклиент, встроенный прямо в десктоп. «Было бы здорово», — подумал я и принялся ворошить интересующую предметную область.

Для того, чтобы осуществить все, что описано далее, в системе должны присутствовать, собственно, KDE4, а также следующие установленные пакеты: kdesdk, kdebase-devel, kdelibs-devel, cmake. В зависимости от дистрибутива, пакеты могут иметь немного другие названия (я привожу имена для Fedora Core), но общий смысл, думаю, ясен.

Начнем с того, что плазмоиды бывают бинарные и скриптовые. Особо не раздумывая, я решил использовать близкие сердцу «плюсы» и создавать, соответсвенно, бинарный виджет, который представляет из себя .so (shared object) модуль, то есть динамически загружаемую библиотеку. Элементарный каркас должен выглядеть примерно так:

#ifndef __lj_plasmoid_h
#define __lj_plasmoid_h

#include <Plasma/Applet>

class QSizeF;
class LjPlasmoid : public Plasma::Applet
{
  Q_OBJECT
  public:

    LjPlasmoid(QObject *parent, const QVariantList &args);
    ~LjPlasmoid();

    void paintInterface(QPainter *painter, const QStyleOptionGraphicsItem *option, const QRect& contentsRect);
      void init();

};

#endif


* This source code was highlighted with Source Code Highlighter.



Все вполне очевидно: конструктор, деструктор, инициализация и отрисовка интерфейса. Ничего сложного. Почти пустая реализация:

#include "lj_plasmoid.h"

#include <QPainter>
#include <QFontMetrics>
#include <QSizeF>

#include <plasma/svg.h>
#include <plasma/theme.h>

// этот макрос призван рассказать плазме о нашем существовании
// а также связать сам модуль и .desktop файл

K_EXPORT_PLASMA_APPLET(ljplasmoid, LjPlasmoid)

LjPlasmoid::LjPlasmoid(QObject *parent, const QVariantList &args)
  : Plasma::Applet(parent, args)
{
  // воспользуемся стандартным фоном
  // и зададим размеры окна  

  setBackgroundHints(DefaultBackground);
  resize(400, 300);
}

LjPlasmoid::~LjPlasmoid()
{
}

void LjPlasmoid::init()
{
}

void LjPlasmoid::paintInterface(QPainter *p,
    const QStyleOptionGraphicsItem *option, const QRect &contentsRect)
{
  // рисовать нам пока нечего
}

* This source code was highlighted with Source Code Highlighter.



Сохранив все это, я принялся собирать свой плазмоид, чтобы безотлагательно убедиться, что что мой виджет, отрисовывает пустую область в красивой рамке. Для этого помимо самого модуля необходим .desktop файл следующего содержания:

[Desktop Entry]
Name=LjPlasmoid
Comment=LiveJournal Plasmoid
Type=Service
ServiceTypes=Plasma/Applet

X-KDE-Library=plasma_applet_ljplasmoid
X-KDE-PluginInfo-Author=Ivan Ivanov
X-KDE-PluginInfo-Email=ivan.ivanov@mail.ru
X-KDE-PluginInfo-Name=ljplasmoid
X-KDE-PluginInfo-Version=0.1
X-KDE-PluginInfo-Website=http://plasma.kde.org/
X-KDE-PluginInfo-Category=Examples
X-KDE-PluginInfo-Depends=
X-KDE-PluginInfo-License=GPL
X-KDE-PluginInfo-EnabledByDefault=true


Внимание здесь следует обратить на то, что значение поля X-KDE-PluginInfo-Name должно совпадать с первым параметром макроса K_EXPORT_PLASMA_APPLEТ. Далее сам модуль необходимо скопировать в папку с библиотеками KDE4 (для большинства случаев это '/usr/lib/kde4', либо в случае x86_64 сборки '/usr/lib64/kde4'), а .desktop файл следует отправить по адресу '/usr/share/kde4/services'. Ну и финальным штрихом нам нужно заставить плазму перечитать свои данные, чтоб вновь созданный виджет появился в списке доступных. Делается это путем выполнения смешной команды 'kbuildsycoca4'.

Есть два способа увидеть плазмоид в действии: добавить его непосредственно на рабочий стол, либо использовать утилиту 'plasmoidviewer', которая покажет виджет в отедельном окне. Однако мне не помог ни тот, ни другой. Вместо своего «детища» я упорно взирал на сообщение об ощибке, которое утверждало, что объект с именем 'ljplasmoid' ну никак не может быть создан.

Попытки локализовать проблему протекали долго и мучительно. В ход пошли все средства, вплоть до прекрасной утилиты 'strace', с помощью которого я пытался определить, обращается ли процесс 'plasma-desktop' к моего бинарн��му модулю. Оказалось, таки да, обращается, но толку от этого было мало — ошибка упорно продолжала проявляться. В самую последнюю очередь через несколько часов топтания на одном и том же месте я стал подозревать, что проблема возможно кроется в сборке (для редактирования исходного кода и сборки я использовал IDE CodeBlocks). Отчаявшись решить проблему иным способом, я решил попробовать использовать для компиляции предлагаемый на сайте KDE cmake-script. Вот он c минимальными изменениями:

project(ljplasmoid)

set(CMAKE_VERBOSE_MAKEFILE ON)
find_package(KDE4 REQUIRED)
include(KDE4Defaults)

add_definitions (${QT_DEFINITIONS} ${KDE4_DEFINITIONS})
include_directories( ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${KDE4_INCLUDES})

set(ljplasmoid_SRCS lj_plasmoid.cpp)
kde4_add_plugin(plasma_applet_ljplasmoid ${ljplasmoid_SRCS})
target_link_libraries(plasma_applet_ljplasmoid ${KDE4_PLASMA_LIBS} ${KDE4_KDEUI_LIBS})


Для сборки с использованием утилиты 'cmake' нужно выполнить команду 'cmake -G«Unix Makefiles» && make' в папке, где находится файл CMakeLists.txt (в нем и содержится сам скрипт). По не совсем пока ясным для меня причинам смена метода компиляции принесла плоды — плазмоид заработал! Появившийся на рабочем столе полупрозрачный пустой прямоугольник помимо неописуемой радости, вызвал во мне сильнейший приступ энтузиазма, и я принялся продолжать начатое. Единственное, что омрачало мое удовольствие при такой модели разработки — это отсутствие единой IDE и как следствие необходимость постоянно переключаться между терминалом и редактором. Но и тут выход был найден довольно быстро. Среда разработки KDevelop и ее встроенный шаблон проекта 'CMake-based project' быстро решили все мои проблемы.

Дальше дело техники — добави��ь элементы пользовательского интерфейса (мне всего-то нужно поле для ввода и кнопка «Отправить»). Плазма использует Qt, хотя по факту приходится подключать классы, которые являются обвязками над Qt-шными. Итого у нас появляется несколько новых include'ов и объявлений в классе:

...

#include <Plasma/PushButton>
#include <Plasma/TextEdit>

...

class LjPlasmoid : public Plasma::Applet
{
  ...
  
  public slots:

    void PostPressed();

  public:

    Plasma::PushButton m_post_button;
    Plasma::TextEdit m_text_edit;
};


* This source code was highlighted with Source Code Highlighter.



Ну, и, естественн, о реализация становится при этом чуть более осмысленной:

LjPlasmoid::LjPlasmoid(QObject *parent, const QVariantList &args)
  : Plasma::Applet(parent, args), m_post_button(this), m_text_edit(this)
{
  m_post_button.setText("Post");
  m_text_edit.setText("[ok: put your text here]");

  connect(&m_post_button, SIGNAL(clicked()), SLOT(PostPressed()));
  
  setBackgroundHints(DefaultBackground);
  resize(300, 200);
}

void LjPlasmoid::paintInterface(QPainter *p,
    const QStyleOptionGraphicsItem *option, const QRect &contentsRect)
{
  p->setRenderHint(QPainter::SmoothPixmapTransform);
  p->setRenderHint(QPainter::Antialiasing);

  m_post_button.setGeometry(QRect(contentsRect.x() + contentsRect.width() - 75,
    contentsRect.y() + contentsRect.height() - 30, 10, 30));
  m_text_edit.setGeometry(QRect(contentsRect.x(), contentsRect.y(),
    contentsRect.width(), contentsRect.height() - 35));
}


* This source code was highlighted with Source Code Highlighter.



Таким образом у меня получился ничего не умеющий, но обладающий простенькой «мордочкой» виджет:

image

Для работы непосредственно с LiveJournal использовался API, описанный на официальном сайте, а также написанный мной для этих целей простой набор классов, использующий 'libcurl'. Исходники данного класса я публиковать не буду, поскольку они выходят за тему топика, но заинтересовавшиеся могут скачать их здесь.

Итак, остается реализоваться последний метод — отправку сообщения в журнал:

void LjPlasmoid::PostPressed()
{
 LjSession session;
 LjRequest post(&session, recieve_responce, this);

 post.AddData("mode", "postevent");
 post.AddData("user", "user_name");
 post.AddData("password", "user_password");
 post.AddData("ver", "1");
 post.AddData("event", m_text_edit.nativeWidget()->toPlainText().toAscii().data());
 post.AddData("lineendings", "unix");
 post.AddData("subject", "[Posted via LjPlasmoid]");
 post.AddData("security", "public");
 post.AddData("year", "2009");
 post.AddData("mon", "11");
 post.AddData("day", "23");
 post.AddData("hour", "20");
 post.AddData("min", "00");

 printf("text2send: %s\n", m_text_edit.nativeWidget()->toPlainText().toAscii().data());

 post.Post();
 m_text_edit.setText("[ok: put your text here]");
}


* This source code was highlighted with Source Code Highlighter.



Это всего лишь пример того, как создать плазмоид для свои�� собственных нужд. Именно по этой причине здесь нет обратки ошибок, тонкой настройки параметров поведения и т.д. Однако, общее и самое поверхностное представление о том, что что такое Plasma изнутри, этот топик дать вполне способен.

Что касается данного виджета, то планы следующие: довести до ума; реализовать вменяемую настройку; выложить на KDE-Look. С недавних пор обдумываю идею вынести всю протоколо-зависисую логику в lua-скрипты, обеспечив тем самым расширяемость и возможность работать с различными блог-платформами.

P. S. И самое главное: огромное спасибо хабраюзеру nsinreal за инвайт!