• 1(current)
  • 2
  • 3
  • 4
  • 5
  • 27
Release PyXBMCt: a Python framework for simple creating UI for XBMC addons
#1
I'm glad to present you PyXBMCt — a framework which simplifies creating arbitrary UIs for Kodi addons. UIs based on PyXBMCt may not be so fancy as skinned UIs based on WindowXML, but, in my opinion, it is relatively easy to learn, especially for those who are familiar with general-purpose Python GUI frameworks, like Tkinter, PyQt, PySide etc. And it does not require the knowledge of Kodi skinning, so it's a good choice for beginners.

Introduction
PyXBMCt is a Python framework for simple Kodi (formerly XBMC) addon UI building. It was inspired by PyQt (hence the name) and shares the same basic principles, so those who are familiar with PyQt/PySide should feel themselves right at home. The framework provides 4 base classes, 9 ready-to-use widgets or, in Kodi terms, Controls, a Grid Layout and the event connection function.
PyXBMCt uses texture images from XBMC’s default Confluence skin to decorate its visual elements. Those textures are included in PyXBMCt, so UI based on it will have the same look in different skins.

Base Classes
PyXBMCt provides 4 base classes: AddonDialogWindow, BlankDialogWindow (both based on xbmcgui.WindowDialog), AddonFullWindow and BlankFullWindow (both based on xbmcgui.Window). These classes serve as containers for other UI elements (controls). All base classes are “new-style” Python classes.

AddonDialogWindow class is based on xbmcgui.WindowDialog and provides a parent interface window with a background and a title-bar. The window is always displayed on top of XBMC UI.
AddonFullWinow is based on xbcmgui.Window class. It is similar to AddonDialogWindow and also provides a parent window for other UI controls. But, unlike AddonDialogWindow, it has a solid main background, which covers XBMC UI completely, and can hide under video or music visualization. The default Confluence background is used as the standard background for AddonFullWinow, but it can be changed to any image.
BlankDialogWindow and BlankFullWindow are based on xbmcgui.WindowDialog and xbmcgui.Window respectively. They have no visual elements whatsoever and are meant for DIY developers who want full control over the visual appearance of their addons.

Controls
PyXBMCt provides 9 ready-to-use UI controls — Label, FadeLabel, TextBox, Image, Button, RadioButton, Edit, List and Slider — that are based on the respective xbmcgui controls with the following differences:
- You don’t need to specify coordinates and size for the controls explicitly. The Grid layout manager takes care of control placement.
- All controls that require textures are provided with default textures (borrowed from Confluence skin resources). You can specify your own textures for PyXBMCt controls, but you need to do this through keyword arguments (important!).
- Button caption is center-aligned by default. You can change button caption alignment by providing a necessary alignment parameter through a keyword argument (PyXBMCt already includes symbolic constants for control text alignment).

A screenshot with all PyXBMCt controls:

Image

Grid Layout
The Grid Layout helps to place UI controls within the parent window. It is similar to PyQt’s QGridLayout or Tkniter’s Grid geometry manager. The Grid Layout is implemented through setGeometry and placeControl methods of a base PyXBMCt class. To place a control you simply provide it as the 1st positional argument to placeControl then specify a row and a column for the control as the next arguments, and the control will be placed in a specific grid cell.
Note that row and column numbers start from zero, i.e. the top-left cell will have row# = 0, column# = 0.
Warning: currently PyXBMCt does not support changing window geometry at runtime so you must call setGeometry method only once.

Connecting Events
Event connecting works similarly to the signal-slot connection mechanism of Qt framework. You can connect an event — a control or a key action code — to a function or a method to be called when the respective control is activated or when a key is pressed, thus invoking the key action bound to it. The event connecting function is implemented through connect method of a base PyXBMCt class.
You can only connect the following controls: Button, RadioButton and List. Other controls do not generate any UI events, so connecting them won’t have any effect.
Note that for connection you must provide a function object without brackets (), not a function call!!!. Similarly to PyQt’s signal-slot connection, lambda can be used to connect a function/method with arguments known at runtime.
The key code ACTION_PREVIOUS_MENU or 10 (bound to ESC key by default) is already connected to the method which closes a current addon window (close), so you cannot connect it to any function/method. It guarantees that you can always close an active addon window without killing XBMC.

How to use PyXBMCt
To import PyXBMCt classes and constants into your addon, add the following statement to your addon code:
PHP Code:
import pyxbmct 
PyXBMCt Python module is included in the official XBMC Gotham repo, so to use it you need to add the following line into the 'requires' section of your addon.xml:
Code:
<requires>
...
<import addon="script.module.pyxbmct" version="1.1.4"/>
...
</requires>
Then when your addon is installed, PyXBMCt module should also be installed automatically from XBMC official repo.

Note: Since PyXBMCt is included in the official Kodi repo, the local version is depreciated.

