...

воскресенье, 8 сентября 2013 г.

Еще один гайд по созданию плазмоида: конфигурация, события и уведомления



Dr.Konqi, мы с ним дружим, я его часто вижу %)

Вместо предисловия




Привет!

На хабре уже писали про то, что все плазмоиды нужно портировать на QML/JS, но я все равно продолжаю измываться над трупом CPP и пишу виджеты для плазмы на плюсах. Но, возможно, не все так плохо, %username%?

Для более простого примера написания плазмоида на C++ можно обратиться к этой статье. В настоящей же статье на голый виджет мы попробуем добавить немного фич (в порядке возрастания) — конфигурационный интерфейс, обработку некоторых событий и уведомления.

Если кого заинтересовало — продолжение ниже.



Идея виджета




Так как задачка для меня была исключительно учебно-самообразовательной, то идея виджета проста: возьмем и форкнем виджет Oblique Strategies для GNOME. Таким образом, в нашем виджете будет:


  • Label с текстом с карточек

  • Label с копирайтом (служит в первую очередь для указания текущей редакции)

  • Конфигурационный интерфейс, включающий в себя настройку текста и выбор редакции

  • Обновление текста по клику мышкой

  • Опциональная функция автообновления

  • Опциональная функция вывода текущего сообщения при автоматической смене в стандартные уведомления




Компоненты




Виджет



Хидер


#ifndef OBLIKUESTRATEGIES_H
#define OBLIKUESTRATEGIES_H

#include <Plasma/Applet>
#include <Plasma/Label>

#include <ui_configwindow.h>

class QGraphicsLinearLayout;

class oblikuestrategies : public Plasma::Applet
{
Q_OBJECT
public:
oblikuestrategies(QObject *parent, const QVariantList &args);
~oblikuestrategies();
int setMessagesText();
void init();

public slots:
int autoUpdateEvent();
int sendNotification(QString eventId, int num);
int updateEvent();
void mousePressEvent(QGraphicsSceneMouseEvent *event);
// for configuration interface
int setAutoUpdate();
void configAccepted();
void configChanged();

protected:
void createConfigurationInterface(KConfigDialog *parent);

private:
// ui
Plasma::Label *main_label;
Plasma::Label *info_label;
QTimer *timer;
// variables
bool autoUpdate_bool, notify_bool;
int autoUpdate_int, edition, fontSize, fontWeight;
QString fontFamily, fontColor, fontStyle;
QStringList formatLine, copyright;
QList<QStringList> mess;
// configuration interface
Ui::ConfigWindow uiConfig;
};

K_EXPORT_PLASMA_APPLET(oblikue-strategies, oblikuestrategies)
#endif /* OBLIKUESTRATEGIES_H */





Это будет эдакая «карта». Суть K_EXPORT_PLASMA_APPLET рассказана в указанной выше статье (это единственная принципиально важная штука в хидере). Подключим первоначальные библиотеки, объявим класс и деструктор

#include "oblikue-strategies.h"
#include <QGraphicsLinearLayout>
#include <plasma/theme.h>

oblikuestrategies::oblikuestrategies(QObject *parent, const QVariantList &args) :
Plasma::Applet(parent, args)
{
setBackgroundHints(DefaultBackground);
setHasConfigurationInterface(true);
}

oblikuestrategies::~oblikuestrategies()
{
delete info_label;
delete main_label;
delete timer;
}




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

void oblikuestrategies::init()
{
if (setMessagesText() != 0)
return;

// generate ui
// layout
QGraphicsLinearLayout *layout = new QGraphicsLinearLayout(this);
layout->setOrientation(Qt::Vertical);
// label
layout->addStretch(1);
main_label = new Plasma::Label(this);
main_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
main_label->setToolTip(qApp->translate("tooltip", "Click here to update message"));
layout->addItem(main_label);
layout->addStretch(1);
// copyright label
info_label = new Plasma::Label(this);
layout->addItem(info_label);
}




Функция int setMessagesText() забивает переменные copyright и собственно текст карт (mess), попутно создает список из двух элементов formatLine. Затем мы создаем Layout, на ней две метки — одна для текста, другая для копирайта — и кидаем распорки (stretch) для красивого вида. Далее перейдем к конфигурационному интерфейсу

Конфигурация



Нарисуем формочку. Пусть она будет такой:



Теперь прикрутим функции. Считывание конфига:

void oblikuestrategies::configChanged()
{
KConfigGroup cg = config();

edition = cg.readEntry("edition", 1);
fontFamily = cg.readEntry("font_family", "Terminus");
...
}




Обращу ваше внимание на то, как считываются переменные. Метод readEntry имеет два аргумента — первый имя переменной в конфигурационном файле (в глубине хомяка plasma-desktop-appletrc), второй — дефолтное значение. Настройки, записанные с помощью KConfigGroup, будут сохранены в указанном выше файле. Добавлю, что эта функция автоматически вызовется, если были изменены настройки виджета.

Не забыли про запись конфига:

void oblikuestrategies::configAccepted()
{
KConfigGroup cg = config();

cg.writeEntry("edition", uiConfig.comboBox_edition->currentIndex()+1);
cg.writeEntry("font_family", uiConfig.fontComboBox_font->currentFont().family());
...
}




Аналогично считыванию. Метод writeEntry также имеет 2 аргумента — имя и то, откуда брать значения. Теперь сам интерфейс прикрутим:

