Вступление
Это I часть цикла статей, посвященных написанию плагинов для XBMC с собственным интерфейсом. В ней будет рассмотрена основная информация о создании интерфейса плагина и приведен простейший пример.
Во II части я планирую дать еще немного теории и чуть более сложный пример.
В III части будет дан пример сложного плагина, который использует написанный мною микро-фреймоворк, упрощающий компоновку интерфейса.
В своей первой статье «Подробная анатомия простого плагина для XBMC» я попытался максимально подробно описать структуру плагина для XBMC. Кроме того, я постарался рассказать, чем отличаются плагина-источники контента и плагины скрипты. Для тех, кто забыл, напомню, что плагины-источники контента формируют многоуровневые списки (каталоги) контента при помощи функции addDirectoryItem(). Т. е. мы последовательно «скармливаем» функции элементы списка, а всем остальным — отображением списка, обработкой событий при выборе элементов списка и т. п. — занимается XBMC. В свою очередь, плагины-скрипты лишены такой привилегии, и в них все упомянутые задачи ложатся на разработчика. Также напомню, что, как и плагины-источники, скрипты могут отображаться в разделах «Видео», «Музыка» и/или «Фото», если они выполняют некие задачи по работе с указанным контентом, либо же в разделе «Программы», если это плагины-скрипты общего назначения. Кроме того, плагины-скрипты также могут скачивать субтитры, работать как фоновые службы и т. п. Функция плагина определяется тэгами в файле Addon.xml, содержащем всю информацию о нашем плагине.
К сожалению, никаких гениальных идей меня не посетило, поэтому примеры в этих статьях носят несколько абстрактно-теоретический характер. Применение этой информации для написания чего-нибудь полезного я оставляю вам.
Для понимания этих статей весьма желательно хорошее знание принципов ООП и написания GUI-приложений на языке Python (далее — Питон).
Общая информация
За работу с интерфейсом отвечает модуль xbmcgui, входящий в XBMC Python API. Этот модуль содержит общие классы-контейнеры, а также классы, отвечающие за разные элементы интерфейса. К сожалению, часть API, отвечающая за интерфейс менее удобна, чем традиционные питоновские библиотеки GUI, и, к тому же, документация изобилует неточностями. Я рекомендую пользоваться модулями-заглушками, о которых я писал в своей предыдущей статье. Кроме удобства разработки, они также содержат более актуальную информацию: как по набору классов, методов и функций, так и по их использованию (в докстрингах).
Компоненты интерфейса плагина
Для создания интерфейса плагина модуль xbmcgui предоставляет нам различные компоненты:
— классы-контейнеры (класс Window и его наследники);
— виджеты, или в терминологии XBMC контролы (controls), отвечающие за разные элементы интерфейса (классы-наследники Control);
— диалоги, служащие для вывода информации и взаимодействия с пользователем (методы класса Dialog и другие).
Рассмотрим первые 2 пункта подробнее.
Классы WindowXML и WindowXMLDialog
Как следует из названий, эти классы-контейнеры используют скелет интерфейса, написанный на XML и имеющий структуру, аналогичную структуре скинов XBMC. Т. е. по сути интерфейс на базе этих классов имеет свой собственный мини-скин. Углубляться в дебри скинописательства (в котором я, признаться, разбираюсь слабо) я не хочу, поэтому останавливаться подробно на этих классах я не буду.
Классы Window и WindowDialog
Классы Window и WindowDialog представляют собой родительские контейнеры, в которых размещаются другие элементы интерфейса — контролы. Для размещения контролов эти классы предоставляют координатную сетку, начинающуюся от левого верхнего угла экрана. По умолчанию разрешение видимой области координатной сетки равно 1280 х 720 пикселей. Это разрешение можно менять, но лично я не вижу в этом особого смысла, поэтому в примерах будет использоваться стандартное разрешение видимой области. Я не зря употребил термин «видимая область». Координаты контролов могут иметь любые значения, в т. ч. и отрицательные, но на экране будет видно только то, что попадает в видимую область. Это можно использовать, к примеру, чтобы временно скрыть какой-то контрол, задав для него координаты, заведомо выходящие за видимую область (например, -5000, -5000).
Внимание: не путайте разрешение видимой области координатной сетки и разрешение экрана. Видимая область имеет одинаковое разрешение при любом фактическом разрешении экрана, чтобы элементы интерфейса всегда имели одинаковый масштаб независимо от фактического разрешения.
Отличие между классами Window и WindowDialog заключается в том, что класс Window имеет черный непрозрачный фон и может прятаться под окном воспроизведения видео или музыкальной визуализации. В свою очередь, WindowDialog имеет прозрачный фон, и его контролы всегда отображаются поверх других элементов интерфейса XBMC. Таким образом, если ваш плагин будет воспроизводить видео и проигрывать музыку, то для него лучше использовать класс Window, а в остальных случаях выбор класса зависит от личных предпочтений и задач плагина.
Для обработки событий интерфейса оба класса имеют методы onAction() и onControl(). Первый перехватывает клавиатурные команды, а второй — выбранные контролы. Таким образом, чтобы обеспечить взаимодействие с пользователем, эти методы необходимо определить в дочернем классе, реализующем интерфейс вашего плагина, прописав в них соответствующие обработчики событий.
Внимание! Оба метода являются абстрактными и по умолчанию не содержат никаких встроенных обработчиков событий. Поэтому дочернем классе нужно обязательно прописать как минимум команду (команды) выхода из плагина. Иначе выйти из плагина можно будет, только принудительно остановив («убив») XBMC, что может представлять собой проблему, скажем, во встроенных системах (OpenELEC и др.).
Контролы
Теперь познакомимся подробнее с контролами. Я расскажу не о всех контролах, а только о тех, которые изучил лично, но, думаю, их хватит для большинства задач. Контролы представляют собой элементы интерфейса (виджеты) плагина. Однако в отличие от виджетов в GUI-фреймворках общего назначения (Tkinter, PyQt), контролы представляют собой своеобразные «скелеты» элементов интерфейса. Для визуального оформления контролов нужны графические файлы с картинками (текстурами).
Те, кто хорошо владеет фотожабой графическими программами, могут нарисовать текстуры самому, а остальные плагинописатели могут взять нужные файлы текстур в ресурсах скинов XBMC. К сожалению, в готовых скинах текстуры запакованы в специальном формате, поэтому нам понадобятся исходники скина. Исходники стандартного скина XBMC, Confluence, включая картинки, можно найти здесь.
Примечание: некоторые контролы (например, кнопка) могут автоматически использовать нужные текстуры текущего скина, но полагаться на такое поведение нельзя. Во-первых, это умеют не все контролы, и, во-вторых, при переключении на другой скин может нарушиться внешний вид плагина. Поэтому файлы текстур для контролов лучше задавать явно.
Как можно догадаться из написанного выше, при создании плагинов с собственным интерфейсом можно использовать 2 подхода: простой, при котором внешний вид плагина будет иметь одинаковый вид в любом скине, и сложный, при котором файлы текстур для виджетов будут выбираться в зависимости от текущего скина, чтобы интерфейс плагина был оформлен в том же стиле, что и скин. Понятное дело, что нельзя объять необъятное, поэтому при втором подходе придется ограничиться несколькими популярными скинами, а для остальных использовать оформление по умолчанию.
Далее краткое описание основных контролов интерфейса.
ControlLabel
Простая надпись с прозрачным фоном. Текстуры не использует. Это полный аналог классов Label в Tkinter или QLabel в PyQt. Выравнивание текста задается одной из следующих числовых констант:
ALIGN_LEFT = 0 ALIGN_RIGHT = 1 ALIGN_CENTER_X = 2 ALIGN_CENTER_Y = 4 ALIGN_CENTER = 6 ALIGN_TRUNCATED = 8 ALIGN_JUSTIFY = 10
На всякий случай напомню, что в реальном плагине текстовые строки для этого и других контролов, отображающих текстовую информацию, лучше получать из языковых файлов при помощи метода Addon.getLocalizedString(), чтобы интерфейс вашего плагина отвечал текущим языковым настройкам XBMC.
ControlFadeLabel
Аналог ControlLabel с той лишь разницей, что слишком длинная строка автоматически прокручивается.
ControlTextBox
Тестовое окно, которое может содержать длинный текст с автоматическим переносом строк. Прокрутки нет, и текст, который не поместился в текстовое окно, обрезается.
ControlImage
Картинка. Поддерживаются основные форматы файлов изображений (jpg, png, gif). В файлах png и gif поддерживается прозрачность (если есть). По умолчанию картинка вписывается в отведенное место с искажением пропорций, но способ вписывания можно задавать.
ControlButton
Кнопка. Обработчик нажатия на кнопку прописывается в методе onControl() класса-контейнера.
ControlRadioButton и ControlCheckMark
Радиокнопка и флажок («галочка»). Функционально полностью аналогичны и отличаются только внешним видом. Используются в качестве переключателя с двумя состояниями.
ControlEdit
Поле для ввода текста. При выборе открывается экранная клавиатура, позволяющая ввести нужный текст. Если указать параметр isPassword=True, вводимый текст будет заменяться звездочками.
ControlList
Список. В список можно добавлять простые текстовые строки, но на самом деле каждый элемент списка представляет собой экземпляр класса ListItem. Напомню, что из таких же элементов состоят списки в плагинах-источниках контента. Однако, в отличие от плагинов-источников, в ControlList функциональность элементов списка ограничена. Фактически поддерживаются только текстовые подписи и маленькие иконки (thumbnailImage). Всю остальную логику, например действие при выборе элемента списка, нужно реализовывать самому. Контрол требует текстуры для оформления списка.
Если все элементы списка не помещаются в отведенное место, список прокручивается.
ControlSlider
Ползунок со шкалой. Служит для плавной регулировки какого-либо параметра. Требует явного указания текстур.
Примеры контролов, оформленных с использованием текстур скина Confluence, можно увидеть на скриншоте ниже.
ControlButton, ControlCheckMark, ControlEdit, ControlList и ControlSlider представляют собой интерактивные элементы, которые меняют свой вид при их выборе. Теоретически выбрать можно любой контрол, например, ControlLabel, но при этом не будет никакой визуальной обратной связи.
Для навигации по интерфейсу плагина при помощи клавиш со стрелками на клавиатуре или пульте ДУ каждому контролу нужно назначить соседей, на которых будет перемещаться фокус при нажатии соответствующей клавиши со стрелкой. Для этого служат методы setNavigation, а также controlUp, controlDown, controlLeft и controlRight, унаследованные от Control.
Кроме того, чтобы навигация работала, при начальном отображении интерфейса не забываем установить фокус на один из контролов.
Контролы добавляются при помощи метода addControl класса-контейнера. При этом контролы, добавленные позже, отображаются поверх тех, что добавлены ранее. Например, мы можем сначала вывести на экран картинку, которая будет служить в качестве фона, а все остальные контролы размещать поверх нее.
Теперь перейдем к практическим примерам. Начнем, конечно же, с классического «Hello, World!», или в русском варианте «Привет, мир!».
Плагин «Привет, мир!»
Как я уже говорил в своей первой статье, для каждого плагина нужен обязательный служебный файл addon.xml.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <addon id="script.test" name="Test script" version="0.0.1" provider-name="Roman_V_M"> <requires> <import addon="xbmc.python" version="2.0"/> </requires> <extension point="xbmc.python.script" library="default.py"> <provides>executable</provides> </extension> <extension point="xbmc.addon.metadata"> <platform>all</platform> <summary lang="en">Test script</summary> <description lang="en">My test script.</description> </extension> </addon>
Строки
<extension point="xbmc.python.script" library="default.py"> <provides>executable</provides> </extension>
говорят нам (и XBMC) о том, что это программный плагин-скрипт, который будет доступен в разделе «Программы».
Теперь непосредственно код нашего плагина:
# -*- coding: utf-8 -*- # Licence: GPL v.3 http://www.gnu.org/licenses/gpl.html # Импортируем нужный модуль import xbmcgui # Коды клавиатурных действий ACTION_PREVIOUS_MENU = 10 # По умолчанию - ESC ACTION_NAV_BACK = 92 # По умолчанию - Backspace # Главный класс-контейнер class MyAddon(xbmcgui.Window): def __init__(self): # Создаем текстовую подпись. label = xbmcgui.ControlLabel(550, 300, 200, 50, u'Привет, мир!') # Добавляем подпись в контейнер self.addControl(label) def onAction(self, action): # Если нажали ESC или Backspace... if action == ACTION_NAV_BACK or action == ACTION_PREVIOUS_MENU: # ...закрываем плагин. self.close() if __name__ == '__main__': # Создаем экземпляр класса-контейнера. addon = MyAddon() # Выводим контейнер на экран. addon.doModal() # По завершении удаляем экземпляр. del addon
Далее краткий разбор, поскольку основные моменты уже указаны в комментариях к коду. Для отображения номеров строк используйте текстовый редактор с соответствующей функцией, например Notepad++.
Строки 8, 9: здесь мы задаем константы, соответствующие числовым кодам клавиатурных событий, используемых XBMC. Полные перечень клавиатурных событий можно найти в исходниках XBMC. Соответствие между клавиатурными событиями и клавишами клавиатуры, пульта ДУ или другого органа управления задается в конфигурационном файле keyboard.xml
13—25: описываем класс, реализующий наш интерфейс. В данном случае класс наследует от Window. Наш интерфейс содержит единственный контрол — ControlLabel с соответствующей надписью. Обратите внимание, что первые 4 параметра контрола — его координаты и размеры по ширине и высоте в пикселях координатной сетки. Эти параметры являются обязательными. Координаты и размеры можно впоследствии менять при необходимости. Если мы хотим создать контрол, но отложить его вывод на экран, то можно указать фиктивные координаты и/или размеры, а реальные параметры задавать после отображения контрола.
За добавление контролов в контейнер отвечает метод addControl() родительского класса.
В методе onAction мы перехватываем клавиатурные события, соответствующие клавишам ESC и Backspace, чтобы иметь возможность выйти из нашего плагина. Еще раз повторюсь, плагин-скрипт, использующий классы Window или WindowDialog, должен обязательно иметь код, позволяющий выйти из него, не прибегая к радикальным мерам, вроде диспетчера задач.
Примечание: несмотря на сравнение с целыми числами, параметр action на самом деле представляет собой экземпляр служебного класса Action.
32: метод doModal() выводит созданный нами интерфейс на экран. Этот метод аналогичен методам mainloop() в Tkinter.Tk или show() в QWidget. Интерфейс будет отображаться до тех пор, пока не будет вызван метод close().
34: зачем удалять экземпляр класса по завершении работы я, честно говоря, не знаю. Это я увидел в примере в официальной WiKi.
Если всё сделано правильно, мы должны увидеть
Согласитесь, не очень привлекательное зрелище. Как сказано выше, класс Window имеет черный непрозрачный фон. Поэтому в следующем примере мы попытаемся немного украсить наш интерфейс, а также добавить некоторые интерактивные элементы.
Ради любопытства попробуйте наследовать от WindowDialog. При таком варианте надпись появится поверх интерфейса XBMC.
Готовый плагин «Привет, мир!» можно скачать отсюда. Напомню, что установить плагин можно из меню «Система» > «Дополнения» > «Установить из файла ZIP». Все новые плагины устанавливаются в папку \addons папки с пользовательскими настройками. В Windows это, как правило, %AppData%\XBMC, в Linux — $HOME/.xbmc.
Заключение
В I части статьи, посвященной написанию плагинов для XBMC с собственным интерфейсом, были рассмотрены основные элементы интерфейса плагина, а также дан простейший пример.
В следующей части я планирую рассказать о диалогах, а также привести чуть более сложный пример, демонстрирующий использование картинок для украшения интерфейса, а также создание интерактивных элементов.
Те, кто хочет забежать немного вперед, могут скачать этот плагин, демонстрирующий описанные выше контролы. Именно с него сделан скриншот с примерами контролов выше. Плагин использует написанный мною микро-фрейморк, о котором я планирую рассказать в III части, но опытные питонщики, думаю, без труда во всем разберутся.
ссылка на оригинал статьи http://habrahabr.ru/post/193704/
Добавить комментарий