Now a simple example without using OOP:
PHP Code:
# Import PyXBMCt module.
import pyxbmct

# Create a window instance.
window pyxbmct.AddonDialogWindow('Hello, World!')
# Set the window width, height and the grid resolution: 2 rows, 3 columns.
window.setGeometry(35015023)
# Create a text label.
label pyxbmct.Label('This is a PyXBMCt window.'alignment=pyxbmct.ALIGN_CENTER)
# Place the label on the window grid.
window.placeControl(label00columnspan=3)
# Create a button.
button pyxbmct.Button('Close')
# Place the button on the window grid.
window.placeControl(button11)
# Set initial focus on the button.
window.setFocus(button)
# Connect the button to a function.
window.connect(buttonwindow.close)
# Connect a key action to a function.
window.connect(pyxbmct.ACTION_NAV_BACKwindow.close)
# Show the created window.
window.doModal()
# Delete the window instance when it is no longer used.
del window 

The same example in OOP fashion:
PHP Code:
# Import PyXBMCt module.
import pyxbmct


class MyWindow(pyxbmct.AddonDialogWindow):

    
def __init__(selftitle=''):
        
# You need to call base class' constructor.
        
super(MyWindowself).__init__(title)
        
# Set the window width, height and the grid resolution: 2 rows, 3 columns.
        
self.setGeometry(35015023)
        
# Create a text label.
        
label pyxbmct.Label('This is a PyXBMCt window.'alignment=pyxbmct.ALIGN_CENTER)
        
# Place the label on the window grid.
        
self.placeControl(label00columnspan=3)
        
# Create a button.
        
button pyxbmct.Button('Close')
        
# Place the button on the window grid.
        
self.placeControl(button11)
        
# Set initial focus on the button.
        
self.setFocus(button)
        
# Connect the button to a function.
        
self.connect(buttonself.close)
        
# Connect a key action to a function.
        
self.connect(pyxbmct.ACTION_NAV_BACKself.close)


# Create a window instance.
window MyWindow('Hello, World!')
# Show the created window.
window.doModal()
# Delete the window instance when it is no longer used.
del window 

The result:

Image

By default a window is placed at the center of the screen, unless you provide explicit coordinates of the top-left corner to setGeometry method. Note that window width, height and coordinates are specified in XBMC UI coordinate grid pixels. By default, the resolution of the XBMC UI coordinate grid is 1280x720 regardless of you actual display resolution and aspect.

Links

PyXBMCt developer's documentation: http://romanvm.github.io/script.module.pyxbmct
PyXBMCt module repository on Github: https://github.com/romanvm/script.module.pyxbmct
A demo addon with a comprehensive usage example of all PyXBMCt controls: https://github.com/romanvm/pyxbmct.demo

The example of a multi-select dialog based on PyXBMCt.

Your comments, suggestions and questions are welcomed.Smile
Reply
#2
This is very cool, Roman. Nice work!

-Jerimiah
Reply
#3
Indeed - very nice.
Always read the XBMC online-manual, FAQ and search the forum before posting.
Do not e-mail XBMC-Team members directly asking for support. Read/follow the forum rules.
For troubleshooting and bug reporting please make sure you read this first.


Image
Reply
#4
Thanks guys.Smile

