Release - PyXBMCt: a Python framework for simple creating UI for XBMC addons

  Thread Rating:
  • 1 Votes - 5 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Post Reply
Roman_V_M Offline
Fan
Posts: 510
Joined: Jun 2011
Reputation: 13
Location: Kyiv, Ukraine
Post: #1
I'm glad to present you PyXBMCt — a framework which simplifies creating arbitrary UIs for XBMC 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 XBMC skinning, so it's a good choice for beginners.

Introduction
PyXBMCt is a Python framework for simple 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 XBMC terms, controls, a Grid layout manager and an event connection manager.
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: 0nd9.jpg]

Grid layout manager
The Grid layout manager 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 manager 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.

Connection manager
Connection manager is similar to the signal-slot connection mechanism of Qt framework. It allows you to 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 connection manager 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 (important!). 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
There are 2 ways how to use PyXBMCt in your addon: PyXBMCt as an XBMC Python module and a “local” version. With both ways in order to import PyXBMCt classes and constants into your addon, add the following statement to your addon code:
PHP Code:
from pyxbmct.addonwindow import 
To use PyXBMCt as an XBMC Python module you need to install it into your XBMC addon folder. A link to PyXBMCt module addon in ZIP file is provided below.
If you want to create a standalone addon without an external dependency, as an alternative, you can include a “local” version of PyXBMCt in your addon. To use a “local” version, copy /pyxbmct sub-folder from /local_version folder on Github repository to your addon folder. Your code that uses PyXBMCt must be in the top-level folder of your addon, along with /pyxbmct folder.

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

# Create a window instance.
window AddonDialogWindow('Hello, World!')
# Set the window width, height and the grid resolution: 2 rows, 3 columns.
window.setGeometry(35015023)
# Create a text label.
label Label('This is a PyXBMCt window.'alignment=ALIGN_CENTER)
# Place the label on the window grid.
window.placeControl(label00columnspan=3)
# Create a button.
button 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(ACTION_NAV_BACKwindow.close)
# Show the created window.
window.doModal() 

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

class 
MyWindow(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 Label('This is a PyXBMCt window.'alignment=ALIGN_CENTER)
        
# Place the label on the window grid.
        
self.placeControl(label00columnspan=3)
        
# Create a button.
        
button 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(ACTION_NAV_BACKself.close)


# Create a window instance.
window MyWindow('Hello, World!')
# Show the created window.
window.doModal() 

The result:

[Image: gn9n.jpg]

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 repository on Github: https://github.com/romanvm/PyXBMCt
PyXBMCt QuickStart Guide: https://dl.dropboxusercontent.com/u/2435...kStart.pdf
Auto-generated developer's documentation: http://romanvm.github.io/PyXBMCt/docs
PyXBMCt framework as a Python module addon: https://dl.dropboxusercontent.com/u/2435...-1.1.1.zip
A demo addon with a comprehensive example of usage for all PyXBMCt controls: https://dl.dropboxusercontent.com/u/2435...-0.0.8.zip

Your comments, suggestions and questions are welcomed.Smile

AMD A4-3300 + AsRock A75M-ITX
XBMC-DSplayer 12.3 (custom build) on Windows 7
(This post was last modified: 2014-04-10 15:47 by Roman_V_M.)
find quote
jerimiah797 Offline
Member
Posts: 57
Joined: Mar 2011
Reputation: 2
Post: #2
This is very cool, Roman. Nice work!

-Jerimiah
find quote
jmarshall Offline
Team-XBMC Developer
Posts: 25,689
Joined: Oct 2003
Reputation: 169
Post: #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: badge.gif]
find quote
Roman_V_M Offline
Fan
Posts: 510
Joined: Jun 2011
Reputation: 13
Location: Kyiv, Ukraine
Post: #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.

AMD A4-3300 + AsRock A75M-ITX
XBMC-DSplayer 12.3 (custom build) on Windows 7
find quote
nickr Online
Donor
Posts: 6,241
Joined: May 2009
Reputation: 103
Location: Christchurch NZ
Post: #5
Looks very interesting, many thanks.

If I have helped you or increased your knowledge, click the plus button and increase my reputation :)
find quote
divingmule Offline
Skilled Python Coder
Posts: 1,297
Joined: Oct 2008
Reputation: 61
Post: #6
Thanks for this, it's awesome!
find quote
divingmule Offline
Skilled Python Coder
Posts: 1,297
Joined: Oct 2008
Reputation: 61
Post: #7
Okay... first question Blush

How to capture onFocus eventsConfused
find quote
Roman_V_M Offline
Fan
Posts: 510
Joined: Jun 2011
Reputation: 13
Location: Kyiv, Ukraine
Post: #8
(2013-10-07 14:04)divingmule Wrote:  Okay... first question Blush

How to capture onFocus eventsConfused

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.

AMD A4-3300 + AsRock A75M-ITX
XBMC-DSplayer 12.3 (custom build) on Windows 7
(This post was last modified: 2013-10-07 14:27 by Roman_V_M.)
find quote
divingmule Offline
Skilled Python Coder
Posts: 1,297
Joined: Oct 2008
Reputation: 61
Post: #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.
find quote
Roman_V_M Offline
Fan
Posts: 510
Joined: Jun 2011
Reputation: 13
Location: Kyiv, Ukraine
Post: #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.

AMD A4-3300 + AsRock A75M-ITX
XBMC-DSplayer 12.3 (custom build) on Windows 7
(This post was last modified: 2013-10-07 15:26 by Roman_V_M.)
find quote
divingmule Offline
Skilled Python Coder
Posts: 1,297
Joined: Oct 2008
Reputation: 61
Post: #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!
(This post was last modified: 2013-10-07 16:28 by divingmule.)
find quote
Roman_V_M Offline
Fan
Posts: 510
Joined: Jun 2011
Reputation: 13
Location: Kyiv, Ukraine
Post: #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.

AMD A4-3300 + AsRock A75M-ITX
XBMC-DSplayer 12.3 (custom build) on Windows 7
find quote
Roman_V_M Offline
Fan
Posts: 510
Joined: Jun 2011
Reputation: 13
Location: Kyiv, Ukraine
Post: #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.

AMD A4-3300 + AsRock A75M-ITX
XBMC-DSplayer 12.3 (custom build) on Windows 7
find quote
divingmule Offline
Skilled Python Coder
Posts: 1,297
Joined: Oct 2008
Reputation: 61
Post: #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.
find quote
Roman_V_M Offline
Fan
Posts: 510
Joined: Jun 2011
Reputation: 13
Location: Kyiv, Ukraine
Post: #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.

AMD A4-3300 + AsRock A75M-ITX
XBMC-DSplayer 12.3 (custom build) on Windows 7
find quote
Post Reply