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

в 23:15, , рубрики: Без рубрики

Еще один гайд по созданию плазмоида: конфигурация, события и уведомления
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 для создания конфигурационного интерфейса. И установка файла с нотификациями.

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

Исходники этого безобразия.
Что получилось:
Еще один гайд по созданию плазмоида: конфигурация, события и уведомления
По материалам

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

Автор: arcan1s

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js