KDE4 Plasma Desktop. Создание плазмоида

    Plasma
    Плазмоид (plasmoid) — это виджет рабочего стола в KDE4 Desktop. Любой видимый элемент управления на рабочем столе является плазмоидом, будь то часы, системный трей, монитор загруженности процессора или окошко с прогнозом погоды.

    Этот урок описывает создание плазмоида, умеющего делать запросы к некоему серверу и показывать полученный результат. Так как сервер требует авторизации пользователя, будет разобран процесс хранения данных учетной записи пользователя в KWallet. Язык разработки: Python.

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

    Пакет


    Каждый плазмоид — это набор файлов, упакованных в zip архив. В этом уроке плазмоид будет называться «bwc-balance-plasmoid». Создадим одноименную директорию, в которой будут храниться все файлы проекта:
    ./contents/
    ./contents/code/
    ./contents/code/main.py
    ./metadata.desktop


    metadata.desktop

    metadata.desktop содержит все мета-данные о плазмоиде, например его название, автора или язык программирования, на котором он написан.
    [Desktop Entry]
    Encoding=UTF-8
    Name=BWC Balance
    Name[ru]=Баланс BWC
    Type=Service
    ServiceTypes=Plasma/Applet
    Icon=phone
    X-Plasma-API=python
    X-Plasma-MainScript=code/main.py
    X-KDE-PluginInfo-Author=SvartalF
    X-KDE-PluginInfo-Email=self@svartalf.info
    X-KDE-PluginInfo-Name=bwc-balance
    X-KDE-PluginInfo-Version=0.1
    X-KDE-PluginInfo-Website=http://bitbucket.org/svartalf/bwc-balance-plasmoid/
    X-KDE-PluginInfo-Category=Online Services
    X-KDE-PluginInfo-Depends=
    X-KDE-PluginInfo-License=GPL
    X-KDE-PluginInfo-EnabledByDefault=true

    Назначение всех полей достаточно очевидно, поэтому не будем останавливаться здесь.

    main.py



    Основной код плазмоида находится в файле main.py.

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

    Copy Source | Copy HTML
    1. from PyQt4.QtCore import *
    2. from PyQt4.QtGui import *
    3. from PyKDE4.kio import *
    4. from PyKDE4.kdeui import *
    5. from PyKDE4.kdecore import *
    6. from PyKDE4.plasma import Plasma
    7. from PyKDE4 import plasmascript
    8. from PyKDE4.solid import Solid


    Создаем класс плазмоида:
    Copy Source | Copy HTML
    1. class BWCBalancePlasmoid(plasmascript.Applet):
    2.     def __init__(self, parent, args=None):
    3.         plasmascript.Applet.__init__(self, parent)
    4.  
    5.     def init(self):
    6.         """Инициализация настроек"""
    7.  
    8.         # Плазмоид имеет пользовательские настройки
    9.         self.setHasConfigurationInterface(True)
    10.         self.setAspectRatioMode(Plasma.IgnoreAspectRatio)
    11.  
    12.         self.theme = Plasma.Svg(self)
    13.         self.theme.setImagePath("widgets/background")
    14.         self.setBackgroundHints(Plasma.Applet.DefaultBackground)
    15.  
    16.         # Расположение элементов плазмоида
    17.         self.layout = QGraphicsLinearLayout(Qt.Horizontal, self.applet)
    18.  
    19.         # Label для выводимых данных
    20.         self.label = Plasma.Label(self.applet)
    21.         self.label.setText("0.0") # Изначально пусть будет 0
    22.  
    23.         # Добавляем Label к схеме расположения
    24.         self.layout.addItem(self.label)
    25.         self.applet.setLayout(self.layout)
    26.  
    27.         # И изменяем размеры плазмоида
    28.         self.resize(50, 50)


    После описания класса добавьте небольшую функцию, вызываемую Plasma для создания плазмоида:
    Copy Source | Copy HTML
    1. def CreateApplet(parent):
    2.     return BWCBalancePlasmoid(parent)


    Для тестирования работы существует программа plasmoidviewer:
    svartalf ~ $ plasmoidviewer bwc-balance-plasmoid
    В результате получится такое красивое, но абсолютно не функциональное окошко.

    Plasmoid basic view

    Когда плазмоид будет готов, запакуйте его в zip архив.

    Получившийся файл устанавливается в систему следующей командой:
    plasmapkg -i bwc-balance-plasmoid.zip
    и удаляется:
    plasmapkg -r bwc-balance-plasmoid.zip

    Настройки



    Займемся реализацией пользовательских настроек.

    В Qt Designer делаем диалог на основе QDialog с двумя полями ввода, и сохраняем его в settings_ui.ui

    QT Designer

    Полученный .ui файл конвертируем в .py файл. Это будет базовый диалог настроек.

    pyuic4 settings_ui.ui -o settings_ui.py

    Созданный диалог будет наследоваться нашей формой настроек. При инициализации формы будем пытаться заполнить поля уже имеющимися данными из KWallet.

    Copy Source | Copy HTML
    1. class SettingsDialog(QWidget, Ui_SettingsDialog):
    2.     def __init__(self, parent=None):
    3.         QWidget.__init__(self)
    4.  
    5.         self.parent = parent
    6.         self.setupUi(self)
    7.  
    8.         # Открываем локальный «бумажник»
    9.         self.wallet = KWallet.Wallet.openWallet(KWallet.Wallet.LocalWallet(),  0)
    10.         if self.wallet:
    11.             # Выбираем нашу «папку» для хранения паролей
    12.             self.wallet.setFolder("bwc-balance-plasmoid")
    13.  
    14.             if not self.wallet.entryList().isEmpty():
    15.                 phone = str(self.wallet.entryList().first())
    16.                 password = QString()
    17.                 # Считываем пароль для номера телефона
    18.                 self.wallet.readPassword(phone, password)
    19.                 # И заполняем этими данными поля диалога
    20.                 self.textPhone.setText(phone)
    21.                 self.textPassword.setText(str(password))
    22.  
    23.     def get_settings(self):
    24.         return {"phone": str(self.textPhone.text()), "password": str(self.textPassword.text())}


    Функция KWallet.Wallet.openWallet() принимает третий, необязательный параметр OpenType: синхронный/асинхронный режим открытия бумажника

    При открытии в асинхронном режиме необходимо проверить возвращаемое значение функции, и если оно не равно None, подключить сигнал walletOpened() к слоту, который будет осуществлять работу с данными бумажника.

    В этом случае бумажник открыт в синхронном режиме, из него считываются данные и заполняют поля диалога.

    Теперь необходимо показывать этот диалог по просьбе пользователя и сохранять введенные данные.

    Допишем у класса плазмоида в init() следующие строки:
    Copy Source | Copy HTML
    1. # Здесь будет храниться объект диалога настроек
    2. self.settings_dialog = None
    3. # А это сами настройки
    4. self.settings = {"phone": None, "password": None}
    5.  
    6. # При загрузке плазмоида открываем бумажник в асинхронном режиме
    7. # Так пользователь сразу увидит плазмоид и предложение разрешить доступ
    8. # этому приложению к бумажнику
    9. self.wallet = KWallet.Wallet.openWallet(KWallet.Wallet.LocalWallet(),  0, 1)
    10. if self.wallet:
    11.     self.connect(self.wallet, SIGNAL("walletOpened(bool)"), self.walletOpened)


    В функции self.walletOpened() открываем бумажник, считываем пользовательские данные и запускаем таймер, который будет через определенный промежуток времени обновлять информацию.

    Wallet request

    Добавим к классу BWCBalancePlasmoid функцию, которая будет вызываться при выборе соответствующего пункта меню.

    Plasmoid call settings
    Copy Source | Copy HTML
    1. def showConfigurationInterface(self):
    2.     # Создаем объект нашего диалога
    3.     self.settings_dialog = SettingsDialog(self)
    4.  
    5.     # Встраиваем его в стандартную форму-диалог
    6.     dialog = KPageDialog()
    7.     dialog.setFaceType(KPageDialog.Plain)
    8.     dialog.setButtons(KDialog.ButtonCode(KDialog.Ok | KDialog.Cancel))
    9.     page = dialog.addPage(self.settings_dialog, u"Настройки ИССА")
    10.  
    11.     # Соединяем слоты с сигналами
    12.     self.connect(dialog, SIGNAL("okClicked()"), self.configAccepted)
    13.     self.connect(dialog, SIGNAL("cancelClicked()"), self.configDenied)
    14.  
    15.     dialog.resize(350, 200)
    16.     # Показываем диалог
    17.     dialog.exec_()


    Функции self.configAccepted() и self.ConfigDenied() будут вызываться при нажатии кнопок «Ok» и «Отмена» в диалоге. Так как при нажатии кнопки «Отмена» нам не нужно производить никаких действий, остается только описать логику configAccepted().

    Copy Source | Copy HTML
    1. def configAccepted(self):
    2.     # Обновляем данные в программе
    3.     self.settings = self.settings_dialog.get_settings()
    4.  
    5.     # И сохраняем их в бумажник
    6.     wallet = KWallet.Wallet.openWallet(KWallet.Wallet.LocalWallet(),  0)
    7.     if wallet:
    8.         if not wallet.hasFolder("bwc-balance-plasmoid"):
    9.             wallet.createFolder("bwc-balance-plasmoid")
    10.         wallet.setFolder("bwc-balance-plasmoid")
    11.         for e in wallet.entryList():
    12.             wallet.removeEntry(e)
    13.         wallet.writePassword(self.settings["phone"], self.settings["password"])


    После изменения настроек запускаем таймер обновления, и чтобы не ждать пока он сработает в первый раз, делаем загрузку данных.

    Обновление данных



    Условимся, что данные будут обновляться в раз в час. Прежде всего создадим таймер, по которому будет выполняться загрузка данных. Допишем к функции init() класса BWCBalancePlasmoid следующее:
    Copy Source | Copy HTML
    1. self.timer = QTimer()
    2. self.connect(self.timer, SIGNAL("timeout(bool)"), self.loadBalance)


    Здесь мы создаем объект класса QTimer() и подключаем его сигнал timeout() к функции loadBalance().

    Остается только после загрузки плазмоида запустить таймер:
    Copy Source | Copy HTML
    1. self.timer.start(1000*60*60) # Время указывается в миллисекундах 


    Загрузка данных



    Изначально загрузка данных производилась через urllib2.build_opener() и urllib2.Request(), но у этого метода были следующие минусы:
    • Загрузка субъективно занимала больше времени
    • Производилась в синхронном режиме, поэтому в процессе работы плазмоид не отзывался на действия пользователя

    Проблему решило использование сетевых функций KIO, а именно storedGet() и storedHttpPost(), которые работают в асинхронном режиме.

    Я не буду подробно останавливаться на процессе работы с этими функциями, только приведу примеры GET и POST запросов:

    GET

    Copy Source | Copy HTML
    1. self.job = KIO.storedGet(KUrl("http://example.com/account"), KIO.Reload, KIO.HideProgressInfo)
    2. self.job.addMetaData("User-Agent", "User-Agent: bwc-balance-plasmoid")
    3.  
    4. self.connect(self.job, SIGNAL("result(KJob*)"), self._get_result)


    POST

    Copy Source | Copy HTML
    1. self.job = KIO.storedHttpPost(QByteArray(urlencode({'phone': self.settings.get("phone"), 'password': self.settings.get("password")})), \
    2.     KUrl("</code><code>http://example.com/</code><code>login"), KIO.HideProgressInfo)
    3. self.job.addMetaData("Content-type", "Content-Type: application/x-www-form-urlencoded")
    4. self.job.addMetaData("Accept", "Accept: text/plain")
    5. self.job.addMetaData("User-Agent", "User-Agent: bwc-balance-plasmoid")
    6.  
    7. self.connect(self.job, SIGNAL("result(KJob*)"), self._get_result)


    Остается только описать функцию self._get_result():
    Copy Source | Copy HTML
    1. def self._get_result(self, job):
    2.     print job.data() # Просто сделаем что-нибудь с полученным ответом 


    Итог



    Был разобран процесс работы с настройками плазмоида, KWallet и сетевыми функциями KIO. Надеюсь, этого примера достаточно, чтобы в скором времени количество полезных плазмоидов для KDE Desktop заметно увеличилось. Горячей плазмы!

    Полный код плазмоида вы можете найти здесь.
    Поделиться публикацией
    Похожие публикации
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 20
    • 0
      Я как-то писал бинарный плазмоид — инетересно было посмотреть, как дело обстоит со скриптами. Спасибо.
      • 0
        впринципе все тоже самое) сравниваю этот псто и плазмоид, который я делал когда-то (тоже бумажник и сеть, только я ее делал через кутешные методы, а не через кдешные). И подобное единообразие не может не радовать.
        • 0
          Единообразие — это хорошо, да.
      • НЛО прилетело и опубликовало эту надпись здесь
        • 0
          О, да! Плазма девелопмент в связи с этим часто становится крайне «увлекательным» занятием. :)
          • НЛО прилетело и опубликовало эту надпись здесь
            • 0
              да, у меня она теперь обычно просто не запускается
        • 0
          О, настройки добавлю в пару своих, спасибо!
          • 0
            Спасибо, скоро попробую)
            • 0
              Полезная статейка и все на питоне, не думал :) как-то руки не доходили поковыряться.
              Вот только один минус в новых кедах с этим плазмоидом, что десктоп немного не юзабельный. папки и иконки в отдельном окне. Привык, что в разных углах рабочего стола находятся разные по важности папки, а тут не получится так сделать. Только если накидать этих виджетов… Надо будет посмотреть на новые версии кде :), пользовался старыми т.к. новые мне не очень пришлись по душе в плане юзабилити.

              Хорошая работа, автор!
              • 0
                Начиная с KDE 4.2 можно этот виджет растянуть на весь стол.
                • +1
                  что не получится сделать? поверьте, десктоп очень даже юзабельный и удобный. я вполне себе раскидал по разным частям разные по логике папки. вот только они у меня точно не перемешаются при случайном нажатии «упорядочить значки по». и если не влезут, то все-равно будут сгруппированы и с ненавязчивым скроллом.
                  Free Image Hosting at FunkyIMG.com
                  • 0
                    7 лет эволюции:

                    image

                    P.S да, я не любитель виджетов image
                • 0
                  извиняюсь, за оффтоп, но что использовать для подобного класса виджетов в Гноме или вообще в ДЕ независимой среде?
                  • 0
                    Для Gnome это Gnome Applets, и если мы говорим о Python, то вам понадобятся pygtk и gnomeapplet
                    • 0
                      а апплеты живут где-нибудь кроме панелей?
                • 0
                  Огромная просьба: добавьте в ваш плазмоид ещё 2 поля настройки: УРЛ и регулярное выражение. Выложите его куда-нибудь.

                  И вы решите проблему проверки баланса у всех пользователей KDE.

                  Спасибо!

                  P.S. Не забудьте дать ссылку мне :-)
                  • 0
                    Я думаю периодичность обновления (в минутах) тоже можно загнать в настройки.

                    Ещё полезно будет окошко в настройках с названием оператора: теоретически можно разместить несколько плазмоидов с разными операторами на рабочем столе, да и для хранения данных (чтобы ничего не путалось) возможно это будет полезно.

                    Если последнее слишком сложно, то лично меня устроит версия и с одним оператором.

                    Пожалуйста, сделайте свой плазмоид универсальным и выложите!

                    Люди отблагодарят. Как минимум добрым словом. А некоторые (я например) и материально.
                    • 0
                      размышляя я пришёл к тому, что в настройки нужно добавлять ещё и названия полей.

                      Итого оптимальный вариант полей в настроек:
                      — УРЛ (то что написано в action у формы)
                      — регулярное выражение
                      — название поля с логином в форме
                      — логин
                      — название поля с паролем
                      — пароль
                      — время обновления

                      теоретически можно сократить на 3 поля, если все настройки логина и пароля вынести в одно поле так:
                      login=vasilii&password=secretpupkin

                      при последнем варианте мы можем дописать какие-либо ещё параметры, которые могут понадобиться для успешного логина.

                      в принципе, чтобы защититься от злых провайдеров хорошо бы ещё поле с идентификатором браузера, а то если он будет фиксированный, то они просто его заблокируют вход этому плазмоиду раз и навсегда…

                      Такой плазмоид мог бы помочь людям смотреть свой баланс в тысячах сервисов, а не только мобильных операторов: провайдеров, посредников и т.п…

                      Последний вариант полей настроек:
                      — УРЛ (то что написано в action у формы)
                      — регулярное выражение
                      — поле в котором все необходимые значения полей для логина типа login=vasilii&password=secretpupkin
                      — регулярность обновления
                      — идентификатор браузера

                      Всего на 3 поля больше, чем в описанном вами плазмоиде, но это делает продукт ценным для практически всех линуксоидов!

                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.