I've also added a "local" version to the repository for those who would want to try PyXBMCt in their addons without having to install extra module addon as a dependency.
Reply
#5
Looks very interesting, many thanks.
If I have helped you or increased your knowledge, click the 'thumbs up' button to give thanks :) (People with less than 20 posts won't see the "thumbs up" button.)
Reply
#6
Thanks for this, it's awesome!
Reply
#7
Okay... first question Blush

How to capture onFocus eventsHuh
Reply
#8
(2013-10-07, 14:04)divingmule Wrote: Okay... first question Blush

How to capture onFocus eventsHuh

First, since PyXBMCt classes inherit either from Window or from WindowDialog, all their methods (including onFocus) should work in child classes, though I strongly do not recommend re-implementing onAction and onControl, because it would break the connection manager which handles onAction and onControl events.

Second, I'm not sure that onFocus method even work with Window and WindowDialog (and thus in their child classes too), and due to lack of documentation for this method I can only guess what is supposed to do.
I did quick tests when I was studying xbmcgui classes behavior, and got no results with onFocus. Probably it is not implemented in Window and WindowDialog, like onClick which also does not work, i.e. does not capture anything in these classes. Maybe it works only in WindowXML and WindowDialogXML, I don't know.

Could you explain more about onFocus? Particularly, what events is it supposed to capture, and what is a possible use case for you? Maybe there is a workaround to achieve your particular goal.
Reply
#9
I am trying to set a group of controls up to change when navigating a list, kinda like the media info view mode. Using your example with connecting key events works but if using mouse you can focus an item but not have it selected.

My limited experience with onClick, onFocus, onAction have always worked as documented, but I've always used controls defined in a window XML.
Reply
#10
(2013-10-07, 14:48)divingmule Wrote: I am trying to set a group of controls up to change when navigating a list, kinda like the media info view mode. Using your example with connecting key events works but if using mouse you can focus an item but not have it selected.

Actually, using a mouse does select a List item (do not confuse this with actual clicking on a List item which generates onControl event).
I've tested a similar use case a couple days ago. You need to connect the following action codes:
Code:
ACTION_MOVE_UP
ACTION_MOVE_DOWN
ACTION_MOUSE_WHEEL_UP
ACTION_MOUSE_WHEEL_DOWN
ACTION_MOUSE_MOVE
to a method that will check if your list is selected using getFocus() and will update your info controls accordingly. Look how it is done in the example addon which you can find in PyXBMCt Github repo:
PHP Code:
...
        
self.connect(ACTION_MOVE_DOWNself.list_update)
        
self.connect(ACTION_MOVE_UPself.list_update)
...
def list_update(self):
        
# Update list_item label when navigating through the list.
        
try:
            if 
self.getFocus() == self.list:
                
self.list_item_label.setLabel(self.list.getListItem(self.list.getSelectedPosition()).getLabel())
            else:
                
self.list_item_label.setLabel('')
        
except (RuntimeErrorSystemError):
            
pass
... 
It uses only ACTION_MOVE_UP and ACTION_MOVE_DOWN action codes, but the general principle is the same.
Maybe it is not as nice as using onFocus (if it actually worked) but it does the job.

I guess I need to add a method to connect a list of events to a function specifically for such cases (another case - updating a control which shows a slider value, as it's done in the example addon).
Also it seems that I need to update the example addon itself to show a full implementation of such use case.

Quote:My limited experience with onClick, onFocus, onAction have always worked as documented, but I've always used controls defined in a window XML.

Yes, but as I've said, with non-XML classes things are slightly different.
Reply
#11
Oh... Okay, ACTION_MOUSE Blush

FYI, mouse actions don't yet seem to be defined in PyXBMCt.

Code:
ACTION_MOUSE_START = 100
ACTION_MOUSE_LEFT_CLICK = 100
ACTION_MOUSE_RIGHT_CLICK = 101
ACTION_MOUSE_MIDDLE_CLICK = 102
ACTION_MOUSE_DOUBLE_CLICK = 103
ACTION_MOUSE_WHEEL_UP = 104
ACTION_MOUSE_WHEEL_DOWN = 105
ACTION_MOUSE_DRAG = 106
ACTION_MOUSE_MOVE = 107
ACTION_MOUSE_END = 109

Thanks!
Reply
#12
(2013-10-07, 15:44)divingmule Wrote: Oh... Okay, ACTION_MOUSE Blush

FYI, mouse actions don't yet seem to be defined in PyXBMCt.

I've included only some of mouse action codes which might be usable when working with Controls:
PHP Code:
## Mouse wheel up
ACTION_MOUSE_WHEEL_UP 104
## Mouse wheel down
ACTION_MOUSE_WHEEL_DOWN 105
## Mouse drag
ACTION_MOUSE_DRAG 106
## Mouse move
ACTION_MOUSE_MOVE 107 
The list of available action codes is quite extensive: https://github.com/xbmc/xbmc/blob/master...ilib/Key.h
and I see no point to include all of them because you can still use the corresponding numeric codes or define your own set of symbolic constants in your addon to suit your particular needs.
But If you think that some actions will be used more frequently than others I will consider including them in PyXBMCt as symbolic constants.
Reply
#13
I've added connectEventList and disconnectEventList methods to the framework and also updated the example addon to better demonstrate how to track List item focus change to update other control contents.

Also onFocus method does seem to work after all. The following code:
PHP Code:
def onFocus(selffocus):
        print 
'!!!TEST!!! onFocus:'focus 
does absolutely nothing in Window and WindowDialog child classes no matter what I do: move a mouse cursor, navigate between controls or click them.
Reply
#14
FWIW:

When defining controls in an XML you can assign the control attribute 'id' > http://wiki.xbmc.org/index.php?title=List_Container . This 'id' is what is returned with onClick, onFocus. There doesn't seem to be any way to set an 'id' in python.
Reply
#15
(2013-10-07, 23:42)divingmule Wrote: FWIW:

When defining controls in an XML you can assign the control attribute 'id' > http://wiki.xbmc.org/index.php?title=List_Container . This 'id' is what is returned with onClick, onFocus. There doesn't seem to be any way to set an 'id' in python.

Thanks for the info. Strangely enough, the base xbmcgui.Control class has getID method, but there is no setID or something like that.
Reply
  • 1(current)
  • 2
  • 3
  • 4
  • 5
  • 27

Logout Mark Read Team Forum Stats Members Help
PyXBMCt: a Python framework for simple creating UI for XBMC addons4