HOW-TO:Write Python Scripts
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.
|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):
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
- XBMC Online Manual Python related sections
- XBMC Manual – Python Basics
- XBMC Manual – Built-in Scripting
- Web-Server HTTP-API (HTTPAPI)
- XBMC Manual – List of Built In Functions
- XBMC Manual – Labels Available In XBMC (InfoLabels)
- HOW-TO write Python Scripts for XBMC, a Tutorial
- This HOW-TO tutorial should be the perfect place if you about to start making your first python script for XBMC and you do not yet know very much about python, (note though that this tutorial could now be considered a little old-fashioned, but it is still a very good starting point).
- Other XBMC Python related articles
- XBMC Python Documentation - Updated from XBMC SVN on a regular bases
- XBMC HTTP API Commands (quick overview, with link to doc)
- XBMC Python emulator for Windows PC (BETA)
- XBMC Forum – Python Scripts Development
- XBMC Forum – Python Scripts Support and Requests
- XBMC Python scripts – Help for French users and coders
- Python Documentation on python.org
- An Introduction to Python - A Brief Tour of Python
- Based on presentation materials by David Beazley (author of Python Essential Reference)
- Snyppets - Python snippets
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...
- 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
- Open a text document.
- Paste some code in there. Read this tutorial: HOW-TO write Python Scripts for XBMC
- Save it to any name you wish with .py on the end for the file extension. (myscript.py)
- FTP it to the scripts folder in XBMC (F:/xbmc/scripts/)
- In XBMC go to the submenu naturally beside the power button on the skin in Project Mayhem 1.
- Click scripts in the submenu.
- Find the script you uploaded and run it.
- “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)
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
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.1 Import Time
# 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)
126.96.36.199 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
188.8.131.52 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
184.108.40.206 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'
220.127.116.11 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()
18.104.22.168 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()
22.214.171.124 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()
126.96.36.199 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 (Something) and item (Hello) listname.append(item) listoptions.append(item)
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 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 +" 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:
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):
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