HOW-TO:Write Python Scripts

From XBMC
Jump to: navigation, search
Home icon grey.png   ▶ Development ▶ Add-on development ▶ Python development ▶ HOW-TO:Write Python Scripts


Time.png OUTDATED:
Some information in this article or section has not been updated or refers to XBMC features that have been superseded, and may not be reliable.
Cleanup.png This page or section may require cleanup, updating, spellchecking, reformatting and/or updated images. Please improve this page if you can. The discussion page may contain suggestions.


XBMC features a Python Scripts Engine and WindowXML application framework (a XML-based widget toolkit for creating GUI window structure) in a similar fashion to Apple Mac OS X Dashboard Widgets and Microsoft Gadgets in Windows Sidebar. So normal users can add new functionality to XBMC themselves (using the easy to learn Python programming language) without an illegal copy of the XDK and without knowledge of the complex C/C++ programming language. Current plugin scripts include functions like Internet-TV and movie-trailer browsers, weather forecast and cinemaguides, TV-guides (EPG), e-mail clients, instant messaging, train-timetables, scripts to front-end control PVR software and hardware (like: MediaPortal, MythTV, TiVo, ReplayTV, Dreambox/DBox2), Internet-radio-station browsers (example SHOUTcast, Xm radio, Sirius Satellite Radio), P2P file-sharing downloaders (BitTorrent), IRC, also casual games (sometimes also referred to as mini-games or party-games) such as Tetris, Snake, Space Invaders, Sudoku, and much more.

Please feel free to add more samples of simple scripting functions with comments that you know or figure out while you're learning to script. Adding anything at all no matter how basic, if its not already here add it! someone will more than likely benefit from it. The more difficult your snippet please heavily comment it with "#" don't be stingy on the comments you can never have too much information, what's simple to you may make no sense at all to someone else, also URLs that were helpful to what you were doing would be great to add to your snippet or to the bookmark section (python sites, chatrooms, etc):

Contents

1 Python Example

Description: Python won't run the lines that we put a '#' in front of they are considered as “commented out” python will skip them.

# import the XBMC libraries so we can use the controls and functions of XBMC
import xbmc, xbmcgui
 
# name and create our window 
class BlahMainWindow(xbmcgui.Window): 
    # and define it as self
    def __init__(self):
    # add picture control to our window (self) with a hardcoded path name to picture
    self.addControl(xbmcgui.ControlImage(0,0,720,480, 'Q:\\scripts\\background.jpg'))
 
# store our window as a short variable for easy of use
W = BlahMainWindow()
# run our window we created with our background jpeg image
W.doModal()
# after the window is closed, Destroy it.
del W

2 Python resources

2.1 Python resources, guides and documentation directly related to XBMC

XBMC Online Manual Python related sections
Other XBMC Python related articles

2.2 Other python resources (indirectly related to XBMC scripting)

2.3 IRC Channels

 /server irc.freenode.net /join #xbmc-scripting
 /server irc.freenode.net /join #python
 /server irc.freenode.net /join #python-cleese
 /server irc.freenode.net /join #python-gilliam

3 Basic Information

To code python all you basically need is notepad or any other form of texteditor its not very hard to figure out the basics with the documents in Bookmark section above, have a run through them first if you have no clue whats going on in the code snippets to enlighten yourself a bit. – I would suggest looking at the ALEX's Tutorial below first to start, and remember python is all about indents...

4 Structure

  • Please keep all your files related to your script in its own folder.
  • Name the Main File default.py
  • If you want a thumbnail call it default.tbn (tbn can be jpg or png)

Use this code to find the path you want

Root = os.getcwd().replace(";","")+"\\"

The Above line will return a folder so its like q:\\scripts\\GoogleScript\\ or where ever the script is located

