Introduction
This is part I of a series of articles on writing plug-ins for XBMC with its own interface. It will cover the basic information about creating a plug-in interface and provides a simple example.
In Part II, I plan to give a little more theory and a slightly more complex example.
In Part III, I will demonstrate a micro-framework written by me that simplifies the layout of the interface.
In my first article,
Detailed Anatomy of a Simple XBMC Plug-in , I tried to describe the structure of the XBMC plug-in in as much detail as possible. In addition, I tried to tell you the difference between plug-in content sources and plug-ins scripts.
For those who have forgotten, I remind you that content source plugins form multi-level lists (directories) of content using the addDirectoryItem () function. That is, we consistently “feed” functions to the elements of the list, and the rest — displaying the list, handling events when selecting elements of the list, and so on — is handled by XBMC. In turn, script plugins are deprived of this privilege, and in them all the mentioned tasks fall on the developer. I also remind you that, like source plugins, scripts can be displayed in the “Video”, “Music” and / or “Photo” sections, if they perform certain tasks for working with the specified content, or in the “Programs” section, if These are general purpose script plugins. In addition, script scripts can also download subtitles, work as background services, etc. The function of a plugin is defined by tags in the
Addon.xml file, which contains all the information about our plugin.
Unfortunately, no brilliant ideas came to me, so the examples in these articles are somewhat abstract theoretical. The use of this information to write something useful, I leave to you.
')
To understand these articles, it is highly desirable to have a good knowledge of OOP principles and to write GUI applications in Python (hereinafter referred to as Python).
general information
The
xbmcgui module, part of the XBMC Python API, is responsible for working with the interface. This module contains general container classes, as well as classes responsible for different interface elements. Unfortunately, the API part that is responsible for the interface is less convenient than the traditional Python GUI libraries, and, moreover, the documentation is replete with inaccuracies. I recommend using
the stub modules , which I wrote about in my previous article. In addition to the convenience of development, they also contain more up-to-date information: both on the set of classes, methods and functions, and on their use (in dostripes).
Plugin Interface Components
To create a plug-in interface, the xbmcgui module provides us with various components:
- container classes (Window class and its heirs);
- Widgets, or in the XBMC terminology, controls (controls), responsible for different interface elements (classes inherited from Control);
- dialogs that serve to display information and user interaction (methods of the class Dialog and others).
Consider the first 2 points in more detail.
The classes WindowXML and WindowXMLDialog
As the names imply, these container classes use an interface skeleton written in XML and having a structure similar to the XBMC skin structure. That is, in fact, the interface based on these classes has its own mini-skin. I don’t want to go deeper into the jungle of book writing (which, I confess, I understand little), so I’ll not dwell on these classes in detail.
Classes Window and WindowDialog
The classes Window and WindowDialog are the parent containers in which other interface elements are placed - controls. For placement of controls, these classes provide a coordinate grid starting from the upper left corner of the screen. By default, the resolution of the visible area of ​​the grid is 1280 x 720 pixels. This resolution can be changed, but personally I do not see much point in this, so the examples will use the standard resolution of the visible area. I knowingly used the term "visible area". The coordinates of the controls can have any values, including negative ones, but only what falls into the visible area will be visible on the screen. This can be used, for example, to temporarily hide some kind of control by setting coordinates for it that are obviously beyond the visible area (for example, -5000, -5000).
Attention: do not confuse the resolution of the visible area of ​​the grid and the screen resolution. The visible area has the same resolution at any actual screen resolution so that the interface elements always have the same scale regardless of the actual resolution.
The difference between the Window and WindowDialog classes is that the Window class has a black, opaque background and can hide under the video or music rendering window. In turn, WindowDialog has a transparent background, and its controls are always displayed on top of other elements of the XBMC interface. Thus, if your plug-in plays video and plays music, then it is better to use the Window class for it, and in other cases, the choice of class depends on personal preferences and tasks of the plug-in.
To handle interface events, both classes have onAction () and onControl () methods. The first intercepts keyboard commands, and the second - activated controls. Thus, in order to provide user interaction, these methods must be defined in the child class that implements your plugin's interface by writing the appropriate event handlers to them.
Attention! By default, the onAction method “catches” keyboard commands corresponding to the BACKSPACE and ESC keys, which call the close method (exit from the plugin). Therefore, if this method is redefined in a child class, it is
necessary to prescribe at least the exit command (s) from the plugin in this class. Otherwise, it will be possible to exit the plugin only by forcibly stopping (“killing”) XBMC, which may be a problem, for example, in embedded systems (OpenELEC, etc.).
Controls
Now let's get acquainted with the controls. I will not tell about all controls, but only about those that I studied personally, but I think they will be enough for most tasks. Controls are plug-in interface elements (widgets). However, unlike widgets in GUI frameworks for general use (Tkinter, PyQt), controls are a kind of "skeletons" of interface elements. For visual design kontrolov need graphic files with pictures (textures).
Those who are fluent in
photojob graphics programs, can draw the textures on their own, and other plug-ins can take the necessary texture files in the XBMC skin resources. Unfortunately, in ready-made skins, the textures are packed in a special format, so we need the sources of the skin. The sources of the standard XBMC skin, Confluence, including images, can be found
here .
Note: some controls (for example, a button) can automatically use the desired textures of the current skin, but you cannot rely on this behavior. Firstly, not all controls can do this, and, secondly, when switching to another skin, the appearance of the plug-in can be disrupted. Therefore, it is better to explicitly set texture files for controls.
As you can guess from the above, when creating plug-ins with your own interface, you can use 2 approaches: simple, in which the appearance of the plugin will have the same appearance in any skin, and complex, in which the texture files for widgets will be selected depending on the current skin, so that the interface of the plugin is designed in the same style as the skin. It is clear that you can not grasp the immensity, so the second approach will have to restrict a few popular skins, and for the rest to use the default design.
Further a brief description of the main interface controls.
Controllabel
Simple inscription with a transparent background. Texture does not use. This is a complete analogue of the Label classes in Tkinter or QLabel in PyQt. Text alignment is specified by one of the following numeric constants:
ALIGN_LEFT = 0 ALIGN_RIGHT = 1 ALIGN_CENTER_X = 2 ALIGN_CENTER_Y = 4 ALIGN_CENTER = 6 ALIGN_TRUNCATED = 8 ALIGN_JUSTIFY = 10
Just in case, let me remind you that in a real plugin, text strings for this and other controls displaying textual information should be obtained from language files using the Addon.getLocalizedString () method, so that your plugin's interface responds to the current XBMC language settings.
ControlFadeLabel
Analog ControlLabel with the only difference that a line that is too long automatically scrolls.
ControlTextBox
A test window that can contain long text with automatic line breaks. There is no scrolling, and text that does not fit in the text box is clipped.
ControlImage
Picture. Main image file formats are supported (jpg, png, gif). Transparency and animation (if any) are supported in the png and gif files. By default, the picture fits in the allotted space with distorted proportions, but the way of inscribing can be set. Unfortunately, the fit option does not work when the picture is subsequently changed.
Controlbutton
Button. The button handler is written in the onControl () method of the container class.
ControlRadioButton and ControlCheckMark
Radio button and checkbox (tick). Functionally completely similar and differ only in appearance. Used as a two-state switch. Requires texture.
ControlEdit
The text box. When selected, an on-screen keyboard opens allowing you to enter the desired text. If you specify the isPassword = True parameter, the entered text will be replaced with asterisks.
Controllist
List. You can add simple text strings to the list, but in fact each item in the list is an instance of the ListItem class. Let me remind you that lists of source content plugins consist of the same elements. However, unlike the source plugins, ControlList has limited list item functionality. In fact, only text captions and small icons (thumbnailImage) are supported. All the rest of the logic, such as the action when selecting an item in the list, must be implemented by yourself. The control requires a texture to decorate the list.
If all items in the list do not fit in the space provided, the list is scrolled.
ControlSlider
Slider with scale. It is used to smoothly adjust any parameter. Requires explicit textures.
Examples of controls designed using the Confluence skin textures can be seen in the screenshot below.
Basic plugin interface controls ControlButton, ControlRadioButton, ControlEdit, ControlList and ControlSlider are interactive elements that change their appearance when they are selected. Theoretically, you can choose any control, for example, ControlLabel, but there will be no visual feedback.
To navigate through the interface of the plug-in using the arrow keys on the keyboard or the remote control, each control must be assigned neighbors, in which the focus will move when the corresponding arrow key is pressed. To do this, use the setNavigation methods, as well as controlUp, controlDown, controlLeft and controlRight, inherited from Control.
In addition, in order for the navigation to work, when the interface is initially displayed, do not forget to set the focus to one of the controls.
Controls are added using the addControl method of the container class. At the same time, controls added later are displayed on top of those added earlier. For example, we can first display a picture that will serve as a background, and place all the other controls on top of it.
Attention: almost all controls have methods that allow you to change their properties (text, image, etc.) during the execution of the plugin. So, always change the property of the control (for example, the label ControlLabel with setLabel ('Some Text')
after this control was added to the container class using the addControl method. When you try to change the properties of the "unbound" control, at best, nothing will happen , and at worst all sorts of interesting XBMC interface glitches are possible.
We now turn to practical examples. We begin, of course, with the classic "Hello, World!", Or in the Russian version of "Hello, world!".
Plugin "Hello world!"
As I said in my first article, for each plug-in you need the required service file addon.xml.
Content 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>
Strings
<extension point="xbmc.python.script" library="default.py"> <provides>executable</provides> </extension>
they tell us (and XBMC) that this is a software plugin script that will be available in the “Programs” section.
Now directly the code of our plugin:
Further, a brief analysis, since the main points are already indicated in the comments to the code. To display line numbers, use a text editor with the appropriate function, such as Notepad ++.
Lines 8, 9: here we set the constants corresponding to the numeric codes of the keyboard events used by XBMC. Full list of keyboard events can be found in the
XBMC source . The correspondence between keyboard events and keyboard, remote control or other control buttons is set in the
keyboard.xml configuration file
13-25: describe the class that implements our interface. In this case, the class inherits from Window. Our interface contains a single control - ControlLabel with the appropriate caption. Note that the first 4 parameters of the control are its coordinates and dimensions in width and height in pixels of the coordinate grid. These parameters are required. Coordinates and dimensions can be subsequently changed if necessary. If we want to create a control, but postpone its output to the screen, then we can specify dummy coordinates and / or dimensions, and set the real parameters after the control is displayed.
The addControl () method of the parent class is responsible for adding controls to the container.
In the onAction method, we intercept keyboard events corresponding to the ESC and Backspace keys to be able to exit our plugin. Once again, if the onAction method is redefined in a child class, the plugin must have code that allows you to exit it without resorting to drastic measures, like a task manager.
Note: despite the comparison with integers, the action parameter is actually an instance of the Action service class.
32: The doModal () method displays the interface we created on the screen. This method is similar to the mainloop () methods in Tkinter.Tk or exec_ () in QDialog. The interface will be displayed until the close () method is called.
In addition to doModal (), you can use show (). In this case, the interface will be displayed while the plugin is running or until close () is called. If you use show (), the event loop organization falls entirely on the programmer.
34: Why delete an instance of the class at the end of work, I honestly do not know. I saw this in the example in the official WiKi.
If done correctly, we should see
Agree, not a very attractive sight. As mentioned above, the Window class has a black opaque background. Therefore, in the following example, we will try to slightly decorate our interface, as well as add some interactive elements.
For curiosity, try inheriting from WindowDialog. With this option, the label will appear on top of the XBMC interface.
The ready plugin “Hello world!” Can be downloaded
from here . Let me remind you that you can install the plugin from the menu "System"> "Add-ons"> "Install from ZIP file". All new plugins are installed in the folder \ addons folder with user settings. On Windows, this is usually% AppData% \ XBMC, on Linux it is $ HOME / .xbmc.
Conclusion
Part I of an article on writing plugins for XBMC with its own interface, discussed the main interface elements of the plug-in, and also gave the simplest example.
In the next part, I plan to talk about the dialogues, as well as give a slightly more complex example, demonstrating the use of pictures to decorate the interface, as well as the creation of interactive elements.
Those who want to run a little ahead can download
this plugin showing the controls described above. It was from him that a screenshot was made with examples of controls above. The plugin uses a micro-framework written by me, which I plan to talk about in Part III, but I think experienced python builders will figure it out without difficulty.
PS Made a few minor fixes and additions.
PPS Corrected information about the onClose method.
Continuation
We are writing a plugin for XBMC with its own interface: Part II - dialogues and embellishments .