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 указываем имя апплета (чтоб не путаться и для упрощения). Ставим подпись, текст и отправляем сообщение в систему.
Сборка
CMakeLists.txt
configwindow.ui
oblikue-strategies.cpp
oblikue-strategies.h
oblikue-strategies.png
plasma-applet-oblikue-strategies.desktop
plasma_applet_oblikue-strategies.notifyrc
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 для создания конфигурационного интерфейса. И установка файла с нотификациями.
Постскриптум
Исходники этого безобразия.
Что получилось:
По материалам
Спасибо за внимание!
Автор: arcan1s