void oblikuestrategies::createConfigurationInterface(KConfigDialog *parent)
{
QWidget *configwin = new QWidget;
uiConfig.setupUi(configwin);

uiConfig.comboBox_edition->setCurrentIndex(edition-1);
...
parent->addPage(configwin, i18n("Oblikue Strategies"), Applet::icon());
connect(parent, SIGNAL(okClicked()), this, SLOT(configAccepted()));
}




Объявили интерфейс, выставили значения (переменные уже считаны в init() вызовом метода configChanged()). Потом добавили интерфейс к окошечку (метод addPage — первый аргумент что, второй как назвать и третий иконка). И связали кнопку с сохранением настроек. Дальше прикрутим эвенты.

Обработка событий



Пусть у нас есть некоторый метод, который отвечает за обновление текста. Назовем его int updateEvent() (возвращает номер вызванного сообщения). Прикрутим обработку клика мыши:

void oblikuestrategies::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
// mouse click event
if (event->buttons() == Qt::LeftButton)
updateEvent();
}




Если двойной клик, то нужно наследоваться от mouseDoubleClickEvent и, очевидно, проверка на кнопку не нужна. Теперь добавим автообновление. Сначала добавим в метод init() объявление таймера:

...
// timer
timer = new QTimer(this);
timer->setSingleShot(false);
...




Установим его в «многоразовое» использование (setSingleShot(false)). Далее прикрутим отключение и включение таймера в методе configChanged():

if (autoUpdate_bool == true)
{
disconnect(timer, SIGNAL(timeout()), this, SLOT(autoUpdateEvent()));
timer->stop();
}
// считывание переменных
...
if (autoUpdate_bool == true)
{
connect(timer, SIGNAL(timeout()), this, SLOT(autoUpdateEvent()));
timer->start(autoUpdate_int * MSEC_IN_MIN);
}




На всякий случай: метод start() имеет аргумент период в мсек. Добавим функцию автообновления:

int oblikuestrategies::autoUpdateEvent()
{
// auto update text
int num = updateEvent();
if (notify_bool == true)
if (sendNotification(QString("newMessage"), num) != 0)
return 1;
return 0;
}




Тут то нам и понадобилось значение, возвращаемое методом updateEvent(). Данный метод вызывает обновление текста, затем, если установлено notify_bool == true вызывает метод, который отправляет уведомления в KDE.

Уведомления



Сначала создадим файлик plasma_applet_oblikue-strategies.notifyrc

[Global]
IconName=oblikue-strategies
Name=Oblikue Strategies
Comment=Oblikue Strategies

[Event/newMessage]
Name=New message
Comment=There is auto updated message in widget
Action=Popup




Первое — общие настройки, их можно оставить без комментариев. Дальше идет перечисление событий и что они из себя представляют. Теперь вернемся к исходникам. Один единственный метод:

int oblikuestrategies::sendNotification(QString eventId, int num)
{
// send notification
KNotification *notification = new KNotification(eventId);
notification->setComponentData(KComponentData("plasma_applet_oblikue-strategies"));
notification->setTitle(QString(i18n("Oblikue Strategies")));
notification->setText(mess[edition-1][num]);
notification->sendEvent();
delete notification;
return 0;
}




eventId — собственно наш эвент. В методе setComponentData указываем имя апплета (чтоб не путаться и для упрощения). Ставим подпись, текст и отправляем сообщение в систему.

Сборка



ls -1 sources


CMakeLists.txt
configwindow.ui
oblikue-strategies.cpp
oblikue-strategies.h
oblikue-strategies.png
plasma-applet-oblikue-strategies.desktop
plasma_applet_oblikue-strategies.notifyrc





CMakeLists.txt


project (plasma_applet_oblikue-strategies)

find_package (KDE4 REQUIRED)
include (KDE4Defaults)

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

set (PLUGIN_NAME ${PROJECT_NAME})
file (GLOB PROJECT_DESKTOP *.desktop)
file (GLOB PROJECT_ICON *.png)
file (GLOB PROJECT_NOTIFY *.notifyrc)
file (GLOB PROJECT_SOURCE *.cpp)
file (GLOB PROJECT_UI *.ui)

kde4_add_ui_files (PROJECT_SOURCE ${PROJECT_UI})
kde4_add_plugin (${PLUGIN_NAME} ${PROJECT_SOURCE})
target_link_libraries (${PLUGIN_NAME} ${KDE4_PLASMA_LIBS} ${KDE4_KDEUI_LIBS})

# install
install (TARGETS ${PLUGIN_NAME} DESTINATION ${PLUGIN_INSTALL_DIR})
install (FILES ${PROJECT_DESKTOP} DESTINATION ${SERVICES_INSTALL_DIR})
install (FILES ${PROJECT_ICON} DESTINATION ${ICON_INSTALL_DIR})
install (FILES ${PROJECT_NOTIFY} DESTINATION ${DATA_INSTALL_DIR}/${PLUGIN_NAME})





Файлик не совсем правильный (с точки зрения человеческого фактора, например), но универсальный (поменять только имя проекта на нужное). Из отличий от обычных файлов сборок — вызов kde4_add_ui_files для создания конфигурационного интерфейса. И установка файла с нотификациями.

Постскриптум




Исходники этого безобразия.

Что получилось:



По материалам

Спасибо за внимание!


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends:



Комментариев нет:

Отправить комментарий