As of the 13th of July, MyScripts now flatterns meaning it will it will no longer shows the folders and their contents if there is a default.py in the folder (won't let you view any deeper)

Another nice way to have all your files inside your app is to put them inside a lib/ dir inside it, so "the first level" can only have the default.py and default.tbn files. To have all your sub-scripts in that way you have to put at the head of your script something similar to this code:

LIB_DIR = ROOT+"libary\\"
sys.path.append(LIB_DIR)
 
import your_own_personal_python_library

so you'll be able to import your files as if them were on the root directory of your python app.

5 Basic Steps

  1. Open a text document.
  2. Paste some code in there. Read this tutorial: HOW-TO write Python Scripts for XBMC
  3. Save it to any name you wish with .py on the end for the file extension. (myscript.py)
  4. FTP it to the scripts folder in XBMC (F:/xbmc/scripts/)
  5. In XBMC go to the submenu naturally beside the power button on the skin in Project Mayhem 1.
  6. Click scripts in the submenu.
  7. Find the script you uploaded and run it.
  8. “White Button” on the controller for Python debug whilst in scripts window (Added:13-02-05)

To find out a lot of stuff of what there is available to control in XBMC take a look at these documents from the CVS: XBMC python documentation

6 Code Snippets

(Warning: Some code may require you to first create a window or something along those lines but might not be specified)

Thanks to the following contributors:

  • ThreeZee (on EFNet: ThreeZee)
  • EnderW
  • alexsolex
  • Donno

6.1 XBMC Core Player Options

6.1.1 Play A File

If you want to play a file this is how, you dont need to make it a variable but it makes your code a little more cleaner. (this code doesnt need a window but import your xbmc libraries as normal)

# variable to contain the file location
file = 'q:\\scripts\\Music\\Underworld-BornSlippy.mp3'
# tell xbmc to play our file we specified in the above variable
xbmc.Player().play(file)

6.1.2 Fetching artist name and song title from currently playing song

By using the code example below you can get the song title and artist name of the song currently playing in XBMC.

# Import XBMC module
import xbmc
 
# First we need to create an object containing the getMusicInfoTag() class from XBMC.Player(). 
# This is in order to use the same instance of the class twice and not create a new class
# for every time we query for information. 
# This is a bit important to notice, as creating many instances is rarely
# a good thing to do (unless you need it, but not in this case).
 
tag = xbmc.Player().getMusicInfoTag()
 
# Now tag contains the getMusicInfoTag() class which then again contains song information.
# Now we use this object to get the data by calling functions within that class:
 
artist = tag.getArtist()
title = tag.getTitle()
 
# Now you have two strings containing the information. An example of what you could do next is to print it:
 
print "Playing: " + artist " - " + title
 
# This will produce i.e: "Playing: AC/DC - Back in black"

6.2 XBMC GUI

Also see WindowXML GUI Toolkit/WindowXML

6.2.1 Adding Buttons the Nice Way

Using The following addButton and setupButtons

import xbmcgui
 
try: Emulating = xbmcgui.Emulating
except: Emulating = False 
 
class Example(xbmcgui.Window): 
    """
        Example Showing Of Using Sub Buttons Module to Create
        Buttons on a Window
    """
    def __init__(self,):
        if Emulating: xbmcgui.Window.__init__(self)
        setupButtons(self,10,10,100,30,"Vert")
        self.h1 = addButon(self,"Click Me")
        self.something = addButon(self,"Something")
        self.btn_quit = addButon(self,"Quit")
    def onControl(self, c):
        if self.h1 == c:
            print "hey"
        if self.something == c:
            print "you press med"
        if self.btn_quit == c:
            self.close()
### The adding button Code (only really need this bit)
def setupButtons(self,x,y,w,h,a="Vert",f=None,nf=None):
    self.numbut  = 0
    self.butx = x
    self.buty = y
    self.butwidth = w
    self.butheight = h
    self.butalign = a
    self.butfocus_img = f
    self.butnofocus_img = nf
 
def addButon(self,text):
    if self.butalign == "Hori":
        c =  xbmcgui.ControlButton(self.butx + (self.numbut * self.butwidth),self.buty,self.butwidth,self.butheight,text,self.butfocus_img,self.butnofocus_img)
        self.addControl(c)
    elif self.butalign == "Vert":
        c = xbmcgui.ControlButton(self.butx ,self.buty + (self.numbut * self.butheight),self.butwidth,self.butheight,text,self.butfocus_img,self.butnofocus_img)
        self.addControl(c)
    self.numbut += 1
    return c
### The End of adding button Code 
 
Z = Example()
Z.doModal()
del Z

6.3 Scaling your script using setCoordinateResolution()

Example: Skinned for PAL resolution

# Import the XBMC/XBMCGUI modules.
import xbmc, xbmcgui
 
# resolution values
#1080i = 0
#720p = 1
#480p = 2
#480p16x9 = 3
#ntsc = 4
#ntsc16x9 = 5
pal = 6
#pal16x9 = 7
#pal60 = 8
#pal6016x9 = 9
 
class MyScript(xbmcgui.Window):
    def __init__(self):
        self.setResolution(pal)
 
    def setResolution(self, skinnedResolution):
        # get current resolution
        currentResolution = self.getResolution()
        offset = 0
        # if current and skinned resolutions differ and skinned resolution is not
        # 1080i or 720p (they have no 4:3) calculate widescreen offset
        if currentResolution != skinnedResolution and skinnedResolution > 1:
            # check if current resolution is 16x9
            if currentResolution == 0 or currentResolution % 2: iCur16x9 = 1
            else: iCur16x9 = 0
            # check if skinned resolution is 16x9
            if skinnedResolution % 2: i16x9 = 1
            else: i16x9 = 0
            # calculate offset
            offset = iCur16x9 - i16x9
        self.setCoordinateResolution(skinnedResolution + offset)
 
# We need to link the class to an object, and doModal to display it.
My_Window = MyScript()
My_Window.doModal()
del My_Window

6.3.1 Scaling your script for any size screen

Based on NTSC 720x480

# Import the XBMC/XBMCGUI modules.
import xbmc, xbmcgui
 
class MyScript(xbmcgui.Window):
 
    def __init__(self):
 
        if Emulating: xbmcgui.Window.__init__(self)    
 
        # This will calculate the actual screen size to 720x480 ratio
        self.scaleX = self.getWidth()  / 720.0
        self.scaleY = self.getHeight() / 480.0
 
        self.addControl(xbmcgui.ControlImage(0, 0, int(720 * self.scaleX), int(480 * self.scaleY), "Q:\\scripts\\background.gif"))
        self.addControl(xbmcgui.ControlImage(int(0 * self.scaleX), int(23 * self.scaleY), int(720 * self.scaleX), int(53 * self.scaleY), "Q:\\scripts\\top.gif"))
 
        # Any X (width / left) setting should be changed to * self.scaleX
        # i.e. The above was: self.addControl(xbmcgui.ControlImage(0, 23, 720, 53, "Q:\\scripts\\top.gif"))
        # This change will make the controls/images/etc scale to the screen.
 
# We need to link the class to an object, and doModal to display it.
 
My_Window = MyScript()
My_Window.doModal()
del My_Window

6.4 String Manipulation

6.4.1 Add strings of text together

# add strings of text together use the "+"
'Cookie' + ' Monster'
# If was added to a label etc would look like: Cookie Monster

6.4.2 Split strings of text apart into variables

# Our variable of a string we want to split up.
data = '1|(123)456-7890|JimmyRay|06262305'
# make sure our data is a string and save it in another variable
varData = str(data)
# Split our string at the character '|' or whatever one you specify 
# could be a space if you wish and save them in the specified variables
i, Name, Number, DT = varData.split('|')

6.4.3 Replace strings of text with other text

# letter or text we want to replace current text with
rplText = 'cat'
# current text we want to replace
fndText = 'dog'
# String we want to replace those words in
strText = 'dogbatdog'
# our new string variable which would be 'catbatcat' now
# or basically strText.replace('dog', 'cat')
strOutput = strText.replace(fndText, rplText)
print strOutput

6.4.4 Convert Strings, Integer

# Number variable notice no quotes
NUM = 43
# String variable notice quotes
SNUM = '43'
# Convert our variable to STRING
S1 = str(NUM)
# Convert our variable to INTEGER
S2 = int(SNUM)
print S1 + S2

6.4.5 Check if text is in a string

Note this is case sensitive, so you may want to use mystring.lower() and have the “world” all in lower case.

"""
The output of this script is
  world is in the string
  data is not in the string
"""
mystring = "Hello world"
if "world" in mystring:
    print "world is in the string"
else:
    print "world is not in the string"
 
if "data" in mystring:
    print "data is in the string"
else:
    print "data is not in the string"

6.5 Imports

6.5.1 Import Time

6.5.1.1 Delay
# import our time class
import time
# call time with the sleep function for ten seconds or however many wanted
# until script continues on running.
time.sleep(10)
6.5.1.2 System Clock
#Import our time class
import time
# call time with strftime function followed by the formatting we want to use
# you can put any character between the format symbols you want EX: '/'
Time = str(time.strftime ('%H:%M:%S%p'))
Date = str(time.strftime ('%d/%a/%Y'))
print Date + Time
# END OF CODE
 
# FORMAT DIRECTIVES
Directive | Meaning Notes
- - - - - - - - - - - - - - - 
%a Locales abbreviated weekday name. 
%A Locales full weekday name. 
%b Locales abbreviated month name. 
%B Locales full month name. 
%c Locales appropriate date and time representation. 
%d Day of the month as a decimal number [01,31]. 
%H Hour (24-hour clock) as a decimal number [00,23]. 
%I Hour (12-hour clock) as a decimal number [01,12]. 
%j Day of the year as a decimal number [001,366]. 
%m Month as a decimal number [01,12]. 
%M Minute as a decimal number [00,59]. 
%p Locales equivalent of either AM or PM. (1) 
%S Second as a decimal number [00,61]. (2) 
%U Week number of the year (Sunday as the first day of the week) as a decimal number [00,53]. 
   All days in a new year preceding the first Sunday are considered to be in week 0. (3) 
%w Weekday as a decimal number [0(Sunday),6]. 
%W Week number of the year (Monday as the first day of the week) as a decimal number [00,53]. 
   All days in a new year preceding the first Monday are considered to be in week 0. (3) 
%x Locale's appropriate date representation. 
%X Locale's appropriate time representation. 
%y Year without century as a decimal number [00,99]. 
%Y Year with century as a decimal number. 
%Z Time zone name (no characters if no time zone exists).

6.5.2 Import OS

6.5.2.1 Retrieving the contents of a directory
# need to use os functions
import os
 
# Get the directory contents and put them in **lstDirList**
lstDirList = os.listdir("Q:\\scripts\\")
 
# Cycle through each one, performing the functions within indentation
# Example is print, you could do anything with it though.
# Keep in mind, this will return files AND directories.
 
for strFileDir in lstDirList:
     print strFileDir
6.5.2.2 File Exist

Check to see if file exists on harddrive

import os
# Pretty self explanatory but basically we check to see if the Me.png file exist 
# where we specifiy if not the statement returns false so then we tell our variable
# to use a different source ex.) NoPhoto.png.
if os.path.isfile('Q:\\scripts\Photos\Me.png') == False: Photo = 'Q:\\scripts\Photos\NoPhoto.png'
6.5.2.3 Write to a text file
# LF here stands for LOG FILE you can put anything there doesnt really 
# matter its basically our variable for the location of our text file.
LF = open('Q:\\scripts\\BLAH.LOG', 'a')
# Write to our text file the information we have provided and then goto next line in our file.
(so if it was in a loop it would write to the next line instead of everything on the same line)
LF.write('Some log information blah blah blah' + '\n')
# Close our file so no further writing is posible.
LF.close()
6.5.2.4 Read text file
# LF here stands for LOG FILE you can put anything there doesnt really 
# matter it's basically our variable for the location of our text file.
LF = 'Q:\\scripts\\BLAH.LOG'
# Opens our Log File to 'r' READ from it.
log = open(LF, 'r')
# load our text file into an array for easy access.
for line in log:
  # Output all of the array text file to the screen.
  print(line[:-1])
  # Close our text file so no further reading is posible.
  log.close()
6.5.2.5 Clear text file
# LF here stands for LOG FILE you can put anything there doesnt really 
# matter its basically our variable for the location of our text file.
LF = 'Q:\\scripts\\BLAH.LOG'
# basically all this is doing is opening our file and write nothing to it so it will be blank.
clearfile = open(FL, 'w')
# Close our file so no further writing is possible.
clearfile.close()
6.5.2.6 Reading a file
listname = []
listoptions = []
f = open('Q:\\test.txt',"r")
s = f.write()
f.close()
ls = s.split("\n") # This means each new line will be loaded into it's own array.
for l in ls:
  if l != "":
    item = l.split("=") 
    # The above line will split Something=Hello into item[0] (Something) and item[1] (Hello)
    listname.append(item[0])
    listoptions.append(item[1])

6.6 HTTP

6.6.1 Read URLS from a web page

The following code will search a page for all <a href="urls">Description</a> and store all the urls in one list array and all the descriptions in another

import urllib,urllib2 , re
 
# The url in which to use
Base_URL = "http://ftp.iinet.net.au/pub/games/movies/Red_Vs_Blue/Season1/"
 
#Pre-define global Lists
LinkDescription = []
LinkURL = []
 
WebSock = urllib.urlopen(Base_URL) # Opens a 'Socket' to URL
WebHTML = WebSock.read() # Reads Contents of URL and saves to Variable
WebSock.close() # Closes connection to url
 
Temp_Web_URL = re.compile('<a href=["](.*)[.]zip["]>', re.IGNORECASE).findall(WebHTML) # Using find all mentions of stuff using regexp to use wildcards
Temp_Web_Desc = re.compile('<a href=["].*[.]zip["]>(.*)</a>').findall(WebHTML) # find it
 
for urls, desc in zip(Temp_Web_URL,Temp_Web_Desc):
    LinkURL.append(urls[9:-2])    # Adds urls to a list   # note need to add extention for these links to really work
    LinkDescription.append(desc)    # Adds the descrptions as a array)

6.6.2 Download Files with Progressbar

Download a url from the net and saves to a file and shows the progressbar. Usage: to call the function use DownloaderClass(url,dest)

import urllib, os,re,urllib2
import xbmc,xbmcgui
 
def DownloaderClass(url,dest):
    dp = xbmcgui.DialogProgress()
    dp.create("My Script","Downloading File",url)
    urllib.urlretrieve(url,dest,lambda nb, bs, fs, url=url: _pbhook(nb,bs,fs,url,dp))
 
def _pbhook(numblocks, blocksize, filesize, url=None,dp=None):
    try:
        percent = min((numblocks*blocksize*100)/filesize, 100)
        print percent
        dp.update(percent)
    except:
        percent = 100
        dp.update(percent)
    if dp.iscanceled(): 
        print "DOWNLOAD CANCELLED" # need to get this part working
        dp.close()
 
url ='http://ftp.iinet.net.au/adsl.test'
DownloaderClass(url,"e:\something.txt")

7 Script Arguments

7.1 Passing Arguments to a Script

As of 2007/02/24, arguments can be passed into a script using the builtin command XBMC.RunScript. The first parameter this builtin takes is the absolute location of the python script and all additional parameters are passed as arguments to the script.

import os
import xbmc
# get the parent path of the current script
path = os.getcwd()[:-1]+"\\"
# call a different script with the argument 'Hello World'
xbmc.executebuiltin("XBMC.RunScript("+path+"argtest.py,Hello World)")

7.2 Using Arguments from sys.argv

The arguments can be accessed from a different script using sys.argv. This is a list of strings that is populated when the script is launched using the builtin command. sys.argv[0] is the name of the script while the rest of the list, sys.argv[1:], are the arguments passed to the script.

import xbmcgui
import sys
 
count = len(sys.argv) - 1
 
if count > 0:
	xbmcgui.Dialog().ok("Status",sys.argv[0] +" called with " + str(count)+" args",
		"["+", ".join(sys.argv[1:])+"]")
else:
	xbmcgui.Dialog().ok("Status","no arguments specified")

7.3 Script Settings

7.3.1 Script install path

This tips is usefull to let user the opportunity to install the script in the path of their choice. The tricks is to store the path where the script has just been launched

# you'll need to import the os library
import os
# Then catch the actual path
HOME_DIR=os.getcwd()
# the returned path is not correct as it finish with a trailer coma
HOME_DIR=HOME_DIR[:-1] # will delete the last char ; NOTE : with Win OS you'll not delete this trailing char
# to be a good path, add double \ 
HOME_DIR=HOME_DIR+"\\"
# all this can be done with a unique line : HOME_DIR=os.getcwd()[:-1]+"\\"
# 
# Now we can set every path we need for the script
PICS_DIR=HOME_DIR+"pics\\"
DATA_DIR=HOME_DIR+"datas\\"
# and when we need to get for example an image, we just use these previous vars as path :
backgroundpic=PICS_DIR+"background_file.png"

8 Keymapping: How To Map Your Controls In Python

8.1 Control Types

There are four Control Types available to map in XBMC:

Controller, Remote, Mouse, & Keyboard


8.1.1 Control Type: Remote

The Remote is the another Control Type that has two methods you can use to map actions to your Xbox conroller button(s):

Button Codes & Action Codes (see Key.h for a full list of codes)

8.1.2 Button Code Reference

  Controls                       IDs            Extra Info
  ========                       ===            ==========
 
  A Button                       256
  B Button                       257
  X Button                       258
  Y Button                       259
  Start Button                   274
  Back Button                    275
  Black Button                   260
  White Button                   261
 
  Left Trigger Button            262            "Pressing the Left Trigger"
  Left Trigger Analog            278            "Holding down the Left Trigger"
  Right Trigger Button           263            "Pressing the Right Trigger"
  Right Trigger Analog           279            "Holding down the Right Trigger"
 
  Left ThumbStick                264            "Action is sent when the Left ThumbStick is moved"
  Left ThumbStick Button         276
  Left ThumbStick Up             280
  Left ThumbStick Down           281
  Left ThumbStick Left           282
  Left ThumbStick Right          283
 
  Right ThumbStick               265            "Action is sent when the Right ThumbStick is moved"
  Right ThumbStick Button        277         
  Right ThumbStick Up            266
  Right ThumbStick Down          267
  Right ThumbStick Left          268
  Right ThumbStick Right         269
 
  DPad Up                        270
  DPad Down                      271
  DPad Left                      272
  DPad Right                     273

To obtain an updated list of Action & Button Codes, please see: key.h


9 See also

Development:

Personal tools
Namespaces

Variants
Actions
Navigation
Wiki help
Toolbox