OVER2HOURS
OFVIDEOTUTORIALS
Over20
incredible
projects
The ultimate guide to coding with Python
Learn to use Python t1SPHSBNHBNFTt(FUDSFBUJWFXJUI1J
Python
The
NEW
Welcometo
Pythonisanincrediblyversatile,expansivelanguagewhich,duetoitssimilarityto
everydaylanguage,issurprisinglyeasytolearnevenforinexperiencedprogrammers.It
hasseenahugeincreaseinpopularitysincethereleaseandriseoftheRaspberryPi,for
whichPythonistheofficiallyrecognisedprogramminglanguage.Inthisnewedition
ofThePythonBook,you’llfindplentyofcreativeprojectstohelpyougettogripswith
thecombinationofyourRaspberryPiandPython’spowerfulfunctionality,pluslotsof
tutorialsthatfocusonPython’seffectivenessawayfromtheRaspberryPi.You’lllearn
allabouthowtocodewithPythonfromastandingstart,withourcomprehensive
masterclass,thengoontocompletetutorialsthatwillconsolidateyourskillsandhelp
youtobecomefluentinthelanguage.You’lllearnhowtomakePythonworkforyou
withtutorialsoncodingwithDjango,Flask,Pygameandevenmoreusefulthird-party
frameworks.GetreadytobecomeatruePythonexpertwiththewealthofinformation
containedwithinthesepages.
Python
The
Imagine Publishing Ltd
Richmond House
33 Richmond Hill
Bournemouth
Dorset BH2 6EZ
 +44 (0) 1202 586200
Website: www.imagine-publishing.co.uk
Twitter: @Books_Imagine
Facebook: www.facebook.com/ImagineBookazines
Publishing Director
Aaron Asadi
Head of Design
Ross Andrews
Production Editor
Alex Hoskins
Senior Art Editor
Greg Whitaker
Assistant Designer
Steve Dacombe
Printed by
William Gibbons, 26 Planetary Road, Willenhall, West Midlands, WV13 3XT
Distributed in the UK, Eire  the Rest of the World by
Marketforce, 5 Churchill Place, Canary Wharf, London, E14 5HU
Tel 0203 787 9060 www.marketforce.co.uk
Distributed in Australia by
Network Services (a division of Bauer Media Group), Level 21 Civic Tower, 66-68 Goulburn Street,
Sydney, New South Wales 2000, Australia Tel +61 2 8667 5288
Disclaimer
The publisher cannot accept responsibility for any unsolicited material lost or damaged in the
post. All text and layout is the copyright of Imagine Publishing Ltd. Nothing in this bookazine may
be reproduced in whole or part without the written permission of the publisher. All copyrights are
recognised and used specifically for the purpose of criticism and review. Although the bookazine has
endeavoured to ensure all information is correct at time of print, prices and availability may change.
This bookazine is fully independent and not affiliated in any way with the companies mentioned herein.
The Python Book Second Edition © 2016 Imagine Publishing Ltd
ISBN 9781785462382
bookazine series
Part of the
Python
The
Python essentials
26 Code rock, paper, scissors
Put basic coding into action
32 Program a hangman game
Use Python to make the classic game
38 Play poker dice
Test your luck and your coding
44 Create a graphical interface
Add interface to your projects
50 Bring graphics to games
Add images to simple games
56 Build an app for Android
Make your own app with Kivy
62 Making web apps
Use Python to create online apps
66 50 Python tips
Essential knowledge for Python users
Contents
8 Get started
with Python
Master the basics the right way
Work with Python
74 Python for professionals
Use your coding skills at work
82 Make extensions for XBMC
Enhance XBMC with this tutorial
88 Scientific computing
Get to grips with NumPy
92 Instant messaging
Get chatting using Python
98 Replace your shell
Use Python for your primary shell
102 Python for system admins
How Python helps system administration
6 The Python Book
82
16 50 essential
commands
The commands you need to know
92
116
124
Create with Python
108 Build tic-tac-toe with Kivy
Program noughts and crosses
112 Create two-step authentication
Use Twilio for safe authentication
116 Program a Space Invaders clone
Make the basic Pivaders game
120 Add animation and sound
Enhance your Pivaders game
124 Make a visual novel
Program a book-style game
128 Pygame Zero
Turn your ideas into games
Python
The
“Gettogrips
withPython,and
masterhighly
versatilecode”
Web development
136 Develop with Python
Why Python is perfect for the web
142 Create dynamic templates
Use Jinja, Flask and more
146 Build your own blog
Begin developing your blog
150 Deliver content to your blog
Add content to your site
154 Enhance your blog
Complete your blog with add-ons
Use Python with Pi
160 Programming in Python on
Raspberry Pi
Learn how to optimise for Pi
164 Turn Raspberry Pi into a
stop-motion studio
Learn how to optimise for Pi
168 Send SMS with Pi
Send text messages for free
170 Build an LED Matrix
Use Pi to control light sequences
50
Python
tips
66
The Python Book 7
170
136
8 The Python Book
Get started with Python
P
ython is a great programming language for
both beginners and experts. It is designed with
code readability in mind, making it an excellent
choice for beginners who are still getting used to
various programming concepts.
The language is popular and has plenty of libraries
available, allowing programmers to get a lot done with
relatively little code.
YoucanmakeallkindsofapplicationsinPython:
youcouldusethePygameframeworktowrite
simple2Dgames,youcouldusetheGTK
librariestocreateawindowedapplication,oryoucould
trysomethingalittlemoreambitiouslikeanappsuch
ascreatingoneusingPython’sBluetoothandInput
librariestocapturetheinputfromaUSBkeyboardand
relaytheinputeventstoanAndroidphone.
Forthisguide we’regoingtobeusingPython2.xsince
thatistheversionthatismostlikelytobeinstalledonyour
Linuxdistribution.
Inthefollowingtutorials,you’lllearnhowtocreate
populargamesusingPythonprogramming.We’llalso
showyouhowtoaddsoundandAItothesegames.
Alwayswantedtohaveagoat
programming?Nomoreexcuses,
becausePythonistheperfectwaytogetstarted!
The Python Book 9
Get started with Python
Get started with Python
10 The Python Book
TIP
Ifyouwereusingagraphical
editorsuchasgedit,then
youwouldonlyhavetodo
thelaststepofmakingthe
fileexecutable.Youshould
onlyhavetomarkthefileas
executableonce.Youcan
freelyeditthefileonceit
isexecutable.
Interpretedvscompiledlanguages
AninterpretedlanguagesuchasPythonisonewherethesource
codeisconvertedtomachinecodeandthenexecutedeachtimethe
programruns.ThisisdiferentfromacompiledlanguagesuchasC,
wherethesourcecodeisonlyconvertedtomachinecodeonce–the
resultingmachinecodeisthenexecutedeachtimetheprogramruns.
HelloWorld
Let’s get stuck in, and what better way than with the
programmer’s best friend, the ‘Hello World’ application! Start
by opening a terminal. Its current working directory will be your
home directory. It’s probably a good idea to make a directory for
the files we’ll be creating in this tutorial, rather than having them
loose in your home directory. You can create a directory called
Python using the command mkdir Python. You’ll then want to
change into that directory using the command cdPython.
The next step is to create an empty file using the command
‘touch’ followed by the filename. Our expert used the command
touch hello_world.py. The final and most important part of
setting up the file is making it executable. This allows us to run
code inside the hello_world.py file. We do this with the command
chmod +x hello_world.py. Now that we have our file set up, we
can go ahead and open it up in nano, or any text editor of your
choice. Gedit is a great editor with syntax highlighting support
that should be available on any distribution. You’ll be able to
install it using your package manager ifyou don’t have it already.
[liam@liam-laptop ~]$ mkdir Python
[liam@liam-laptop ~]$ cd Python/
[liam@liam-laptop Python]$ touch hello_world.py
[liam@liam-laptop Python]$ chmod +x hello_world.py
[liam@liam-laptop Python]$ nano hello_world.py
Our Hello World program is very simple, it only needs two lines.
The first line begins with a ‘shebang’ (the symbol #! – also known
as a hashbang) followed by the path to the Python interpreter.
The program loader uses this line to work out what the rest of the
lines need to be interpreted with. If you’re running this in an IDE
likeIDLE, you don’t necessarily needto do this.
The code that is actually read by the Python interpreter is only
a single line. We’re passing the value Hello World to the print
function by placing it in brackets immediately after we’ve called
the print function. Hello World is enclosed in quotation marks to
indicate that it is a literal value and should not be interpreted as
source code. As expected, the print function in Python prints any
value that getspassedto it from the console.
You can save the changes you’ve just made to the file in nano
using the key combination Ctrl+O, followed by Enter. Use Ctrl+X
toexit nano.
#!/usr/bin/env python2
print(“Hello World”)
You can run the Hello World program by prefixing
its filename with ./ – in this case you’d type:
./hello_world.py.
[liam@liam-laptop Python]$ ./hello_world.py
Hello World
Variablesanddatatypes
A variable is a name in source code that is associated with an
area in memory that you can use to store data, which is then
called upon throughout the code. The data can be one of many
types,including:
Integer Stores whole numbers
Float Storesdecimalnumbers
Boolean Can havea value ofTrueorFalse
String Storesacollectionofcharacters.“Hello
World”is astring
As well as these main data types, there are sequence types
(technically, a string is a sequence type but is so commonly used
we’ve classed it asa main data type):
List Containsacollectionofdatainaspecificorder
Tuple Contains a collection immutable data in a
specific order
A tuple would be used for something like a co-ordinate,
containing an x and y value stored as a single variable, whereas
a list is typically used to store larger collections. The data
stored in a tuple is immutable because you aren’t able to
change values of individual elements in a tuple. However, you
can do so in a list.
It will also be useful to know about Python’s dictionary
type. A dictionary is a mapped data type. It stores data in
key-value pairs. This means that you access values stored in
the dictionary using that value’s corresponding key, which is
different to how you would do it with a list. In a list, you would
access an element of the list using that element’s index (a
number representing the element’s position in the list).
Let’s work on a program we can use to demonstrate how to
use variables and different data types. It’s worth noting at
this point that you don’t always have to specify data types
in Python. Feel free to create this file in any editor you like.
Everything will work just fine as long as you remember to make
the file executable. We’re going to call ours variables.py.
“A variable is a name
in source code that is
associated with an area in
memory that you can use to
store data”
Get started with Python
The following line creates an
integer variable called hello_int
with the # value of 21. Notice
how it doesn’t need to go in
quotation marks
You could also create the
same list in the following way
We might as well create a
dictionary while we’re at it.
Notice how we’ve aligned the
colons below to make the
code tidy
The same principal is true of
Boolean values
We create a tuple in the
following way
And a list in this way
#!/usr/bin/env python2
# We create a variable by writing the name of the variable we want followed
# by an equals sign, which is followed by the value we want to store in the
# variable. For example, the following line creates a variable called
# hello_str, containing the string Hello World.
hello_str =“Hello World”
hello_int = 21
hello_bool = True
hello_tuple = (21, 32)
hello_list = [“Hello,”,“this”,“is”,“a”,“list”]
# This list now contains 5 strings. Notice that there are no spaces
# between these strings so if you were to join them up so make a sentence
# you’d have to add a space between each element.
hello_list = list()
hello_list.append(“Hello,”)
hello_list.append(“this”)
hello_list.append(“is”)
hello_list.append(“a”)
hello_list.append(“list”)
# The irst line creates an empty list and the following lines use the append
# function of the list type to add elements to the list. This way of using a
# list isn’t really very useful when working with strings you know of in
# advance, but it can be useful when working with dynamic data such as user
# input. This list will overwrite the irst list without any warning as we
# are using the same variable name as the previous list.
hello_dict = {“irst_name”:“Liam”,
“last_name” :“Fraser”,
“eye_colour”:“Blue”}
# Let’s access some elements inside our collections
# We’ll start by changing the value of the last string in our hello_list and
# add an exclamation mark to the end. The“list”string is the 5th element
# in the list. However, indexes in Python are zero-based, which means the
# irst element has an index of 0.
print(hello_list[4])
hello_list[4] +=“!”
# The above line is the same as
hello_list[4] = hello_list[4] +“!”
print(hello_list[4])
TIP
Atthispoint,it’sworth
explainingthatanytextin
aPythonfilethatfollows
a#characterwillbe
ignoredbytheinterpreter.
Thisissoyoucanwrite
commentsinyourcode.
Notice that there will now be
two exclamation marks when
we print the element
“Any text in a Python file that follows a #
character will be ignored”
12 The Python Book
Get started with Python
Indentationindetail
Aspreviouslymentioned,thelevelofindentation
dictateswhichstatementablockofcodebelongs
to.IndentationismandatoryinPython,whereasin
otherlanguages,setsofbracesareusedtoorganise
codeblocks.Forthisreason,itisessentialthatyou
useaconsistentindentationstyle.Fourspaces
aretypicallyusedtorepresentasinglelevelof
indentationinPython.Youcanusetabs,buttabsare
notwelldefined,especiallyifyouhappentoopena
fileinmorethanoneeditor.
Moreabouta
Pythonlist
APythonlistissimilartoan
arrayinotherlanguages.A
list(ortuple)inPythoncan
containdataofmultiple
types,whichisnotusually
thecasewitharraysinother
languages.Forthisreason,
werecommendthatyou
onlystoredataofthesame
typeinalist.Thisshould
almostalwaysbethecase
anywayduetothenatureof
thewaydatainalistwould
beprocessed.
print(str(hello_tuple[0]))
# We can’t change the value of those elements like we just did with the list
# Notice the use of the str function above to explicitly convert the integer
# value inside the tuple to a string before printing it.
print(hello_dict[“irst_name”] +““ + hello_dict[“last_name”] +“ has“ +
hello_dict[“eye_colour”] +“ eyes.”)
print(“{0} {1} has {2} eyes.”.format(hello_dict[“irst_name”],
hello_dict[“last_name”],
hello_dict[“eye_colour”]))
Remember that tuples are
immutable, although we can
access the elements of them
like so
Let’s create a sentence using the
data in our hello_dict
A tidier way of doing this
would be to use Python’s
string formatter
Controlstructures
In programming, a control structure is any kind of statement that
can change the path that the code execution takes. For example, a
control structure that decided to end the program if a number was
less than 5 would looksomething like this:
#!/usr/bin/env python2
import sys # Used for the sys.exit function
int_condition = 5
if int_condition  6:
sys.exit(“int_condition must be = 6”)
else:
print(“int_condition was = 6 - continuing”)
The path that the code takes will depend on the value of
the integer int_condition. The code in the ‘if’ block will only be
executed if the condition is true. The import statement is used
to load the Python system library; the latter provides the exit
function, allowing you to exit the program, printing an error
message. Notice that indentation (in this case four spaces per
indent) is used to indicate which statement a block of code
belongs to.
‘If’ statements are probably the most commonly used control
structures. Othercontrol structures include:
•For statements, which allow you to iterate over items in
collections, or to repeat a piece of code a certain number
oftimes;
• While statements, a loop that continues while the condition
istrue.
We’re going to write a program that accepts user input from the
user to demonstrate how control structures work. We’re calling it
construct.py.
The ‘for’ loop is using a local copy of the current value, which
means any changes inside the loop won’t make any changes
affecting the list. On the other hand however, the ‘while’ loop is
directly accessing elements in the list, so you could change the list
there should you want to do so. We will talk about variable scope in
some more detail later on. The output from the above program is
as follows:
[liam@liam-laptop Python]$ ./construct.py
How many integers? acd
You must enter an integer
[liam@liam-laptop Python]$ ./construct.py
How many integers? 3
Please enter integer 1: t
You must enter an integer
Please enter integer 1: 5
Please enter integer 2: 2
Please enter integer 3: 6
Using a for loop
5
2
6
Using a while loop
5
2
6
“The ‘for‘ loop uses
a local copy, so
changes in the loop
won’t affect the list”
The Python Book 13
The number of integers we
want in the list
A list to store the integers
These are used to keep track
of how many integers we
currently have
If the above succeeds then isint
will be set to true: isint =True
#!/usr/bin/env python2
# We’re going to write a program that will ask the user to input an arbitrary
# number of integers, store them in a collection, and then demonstrate how the
# collection would be used with various control structures.
import sys # Used for the sys.exit function
target_int = raw_input(“How many integers?“)
# By now, the variable target_int contains a string representation of
# whatever the user typed.We need to try and convert that to an integer but
# be ready to # deal with the error if it’s not. Otherwise the program will
# crash.
try:
target_int = int(target_int)
except ValueError:
sys.exit(“You must enter an integer”)
ints = list()
count = 0
# Keep asking for an integer until we have the required number
while count  target_int:
new_int = raw_input(“Please enter integer {0}:“.format(count + 1))
isint = False
try:
new_int = int(new_int)
except:
print(“You must enter an integer”)
# Only carry on if we have an integer. If not, we’ll loop again
# Notice below I use ==, which is diferent from =.The single equals is an
# assignment operator whereas the double equals is a comparison operator.
if isint == True:
# Add the integer to the collection
ints.append(new_int)
# Increment the count by 1
count += 1
print(“Using a for loop”)
for value in ints:
print(str(value))
By now, the user has given up or
we have a list illed with integers.
We can loop through these in a
couple of ways. The irst is with
a for loop
14 The Python Book
Get started with Python
#!/usr/bin/env python2
# Below is a function called modify_string, which accepts a variable
# that will be called original in the scope of the function. Anything
# indented with 4 spaces under the function definition is in the
# scope.
def modify_string(original):
original += “ that has been modified.”
# At the moment, only the local copy of this string has been modified
def modify_string_return(original):
original += “ that has been modified.”
# However, we can return our local copy to the caller. The function
# ends as soon as the return statement is used, regardless of where it
# is in the function.
return original
test_string = “This is a test string”
modify_string(test_string)
print(test_string)
test_string = modify_string_return(test_string)
print(test_string)
# The function’s return value is stored in the variable test string,
# overwriting the original and therefore changing the value that is
# printed.
We are now outside of
the scope of the modify_
string function, as we
have reduced the level
of indentation
The test string won’t be
changed in this code
However, we can call the
function like this
TIP
Youcandefinedefaults
forvariablesifyouwant
tobeabletocallthe
functionwithoutpassing
anyvariablesthroughat
all.Youdothisbyputting
anequalssignafter
thevariablename.For
example,youcando:
def modify_string
(original=” Default
String”)
# Or with a while loop:
print(“Using a while loop”)
# We already have the total above, but knowing the len function is very
# useful.
total = len(ints)
count = 0
while count  total:
print(str(ints[count]))
count += 1
Functionsandvariablescope
Functions are used in programming to break processes down into smaller
chunks. This often makes code much easier to read. Functions can also be
reusable if designed in a certain way. Functions can have variables passed
to them. Variables in Python are always passed by value, which means that
a copy of the variable is passed to the function that is only valid in the scope
of the function. Any changes made to the original variable inside the function
will be discarded. However, functions can also return values, so this isn’t
an issue. Functions are defined with the keyword def, followed by the
name of the function. Any variables that can be passed through are put in
brackets following the function’s name. Multiple variables are separated by
commas. The names given to the variables in these brackets are the ones
that they will have in the scope of the function, regardless of what
the variable that’s passed to the function is called. Let’s see this
in action.
Theoutputfromtheprogramoppositeisasfollows:
“Functions are used in
programming to break
processes down in”
Get started with Python
[liam@liam-laptop Python]$ ./functions_and_scope.py
This is a test string
This is a test string that has been modified.
Scope is an important thing to get the hang of, otherwise it can get you
into some bad habits. Let’s write a quick program to demonstrate this. It’s
going to have a Boolean variable called cont, which will decide if a number
will be assigned to a variable in an if statement. However, the variable
hasn’t been defined anywhere apart from in the scope of the if statement.
We’ll finish off by trying to print the variable.
#!/usr/bin/env python2
cont = False
if cont:
var = 1234
print(var)
In the section of code above, Python will convert the integer to a string
before printing it. However, it’s always a good idea to explicitly convert
things to strings – especially when it comes to concatenating strings
together. If you try to use the + operator on a string and an integer, there
will be an error because it’s not explicitly clear what needs to happen.
The + operator would usually add two integers together. Having said that,
Python’s string formatter that we demonstrated earlier is a cleaner way of
doing that. Can you see the problem? Var has only been defined in the scope
of the if statement. This means that we get a very nasty error when we try to
access var.
[liam@liam-laptop Python]$ ./scope.py
Traceback (most recent call last):
File “./scope.py”, line 8, in module
print var
NameError: name ‘var’ is not defined
If cont is set to True, then the variable will be created and we can access
it just fine. However, this is a bad way to do things. The correct way is to
initialise the variable outside of the scope of the if statement.
#!/usr/bin/env python2
cont = False
var = 0
if cont:
var = 1234
if var != 0:
print(var)
The variable var is defined in a wider scope than the if statement, and
can still be accessed by the if statement. Any changes made to var inside
the if statement are changing the variable defined in the larger scope.
This example doesn’t really do anything useful apart from illustrate the
potential problem, but the worst-case scenario has gone from the program
crashing to printing a zero. Even that doesn’t happen because we’ve added
an extra construct to test the value of var before printing it.
Codingstyle
It’s worth taking a little time to talk about coding style. It’s simple to write
tidy code. The key is consistency. For example, you should always name
your variables in the same manner. It doesn’t matter if you want to use
camelCase or use underscores as we have. One crucial thing is to use
self-documenting identifiers for variables. You shouldn’t have to guess
what a variable does. The other thing that goes with this is to always
comment your code. This will help anyone else who reads your code,
and yourself in the future. It’s also useful to put a brief summary at
the top of a code file describing what the application does, or a part of
the application ifit’smadeup ofmultiplefiles.
Summary
This article should have introduced you to the basics of programming
in Python. Hopefully you are getting used to the syntax, indentation
and general look and feel of a Python program. The next step is
to learn how to come up with a problem that you want to solve, and
break it down into small enough steps that you can implement in a
programming language.
Google, or any other search engine, is very helpful. If you are stuck
with anything, or have an error message you can’t work out how to
fix, stick it into Google and you should be a lot closer to solving your
problem. For example, if we Google ‘play mp3 file with python’, the
first link takes us to a Stack Overflow thread with a bunch of useful
replies. Don’t be afraid to get stuck in – the real fun of programming is
solving problemsone manageablechunk ata time.
Happyprogramming!
Comparisonoperators
The common comparison operators available in Python include:
 strictlylessthan
= lessthanorequal
 strictlygreaterthan
= greaterthanorequal
== equal
!= notequal
Python has a massive environment of extra modules
that can provide functionality in hundreds of
different disciplines. However, every programming
language has a core set of functionality that everyone
should know in order to get useful work done. Python
is no different in this regard. Here, we will look at
50 commands that we consider to be essential to
programming in Python. Others may pick a slightly
differentset,butthislistcontainsthebestofthebest.
We will cover all of the basic commands, from
importing extra modules at the beginning of a program
to returning values to the calling environment at the
end. We will also be looking at some commands that
are useful in learning about the current session within
Python, like the current list of variables that have been
definedandhowmemoryisbeingused.
BecausethePythonenvironmentinvolvesusingalot
of extra modules, we will also look at a few commands
that are strictly outside of Python. We will see how to
install external modules and how to manage multiple
environments for different development projects.
Sincethisisgoingtobealistofcommands,thereisthe
assumption that you already know the basics of how
to use loops and conditional structures. This piece is
designed to help you remember commands that you
know you’ve seen before, and hopefully introduce you
toafewthatyoumaynothaveseenyet.
Although we’ve done our best to pack everything
you could ever need into 50 tips, Python is such an
expansive language that some commands will have
been left out. Make some time to learn about the ones
thatwedidn’tcoverhere,onceyou’vemasteredthese.
PYTHON
ESSENTIAL
COMMANDS
Pythonisknownasavery
denselanguage,withlotsof
modulescapableofdoing
almostanything.Here,
wewilllookatthecore
essentialsthateveryone
needstoknow
16 The Python Book
50 Python commands
Importing modules
The strength of Python is its ability to be
extended through modules. The first step in many
programs is to import those modules that you need.
The simplest import statement is to just call ‘import
modulename’. In this case, those functions and
objects provided are not in the general namespace.
You need to call them using the complete name
(modulename.methodname). You can shorten the
‘modulename’ part with the command ‘import
modulename as mn’. You can skip this issue
completely with the command ‘from modulename
import *’ to import everything from the given module.
Then you can call those provided capabilities directly.
If you only need a few of the provided items, you can
import them selectively by replacing the ‘*’ with the
methodorobjectnames.
Evaluating code
Sometimes, you may have chunks of
code that are put together programmatically. If
these piecesof codeare put togetherasa string,
you can execute the result with the command
‘eval(“code_string”)’. Any syntax errors within
the code string are reported as exceptions. By
default, this code is executed within the current
session, using the current globals and locals
dictionaries. The ‘eval’ command can also take
two other optional parameters, where you can
provide a different set of dictionaries for the
globals and locals. If there is only one additional
parameter, then it is assumed to be a globals
dictionary. You can optionally hand in a code
objectthatiscreatedwiththecompilecommand
insteadofthecodestring.Thereturnvalueofthis
commandisNone.
An enhanced shell
The default interactive shell is provided
through the command ‘python’, but is
rather limited. An enhanced shell is provided by
the command ‘ipython’. It provides a lot of extra
functionality to the code developer. A thorough
history system is available, giving you access to
not only commands from the current session,
but also from previous sessions. There are also
magic commands that provide enhanced ways of
interacting with the current Python session. For
more complex interactions, you can create and use
macros. You can also easily peek into the memory
of the Python session and decompile Python code.
You can even create profiles that allow you to handle
initialisationstepsthatyoumayneedtodoeverytime
youuseiPython.
Installing new modules
While most of the commands we are looking at are Python commands
that are to be executed within a Python session, there are a few essential
commands that need to be executed outside of Python. The first of these is pip.
Installingamoduleinvolvesdownloadingthesourcecode,andcompilinganyincluded
externalcode.Luckily,thereisarepositoryofhundredsofPythonmodulesavailable
at http://pypi.python.org. Instead of doing everything manually, you can install a
new module by using the command ‘pip install modulename’. This command will
also do a dependency check and install any missing modules before installing the
one you requested. You may need administrator rights if you want this new module
installed in the global library for your computer. On a Linux machine, you would
simplyrunthepipcommandwithsudo.Otherwise,youcaninstallittoyour
personallibrarydirectorybyaddingthecommandlineoption‘—user’.
“Every programming language out there has a
core set of functionality that everyone should
know in order to get useful work done. Python is
no different”
01
Executing a script
Importing a module does run the code
within the module file, but does it through the
module maintenance code within the Python
engine. This maintenance code also deals with
running initialising code. If you only wish to
take a Python script and execute the raw code
within the current session, you can use the
‘execfile(“filename.py”)’ command, where the
main option is a string containing the Python file
to load and execute. By default, any definitions
are loaded into the locals and globals of the
current session. You can optionally include
two extra parameters the execfile command.
These two options are both dictionaries, one
for a different set of locals and a different set of
globals. If you only hand in one dictionary, it is
assumed to be a globals dictionary. The return
valueofthiscommandisNone.
04
Reloading modules
When a module is first imported, any initialisation functions are run at that time. This may involve
creating data objects, or initiating connections. But, this is only done the first time within a given session.
Importing the same module again won’t re-execute any of the initialisation code. If you want to have this
codere-run,youneedtousethereloadcommand.Theformatis‘reload(modulename)’.Somethingtokeep
in mind is thatthedictionary from thepreviousimportisn’tdumped, but only written over. This means that
any definitions that have changed between the import and the reload are updated correctly. But if you
delete a definition, the old one will stick around and still be accessible. There may be other side effects, so
alwaysusewithcaution.
02
06
05
03
The Python Book 17
18 The Python Book
Reductions
In many calculations, one of the
computations you need to do is a reduction
operation. This is where you take some list of values
and reduce it down to a single value. In Python, you
can use the command ‘reduce(function, iterable)’ to
apply the reduction function to each pair of elements
in the list. For example, if you apply the summation
reduction operation to the list of the first five
integers, you would get the result ((((1+2)+3)+4)+5).
You can optionally add a third parameter to act as an
initialisation term. It is loaded before any elements
from the iterable, and is returned as a default if the
iterable is actually empty. You can use a lambda
function as the function parameter to reduce to keep
your code as tight as possible. In this case, remember
thatitshouldonlytaketwoinputparameters.
Virtualenvs
Because of the potential complexity of
the Python environment, it is sometimes best to
setupacleanenvironmentwithinwhichtoinstall
only the modules you need for a given project. In
this case, you can use the virtualenv command
to initialise such an environment. If you create
a directory named ‘ENV’, you can create a new
environment with the command ‘virtualenv
ENV’. This will create the subdirectories bin, lib
and include, and populate them with an initial
environment. You can then start using this new
environment by sourcing the script ‘ENV/bin/
activate’, which will change several environment
variables, such as the PATH. When you are done,
you can source the script ‘ENV/bin/deactivate’
to reset your shell’s environment back to its
previous condition. In this way, you can have
environments that only have the modules you
needforagivensetoftasks.
Mapping functions
A common task that is done in modern
programs is to map a given computation
to an entire list of elements. Python provides the
command ‘map()’ to do just this. Map returns a list of
the results of the function applied to each element of
an iterable object. Map can actually take more than
one function and more than one iterable object. If it
is given more than one function, then a list of tuples
is returned, with each element of the tuple containing
the results from each function. If there is more than
one iterable handed in, then map assumes that the
functions take more than one input parameter, so
it will take them from the given iterables. This has
the implicit assumption that the iterables are all of
the same size, and that they are all necessary as
parametersforthegivenfunction.
Loops
While not strictly commands, everyone needs
to know how to deal with loops. The two main
types of loops are a fixed number of iterations loop (for) and
a conditional loop (while). In a for loop, you iterate over some
sequence of values, pulling them off the list one at a time
andputtingtheminatemporaryvariable.Youcontinueuntil
either you have processed every element or you have hit a
breakcommand.Inawhileloop,youcontinuegoingthrough
the loop as long as some test expression evaluates to True.
While loops can also be exited early by using the break
command, you can also skip pieces of code within either
loop by using a continue command to selectively stop this
currentiterationandmoveontothenextone.
“While not strictly commands, everyone needs to
know how to deal with loops. The two main types
of loops are a fixed number of iterations loop (for)
and a conditional loop (while)”
12
Asserting values
At some point, we all need to debug
some piece of code we are trying to write. One
of the tools useful in this is the concept of an
assertion. The assert command takes a Python
expression and checks to see if it is true. If so,
then execution continues as normal. If it is not
true, then an AssertionError is raised. This way,
you can check to make sure that invariants
within your code stay invariant. By doing so,
you can check assumptions made within your
code. You can optionally include a second
parameter to the assert command. This second
parameter is Python expression that is executed
if the assertion fails. Usually, this is some type of
detailed error message that gets printed out. Or,
you may want to include cleanup code that tries
torecoverfromthefailedassertion.
07
Filtering
Where the command map returns a result for every element in an iterable, filter only returns a
result if the function returns a True value. This means that you can create a new list of elements where
only the elements that satisfy some condition are used. As an example, if your function checked that
the values were numbers between 0 and 10, then it would create a new list with no negative numbers
and no numbers above 10. This could be accomplished with a for loop, but this method is much
cleaner. If the function provided to filter is ‘None’, then it is assumed to be the identity function. This
means that only those elements that evaluate to True are returned as part of the new list. There are
iterable versions of filter available in the itertools module.
11
09
08
10
The Python Book 19
50 Python commands
Enumerating
Sometimes,weneedtolabeltheelements
that reside within an iterable object with their
indices so that they can be processed at some later
point. You could do this by explicitly looping through
each of the elements and building an enumerated
list. The enumerate command does this in one line.
It takes an iterable object and creates a list of tuples
as the result. Each tuple has the 0-based index of
the element, along with the element itself. You can
optionally start the indexing from some other value
by including an optional second parameter. As an
example, you could enumerate a list of names with
the command ‘list(enumerate(names, start=1))’. In
this example, we decided to start the indexing at 1
insteadof0.
Casting
Variables in Python don’t have any type
information, and so can be used to store
any type of object. The actual data, however, is of
one type or another. Many operators, like addition,
assume that the input values are of the same type.
Very often, the operator you are using is smart
enough to make the type of conversion that is
needed. If you have the need to explicitly convert
your data from one type to another, there are a class
of functions that can be used to do this conversion
process. The ones you are most likely to use is ‘abs’,
‘bin’, ‘bool’, ‘chr’, ‘complex’, ‘float’, ‘hex’, ‘int’, ‘long’,
‘oct’, and ‘str’. For the number-based conversion
functions, there is an order of precedence where
some types are a subset of others. For example,
integers are “lower” than floats. When converting
up, no changes in the ultimate value should happen.
When converting down, usually some amount of
information is lost. For example, when converting
from float to integer, Python truncates the number
towardszero.
14
15
How true is a list?
In some cases, you may have collected a number of elements within a list that can be evaluated
to True or False. For example, maybe you ran a number of possibilities through your computation and
have created a list of which ones passed. You can use the command ‘any(list)’ to check to see whether
any of the elements within your list are true. If you need to check whether all of the elements are True,
you can use the command ‘all(list)’. Both of these commands return a True if the relevant condition is
satisfied, and a False if not. They do behave differently if the iterable object is empty, however. The
command ‘all’ returns a True if the iterable is empty, whereas the command ‘any’ returns a False when
givenanyemptyiterable.
13
What is this?
Everything in Python is an object. You can
check to see what class this object is an instance
of with the command ‘isinstance(object, class)’.
This command returns a Boolean value.
16
Is it a subclass?
The command ‘issubclass(class1, class2)’
checks to see if class1 is a subclass of class2. If
class1 and class2 are the same, this is returned
as True.
17
Global objects
You can get a dictionary of the global
symbol table for the current module with the
command ‘globals()’.
18
Local objects
You can access an updated dictionary
of the current local symbol table by using the
command ‘locals()’.
19
Variables
The command ‘vars(dict)’ returns writeable
elements for an object. If you use ‘vars()’, it
behaves like ‘locals()’.
20
Making a global
A list of names can be interpreted as
globals for the entire code block with the
command ‘global names’.
21
Nonlocals
In Python 3.X, you can access names from
the nearest enclosing scope with the command
‘nonlocal names’ and bind it to the local scope.
22
Raising an exception
When you identify an error condition,
you can use the ‘raise’ command to throw up an
exception. You can include an exception type and
a value.
23
Dealing with an exception
Exceptions can be caught in a try-except
construction. If the code in the try block raises an
exception, the code in the except block gets run.
24
Static methods
You can create a statis method, similar
to that in Java or C++, with the command
‘staticmethod(function_name)’.
25
20 The Python Book
Printing
The most direct way of getting output
to the user is with the print command.
This will send text out to the console window. If you
are using version 2.X of Python, there are a couple
of ways you can use the print command. The most
common way had been simply call it as ‘print
“Some text”’. You can also use print with the same
syntax that you would use for any other function.
So, the above example would look like ‘print(“Some
text”)’. This is the only form available in version 3.X.
If you use the function syntax, you can add extra
parameters that give you finer control over this
output. For example, you can give the parameter
‘file=myfile.txt’ and get the output from the print
command being dumped into the given text file.
It also will accept any object that has some string
representation available.
With modules
The ‘with’ command provides the ability to
wrap a code block with methods defined
by a context manager. This can help clean up code
and make it easier to read what a given piece of
code is supposed to be doing months later. A classic
example of using ‘with’ is when dealing with files.
You could use something like ‘with open(“myfile.
txt”, “r”) as f:’. This will open the file and prepare it for
reading. You can then read the file in the code block
with ‘data=f.read()’. The best part of doing this is that
the file will automatically be closed when the code
block is exited, regardless of the reason. So, even if
the code block throws an exception, you don’t need to
worry about closing the file as part of your exception
handler. If you have a more complicated ‘with’
example, you can create a context manager class to
helpout.
32
31
Memoryview
Sometimes, you need to access the raw data of some object, usually as a buffer of bytes. You
can copy this data and put it into a bytearray, for example. But this means that you will be using extra
memory, and this might not be an option for large objects. The command ‘memoryview(object_name)’
wraps the object handed in to the command and provides an interface to the raw bytes. It gives access
to these bytes an element at a time. In many cases, elements are the size of one byte. But, depending
on the object details, you could end up with elements that are larger than that. You can find out the size
of an element in bytes with the property ‘itemsize’. Once you have your memory view created, you can
accesstheindividualelementsasyouwouldgetelementsfromalist(mem_view[1],forexample).
33
“A classic example of using ‘with’ is when dealing
with files. The best part of doing this is that the
file will automatically be closed when the code
block is exited, regardless of the reason”
Ranges
You may need a list of numbers, maybe in
a ‘for’ loop. The command ‘range()’ can create an
iterable list of integers. With one parameter, it
goes from 0 to the given number. You can provide
an optional start number, as well as a step size.
Negative numbers count down.
Xranges
One problem with ranges is that all of the
elements need to be calculated up front and
stored in memory. The command ‘xrange()’ takes
the same parameters and provides the same
result, but only calculates the next element as it
is needed.
27
Iterators
Iteration is a very Pythonic way of doing
things. For objects which are not intrinsically
iterable, you can use the command ‘iter(object_
name)’ to essentially wrap your object and provide
an iterable interface for use with other functions
and operators.
28
Sorted lists
You can use the command ‘sorted(list1)’
to sort the elements of a list. You can give it
a custom comparison function, and for more
complex elements you can include a key function
that pulls out a ranking property from each
element for comparison.
29
Summing items
Above, we saw the general reduction
function reduce. A specific type of reduction
operation, summation, is common enough to
warrant the inclusion of a special case, the
command ‘sum(iterable_object)’. You can include
a second parameter here that will provide a
starting value.
30
26
The Python Book 21
50 Python commands
Threads
You can do multiple threads of execution
within Python. The ‘thread()’ command can create a
new thread of execution for you. It follows the same
techniques as those for POSIX threads. When you first
create a thread, you need to hand in a function name,
along with whatever parameters said function needs.
One thing to keep in mind is that these threads behave
just like POSIX threads. This means that almost
everythingistheresponsibilityoftheprogrammer.You
needtohandlemutexlocks(withthemethods‘acquire’
and ‘release’), as well as create the original mutexes
with the method ‘allocate_lock’. When you are done,
youneedto‘exit’thethreadtoensurethatitisproperly
cleaned up and no resources get left behind. You also
have fine-grained control over the threads, being able
tosetthingslikethestacksizefornewthreads.
Shelving data
While pickling allows you save data and
reload it, sometimes you need more structured
object permanence in your Python session. With the
shelve module, you can create an object store where
essentially anything that can be pickled can be stored
there. The backend of the storage on the drive can be
handled by one of several systems, such as dbm or
gdbm.Onceyouhaveopenedashelf,youcanreadand
writetoitusingkeyvaluepairs.Whenyouaredone,you
need to be sure to explicitly close the shelf so that it is
synchronised with the file storage. Because of the way
the data may be stored in the backing database, it is
besttonotopentherelevantfilesoutsideoftheshelve
module in Python. You can also open the shelf with
writeback set to True. If so, you can explicitly call the
syncmethodtowriteoutcachedchanges.
Pickling data
There are a few different ways of
serialisingmemorywhenyouneedtocheckpoint
results to disk. One of these is called pickling.
Pickle is actually a complete module, not just a
single command. To store data on to the hard
drive, you can use the dump method to write
the data out. When you want to reload the same
data at some other point in the future, you can
use the load method to read the data in and
unpickle it. One issue with pickle is its speed, or
lack of it. There is a second module, cPickle, that
provides the same basic functionality. But, since
it is written in C, it can be as much as 1000 times
faster.Onethingtobeawareofisthatpickledoes
not store any class information for an object,
but only its instance information. This means
that when you unpickle the object, it may have
different methods and attributes if the class
definitionhaschangedintheinterim.
Weak references
You sometimes need to have a reference
to an object, but still be able to destroy it if
needed. A weak reference is one which can
be ignored by the garbage collector. If the only
references left to n object are weak references,
then the garbage collector is allowed to destroy
that object and reclaim the space for other
uses. This is useful in cases where you have
caches or mappings of large datasets that
don’t necessarily have to stay in memory. If an
object that is weakly referenced ends up being
destroyed and you try to access it, it will appear
as a None. You can test for this condition and
then reload the data if you decide that this is a
necessarystep.
Yielding
In many cases, a function may need to
yield the context of execution to some other
function.Thisisthecasewithgenerators.Thepreferred
method for a generator is that it will only calculate the
next value when it is requested through the method
‘next()’. The command ‘yield’ saves the current state of
the generator function, and return execution control
to the calling function. In this way, the saved state of
the generator is reloaded and the generator picks up
where it left off in order to calculate the next requested
value.Inthisway,youonlyneedtohaveenoughmemory
available to store the bare minimum to calculate the
next needed value, rather than having to store all of the
possiblevaluesinmemoryallatonce.
39
38
Files
When dealing with files, you need to create a file object to interact with it. The file command takes
a string with the file name and location and creates a file object instance. You can then call the file object
methods like ‘open’, ‘read’ and ‘close’, to get data out of the file. If you are doing file processing, you can
also use the ‘readline’ method. When opening a file, there is an explicit ‘open()’ command to simplify the
process. It takes a string with the file name, and an optional parameter that is a string which defines the
mode. The default is to open the file as read-only (‘r’). You can also open it for writing (‘w’) and appending
(‘a’).Afteropeningthefile,afileobjectisreturnedsothatyoucanfurtherinteractwithit.Youcanthenread
it,writetoit,andfinallycloseit.
34
37
36
35
22 The Python Book
50 Python commands
Slices
While not truly a command, slices are
too important a concept not to mention in this
list of essential commands. Indexing elements
in data structures, like lists, is one of the most
common things done in Python. You can select a
single element by giving a single index value. More
interestingly, you can select a range of elements by
giving a start index and an end index, separated by
a colon. This gets returned as a new list that you can
save in a new variable name. You can even change
the step size, allowing you to skip some number of
elements. So, you could grab every odd element from
the list ‘a’ with the slice ‘a[1::2]’. This starts at index 1,
continues until the end, and steps through the index
values 2 at a time. Slices can be given negative index
values. If you do, then they start from the end of the
listandcountbackwards.
43
Inputting data
Sometimes, you need to collect input
from an end user. The command ‘input()’ can
take a prompt string to display to the user, and
then wait for the user to type a response. Once
theuserisdonetypingandhitstheenterkey,the
text is returned to your program. If the readline
module was loaded before calling input, then
you will have enhanced line editing and history
functionality. This command passes the text
through eval first, and so may cause uncaught
errors. If you have any doubts, you can use the
command ‘raw_input()’ to skip this problem. This
command simply returns the unchanged string
inputted by the user. Again, you can use the
readlinemoduletogetenhancedlineediting.
40
Comparing objects
There are several ways to compare objects within Python, with several caveats. The first is that
you can test two things between objects: equality and identity. If you are testing identity, you are testing
to see if two names actually refer to the same instance object. This can be done with the command
‘cmp(obj1, obj2)’. You can also test this condition by using the ‘is’ keyword. For example, ‘obj1 is obj2’. If
you are testing for equality, you are testing to see whether the values in the objects referred to by the
two names are equal. This test is handled by the operator ‘==’, as in ‘obj1 == obj2’. Testing for equality
canbecomecomplexformorecomplicatedobjects.
42
Internal variables
Forpeoplecomingfromotherprogramminglanguages,thereisaconceptofhavingcertainvariables
or methods be only available internally within an object. In Python, there is no such concept. All elements of an
object are accessible. There is a style rule, however, that can mimic this type of behaviour. Any names that start
with an underscore are expected to be treated as if they were internal names and to be kept as private to the
object.Theyarenothidden,however,andthereisnoexplicitprotectionforthesevariablesormethods.Itisupto
theprogrammertohonourtheintentionfromtheauthortheclassandnotalteranyoftheseinternalnames.You
arefreetomakethesetypesofchangesifitbecomesnecessary,though.
41
The Python Book 23
50 Python commands
“Python is an interpreted language, which means
that the source code that you write needs to be
compiled into a byte code format. This byte code
then gets fed into the actual Python engine”
Lambda expressions
Since objects, and the names that point to them, are truly different things, you can have objects
thathavenoreferencestothem.Oneexampleofthisisthelambdaexpression.Withthis,youcancreate
an anonymous function. This allows you use functional programming techniques within Python. The
format is the keyword ‘lambda’, followed by a parameter list, then a colon and the function code. For
example,youcouldbuildyourownfunctiontosquareanumberwith‘lambdax:x*x’.Youcanthenhavea
function that can programmatically create new functions and return them to the calling code. With this
capability, you can create function generators to have self-modifying programs. The only limitation is
thattheyarelimitedtoasingleexpression,soyoucan’tgenerateverycomplexfunctions.
44
__del__ method
When an instance object is about to be
destroyed, the __del__ method is called. This
gives you the chance to do any kind of cleanup
that may be required. This might be closing files,
or disconnecting network connections. After this
code is completed, the object is finally destroyed
and resources are freed.
47
Return values
Functions may need to return some value
to the calling function. Because essentially no
name has a type, this includes functions. So
functions can use the ‘return’ command to return
any object to the caller.
49
String concatenation
We will finish with what most lists start
with – string concatenation. The easiest way to
build up strings is to use the ‘+’ operator. If you
want to include other items, like numbers, you
can use the ‘str()’ casting function to convert it to
a string object.
50
Exiting your program
There are two pseudo-commands
available to exit from the Python interpreter:
‘exit()’ and quit()’. They both take an optional
parameter which sets the exit code for the
process. If you want to exit from a script, you are
better off using the exit function from the sys
module (‘sys.exit(exit_code)’.
48
__init__ method
When you create a new class, you can
include a private initialisation method that
gets called when a new instance of the class is
created. This method is useful when the new
object instance needs some data loaded in the
new object.
46
Compiling
code objects
Python is an interpreted
language, which means that the source
codethatyouwriteneedstobecompiled
into a byte code format. This byte code
then gets fed into the actual Python engine
to step through the instructions. Within your program, you may
have the need to take control over the process of converting
code to byte code and running the results. Maybe you wish to
build your own REPL. The command ‘compile()’ takes a string
object that contains a collection of Python code, and returns
an object that represents a byte code translation of this code. This
new object can then be handed in to either ‘eval()’ or ‘exec()’ to be actually
run. You can use the parameter ‘mode=’ to tell compile what kind of code is being
compiled. The ‘single’ mode is a single statement, ‘eval’ is a single expression and
‘exec’isawholecodeblock.
45
Essentials
Python
26 Code rock, paper, scissors
Put basic coding into action
32 Program a hangman game
Use Python to make the classic game
38 Play poker dice
Test your luck and your coding
44 Create a graphical interface
Add interface to your projects
“Gettogripswith
Pythonandstart
buildingonthe
basicswiththese
expertguides”
56
24 The Python Book
50 Bring graphics to games
Add images to simple games
56 Build an app for Android
Make your own app with Kivy
62 Making web apps
Use Python to create online apps
66 50 Python tips
Essential knowledge for Python users
50
The Python Book 25
44
26 The Python Book
Python essentials
LearnhowtodosomebasicPythoncodingbyfollowing
ourbreakdownofasimplerock,paper,scissorsgame
Codeagameof
rock,paper,scissors
This tutorial will guide you through making
a rock, paper, scissors game in Python. The
codeappliesthelessonsfromthemasterclass–
and expands on what was included there – and
doesn’trequireanyextraPythonmodulestorun,
likePygame.
Rock, paper, scissors is the perfect game to
show off a little more about what exactly Python
can do. Human input, comparisons, random
selections and a whole host of loops are used in
making a working version of the game. It’s also
easy enough to adapt and expand as you see
fit, adding rules and results, and even making a
rudimentaryAIifyouwish.
For this particular tutorial, we also
recommend using IDLE. IDLE is a great Python
IDE that is easily obtainable in most Linux
distributions and is available by default on
Raspbian for Raspberry Pi. It helps you by
highlighting any problems there might be with
your code and allows you to easily run it to make
sureit’sworkingproperly.
Resources
Python2:www.python.org/download
IDLE:www.python.org/idle
AllowthePythonscript
toruninaterminal,
andoutsidetheIDE
Humaninputintheform
ofintegersisusedfor
comparingmovesand,
ultimately,playingthegame
Usedeductionto
determineoneof
threeoutcomes
Loopthecodeover
againandstart
fromthebeginning
Appendtointeger
variablestokeeptrack
ofscoresandmore
The Python Book 27
Python essentials
01This section imports the extra Python
functionswe’llneedforthecode–they’re
still parts of the standard Python libraries, just
notpartofthedefaultenvironment
02The initial rules of the game are created
here.Thethreevariableswe’reusingand
their relationship is defined. We also provide a
variablesowecankeepscoreofthegames
03We begin the game code by defining the
startofeachround.Theendofeachplay
session comes back through here, whether we
wanttoplayagainornot
04The game is actually contained all in
here, asking for the player input, getting
the computer input and passing these on to get
theresults.Attheendofthat,itthenasksifyou’d
liketoplayagain
05Player input is done here. We give the
player information on how to play this
particular version of the game and then allow
their choice to be used in the next step. We also
have something in place in case they enter an
invalidoption
06There are a few things going on when we
showtheresults.First,we’reputtingina
delay to add some tension, appending a variable
to some printed text, and then comparing what
the player and computer did. Through an if
statement, we choose what outcome to print,
andhowtoupdatethescores
07We now ask for text input on whether
or not someone wants to play again.
Depending on their response, we go back to the
start,orendthegameanddisplaytheresults
28 The Python Book
Python essentials
01We need to start with the path to the
Python interpreter here. This allows
us to run the program inside a terminal or
otherwise outside of a Python-specific IDE
like IDLE. Note that we’re also using Python 2
rather than Python 3 for this particular script,
which needs to be specified in the code to
make sure it calls upon the correct version
fromthesystem.
03We’re setting each move to a specific
number so that once a selection is
made by the player during the game, it will be
equated to that specific variable. This makes
the code slightly easier later on, as we won’t
need to parse any text for this particular
function.Ifyousowish,youcanaddadditional
moves,andthiswillstarthere.
05Similar to the way the text names of
thevariablesaredefinedandusedonly
when needed, the rules are done in such a way
that when comparing the results, our variables
are momentarily modified. Further down in the
code we’ll explain properly what’s happening,
but basically after determining whether or
not there’s a tie, we’ll see if the computer’s
move would have lost to the player move. If the
computer move equals the losing throw to the
player’s move, you win.
02We’reimportingtwoextramoduleson
top of the standard Python code so
we can use some extra functions throughout
the code. We’ll use the random module to
determinewhatmovethecomputerwillthrow,
and the time module to pause the running of
the code at key points. The time module can
also be used to utilise dates and times, either
todisplaythemorotherwise.
04Here we specify the rules for the game,
and the text representations of each
move for the rest of the code. When called upon,
our script will print the names of any of the three
moves,mainlytotelltheplayerhowthecomputer
moved. These names are only equated to these
variables when they are needed – this way, the
number assigned to each of them is maintained
whileit’sneeded.
06Very simply, this creates a variable that
can be used throughout the code to
keep track of scores. We need to start it at zero
now so that it exists, otherwise if we defined
it in a function, it would only exist inside that
function. The code adds a point to the computer
orplayerdependingontheoutcomeoftheround,
although we have no scoring for tied games in
thisparticularversion.
Thebreakdown
There are other modules you can import with
basic Python. Some of the major ones are
shown to the right. There are also many more
that are included as standardwith Python.
Pythonmodules string Perform common string operations
datetime andcalendar Other modules related to time
math Advanced mathematical functions
json JSON encoder and decoder
pydoc Documentation generator and online help system
01
02
03
04
05
06
The Python Book 29
Python essentials
07Here we define the actual beginning of the code, with the function
we’ve called ‘start’. It’s quite simple, printing our greeting to the
player and then starting a while loop that will allow us to keep playing the
game as many times as we wish. The pass statement allows the while loop
tostoponcewe’vefinished,andcouldbeusedtoperformanumberofother
tasksifsowished.Ifwedostopplayingthegame,thescorefunctionisthen
calledupon–we’llgooverwhatthatdoeswhenwegettoit.
09We start the move function off by putting it into
a while loop. The whole point of move is to obtain
an integer between one and three from the player, so the
while loop allows us to account for the player making an
unsupported entry. Next, we are setting the player variable
to be created from the player’s input with raw_input. We’ve
alsoprintedinstructiontexttogoalongwithit.The‘n’we’ve
used in the text adds a line break; this way, the instructions
appearasalist.
10The try statement is used to clean up code and
handleerrorsorotherexceptions.Weparsewhatthe
player entered by turning it into an integer using int(). We use
the if statement to check if it is either 1, 2, or 3 – if it is, move
returns this value back up to the game function. If it throws
upaValueError,weuseexcepttodonothing.Itprintsanerror
message and the while loop starts again. This will happen
until an acceptable move is made.
08We’ve kept the game function fairly simple so we can break down
eachstepabitmoreeasilyinthecode.Thisiscalleduponfromthe
start function, and first of all determines the player move by calling upon
the move function below. Once that’s sorted, it sets the computer move. It
uses the random module’s randint function to get an integer between one
and three (1, 3). It then passes the player and computer move, stored as
integers,ontotheresultfunctionwhichweusetofindtheoutcome.
07
08
09
10
Thecodeinaction
30 The Python Book
Python essentials
11Theresultfunctiononlytakesthevariables
playerandcomputerforthistask,whichis
why we set that in result(player, computer). We’re
starting off by having a countdown to the result.
The printed numbers are self-explanatory, but
we’ve also thrown in sleep from the time module
we imported. Sleep pauses the execution of the
lookupwhatthetextversionofthemoveiscalled
from the names we set earlier on, and then to
insertthatwhere{0}is.
13Here we’re simply calling the scores we
set earlier. Using the global function
allows for the variable to be changed and used
outside of the variable, especially after we’ve
appendedanumbertooneoftheirscores.
15Ifit’snotatie,weneedtokeepchecking,
as it could still be a win or a loss. Within
the else, we start another if statement. Here,
we use the rules list from earlier to see if the
losing move to the player’s move is the same
as the computer’s. If that’s the case, we print
the message saying so, and add one to the
player_scorevariablefrombefore.
code by the number of seconds in the brackets.
We’ve put a one-second pause between counts,
thenhalfasecondafterthattoshowtheresults.
12To print out what the computer threw,
we’re using string.format(). The {0} in the
printed text is where we’re inserting the move,
which we have previously defined as numbers.
Usingnames[computer],we’retellingthecodeto
14The way we’re checking the result is
basicallythroughaprocessofelimination.
Our first check is to see if the move the player
and computer used were the same, which is the
simplest part. We put it in an if statement so that
if it’s true, this particular section of the code ends
here.Itthenprintsourtiemessageandgoesback
tothegamefunctionforthenextstep.
16If we get to this point, the player has lost.
We print the losing message, give the
computer a point and it immediately ends the
resultfunction,returningtothegamefunction.
11
12
13
14
15
16
Thecodeinaction
The Python Book 31
Python essentials
17The next section of game calls upon
a play_again function. Like the move
function, we have human input, asking the player
if they would like to play again via a text message
withraw_input,withthesimple‘y/n’suggestionin
anattempttoelicitanexpectedresponse.
19Ifwedon’tgetanexpectedresponse,we
will assume the player does not want to
play again. We’ll print a goodbye message, and
that will end this function. This will also cause
the game function to move onto the next section
andnotrestart.
18Giving users an option of y/n like we have
should expect a response in kind. The
if statement checks to see if any of our defined
positiveresponseshavebeenentered.AsPython
doesn’t differentiate between upper or lower
case, we’ve made sure that it accepts both y and
Y.Ifthisisthecase,itreturnsapositiveresponse
togame,whichwillstartitagain.
20Going back to the start function, after
gamefinisheswemoveontotheresults.
This section calls the scores, which are integers,
andthenprintsthemindividuallyafterthenames
of the players. This is the end of the script, as far
as the player is concerned. Currently, the code
won’t permanently save the scores, but you can
havePythonwriteittoafiletokeepifyouwish.
21The final part allows for the script to
be used in two ways. Firstly, we can
execute it in the command line and it will work
fine. Secondly, we can import this into another
Python script, perhaps if you wanted to add it as
a game to a collection. This way, it won’t execute
thecodewhenbeingimported.
IFalsohastheELIF(elseif)operator,whichcan
be used in place of the second IF statement
we employed. It’s usually used to keep code
clean,butperformsthesamefunction.
ELIF
17
18
19
20
21
Thecodeinaction
32 The Python Book
Python essentials
ThissectionimportstheextraPython
functions we’ll need for the code –
they’re still parts of the standard
Python libraries, just not part of the
defaultenvironment
We’re again providing variables so we
can keep score of the games played,
andthey’reupdatedeachround
Our very basic graphics involve ASCII
art of the game’s stages, printed out
aftereveryturn
LearnhowtodosomemorePython
codingbyfollowingourbreakdownofa
simpleHangmangame
Programa
gameof
Hangman
One of the best ways to get to know Python is
by building lots of simple projects so you can
understand a bit more about the programming
language. This time round, we’re looking at
Hangman, a multi-round game relying on if
and while loops and dealing with strings of text
in multiple ways. We’ll be using some of the
techniquesweimplementedlasttimeaswell,so
wecanbuilduponthem.
Hangman still doesn’t require the Pygame
set of modules, but it’s a little more advanced
than rock-paper-scissors. We’re playing
around with a lot more variables this time.
However, we’re still looking at comparisons,
random selections and human input, along
with splitting up a string, editing a list and even
displaying rudimentary graphics.
You should continue to use IDLE for these
tutorials. As we’ve mentioned before, its built-
in debugging tools are simple yet effective and
it can be used on any Linux system, as well as
the Raspberry Pi.
Codelisting
#!/usr/bin/env python2
from random import *
player_score = 0
computer_score = 0
def hangedman(hangman):
graphic = [
“””
+-------+
|
|
|
|
|
==============
“””,
“””
+-------+
| |
| O
|
|
|
===============
“””,
“””
“””,
“””
+-------+
| |
| O
| -|-
| / 
|
===============
“””]
print graphic[hangman]
return
Resources
Python2:www.python.org/download
IDLE:www.python.org/idle
The Python Book 33
Python essentials
The actual game starts here, with a while loop to
let you continually play the game until you decide
otherwise, thenending the program
The game rules are decided here, as well as the
setup for the word and keeping track of tries and
incorrectanswers
Each round of the game is played here, asking for
aninput,thentellingyouifyouwerecorrectornot.
It prints out the graphic and changes any variables
that need to be updated, especially incorrect and
correctguesses
After each round, the code checks if you’ve won or
lostyet–thewinconditionbeingthatyouguessed
theword,orlosingifyou’ve made sixguesses
The human input for the game takes the letter
and turns it into something the code can use. It’s
verified in the previous block of code and then
referred back to if you’ve entered an unsupported
or already used character
The same class as last time, which allows you to
selectwhether or not you wishto play again
Upon quitting the game, scores are given for the
durationoftheplaysession.Wealsoendthescript
withtheif__name__code like before
def start():
print “Let’s play a game of Linux Hangman.”
while game():
pass
scores()
def game():
dictionary = [“gnu”,”kernel”,”linux”,”mageia”,”penguin”,”ubuntu”]
word = choice(dictionary)
word_length = len(word)
clue = word_length * [“_”]
tries = 6
letters_tried = “”
guesses = 0
letters_right = 0
letters_wrong = 0
global computer_score, player_score
while (letters_wrong != tries) and (“”.join(clue) != word):
letter=guess_letter()
if len(letter)==1 and letter
.isalpha():
if letters_tried.find(letter) != -1:
print “You’ve already picked”, letter
else:
letters_tried = letters_tried + letter
first_index=word.find(letter)
if first_index == -1:
letters_wrong +=1
print “Sorry,”,letter
,”isn’t what we’re looking for
.”
else:
print”Congratulations,”,letter
,”is correct.”
for i in range(word_length):
if letter == word[i]:
clue[i] = letter
else:
print “Choose another
.”
hangedman(letters_wrong)
print “ “.join(clue)
print “Guesses: “, letters_tried
if letters_wrong == tries:
print “Game Over
.”
print “The word was”,word
computer_score += 1
break
if “”.join(clue) == word:
print “You Win!”
print “The word was”,word
player_score += 1
break
return play_again()
def guess_letter():
print
letter = raw_input(“Take a guess at our mystery word:”)
letter
.strip()
letter
.lower()
print
return letter
def play_again():
answer = raw_input(“Would you like to play again? y/n: “)
if answer in (“y”, “Y”, “yes”, “Yes”, “Of course!”):
return answer
else:
print “Thank you very much for playing our game. See you next time!”
def scores():
global player_score, computer_score
print “HIGH SCORES”
print “Player: “, player_score
print “Computer: “, computer_score
if __name__ == ‘__main__’:
start()
Codehighlighting
IDLEautomaticallyhighlightsthecodetomake
reading your work that bit easier. It also allows
youtochangethesecoloursandhighlightingin
IDLE’s Preferences, in case you’re colour blind
or are just used to a different colour scheme
in general.
Codelistingcontinued
34 The Python Book
Python essentials
IseeASCII
Here’s a close-up of the seven
stages we’ve used for Hangman’s
graphics. You can change them
yourself, but you need to make
sure the quote marks are all in
the correct place so that the art
is considered a text string to be
printedout.
#!/usr/bin/env python2
from random import *
player_score = 0
computer_score = 0
def hangedman(hangman):
graphic = [
“””
+-------+
|
|
|
|
|
==============
“””,
“””
def start():
print “Let’s play a game of Linux Hangman.”
while game():
pass
scores()
01
02
03
04
05
01We begin by using this line to enter the path
to the Python interpreter. This allows us to
run the program inside a terminal or otherwise outside
of a Python-specific IDE like IDLE. Note that we’re
also using Python 2 for this particular script, as it is
installed by default on most Linux systems and will
thereforeensurecompatibility.
02We’re importing the ‘random’ module slightly
differently this time, importing the actual
names of the functions from random rather than just
the module itself. This allows us to use the functions
without having syntax like random.function. The
asterisk imports all the functions from random,
although you can switch that for specific names of
any of random’s functions. We’ll be using the random
functiontoselectawordfortheplayertoguess.
03Very simply, this creates a variable that can
be used throughout the code to keep track
of scores. We need to start it at zero now so that it
exists; otherwise if we defined it in a function, it would
only exist inside that function. The code adds a point
to the computer or player depending on the outcome
of the round.
04Our simple graphics consist of a series of
ASCII hanging man stages. We’re storing
these in a function as a list of separate string objects
so we can call upon them by passing on the number of
incorrect guesses to it. There are seven graphics in all,
like in the pen-and-paper version. We also include the
print command with the function, so when it’s called it
will completely handle the selection and display of the
hanging man, with the first one being printed after the
firstletterisguessed.
05Here we define the actual beginning of the
code, with the function we’ve called ‘start’.
It’s quite simple, printing our greeting to the player
and then starting a while loop that will allow us to keep
playing the game as many times as we wish. The pass
statement allows the while loop to stop once we’ve
finished, and could be used to perform a number
“””
+-------+
|
|
|
|
|
==============
“””,
“””
+-------+
| |
| O
|
|
|
===============
“””,
“””
+-------+
| |
| O
| |
|
|
===============
“””,
“””
+-------+
| O
| -|
|
|
|
===============
“””,
“””
+-------+
| |
| O
| -|-
|
|
===============
“””,
“””
+-------+
| |
| O
| -|-
| /
|
===============
“””,
“””
+-------+
| |
| O
| -|-
| / 
|
===============
“””]
Therules
Although we’ve moved some of
the rules to the ‘game’ function
this month, you can always put
them back here and call upon
them using the global variable, as
we would do with the scores. For
thewords,youcouldalsocreatea
separatefileandimportthemlike
therandom module.
The Python Book 35
Python essentials
of other tasks if so wished. If we do stop playing the
game, the score function is then called upon –we’ll go
overwhatthatdoeswhenwegettoit.
06We have put a majority of the game code
in the ‘game’ function this time around, as
there’s not as much that needs to be split up. You can
split it up further if you wish, using the style of code
from last issue, if it would make the code cleaner
for you or help you understand the building blocks a
bitmore.
07The first four lines quickly set up the word
for the player to guess. We’ve got a small
selection of words in a list here. However, these can be
imported via HTML or expanded upon. Choice is used
to select a random element from the list, which comes
from the random module we imported. Finally, we
ascertain how long the string is of the word to guess,
and then create the clue variable with a number of
underscores of that length. This is used to display the
wordasyoubuilditupfromguesses.
08We start to set up the rules and the individual
variables to keep track of during the game.
There can only be six incorrect guesses before the
hanging man is fully drawn, or in our case displayed,
so we set the tries variable to six. We’ll keep track of
the letters through letters_tried to make sure that not
only will the player know, but also the code for when
def game():
dictionary = [“gnu”,”kernel”,”linux”,”mageia”,”penguin”,”ubuntu”]
word = choice(dictionary)
word_length = len(word)
clue = word_length * [“_”]
tries = 6
letters_tried = “”
guesses = 0
letters_right = 0
letters_wrong = 0
global computer_score, player_score
while (letters_wrong != tries) and (“”.join(clue) != word):
letter=guess_letter()
if len(letter)==1 and letter
.isalpha():
if letters_tried.find(letter) != -1:
print “You’ve already picked”, letter
it’s checking against letters already played. Finally,
we create empty variables for the number of guesses
made, letters correct and letters incorrect, to make
the code slightly easier. We also import the global
scoreshere.
09We’re starting a while loop to perform the
player selection and check the status of the
game. This loop continues until the player wins or loses.
It starts by checking if all the tries have been used up
by seeing if letters_wrong is not equal to tries. As each
try will only add one point to wrong, it will never go
above six. It then concatenates ‘clue’ and sees if it’s the
same as the word the computer selected. If both these
statementsaretrue,itgoesontothenextturn.
10We call upon the function we’re using to
input a letter and give it the variable ‘letter’.
We check what it returns by first of all making sure
it’s only a single letter, with len(letter), then by
using isalpha to see if it’s one of the 26 letters of the
alphabet. If these conditions are satisfied, we start
a new if statement to make sure it’s a new guess,
and tell the player if it’s already been chosen so they
can start again. If all this is acceptable, we move on
to the next section of the code to see if it’s a correct
guess or not.
06
08
09
10
07
Indentations
While IDLE will keep track of the
indentsinthecode,ifyou’reusing
atexteditortowritesomePython,
you’ll have to make sure you’re
using them correctly. Python is
very sensitive to whether or not
indents are used correctly, and it
doesaid in readability aswell.
36 The Python Book
Python essentials
11
12
13
14
16
15
11If it’s a new letter that we find acceptable,
the first thing we do is add it to the list
of letters tried. This is done simply by adding
the strings together. We then use the find
command to search the word string for the letter
entered, which will then return a number of the
placement of the letter in the string. If it doesn’t
find the letter, it returns a -1 value, which we use
in the next if statement to see if the first_index
variable is -1. If so, it adds one to the number of
letters_wrong and then prints a message to let
theplayerknowthatitwasanincorrectguess.
12If we’ve got this far and the letter is not
incorrect, than we can only assume
it is correct. Through this simple process of
elimination, we first print out a message to let
theplayerknowthatthey’vebeensuccessfuland
thenmakearecordofit.
13We’re going to start a small loop here so
we can update the clue with the correct
letter we’ve added. We use the range function to
tell the code how many times we wish to iterate
over the clue by using the word_length variable.
We then check to see which letter in the word
has been guessed correctly and change that
specific part of the clue to be that letter so it can
be printed out for the player to see, and for us to
checkwhetherornotthegameisover.
14We end the original if statement by telling
the player to choose again if they did not
enter a supported input. Before we go on to the
next round of choices, we print out the hanging
else:
letters_tried = letters_tried + letter
first_index=word.find(letter)
if first_index == -1:
letters_wrong +=1
print “Sorry,”
,letter
,”isn’t what we’re looking for
.”
else:
print”Congratulations,”
,letter
,”is correct.”
for i in range(word_length):
if letter == word[i]:
clue[i] = letter
else:
print “Choose another
.”
hangedman(letters_wrong)
print “ “.
join(clue)
print “Guesses: “, letters_tried
if letters_wrong == tries:
print “Game Over
.”
print “The word was”
,word
computer_score += 1
break
if “”
.
join(clue) == word:
print “You Win!”
print “The word was”
,word
player_score += 1
break
return play_again()
man graphic as it stands, by calling the graphic
in the list that corresponds to the number of
incorrect guesses that have been made. We then
print how the clue currently looks, with a space
in between each character, and then print the
numberofguessesthathavebeenmade.
15Here we check to see if the game is
over again, first of all comparing the
letters_wrong to the number of tries. If that’s
true, we print a message that the game has
endedandrevealthemysteryofthehiddenword.
We increase the computer’s score and break the
loop. The next loop checks to see if the full clue
concatenatedisthesameastheoriginalword–if
that’sthecase,weprintthewinmessage,thefull
wordandaddonepointtotheplayerscorebefore
breaking the loop again. This can also be done
withifsandelifstoavoidusingbreaks.
Continuation
This code is still part of the
game function we started on the
previous page, so make sure your
indentations are in alignment if
you’renotusinganIDE.Ifyouplan
to split this code up, we’d suggest
starting with the word selection
andresults.
The Python Book 37
Python essentials
def guess_letter():
print
letter = raw_input(“Take a guess at our mystery word:”)
letter
.strip()
letter
.lower()
print
return letter
def play_again():
answer = raw_input(“Would you like to play again? y/n: “)
if answer in (“y”, “Y”, “yes”, “Yes”, “Of course!”):
return answer
else:
print “Thank you very much for playing our game. See you next time!”
def scores():
global player_score, computer_score
print “HIGH SCORES”
print “Player: “, player_score
print “Computer: “, computer_score
if __name__ == ‘__main__’:
start()
17
18
21
19
20
22
16We end the entire game function loop by
calling upon return again, which we will
thenpassallthewayuptothestartfunctiononce
it’sfinished.
17The human input function first of
all prints out a raw_input message.
Once the player enters the letter, the function
parses it to be used with the rest of the code.
Firstly, strip is used to remove any white space
from the input given, as we’ve not given it any
extra parameters. We then convert it into
lower-case letters, as Python will not be able
to correctly compare an upper-case character
with a lower-case alternative. We then print the
selection for the record and return it up to the
game function.
18The last part of the game function is to
ask the player if they wish to try again.
The play_again function takes a human input
with a simple message and then analyses the
input so it knows what to send back.
19Giving users an option of y/n like we
have should expect a response in kind.
The if statement checks to see if any of our
defined positive responses have been entered.
As Python doesn’t differentiate between upper
or lower case, we’ve made sure it accepts both
y and Y. If this is the case, it returns a positive
response to game, which will start it again.
20If we don’t get an expected response,
we will assume the player does
not want to play again. We’ll print a goodbye
message and that will end this function. This
will also cause the start function to move onto
the next section and not restart.
21Going all the way back to the start
function, after game finishes we move
onto the results. This section is quite simple – it
calls the scores, which are integers, and then
prints them individually after the names of the
players. This is the end of the script, as far as
the player is concerned. Currently, the code will
not permanently save the scores, but you can
have Python write it to a file to keep if you wish.
22The final part of the code allows for
the script to be used in two ways.
Firstly, we can execute it in the command line
and it will work fine. Secondly, we can import
this into another Python script, perhaps if
you wanted to add it as a game to a collection.
This way, it will not execute the code when
being imported.
Homework
Now that you’ve finished with the code, why
not make your own changes? Increase the
word count; create different, selectable word
categories; or even let people guess the full
word. You have all the tools to do this in the
currentcodeandlast month’stutorial.
38 The Python Book
Python essentials
TheStart
Here we’re doing some minor setups so we can
get our code to run with some extra modules not
includedwiththe basics
TheRules
We’re setting names for each dice roll so they can
be properly identified to the player – much more
interesting than numbers
TheScore
Again we’ve got some basic variables set up so we
cankeepscoreofthe gamesifwe want to
TheScript
The game is handled here, passing the player onto
thenextfunctiontoactuallyplay,andhandlingthe
endofthesession aswell
TheGame
We access the full game loop via here, and the
function that allows us to play again if we’re
so inclined
TheThrow
The initial hand is dealt, so to speak, at the start of
the throws function. This function handles all the
decision making in the game, while passing off the
dicerollstoanother function
TheHand
We’ve also got a special function so we can inform
the player exactly whatstyle ofhandthey have
TheDecision
There are two rounds in this version of poker
dice, and you can select how many dice you wish
to re-roll in this small while loop that makes sure
you’realso usinga correct number
#!/usr/bin/env python2
import random
from itertools import groupby
nine = 1
ten = 2
jack = 3
queen = 4
king = 5
ace = 6
names = { nine: “9”, ten: “10”, jack: “J”, queen: “Q”, king: “K”, ace: “A” }
player_score = 0
computer_score = 0
def start():
print “Let’s play a game of Linux Poker Dice.”
while game():
pass
scores()
def game():
print “The computer will help you throw your 5 dice”
throws()
return play_again()
def throws():
roll_number = 5
dice = roll(roll_number)
dice.sort()
for i in range(len(dice)):
print “Dice”,i + 1,”:”,names[dice[i]]
result = hand(dice)
print “You currently have”, result
while True:
rerolls = input(“How many dice do you want to throw again? “)
try:
if rerolls in (1,2,3,4,5):
break
except ValueError:
pass
print “Oops! I didn’t understand that. Please enter 1, 2, 3, 4 or 5.”
Codelisting
Putonyourpokerfaceandgetreadytogambleasyouhone
yourprogrammingskillwithabitofpokerdice
PlaypokerdiceusingPython
So you’ve learnt how to program tic-tac-toe
and guessed your way to victory at hangman.
Now it’s time to head to Las Vegas and play our
cardsright.Orinthiscase,virtualdice,andmore
like Reno as we continue with our Python game
tutorialsandintroduceyoutosomepokerdice.
We’re again using some of the lessons we’ve
already learnt, including random number
generation, list creation and modification,
human input, rule setting, scoring and more.
But we’ll also be adding some new skills in this
tutorial.Namely,we’llbecreatingandappending
lists with random numbers, and using functions
multiple times in one block of code to cut down
onbloat.
Again, we recommend using IDLE, and we’re
using Python 2 to ensure compatibility with a
wider variety of distros, including the Raspberry
Pi. So, we hope luck is a lady for you and that the
odds are ever in your favour – just keep those
fingers crossed that you don’t roll a snake eyes
(wearecodinginPython,afterall)!
Resources
Python2:www.python.org/download
IDLE:www.python.org/idle
The Python Book 39
Python essentials
TheRe-roll
We’re doing the second set of rolls and starting
the end of the game here by calling on the same
function as before, but we’re also aware that
choosing no re-rollsmeans the endofthe game
TheDice
Here we’re finding out which dice the player wants
to re-roll, and also making sure that they enter
a valid number. Just so they know they’re doing
something, weprint something after every turn
SecondHand
We change and display the new dice hand to end
the game. Again, we make sure to tell the player
what theactualhandtheyhaveis
TheRolls
The function we reuse to roll our virtual six dice
using a simple while loop. This allows us to keep
thecodebase smaller
TheAnalysis
There are eight possible types of hands in poker
dice, and we can use a bit of logic to work out all
but one of them without checking against all 7,776
outcomes – in fact, we only specifically have to
checkfortwo
TheQuestion
Our simple ‘play again’ function that parses player
input so wecan restart or end the script
TheEnd
Scores are displayed at the end of the script, and
the very final part allows us to import this into
otherPythonscriptsasa module
EXTRAFUNCTIONS
Splitting up actions into functions
makes it easier to not only perform
them multiple times, but reduce
the amount of code. On larger
projects,thiscanaidwithspeed.
if rerolls == 0:
print “You finish with”, result
else:
roll_number = rerolls
dice_rerolls = roll(roll_number)
dice_changes = range(rerolls)
print “Enter the number of a dice to reroll: “
iterations = 0
while iterations  rerolls:
iterations = iterations + 1
while True:
selection = input(“”)
try:
if selection in (1,2,3,4,5):
break
except ValueError:
pass
print “Oops! I didn’t understand that. Please enter 1, 2, 3, 4 or 5.”
dice_changes[iterations-1] = selection-1
print “You have changed dice”, selection
iterations = 0
while iterations  rerolls:
iterations = iterations + 1
replacement = dice_rerolls[iterations-1]
dice[dice_changes[iterations-1]] = replacement
dice.sort()
for i in range(len(dice)):
print “Dice”,i + 1,”:”,names[dice[i]]
result = hand(dice)
print “You finish with”, result
def roll(roll_number):
numbers = range(1,7)
dice = range(roll_number)
iterations = 0
while iterations  roll_number:
iterations = iterations + 1
dice[iterations-1] = random.choice(numbers)
return dice
def hand(dice):
dice_hand = [len(list(group)) for key, group in groupby(dice)]
dice_hand.sort(reverse=True)
straight1 = [1,2,3,4,5]
straight2 = [2,3,4,5,6]
if dice == straight1 or dice == straight2:
return “a straight!”
elif dice_hand[0] == 5:
return “five of a kind!”
elif dice_hand[0] == 4:
return “four of a kind!”
elif dice_hand[0] == 3:
if dice_hand[1] == 2:
return “a full house!”
else:
return “three of a kind.”
elif dice_hand[0] == 2:
if dice_hand[1] == 2:
return “two pair.”
else:
return “one pair.”
else:
return “a high card.”
def play_again():
answer = raw_input(“Would you like to play again? y/n: “)
if answer in (“y”, “Y”, “yes”, “Yes”, “Of course!”):
return answer
else:
print “Thank you very much for playing our game. See you next time!”
def scores():
global player_score, computer_score
print “HIGH SCORES”
print “Player: “, player_score
print “Computer: “, computer_score
if __name__ == ‘__main__’:
start()
Codelistingcontinued
40 The Python Book
Python essentials
it later with a different number that the player
chooses. We get five random numbers in a list
returnedfrom thefunction,andwe order itusing
sort to make it a bit more readable for the player
andalsolateronforthehandfunction.
08Dicedisplay
We print out each dice, numbering them
so the player knows which dice is which, and
also giving it the name we set at the start of the
script. We’re doing this with a loop that repeats
itself the number of times as the dice list is
long using the range(len(dice)) argument. The
i is increased each turn, and it prints out that
specificnumberofthedicelist.
09Currenthand
We want to find the type of hand the
player has multiple times during the game, so set
a specific function to find out. We pass the series
ofdicewehaveontothisfunction,andprint.
10Throwagain
Before we can throw the dice for the
second round, we need to know which dice the
#!/usr/bin/env python2
import random
from itertools import groupby
nine = 1
ten = 2
jack = 3
queen = 4
king = 5
ace = 6
names = { nine: “9”, ten: “10”, jack: “J”, queen: “Q”, king: “K”, ace: “A” }
player_score = 0
computer_score = 0
def start():
print “Let’s play a game of Linux Poker Dice.”
while game():
pass
scores()
def game():
print “The computer will help you throw your 5 dice”
throws()
return play_again()
01
02
03
04
06
05
01Begin
As before, we use this line to enter the
path to the Python interpreter. This allows us to
run the program inside a terminal or otherwise
outside of a Python-specific IDE like IDLE. Note
thatwe’realsousingPython2forthisscript.
02Importing
Aswellasimportingtherandommodule
for our dice throws, we need to get the groupby
function so we can order the dice in a way that is
more readable and also easier for analysis when
tellingtheplayerwhathandtheyhave.
03Cards
While we’re using random numbers for
the dice rolls, unless we assign the correct cards
to each number, the player won’t know what
they’ve rolled and what constitutes a better
hand. We set each card to a number and then
equatewhattheseshouldbeprintedoutas.
04Scores
As usual, we have the empty scores
for the player and computer so we can update
these as we go. While it’s not specifically used
in this version of the code, it’s easy enough
to expand on it and add your own simple
computer roll, or limited AI for both rolls.
05Start
We’re starting the interactive part of the
code with the ‘start’ function. It prints a greeting
to the player, then starts a while loop that’ll allow
us to replay the game as many times as we wish.
The pass statement allows the while loop to stop
once we’ve finished. If we do stop playing the
game,thescorefunctionisthencalledupon.
06Game
Like our Rock, Paper, Scissors code,
def game pawns the rest of the game onto other
functions, with its main function allowing us to
keep repeating the game by passing the player
through to the play_again function.
07Throws
For our first throw, we want to have five
random dice. We’ve set a variable here to pass
on to our throwing function, allowing us to reuse
RECYCLING
There are a few variables that
have duplicates throughout the
code – while we’ve been careful
to make sure they work where
we want them to, it’s not the best
code conduct. The names of the
variables don’t specifically matter
– it’s just best to label them in a
way you understand for bug fixing
and otherstoread.
The Python Book 41
Python essentials
def throws():
roll_number = 5
dice = roll(roll_number)
dice.sort()
for i in range(len(dice)):
print “Dice”,i + 1,”:”,names[dice[i]]
result = hand(dice)
print “You currently have”, result
while True:
rerolls = input(“How many dice do you want to throw again? “)
try:
if rerolls in (1,2,3,4,5):
break
except ValueError:
pass
print “Oops! I didn’t understand that. Please enter 1, 2, 3, 4 or 5.”
if rerolls == 0:
print “You finish with”, result
else:
roll_number = rerolls
dice_rerolls = roll(roll_number)
dice_changes = range(rerolls)
print “Enter the number of a dice to reroll: “
iterations = 0
while iterations  rerolls:
iterations = iterations + 1
while True:
selection = input(“”)
try:
if selection in (1,2,3,4,5):
break
except ValueError:
pass
print “Oops! I didn’t understand that. Please enter 1, 2, 3, 4 or 5.”
dice_changes[iterations-1] = selection-1
print “You have changed dice”, selection
playerwantstorollagain.Westartthisbyasking
them how many re-rolls they want to do, which
allows us to create a custom while loop to ask
the user which dice to change that iterates the
correctnumberoftimes.
We also have to make sure it’s a number
within the scope of the game, which is why
we check using the try function, and print out
a message which tells the user if and how they
arewrong.
11Stick
One of the things we’ve been trying to do
in these tutorials is point out how logic can cut
down on a lot of coding by simply doing process
07
08
10
11
12
13
09
INDENTATIONS
Watch the indentations again as
we split the else function. The
following page’s code is on the
same level as roll roll_number,
dice_rerolls and dice_changes in
thecode.
WHITESPACE
The big if function at the end of
throws doesn’t have many line
breaks between sections – you
canaddtheseasmuchasyouwant
to break up the code into smaller
chunksvisually,aidingdebugging.
of eliminations or following flow charts. If the
userwantstore-rollzerotimes,thenthatmeans
they’re happy with their hand, and it must be the
end of the game. We print a message to indicate
thisanddisplaytheirhandagain.
12There-rolls
Here’s where we start the second roll
and the end of the game, using a long else to the
if statement we just started. We first of all make
sure to set our variables – updating roll_number
to pass onto the roll function with the re-roll
number the user set, and creating the list that’s
the exact length of the new set of rolls we wish to
usethankstorange(rerolls).
13Parse
We ask the player to enter the numbers
of the dice they wish to re-roll. By setting an
iterations variable, we can have the while loop
last the same number of times as we want re-
rolls by comparing it to the reroll variable itself.
We check each input to make sure it’s a number
thatcanbeused,andaddthevalidchoicestothe
dice_changes list. We use iterations-1 here as
Pythonlistsbeginat0ratherthan1.Wealsoprint
out a short message so the player knows the
selectionwassuccessful.
42 The Python Book
Python essentials
14New dice
We’re resetting and reusing the iterations
variabletoperformasimilarwhilelooptoupdate
the rolls we’ve done to the original dice variable.
The main part of this while loop is using the
iterations-1 variable to find the number from
dice_changes list, and using that to change that
specific integer in the dice list with the number
from the replacement list. So if the first item on
the dice_changes list is two, then the second
item on the dices list is changed to the number
wewanttoreplaceitwith.
15Sorting
We’re ending the throw function in
basically the same way we ended the first throw.
First of all, we re-sort the dice list so that all the
numbers are in ascending order. Then we print
out the final cards that the dice correspond to,
before again passing it onto the hand function
so that we can fully determine the hand that the
player has. We print out this result and that ends
thefunction,sendingthewholethingbacktothe
gamefunctiontoaskifyouwanttoplayagain.
16Dicerolling
The roll function is used twice in the
code for both times that we roll the dice. Being
able to use the same code multiple times means
we can cut down on bloat in the rest of the
script, allowing it to run a little faster, as we’ve
explained. It also means in this case that we can
use it again if you want to change the game to
threerounds,ormodifyitforrealpoker.
17Numberofrolls
We begin the whole thing by bringing
over the roll_number variable into the function
– this is because while in the original roll it will
always be five, the second roll could between
one and the full five dice. We create a list with
the number of entries we need for each roll, and
again set an iterations variable for the upcoming
whileloop.
18Remember
Much like the while loops in the rest of the
code so far, we’re keeping it going until iterations
is the same as roll_number. Each entry in the
dice list is replaced with a random number using
the random.choice function and keeping it in the
range of the numbers variable, which is one to
six for each side of the dice. After this is done, we
return the dice variable to the throw function that
makesupthemajorityofthegame.
19Handanalysis
While not technically a hand of cards,
the poker terminology still applies. We start in
this function by setting up a few things. The first
part uses the groupby function we imported –
this is used in this case to count the numbers
that make up the dice variable. If there are three
twos,afourandafive,itwillreturn[3,1,1].We’re
using this to ascertain what kind of hand the
player has. As the output of this groupby won’t
be in any specific order, weuse thesortfunction
again to sort it; however, this time we use the
reverse=TRUE argument to make the analysis
easieragain.
20Straights
Straights and high cards are odd ones
out in poker dice, as they do not rely on being
able to count any repetitions in the cards.
There are, however, only two hands that create
a straight in poker dice, so we have created
two lists here that contain them. We can then
check first to see if the dice make these hands,
and then if all other checks fail, it has to be a
high card.
2
1Yourhand
While seemingly lengthy, this a fairly
simple if statement. As we stated before, we
check to see if it’s one of the two straight hands.
As there are no flushes or royal straight flushes
inpokerdice,wedon’thavetoworryaboutthose.
We then check to see if the first item in the list
is five, which can only result in five of a kind;
similarly, if the first item is four then the hand
mustbefourofakind.Ifthefirstnumberisthree,
thenitcanbeeitherafullhouseorthreeofakind,
iterations = 0
while iterations  rerolls:
iterations = iterations + 1
replacement = dice_rerolls[iterations-1]
dice[dice_changes[iterations-1]] = replacement
dice.sort()
for i in range(len(dice)):
print “Dice”,i + 1,”:”,names[dice[i]]
result = hand(dice)
print “You finish with”, result
def roll(roll_number):
numbers = range(1,7)
dice = range(roll_number)
iterations = 0
while iterations  roll_number:
iterations = iterations + 1
dice[iterations-1] = random.choice(numbers)
return dice
14
15
16
18
17
HIGHERORLOWER
Which hand is best? What are the
odds of getting certain hands in
the game? Some of the answers
are surprising, as the poker
hands they’re based on trump the
differing odds the dice produce.
We’ve ranked hands from highest
to lowest.
FiveofaKind .................6/7776
FourofaKind ............150/7776
FullHouse .................300/7776
Straight......................240/7776
ThreeofaKind ........1200/7776
TwoPairs .................1800/7776
OnePair ...................3600/7776
HighCard ...................480/7776
The Python Book 43
Python essentials
def hand(dice):
dice_hand = [len(list(group)) for key, group in groupby(dice)]
dice_hand.sort(reverse=True)
straight1 = [1,2,3,4,5]
straight2 = [2,3,4,5,6]
if dice == straight1 or dice == straight2:
return “a straight!”
elif dice_hand[0] == 5:
return “five of a kind!”
elif dice_hand[0] == 4:
return “four of a kind!”
elif dice_hand[0] == 3:
if dice_hand[1] == 2:
return “a full house!”
else:
return “three of a kind.”
elif dice_hand[0] == 2:
if dice_hand[1] == 2:
return “two pair
.”
else:
return “one pair
.”
else:
return “a high card.”
def play_again():
answer = raw_input(“Would you like to play again? y/n: “)
if answer in (“y”, “Y”, “yes”, “Yes”, “Of course!”):
return answer
else:
print “Thank you very much for playing our game. See you next time!”
def scores():
global player_score, computer_score
print “HIGH SCORES”
print “Player: “, player_score
print “Computer: “, computer_score
if __name__ == ‘__main__’
:
start()
19
20
21
22
23
24
HOMEWORK
There is currently no scoring in
place for this version of the game.
Try adding a computer player, or
create a rule set that requires a
certain hand or higher. You could
even makeittwo-player.
so we nest an if statement. Again, we do this for
pairs, where that could be one or two pairs. If all
else fails then, by a process of elimination, it can
only be a high card. We give each outcome a text
stringtosendbacktothethrowfunctionsothatit
canbeprinted.
22Playagain
As before, we ask the player for raw input
with the text offering another game. Instead of
parsing it, we assume the player will choose a
specified yes response based on the text, and if
none of these versions is received, we print out
themessagethankingthemforplayingthegame.
Thisendsthegamefunction.
23Finalscores
Going all the way back to the start
function, after the game finishes we move onto
the results. This section is quite simple – it
calls the scores, which are integers, and then
prints them individually after the names of the
players. This is the end of the script, as far as the
player is concerned. Currently, the code will not
permanently save the scores, but you can have
Pythonwriteittoafiletokeepifyouwish.
24Modules
The final part of the code allows for
the script to be used in two ways. Firstly, we
can execute it in the command line and it will
work just fine. Secondly, we can import this into
another Python script, perhaps if you wanted to
add it as a game to a collection. This last piece of
code will prevent our script being executed when
imported by another module – it will only do so
whenbeingrundirectly.
TEXTEDITORS
Instead of the IDE we’ve suggested, you
should also try coding in a text editor. Some
of them are a little more lightweight and
format code similar to the way the IDE does,
separating functions and strings by colours
etc. Some of the ones we’d recommend are
the classic gedit, a popular text editor from
GNOME desktops; Geany, which has a few
IDE-esque features written into it; TEA, a
multifunctioning text editor and project
manager; and Jedit, a text editor that lives
in the command line for minimum resource
usage. These can also be used with multiple
programminglanguages,soyoucangetused
tothem with Python,thenmaketheswitch.
44 The Python Book
Python essentials
Thestart
Herewe’re doing someminor setup, including getting a new
modulethathelpsuscreatea simplegraphical interface
Theimports
We’re importing the three games wecreated in past issuesso
wecancalluponor use them
Thewindow
Createagraphicalwindow andgive it a name sowe can add
some functions toit
Theframe
Define the dimensionsofthe windowand givea rough guide to
placement ofthe objects within
Thewelcome
Print a messagein the windowand place it in a specific
orientation. Thisworks a littledifferently to print
Thebutton
The focus of this month’s tutorial is making Rock-Paper-
Scissors work in a graphical interface, so we’re calling a new
function we’re creating
Theinterface
Creatingandformattingbuttonstostart the other twotutorial
games in the command lineor shell
Theexit
Here we create a button that quits the window and ends
the script. We’ve also placed it specifically at the bottom of
the window
Theloop
The mainloop allowsthe main windowto continueto work and
beupdated without exiting the program unlessspecified
#!/usr/bin/env python2
#Linux User  Developer presents: Mega Microgrames Collection
from Tkinter import *
import rockpaperscissors
import hangman
import pokerdice
root = Tk()
root.title (“Linux User  Developer’s Mega Microgames Collection”)
mainframe = Frame(root, height = 200, width = 500)
mainframe.pack_propagate(0)
mainframe.pack(padx = 5, pady = 5)
intro = Label(mainframe, text = “””Welcome to Linux User  Developers Mega
Microgames Collection.
Please select one of the following games to play:
“””)
intro.pack(side = TOP)
rps_button = Button(mainframe, text = “Rock, Paper, Scissors”, command =
rockpaperscissors.gui)
rps_button.pack()
hm_button = Button(mainframe, text = “Hangman”, command = hangman.start)
hm_button.pack()
pd_button = Button(mainframe, text = “Poker Dice”, command = pokerdice.start)
pd_button.pack()
exit_button = Button(mainframe, text = “Quit”, command = root.destroy)
exit_button.pack(side = BOTTOM)
root.mainloop()
MainInterfaceCodeListing
BringeverythingtogetherwithaPythonGUIandtakethenext
stepinprogrammingyourownsoftware
Createagraphicalinterface
forPythongames
The three basic games we have made in
Python so far have all run in the command line
or via IDLE, a Python IDE. While this allowed us
to show off different ways to use Python code,
we haven’t actually shown you how to present
it yet. In this tutorial, we will take all three
games and put them all into one neatly unified
graphicalinterface.
To this end, we’ll be making use of the small
line of code we added at the bottom of each
previous tutorial so we can import them as
modules into our main graphical script. We’ll
also modify the existing code to add some
graphical elements. To do all this we’ll be using
Tkinter, a default module available in Python
that allows you to create windows and frames
with fairly simple code.
All you need for this tutorial is an up-to-date
copy of Python, from your distro’s repository
or the website, and the IDLE development
environment. This will also work great on
Raspberry Pi distros, such as Raspbian.
Resources
Python2:www.python.org/download
IDLE:www.python.org/idle
The Python Book 45
Python essentials
Newimports
ImportnewmodulesthatallowustocreatetheGUI
part of Rock, Paper, Scissors, as well as removing
themoduleswenolonger need
Newinterface
Our new main function allows us to call the
majorityofthegamescriptwhentherps_buttonis
pressed. This contains the game components and
thegraphicalcomponents
Newstart
We’ve changed the start function so that it no
longergoestothescorefunctionafterit’sfinished.
We’vealsoremovedthescorefunction,aswetrack
thatdifferentlysoit can be displayedproperly
Newgame
We’ve changed the game function so that it now
takes the input from our graphical interface. We
use a new variable to do this that works with the
GUI,otherwiseitworksroughlythesameasbefore
Newresults
The result function remains largely unchanged,
only now it sends the outcome message to a
variable we use for the interface, and generally
usesthenewGUI’svariables
Newwindow
Wecreatethegamewindowwithaslightlydifferent
method due to already having a ‘mainloop’ root
window. We’re also giving it a name so you can
identify it properly
Newvariables
Ournewvariablesaresetupsotheycaninteractwith
boththegamecodeandtheinterfacecodeproperly.
We’ve also made sure to have a default selection for
theplayersothatthecoderunsproperly
Newframe
Determine the size and layout of the window for
the game using a slightly different method than
before. We’ve also allowed for elements to be
anchored in certain positions around the window
Newchoice
Here we place radio buttons in a specific
configuration in the window, giving the user the
choice of three moves. This is then passed along to
thevariableandusedbythegamecode
Newmove
Here we allow for the computer’s move to be
displayedunderthe‘Computer’ label
Newbutton
Pressing the Play button we’ve put here runs the
game script, prints out the scores and finally a
messagebasedonthe outcome
Newending
We’ve changed this so that the main script begins
withguinowratherthan the start function
#!/usr/bin/env python2
# Linux User  Developer presents: Rock, Paper, Scissors: The Video Game: The Module
from Tkinter import *
from ttk import *
import random
def gui():
rock = 1
paper = 2
scissors = 3
names = { rock: “Rock”, paper: “Paper”, scissors: “Scissors” }
rules = { rock: scissors, paper: rock, scissors: paper }
def start():
while game():
pass
def game():
player = player_choice.get()
computer = random.randint(1, 3)
computer_choice.set(names[computer])
result(player, computer)
def result(player, computer):
new_score = 0
if player == computer:
result_set.set(“Tie game.”)
else:
if rules[player] == computer:
result_set.set(“Your victory has been assured.”)
new_score = player_score.get()
new_score += 1
player_score.set(new_score)
else:
result_set.set(“The computer laughs as you realise you have been defeated.”)
new_score = computer_score.get()
new_score += 1
computer_score.set(new_score)
rps_window = Toplevel()
rps_window.title (“Rock, Paper, Scissors”)
player_choice = IntVar()
computer_choice = StringVar()
result_set = StringVar()
player_choice.set(1)
player_score = IntVar()
computer_score = IntVar()
rps_frame = Frame(rps_window, padding = ‘3 3 12 12’
, width = 300)
rps_frame.grid(column=0, row = 0, sticky=(N,W,E,S))
rps_frame.columnconfigure(0, weight=1)
rps_frame.rowconfigure(0,weight=1)
Label(rps_frame, text=’Player’).grid(column=1, row = 1, sticky = W)
Radiobutton(rps_frame, text =’Rock’
, variable = player_choice, value = 1).grid(column=1,
row=2, sticky=W)
Radiobutton(rps_frame, text =’Paper’
, variable = player_choice, value = 2).grid(column=1,
row=3, sticky=W)
Radiobutton(rps_frame, text =’Scissors’
, variable = player_choice, value =
3).grid(column=1, row=4, sticky=W)
Label(rps_frame, text=’Computer’).grid(column=3, row = 1, sticky = W)
Label(rps_frame, textvariable = computer_choice).grid(column=3, row=3, sticky = W)
Button(rps_frame, text=”Play”, command = start).grid(column = 2, row = 2)
Label(rps_frame, text = “Score”).grid(column = 1, row = 5, sticky = W)
Label(rps_frame, textvariable = player_score).grid(column = 1, row = 6, sticky = W)
Label(rps_frame, text = “Score”).grid(column = 3, row = 5, sticky = W)
Label(rps_frame, textvariable = computer_score).grid(column = 3, row = 6, sticky = W)
Label(rps_frame, textvariable = result_set).grid(column = 2, row = 7)
if __name__ == ‘__main__’:
gui()
ModifiedRPSCodeListing
46 The Python Book
Python essentials
01
02
03
04
07
08
09
06
05
01Firstline
We use this line to enter the path to the
Python interpreter. This lets us run the program
inside a terminal or otherwise outside of a
Python-specific IDE like IDLE. Note that we’re
alsousingPython2forthisparticularscript.
06Introductions
We create the intro variable as a label
that lives in the main frame. We give it text to
introduce the interface, using the triple quote
marks to have it go across multiple lines and
format better. We then use pack to display it, and
tellTkintertoputitatthetopoftheinterface.
07Rock,Paper,Scissors
We create a button for the Rock, Paper,
Scissors game using the Button function. We
attach to it the main frame, give it a label using
02Importgraphics
Tkinter is the graphical interface we’re
using and while it’s a standard Python function,
you’llneedtoimportthemodulesoyoucanuseit.
We’ve used the ‘from [module] import *’ method
so that we can use the functions from it without
havingtoaddTkinteratthebeginning.
04Rootwindow
Using the Tk() function creates the
window we’re going to be placing everything
into. We’ve decided to call it root for now;
however, you can call it anything you like, as
long as you’re consistent with it. We’ve also
named it using the title command from Tkinter
and a string of text.
05Mainframe
The first line has us set the variable
mainframe as a Frame in the interface. We’ve
attached it to root, the main window, and given
03Importgames
We’re importing the modules for the
three games. We added the line at the bottom
MAINWINDOW
The main interface window that
this code creates is fairly basic,
but contains the functions we
require. The window exit button
will do the same job as the Quit
button, and the Hangman and
Poker Dice buttons run the old
scripts in the Python shell.
#!/usr/bin/env python2
#Linux User  Developer presents: Mega Microgrames Collection
from Tkinter import *
import rockpaperscissors
import hangman
import pokerdice
root = Tk()
root.title (“Linux User  Developer’s Mega Microgames Collection”)
mainframe = Frame(root, height = 200, width = 500)
mainframe.pack_propagate(0)
mainframe.pack(padx = 5, pady = 5)
intro = Label(mainframe, text = “””Welcome to Linux User  Developers Mega Microgames Collection.
Please select one of the following games to play:
“””)
intro.pack(side = TOP)
rps_button = Button(mainframe, text = “Rock, Paper
, Scissors”, command = rockpaperscissors.gui)
rps_button.pack()
hm_button = Button(mainframe, text = “Hangman”, command = hangman.start)
hm_button.pack()
pd_button = Button(mainframe, text = “Poker Dice”, command = pokerdice.start)
pd_button.pack()
exit_button = Button(mainframe, text = “Quit”, command = root.destroy)
exit_button.pack(side = BOTTOM)
root.mainloop()
it a minimum height and width in pixels. We
use pack_propogate to create the window, and
then make sure it’s the size that we’ve defined.
We’ve then used pack to pad the borders,
allowing the contents of the window to not
touch the sides of it.
of each script so we can do this. To make sure
to differentiate the functions in each game, we
will have to specify [module].[function] so there
are no errors in the code.
The Python Book 47
Python essentials
#!/usr/bin/env python2
# Linux User  Developer presents: Rock, Paper
, Scissors: The Video Game: The Module
from Tkinter import *
from ttk import *
import random
def gui():
rock = 1
paper = 2
scissors = 3
names = { rock: “Rock”, paper: “Paper”, scissors: “Scissors” }
rules = { rock: scissors, paper: rock, scissors: paper }
def start():
while game():
pass
def game():
player = player_choice.get()
computer = random.randint(1, 3)
computer_choice.set(names[computer])
result(player
, computer)
10
11
12
13
14
09Breaktheloop
The exit button works similarly to the
other buttons we’ve created, but instead it uses
the command root.destroy. This ends the loop
that we’ve created with root.mainloop(), which
allows the interface code to continue looping,
allowing us to continually use it. We place the
exit button at the bottom of the window with
‘side = BOTTOM’.
12Gamevariables
The variables are staying the same
so that we can do the same comparisons we
made in the original code. We’ve put them into
the function itself so that they don’t affect the
other imported code into the main interface –
and so that when calling just this function, we
don’t need to use global to bring them in.
14Gamefunction
The game function has had a few
modifications to make sure it works with
the interface. First of all, the player variable
is retried using get() on the special variable
we’ve created to contain the player choice.
We do a similar thing for the computer, using
‘set’ to change the variable in our interface-
friendly computer_choice value. We still use
the name variable to set the text that goes into
computer_choice. This then passes the player
and computer variables along in the same way
we did before.
13Startfunction
We’ve removed the part that calls
the score function from the start function,
as we have the interface handle the scoring
now. It still calls upon the game function,
though, putting it into a loop so it can be
used continuously. This function is called by
the interface to begin the game by setting a
computer move and then comparing it to the
player’s choice.
10Gamecode
Nothingmuchhaschangedinthestartof
this code, other than a few import changes. The
code for running it in the command line is still
PYTHONSHELL
Our other code will run in the shell
or via a command line in the same
way as before when the buttons
arepressed.
08Othergames
For the other two games, the code is
mostlythesame;however,wecalluponthestart
function in both of them. In the final interface,
this will cause the games to run in the shell or
commandlineasthey’vebeenrunningbefore.
11Gameinterface
One of the biggest changes we’re making
to this script is having it all contained in one
function, ‘def gui’. The interface code needs to
be put into a function, otherwise it will be run
during import. While we’ve chosen to put the
entirety of the code in a function, you can also
try just having the graphical interface code in
one. All our variables are kept in here so that
they still work properly.
there, and with a few modifications the code will
run independently of the main interface. We’ve
removed the time module, as we no longer need
it, and imported not only the Tkinter module,
but the ttk module. The ttk module allows us to
arrange the GUI in a grid, which will be slightly
easiertouseandunderstand.
text that appears on the button, and then have
it run a command. In this case, we use the
modified rockpapershotgun.py code that has a
gui function, hence rockpapershotgun.py. We
then use pack to place it in the window
48 The Python Book
Python essentials
def result(player
, computer):
new_score = 0
if player == computer:
result_set.set(“Tie game.”)
else:
if rules[player] == computer:
result_set.set(“Your victory has been assured.”)
new_score = player_score.get()
new_score += 1
player_score.set(new_score)
else:
result_set.set(“The computer laughs as you realise you have been defeated.”)
new_score = computer_score.get()
new_score += 1
computer_score.set(new_score)
rps_window = Toplevel()
rps_window.title (“Rock, Paper
, Scissors”)
player_choice = IntVar()
computer_choice = StringVar()
result_set = StringVar()
player_choice.set(1)
player_score = IntVar()
computer_score = IntVar()
15
16
17
19
20
18
GAMEWINDOW
In its default state, the game
window will have rock selected
and no message will be displayed.
Once the player makes a move, the
message will be displayed at the
bottom and the computer’s move
will be printed. There’s no quit
button on this menu, but clicking
the window exit will bring you back
to the main interface.
15Resultfunction
The result function still takes the same
two variables as before, which we set in the
game function. While technically we can use
the variables set up for the interface, these
are not pure integers and can cause an error if
not handled correctly. With that in mind, we’ve
created an empty new_score variable that we
can use to effectively clean the interface value
beforeaddingitbackintoit.
20Interfacevariables
Here is the reason we had to call and
change the variables in a different manner.
For Tkinter, we need to let the interface know
whether or not a variable is an integer or a text
value. IntVar and StringVar allow for these
respectively. We’ve also set the player_choice
variable to be one, which we have already set as
thechoiceforrock.Thismeanstherewillatleast
be a default choice when the game is started,
anditwon’tcauseanerror.
21Gameframe
We’ve created the frame for our
interface items slightly differently. Instead
of using the pack command in the main
interface, we’re using grid to make sure they’re
orientated in such a way that makes sense
for the user. Padding does just that, setting
up values to make sure the items in the frame
don’t touch the edge of the window. Using the
.grid command, we then create this frame.
The row and column variables allow for rows
and columns to be included in the structure of
16Tie
The logic for determining the result is
the same as before. We first do the easy check –
whether or not the numeric value for the player
andcomputervariableisthesame.Whatchanges
this time is that, instead of printing the text,
we send the “Tie game” message to our result
variableusingthesetfunctionfromTkinter.
18Lose
This part of the overall if statement
works in the same way as before, by assuming
that if it isn’t a tie or a win, it’s a loss. Like the
new version of the win code, it then uses set
to change the message that will be displayed
to the player, and calls upon and changes
the computer score by putting it through the
new_score variable.
19New window
As the original window is part of the
mainloop, we cannot have the window be
created using Tk() like in the main interface
code. As this window is coming off it, though,
we instead create it using Toplevel(). This
allows the window to run separately and on
top of the main window. We’ve also given
17Win
The if statement continues by seeing if
the player has won. Like before, we use the rules
we set to make the comparison for the code to
it a name, which will not change the main
window’s name in the process.
make. We set the result_set like we did in the
tie game, with a different message to the user.
Finally, we set the new_score variable to be the
current player score, using the get function to
obtain it, plus one to the score, and then use
set again to put it back into the player_score
variable. We can’t use += with the player_score
variable,asitisnotastandardvariable.
The Python Book 49
Python essentials
rps_frame = Frame(rps_window, padding = ‘3 3 12 12’
, width = 300)
rps_frame.grid(column=0, row = 0, sticky=(N,W,E,S))
rps_frame.columnconfigure(0, weight=1)
rps_frame.rowconfigure(0,weight=1)
Label(rps_frame, text=’Player’).grid(column=1, row = 1, sticky = W)
Radiobutton(rps_frame, text =’Rock’
, variable = player_choice, value = 1).grid(column=1, row=2,
sticky=W)
Radiobutton(rps_frame, text =’Paper’
, variable = player_choice, value = 2).grid(column=1, row=3,
sticky=W)
Radiobutton(rps_frame, text =’Scissors’
, variable = player_choice, value = 3).grid(column=1,
row=4, sticky=W)
Label(rps_frame, text=’Computer’).grid(column=3, row = 1, sticky = W)
Label(rps_frame, textvariable = computer_choice).grid(column=3, row=3, sticky = W)
Button(rps_frame, text=”Play”, command = start).grid(column = 2, row = 2)
Label(rps_frame, text = “Score”).grid(column = 1, row = 5, sticky = W)
Label(rps_frame, textvariable = player_score).grid(column = 1, row = 6, sticky = W)
Label(rps_frame, text = “Score”).grid(column = 3, row = 5, sticky = W)
Label(rps_frame, textvariable = computer_score).grid(column = 3, row = 6, sticky = W)
Label(rps_frame, textvariable = result_set).grid(column = 2, row = 7)
if __name__ == ‘__main__’:
gui()
21
22
23
23
24
25
22Player’schoice
We create a label for the player’s move
and assign it to a grid location, on the first row,
on the first column. We also justify it to the left
using ‘sticky = W’. We then add the radio buttons
for the player’s move, each on the same column
but the following row down. We give each choice
a name, then assign it to the player_choice
variable. We then make each choice have a
numerical value that corresponds to the moves
we’vedeterminedinthefirstsetofrules.
23Computer’smove
We display the computer move here.
First of all, we label what this is and then create
24PressPlay
The running of the code all hinges on
the Play button. It’s very simple: we put it in the
row between the Player and Computer move as
part of our three-column system; and it runs the
start functionusing the command option.Due to
the loop of the interface, we can keep pressing
this without needing to be asked to play again.
Simply exiting the window will go back to the
main interface window as well, meaning we do
notneedaspecificquitbutton.
25Running score
We have two sets of scores to display
– one for the player and the other for the
26End game
The final part of the code allows for
the script to be used by the main window, and
also allows for it to run on its own when used
in the command line or shell. You’ll need to
perform some modifications to make it run on
its own, such as making it the mainloop and not
a Toplevel window. However, it will run just fine
from both without the need to be launched from
themaininterface.
the window, and the sticky allows us to justify
items with specific directions – in this case top,
left, right and bottom justification. Finally, we
then make sure each column and row is treated
equally by giving them the same weighting, and
starting from zero.
a second label to display the actual move. We
do this by adding the textvariable option to
Label, and using the computer_choice variable
we updated earlier in the game function. This
merely prints the text from the names list and
justifiesthistotheleft.
computer. We label these the same way we’ve
done with labelling the Player and Computer
move, having them on a lower row but still in
the relevant columns. Below that, we use the
textvariable option again to get the numerical
score we assigned to the separate score
variable. Finally, we create another label to
displaythemessageforthegame’soutcome
50 The Python Book
Python essentials
Completeyourtrioofgameswithagraphicalinterfaceforthe
hangmanandpokerdicecode
Bringgraphicstosimple
Pythongames
We have now created a simple selector for the
trio of Python games we made previously. This
interface was able to launch a GUI for our rock,
paper, scissors game, and run the other two in
the terminal. Now, we’re going to convert the
hangman and poker dice codes to work in a
similar way to rock, paper, scissors.
The trick with hangman comes in allowing
for a different type of input, text, and the ability
to have multiple rounds of the game. Tkinter
allows for text entry, and we rely a lot less on
‘while’ loops to play the game in its entirety.
Poker Dice needs to keep the dice analysis
code, and the option to change specific dice
using checkboxes.
We’ll be modifying a large amount of the
original code to fit in with the new graphical
scheme. This mainly involves cutting specific
parts and having the Tkinter-specific code
handle these itself. The code listings on these
pages include the modified code – we’ll discuss
the graphical part on the following pages.
Resources
Python2:www.python.org/download
IDLE:www.python.org/idle
1Imported
Herewe’re doing the
sameminorsetup,
including getting
theTkinter module
that helps us create
asimplegraphical
interface
2Words
We’re keeping
ourvariablesthat
determine the word to
guessheresoitcan
be easily accessed
anywhere in the code
3Function
Likelasttime,we’re
putting the majority of
our originalcodeinto a
newfunction,gui
4Analysis
Weselecttheword
andanalyseitbefore
continuingon with the
restofthecode
5Graphics
Thehangedman
functionislargely
unchanged,albeitwith
newcode todisplay
ourASCIIgraphicson
the interface
6Guesses
Wecheckthe number
ofmistakesmade,and
calltheguess_letter
function tocheck the
letter entered
from Tkinter import *
from ttk import *
from random import *
word = 0
word_length = 0
clue = 0
def gui():
global word, word_length, clue
dictionary = [“gnu”,”kernel”,”linux”,”magei
a”,”penguin”,”ubuntu”]
word = choice(dictionary)
word_length = len(word)
clue = word_length * [“_”]
tries = 6
def hangedman(hangman):
graphic = [
“””
+-------+
| |
| O
| -|-
| / 
|
===============
“””]
graphic_set = graphic[hangman]
hm_graphic.set(graphic_set)
def game():
letters_wrong = incorrect_guesses.get()
letter=guess_letter()
first_index=word.find(letter)
if first_index == -1:
letters_wrong +=1
incorrect_guesses.set(letters_
wrong)
else:
for i in range(word_length):
if letter == word[i]:
clue[i] = letter
hangedman(letters_wrong)
clue_set = “ “.join(clue)
word_output.set(clue_set)
if letters_wrong == tries:
result_text = “Game Over. The word
was “ + word
result_set.set(result_text)
new_score = computer_score.get()
new_score += 1
computer_score.set(new_score)
if “”.join(clue) == word:
result_text = “You Win! The word
was “ + word
result_set.set(result_text)
new_score = player_score.get()
new_score += 1
player_score.set(new_score)
def guess_letter():
letter = letter_guess.get()
letter.strip()
letter.lower()
return letter
def reset_game():
global word, word_length, clue
incorrect_guesses.set(0)
hangedman(0)
result_set.set(“”)
letter_guess.set(“”)
word = choice(dictionary)
word_length = len(word)
clue = word_length * [“_”]
new_clue = “ “.join(clue)
word_output.set(new_clue)
if __name__ == ‘__main__’:
gui()
01
02
03
04
05
06
HangmanCodeListing
The Python Book 51
Python essentials
1Moreimports
We’ve added the new imported
modulesweneedto make Tkinter
workandkeeptherest the same
2Dicelist
Thelistthatholdsthe dice iskept
outside the main function so that it
canbeaccessedeverywhere
3Rolls
Samegoesfortherollfunction.
It doesn’tspecificallyneed to be
inside the gui functionanyway
4Decisions
The checkboxes in the graphical
codewe’re going to createlater will
giveusnumberswecan analyse for
thecode.Weretrieve thesenumbers
andcheckthemtofindoutwhich
dicetheuser wishestore-roll
5Hands
Finally,ourhandanalysisfunction
isthe lastpartofthe originalcode
thatiskeptoutsidethe guifunction.
Boththis and the abovefunction
passthe necessary details back
upthe chain to thenbeaddedinto
thenewgraphicalelementsofthe
newinterface
6Nodice
Ifnodicehavebeenselectedto
re-roll, the hand output ischanged
to showa finalmessage
7Re-roll
Thispartisalmostthe same as
before–anewsetofdice are rolled
and then inserted into the list ofdice
likebefore,thenre-sortedtomake
the handanalysiseasier
8Morefunctions
The new gui functionisthe main
changetothePoker Dice code,
and asbeforeincludesthe Tkinter
elementsandotherpartsofthe
original code
9Gamestart
Asimplefunctionthat we can use to
activatethere-rollsofthedice
10Newhand
Thenewdicearenamed,analysed,
andeverythingisthen set for the gui
to display the finaloutcome
11Reset
Likewiththehangman code,we
haveafunctiontoreset allthe
variables,allowingyoutostart the
gameagain
from Tkinter import *
from ttk import *
import random
from itertools import groupby
dice = 0
def roll(roll_number):
numbers = range(1,7)
dice = range(roll_number)
iterations = 0
while iterations  roll_number:
iterations = iterations + 1
dice[iterations-1] = random.
choice(numbers)
return dice
def hand(dice):
dice_hand = [len(list(group)) for key,
group in groupby(dice)]
dice_hand.sort(reverse=True)
straight1 = [1,2,3,4,5]
straight2 = [2,3,4,5,6]
if dice == straight1 or dice ==
straight2:
return “a straight!”
elif dice_hand[0] == 5:
return “five of a kind!”
elif dice_hand[0] == 4:
return “four of a kind!”
elif dice_hand[0] == 3:
if dice_hand[1] == 2:
return “a full house!”
else:
return “three of a kind.”
elif dice_hand[0] == 2:
if dice_hand[1] == 2:
return “two pair.”
else:
return “one pair.”
else:
return “a high card.”
def gui():
global dice
dice = roll(5)
dice.sort()
nine = 1
ten = 2
jack = 3
queen = 4
king = 5
ace = 6
names = { nine: “9”, ten: “10”, jack:
“J”, queen: “Q”, king: “K”, ace: “A” }
result = “You have “ + hand(dice)
def game():
throws()
def throws():
global dice
dice1_check = dice1.get()
dice2_check = dice2.get()
dice3_check = dice3.get()
dice4_check = dice4.get()
dice5_check = dice5.get()
dice_rerolls = [dice1_check,
dice2_check, dice3_check, dice4_check,
dice5_check]
for i in range(len(dice_rerolls)):
if 0 in dice_rerolls:
dice_rerolls.remove(0)
if len(dice_rerolls) == 0:
result = “You finish with “ +
hand(dice)
hand_output.set(result)
else:
roll_number = len(dice_rerolls)
number_rerolls = roll(roll_num-
ber)
dice_changes = range(len(dice_
rerolls))
iterations = 0
while iterations  roll_number:
iterations = iterations + 1
dice_changes[iterations-1]
= number_rerolls[iterations-1]
iterations = 0
while iterations  roll_number:
iterations = iterations + 1
replacement = number_
rerolls[iterations-1]
dice[dice_
changes[iterations-1]] = replacement
dice.sort()
new_dice_list = [0,0,0,0,0]
for i in range(len(dice)):
new_dice_list[i] =
names[dice[i]]
final_dice = “ “.join(new_dice_
list)
dice_output.set(final_dice)
final_result = “You finish with
“ + hand(dice)
hand_output.set(final_result)
def reset_game():
global dice
dice = roll(5)
dice.sort()
for i in range(len(dice)):
empty_dice[i] = names[dice[i]]
first_dice = “ “.join(empty_dice)
dice_output.set(first_dice)
result = “You have “ + hand(dice)
hand_output.set(result)
if __name__ == ‘__main__’:
gui()
01
02
03
04
05
11
10
09
08
07
06
PokerDiceCodeListing
52 The Python Book
Python essentials
01
02
03
01Firstlines
As usual, we start off each program with
the code that lets us run it in the command line,
followed by importing the necessary modules:
random, to determine the word to use; Tkinter,
for the majority of the graphical code; and
06Gamesbegin
All the analysis of the letter we’ve
entered is done in this function. To that end, we
start by obtaining the incorrect guesses so far
from the variable we’ve set up so the interface
can access it if we want it to. The letter from
the entry field in the interface is then obtained
and cleaned up so it can be used with the rest of
thecode.
07Checktheletter
This section of the code is again largely
unchanged–theletteristakenandcomparedto
the word with find to see if it matches with one
of the letters. The if statement then adds one to
the incorrect guess variable, or updates the clue
variabletoaddtheletterintherightspot.
09Updatescores
Exactly as before, we check to see if the
player has won or lost yet. In the event of either,
a message is displayed to signify this, and the
winsandlossesscoreisupdatedusingset.
02Globalvariables
We have kept these three variables
outside of the gui function so they can be
04Randomword
We bring in the three variables with
global so we can modify them throughout
the code, and then set the word. As before, a
random item from the list of words is selected
with choice, the length is ascertained, and the
cluetodisplayisset.
08Updateinterface
These three lines set the graphic for this
round, join the current clue together as a string,
and then set it on the variable for the interface
toread.
03Graphicalfunction
We’re putting all the working code into
the gui function so it can be activated from the
main interface. This means we can import the
Hangman code into the interface without the
game window popping up, and only run it when
weactivatetheguifunctionfromhere.
05Thehangedman
The main difference this time for the
Hangman graphics is that instead of printing
these out, we’re going to display them in the
interface. When the function is called and the
graphic selected, it’s placed in the variable we’ve
set up in the interface code that we’re using to
displaytheresult.
ttk, for the grid code we’ll be using to align the
differentelements.
accessedatallpointsinthecode.Python2does
not allow you to call upon global variables when
you’re in a nested function, whereas in Python 3
thiscouldhavegoneintotheguifunction.
09
08
07
06
05
04
#!/usr/bin/env python2
from Tkinter import *
from ttk import *
from random import *
word = 0
word_length = 0
clue = 0
def gui():
global word, word_length, clue
dictionary = [“gnu”,”kernel”,”linux”,”mageia”,”penguin”,”ubuntu”]
word = choice(dictionary)
word_length = len(word)
clue = word_length * [“_”]
tries = 6
def hangedman(hangman):
graphic = [
“””
+-------+
| |
| O
| -|-
| / 
|
===============
“””]
graphic_set = graphic[hangman]
hm_graphic.set(graphic_set)
def game():
letters_wrong = incorrect_guesses.get()
letter=guess_letter()
first_index=word.find(letter)
if first_index == -1:
letters_wrong +=1
incorrect_guesses.set(letters_wrong)
else:
for i in range(word_length):
if letter == word[i]:
clue[i] = letter
hangedman(letters_wrong)
clue_set = “ “.join(clue)
word_output.set(clue_set)
if letters_wrong == tries:
result_text = “Game Over. The word was “ + word
result_set.set(result_text)
new_score = computer_score.get()
new_score += 1
computer_score.set(new_score)
if “”.join(clue) == word:
result_text = “You Win! The word was “ + word
result_set.set(result_text)
new_score = player_score.get()
new_score += 1
player_score.set(new_score)
YOULOSE
When you’ve run out of guesses,
the game stops. From here, you
can also reset the game to play
againifyouwish.
The Python Book 53
Python essentials
10
11
15
12
16
13
14
12Interfacevariables
Tkinter only works with specific variables
– we’ve created all the ones we need or can use
here. IntVars take integers, while StringVars take
strings. We’ve used get and set throughout the
restofthecodewiththesetogetandsetvalues.
14CluetoHangman
These labels are fairly straightforward
– we’re either giving them fixed text, or telling
them to use a specific textvariable so they can
beupdatedasweplaythegame.
13Framedwindow
The frame is set up the same way as
last time. We pad the frame from the edge of
the window, set a grid, give it sticky points at
compass points, and allow for setting objects
withspecificrowandcolumnpoints.
16Resultsandreset
The rest of the code is similar to what
we’ve done already: labels to display fixed text
and the scores/result text that change. The
button that activates the reset function is also
put at the bottom here. The final two lines allow
ustoimportthemoduleintotheinterfacecode.
10Sanitiseinput
The guess_letter function purely gets
the letter from the player input variable, strips it
of any formatting, makes it lower case, and then
returns it back to the game function. This is so
thelettercanbeusedproperly.
11New window
We use the Toplevel command from
Tkinter like last month to separate the loops of
the main interface and game window. We then
usetitletocallitHangman.
15Textentry
Entry here sets a text box we will add the
letters to. The exportselection option makes it
so selecting the letter won’t immediately copy it
to the clipboard, and the textvariable selection
is where the code stores the letter added. The
button activates the game function, analysing
thelettertheplayerentered.
Theframeisset
upasbefore
def guess_letter():
letter = letter_guess.get()
letter.strip()
letter.lower()
return letter
def reset_game():
global word, word_length, clue
incorrect_guesses.set(0)
hangedman(0)
result_set.set(“”)
letter_guess.set(“”)
word = choice(dictionary)
word_length = len(word)
clue = word_length * [“_”]
new_clue = “ “.join(clue)
word_output.set(new_clue)
hm_window = Toplevel()
hm_window.title (“Hangman”)
incorrect_guesses = IntVar()
incorrect_guesses.set(0)
player_score = IntVar()
computer_score = IntVar()
result_set = StringVar()
letter_guess = StringVar()
word_output = StringVar()
hm_graphic = StringVar()
hm_frame = Frame(hm_window, padding = ‘3 3 12 12’, width = 300)
hm_frame.grid(column=0, row = 0, sticky=(N,W,E,S))
hm_frame.columnconfigure(0, weight=1)
hm_frame.rowconfigure(0,weight=1)
Label(hm_frame, textvariable = hm_graphic).grid(column=2, row = 1)
Label(hm_frame, text=’Word’).grid(column=2, row = 2)
Label(hm_frame, textvariable = word_output).grid(column=2, row = 3)
Label(hm_frame, text=’Enter a letter’).grid(column=2, row = 4)
hm_entry = Entry(hm_frame, exportselection = 0, textvariable = letter_guess).grid(column = 2, row = 5)
hm_entry_button = Button(hm_frame, text = “Guess”, command = game).grid(column = 2, row = 6)
Label(hm_frame, text = “Wins”).grid(column = 1, row = 7, sticky = W)
Label(hm_frame, textvariable = player_score).grid(column = 1, row = 8, sticky = W)
Label(hm_frame, text = “Losses”).grid(column = 3, row = 7, sticky = W)
Label(hm_frame, textvariable = computer_score).grid(column = 3, row = 8, sticky = W)
Label(hm_frame, textvariable = result_set).grid(column = 2, row = 9)
replay_button = Button(hm_frame, text = “Reset”, command = reset_game).grid(column = 2, row = 10)
if __name__ == ‘__main__’:
gui()
THEHANGMANGUI
Press the updated Hangman
button to launch a new window.
Here we have the initial graphic,
wordclueandentryfortheplayer
to interact with. The scores
are set to zero, and no result
message is displayed as no
games havebeen played yet.
ORIGINALINTERFACE
You’ll also need the interface
code from last issue, which
already works with the modified
Rock, Paper, Scissors code. The
way it was left off means it won’t
work with the new code, so you’ll
have to change the command in
each button from [game].start
to [game].gui.
54 The Python Book
Python essentials
17
19
24
22
20
18
21
23
22Firstroll
As the window opens, we immediately
make the first roll. This is then sorted, each
number is attributed to a card, and then the
result is created to be displayed in the main
window. This is similar to how it worked before,
but instead it’s now entered into the StringVars
fortheinterfacetowardstheendofthescript
23Startgame
When we activate the button that starts
game, it immediately sends us to the rest of the
code. This would also work if you had the button
go to the throws function instead; however, you
canaddotherfunctionstothispartifyouwish.
24Diceselection
The first thing we do is find out what
checkboxes have been ticked by the player. We
then put these in a list so we can change out the
correct dice numbers. We’ve also brought in dice
so we can check against that what the current
dicerollsare.
20Handofdice
Like roll, nothing has changed for the
hand function. It’s simply now placed outside
the gui function for the exact same reasons.
It also means that you can easily import this
functionintoanotherscriptifyouwish.
21GUIstart
As we’ve mentioned last month and in
the Hangman code, we put all the GUI code into
afunctionsothatwecancallonitwhenwewant
to. In this case, pressing the Poker Dice button
on the main interface activates pokerdice.gui,
whichisthisfunction.
18Outsidedice
For Poker Dice, there’s only one variable
to show at any one time, the dice. Again, due to
the nested functions, and because we’re using
Python2,weneedtocallitwithglobalfromhere
tomakesurethegamecanberesetproperly.
19Dicerolls
The roll function has been removed from
the gui function so as not to create any code
errors with some of its variables. It can be easily
called within the nested functions. It hasn’t
changedatallfromtheoriginalcode.
17Startover
The usual array of command-line
compatibility and module importing here. The
groupby function is specifically imported here
fordiceanalysis.
#!/usr/bin/env python2
from Tkinter import *
from ttk import *
import random
from itertools import groupby
dice = 0
def roll(roll_number):
numbers = range(1,7)
dice = range(roll_number)
iterations = 0
while iterations  roll_number:
iterations = iterations + 1
dice[iterations-1] = random.choice(numbers)
return dice
def hand(dice):
dice_hand = [len(list(group)) for key, group in groupby(dice)]
dice_hand.sort(reverse=True)
straight1 = [1,2,3,4,5]
straight2 = [2,3,4,5,6]
if dice == straight1 or dice == straight2:
return “a straight!”
elif dice_hand[0] == 5:
return “five of a kind!”
elif dice_hand[0] == 4:
return “four of a kind!”
elif dice_hand[0] == 3:
if dice_hand[1] == 2:
return “a full house!”
else:
return “three of a kind.”
elif dice_hand[0] == 2:
if dice_hand[1] == 2:
return “two pair.”
else:
return “one pair.”
else:
return “a high card.”
def gui():
global dice
dice = roll(5)
dice.sort()
nine = 1
ten = 2
jack = 3
queen = 4
king = 5
ace = 6
names = { nine: “9”, ten: “10”, jack: “J”, queen: “Q”, king: “K”,
ace: “A” }
result = “You have “ + hand(dice)
def game():
throws()
def throws():
global dice
dice1_check = dice1.get()
dice2_check = dice2.get()
dice3_check = dice3.get()
dice4_check = dice4.get()
dice5_check = dice5.get()
dice_rerolls = [dice1_check, dice2_check, dice3_check, dice4_
check, dice5_check]
THEPOKERDICEGUI
Two things are being printed out
on the initial window. The first
set of dice, ordered in the way
we did last time, and the current
hand. The checkboxes activate
a specific number that is used
when re-rolling dice with the
Rerollbutton.
EXTRAGAMEFUNCTIONS
We mentioned that the game function
doesn’t necessarily need to be used right
now. You can either clean up the code and
remove it, or add extra functions, such
as being able to choose a random new
selection of dice, or making it two-player.
Experimentwith whatyouwanttodo!
The Python Book 55
Python essentials
25Dicetore-roll
If a checkbox isn’t selected, we have
it set to give a zero value. We want to remove
these from the list so that the correct dice
are changed, so we use the for loop to check
each part of the list, and then use the remove
functionwhentheelementdoesequalzero.
26Earlyfinish
If no dice have been selected to re-roll,
the list will contain all 0s, which will then be
removed. The length of this list will then also be
zero,meaningwecanusethattoendthegameif
theplayerhitsRerollwithoutselectinganydice.
27New dice
This else function works roughly
the same as before. We start by getting the
necessary information for how many dice to roll,
andalisttoputthere-rolls.Wethenrollasmany
newdiceasweneedwiththefirstwhileloop
28Gameover
We use the same kind of while loop to
replace the new numbers into the original list,
much like last time. Then the dice are re-sorted,
analysed, joined as a string and then set into the
interface’s variable. The final hand message is
alsocreateandset.
29Graphicalvariables
As we’re rolling the dice as soon as
we launch the game, but the interface code
doesn’t start until the end, you can see that
after creating the necessary variables, we also
then set them. Of note, the dice have to be made
into a string separately with the for loop before
addingtothevariable.
30Checkbuttons
The main new addition to this code is
the check buttons with Checkbutton. You can
set an on and off value, with default off being 0.
We’ve made it so that the check buttons return
the same number as the dice they’re changing,
which we explained how we used earlier in the
code. The variable option sets whatever the
outcomeistothespecificTkintervariable.
25
26
27
28
29
30
Thecheck
buttonsarenew
for i in range(len(dice_rerolls)):
if 0 in dice_rerolls:
dice_rerolls.remove(0)
if len(dice_rerolls) == 0:
result = “You finish with “ + hand(dice)
hand_output.set(result)
else:
roll_number = len(dice_rerolls)
number_rerolls = roll(roll_number)
dice_changes = range(len(dice_rerolls))
iterations = 0
while iterations  roll_number:
iterations = iterations + 1
dice_changes[iterations-1] = number_rerolls[iterations-1]
iterations = 0
while iterations  roll_number:
iterations = iterations + 1
replacement = number_rerolls[iterations-1]
dice[dice_changes[iterations-1]] = replacement
dice.sort()
new_dice_list = [0,0,0,0,0]
for i in range(len(dice)):
new_dice_list[i] = names[dice[i]]
final_dice = “ “.join(new_dice_list)
dice_output.set(final_dice)
final_result = “You finish with “ + hand(dice)
hand_output.set(final_result)
def reset_game():
global dice
dice = roll(5)
dice.sort()
for i in range(len(dice)):
empty_dice[i] = names[dice[i]]
first_dice = “ “.join(empty_dice)
dice_output.set(first_dice)
result = “You have “ + hand(dice)
hand_output.set(result)
pd_window = Toplevel()
pd_window.title (“Poker Dice”)
dice_output = StringVar()
empty_dice = [0,0,0,0,0]
for i in range(len(dice)):
empty_dice[i] = names[dice[i]]
first_dice = “ “.join(empty_dice)
dice_output.set(first_dice)
hand_output = StringVar()
hand_output.set(result)
dice1 = IntVar()
dice2 = IntVar()
dice3 = IntVar()
dice4 = IntVar()
dice5 = IntVar()
result_set = StringVar()
player_score = IntVar()
computer_score = IntVar()
pd_frame = Frame(pd_window, padding = ‘3 3 12 12’, width = 300)
pd_frame.grid(column=0, row = 0, sticky=(N,W,E,S))
pd_frame.columnconfigure(0, weight=1)
pd_frame.rowconfigure(0,weight=1)
Label(pd_frame, text=’Dice’).grid(column=3, row = 1)
Label(pd_frame, textvariable = dice_output).grid(column=3, row = 2)
Label(pd_frame, textvariable = hand_output).grid(column=3, row = 3)
Label(pd_frame, text=’Dice to Reroll?’).grid(column=3, row = 4)
reroll1 = Checkbutton(pd_frame, text = “1”, variable = dice1, onvalue = 1, offvalue
= 0).grid(column=1, row = 5)
reroll2 = Checkbutton(pd_frame, text = “2”, variable = dice2, onvalue = 2, offvalue
= 0).grid(column=2, row = 5)
reroll3 = Checkbutton(pd_frame, text = “3”, variable = dice3, onvalue = 3, offvalue
= 0).grid(column=3, row = 5)
reroll4 = Checkbutton(pd_frame, text = “4”, variable = dice4, onvalue = 4, offvalue
= 0).grid(column=4, row = 5)
reroll5 = Checkbutton(pd_frame, text = “5”, variable = dice5, onvalue = 5, offvalue
= 0).grid(column=5, row = 5)
pd_reroll_button = Button(pd_frame, text = “Reroll”, command = game).grid(column =
3, row = 6)
replay_button = Button(pd_frame, text = “Reset”, command = reset_game).grid(column
= 3, row = 7)
if __name__ == ‘__main__’:
gui()
ONEWINDOW
Thewaywe’vemadetheseTkinter
interfaces is to have the games
launch in a separate window. You
can have them all running in one
window, though, by replacing the
labels and buttons of the original
interface by putting them as
different functions or classes.
Make sure to add a quit button to
the games that lets you go back
to the main page.
56 The Python Book
Python essentials
Buildanappfor
AndroidwithPython
MasterKivy,theexcellentcross-platformapplication
frameworktomakeyourfirstAndroidapp…
The great thing about Kivy is there are loads
of directions we could take it in to do some
pretty fancy things. But, we're going to make
a beeline for one of Kivy's coolest features
- the ability it affords you to easily run your
programs on Android.
We'll approach this by first showing how to
make a new app, this time a dynamic Breakout-
to be drawn anywhere on your screen and on
any widget type.
Beforewecandoanyofthiswe'llneedaclass
for each kind of game object, which we’re going
to pre-populate with some of the properties
that we'll need later to control them. Remember
from last time, Kivy properties are special
attributes declared at class level, which (among
other things) can be modified via kv language
and dispatch events when they are modified
(Fig. 02).
The Game class will be one big widget
containing the entire game. We've specifically
made it a subclass of FloatLayout because
this special layout is able to position and size
its children in proportion to its own position
and size – so no matter where we run it or
how we resize the window, it will place all the
game objects appropriately.
Next we can use Kivy's graphics instructions
to draw various shapes on our widgets. We'll
just demonstrate simple rectangles to show
their locations, though there are many more
advanced options you might like to investigate.
In a Python file we can apply any instruction
by declaring it on the canvas of any widget, an
example of which is shown in Fig. 03.
This would draw a red rectangle with the
same position and size as the player at its
moment of instantiation – but this has a
Before anything else, let's throw together a
basic Kivy app (Fig. 01). We've pre-imported
the widget types we'll be using, which this
time are just three: the basic Widget with
no special behaviour, the ModalView with a
pop-up behaviour as used last time, and the
FloatLayout as we will explaine later. Kivy
has many other pre-built widgets for creating
GUIs, but this time we’re going to focus on
drawing the whole GUI from scratch using
Kivy's graphics instructions. These comprise
either vertex instructions to create shapes
(including rectangles, lines, meshes, and so
on) or contextual graphics changes (such as
translation, rotation, scaling, etc), and are able

QHerewe'vedrawnallthesimplegraphics
forourgame…nowwejusthavetomake
theshapesactuallydosomething!
style game. We'll then be able to compile this
straight to an Android APK that you can use just
likeanyother.
Of course, once you have mastered the
basic techniques you aren't limited to using
any particular kind of app, as even on Android
you can make use of all your favourite Python
librariestomakeanysortofprogramyoulike.
Once you've mastered Kivy, your imagination
is the only limit. If you're pretty new to Kivy,
don't worry, we won't assume that you have
any pre-existing knowledge. As long as you
have mastered some of the Python tutorials
in this book so far, and so have a fairly good
understanding of the language, you shouldn’t
haveanyproblemsfollowingalong.
The Python Book 57
Python essentials
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.modalview import ModalView
__version__ = '0.1' # Used later during Android compilation
class BreakoutApp(App):
pass
BreakoutApp().run()
Fig. 01
from kivy.graphics.context_instructions import Color
from kivy.graphics.vertex_instructions import Rectangle
class Player(Widget):
def __init__(self
, **kwargs):
super(Player
, self).__init__(**kwargs)
with self
.canvas:
Color(1, 0, 0, 1) # r
, g, b, a - red
Rectangle(pos=self
.pos, size=self
.size)
# or without the with syntax, self
.canvas.add(...
)
Fig. 03
from kivy.properties import (ListProperty, NumericProperty,
ObjectProperty, StringProperty)
class Game(FloatLayout): # Will contain everything
blocks = ListProperty([])
player = ObjectProperty() # The game's Player instance
ball = ObjectProperty() # The game's Ball instance
class Player(Widget): # A moving paddle
position = NumericProperty(0.5)
direction = StringProperty('none')
class Ball(Widget): # A bouncing ball
# pos_hints are for proportional positioning, see below
pos_hint_x = NumericProperty(0.5)
pos_hint_y = NumericProperty(0.3)
proper_size = NumericProperty(0.
)
velocity = ListProperty([0.1, 0.5])
class Block(Widget): # Each coloured block to destroy
colour = ListProperty([1, 0, 0])
Fig. 02
problem, unfortunately, as the drawing is
static. When we later move the player widget,
the red rectangle will stay in the same place,
and the widget will be invisible when it is in its
real position.
We could fix this by keeping references to our
canvas instructions and repeatedly updating
their properties to track the player, but there's
actually an easier way to do all of this - we
can use the Kivy language we introduced last
time. It has a special syntax for drawing on
the widget canvas, which we can use to draw
each of our widget shapes:
Player:
canvas:
Color:
rgba: 1, 1, 1, 1
Rectangle:
pos: self.pos
size: self.size
Ball:
canvas:
Color:
rgb: 1, 0.55, 0
Rectangle:
pos: self.pos
size: self.size
Block:
canvas:
Color:
rgb: self.colour
# A property we predefined above
Rectangle:
pos: self.pos
size: self.size
Color:
rgb: 0.1, 0.1, 0.1
Line:
rectangle:
[self.x, self.y,
self.width, self.height]
The canvas declaration is special, underneath
it we can write any canvas instructions we
like. Don't get confused, canvas is not a
widget and nor are graphics instructions
like Line. This is just a special syntax that is
unique to the canvas. Instructions all have
different properties that can be set, like the
pos and size of the rectangle, and you can
check the Kivy documentation online for all
the possibilities. The biggest advantage is
that although we still declare simple canvas
instructions, kv language is able to detect
what Kivy properties we have referred to and
automatically track them, so when they are
updated (the widget moves or is resized) the
canvas instructions move to follow!
Onceyouhavethebasictechniques,
youaren’tlimitedtooneapp…your
imaginationistheonlylimit
58 The Python Book
Python essentials
You probably noticed we had one of the
Block’s ‘Color’ instructions refer to its colour
property. This means that we can change
the property any time to update the colour
of the block, or in this case to give each block
arandomcolour(Fig.04).
Now that each of our widgets has a graphical
representation, let’s now tell our Game where to
place them, so that we can start up the app and
actuallyseesomethingthere.
class Game(FloatLayout):
def setup_blocks(self):
for y_jump in range(5):
for x_jump in range(10):
block = Block(pos_hint={
'x'
: 0.05 + 0.09*x_jump,
'y'
: 0.05 + 0.09*y_jump})
self
.blocks.append(block)
self
.add_widget(block)
class BreakoutApp(App):
def build(self):
g = Game()
g.setup_blocks()
return g
Here we create the widgets we want then use
add_widgettoaddthemtothegraphicstree.Our
root widget on the screen is an instance of Game
andeveryblockisaddedtothattobedisplayed.
The only new thing is that every Block
has been given a pos_hint. All widgets have
this special property, and it is used by
FloatLayouts like our Game to set their
positionproportionatetothelayout.
The dictionary is able to handle
various parameters, but in this
case ‘x’and ‘y’ give x and y Block
position as a relative fraction of the
parentwidthandheight.
You can run the app now, and this time
it will add 50 blocks to the Game before
displaying it on the screen. Each should have
one of the three possible random colours
and be positioned in a grid, but you'll now
notice their sizes haven't been manually set
so they all overlap. We can fix this by setting
their size_hint properties – and let's also
take this opportunity to do the same for the
otherwidgets aswell(Fig.05).
This takes care of keeping all our game
widgets positioned and sized in proportion
to the Game containing them. Notice that
the Player and Ball use references to the
properties we set earlier, so we'll be able to
move them by just setting these properties
and letting kv language automatically update
theirpositions.
The Ball also uses an extra property to
remain square rather than rectangular, just
because the alternative would likely look a
littlebitodd.
We've now almost finished the basic
graphics of our app! All that remains is to add
aBallandaPlayerwidgettotheGame.
Game:
ball: the_ball
player: the_player
Ball:
id: the_ball
Player:
id: the_player
You can run the game again now, and should
be able to see all the graphics working
properly. Nothing moves yet, but thanks to
the FloatLayout everything should remain in
proportion if you resize the game/window.
Now we just have to add the game
mechanics. For a game like this you usually
want to run some update function many times
per second, updating the widget positions and
carrying out game logic – in this case collisions
with the ball (Fig. 06).
The Clock can schedule any function at
any time, either once or repeatedly. A function
scheduled at interval automatically receives the
timesinceitslastcall(dthere),whichwe'vepassed
through to the ball and player via the references
we created in kv language. It's good practice to
scale the update (eg ball distance moved) by this
dt, so things remain stable even if something
interrupts the clock and updates don't meet
theregular1/60syouwant.
At this point we have also added the first steps
toward handling keyboard input, by binding to
the kivy Window to call a method of the Player
every time a key is pressed. We can then finish
offthePlayerclassbyaddingthiskeyhandleralong
withtouch/mouseinput.
class Player(Widget):
def on_touch_down(self
, touch):
self
.direction = (
'right' if touch.x  self
.parent.
center_x else 'left')
def on_touch_up(self
, touch):
self
.direction = 'none'
def on_key_down(self
, keypress,
scancode, *args):
if scancode == 275:
self
.direction = 'right'
elif scancode == 276:
self
.direction = 'left'
else:
self
.direction = 'none'
def on_key_up(self
, *args):
self
.direction = 'none'
def update(self
, dt):
dir_dict = {'right'
: 1, 'left'
: -1,

QRunningtheappshowsourcolouredblocksonthe
screen…buttheyalloverlap!Wecanfixthateasily
The Python Book 59
Python essentials
'none'
: 0}
self
.position += (0.5 * dt * dir_
dict[self
.direction])
These on_touch_ functions are Kivy's general
method for interacting with touch or mouse input,
they are automatically called when the input
is detected and you can do anything you like in
response to the touches you receive. In this case
we set the Player's direction property in response
to either keyboard and touch/mouse input, and
use this direction to move the Player when its
updatemethodiscalled.Wecanalsoaddtheright
behaviourfortheball(Fig.07).
This makes the ball bounce off every wall by
forcing its velocity to point back into the Game,
as well as bouncing from the player paddle –
but with an extra kick just to let the ball speed
change. It doesn't yet handle any interaction
with the blocks or any win/lose conditions,
but it does try to call Game.lose() if the
ball hits the bottom of the player's screen,
solet'snowaddinsomegameendcodetohandle
allofthis(Fig.08).AndthenaddthecodeinFig.09
toyour'breakout.kv'file.
This should fully handle the loss or win,
opening a pop-up with an appropriate message
and providing a button to try again. Finally, we
have to handle destroying blocks when the
ballhitsthem(Fig.10).
Thisfullycoverstheselastconditions,checking
collision via Kivy's built-in collide_widget method
that compares their bounding boxes (pos and
size). The bounce direction will depend on how far
the ball has penetrated, as this will tell us how it
firstcollidedwiththeBlock.
So there we have it, you can run the code to
play your simple Breakout game. Obviously it's
very simple right now, but hopefully you can
see lots of different ways to add whatever extra
behaviour you like – you could add different
types of blocks and power-ups, a lives system,
more sophisticated paddle/ball interaction, or
even build a full game interface with a menu and
settingsscreenaswell.
We’rejustgoingtofinishshowingonecoolthing
that you can already do – compile your game for
Android!GenerallyspeakingyoucantakeanyKivy
app and turn it straight into an Android APK that
will run on any of your Android devices. You can
even access the normal Android API to access
hardware or OS features such as vibration,
sensorsornativenotifications.
We'll build for Android using the Buildozer tool,
and a Kivy sister project wrapping other build
tools to create packages on different systems.
This takes care of downloading and running the
Android build tools (SDK, NDK, etc) and Kivy's
Python-for-AndroidtoolsthatcreatetheAPK.
import random
class Block(Widget):
def __init__(self
, **kwargs):
super(Block, self).__init__(**kwargs)
self
.colour = random.choice([
(0.78, 0.28, 0), )0.28, 0.63, 0.28), )0.25, 0.28, 0.78)])
Fig. 04
from kivy.clock import Clock
from kivy.core.window import Window
from kivy.utils import platform
class Game(FloatLayout):
def update(self
, dt):
self
.ball.update(dt) # Not defined yet
self
.player
.update(dt) # Not defined yet
def start(self
, *args):
Clock.schedule_interval(self
.update, 1./60.
)
def stop(self):
Clock.unschedule(self
.update)
def reset(self):
for block in self
.blocks:
self
.remove_widget(block)
self
.blocks = []
self
.setup_blocks()
self
.ball.velocity = [random.random(), 0.5]
self
.player
.position = 0.5
class BreakoutApp(App):
def build(self):
g = Game()
if platform() != 'android'
:
Window.bind(on_key_down=g.player
.on_key_down)
Window.bind(on_key_up=g.player
.on_key_up)
g.reset()
Clock.schedule_once(g.start, 0)
return g
Fig. 06
Block:
size_hint: 0.09, 0.05
# ... canvas part
Player:
size_hint: 0.1, 0.025
pos_hint: {'x'
: self
.position, 'y'
: 0.1}
# ... canvas part
Ball:
pos_hint: {'x'
: self
.pos_hint_x, 'y'
: self
.pos_hint_y}
size_hint: None, None
proper_size:
min(0.03*self
.parent.height, 0.03*self
.parent.width)
size: self
.proper_size, self
.proper_size
# ... canvas part
Fig. 05
60 The Python Book
Python essentials
Here you will be needing some basic
dependencies, which can be installed with
ease just by using your distro's normal
repositories. The main ones to use are
OpenJDK7, zlib, an up-to-date Cython,
and Git. If you are using a 64-bit distro you will also
be in need of 32-bit compatibility libraries for zlib,
libstdc++,aswellaslibgcc.Youcanthengoonand
downloadandinstallBuildozer:
git clone git://github.com/kivy/buildozer
cd buildozer
sudo python2.7 setup.py install
When you’re done with that part you
can then go on and navigate to your
Kivyapp,andyou’llhavetonamethemaincodefile
‘main.py’, this is the access point that the Android
APKwillexpect.Then:
buildozer init
This creates a ‘buildozer.spec’ file, a settings file
containing all the information that Buildozer needs
to create your APK, from the name and version to
thespecificAndroidbuildoptions.Wesuggestthat
you check through the whole file just to see what's
available but most of the default settings will be
fine,theonlythingwesuggestchangingis(Fig.11).
There are various other options you will often
want to set, but none are really all that vital right
now, so you’re able to immediately tell Buildozer to
buildyourAPKandgetgoing!
buildozer android debug
This will take some time, so be patient and it will
workoutfine.Whenyoufirstrunit,itwilldownload
both the Android SDK and NDK, which are large
(at least hundreds of megabytes) but vital to the
build. It will also take time to build these and to
compile the Python components of your APK. A lot
ofthisonlyneedstobedoneonce, asfuturebuilds
will take a couple of minutes if you change the
buildozer.spec, or just a few seconds if you've only
changedyourcode.
The APK produced is a debug APK, and you can
install and use it but there are extra steps if you
wanttofullydigitallysignitsothatitcanbeposted
on the Play store. This isn't hard, and Buildozer
can do some of the work, but you can check the
documentationonlineforfulldetails.
Assuming everything goes fine (it should!),
your Android APK will be in a newly created 'bin'
directory with the name ‘KivyBreakout-0.1-debug.
apk’. You can send it to your phone any way you
like (eg email), though you may need to enable
application installation from unknown sources in
yourSettingsbeforeyoucaninstallit.
class Ball(Widget)
def update(self
, dt):
self
.pos_hint_x += self
.velocity[0] * dt
self
.pos_hint_y += self
.velocity[1] * dt
if self
.right  self
.parent.right: # Bounce from right
self
.velocity[0] = -1 * abs(self
.velocity[0])
if self
.x  self
.parent.x: # Bounce from left
self
.velocity[0] = abs(self
.velocity[0])
if self
.top  self
.parent.top: # Bounce from top
self
.velocity[1] = -1 * abs(self
.velocity[1])
if self
.y  self
.parent.y: # Lose at bottom
self
.parent.lose() # Not implemented yet
self
.bounce_from_player(self
.parent.player)
def bounce_from_player(self
, player):
if self
.collide_widget(player):
self
.velocity[1] = abs(self
.velocity[1])
self
.velocity[0] += (
0.1 * ((self
.center_x - player
.center_x) /
player
.width))
Fig. 07
class GameEndPopup(ModalView):
message = StringProperty()
game = ObjectProperty()
class Game(Widget):
def lose(self):
self
.stop()
GameEndPopup(message='[color=#ff0000]You lose![/color]'
,
game=self).open()
def win(self): # Not called yet, but we'll need it later
self
.stop()
GameEndPopup(message='[color=#00ff00]You win![/color]'
,
game=self).open()
Fig. 08
GameEndPopup:
size_hint: 0.8, 0.8
auto_dismiss: False # Don't close if player clicks outside
BoxLayout:
orientation: 'vertical'
Label:
text: root.message
font_size: 60
markup: True
halign: 'center'
Button:
size_hint_y: None
height: sp(80)
text: 'Play again?'
font_size: 60
on_release: root.game.start(); root.dismiss()
Fig. 09
self
.parent.do_layout()
self
.parent.destroy_blocks(self)
class Game(FloatLayout):
def destroy_blocks(self
, ball):
for i, block in enumerate(self
.blocks):
if ball.collide_widget(block):
y_overlap = (
ball.top - block.y if ball.velocity[1]  0
else block.top - ball.y) / block.size_hint_y
x_overlap = (
ball.right - block.x if ball.velocity[0]  0
else block.right - ball.x) / block.size_hint_x
if x_overlap  y_overlap:
ball.velocity[0] *= -1
else:
ball.velocity[1] *= -1
self
.remove_widget(block)
self
.blocks.pop(i)
if len(self
.blocks) == 0:
self
.win()
return # Only remove at most 1 block per frame
Fig. 10
title = Kivy Breakout # Displayed in your app drawer
package.name = breakout # Just a unique identifying string,
# along with the package.domain
fullscreen = 0 # This will mean the navbar is not covered
log_level = 2 # Not vital, but this will print a lot more debug
# information and may be useful if something
# goes wrong
Fig. 11
PuttingyourAPK
onthePlayStore
Findouthowtodigitallysignarelease
APKanduploadittoanappstoreof
yourchoice
1
BuildandsignareleaseAPK
First we have to begin by creating a personal
digital key, then using it to digitally sign a
special release version of the APK. Run these
commands, and follow the instructions they then
giveyou.
## Create your personal digital key
## You can choose your own
## keystore name, alias, and passwords.
$ keytool -genkey -v -keystore test-
release-key.keystore 
-alias test-alias -keyalg RSA
-keysize 2048 -validity 10000
## Compile your app in release mode
$ buildozer android release
## Sign the APK with your new key
$ jarsigner -verbose -sigalg
SHA1withRSA -digestalg SHA1 
-keystore ./test-release-key.keystore 
./bin/KivyBreakout-0.1-release-
unsigned.apk test-alias
## Align the APK zip file
$ ~/
.buildozer/android/platform/android-
sdk-21/tools/zipalign -v 4 
./bin/KivyBreakout-0.1-release-
unsigned.apk 
./bin/KivyBreakout-0.1-release.apk
3
Uploadyourapptothestore
Click 'Add new application'
to submit your app the store,
including uploading your APK and
adding description text. When
everything is ready, simply click
Publish, and it should take just a few
hoursforyourapptogolive!
2
SignupasaGoogle
PlayDeveloper
Visit https://play.google.com/
apps/publish/signup, and follow
the instructions. You'll need to pay a
one-off $25 charge, but then you can
uploadasmanyappsasyoulike.
Python essentials

QYourgameshouldrunonanymodernAndroiddevice…you
canevenbuildareleaseversionandpublishtoanappstore!
62 The Python Book
Python essentials
Pythonprovidesquickandeasywaytobuild
applications,includingwebapps.Readontofindout
howtouseittobuildafeature-completewebapp
MakingwebappswithPython
02ConfiguringtheDjangoproject
settings
Before we start working on the application,
let’s configure the Django project as per our
requirements.
Edit ludIssueTracker/settings.py as follows
(onlypartsrequiringmodificationareshown):
Database Settings: We will be using SQLite3
asourdatabasesystem.
NOTE: Red text indicates new code or
updatedcode.
‘default’: {
‘ENGINE’: ‘django.
db.backends.sqlite3’,
‘NAME’: ‘ludsite.db3,
Pathsettings
Django requires an absolute path for directory
settings. But we want to be able to pass in the
relative directory references. In order to do that
we will add a helper Python function. Insert the
followingcodeatthetopofthesettings.pyfile:
import os
def getabspath(*x):
return os.path.join(os.path.
abspath(os.path.dirname(__file__)),
*x)
Nowyoucanupdatethepathoptions:
@code
TEMPLATE_DIRS = (
getabspath(‘templates’)
)
MEDIA_ROOT = getabspath(‘media’)
Python is known for its simplicity and
capabilities. At this point it is so advanced
that there is nothing you cannot do with
Python, and conquering the web is one of the
possibilities. When you are using Python for web
developmentyougetaccess toahuge catalogue
of modules and community support – make the
mostofthem.
Web development in Python can be done
in many different ways, right from using the
plain old CGI modules to utilising fully groomed
web frameworks. Using the latter is the most
popular method of building web applications
with Python, since it allows you to build
applications without worrying about all that
low-level implementation stuff. There are many
web frameworks available for Python, such
as Django, TurboGears and Web2Py. For this
tutorial we will be using our current preferred
option,Django.
01CreatingtheDjangoProject
magazineissuetracker
The django-admin.py file is used to create new
Django projects. Let’s create one for our issue
trackerproject…
In Django, a project represents the site and
its settings. An application, on the other hand,
represents a specific feature of the site, like
blogging or tagging. The benefit of this approach
is that your Django application becomes
portable and can be integrated with other
Djangositeswithverylittleeffort.
$ django-admin.py startproject
ludIssueTracker
A project directory will be created. This will also
act as the root of your development web server
that comes with Django. Under the project
directoryyouwillfindthefollowingitems…
manage.py: Python script to work with your
project.
ludIssueTracker: A python package (a directory
with __init__.py file) for your project. This
package contains your project’s settings and
configurationdata.
ludIssueTracker/settings.py: This file contains
alltheconfigurationoptionsfortheproject.
ludIssueTracker/urls.py: This file contains
variousURLmappings.
wsgi.py: An entry-point for WSGI-compatible
web servers to serve your project. Only useful
when you are deploying your project. For this
tutorialwewon’tbeneedingit.
Resources
Python2.7:
https://www.python.org/download/releases/2.7/
Djangoversion1.4:
https://www.djangoproject.com/
The Python Book 63
Python essentials
03Creatingludissuesapp
In this step we will create the primary
app for our site, called ludissues. To do that, we
willusethemanage.pyscript:
$ python manage.py startapp
ludissues
We will need to enable this app in the config file
aswell:
INSTALLED_APPS = (
.............
'django.contrib.admin',
‘ludissues’,
)
04Creatingthedatamodel
This is the part where we define the
data model for our app. Please see the inline
commentstounderstandwhatishappening.
Fromdjango.dbimportmodels:
# We are importing the user
authentication module so that we use
the built
# in authentication model in this
app
from django.contrib.auth.models
import User
# We would also create an admin
interface for our app
from django.contrib import admin
# A Tuple to hold the multi choice
char fields.
# First represents the field name
the second one repersents the
display name
ISSUE_STATUS_CHOICES = (
MEDIA_URL = ‘/media/’
Nowwewillneedtoenabletheadmininterface
forourDjangosite.ThisisaneatfeatureofDjango
which allows the automatic creation of an admin
interfaceofthesitebasedonthedatamodel.The
admin interface can be used to add and manage
contentforaDjangosite.
Uncommentthefollowingline:
INSTALLED_APPS = (
‘django.contrib.auth’,
‘django.contrib.contenttypes’,
‘django.contrib.sessions’,
‘django.contrib.sites’,
‘django.contrib.messages’,
‘django.contrib.staticfiles’,
‘django.contrib.admin’,
# ‘django.contrib.admindocs’,
)
('new', 'New'),
('accepted','Accepted'),
('reviewed','Reviewed'),
('started','Started'),
('closed','Closed'),
)
class Issue(models.Model):
# owner will be a foreign key
to the User model which is already
built-in Django
owner = models.ForeignKey(User,n
ull=True,blank=True)
# multichoice with defaulting to
new
status = models.CharField(max_
length=25,choices=ISSUE_STATUS_
CHOICES,default='new')
summary = models.TextField()
# date time field which will be
set to the date time when the record
is created
opened_on = models.
DateTimeField('date opened', auto_
now_add=True)
modified_on = models.
DateTimeField('date modified', auto_
now=True)
def name(self):
return self.summary.
split('n',1)[0]
# Admin front end for the app. We
are also configuring some of the
# built in attributes for the admin
interface on
# how to display the list, how it
will be sorted
# what are the search fields etc.
class IssueAdmin(admin.ModelAdmin):
date_hierarchy = 'opened_on'
list_filter = ('status','owner')
list_display = ('id','name','sta
tus','owner','modified_on')
search_fields =
['description','status']
# register our site with the Django
admin interface
admin.site.
register(Issue,IssueAdmin)
To have the created data model reflected in the
database,runthefollowingcommand:
$ python manage.py syncdb
You’llbealsoaskedtocreateasuperuserforit:
You just installed Django's auth
system, which means you don't have
any superusers defined.
Would you like to create one now?
(yes/no): yes
05Enablingtheadminsite
The admin site is already enabled,
but we need to enable it in the urls.py file – this
contains the regex-based URL mapping from
modeltoview.Updatetheurls.pyfileasfollows:
from django.conf.urls import
patterns, include, url
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns(‘’,
url(r’^admin/’, include(admin.
site.urls)),
)
06StartingtheDjangowebserver
Django includes a built-in web server
which is very handy to debug and test Django
applications. Let’s start it to see how our admin
interfaceworks…
Tostartthewebserver:
$ python manage.py runserver
If you do not have any errors in your code, the
server should be available on port 8000. To
launch the admin interface, navigate your
browsertohttp://localhost:8000/admin.
You will be asked to log in here. Enter the
username and password that you created while
syncingthedatabase.
After logging in, you will notice that all the apps
installedinyourprojectareavailablehere.Weare
onlyinterestedintheAuthandLudIssuesapp.
You can click the +Add to add a record. Click
the Add button next to Users and add a few
users to the site.
Once you have the users inside the system,
youcannowaddafewissuestothesystem.
Q Adminloginscreen
64 The Python Book
Python essentials
07Creatingthepublicuserinterface
forludissues
At this point, the admin interface is working. But
we need a way to display the data that we have
added using the admin interface. But there is no
publicinterface.Let’screateitnow.
We will have to begin by editing the main
urls.py(ludIssueTracker/urls.py).
urlpatterns = patterns(‘’,
(r’^’,include(‘ludissues.
urls’)),
(r’^admin/’, include(admin.site.
urls)),
)
This ensures that all the requests will be
processedbyludissues.urlsfirst.
08Creatingludissues.url
Create a urls.py file in the app directory
(ludissues/urls.py)withthefollowingcontent:
from django.conf.urls import
patterns, include, url
# use ludissues model
from models import ludissues
# dictionary with all the objects in
ludissues
info = {
‘queryset’:ludissues.objects.
all(),
}
# To save us writing lots of python
code
# we are using the list_detail
generic view
#list detail is the name of view we
are using
urlpatterns = patterns(‘django.
views.generic.list_detail’,
#issue-list and issue-detail are the
09Settinguptemplateandmedia
directories
In this step we will create the template and
media directories. We have already mentioned
thetemplatedirectoryas
TEMPLATE_DIRS = (
getabspath(‘templates’)
)
Which translates to ludIssueTracker/
ludIssueTracker/templates/. Since we will be
accessing the templates from the ludissues
app, the complete directory path would be
ludIssueTracker/ludIssueTracker/templates/
ludissues. Create these folders in your
projectfolder.
Also, create the directory ludIssueTracker/
ludIssueTracker/media/ for holding the CSS
file. Copy the style.css file from the resources
directoryofthecodefolder.
Toservefilesfromthisfolderweneedtomake
it available publicly. To do that, open settings.py
and add the following lines in ludIssueTracker/
ludIssueTracker/urls.py:
from django.conf.urls import
patterns, include, url
from django.conf import settings
# Uncomment the next two lines to
enable the admin:
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns(‘’,
(r’^’,include(‘ludissues.
urls’)),
(r’^admin/’, include(admin.site.
Q Adminhomepage
Q The‘Addissue’menu
Q Thelistviewforissues
Click the Add button next to Issues. Here you
will notice that you can enter Owner, Status
and Summary for the issue. But what about
the opened_on and modified_on field that
we defined while modelling the app? They
are not here because they are not supposed
to be entered by the user. opened_on will
automatically set to the date time it is created
and modified_on will automatically set to the
datetimeonwhichanissueismodified.
Another cool thing is that the owner field is
automaticallypopulatedwithalltheusersinside
thesite.
We have defined our list view to show ID,
name, status, owner and ‘modified on’ in the
model. You can get to this view by navigating to
http://localhost:8000/admin/ludissues/issue/.
template names
#which will be looked in the default
template
#directories
url(r’^$’,’object_
list’,info,name=’issue-list’),
url(r’^(?Pobject_
idd+)/$’,’object_
detail’,info,name=’issue-detail’),
)
To display an issue list and details, we are using
a Django feature called generic views. In this
case we are using views called list and details.
This allow us to create an issue list view and
issue detail view. These views are then applied
using the issue_list.html and issue_detail.html
template. In the following steps we will create
the template files.
The Python Book 65
Python essentials
10Creatingthetemplatefiles
Templates will be loaded from the
ludIssueTracker/ludIssueTracker/templates
directory. In Django, we start with the
ludIssueTracker/ludIssueTracker/templates/
base.html template. Think of it as the master
templatewhichcanbeinheritedbyslaveones.
ludIssueTracker/ludIssueTracker/templates/
base.html
!DOCTYPE html PUBLIC “-//W3C//DTD
XHTML Strict//EN”
“ HYPERLINK “http://www.w3.org/
TR/xhtml1/DTD/xhtml1-strict.dtd”
http://www.w3.org/TR/xhtml1/DTD/
xhtml1-strict.dtd”
html
head
title{% block title %}{%
endblock %}LUD Issues/title
link rel=”stylesheet”
href=”{{ MEDIA_URL }}style.css”
type=”text/css” media=”screen” /
/head
body
div id=”hd”
h1LUD Issue
Tracker/span/h1
/div
div id=”mn”
ul
lia
href=”{% url issue-list %}”
class=”sel”View Issues/a/li
lia
href=”/admin/”Admin Site/a/li
/ul
/div
div id=”bd”
{% block content %}
{% endblock %}
/div
/body
/html
{{variablename}}representsaDjangovariable.
(% block title %} represents blocks. Contents
of a block are evaluated by Django and are
displayed. These blocks can be replaced by the
childtemplates.
Q ThemagazineIssueTrackerinaction–listofissues
urls)),
(r’^media/
(?Ppath.*)$’,’django.views.static.
serve’,
{‘document_root’:settings.
MEDIA_ROOT})
)
Now we need to create the issue_list.html
template. This template is responsible for
displayingalltheissuesavailableinthesystem.
ludIssueTracker/ludIssueTracker/templates/
ludissues/issue_list.html
{% extends ‘base.html’ %}
{% block title %}View Issues - {%
endblock %}
{% block content %}
table cellspacing=”0”
class=”column-options”
tr
thIssue/th
thDescription/th
thStatus/th
thOwner/th
/tr
{% for issue in object_list %}
tr
tda href=”{% url issue-
detail issue.id %}”{{ issue.id }}/
a/td
tda href=”{% url issue-
detail issue.id %}”{{ issue.name
}}/a/td
td{{ issue.status }}/td
td{{ issue.owner}}/td
/tr
{% endfor %}
/table
{% endblock %}
Here we are inheriting the base.html file that we
created earlier. {% for issue in object_list %}
runs on the object sent by the urls.py. Then we
are iterating on the object_list for issue.id and
issue.name.
Now we will create issue_detail.html. This
template is responsible for displaying the detail
viewofacase.
ludIssueTracker/ludIssueTracker/templates/
ludissues/issue_detail.html
{% extends ‘base.html’ %}
{% block title %}Issue #{{ object.id
}} - {% endblock %}
{% block content %}
h2Issue #{{ object.id }} span{{
object.status }}/span/h2
div class=”issue”
h2Information/h2
div class=”date”
p class=”cr”Opened {{
object.opened_on }} ago/p
p class=”up”Last modified
{{ object.modified_on }} ago/p
/div
div class=”clear”nbsp;/div
div class=”block w49 right”
p class=”ass title”Owner/
p
p class=”ass”{{ object.
owner }}/p
/div
div class=”clear”nbsp;/div
div class=”block”
p class=”des
title”Summary/p
p class=”des”{{ object.
summary }}/p
/div
/div
{% endblock %}
And that’s everything! The issue tracker app is
now complete and ready to use. You can now
point your browser at localhost:8000 to start
usingtheapp.
66 The Python Book
Python essentials
Pythonisaprogramminglanguagethatletsyouworkmorequicklyand
integrateyoursystemsmoreeffectively.Today,Pythonisoneofthemost
popularprogramminglanguagesintheopensourcespace.Lookaround
andyouwillfinditrunningeverywhere,fromvariousconfigurationtools
toXMLparsing.Hereisthecollectionof50gemstomakeyourPython
experienceworthwhile…
Basics
1.RunningPythonscripts
On most of the UNIX systems, you can run
Pythonscriptsfromthecommandline.
$ python mypyprog.py
2.RunningPython
programsfrom
Pythoninterpreter
The Python interactive interpreter makes it
easy to try your first steps in programming and
using all Python commands. You just issue each
command at the command prompt (), one by
one,andtheanswerisimmediate.
Python interpreter can be started by issuing
thecommand:
$ python
kunal@ubuntu:~$ python
Python 2.6.2 (release26-maint, Apr
19 2009, 01:56:41)
[GCC 4.3.3] on linux2
Type “help”, “copyright”, “credits”
or “license” for more information.
 type commands here
Inthisarticle,allthecodestartingatthe
symbolismeanttobegivenatthe
Pythonprompt.
It is also important to remember that Python
takes tabs very seriously – so if you are
receiving any error that mentions tabs, correct
thetabspacing.
3.Dynamictyping
In Java, C++, and other statically typed
languages, you must specify the data type of
the function return value and each function
argument. On the other hand, Python is
a dynamically typed language. In Python
you never have to explicitly specify the data
type of anything. Based on what value you
assign, Python will keep track of the data
typeinternally.
50
Python
tips
The Python Book 67
Python essentials
x,y = my_function.minmax(25, 6.3)
9.Moduledefinednames
Example:
The built-in function ‘dir()’ can be used to find
out which names a module defines. It returns a
sortedlistofstrings.
 import time
 dir(time)
[‘__doc__’, ‘__file__’, ‘__name__’,
‘__package__’, ‘accept2dyear’,
‘altzone’, ‘asctime’, ‘clock’,
‘ctime’, ‘daylight’, ‘gmtime’,
‘localtime’, ‘mktime’, ‘sleep’,
‘strftime’, ‘strptime’, ‘struct_
time’, ‘time’, ‘timezone’, ‘tzname’,
‘tzset’]
10.Moduleinternal
documentation
You can see the internal documentation (if
available) of a module name by looking at
.__doc__.
Example:
 import time
 print time.clock.__doc__
clock() - floating point number
This example returns the CPU time or real time
since the start of the process or since the first
call to clock(). This has as much precision as the
systemrecords.
11.Passingarguments
toaPythonscript
Pythonletsyouaccesswhateveryouhavepassed
to a script while calling it. The ‘command line’
contentisstoredinthesys.argvlist.
import sys
print sys.argv
12.Loadingmodulesor
commandsatstartup
You can load predefined modules or
commands at the startup of any Python
script by using the environment variable
$PYTHONSTARTUP. You can set environment
variable $PYTHONSTARTUP to a file which
contains the instructions load necessary
modulesorcommands.
13.Convertingastring
todateobject
You can use the function ‘DateTime’ to convert a
stringtoadateobject.
Example:
from DateTime import DateTime
dateobj = DateTime(string)
14.Convertingalist
toastringfordisplay
You can convert a list to string in either of the
followingways.
1stmethod:
 mylist = [‘spam’, ‘ham’, ‘eggs’]
 print ‘, ‘.join(mylist)
spam, ham, eggs
2ndmethod:
 print ‘n’.join(mylist)
spam
ham
eggs
15.Tabcompletion
inPythoninterpreter
You can achieve auto completion inside Python
interpreterbyaddingtheselinestoyour.pythonrc
file(oryourfileforPythontoreadonstartup):
import rlcompleter, readline
readline.parse_and_bind(‘tab: complete’)
This will make Python complete partially typed
function, method and variable names when you
presstheTabkey.
16.Python
documentationtool
Youcanpopupagraphicalinterfaceforsearching
thePythondocumentationusingthecommand:
$ pydoc -g
Youwillneedpython-tkpackageforthistowork.
17.Python
documentationserver
You can start an HTTP server on the given port on
thelocalmachine.Thiswillgiveyouanice-looking
access to all Python documentation, including
third-partymoduledocumentation.
$ pydoc -p portNumber
18.Pythondevelopment
software
There are plenty of tools to help with Python
development.Hereareafewimportantones:
IDLE: The Python built-in IDE, with
autocompletion, function signature popup help,
andfileediting.
IPython: Another enhanced Python shell with
tab-completionandotherfeatures.
Eric3: A GUI Python IDE with autocompletion,
classbrowser,built-inshellanddebugger.
WingIDE: Commercial Python IDE with
free licence available to open-source
developerseverywhere.
4.Pythonstatements
Python uses carriage returns to separate
statements, and a colon and indentation to
separate code blocks. Most of the compiled
programming languages, such as C and C++, use
semicolons to separate statements and curly
bracketstoseparatecodeblocks.
5. ==and=operators
Python uses ‘==’ for comparison and ‘=’ for
assignment. Python does not support inline
assignment, so there’s no chance of accidentally
assigning the value when you actually want to
compareit.
6.Concatenatingstrings
Youcanuse‘+’toconcatenatestrings.
 print ‘kun’+’al’
kunal
7.The__init__method
The __init__ method is run as soon as
an object of a class is instantiated. The
method is useful to do any initialization
you want to do with your object. The
__init__ method is analogous to a constructor in
C++,C#orJava.
Example:
class Person:
def __init__(self, name):
self.name = name
def sayHi(self):
print ‘Hello, my name is’, self.name
p = Person(‘Kunal’)
p.sayHi()
Output:
[~/src/python $:] python initmethod.py
Hello, my name is Kunal
8.Modules
To keep your programs manageable as they
grow in size, you may want to break them up into
several files. Python allows you to put multiple
function definitions into a file and use them as a
modulethatcanbeimportedintootherscriptsand
programs.Thesefilesmusthavea.pyextension.
Example:
# file my_function.py
def minmax(a,b):
if a = b:
min, max = a, b
else:
min, max = b, a
return min, max
Module Usage
import my_function
68 The Python Book
Python essentials
19.Executingfunctions
atthetimeofPython
interpretertermination
You can use ‘atexit’ module to execute functions
atthetimeofPythoninterpretertermination.
Example:
def sum():
print(4+5)
def message():
print(“Executing Now”)
import atexit
atexit.register(sum)
atexit.register(message)
Output:
Executing Now
9
20.Convertingfrominteger
tobinary,hexadecimal
andoctal
Python provides easy-to-use functions – bin(),
hex() and oct() – to convert from integer to binary,
decimalandoctalformatrespectively.
Example:
 bin(24)
‘0b11000’
 hex(24)
‘0x18’
 oct(24)
‘030’
21.Convertingany
charsettoUTF-8
You can use the following function to convert any
charsettoUTF-8.
data.decode(“input_charset_here”).
encode(‘utf-8’)
22.Removing
duplicatesfromlists
If you want to remove duplicates from a list,
just put every element into a dict as a key (for
example with ‘none’ as value) and then check
dict.keys().
from operator import setitem
def distinct(l):
d = {}
map(setitem, (d,)*len(l), l, [])
return d.keys()
23.Do-whileloops
Since Python has no do-while or do-until loop
constructs (yet), you can use the following
methodtoachievesimilarresults:
while True:
do_something()
if condition():
break
24.Detectingsystem
platform
To execute platform-specific functions, it is very
usefultodetecttheplatformonwhichthePython
interpreter is running. You can use ‘sys.platform’
tofindoutthecurrentplatform.
Example:
OnUbuntuLinux
 import sys
 sys.platform
‘linux2’
OnMacOSXSnowLeopard
 import sys
 sys.platform
‘darwin’
25.Disablingandenabling
garbagecollection
Sometimes you may want to enable or disable
the garbage collector at runtime. You can
use the ‘gc’ module to enable or disable the
garbagecollection.
Example:
 import gc
 gc.enable
built-in function enable
 gc.disable
built-in function disable
26.UsingC-basedmodules
forbetterperformance
Many Python modules ship with counterpart
C modules. Using these C modules will
give a significant performance boost in
complex applications.
Example:
cPickle instead of Pickle, cStringIO
instead of StringIO .
27.Calculatingmaximum,
minimumandsum
outofanylistoriterable
Youcanusethefollowingbuilt-infunctions.
max:Returnsthelargestelementinthelist.
min:Returnsthesmallestelementinthelist.
sum: This function returns the sum of all
elements in the list. It accepts an optional
second argument: the value to start with when
summing(defaultsto0).
28.Representing
fractionalnumbers
Fraction instance can be created using the
followingconstructor:
Fraction([numerator
[,denominator]])
29.Performing
mathoperations
The ‘math’ module provides a plethora of
mathematical functions. These work on integer
and float numbers, except complex numbers.
For complex numbers, a separate module is
used,called‘cmath’.
Forexample:
math.acos(x): Return arc cosine of
x.
math.cos(x): Returns cosine of x.
math.factorial(x) : Returns x
factorial.
30.Workingwitharrays
The ‘array’ module provides an efficient way to
use arrays in your programs. The ‘array’ module
definesthefollowingtype:
array(typecode [, initializer])
Once you have created an array object, say
myarray,youcanapplyabunchofmethodstoit.
Hereareafewimportantones:
myarray.count(x): Returns the
number of occurrences of x in a.
myarray.extend(x): Appends x at the
end of the array.
myarray.reverse(): Reverse the
order of the array.
31.Sortingitems
The ‘bisect’ module makes it very easy to keep
lists in any possible order. You can use the
following functions to order lists.
bisect.insort(list, item [, low [,
high]])
Inserts item into list in sorted order. If item is
already in the list, the new entry is inserted to
therightofanyexistingentries.
bisect.insort_left(list, item [, low
[, high]])
Inserts item into list in sorted order. If item is
already in the list, the new entry is inserted to
theleftofanyexistingentries.
Built-in
modules
The Python Book 69
Python essentials
32.Usingregular
expression-based
search
The ‘re’ module makes it very easy to use regxp-
based searches. You can use the function
‘re.search()’ with a regexp-based expression.
Checkouttheexamplebelow.
Example:
 import re
 s = “Kunal is a bad boy”
 if re.search(“K”, s): print
“Match!” # char literal
...
Match!
 if re.search(“[@A-Z]”, s): print
“Match!” # char class
... # match either at-sign or capital
letter
Match!
 if re.search(“d”, s): print
“Match!” # digits class
...
33.Workingwithbzip2(.bz2)
compressionformat
You can use the module ‘bz2’ to read and write
datausingthebzip2compressionalgorithm.
bz2.compress() : For bz2
compression
bz2.decompress() : For bz2
decompression
Example:
# File: bz2-example.py
import bz2
MESSAGE = “Kunal is a bad boy”
compressed_message = bz2.
compress(MESSAGE)
decompressed_message = bz2.
decompress(compressed_message)
print “original:”, repr(MESSAGE)
print “compressed message:”,
repr(compressed_message)
print “decompressed message:”,
repr(decompressed_message)
Output:
[~/src/python $:] python bz2-
example.py
original: ‘Kunal is a bad boy’
compressed message: ‘BZh91AYSYxc4
x0fGx98x00x00x02x15x80@x00
x00x084%x8a x00”x00x0cx84r
x03Cxa2xb0xd6sxa5xb3x19x00
xf8xbbx92)xc2x84x86 zxc0’
decompressed message: ‘Kunal is a
bad boy’
34.UsingSQLitedatabase
withPython
SQLiteisfastbecomingaverypopularembedded
database because of its zero configuration
needed, and superior levels of performance. You
can use the module ‘sqlite3’ in order to work with
SQLitedatabases.
Example:
 import sqlite3
 connection = sqlite.connect(‘test.
db’)
 curs = connection.cursor()
 curs.execute(‘’’create table item
... (id integer primary key, itemno
text unique,
... scancode text, descr text, price
real)’’’)
sqlite3.Cursor object at 0x1004a2b30
35.Workingwithzipfiles
You can use the module ‘zipfile’ to work with
zipfiles.
zipfile.ZipFile(filename [, mode [,
compression [,allowZip64]]])
Open a zip file, where the file can be either a path
toafile(astring)orafile-likeobject.
zipfile.close()¶
Closethearchivefile.Youmustcall‘close()’before
exiting your program or essential records will not
bewritten.
zipfile.extract(member[, path[,
pwd]])
Extract a member from the archive to the current
working directory; ‘member’ must be its full
name (or a zipinfo object). Its file information
is extracted as accurately as possible. ‘path’
specifies a different directory to extract to.
‘member’ can be a filename or a zipinfo object.
‘pwd’isthepasswordusedforencryptedfiles.
36.UsingUNIX-style
wildcardstosearch
forfilenames
You can use the module ‘glob’ to find all the
pathnames matching a pattern according to the
rules used by the UNIX shell. *, ?, and character
rangesexpressedwith[]willbe matched.
Example:
 import glob
 glob.glob(‘./[0-9].*’)
[‘./1.gif’, ‘./2.txt’]
 glob.glob(‘*.gif’)
[‘1.gif’, ‘card.gif’]
 glob.glob(‘?.gif’)
[‘1.gif’]
37.Performingbasicfile
operations(copy,delete
andrename)
You can use the module ‘shutil’ to perform basic
file operation at a high level. This module works
with your regular files and so will not work with
special files like named pipes, block devices, and
soon.
shutil.copy(src,dst)
Copiesthefilesrctothefileordirectorydst.
shutil.copymode(src,dst)
Copiesthefilepermissionsfromsrctodst.
shutil.move(src,dst)
Movesafileordirectorytodst.
shutil.copytree(src, dst, symlinks
[,ignore]])
Recursivelycopyanentiredirectoryatsrc.
shutil.rmtree(path [, ignore_errors
[, onerror]])
Deletesanentiredirectory.
38.ExecutingUNIX
commandsfromPython
You can use module commands to execute UNIX
commands. This is not available in Python3 –
insteadyouneedtousethemodule‘subprocess’.
Example:
 import commands
 commands.getoutput(‘ls’)
‘bz2-example.pyntest.py’
39.Readingenvironment
variables
You can use the module ‘os’ to gather operating-
system-specificinformation:
Example:
 import os
 os.path module ‘posixpath’
from ‘/usr/lib/python2.6/posixpath.
pyc’ os.environ {‘LANG’: ‘en_
IN’, ‘TERM’: ‘xterm-color’, ‘SHELL’:
‘/bin/bash’, ‘LESSCLOSE’:
‘/usr/bin/lesspipe %s %s’,
‘XDG_SESSION_COOKIE’:
‘925c4644597c791c704656354adf56d6-
1257673132.347986-1177792325’,
‘SHLVL’: ‘1’, ‘SSH_TTY’: ‘/dev/
pts/2’, ‘PWD’: ‘/home/kunal’,
‘LESSOPEN’: ‘| /usr/bin
lesspipe
......}
 os.name
‘posix’
 os.linesep
‘n’
70 The Python Book
Python essentials
40.Sendingemail
You can use the module ‘smtplib’ to send email
using an SMTP (Simple Mail Transfer Protocol)
clientinterface.
smtplib.SMTP([host [, port]])
Example(sendanemailusingGoogleMail
SMTPserver):
import smtplib
# Use your own to and from email
address
fromaddr = ‘kunaldeo@gmail.com’
toaddrs = ‘toemail@gmail.com’
msg = ‘I am a Python geek. Here is
the proof.!’
# Credentials
# Use your own Google Mail
credentials while running the
program
username = ‘kunaldeo@gmail.com’
password = ‘xxxxxxxx’
# The actual mail send
server = smtplib.SMTP(‘smtp.gmail.
com:587’)
# Google Mail uses secure
connection for SMTP connections
server.starttls()
server.login(username,password)
server.sendmail(fromaddr, toaddrs,
msg)
server.quit()
41.Accessing
FTPserver
‘ftplib’ is a fully fledged client FTP module for
Python. To establish an FTP connection, you
canusethefollowingfunction:
ftplib.FTP([host [, user [, passwd
[, acct [, timeout]]]]])
Example:
host = “ftp.redhat.com”
username = “anonymous”
password = “kunaldeo@gmail.com”
import ftplib
import urllib2
ftp_serv = ftplib.
FTP(host,username,password)
# Download the file
u = urllib2.urlopen (“ftp://
ftp.redhat.com/pub/redhat/linux/
README”)
# Print the file contents
print (u.read())
Output:
[~/src/python $:] python
ftpclient.py
OlderversionsofRedHatLinuxhavebeenmoved
to the following location: ftp://archive.download.
redhat.com/pub/redhat/linux/
42.Launchingawebpage
withthedefaultweb
browser
The ‘webbrowser’ module provides a convenient
way to launch webpages using the default
webbrowser.
Example(launchgoogle.co.ukwithsystem’s
defaultwebbrowser):
 import webbrowser
 webbrowser.open(‘http://google.
co.uk’)
True
43.Creatingsecurehashes
The ‘hashlib’ module supports a plethora of
secure hash algorithms including SHA1, SHA224,
SHA256,SHA384,SHA512andMD5.
Example(createhexdigestofthegiventext):
 import hashlib
# sha1 Digest
 hashlib.sha1(“MI6 Classified
Information 007”).hexdigest()
‘e224b1543f229cc0cb935a1eb9593
18ba1b20c85’
# sha224 Digest
 hashlib.sha224(“MI6 Classified
Information 007”).hexdigest()
‘3d01e2f741000b0224084482f905e9b7b97
7a59b480990ea8355e2c0’
# sha256 Digest
 hashlib.sha256(“MI6 Classified
Information 007”).hexdigest()
‘2fdde5733f5d47b672fcb39725991c89
b2550707cbf4c6403e fdb33b1c19825e’
# sha384 Digest
 hashlib.sha384(“MI6 Classified
Information 007”).hexdigest()
‘5c4914160f03dfbd19e14d3ec1e74bd8b99
dc192edc138aaf7682800982488daaf540be
9e0e50fc3d3a65c8b6353572d’
# sha512 Digest
 hashlib.sha512(“MI6 Classified
Information 007”).hexdigest()
‘a704ac3dbef6e8234578482a31d5ad29d25
2c822d1f4973f49b850222edcc0a29bb89077
8aea807a0a48ee4ff8bb18566140667fbaf7
3a1dc1ff192febc713d2’
# MD5 Digest
 hashlib.md5(“MI6 Classified
Information 007”).hexdigest()
‘8e2f1c52ac146f1a999a670c826f7126’
44.Seedingrandom
numbers
You can use the module ‘random’ to generate
a wide variety of random numbers. The most
used one is ‘random.seed([x])’. It initialises
the basic random number generator. If x is
omitted or None, current system time is used;
current system time is also used to initialise the
generatorwhenthemoduleisfirstimported.
45.WorkingwithCSV
(comma-separated
values)files
CSVfilesareverypopularfordataexchangeover
theweb.Usingthemodule‘csv’,youcanreadand
writeCSVfiles.
Example:
import csv
# write stocks data as comma-
separated values
writer = csv.writer(open(‘stocks.
csv’, ‘wb’, buffering=0))
writer.writerows([
(‘GOOG’, ‘Google, Inc.’, 505.24, 0.47,
0.09),
(‘YHOO’, ‘Yahoo! Inc.’, 27.38, 0.33,
1.22),
(‘CNET’, ‘CNET Networks, Inc.’, 8.62,
-0.13, -1.49)
])
# read stocks data, print status
messages
stocks = csv.reader(open(‘stocks.
csv’, ‘rb’))
status_labels = {-1: ‘down’, 0:
‘unchanged’, 1: ‘up’}
for ticker, name, price, change, pct
in stocks:
status = status_
labels[cmp(float(change), 0.0)]
print ‘%s is %s (%s%%)’ % (name,
status, pct)
46.Installingthird-
partymodulesusing
setuptools
‘setuptools’ is a Python package which lets you
download, build, install, upgrade and uninstall
packages very easily.
To use ‘setuptools’ you will need to install
from your distribution’s package manager.
After installation you can use the command
‘easy_install’ to perform Python package
management tasks.
The Python Book 71
Python essentials
Example(installingsimplejsonusing
setuptools):
kunal@ubuntu:~$ sudo easy_install
simplejson
Searching for simplejson
Reading http://pypi.python.org/simple/
simplejson/
Reading http://undefined.org/
python/#simplejson
Best match: simplejson 2.0.9
Downloading http://pypi.python.
org/packages/source/s/simplejson/
simplejson-2.0.9.tar.gz#md5=af5e67a39c
a3408563411d357e6d5e47
Processing simplejson-2.0.9.tar.gz
Running simplejson-2.0.9/setup.py
-q bdist_egg --dist-dir /tmp/easy_
install-FiyfNL/simplejson-2.0.9/egg-
dist-tmp-3YwsGV
Adding simplejson 2.0.9 to easy-
install.pth file
Installed /usr/local/lib/python2.6/
dist-packages/simplejson-2.0.9-py2.6-
linux-i686.egg
Processing dependencies for simplejson
Finished processing dependencies for
simplejson
47.Loggingtosystemlog
Youcanusethemodule‘syslog’towritetosystem
log. ‘syslog’ acts as an interface to UNIX syslog
libraryroutines.
Example:
import syslog
syslog.syslog(‘mygeekapp: started
logging’)
for a in [‘a’, ‘b’, ‘c’]:
b = ‘mygeekapp: I found letter ‘+a
syslog.syslog(b)
syslog.syslog(‘mygeekapp: the script
goes to sleep now, bye,bye!’)
Output:
$ python mylog.py
$ tail -f /var/log/messages
Nov 8 17:14:34 ubuntu -- MARK --
Nov 8 17:22:34 ubuntu python:
mygeekapp: started logging
Nov 8 17:22:34 ubuntu python:
mygeekapp: I found letter a
Nov 8 17:22:34 ubuntu python:
mygeekapp: I found letter b
Nov 8 17:22:34 ubuntu python:
mygeekapp: I found letter c
Nov 8 17:22:34 ubuntu python:
mygeekapp: the script goes to sleep
now, bye,bye!
48.GeneratingPDF
documents
‘ReportLab’ is a very popular module for PDF
generationfromPython.
PerformthefollowingstepstoinstallReportLab
$ wget http://www.reportlab.org/ftp/
ReportLab_2_3.tar.gz
$ tar xvfz ReportLab_2_3.tar.gz
$ cd ReportLab_2_3
$ sudo python setup.py install
Forasuccessfulinstallation,youshouldseea
similarmessage:
############SUMMARY INFO###########
###################################
#Attempting install of _rl_accel, sgmlop
 pyHnj
#extensions from ‘/home/kunal/python/
ReportLab_2_3/src/rl_addons/rl_accel’
###################################
#Attempting install of _renderPM
#extensions from ‘/home/kunal/python/
ReportLab_2_3/src/rl_addons/renderPM’
# installing with freetype version 21
###################################
Example:
 from reportlab.pdfgen.canvas import
Canvas
# Select the canvas of letter page size
 from reportlab.lib.pagesizes import
letter
 pdf = Canvas(“bond.pdf”, pagesize =
letter)
# import units
 from reportlab.lib.units import cm,
mm, inch, pica
 pdf.setFont(“Courier”, 60)
 pdf.setFillColorRGB(1, 0, 0)
 pdf.drawCentredString(letter[0] / 2,
inch * 6, “MI6 CLASSIFIED”)
 pdf.setFont(“Courier”, 40)
 pdf.drawCentredString(letter[0] / 2,
inch * 5, “For 007’s Eyes Only”)
# Close the drawing for current page
 pdf.showPage()
# Save the pdf page
 pdf.save()
Output:
@image:pdf.png
@title: PDF Output
49.UsingTwitterAPI
You can connect to Twitter using the ‘Python-
Twitter’module.
Performthefollowingstepstoinstall
Python-Twitter:
$ wget http://python-twitter.
googlecode.com/files/python-twitter-
0.6.tar.gz
$ tar xvfz python-twitter*
$ cd python-twitter*
$ sudo python setup.py install
Example(fetchingfollowerslist):
 import twitter
# Use you own twitter account here
 mytwi = twitter.Api(username=’kunald
eo’,password=’xxxxxx’)
 friends = mytwi.GetFriends()
 print [u.name for u in friends]
[u’Matt Legend Gemmell’, u’jono wells’,
u’The MDN Big Blog’, u’Manish Mandal’,
u’iH8sn0w’, u’IndianVideoGamer.com’,
u’FakeAaron Hillegass’, u’ChaosCode’,
u’nileshp’, u’Frank Jennings’,..’]
50.DoingYahoo!news
search
You can use the Yahoo! search SDK to access
Yahoo!searchAPIsfromPython.
Performthefollowingstepstoinstallit:
$wget http://developer.yahoo.com/
download/files/yws-2.12.zip
$ unzip yws*
$ cd yws*/Python/pYsearch*/
$ sudo python setup.py install
Example:
# Importing news search API
 from yahoo.search.news import
NewsSearch
 srch = NewsSearch(‘YahooDemo’,
query=’London’)
# Fetch Results
 info = srch.parse_results()
 info.total_results_available
41640
 info.total_results_returned
10
 for result in info.results:
... print “’%s’, from %s” %
(result[‘Title’], result[‘NewsSource’])
...
‘Afghan Handover to Be Planned at London
Conference, Brown Says’, from Bloomberg
.................
Third-partymodules
72 The Python Book
“WithPython,youcantweak
andrealiseyouridealsystemset-up”
74 Python for professionals
Put your skills to professional use
82 Extensions for XBMC
Enhance XBMC with this tutorial
88 Scientific computing
Get to grips with NumPy
92 Instant messaging
Get chatting using Python
98 Replace your shell
Use Python for your primary shell
102 Python for system admins
How Python helps system administration
92
Python
Work
with
The Python Book 73
82 88
74
74 The Python Book
Work with Python
Pythonisrelieduponbywebdevelopers,engineersand
academicresearchersacrosstheworld.Here’showtoputyour
Pythonskillstoprofessionaluse
PYTHON FOR
PROFESSIONALS
The Python Book 75
Work with Python
Systemadministration
System administration tasks are some of the most
annoying things that you need to deal with when you
have to maintain your own system. Because of this,
system administrators have constantly been trying to find
ways to automate these types of tasks to maximise their
time.Theystartedwithbasicshellscripts,andthenmoved
on to various scripting languages. For a long time, Perl had
been the language of choice for developing these types
of maintenance tools. However, Python is now growing in
popularity as the language to use. It has reached the point
where most Linux distributions have a Python interpreter
included in order to run system scripts, so you shouldn’t
haveanyexcusefornotwritingyourownscripts.
Because you will be doing a lot system level work, you
will have most need of a couple of key Python modules.
The first module is ‘os’. This module provides the bulk of
the interfaces to interacting with the underlying system.
The usual first step is to look at the environment your
script is running in to see what information might exist
there to help guide your script. The following code gives
you a mapping object where you can interact with the
environmentvariablesactiverightnow:
import os
os.environ
You can get a list of the available environment variables
with the function “os.environs.keys()”, and then access
individual variables with “os.environs[key]”. These
environment variables are used when you spawn a
subprocess, as well. So you will want to change values,
like the PATH or the current working directory, in order
for you to run these subprocesses correctly. While there
is a “putenv” function that edits these values for you, it
unfortunately does not exist on all systems. So the better
way to do this is to edit the values directly within the
environsmapping.
Another category of tasks you may want to automate
is when working with files. For example, you can get the
currentworkingdirectorywithcodelike
cwd = os.getcwd()
Youcanthengetalistofthefilesinthisdirectorywith
os.listdir(cwd)
You can move around the file system with the function
“os.chdir(new_path)”. Once you’ve found the file you are
interested in, you can open it with “os.open()” and open it
for reading, writing and/or appending. You can then read
or write to it with the functions “os.read()” and “os.write()”.
Oncedone,youcanclosethefilewith“os.close()”.
Left Pythonscripts
enableyoutoinstruct
andinteractwithyour
operatingsystem
GetthemostoutofPythoninhandlingalloftheday-to-day
upkeepthatkeepsyoursystemhealthy
RunningsubprocessesfromPython
The underlying philosophy of Unix is to build small, specialised
programs that do one job extremely well. You then chain these
together to build more complex behaviours. There is no reason
why you shouldn’t use the same philosophy within your Python
scripts. There are several utility programs available to use with
very little work on your part. The older way of handling this was
through using functions like “popen()” and “spawnl()” from the
os module, but a better way of running other programs is by
using the subprocess module instead. You can then launch a
program, like ls, by using:
import subprocess
subprocess.run([‘ls’
, ‘-l’])
This provides you with a long file listing for the current
directory. The function “run()” was introduced in Python
3.5 and is the suggested way of handling this. If you have an
older version, or if you require more control than that, then
you can employ the underlying “popen()” function that we
mentioned earlier instead. If you want to get the output, you
can use the following:
cmd_output = subprocess.run([‘ls’
, ‘-l’],
stdout=subprocess.PIPE)
The variable “cmd_output” is a CompletedProcess object that
containsthereturncodeandastringholdingthestdoutoutput.
SYSTEMADMINISTRATION
BASH,PERL,PYTHON
OPERATINGSYSTEM
CPU FILES/IO
Onceyouhaveyourscript
allwrittenup,youmaywant
toschedulethemtorun
automaticallywithoutyour
intervention.OnUnixsystems,
youcanhavecronrunyour
scriptonwhateverschedule
isnecessary.Theutility
“crontab-l”liststhecurrent
contentsofyourcronfile,and
“crontab-e”letsyoueditthe
scheduledjobsthatyouwant
crontorun.
Scheduling
withcron
76 The Python Book
Work with Python
With the content and the bulk of the computing hosted
on a server, a web application can better guarantee
a consistent experience for the end user. The popular
Django framework provides a very complete environment
of plugins and works on the DRY principle (Don’t Repeat
Yourself). Because of this, you should be able to build
your web application very quickly. Since Django is built
on Python, you should be able to install it with “sudo pip
install Django”. Most distributions should have a package
for Django, too. Depending on what you want to do with
your app, you may need to install a database like MySQL or
postgresqltostoreyourapplicationdata.
There are Django utilities available to automatically
generateastartingpointforyournewproject’scode:
django-admin startproject newsite
This command creates a file named “manage.py” and
a subdirectory named “newsite”. The file “manage.py”
contains several utility functions you can use to administer
your new application. The newly created subdirectory
contains the files “__init__.py”, “settings.py”, “urls.py” and
“wsgi.py”. These files and the subdirectory they reside in
comprise a Python package that gets loaded when your
web site is started up. The core configuration for your site
can be found in the file “settings.py”. The URL declarations,
basically a table of contents for your site, are stored in the
file “urls.py”. The file “wsgi.py” contains an entry point for
WSGI-compatiblewebservers.
Once your application is done, it should be hosted on a
properly configured and hardened web server. But, this is
inconvenient if you are in the process of developing your
web application. To help you out, Django has a web server
built into the framework. You can start it up by changing
directory to the “newsite” project directory and running the
followingcommand:
Left Pythoninterpreterswork
withyourdatabasestopowera
webserver
Bottom TheModel-View-
Controllerarchitectureisoften
usedforUIs
python manage.py runserver
This will start up a server listening to port 8000 on your
local machine. Because this built in server is designed to
be used for development, it automatically reloads your
Python code for each request. This means that you don’t
needtorestarttheservertoseeyourcodechanges.
All of these steps get you to a working project. You are
now ready to start developing your applications. Within the
“newsite”subdirectory,youcantype:
python manage.py startapp newapp
This will create a new subdirectory named “newapp”, with
the files “models.py”, “tests.py” and “views.py”, among
others.Thesimplestpossibleviewconsistsofthecode:
from django.http import HttpResponse
def index(request):
return HttpResponse(“Hello world”)
This isn’t enough to make it available, however. You will
also need to create a URLconf for the view. If the file
“urls.py”doesn’texistyet,createitandthenaddthecode:
from django.conf.urls import url
from . Import views
urlpatterns = [ url(r’^$’
, views.index,
name=‘index’), ]
The last step is to get the URL registered within your
project.Youcandothiswiththecode
from django.conf.urls import include, url
from django.contrib import admin
urlpatterns = [ url(r’^newapp/’
, include(‘newapp.
urls’)
),
Webdevelopment
Pythonhas several frameworksavailable forallofyour
variouswebdevelopmenttasks. Wewilllook at someofthe
morepopularones
Whenyoustartdeveloping
yourownapplications,you
maybeginadescentinto
dependencyhell.Several
Pythonpackagesdepend
onotherPythonpackages.
Thisisitsstrength,butalso
itsweakness.Luckily,you
havevirtualenvavailable
tohelptamethisjungle.
Youcancreatenewvirtual
environmentsforeachofyour
projects.Thankfullywiththis,
youcanbesuretocaptureall
ofthedependenciesforyour
ownpackage.
Virtual
environments
USER
WEBSERVER
DATABASE
PYTHON
INTERPRETER
MODEL
USER
VIEW CONTROLLER
Manipulates
Sees
Updates
Uses
The Python Book 77
Work with Python
url(r’^admin’
, admin.site.urls), ]
This needs to be put in the “urls.py” file for the main
project. You can now pull up your newly created
applicationwiththeURLhttp://localhost:8000/newapp/.
The last part of many applications is the database
side. The actual connection details to the database, like
the username and password, are contained in the file
“settings.py”. This connection information is used for
all of the applications that exist within the same project.
You can create the core database tables for your site with
thiscommand:
python manage.py migrate
For your own applications, you can define the data model
you need within the file “models.py”. Once the data
model is created, you can add your application to the
INSTALLED_APPS section of the “settings.py” so that
django knows to include it in any database activity. You
initializeitwith:
python manage.py makemigrations newapp
Once these migrations have been created, you need to
applythemtothedatabasebyusingthecommand:
python manage.py migrate
Any time you make changes to your model, you will need to
runthemakemigrationsandmigratestepsagain.
Once you have your application finished, you can make
the move to the final hosting server. Don’t forget to check
the available code within the Django framework before
puttingtoomuchworkintodevelopingyourowncode.
UsingthePyCharmIDE
THEEDITORPANE
Themaineditorpanecanbeconfiguredtomatchyour
ownstyle,orthestyleofoneoftheothermaineditors,
likeemacs.Ithandlessyntaxhighlighting,andeven
displayserrorlocations
THEPROJECTPANE
Thispaneisthecentrallocationforyourproject.Allof
yourfilesandlibrariesarelocatedhere.Right-clicking
inthepanebringsupadrop-downmenuwhereyoucan
addnewfilesorlibraries,rununittests,orevenstartup
adebugger
THESTATUSBARE
PyCharmdoesalotofworkbehindthescenes.
Thestatusbarhelpsyoukeeptrackofallofthese
backgroundprocesses
OtherPython
frameworks
WhileDjangoisoneofthemostpopularframeworksaround
fordoingwebdevelopment,itisbynomeanstheonlyone
around.Thereareseveralothersavailablethatmayproveto
beabetterfitforparticularproblemdomains.Forexample,
ifyouarelookingforareallyself-containedframework,you
couldlookatweb2py.Everythingyouneedtobeabletohave
acompletesystem,fromdatabasestowebserverstoa
ticketingsystem,areincludedaspartoftheframework.Itis
soself-containedthatitcanevenrunfromaUSBdrive
Ifyouneedevenlessofaframework,thereareseveral
mini-frameworksthatareavailable.Forexample,CherryPy
isapurelyPythonicmulti-threadedwebserverthatyou
canembedwithinyourownapplication.Thisisactually
theserverincludedwithTurboGearsandweb2py.Areally
popularmicroframeworkisaprojectcalledflask.Itincludes
integratedunittestingsupport,jinja2templatingandRESTful
requestdispatching.
Oneoftheoldestframeworksaroundiszope,nowupto
version3.ThislatestversionwasrenamedBlueBream.Zope
isfairlylow-level,however.Youmaybemoreinterestedin
lookingatsomeoftheotherframeworksthatarebuilton
topofwhatisprovidedbyzope.Forexample,pyramidisa
veryfast,easytouseframeworkthatfocusesonthemost
essentialfunctionsrequiredbymostwebapplications.To
thisend,itprovidestemplating,theservingofstaticcontent,
mappingofURLstocode,amongotherfunctions.Ithandles
thiswhileprovidingtoolsforapplicationsecurity.
Ifyouarelookingforsomeideas,thereareseveralopen
sourceprojectsthathavebeenbuiltusingtheseframeworks,
fromblogs,toforumstoticketingsystems.Theseprojectscan
providesomebest-practiceswhenyougotoconstructyour
ownapplication.
Whenyouareinthemiddleof
developingyourapplication,
youmayneedtohaveseveral
differentterminalwindows
openinordertohaveacode
editoropen,amonitoronthe
server,andpotentiallytesting
output.Ifyouaredoingthison
yourownmachine,thisisn’tan
issue.But,ifyouareworking
remotely,youshouldlookinto
usingtmux.Itcanprovidea
muchmorerobustterminal
environmentforyou.
Terminal
development
environments
78 The Python Book
Work with Python
Python has become one of the key languages used in
science. There is a huge number of packages available
to handle almost any task that you may have and,
importantly, Python knows what it isn’t good at. To deal
with this, Python has been designed to easily incorporate
code from C or FORTRAN. This way, you can offload any
heavycomputationstomoreefficientcode.
The core package of most of the scientific code
available is numpy. One of the problems in Python is that
the object oriented nature of the language is the source
of its inefficiencies. With no strict types, Python always
needs to check parameters on every operation. Numpy
provides a new datatype, the array, which helps solve
some of these issues. Arrays can only hold one type of
object, and because Python knows this it can use some
optimisations to speed things up to almost what you can
get from writing your code directly in C or FORTRAN. The
classic example of the difference is the for loop. Lets say
youwantedtoscaleavectorbysomevalue,somethinglike
a*b.InregularPython,thiswouldlooklike
for elem in b:
c.append(a * elem)
Innumpy,thiswouldlooklike:
a*b
Left Thenumpypackage
makesitsimpletovisualise
yourdata
So, not only is it faster, it is also written in a shorter, clearer
form. Along with the new datatype, numpy provides
overloaded forms of all of the operators that are of
most use, like multiplication or division. It also provides
optimised versions of several functions, like the trig
functions,totakeadvantageofthisnewdatatype.
The largest package available, that is built on top of
numpy, is scipy. Scipy provides sub-sections in several
areas of science. Each of these sub-sections need
to be imported individually after importing the main
scipy package. For example, if you are doing work with
Computationalscience
Pythonisfast becomingthe go-to languagefor
computationalscience
Spyder,theIDEforscientists
THEEDITORPANE
Thispaneiswhereyoucanopenandedityoursource
files.Abovethispanearebuttonstoallowyoutosimply
runthecode,orrunitunderadebugger.Underthe
debugger,youcansetbreakpointsandstepthrough
eachlineofcodeindividually
VARIABLEEXPLORER
Thevariableexplorerpaneletsyouaccessallofthe
datastructureswithinthecurrentPythoninterpreter.
Youneedtoactuallyrunyourcodeforanythingtoshow
uphere
IPYTHONCONSOLE
Theconsolewindowletsyouinteractdirectlywiththe
underlyinginterpreterthatwillbeusedwhenyoutryand
runyourcode
Oneofthereallypowerful
partsofIpython(orjupyter)
isthatitisbuiltwithaclient/
servermodel.Thismeansthat
itisrelativelyeasytosetup
multiplemachinestoactas
aserverpool.Youcanthen
farmoutmultipletasksto
theseothermachinestoget
evenmoreworkdone.While
thisdoesn’trunanyparticular
functioninparallel,itdoeslet
yourunlongerfunctionsinthe
backgroundwhileyouworkon
somethingelse.
Parallel
Python
The Python Book 79
Work with Python
Above Theabilitytogenerate
complexplotsisessential
Above JupyterNotebookisawebapplication thatisused
forcreatingandsharingdocumentsthatcontainlivecode
andequations
differential equations, you can use the “integrate” section
tosolvethemwithcodethatlookslike
import scipy
import scipy.integrate
result = scipy.integrate.quad(lambda x: sin(x), 0,
4.5)
Differential equations crop up in almost every scientific
field. You can do statistical analysis with the “stats”
section. If you want to do some signal processing, you can
use the “signal” section and the “fftpack” section. This
packageisdefinitelythefirststopforanyonewantingtodo
anyscientificprocessing.
Once you have collected your data, you usually need
to graph it, in order to get a visual impression of patterns
within it. The primary package you can use for this is
matplotlib. If you have ever used the graphics package
in R before, the core design of matplotlib has borrowed
quite a few ideas. There are two categories of functions for
graphing, low-level and high-level. High-level functions try
to take care of as many of the menial tasks, like creating a
plot window, drawing axes, selecting a coordinate system,
as possible. The low-level functions give you control over
almost every part of a plot, from drawing individual pixels
to controlling every aspect of the plot window. It also
borrowed the idea of drawing graphs into a memory based
window. This means that it can draw graphs while running
onacluster.
If you need to do symbolic math, you may be more used
to using something like Mathematica or Maple. Luckily,
you have sympy that can be used to do many of the same
things. You can use Python to do symbolic calculus, or to
solve algebraic equations. The one weird part of sympy is
that you need to use the “symbols()” function to tell sympy
Interactivescience
withjupyter
Foralotofscientificproblems,youneedtoplaywithyour
datainaninteractiveway.Theoriginalwayyouwoulddo
thiswastousetheIpythonwebnotebook.Thisprojecthas
sincebeenrenamedJupyter.Forthosewhohaveuseda
programlikeMathematicaorMaple,theinterfaceshould
seemveryfamiliar.Jupyterstartsaserverprocess,by
defaultonport8888,andthenwillopenawebbrowser
whereyoucanopenaworksheet.Likemostotherprograms
ofthistype,theentriesruninchronologicalorder,notin
theorderthattheyhappenontheworksheet.Thiscan
beabitconfusingatfirst,butitmeansthatifyougoto
editanearlierentry,allofthefollowingentriesneedtobe
re-executedmanuallyinordertopropagatethatchange
throughtherestofthecomputations.
Jupyterhassupportforprettyprintingmathwithin
theproducedwebpage.Youcanalsomixdocumentation
blocksandcodeblockswithinthesamepage.Thismeans
thatyoucanuseittoproduceverypowerfuleducational
material,wherestudentscanreadaboutsometechnique,
andthenactuallyrunitandseeitinaction.Bydefault,
Jupyterwillalsoembedmatplotlibplotswithinthesame
worksheetasaresultssection,soyoucanseeagraphof
somedataalongwiththecodethatgeneratedit.Thisis
hugeinthegrowingneedforreproduciblescience.Youcan
alwaysgobackandseehowanyanalysiswasdoneandbe
abletoreproduceanyresultatall.
Sometimesyouneedasmuch
speedasyourarecapableof
pushingonyourhardware.In
thesecases,youalwayshave
theoptionofusingCython.
ThisletsyoutakeCcodefrom
someotherproject,which
hasprobablyalreadybeen
optimised,anduseitwithin
yourownPythonprogram.In
scientificprogramming,you
arelikelytohaveaccessto
codethathasbeenworked
onfordecadesandishighly
specialised.Thereisnoneed
toredothedevelopment
effortthathasgoneintoit.
Theneedfor
speed whatvariablesarevalidtobeconsideredinyourequations.
You can then start doing manipulations using these
registeredvariables.
You may have large amounts of data that you need
to work with and analyze. If so, you can use the pandas
package to help deal with that. Pandas has support
for several different file formats, like CSV files, Excel
spreadsheets or HDF5. You can merge and join datasets,
or do slicing or subsetting. In order to get the best
performance out of the code, the heaviestlifting is done by
CythoncodethatincorporatesfunctionswritteninC.Quite
a few ideas on how to manipulate your data was borrowed
fromhowthingsaredoneinR.
You now have no reason not to start using Python for
your scientific work. You should be able to use it for almost
anyproblemthatcomesup!
80 The Python Book
Work with Python
ROS–RobotOperatingSystem
While you could simply write some code that runs on a
standard computer and a standard Linux distribution, this
is usually not optimal when trying to handle all of the data
processing that a robot needs when dealing with events in
realtime. When you reach this point, you may need to look at
a dedicated operating system – the Robot Operating System
(ROS). ROS is designed to provide the same type of interface
between running code the computer hardware it is running
on, with the lowest possible overhead. One of the really
powerful features of ROS is that it is designed to facilitate
communication between different processes running on the
computer, or potentially over multiple computers connected
over some type of network. Instead of each process being a
silo that is protected from all other processes, ROS is more of
a graph of processes with messages being passed between
them all.
Because ROS is a complete operating system, rather than
a library, it is wrong to think that you can use it in your Python
code. It is better to think that you can write Python code
that can be used in ROS. The fundamental design is to be as
agnostic as possible. This means that interfaces to your code
should be clean and not particularly care where they running
or who is talking to them. Then, it can be used within the graph
of processes running within ROS. There are standard libraries
available that allow you to do coordinate transformations,
useful for figuring out where sensors or limbs are in space.
There is a library available for creating preemptible tasks for
data processing, and another for creating and managing the
types of messages that can be handed around the various
processes. For extremely time-sensitive tasks, there is a
plugin library that allows you to write a C++ plugin that can be
loaded within ROS packages.
Robotics is the most direct way that your code can
interact with the world around you. It can read actual
sensor information and move real actuators and get real
workdone.
The first thing your robot needs is the ability to sense
the world around it. The one sense that we as humans feel
is most useful is sight. With web cameras being so cheap
and easy to connect to hardware, vision is easy to give to
your robot. The real problem is how to interpret this data.
Luckily, you can use the OpenCV project to do just that. It is
a vision package that can provide simple image gathering
and processing, to extremely complex functions like face
recognition and extraction of 3D objects. You can identify
and track objects moving through your field of view. You
can also use OpenCV to give you robot some reasoning
capabilities, too. OpenCV includes a set of functions
for machine learning, where you can do statistical
classificationordataclustering,anduseittofeeddecision
treesorevenneuralnetworks.
Another important sense that you may want to use is
sound. The jasper project is one that is developing a
complete voice control system. This project would
give you the structure you need to give your robot
the ability to listen for and respond to your verbal
commands. The project has gotten to the point where
you can give it a command and the voice recognition
software can translate this into text. You then need to
build a mapping of what pieces of text correspond to what
commandstoexecute.
There are lots of other sensors you could have, but this
begins to leave the realm of store-bought hardware. Most
other sensors, like temperature, pressure, orientation
or location, need specialised hardware that needs to
be interfaced to the computer brain for your robot. This
Roboticsandelectronics
Roboticsisthemostdirect interfacebetweenyour codeand
therealworldaround you
Whilewehaven’tdiscussed
whatkindofcomputertouse
foryourroboticsproject,you
shouldconsiderthefamous
RaspberryPi.Thistiny
computershouldbesmall
enoughtofitintoalmost
anyrobotstructurethatyou
mightbebuilding.Sinceitis
alreadyrunningLinuxand
Python,youshouldbeable
tosimplycopyyourcode
developmentworktothePi.
ItalsoincludesitsownIObus
sothatyoucanhaveitread
it’sownsensors.
RaspberryPi
IncontrasttotheRaspberry
Pi,whichrunsafullOSfrom
itsSDcard,theArduino
boardsaremicrocontrollers
ratherthancomplete
computers.Insteadof
runninganOS,theArduino
platformexecutescodethat
isinterpretedbyitsfirmware.
Itismainlyusedtointerface
withhardwaresuchasmotors
andservos,sensors,and
devicessuchasLEDs,and
isincrediblycapableinthis
regard.Arduinosarewidely
usedinroboticsprojects
andcanbeapowerful
complementtothePi.
Arduino
The Python Book 81
Work with Python
means it is time to get your soldering iron out. As for
reading the data in, this is most often done over a basic
serial connection. You can then use the pySerial module to
connecttotheserialportandreaddataofftheconnection.
Youcanuse:
import serial
to load the module and start communicating with your
sensor. The problem is that this is a very low-level way to
communicate. You, as the programmer, are responsible for
all of the details. This includes communication speed, byte
size, flow control; basically everything. So this will definitely
be an area of your code where you should plan on spending
somedebuggingtime.
Now that you have all of this data coming in, what will
you do with it? You need to be able to move actuators out
in the world and have real effects. This could be motors
for wheels or tracks, levers to shift objects, or potentially
complete limbs, like arms or legs. While you could try and
drive these types of electronic devices directly from the
output ports of your computer, there usually isn’t enough
current available to provide the necessary power. So,
you will need to have some off-board brains capable of
handling the supplying of power to these devices. One of
themostpopularcandidatesforthistaskistheArduino.
Forlow-levelwork,checkoutArduinos
THEMAINEDITOR
Youhaveaccesstoalargenumberoflibraries,
andsupportforalargenumberofversionsofthe
Arduinoboards.ThecodeisessentiallyC,soPython
programmersshouldn’tbetoofaroutoftheirdepths
OUTPUTWINDOW
Thispanecontainsoutputfromvarioustasks.This
mightbecompilingthesourcecode,oruploadingitto
theArduinoboardbeingusedinyourproject
THESTATUSBAR
Thestatusbarremindsyouwhichtypeofboardyour
arecurrentlyprogrammingfor,aswellaswhichportthe
ArduinoIDEthinksitison.Alwaysverifythisinformation
beforetryingtouploadyourcontrolprogramtothe
boardinquestion
Forroboticswork,youmay
needtorunsomecodetruly
inparallel,onmultipleCPUs.
PythoncurrentlyhastheGIL,
whichmeansthatthereisa
fundamentalbottleneckbuilt
intotheinterpreter.Oneway
aroundthisistoactuallyrun
multiplePythoninterpreters,
oneforeachthreadof
execution.Theotheroption
istomovefromCpythonto
eitherJythonorIronPython,as
neitherhasaGIL.
Bypassing
theGIL
Luckily, the Arduino is designed to connect to the serial
port of your computer, so you can simply use pySerial to
talk to it. You can send commands to code that you have
written and uploaded to the Arduino to handle the actual
manipulations of the various actuators. The Arduino
can talk back, however. This means that you can read
feedback data to see what effect your movements have
had. Did you end up turning your wheels as far as you
wanted to? This means that you could also use the Arduino
as an interface between your sensors and the computer,
thus simplifying your Python code even more. There are
loads of add-on modules available, too, that might be able
toprovidethesensingcapabilitiesthatyourequirestraight
out of the box. There are also several models of Arduino, so
you may be able to find a specialised model that best fits
yourneeds.
Now that you have all of this data coming in and the
ability to act out in the real world, the last step is giving
your robot some brains. This is where the state of the art
unfortunately does not live up to the fantasy of R2-D2 or
C-3P0. Most of your actual innovative coding work will
likely take place in this section of the robot. The general
term for this is artificial intelligence. There are several
projects currently underway that you could use as a
starting point to giving your robot some real reasoning
capability,likeSimpleAIorPyBrain.
82 The Python Book
Work with Python
Python is the world’s most popular easy-to-use open source
language. Learn how to use it to build your own features for
XBMC, the world’s favourite FOSS media centre
Makeextensionsfor
XBMCwithPython
XBMC is perhaps the most important thing that
has ever happened in the open source media
centre space. It started its life on the original
Xbox videogames console and since then it has
become the de facto software for multimedia
aficionados. It also has been forked into many
other successful media centre applications such
as Boxee and Plex. XBMC has ultimately grown
into a very powerful open source application with
a solid community behind it. It supports almost
all major platforms, including different hardware
architectures. It is available for Linux, Windows,
MacOSX,Android,iOSandRaspberryPi.
In these pages we will learn to build extensions
for XBMC. Extensions are a way of adding
features to XBMC without having to learn the
core of XBMC or alter that core in any way. One
additional advantage is that XBMC uses Python
as its scripting language, and this can be also
used to build the extensions. This really helps
new developers get involved in the project since
Python is easy to learn compared to languages
likeC/C++(fromwhichthecoreofXBMCismade).
XBMC supports various types of extensions (or
Add-ons): Plugins, Programs and Skins. Plugins
add features to XBMC. Depending on the type
of feature, a plug-in will appear in the relevant
media section of XBMC. For example, a YouTube
plug-in would appear in the Videos section.
Scripts/Programs are like mini-applications for
XBMC. They appear in the Programs section.
Skins are important since XBMC is a completely
customisable application – you can change
Resources
XBMC: www.xbmc.org/download
Python2.7x
PythonIDE(optional)
CodeonFileSilo
Listof
installed
plug-ins
Currentmedia
selection
Rating(onlyavailablefor
hostedplug-ins)
Configure
launcher
Openschangelog
fortheplug-in
Localised
descriptionstring
the look and feel of just about every facet of
thepackage.
Depending upon which category your
extension fits, you will have to create the
extensiondirectoryaccordingly.Forexample…
Plug-ins:
plugin.audio.ludaudi:Anaudioplug-in
plugin.video.ludvidi:Avideoplug-in
script.xxx.xxx:Aprogram
In this tutorial we will build an XBMC plug-in
called LUD Entertainer. This plug-in will provide a
nice way to watch videos from Reddit from within
XBMC.Ourplug-inwillshowvariouscontentsuch
as trailers and documentaries from Reddit. We’ll
also allow our users to add their own Subreddit.
Each video can then be categorised as Hot, New,
Top, Controversial etc. With this plug-in we will
demonstrate how easy it is hook into XBMC’s
built-in method to achieve a very high-quality
userexperience.
Due to space limitations,we aren’t able to print
the full code here. We recommend downloading
thecompletecodefromFileSilo.
The Python Book 83
Work with Python
01Preparingthedirectorystructure
As we have mentioned previously, each
XBMC extension type follows a certain directory
naming convention. In this case we are building
a video plug-in, so the plug-in directory name
would be plugin.video.ludlent. But that’s just the
root directory name – we will need several other
foldersandfilesaswell.
The following describes the directory structure of
LUDLinuxEntertainer:
plugin.video.ludent–RootPlugindirectory
|--addon.xml
|--changelog.txt
|--default.py
|--icon.png
|--LICENSE.txt
|--README
`--resources
|--lib
`--settings.xml
02Creatingaddon.xml
An addon.xml file needs to be created in
therootoftheextensiondirectory.Theaddon.xml
file contains the primary metadata from a XBMC
extension. It contains overview, credits, version
information and dependenciesinformation about
theextension.
The root element of addon.xml is the addon
element.Itisdefinedas:
addon id=plugin.video.
ludent name=LUD HSW Viewer
version=0.0.1 provider-
name=LUDK
rest of the content is placed here
/addon
Here, id is the identifier for the plug-in, so
it should be unique among all the XBMC
extensions, and id is also used for the directory
name; version tells XBMC the extension
version number, which helps in its ability to
deliver automatic updates – XBMC follows the
Major.Minor.Patchversioningconvention;nameis
theEnglishtitleoftheplug-in.
Note:Steps3to5coverentriesthatneedtobe
addedwithintheaddon.xmlfile.
03Addingdependencyinformation
Dependency inside an extension is
managedusingtherequireselement.
requires
import addon=xbmc.python
version=2.1.0/
import addon=plugin.video.
youtube version=3.0.0/
import addon=plugin.video.vimeo
version=2.3.0/
import addon=plugin.video.
dailymotion_com version=1.0.0/
/requires
Intheabovecodewehaveaddedadependency
to a library called xbmc.python version
2.1. Currently it is added as a mandatory
dependency. To make the dependency
optional you will need to add optional=true;
eg import addon=kunal.special
version=0.1.0 optional=true /
In the above example we have added core
dependency xbmc.python to 2.1.0 because it’s
the version shipped with XBMC version Frodo
12.0 and 12.1 . If you were to add xbmc.python
to2.0thenitwouldonlyworkinXBMCEden11.0
andnotinthelatestversion.
For the current version of XBMC 12.1, the
following versions of core XBMC components
areshipped:
xbmc.python2.1.0
xbmc.gui4.0.0
xbmc.json6.0.0
xbmc.metadata2.1.0
xbmc.addon12.0.0
Inadditiontoxbmc.pythonwearealsoadding
some third-party plug-ins as dependencies,
such as plugin.video.youtube. These plug-ins
will be installed automatically when we install
plugin.video.ludent.
04Settinguptheproviderand
entrypoint
Our extension is supposed to provide the video
content for XBMC. In order to convey that, we
havetosetupthefollowingelement:
extension point=xbmc.python.
pluginsource library=default.
py
providesvideo/provides
/extension
Here, the library attribute sets up the plug-in
entry point. In this example default.py will be
executed when the user activates the plug-in.
The provides elements sets up the media
type it provides. This also gets reflected in the
placement of the plug-in. Since ours is a video
plug-in, it will show up in the Videos section
ofXBMC.
05Settingupplug-inmetadata
Metadataabouttheplug-inisprovidedin
extensionpoint=xbmc.addon.metadata.The
followingaretheimportantelements…
platform: Most of the time, XBMC extensions
are cross-platform compatible. However, if you
depend on the native platform library that is only
available on certain platforms then you will need
to set the supported platforms here. Accepted
values for the platform are: all, linux, osx, osx32,
osx64, ios (Apple iOS) , windx (Windows DirectX),
wingl(WindowsOpenGL)andandroid.
summary lang=en: This gives a brief
description of the plug-in. Our example sets the
language attribute as English, but you can use
otherlanguagestoo.
description: A detailed description of the
plug-in.
website:Webpagewheretheplug-inishosted.
source: Source code repository URL. If you are
hosting your plug-in on GitHub, you can mention
therepositoryURLhere.
forum:DiscussionforumURLforyourplug-in.
email: Author email. You can directly type email
or use a bot-friendly email address like max at
domaindotcom.
06Settingchangelog,icon,fanart
andlicence
We need a few additional files in the plug-in
directory…
changelog.txt: You should list the changes made
to your plug-in between releases. The changelog
isvisiblefromtheXBMCUI.
Anexamplechangelog:
0.0.1
-InitialRelease
0.0.2
-FixedVideoBufferingIssue
icon.png: This will represent the plug-in in the
XBMC UI. It needs to be a non-transparent PNG
fileofsize256x256.
fanart.jpg (optional): The fanart.jpg is rendered
in the background if a user selects the plug-in
in XBMC. The art needs to be rendered in HDTV
formats, so its size can range from 1280x720
(720p)uptothemaximum1920x1080(1080p).
84 The Python Book
Work with Python
License.txt: This file contains the licence of
the distributed plug-in. The XBMC project
recommends the use of the Creative Commons
Attribution-ShareAlike 3.0 licence for skins,
and GPL 2.0 for add-ons. However, most of the
copyleftlicencescanbeused.
Note: For the purpose of packaging, extensions/
add-ons/themes/plug-insarethesame.
07Providingsettingsfortheplug-in
Settings can be provided by the file
resources/settings.xml.Thesearegreatforuser-
configurableoptions.
Partial:resources/settings.xml
settings
category label=30109
setting id=filter type=bool
label=30101 default=false/
setting type=sep /
setting id=showAll type=bool
label=30106 default=false/
setting id=showUnwatched
type=bool label=30107
default=true/
setting id=showUnfinished
type=bool label=30108
default=false/
setting type=sep /
setting id=forceViewMode
type=bool label=30102
default=true/
setting id=viewMode type=number
label=30103 default=504/
/category
category label=30110
setting id=cat_hot type=bool
label=30002 default=true/
setting id=cat_new type=bool
label=30003 default=true/
/category
/settings
Here, label defines the language id string which
will then be used to display the label. id defines
the name which will be used for programmatic
access. type defines the data type you want
to collect; it also affects the UI which will be
displayed for the element. default defines the
default value for the setting. You should always
useadefaultvaluewhereverpossibletoprovidea
betteruserexperience.
The following are a few important settings
typesthatyoucanuse…
text:Usedforbasicstringinputs.
ipaddress:Usedtocollectinternetaddresses.
number: Allows you enter a number. XBMC will
also provide an on-screen numeric keyboard for
theinput.
slider: This provides an elegant way to collect
integer, float and percentage values. You can get
theslidersettinginthefollowingformat:
setting label=21223 type=slider
id=sideinput default=10
range=1,1,10 option=int /
Intheaboveexamplewearecreatingasliderwith
min range 1, max range 10 and step as 1. In the
option field we are stating the data type we are
interested in – we can also set option to float
orpercent.
bool: Provides bool selection in the form of on
oroff.
file: Provides a way to input file paths. XBMC will
provideafilebrowsertomaketheselectionoffile.
If you are looking to make selection for a specific
type of file you can use audio, video, image or
executableinsteadoffile.
folder:Providesawaytobrowseforafolder…
Example:
setting label=12001 type=folder
id=folder source=auto
option=writeable/
Here, source sets the start location for the
folder, while option sets the write parameter for
theapplication.
sep  lsep: sep is used to draw a horizontal line
in the setting dialog; lsep is used for drawing
a horizontal line with text. They do not collect
any input but are there for building better user
interfaceelements…
setting label=21212 type=lsep
/
08Languagesupport
Language support is provided in
the form of the strings.xml file located in
resources/languages/[language name]. This
approach is very similar to many large software
projects, including Android, where static strings
areneverused.
resource/language/english/string.xml
example:
?xml version=1.0 encoding=utf-8
standalone=yes?
strings
string id=30001Add subreddit/
string
string id=30002Hot/string
string id=30003New/string
string id=30004Top/string
string id=30005Controversial/
string
string id=30006Hour/string
string id=30007Day/string
string id=30008Week/string
string id=30009Month/string
string id=30010Year/string
/strings
As you may have seen in the settings.xml
example, all the labels are referring to string
ids. You can have many other languages as
well. Depending upon the language XBMC is
running in, the correct language file will be
loadedautomatically.
Post XBMC Frodo (12.1), strings.xml will be
deprecated. Post Frodo, XBMC will be moved
to a GNU gettext-based translation system;
gettext uses PO files. You can use a tool called
xbmc-xml2po to convert strings.xml into
equivalentPOfiles.
09Buildingdefault.py
Since our plug-in is small, it will all be
contained inside default.py. If you are developing
a more complex add-on then you can create
supporting files in the same directory. If your
library depends upon third-party libraries, you
have two ways to go about it. You can either place
the third-party libraries into the resources/lib
folder; or bundle the library itself into a plug-in,
then add that plug-in as the dependency in the
addon.xmlfile.
Our plug-in works with reddit.tv. This is the
website from Reddit which contains trending
videos shared by its readers. Videos posted on
Reddit are actually sourced from YouTube, Vimeo
andDailymotion.
We will be starting off default.py using the
followingimports:
import urllib
import urllib2
…
import xbmcplugin
The Python Book 85
Work with Python
import xbmcgui
import xbmcaddon
Apart from xbmcplugin, xbmcgui and
xbmcaddon, the rest are all standard Python
libraries which are available on PyPI (Python
Package Index) via pip. You will not need to install
any library yourself since the Python runtime for
XBMChasallthecomponentsbuiltin.
urllib and urllib2 help in HTTP communication.
socket is used for network I/O; re is used
for regular expression matching; sqlite3 is
the Python module for accessing an SQLite
embedded database; xbmcplugin, xbmcgui and
xbmcaddoncontaintheXBMC-specificroutine.
10Initialising
During the initialisation process, we will
be reading various settings from settings.xml.
Settingscanbereadinthefollowingway:
addon = xbmcaddon.Addon()
filterRating = int(addon.
getSetting(filterRating))
filterVoteThreshold = int(addon.getS
etting(filterVoteThreshold))
Inordertoreadsettingsoftypeboolyouwillneed
todosomethinglike:
filter = addon.getSetting(filter)
== true
We are also setting the main URL, plug-in handle
andtheuseragentforit:
pluginhandle = int(sys.argv[1])
urlMain = http://www.reddit.com
userAgent = Mozilla/5.0 (Windows NT
6.2; WOW64; rv:22.0) Gecko/20100101
Firefox/22.0
opener = urllib2.build_opener()
opener.addheaders = [(‘User-Agent’,
userAgent)]
11Readinglocalisedstrings
As mentioned, XBMC uses strings.xml to
serve up the text. In order to read those strings,
youwillneedtousegetLocalizedString.
translation = addon.
getLocalizedString
translation(30002)
In this example, translation(30002) will
return the string Hot when it is running in an
Englishenvironment.
12Buildinghelperfunctions
In this step we will look at some of the
importanthelperfunctions.
getDbPath(): This returns the location of the
SQLite database file for videos. XBMC stores
library and playback information in SQLite DB
files. There are separate databases for videos
and music, located inside the .xbmc/userdata/
Database folder. We are concerned with the
videosDB.Itisprefixedwith‘MyVideos’…
def getDbPath():
path = xbmc.
translatePath(special://userdata/
Database)
files = os.listdir(path)
latest = 
for file in files:
if file[:8] == ‘MyVideos’
and file[-3:] == ‘.db’:
if file  latest:
latest = file
return os.path.join(path,
latest)
getPlayCount(url): Once we have the database
location, we can get the play count using a
simple SQL query. The MyVideo database
contains a table called files, which keeps a
record of all the video files played in XBMC by
filename.InthiscaseitwillbeURL.
dbPath = getDbPath()
conn = sqlite3.connect(dbPath)
c = conn.cursor()
def getPlayCount(url):
c.execute(‘SELECT playCount FROM
files WHERE strFilename=?’, [url])
result = c.fetchone()
if result:
result = result[0]
if result:
return int(result)
return 0
return -1
Theabovetableisanexampleofafilestable.
addSubreddit(): Our plug-in allows users to add
their own Subreddit. This function takes the
Subreddit input from the user, then saves it in
thesubredditsfileinsidetheaddondatafolder.
Thefollowingsetsthesubredditsfilelocation:
subredditsFile = xbmc.
translatePath(special://profile/
addon_data/+addonID+/subreddits)
this translates into .xbmc/userdata/
addon_data/plugin.video.ludent/
subreddits
def addSubreddit():
keyboard = xbmc.Keyboard(‘’,
translation(30001))
keyboard.doModal()
if keyboard.isConfirmed() and
keyboard.getText():
subreddit = keyboard.
getText()
fh = open(subredditsFile,
‘a’)
fh.write(subreddit+’n’)
fh.close()
This function also demonstrates how to take
a text input from the user. Here we are calling
the Keyboard function with a text title. Once it
detects the keyboard, it writes the input in the
subredditsfilewithanewlinecharacter.
getYoutubeUrl(id): When we locate a YouTube
URL to play, we pass it on to the YouTube plug-in
(plugin.video.youtube) to handle the playback. To
doso,weneedtocallitinacertainformat…
def getYoutubeUrl(id):
url = plugin://plugin.
video.youtube/?path=/root/
videoaction=play_videovideoid= +
id
return url
idFile idPath strFilename playCount lastPlayed dateAdded
1 1 plugin://plugin. 2013-08-0623:47
2 2 plugin://plugin. 1 2013-08-0722:42
3 2 plugin://plugin. 1 2013-08-0800:09
4 2 plugin://plugin. 1 2013-08-0800:55
5 2 plugin://plugin. 1 2013-08-0800:58
86 The Python Book
Work with Python
SimilarlyforVimeo:
def getVimeoUrl(id):
url = plugin://plugin.video.
vimeo/?path=/root/videoaction=play_
videovideoid= + id
return url
AndforDailymotion:
def getDailyMotionUrl(id):
url = plugin://plugin.video.
dailymotion_com/?url= + id +
mode=playVideo
return url
Once we have the video URL resolved into the
respectiveplug-in,playingitisverysimple:
def playVideo(url):
listitem = xbmcgui.
ListItem(path=url)
xbmcplugin.
setResolvedUrl(pluginhandle, True,
listitem)
13Populatingplug-incontentlisting
xbmcplugin contains various routines
for handling the content listing inside the
plug-ins UI. The first step is to create directory
entries which can be selected from the XBMC
UI. For this we will use a function called
xbmcplugin.addDirectoryItem.
For our convenience we will be abstracting
addDirectoryItem to suit it to our purpose, so
that we can set name, URL, mode, icon image
andtypeeasily.
def addDir(name, url, mode,
iconimage, type=):
u = sys.argv[0]+?url=+urllib.
quote_plus(url)+mode=+str(mode)+
type=+str(type)
ok = True
liz = xbmcgui.ListItem(name,
iconImage=DefaultFolder.png,
thumbnailImage=iconimage)
liz.setInfo(type=Video,
infoLabels={Title: name})
ok = xbmcplugin.
addDirectoryItem(handle=int(sys.
argv[1]), url=u, listitem=liz,
isFolder=True)
return ok
On the same lines, we can build a function to
placelinksaswell…
def addLink(name, url, mode,
iconimage, description, date):
u = sys.argv[0]+?url=+urllib.
quote_plus(url)+mode=+str(mode)
ok = True
liz = xbmcgui.ListItem(name,
iconImage=DefaultVideo.png,
thumbnailImage=iconimage)
liz.setInfo(type=Video,
infoLabels={Title: name, Plot:
description, Aired: date})
liz.setProperty(‘IsPlayable’,
‘true’)
ok = xbmcplugin.
addDirectoryItem(handle=int(sys.
argv[1]), url=u, listitem=liz)
return ok
Based on the abstractions we have just created,
we can create the base functions which will
populate the content. But before we do that,
let’s first understand how Reddit works. Most of
the Reddit content filters are provided through
something called Subreddits. This allows you to
view discussions related to a particular topic. In
our plug-in we are interested in showing videos;
we also want to show trailers, documentaries
etc. We access these using Subreddits. For
example, for trailers it would be reddit.com/r/
trailers. For domains we can use /domain; for
example, to get all the YouTube videos posted
on Reddit, we will call reddit.com/domain/
youtube.com. Now you may ask what is the
guarantee that this Subreddit will only list
videos? The answer is that it may not. For that
reason we scrape the site ourselves to find
videos.Moreonthisinthenextstep.
The first base function we’ll define is index().
Thisiscalledwhentheuserstartstheplug-in.
def index():
defaultEntries = [videos,
trailers, documentaries,
music]
entries = defaultEntries[:]
if os.path.
exists(subredditsFile):
fh = open(subredditsFile,
‘r’)
content = fh.read()
fh.close()
spl = content.split(‘n’)
for i in range(0, len(spl),
1):
if spl[i]:
subreddit = spl[i]
entries.
append(subreddit)
entries.sort()
for entry in entries:
if entry in defaultEntries:
addDir(entry.title(),
r/+entry, ‘listSorting’, )
else:
addDirR(entry.title(),
r/+entry, ‘listSorting’, )
addDir([ Vimeo.com ],
domain/vimeo.com, ‘listSorting’,
)
addDir([ Youtu.be ], domain/
youtu.be, ‘listSorting’, )
addDir([ Youtube.com
], domain/youtube.com,
‘listSorting’, )
addDir([ Dailymotion.com
], domain/dailymotion.com,
‘listSorting’, )
addDir([B]-
+translation(30001)+ -[/B], ,
‘addSubreddit’, )
xbmcplugin.
endOfDirectory(pluginhandle)
Here, the penultimate entry makes a call to
addSubreddit. listSorting takes care of sorting
out the data based on criteria such as Hot,
New etc. It also calls in Reddit’s JSON function,
whichreturnsniceeasy-to-parseJSONdata.
We have created a settings entry for all the
sorting criteria. Based on what is set, we go
aheadandbuildoutthesortedlist.
def listSorting(subreddit):
if cat_hot:
addDir(translation(30002),
urlMain+/+subreddit+/hot/.
json?limit=100, ‘listVideos’, )
if cat_new:
addDir(translation(30003),
urlMain+/+subreddit+/new/.
json?limit=100, ‘listVideos’, )
if cat_top_d:
addDir(translation(30004)+:
+translation(30007),
urlMain+/+subreddit+/
top/.json?limit=100t=day,
‘listVideos’, )
xbmcplugin.
endOfDirectory(pluginhandle)
The Python Book 87
Work with Python
In the code listed to the left here, we are
opening the URL, then – based on regular
expression matches – we are discovering
the location title, description, date, ups,
downs and rating. We are also locating
video thumbnails and then passing them on
to XBMC.
Later in the code, we also try to match the
URL to a video provider. With our plug-in we are
supporting YouTube, Vimeo and Dailymotion.
If this is detected successfully, we call the
helper functions to locate the XBMC plug-
in based playback URL. During this whole
parsing process, if any exception is raised, the
whole loop is ignored and the next JSON item
is parsed.
15Installing running theadd-on
You can install the add-on using one of
thefollowing two methods:
• You can copy the plug-in directory to
.xbmc/addons.
• You can install the plug-in from the zip file. To
do so, compress the add-on folder into a zip file
using the command:
$ zip -r plugin.video.ludent.zip
plugin.video.ludent
To install the plug-in from the zip file, open
XBMC, go to System then Add-ons, then click
‘Install from zip file’. The benefit of installing
from a zip file is that XBMC will automatically
try to install all the dependent plug-ins as well.
Once you have the plug-in installed, you can
run it by going to the Videos Add-ons section of
XBMC, selecting Get More… and then clicking
on LUD Reddit Viewer.
You can access the settings dialog of the
plug-in by right-clicking the LUD Reddit Viewer,
then selecting ‘Add-on settings’.
So, you have seen how robust and powerful
XBMC’s extension system is. In this example,
we were able to leverage the full power of
Python (including those magical regular
expression matches) from within XBMC.
XBMC itself also offers a robust UI framework,
which provides a very professional look for
our add-on.
As powerful as it may seem, we have only
built a video plug-in. XBMC’s extension system
also provides a framework for building fully
fledged programs (called Programs). We will
coverthisinalaterissue.
14Populatingtheepisodeview(listingvideos)
At this point we have the URL in hand, which returns JSON data; now we need to extract the
dataoutofitwhichwillmakesensetous.
By looking at the JSON data, you can see there’s a lot of interesting information present here. For
example, url is set to youtube.com/watch?v=n4rTztvVx8E; title is set to ‘The Counselor – Official
Trailer’. There also many other bits of data that we will use, such as ups, downs, num_comments,
thumbnail_urlandsoon.Inordertofilteroutthedatathatweneed,wewilluseregularexpressions.
There is one more thing to note: since we are not presenting directories any more but are ready to
placecontent,wehavetosetthexbmcplugin.setContenttoepisodesmode.
def listVideos(url):
currentUrl = url
xbmcplugin.setContent(pluginhandle, episodes)
content = opener
.open(url).read()
spl = content.split(‘content’)
for i in range(1, len(spl), 1):
entry = spl[i]
try:
match = re.compile(‘title: (
.+?)’
, re.DOTALL).findall(entry)
title = match[0].replace(amp;
, )
match = re.compile(‘description: (
.+?)’
, re.DOTALL).
findall(entry)
description = match[0]
match = re.compile(‘created_utc: (
.+?),
’
, re.DOTALL).findall(entry)
downs = int(match[0].replace(}
, ))
rating = int(ups*100/(ups+downs))
if filter and (ups+downs)  filterVoteThreshold and rating 
filterRating:
continue
title = title+ (+str(rating)+%)
match = re.compile(‘num_comments: (
.+?),
’
, re.DOTALL).
findall(entry)
comments = match[0]
description = dateTime+ | +str(ups+downs)+ votes:
+str(rating)+% Up | +comments+ commentsn+description
match = re.compile(‘thumbnail_url: (
.+?)’
, re.DOTALL).
findall(entry)
thumb = match[0]
matchYoutube = re.compile(‘url: http://www.youtube.com/
watch?v=(
.+?)’
, re.DOTALL).findall(entry)
matchVimeo = re.compile(‘url: http://vimeo.com/(
.+?)’
,
re.DOTALL).findall(entry)
url = 
if matchYoutube:
url = getYoutubeUrl(matchYoutube[0])
elif matchVimeo:
url = getVimeoUrl(matchVimeo[0].replace(#
, ))
if url:
addLink(title, url, ‘playVideo’
, thumb, description, date)
except:
pass
match = re.compile(‘after: (
.+?)’
, re.DOTALL).findall(entry)
xbmcplugin.endOfDirectory(pluginhandle)
if forceViewMode:
xbmc.executebuiltin(‘Container
.SetViewMode(‘+viewMode+’)’)
88 The Python Book
Work with Python
Powerfulcalculationswith
NumPy,SciPyandMatplotlib
Scientificcomputing
withNumPy
NumPy is the primary Python package for
performing scientific computing. It has a
powerful N-dimensional array object, tools
for integrating C/C++ and Fortran code, linear
algebra, Fourier transform, and random
number capabilities, among other things.
NumPy also supports broadcasting, which is
a clever way for universal functions to deal in
a meaningful way with inputs that do not have
exactly the same form.
Apart from its capabilities, the other
advantage of NumPy is that it can be integrated
into Python programs. In other words, you may
get your data from a database, the output of
another program, an external file or an HTML
pageandthenprocessitusingNumPy.
This article will show you how to install
NumPy, make calculations, plot data, read and
write external files, and it will introduce you to
some Matplotlib and SciPy packages that work
wellwithNumPy.
NumPy also works with Pygame, a Python
package for creating games, though explaining
itsuseisbeyondofthescopeofthisarticle.
It is considered good practice to try the
various NumPy commands inside the Python
shellbeforeputtingthemintoPythonprograms.
The examples in this article are using either
PythonshelloriPython.
AsimplePython
programfor
PolynomialFitting!
APythonscript
thatusesSciPyto
processanimage
Matplotlib
generatedoutput
Findinghelp
iseasy
Resources
NumPy:
www.numpy.org
SciPy:
www.scipy.org
Matplotlib:
www.matplotlib.org
01 Installing NumPy
Most Linux distributions have a
ready-to-install package you can use. After
installation, you can find out the NumPy version
youareusingbyexecutingthefollowing:
$ python
Python 2.7
.3 (default, Mar 13 2014, 11:03:55)
[GCC 4.7
.2] on linux2
Type help
, copyright
, credits or
license for more information.
 numpy.version.version
The Python Book 89
Work with Python
Traceback (most recent call last):
File stdin
, line 1, in module
NameError: name 'numpy' is not defined
 import numpy
 numpy.version.version
'1.6.2'

Not only have you found the NumPy version but
you also know that NumPy is properly installed.
02AboutNumPy
Despite its simplistic name, NumPy is
a powerful Python package that is mainly for
working with arrays and matrices.
There are many ways to create an array but
the simplest is by using the array() function:
 oneD = array([1,2,3,4])
The aforementioned command creates a
one-dimensional array. If you want to create a
two-dimensional array, you can use the array()
functionasfollows:
 twoD = array([ [1,2,3],
... [3,3,3],
... [-1,-0.5,4],
... [0,1,0]] )
Youcanalsocreatearrayswithmoredimensions.
03Making simple calculations
using NumPy
Given an array named myArray, you can find
the minimum and maximum values in it by
executing the following commands:
 myArray.min()
 myArray.max()
Should you wish to find the mean value of all
array elements, run the next command:
 myArray.mean()
Similarly, you can find the median of the array
by running the following command:
 median(myArray)
The median value of a set is an element that
divides the data set into two subsets (left
and right subsets) with the same number of
elements. If the data set has an odd number of
elements, then the median is part of the data
set. On the other side, if the data set has an
even number of elements, then the median is
the mean value of the two centre elements of
the sorted data set.
04UsingarrayswithNumPy
NumPy not only embraces the indexing
methods used in typical Python for strings and
lists but also extends them. If you want to select
a given element from an array, you can use the
followingnotation:
 twoD[1,2]
You can also select a part of an array (a slice)
usingthefollowingnotation:
 twoD[
:1,1:3]
Finally, you can convert an array into a Python
listusingthetolist()function.
05Readingfiles
Imaginethatyouhavejustextracted
informationfromanApachelogfileusingAWKand
youwanttoprocessthetextfileusingNumPy.
The following AWK code finds out the total
numberofrequestsperhour:
$ cat access.log | cut -d[ -f2 | cut -d]
-f1 | awk -F: '{print $2}
' | sort -n | uniq
-c | awk '{print $2, $1}
'  timeN.txt
The format of the text file (timeN.txt) with the
data is the following:
00 191
01 225
02 121
03 104
Reading the timeN.txt file and assigning it to a
new array variable can be done as follows:
aa = np.loadtxt(timeN.txt)
06Writingtofiles
Writing variables to a file is largely
similar to reading a file. If you have an array
variable named aa1, you can easily save its
contents into a file called aa1.txt by using the
followingcommand:
In [17]: np.savetxt(aa1.txt, aa1)
As you can easily imagine, you can read
the contents of aa1.txt later by using the
loadtxt()function.
07Commonfunctions
NumPy supports many numerical and
statistical functions. When you apply a function
to an array, the function is automatically applied
toallarrayelements.
When working with matrices, you can find the
inverse of a matrix AA by typing “AA.I”. You can
also find its eigenvalues by typing “np.linalg.
eigvals(AA)” and its eigenvector by typing “np.
linalg.eig(BB)”.
08Workingwithmatrices
A special subtype of a two-dimensional
NumPy array is a matrix. A matrix is like an
array except that matrix multiplication replaces
element-by-element multiplication. Matrices
are generated using the matrix (or mat) function
asfollows:
In [2]: AA = np.mat('0 1 1; 1 1 1; 1 1 1
')
You can add two matrices named AA and BB by
typing AA + BB. Similarly, you can multiply them
bytypingAA*BB.
03Makingsimple
calculations
90 The Python Book
Work with Python
SciPyisbuiltontopofNumPy
andismoreadvanced
09PlottingwithMatplotlib
The first move you should make is to
install Matplotlib. As you can see, Matplotlib has
manydependenciesthatyoushouldalsoinstall.
The first thing you will learn is how to
plot a polynomial function. The necessary
commands for plotting the 3x^2-x+1
polynomial are the following:
import numpy as np
import matplotlib.pyplot as plt
myPoly = np.poly1d(np.array([3, -1, 1]).
astype(float))
x = np.linspace(-5, 5, 100)
y = myPoly(x)
plt.xlabel('x values')
plt.ylabel('f(x) values')
xticks = np.arange(-5, 5, 10)
yticks = np.arange(0, 100, 10)
plt.xticks(xticks)
plt.yticks(yticks)
plt.grid(True)
plt.plot(x,y)
The variable that holds the polynomial
is myPoly. The range of values that will
be plotted for x is defined using “x =
np.linspace(-5, 5, 100)”. The other important
variable is y, which calculates and holds the
values of f(x) for each x value.
It is important that you start ipython using
the “ipython --pylab=qt” parameters in order
to see the output on your screen. If you are
interested in plotting polynomial functions,
you should experiment more, as NumPy can
also calculate the derivatives of a function and
plot multiple functions in the same output.
10AboutSciPy
SciPy is built on top of NumPy and
is more advanced than NumPy. It supports
numerical integration, optimisations, signal
processing, image and audio processing,
and statistics. The example in Fig. 01 (to the left)
uses a small part of the scipy.stats package that
isaboutstatistics.
The example uses two statistics distributions
and may be difficult to understand even if you
know mathematics, but it is presented in order
togiveyouabettertasteofSciPycommands.
11UsingSciPyforimageprocessing
Now we will show you how to process
and transform a PNG image using SciPy.
The most important part of the code is the
following line:
In [36]: from scipy.stats import poisson, lognorm
In [37]: mySh = 10;
In [38]: myMu = 10;
In [39]: ln = lognorm(mySh)
In [40]: p = poisson(myMu)
In [41]: ln.rvs((10,))
Out[41]:
array([ 9.29393114e-02, 1.15957068e+01, 9.78411983e+01,
8.26370734e-07, 5.64451441e-03, 4.61744055e-09,
4.98471222e-06, 1.45947948e+02, 9.25502852e-06,
5.87353720e-02])
In [42]: p.rvs((10,))
Out[42]: array([12, 11, 9, 9, 9, 10, 9, 4, 13, 8])
In [43]: ln.pdf(3)
Out[43]: 0.013218067177522842
Fig 01
09Plottingwith
Matplotlib
The Python Book 91
Work with Python
ProcessandtransformaPNG
imageusingSciPy
12Otherusefulfunctions
It is very useful to be able to find out
the data type of the elements in an array; it
can be done using the dtype() function.
Similarly, the ndim() function returns the
number of dimensions of an array.
When reading data from external files, you
can save their data columns into separate
variables using the following way:
In [10]: aa1,aa2 = np.loadtxt(timeN.txt,
usecols=(0,1), unpack=True)
The aforementioned command saves column
1 into variable aa1 and column 2 into variable
aa2. The “unpack=True” allows the data to be
assigned to two different variables. Please
note that the numbering of columns starts
with 0.
14ArraybroadcastinginNumPy
To close, we will talk more about
array broadcasting because it is a very
useful characteristic. First, you should know
that array broadcasting has a rule: in order
for two arrays to be considered for array
broadcasting, “the size of the trailing axes for
both arrays in an operation must either be the
same size or one of them must be one.”
Put simply, array broadcasting allows
NumPy to “change” the dimensions of an array
by filling it with data in order to be able to do
calculations with another array. Nevertheless,
you cannot stretch both dimensions of an
array to do your job.
13Fittingtopolynomials
The NumPy polyfit() function tries to fit
a set of data points to a polynomial. The data
was found from the timeN.txt file, created
earlier in this article.
The Python script uses a fifth degree
polynomial, but if you want to use a different
degree instead then you only have to change
the following line:
coefficients = np.polyfit(aa1, aa2, 5)
image = np.array(Image.open('SA.png').
convert('L
'))
This line allows you to read a usual PNG
file and convert it into a NumPy array for
additional processing. The program will
also separate the output into four parts and
displays a different image for each of these
four parts.
11UsingSciPyfor
imageprocessing
13Fittingto
Polynomials
92 The Python Book
Work with Python
Howtoprogramboththeclient,completewithaGUI,and
serverofasimpleinstantmessengerinPython
InstantmessagingwithPython
He’re we’ll be implementing an instant
messenger in Python with a client-server
architecture. This means each client connects
to the server, which relays any message that
one client sends to all other clients. The server
will also notify the other clients when someone
joinsorleavestheserver.Theinstantmessenger
can work anywhere a TCP socket can: on the
same computer with the loopback interface,
across various computers on a LAN, or even
over the internet if you were to configure your
router correctly. However, our messages aren’t
encrypted,sowewouldn’trecommendthat.
Writing an instant messenger is an interesting
technical problem that covers a bunch of
areas that you may not have come across while
programmingbefore:
• We’ll be employing sockets, which are used
totransmitdataacrossnetworks.
• We’ll also be using threading, which allows a
programtodomultiplethingsatonce.
• We’ll cover the basics of writing a simple
graphical user interface with GTK, as well as
howtointeractwiththatfromadifferentthread.
• Finally, we’ll be touching on the use of
regularexpressionstoeasilyanalyseandextract
datafromstrings.
Before getting started, you’ll need to have
a Python2.x interpreter installed, as well as
the PyGTK bindings and the Python2 GObject
bindings. The chances are that if you have a
system with a fair amount of software on it,
you will already have these packages, so it may
be easier to wait and see if you’re missing any
libraries when you attempt to import them. All of
the above packages are commonly used, so you
should be able to install them using your distro’s
packagemanager.
Resources
Acomputer–runningyourfavouriteLinux
distribution
Internetconnection–toaccess
documentation
Python2.x,PyGTKandGObject–
packagesinstalled
Theservernotifies
allclientswhena
newclientjoins
Eachmessage
hasatimestamp
prefixedtoit
Similarly,theserver
notifiesallclients
whenaclientleaves
Aclientcandetect
whentheserver
exitswithout
crashingorhanging
The Python Book 93
Work with Python
01Theserver
The server will do the following jobs:
• Listen for new clients
• Notify all clients when a new client joins
• Notify all clients when a client leaves
• Receive and deliver messages to all clients
We’re going to write the server side of the
instant messenger first, as the client requires
it. There will be two code files, so it’s a good
idea to make a folder to keep them inside. You
can create an empty file with the command
touch [filename], and mark that file as
executable using chmod +x [filename]. This
file is now ready to edit in your favourite editor.
[liam@liam-laptop Python]$ mkdir
Python-IM
[liam@liam-laptop Python]$ cd
Python-IM/
[liam@liam-laptop Python-IM]$ touch
IM-Server.py
[liam@liam-laptop Python-IM]$ chmod
+x IM-Server.py
02Startingoff
As usual, we need to start off with the
line that tells the program loader what it needs
to interpret the rest of the file with. In your
advisor’s case, that line is:
#!/usr/bin/env python2.
On your system, it may need to be changed to
#!/usr/bin/env/ python2.6 or #!/usr/
bin/env python2.7
After that, we’ve written a short comment about
what the application does, and imported the
required libraries. We’ve already mentioned
what the threading and socket libraries are
for. The re library is used for searching strings
with regular expressions. The signal library is
used for dealing with signals that will kill the
program, such as SIGINT. SIGINT is sent when
Ctrl+C is pressed. We handle these signals so
that the program can tell the clients that it’s
exiting rather than dying unexpectedly. The sys
library is used to exit the program. Finally, the
timelibraryisusedtoputasensiblelimitonhow
frequentlythebodyofwhileloopsexecute.
#!/usr/bin/env python2
# The server side of an instant
messaging application. Written as
part of a Linux User  Developer
tutorial by Liam Fraser in 2013.
import threading
03TheServerclass
TheServerclassisthemainclassofour
instant messenger server. The initialiser of this
class accepts a port number to start listening
for clients on. It then creates a socket, binds the
socket to the specified port on all interfaces,
and then starts to listen on that port. You can
optionally include an IP address in the tuple that
contains the port. Passing in a blank string like
wehavedonecausesittolistenonallinterfaces.
The value of 1 passed to the listen function
specifies the maximum number of queued
connections we can accept. This shouldn’t be
a problem as we’re not expecting a bunch of
clientstoconnectatexactlythesametime.
Now that we have a socket, we’ll create an
empty array that will be later used to store a
collection of client sockets that we can echo
messages to. The final part is to tell the signal
library to run the self.signal_handler function,
which we have yet to write, when a SIGINT or
SIGTERM is sent to the application so that we
cantidyupnicely.
class Server():
def __init__(self, port):
# Create a socket and bind it to a
port
self.listener = socket.
socket(socket.AF_INET, socket.SOCK_
STREAM)
self.listener.bind((‘’,
port))
self.listener.listen(1)
print “Listening on port
{0}”.format(port)
# Used to store all of the client
sockets we have, for echoing
to them
self.client_sockets = []
# Run the function self.signal_
handler when Ctrl+C is pressed
signal.signal(signal.SIGINT,
self.signal_handler)
signal.signal(signal.
SIGTERM, self.signal_handler)
04Theserver’smainloop
The server’s main loop essentially
accepts new connections from clients,
adds that client’s socket to the collection of
import socket
import re
import signal
import sys
import time Threading:docs.python.org/2/library/
threading.html
Sockets: docs.python.org/2/library/
socket.html
Regular expressions:docs.python.
org/2/library/re.html
Thesignalhandler:docs.python.org/
2/library/signal.html
PyGTK: www.pygtk.org/
pygtk2reference
GObject: www.pygtk.org/
pygtk2reference/gobject-functions.html
Useful
documentation
sockets and then starts an instance of the
ClientListener class, which we have yet to
write, in a new thread. Sometimes, defining
interfaces you are going to call before you’ve
written them is good, because it can give an
overview of how the program will work without
worrying about the details.
Note that we’re printing information as we go
along, to make debugging easier should we need
to do it. Sleeping at the end of the loop is useful
to make sure the while loop can’t run quickly
enough to hang the machine. However, this is
unlikely to happen as the line that accepts new
connections is blocking, which means that the
program waits for a connection before moving
on from that line. For this reason, we need to
enclose the line in a try block, so that we can
catch the socket error and exit when we can no
longer accept connections. This will usually be
whenwe’veclosedthesocketduringtheprocess
ofquittingtheprogram.
def run(self):
while True:
# Listen for clients, and create a
ClientThread for each new client
print “Listening for
more clients”
try:
(client_socket,
client_address) = self.listener.
accept()
except socket.error:
sys.exit(“Could not
94 The Python Book
Work with Python
09Tidyingup
We need to have a function to tidy up
the thread. We’ll call this either when the client
sends us a blank string (indicating that it’s
stopped listening on the socket) or sends us the
string “QUIT”. When this happens, we’ll echo to
every client that the user has quit.
def quit(self):
# Tidy up and end the thread
self.listening = False
self.socket.close()
self.server.remove_
socket(self.socket)
self.server.echo({0} has
quit.n.format(self.username))
05Theechofunction
We need a function that can be called
from a client’s thread to echo a message to each
client. This function is pretty simple. The most
important part is that sending data to sockets is
in a try block, which means that we can handle
the exception if the operation fails, rather than
havingtheprogramcrash.
def echo(self, data):
# Send a message to each socket in
self.client_socket
print echoing: {0}.
format(data)
for socket in self.client_
sockets:
# Try and echo to all clients
try:
socket.sendall(data)
except socket.error:
print Unable to send
message
06FinishingtheServerclass
The remainder of the Server class is
taken up with a couple of simple functions;
one to remove a socket from the collection of
sockets, which doesn’t need an explanation,
and the signal_handler function that we talked
about in the initialiser of the class. This function
stops listening for new connections, and
unbinds the socket from the port it was listening
on. Finally, we send a message to each client to
let them know that we are exiting. The signal will
continue to close the program as expected once
thesignal_handlerfunctionhasended.
def remove_socket(self, socket):
07Theclientthread
The class that is used to deal with each
client inherits the Thread class. This means
that the class can be created, then started with
client_thread.start(). At this point, the
code in the run function of the class will be run in
the background and the main loop of the Server
classwillcontinuetoacceptnewconnections.
We have to start by initialising the Thread base
class, using the super keyword. You may have
noticed that when we created a new instance of
theClientListenerclassintheserver’smainloop,
we passed through the server’s self variable. We
dothisbecauseit’sbetterforeachinstanceofthe
ClientListener class to have its own reference to
the server, rather than using the global one that
we’llcreatelatertoactuallystarttheapplication.
class ClientListener(threading.
Thread):
def __init__(self, server,
socket, address):
# Initialise the Thread base class
super(ClientListener,
self).__init__()
# Store the values that have been
passed to the constructor
self.server = server
self.address = address
self.socket = socket
self.listening = True
self.username = No
Username
08Theclientthread’sloop
The loop that runs in the client thread
is pretty similar to the one in the server. It keeps
listening for data while self.listening is true,
and passes any data it gets to a handle_msg
function that we will write shortly. The value
passed to the socket.recv function is the size of
10Handlingmessages
There are three possible messages our
clientscansend:
• QUIT
• USERNAME user
• Arbitrary string to be echoed to all clients
The client will also send a bunch of empty
messages if the socket has been closed, so we
will end their thread if that happens. The code
should be pretty self-explanatory apart from
the regular expression part. If someone sends
the USERNAME message, then the server tells
every client that a new user has joined. This is
tested with a regular expression. ^ indicates the
start of the string, $ indicates the end, and the
brackets containing .* extract whatever comes
after“USERNAME”.
# Remove the specified socket from the
client_sockets list
self.client_sockets.
remove(socket)
def signal_handler(self, signal,
frame):
# Run when Ctrl+C is pressed
print Tidying up
# Stop listening for new connections
self.listener.close()
# Let each client know we are quitting
self.echo(QUIT)
accept any more connections”)
self.client_sockets.
append(client_socket)
print “Starting client
thread for {0}”.format(client_
address)
client_thread =
ClientListener(self, client_socket,
client_address)
client_thread.start()
time.sleep(0.1)
thebuffertousewhilereceivingdata.
def run(self):
# The thread's loop to receive and
process messages
while self.listening:
data = 
try:
data = self.socket.
recv(1024)
except socket.error:
Unable to recieve
data
self.handle_msg(data)
time.sleep(0.1)
# The while loop has ended
print Ending client thread
for {0}.format(self.address)
Work with Python
11Startingtheserver
The code that actually starts the Server
class is as follows. Note that you are probably
best picking a high-numbered port as you need
toberoottoopenports1024.
if __name__ == __main__:
# Start a server on port 59091
server = Server(59091)
server.run()
13Theclientgraphicaluserinterface
The user interface of the client isn’t
the main focus of the tutorial, and won’t be
explained in as much detail as the rest of
the code. However, the code should be fairly
straightforward to read and we have provided
links to documentation that will help.
Our MainWindow class inherits the gtk
Window class, so we need to start by initialising
that using the super keyword. Then we create
the controls that will go on the window, connect
any events they have to functions, and finally
lay out the controls how we want. The destroy
event is raised when the program is closed, and
the other events should be obvious.
GTK uses a packing layout, in which you use
Vboxes and Hboxes to lay out the controls. V
and H stand for vertical and horizontal. These
controls essentially let you split a window
up almost like a table, and will automatically
decide the size of the controls depending on the
size of the application.
GTK doesn’t come with a control to enter
basic information, such as the server’s IP
address, port and your chosen username, so
we’ve made a function called ask_for_info,
which creates a message box, adds a text
box to it and then retrieves the results. We’ve
done this because it’s simpler and uses less
code than creating a new window to accept
the information.
12Theclient
Create a new file for the client as we did
for the server and open it in your favourite editor.
The client requires the same imports as the
server, as well as the gtk, gobject and datetime
libraries. One important thing we need to do is to
tell GObject that we’ll be using threading, so we
can call functions from other threads and have
the main window, which is running in the main
GTKthread,update.
class MainWindow(gtk.Window):
def __init__(self):
# Initialise base gtk window class
super(MainWindow, self).__
init__()
# Create controls
self.set_title(IM Client)
vbox = gtk.VBox()
hbox = gtk.HBox()
self.username_label = gtk.
Label()
self.text_entry = gtk.
Entry()
send_button = gtk.
Button(Send)
self.text_buffer = gtk.
TextBuffer()
text_view = gtk.
TextView(self.text_buffer)
# Connect events
self.connect(destroy,
self.graceful_quit)
send_button.
connect(clicked, self.send_
message)
# Activate event when user presses
Enter
self.text_entry.
connect(activate, self.send_
message)
# Do layout
vbox.pack_start(text_view)
hbox.pack_start(self.
username_label, expand = False)
WeneedtotellGObjectthatwe’llbe
usingthreading
def handle_msg(self, data):
# Print and then process the message
we’ve just recieved
print {0} sent: {1}.
format(self.address, data)
# Use regular expressions to test for
a message like USERNAME liam
username_result =
re.search('^USERNAME (.*)$', data)
if username_result:
self.username =
username_result.group(1)
self.server.echo({0}
has joined.n.format(self.
username))
elif data == QUIT:
# If the client has sent quit then
close this thread
self.quit()
elif data == :
# The socket at the other end is
probably closed
self.quit()
else:
# It's a normal message so echo it to
everyone
self.server.echo(data)
#!/usr/bin/env python2
# The client side of an instant
messaging application. Written as
part of a Linux User  Developer
tutorial by Liam Fraser in 2013.
import threading
import gtk
import gobject
import socket
import re
import time
import datetime
# Tell gobject to expect calls from
multiple threads
gobject.threads_init()
Work with Python
14Configuringtheclient
This code is run after we’ve added the
controls to the main window, and asks the user
for input. Currently, the application will exit if the
user enters an incorrect server address or port;
butthisisn’taproductionsystem,sothat’sfine.
def configure(self):
# Performs the steps to connect to
the server
# Show a dialog box asking for server
address followed by a port
server = self.ask_for_
info(server_address:port)
# Regex that crudely matches an IP
address and a port number
regex = re.search('^(d+.
d+.d+.d+):(d+)$', server)
address = regex.group(1).
strip()
port = regex.group(2).
strip()
# Ask for a username
self.username = self.ask_
for_info(username)
self.username_label.set_
text(self.username)
# Attempt to connect to the server
and then start listening
self.network =
Networking(self, self.username,
address, int(port))
self.network.listen()
Theserverisgoingtoechothe
messagetoeachclient
15TheremainderofMainWindow
The rest of the MainWindow class has
plenty of comments to explain itself, as follows.
One thing to note is that when a client sends a
message, it doesn’t display it in the text view
straight away. The server is going to echo the
message to each client, so the client simply
displays its own message when the server
echoes it back. This means that you can tell if
the server is not receiving your messages when
youdon’tseeamessagethatyousend.
def add_text(self, new_text):
# Add text to the text view
text_with_timestamp = {0}
{1}.format(datetime.datetime.now(),
new_text)
# Get the position of the end of
the text buffer, so we know where to
insert new text
end_itr = self.text_buffer.
get_end_iter()
# Add new text at the end of the buffer
self.text_buffer.insert(end_
itr, text_with_timestamp)
def send_message(self, widget):
# Clear the text entry and send the
message to the server
# We don't need to display it as it
will be echoed back to each client,
including us.
new_text = self.text_entry.
get_text()
self.text_entry.set_text()
message = {0} says: {1}n.
format(self.username, new_text)
self.network.send(message)
def graceful_quit(self, widget):
# When the application is closed,
tell GTK to quit, then tell the
server we are quitting and tidy up
the network
gtk.main_quit()
self.network.send(QUIT)
self.network.tidy_up()
hbox.pack_start(self.text_
entry)
hbox.pack_end(send_button,
expand = False)
vbox.pack_end(hbox, expand
= False)
# Show ourselves
self.add(vbox)
self.show_all()
# Go through the configuration
process
self.configure()
def ask_for_info(self,
question):
# Shows a message box with a text
entry and returns the response
dialog = gtk.
MessageDialog(parent = self, type =
gtk.MESSAGE_QUESTION,
flags = gtk.DIALOG_MODAL |
gtk.DIALOG_DESTROY_WITH_PARENT,
buttons = gtk.BUTTONS_OK_CANCEL,
message_format = question)
entry = gtk.Entry()
entry.show()
dialog.vbox.pack_end(entry)
response = dialog.run()
response_text = entry.
get_text()
dialog.destroy()
if response == gtk.RESPONSE_
OK:
return response_text
else:
return None
Work with Python
The Python Book 97
17Runningafunctionasathread
The listener function above will be run
as a thread. This is trivial to do. Enabling the
daemon option on the thread means that it will
dieifthemainthreadunexpectedlyends.
def listen(self):
# Start the listening thread
self.listen_thread =
threading.Thread(target=self.
listener)
# Stop the child thread from keeping
the application open
self.listen_thread.daemon =
True
self.listen_thread.start()
18FinishingtheNetworkingclass
Again, most of this code is similar to
the code in the server’s Networking class. One
19Startingtheclient
The main window is started by initialising
an instance of the class. Notice that we don’t
need to store anything that is returned. We then
starttheGTKthreadbycallinggtk.main().
if __name__ == __main__:
# Create an instance of the main
window and start the gtk main loop
MainWindow()
gtk.main()
21That’sit!
So, it’s not perfect and could be a little
more robust in terms of error handling, but we
have a working instant messenger server that
can accept multiple clients and relay messages
between them. More importantly, we have
learned a bunch of new concepts and methods
ofworking.
20Tryingitout
You’ll want a few terminals: one to
start the server, and some to run clients. Once
you’ve started the server, open an instance of
the client and enter 127.0.0.1:port, where
‘port’ is the port you decided to use. The server
will print the port it’s listening on to make this
easy. Then enter a username and click OK. Here
is an example output from the server with two
clients. You can use the client over a network
by replacing 127.0.0.1 with the IP address of the
server. You may have to let the port through your
computer’sfirewallifit’snotworking.
[liam@liam-laptop Python]$ ./IM-
Server.py
Listening on port 59091
Listening for more clients
Starting client thread for
('127.0.0.1', 38726)
('127.0.0.1', 38726) sent: USERNAME
client1
echoing: client1 has joined.
Listening for more clients
Starting client thread for
('127.0.0.1', 38739)
('127.0.0.1', 38739) sent: USERNAME
client2
echoing: client2 has joined.
Listening for more clients
('127.0.0.1', 38739) sent: client2
says: Hi
echoing: client2 says: Hi
('127.0.0.1', 38726) sent: client1
says: Hi
echoing: client1 says: Hi
('127.0.0.1', 38726) sent: QUIT
echoing: client1 has quit.
Ending client thread for
('127.0.0.1', 38726)
^CTidying up
echoing: QUIT
Could not accept any more
connections
('127.0.0.1', 38739) sent:
echoing: client2 has quit.
Ending client thread for
('127.0.0.1', 38739)
difference is that we want to add some things to
the text view of our window. We do this by using
the idle_add function of GObject. This allows
us to call a function that will update the window
runninginthemainthreadwhenitisnotbusy.
def send(self, message):
# Send a message to the server
print Sending: {0}.
format(message)
try:
self.socket.
sendall(message)
except socket.error:
print Unable to send
message
def tidy_up(self):
# We'll be tidying up if either we are
quitting or the server is quitting
self.listening = False
self.socket.close()
# We won't see this if it's us
that's quitting as the window will
be gone shortly
gobject.idle_add(self.
window.add_text, Server has
quit.n)
def handle_msg(self, data):
if data == QUIT:
# Server is quitting
self.tidy_up()
elif data == :
# Server has probably closed
unexpectedly
self.tidy_up()
else:
# Tell the GTK thread to add some
text when it's ready
gobject.idle_add(self.
window.add_text, data)
16Theclient’sNetworkingclass
Much of the client’s Networking class is
similar to that of the server’s. One difference is
that the class doesn’t inherit the Thread class –
wejuststartoneofitsfunctionsasathread.
class Networking():
def __init__(self, window,
username, server, port):
# Set up the networking class
self.window = window
self.socket = socket.
socket(socket.AF_INET, socket.SOCK_
STREAM)
self.socket.connect((server,
port))
self.listening = True
# Tell the server that a new user
has joined
self.send(USERNAME {0}.
format(username))
def listener(self):
# A function run as a thread that
listens for new messages
while self.listening:
data = 
try:
data = self.socket.
recv(1024)
except socket.error:
Unable to recieve
data
self.handle_msg(data)
# Don't need the while loop to be
ridiculously fast
time.sleep(0.1)
98 The Python Book
Work with Python
Pythonisagreatprogramminglanguage,butdidyouknowthat
itisevencapableofreplacingyourprimaryshell(command-line
interface)?Here,weexplainall…
We all use shell on a daily basis. For most
of us, shell is the gateway into our Linux
system. For years and even today, Bash has
beenthedefaultshellforLinux.Butitisgettinga
bitlonginthetooth.
No need to be offended: we still believe Bash
is the best shell out there when compared to
some other UNIX shells such as Korn Shell
(KSH),CShell(CSH)orevenTCSH.
This tutorial is not about Bash being
incapable, but it is about how to breathe
completely new life into the shell to do old
things conveniently and new things which were
previously not possible, even by a long shot. So,
withoutfurtherdelay,let’sjumpin.
While the Python programming language
may require you to write longer commands to
accomplish a task (due to the way Python’s
modules are organised), this is not something
to be particularly concerned about. You can
easily write aliases to the equivalent of the Bash
command that you intend to replace. Most of
the time there will be more than one way to do
a thing, but you will need to decide which way
worksbestforyou.
Python provides support for executing
system commands directly (via the os or
subprocess module), but where possible we will
focus on Python-native implementations, as
thisallowsustodevelopportablecode.
Replaceyourshell
withPython
YouwillrequireaversionofPythoninstalledon
yoursystem.Thegoodnewsisyoudon’thaveto
doanythingtogetitinstalled.MostoftheLinux
distributionsalreadyship with eitherPython 2.6or
Python2.7
Resources
The Python Book 99
Work with Python
Youcaneasilywritealiasestothe
equivalentoftheBashcommandthat
youintendtoreplace
SECTION1:Completingbasicshell
tasksinPython
1.Filemanagement
The Python module shutil provides support for
file and directory operations. It provides support
for file attributes, directory copying, archiving
etc.Let’slookatsomeofitsimportantfunctions.
shutil module
copy (src,dst): Copy the src file to
the destination directory. In this
mode permissions bits are copied but
metadata is not copied.
copy2 (src,dst): Same as copy() but
also copies the metadata.
copytree(src, dst[, symlinks=False[,
ignore=None]]): This is similar to ‘cp
-r’, it allows you to copy an entire
directory.
ignore_patterns (*patterns): ignore_
patterns is an interesting function
that can be used as a callable for
copytree(), it allows you to ignore
files and directories specified by the
glob-style patterns.
rmtree(path[, ignore_errors[,
onerror]]): rmtree() is used to delete
an entire directory.
move(src,dst): Similar to mv command it
allows you to recessively move a file
or directory to a new location.
Example:
from shutil import copytree, ignore_
patterns
copytree(source, destination,
ignore=ignore_patterns(‘*.pyc’,
‘tmp*’))
make_archive(base_name, format[, root_
dir[, base_dir[, verbose[, dry_run[,
owner[, group[, logger]]]]]]] : Think
of this as a replacement for tar, zip,
bzip etc. make_archive() creates an
archive file in the given format
such as zip, bztar, tar , gztar.
Archive support can be extended via
Python modules.
Example
 from shutil import make_archive
 import os
 archive_name = os.path.
expanduser(os.path.join(‘~’,
‘ludarchive’))
 root_dir = os.path.expanduser(os.
path.join(‘~’, ‘.ssh’))
 make_archive(archive_name, ‘gztar’,
root_dir)
‘/Users/kunal/ludarchive.tar.gz’
2.Interfacingoperatingsystem
subprocesses
Python provides two modules to interface
with the OS and to manage processes, called
os and subprocess. These modules allow you
to interact with the core operating system
shell and let you work with the environment,
processes,usersandfiledescriptors.
The subprocess module was introduced to
support better management of subprocesses
(part of which already exists in the os
module) in Python and is aimed to replace
os.system, os.spawn*, os.popen, popen2.* and
commands.*modules.
osmodule
environ: environment represents the OS
environmentvariablesinastringobject.
example:
 import os
 os.environ
{‘VERSIONER_PYTHON_PREFER_32_BIT’:
‘no’, ‘LC_CTYPE’: ‘UTF-8’, ‘TERM_
PROGRAM_VERSION’: ‘297’, ‘LOGNAME’:
‘kunaldeo’, ‘USER’: ‘kunaldeo’, ‘PATH’:
‘/System/Library/Frameworks/Python.
framework/Versions/2.7/bin:/Users/
kunaldeo/narwhal/bin:/opt/local/sbin:/
usr/local/bin:/usr/bin:/bin:/usr/sbin:/
sbin:/usr/local/bin:/usr/X11/bin:/opt/
local/bin:/Applications/MOTODEV_Studio_
For_Android_2.0.0_x86/android_sdk/
tools:/Applications/MOTODEV_Studio_For_
Android_2.0.0_x86/android_sdk/platform-
tools:/Volumes/CyanogenModWorkspace/
bin’, ‘HOME’: ‘/Users/kunaldeo’,
‘PS1’: ‘[e[0;32m]u[e[m]
[e[1;34m]w[e[m] 
[e[1;32m]$[e[m] 
[e[1;37m]’, ‘NARWHAL_ENGINE’:
‘jsc’, ‘DISPLAY’: ‘/tmp/launch-s2LUfa/
org.x:0’, ‘TERM_PROGRAM’: ‘Apple_
Terminal’, ‘TERM’: ‘xterm-color’,
‘Apple_PubSub_Socket_Render’: ‘/tmp/
launch-kDul5P/Render’, ‘VERSIONER_
PYTHON_VERSION’: ‘2.7’, ‘SHLVL’:
‘1’, ‘SECURITYSESSIONID’: ‘186a5’,
‘ANDROID_SDK’: ‘/Applications/MOTODEV_
Studio_For_Android_2.0.0_x86/android_
sdk’,’_’: ‘/System/Library/Frameworks/
Python.framework/Versions/2.7/bin/
python’, ‘TERM_SESSION_ID’: ‘ACFE2492-
BB5C-418E-8D4F-84E9CF63B506’, ‘SSH_
AUTH_SOCK’: ‘/tmp/launch-dj6Mk4/
Listeners’, ‘SHELL’: ‘/bin/bash’,
‘TMPDIR’: ‘/var/folders/6s/pgknm8b118
737mb8psz8x4z80000gn/T/’, ‘LSCOLORS’:
‘ExFxCxDxBxegedabagacad’, ‘CLICOLOR’:
‘1’, ‘__CF_USER_TEXT_ENCODING’:
‘0x1F5:0:0’, ‘PWD’: ‘/Users/kunaldeo’,
‘COMMAND_MODE’: ‘unix2003’}
You can also find out the value for an
environmentvalue:
 os.environ[‘HOME’]
‘/Users/kunaldeo’
putenv(varname,value) : Adds or sets
an environment variable with the given
variable name and value.
getuid() : Return the current process’s
user id.
getlogin() : Returns the username of
currently logged in user
getpid(pid) : Returns the process group
id of given pid. When used without
any parameters it simply returns the
current process id.
getcwd() : Return the path of the
current working directory.
chdir(path) : Change the current
working directory to the given path.
100 The Python Book
Work with Python
listdir(path) : Similar to ls, returns
a list with the content of directories
and file available on the given path.
Example:
 os.listdir(“/home/homer”)
[‘.gnome2’, ‘.pulse’, ‘.gconf’,
‘.gconfd’, ‘.beagle’, ‘.gnome2_
private’, ‘.gksu.lock’, ‘Public’,
‘.ICEauthority’, ‘.bash_history’,
‘.compiz’, ‘.gvfs’, ‘.update-
notifier’, ‘.cache’, ‘Desktop’,
‘Videos’, ‘.profile’, ‘.config’,
‘.esd_auth’, ‘.viminfo’, ‘.sudo_
as_admin_successful’, ‘mbox’,
‘.xsession-errors’, ‘.bashrc’, ‘Music’,
‘.dbus’, ‘.local’, ‘.gstreamer-0.10’,
‘Documents’, ‘.gtk-bookmarks’,
‘Downloads’, ‘Pictures’, ‘.pulse-
cookie’, ‘.nautilus’, ‘examples.
desktop’, ‘Templates’, ‘.bash_logout’]
mkdir(path[, mode]) : Creates a
directory with the given path with the
numeric code mode. The default mode is
0777.
makedirs(path[, mode]) : Creates given
path (inclusive of all its directories)
recursively. The default mode is 0777.
:
Example:
 import os
 path = “/home/kunal/greatdir”
 os.makedirs( path, 0755 );
rename (old,new) : The file or
directory “old” is renamed to “new”
If “new” is a directory, an error
will be raised. On Unix and Linux, if
“new” exists and is a file, it will
be replaced silently if the user has
permission to do so.
renames (old,new) : Similar to rename
but also creates any directories
recessively if necessary.
rmdir(path) : Remove directory from the
path mentioned. If the path already
has files you will need to use shutil.
rmdtree()
subprocess:
call(*popenargs, **kwargs) : Runs the
command with arguments. On process
completion it returns the returncode
attribute.
Example:
 import subprocess
 print subprocess.call([“ls”,”-l”])
total 3684688
drwx------+ 5 kunaldeo staff
170 Aug 19 01:37 Desktop
drwx------+ 10 kunaldeo staff
340 Jul 26 08:30 Documents
drwx------+ 50 kunaldeo staff
1700 Aug 19 12:50 Downloads
drwx------@ 127 kunaldeo staff
4318 Aug 19 01:43 Dropbox
drwx------@ 42 kunaldeo staff
1428 Aug 12 15:17 Library
drwx------@ 3 kunaldeo staff
102 Jul 3 23:23 Movies
drwx------+ 4 kunaldeo staff
136 Jul 6 08:32 Music
drwx------+ 5 kunaldeo staff
170 Aug 12 11:26 Pictures
drwxr-xr-x+ 5 kunaldeo staff
170 Jul 3 23:23 Public
-rwxr-xr-x 1 kunaldeo staff
1886555648 Aug 16 21:02 androidsdk.tar
drwxr-xr-x 5 kunaldeo staff
170 Aug 16 21:05 sdk
drwxr-xr-x 19 kunaldeo staff
646 Aug 19 01:47 src
-rw-r--r-- 1 root staff
367 Aug 16 20:36 umbrella0.log
STD_INPUT_HANDLE: The standard input
device.Initially,thisistheconsoleinputbuffer.
STD_OUTPUT_HANDLE: The standard output
device. Initially, this is the active console
screenbuffer.
STD_ERROR_HANDLE: The standard error
device. Initially, this is the active console
screenbuffer.
SECTION2:IPython:aready-made
Pythonsystemshellreplacement
In section 1 we have introduced you to the
Python modules which allow you to do system
shell-related tasks very easily using vanilla
Python. Using the same features, you can build
a fully featured shell and remove a lot of Python
boilerplate code along the way. However, if
you are kind of person who wants everything
ready-made, you are in luck. IPython provides a
powerful and interactive Python shell which you
can use as your primary shell. IPython supports
Python 2.6 to 2.7 and 3.1 to 3.2 . It supports
two type of Python shells: Terminal based and
Qtbased.
Just to reiterate, IPython is purely implemented
in Python and provides a 100% Python-
compliant shell interface, so everything you
have learnt in section 1 can be run inside
IPythonwithoutanyproblems.
IPython is already available in most Linux
distributions. Search your distro’s repositories to
look for it. In case you are not able to find it, you
canalsoinstallitusingeasy_installorPyPI.
IPython provides a lot of interesting features
whichmakesitagreatshellreplacement…
Tab completion: Tab completion provides an
excellent way to explore any Python object that
you are working with. It also helps you to avoid
makingtypos.
Example:
In [3]: import o {hit tab}
objc opcode operator
optparse os os2emxpath
In [3]: import os
In [4]: os.p {hit tab}
os.pardir os.pathconf_names
os.popen os.popen4
os.path os.pathsep
os.popen2 os.putenv
os.pathconf os.pipe
os.popen3
Built In Object Explorer: You can add
‘?’ after any Python object to view
its details such as Type, Base Class,
String Form, Namespace, File and
Docstring.
Example:
In [28]: os.path?
Type: module
Base Class: type ‘module’
String Form:module ‘posixpath’ from
‘/System/Library/Frameworks/Python.
framework/Versions/2.7/lib/python2.7/
posixpath.pyc’
Namespace: Interactive
File: /System/Library/Frameworks/
The Python Book 101
Work with Python
As you can see, it’s easy to tailor Python
for all your shell environment needs.
Python modules like os, subprocess
and shutil are available at your
disposal to do just about everything
you need using Python. IPython turns
this whole experience into an even
more complete package. You get to do
everything a standard Python shell
does and with much more convenient
features. IPython’s magic functions
really do provide a magical Python shell
experience. So next time you open a
Bash session, think again: why settle for
goldwhen platinum is a stepaway?
IPythonalsocomeswithitsown
Qt-basedconsole
Python.framework/Versions/2.7/lib/
python2.7/posixpath.py
Docstring:
Common operations on POSIX pathnames.
Instead of importing this module directly, import
os and refer to this module as os.path. The
‘os.path’ name is an alias for this module on
POSIX systems; on other systems (eg Mac,
Windows),os.pathprovidesthesameoperations
in a manner specific to that platform, and is an
aliastoanothermodule(egmacpath,ntpath).
Some of this can actually be useful on non-
POSIX systems too, eg for manipulation of the
pathnamecomponentofURLs.
You can also use double question marks (??) to
viewthesourcecodefortherelevantobject.
Magic functions: IPython comes with a set of
predefined ‘magic functions’ that you can call
with a command-line-style syntax. IPython
‘magic’ commands are conventionally prefaced
by %, but if the flag %automagic is set to on,
then you can call magic commands without the
preceding%.
To view a list of available magic functions,
you can use ‘magic function %lsmagic’. Magic
functions include functions that work with code
such as %run, %edit, %macro, %recall etc;
functions that affect shell such as %colors,
%xmode, %autoindent etc; and other functions
such as %reset, %timeit, %paste etc. Most of
the cool features of IPython are powered using
magicfunctions.
Example:
In [45]: %lsmagic
Available magic functions:
%alias %autocall %autoindent
%automagic %bookmark %cd %colors
%cpaste %debug %dhist %dirs
%doctest_mode %ed %edit %env %gui
%hist %history %install_default_
config %install_profiles %load_ext
%loadpy %logoff %logon %logstart
%logstate %logstop %lsmagic %macro
%magic %page %paste %pastebin %pdb
%pdef %pdoc %pfile %pinfo %pinfo2
%popd %pprint %precision %profile
%prun %psearch %psource %pushd %pwd
%pycat %pylab %quickref %recall
%rehashx %reload_ext %rep %rerun
%reset %reset_selective %run %save
%sc %sx %tb %time %timeit %unalias
%unload_ext %who %who_ls %whos
%xdel %xmode
Automagic is OFF, % prefix IS needed
for magic functions
To view help on any Magic Function, call
‘%somemagic?’toreaditsdocstring.
Python script execution and runtime code
editing: You can use %run to run any Python
script. You can also control-run the Python
script with pdb debugger using -d, or pdn
profiler using -p. You can also edit a Python
script using the %edit command. %edit will
open the given Python script in the editor
definedbythe$EDITORenvironmentvariable.
Shell command support: If you are in the mood
to just run a shell command, you can do it very
easilybyprefixingthecommandwith!.
Example:
In [5]: !ps
PID TTY TIME CMD
4508 ttys000 0:00.07 -bash
84275 ttys001 0:00.03 -bash
17958 ttys002 0:00.18 -bash
In [8]: !clang prog.c -o prog
prog.c:2:1: warning: type specifier
missing, defaults to ‘int’ [-Wimplicit-
int]
main()
^~~~
1 warning generated.
Qt console : IPython also comes with its own
Qt-based console. This provides a number of
features that are only available in a GUI, such
as inline figures, multiline editing with syntax
highlighting,andgraphicalcalltips.
YoucanstarttheQtconsolewith:
$ ipython qtconsole
If you get errors related to missing modules,
makesurethatyouhaveinstalledthedependent
packages, such as PyQt, pygments, pyexpect
andZeroMQ.
QIPythonQtconsolewithGUIcapabilities
102 The Python Book
Work with Python
LearnhowPythoncanhelpinsystemadministrationasitdaresto
replacetheusualshellscripting…
Pythonforsystem
administrators
Parsingconfigurationfiles
Configurationfilesprovideawayforapplications
to store various settings. In order to write a
script that allows you to modify settings of a
particular application, you should be able to
parse the configuration file of the application.
In this section we learn how to parse INI-style
configuration files. Although old, the INI file
format is very popular with much modern open
sourcesoftware,suchasPHPandMySQL.
Excerptforphp.iniconfigurationfile:
[PHP]
engine = On
zend.ze1_compatibility_mode = Off
short_open_tag = On
asp_tags = Off
precision = 14
y2k_compliance = On
output_buffering = 4096
;output_handler =
zlib.output_compression = Off
[MySQL]
; Allow or prevent persistent links.
mysql.allow_persistent = On
mysql.max_persistent = 20
mysql.max_links = -1
mysql.default_port = 3306
mysql.default_socket =
mysql.default_host = localhost
mysql.connect_timeout = 60
mysql.trace_mode = Off
Python provides a built-in module called
ConfigParser (known as configparser in Python
3.0).Youcanusethismoduletoparseandcreate
configurationfiles.
@code:writeconfig.py
@description: The following demonstrates
addingMySQLsectiontothephp.inifile.
@warning: Do not use this script with the
actual php.ini file, as it’s not designed to
handleallaspectsofacompletephp.inifile.
import ConfigParser
config = ConfigParser.
RawConfigParser()
config.add_section(‘MySQL’)
config.set(‘MySQL’,’mysql.trace_
mode’,’Off’)
config.set(‘MySQL’,’mysql.connect_
timeout’,’60’)
config.set(‘MySQL’,’mysql.default_
host’,’localhost’)
config.set(‘MySQL’,’mysql.default_
port’,’3306’)
config.set(‘MySQL’,’mysql.allow_
persistent’, ‘On’ )
config.set(‘MySQL’,’mysql.max_
persistent’,’20’)
with open(‘php.ini’, ‘ap’) as
configfile:
config.write(configfile)
Output:php.ini
[MySQL]
mysql.max_persistent = 20
mysql.allow_persistent = On
mysql.default_port = 3306
mysql.default_host = localhost
mysql.trace_mode = Off
mysql.connect_timeout = 60
@code:parseconfig.py
@description: Parsing and updatingthe config
file
import ConfigParser
config = ConfigParser.ConfigParser()
config.read(‘php.ini’)
# Print config values
print config.get(‘MySQL’,’mysql.
System administration is an important part of
our computing environment. It does not matter
whether you are managing systems at your work
our home. Linux, being a UNIX-based operating
system, already has everything a system
administrator needs, such as the world-class
shells(notjustonebutmany,includingBash,csh,
zsh etc), handy tools, and many other features
which make the Linux system an administrator’s
dream. So why do we need Python when Linux
already has everything built-in? Being a dynamic
scripting language, Python is very easy to read
and learn. That’s just not us saying that, but
many Linux distributions actually use Python
in core administrative parts. For example, Red
Hat (and Fedora) system setup tool Anaconda
is written in Python (read this line again, got the
snakeconnection?).Also,toolslikeGNUMailman,
CompizConfig Settings Manager (CCSM) and
hundreds of tiny GUI and non-GUI configuration
tools are written using Python. Python does not
limit you on the choice of user interface to follow
–youcanbuildcommand-line,GUIandwebapps
using Python. This way, it has got covered almost
allthepossibleinterfaces.
Here we will look into executing sysadmin-
relatedtasksusingPython.
Python-develPython development
libraries, required for compiling
third-party Python module
setuptoolssetuptools allows you to
download, build, install, upgrade,
and uninstall Python packages
with ease
Resources
Note
This is written for the Python 2.X series,
as it is still the most popular and default
Python distribution across all the
platforms (including all Linux distros,
BSDsandMacOSX).
The Python Book 103
Work with Python
default_host’)
print config.get(‘MySQL’,’mysql.
default_port’)
config.remove_option(‘MySQL’,’mysql.
trace_mode’)
with open(‘php.ini’, ‘wb’) as
configfile:
config.write(configfile)
ParsingJSONdata
JSON (also known as JavaScript Object
Notation) is a lightweight modern data-
interchange format. JSON is an open standard
under ECMA-262. It is a text format and is
completely language-independent. JSON
is also used as the configuration file format
for modern applications such as Mozilla
Firefox and Google Chrome. JSON is also
very popular with modern web services such
as Facebook, Twitter, Amazon EC2 etc. In
this section we will use the Python module
‘simplejson’ to access Yahoo Search (using
the Yahoo Web Services API), which outputs
JSONdata.
To use this section, you should have the
following:
1.Pythonmodule:simplejson.
Note: You can install Python modules using the
command ‘easy_install module name’. This
command assumes that you have a working
internetconnection.
2. Yahoo App ID: The Yahoo App ID can be
created from https://developer.apps.yahoo.
com/dashboard/createKey.html. The Yahoo
App ID will be generated on the next page. See
thescreenshotbelowfordetails.
QGeneratingtheYahooAppID
simplejson is very easy to use. In the following
example we will use the capability of mapping
JSON data structures directly to Python data
types. This gives us direct access to the JSON
datawithoutdevelopinganyXMLparsingcode.
JSONPYTHONDATAMAPPING
JSON Python
object dict
array list
string unicode
number(int) int,long
number(real) float
TRUE TRUE
FALSE FALSE
null None
For this section we will use the simplejson.
load function, which allows us to deserialise a
JSONobjectintoaPythonobject.
@code:LUDSearch.py
import simplejson, urllib
APP_ID = ‘xxxxxxxx’ # Change this to
your APP ID
SEARCH_BASE = ‘http://search.
yahooapis.com/WebSearchService/V1/
webSearch’
class YahooSearchError(Exception):
pass
def search(query, results=20,
start=1, **kwargs):
kwargs.update({
‘appid’: APP_ID,
‘query’: query,
‘results’: results,
‘start’: start,
‘output’: ‘json’
})
url = SEARCH_BASE + ‘?’ +
urllib.urlencode(kwargs)
result = simplejson.load(urllib.
urlopen(url))
if ‘Error’ in result:
# An error occurred; raise
an exception
raise YahooSearchError,
result[‘Error’]
return result[‘ResultSet’]
Let’s use the above code from the Python shell
to see how it works. Change to the directory
where you have saved the LUDYSearch.py and
openaPythonshell.
@code: Python Shell Output. Lines starting
with‘’indicateinput
 execfile(“LUDYSearch.py”)
 results = search(‘Linux User and
Developer’)
 results[‘totalResultsAvailable’]
123000000
 results[‘totalResultsReturned’]
20
 items = results[‘Result’]
 for Result in items:
... print
Result[‘Title’],Result[‘Url’]
...
Linux User http://www.linuxuser.
co.uk/
Linux User and Developer -
Wikipedia, the free encyclopedia
http://en.wikipedia.org/wiki/Linux_
User_and_Developer
Linux User amp;amp; Developer |
Linux User http://www.linuxuser.
co.uk/tag/linux-user-developer/
Gatheringsystem
information
One of the important jobs of a system
administrator is gathering system information.
In this section we will use the SIGAR (System
Information Gatherer And Reporter) API to
demonstrate how we can gather system
information using Python. SIGAR is a very
complete API and it can provide lot of
information,includingthefollowing:
1. System memory, swap, CPU, load average,
uptime,logins.
2. Per-process memory, CPU, credential info,
state,arguments,environment,openfiles.
3.Filesystemdetectionandmetrics.
4. Network interface detection, configuration
infoandmetrics.
5.TCPandUDPconnectiontables.
6.Networkroutetable.
InstallingSIGAR
The first step is to build and install SIGAR. SIGAR
is hosted at GitHub, so make sure that you have
Git installed in your system. Then perform
the following steps to install SIGAR and its
Pythonbindings:
$ git clone git://github.com/
hyperic/sigar.git sigar.git
$ cd sigar.git/bindings/python
$ sudo python setup.py install
Pythondoesn’t
limityourchoice
ofinterface
104 The Python Book
Work with Python
At the end you should see a output similar to
thefollowing:
Writing /usr/local/lib/python2.6/
dist-packages/pysigar-0.1.egg-info
SIGAR is a very easy-to-use library and can be
usedtogetinformationonalmosteveryaspectof
asystem.Thenextexampleshowsyouhow.
The following code shows the memory and the
filesysteminformation
@code:PySysInfo.py
import os
import sigar
sg = sigar.open()
mem = sg.mem()
swap = sg.swap()
fslist = sg.file_system_list()
print “==========Memory
Information==============”
print “tTotaltUsedtFree”
print “Mem:t”,
(mem.total() / 1024), 
(mem.used() / 1024), 
(mem.free() / 1024)
print “Swap:t”, 
(swap.total() / 1024), 
(swap.used() / 1024), 
(swap.free() / 1024)
print “RAM:t”, mem.ram(), “MB”
print “==========File System
Information===============”
def format_size(size):
return sigar.format_size(size *
1024)
print ‘FilesystemtSizetUsed
tAvailtUse%tMounted ontTypen’
for fs in fslist:
dir_name = fs.dir_name()
usage = sg.file_system_
usage(dir_name)
total = usage.total()
used = total - usage.free()
avail = usage.avail()
pct = usage.use_percent() * 100
if pct == 0.0:
pct = ‘-’
print fs.dev_name(), format_
size(total), format_size(used),
format_size(avail),
pct, dir_name, fs.sys_type_
name(), ‘/’, fs.type_name()
@Output
==========Memory
Information==============
Total Used Free
Mem: 8388608 6061884 2326724
Swap: 131072 16048 115024
RAM: 8192 MB
==========File System
Information===============
Filesystem Size Used Avail
Use% Mounted on Type
/dev/disk0s2 300G 175G 124G 59.0 / hfs
/ local
devfs 191K 191K 0 - /dev devfs /
none
AccessingSecureShell
(SSH)services
SSH (Secure Shell) is a modern replacement for an
old remote shell system called Telnet. It allows data
to be exchanged using a secure channel between
two networked devices. System administrators
frequently use SSH to administrate networked
systems. In addition to providing remote shell, SSH
is also used for secure file transfer (using SSH File
Transfer Protocol, or SFTP) and remote X server
forwarding (allows you to use SSH clients as X
server). In this section we will learn how to use the
SSH protocol from Python using a Python module
called paramiko, which implements the SSH2
protocolforPython.
paramiko can be installed using the following
steps:
$ git clone https://github.com/robey/
paramiko.git
$ cd paramiko
$ sudo python setup.py install
To the core of paramiko is the
SSHClient class. This class
wraps L{Transport},L{Channel},andL{SFTPClient}
to handle most of the aspects of SSH. You can use
SSHClientas:
client = SSHClient()
client.load_system_host_keys()
client.connect(‘some.host.com’)
stdin, stdout, stderr = client.exec_
command(‘dir’)
The following example demonstrates a full SSH
clientwrittenusingtheparamikomodule.
@code:PySSHClient.py
import base64, getpass, os, socket, sys,
socket, traceback
import paramiko
import interactive
# setup logging
paramiko.util.log_to_file(‘demo_simple.
log’)
# get hostname
username = ‘’
if len(sys.argv)  1:
hostname = sys.argv[1]
if hostname.find(‘@’) = 0:
username, hostname = hostname.
Note
If you are confused with the tab spacing of
thecode,lookforthecodefilesonFileSilo.
split(‘@’)
else:
hostname = raw_input(‘Hostname: ‘)
if len(hostname) == 0:
print ‘*** Hostname required.’
sys.exit(1)
port = 22
if hostname.find(‘:’) = 0:
hostname, portstr = hostname.
split(‘:’)
port = int(portstr)
# get username
if username == ‘’:
default_username = getpass.
getuser()
username = raw_input(‘Username
[%s]: ‘ % default_username)
if len(username) == 0:
username = default_username
password = getpass.getpass(‘Password
for %s@%s: ‘ % (username, hostname))
# now, connect and use paramiko
Client to negotiate SSH2 across the
connection
try:
client = paramiko.SSHClient()
client.load_system_host_keys()
client.set_missing_host_key_
policy(paramiko.WarningPolicy)
print ‘*** Connecting...’
client.connect(hostname, port,
username, password)
chan = client.invoke_shell()
print repr(client.get_transport())
print ‘*** SSH Server Connected!
***’
print
interactive.interactive_
shell(chan)
chan.close()
client.close()
except Exception, e:
print ‘*** Caught exception: %s:
%s’ % (e.__class__, e)
traceback.print_exc()
try:
client.close()
except:
pass
sys.exit(1)
To run this code you will also need a custom
Python class interactive.py which implements
The Python Book 105
Work with Python
Administrators are comfortable with running raw
scripts by hand, but end-users are not. So if you
are writing a script that is supposed to be used by
common users, it is a good idea to create a user-
friendly interface on top of the script. This way
end-users can run the scripts just like any other
application. To demonstrate this, we will create
a simple GRUB configuration tool which allows
userstoselectdefaultbootentryandthetimeout.
We will be creating a TUI (text user interface)
application and will use the Python module
‘snack’ to facilitate this (not to be confused with
thePythonaudiolibrary,tksnack).
Thisappconsistsoftwofiles…
grub.py: GRUB Config File (grub.conf) Parser
(available on FileSilo). It implements two main
functions, readBootDB() and writeBootFile(),
which are responsible for reading and writing the
GRUBconfigurationfile.
grub_tui.py: Text user interface file for
manipulating the GRUB configuration file using
thefunctionsavailableingrub.py.
@code:grub_tui.py
import sys
from snack import *
from grub import (readBootDB,
writeBootFile)
def main(entry_
value=’1’,kernels=[]):
try:
(default_value, entry_
value, kernels)=readBootDB()
except:
print  sys.stderr,
(“Error reading /boot/grub/grub.
conf.”)
sys.exit(10)
screen=SnackScreen()
while True:
g=GridForm(screen, (“Boot
configuration”),1,5)
if len(kernels)0 :
li=Listbox(height=len(kernels),
width=20, returnExit=1)
for i, x in
enumerate(kernels):
li.append(x,i)
g.add(li, 0, 0)
li.setCurrent(default_value)
bb = ButtonBar(screen,
WritingauserinterfaceusingPython
Learnhowtocreateauser-friendlyinterfaceusingPython
(((“Ok”), “ok”), ((“Cancel”),
“cancel”)))
e=Entry(3, str(entry_
value))
l=Label((“Timeout (in
seconds):”))
gg=Grid(2,1)
gg.setField(l,0,0)
gg.setField(e,1,0)
g.add(Label(‘’),0,1)
g.add(gg,0,2)
g.add(Label(‘’),0,3)
g.add(bb,0,4,growx=1)
result = g.runOnce()
if
bb.buttonPressed(result) ==
‘cancel’:
screen.finish()
sys.exit(0)
else:
entry_value =
e.value()
try :
c = int(entry_
value)
break
except ValueError:
continue
writeBootFile(c,
li.current())
screen.finish()
if __name__== ‘__main__’:
main()
Start the tool using the sudo
command (as it reads the grub.
conf file)
$ sudo grub_tui.py
the interactive shell for the SSH session. Look
for this file on FileSilo and copy it into the same
folderwhereyouhavecreatedPySSHClient.py.
@code_Output
kunal@ubuntu-vm-kdeo:~/src/paramiko/
demos$ python demo_simple.py
Hostname: 192.168.1.2
Username [kunal]: luduser
Password for luduser@192.168.1.2:
*** Connecting...
paramiko.Transport at 0xb76201acL
(cipher aes128-ctr, 128 bits)
(active; 1 open channel(s))
*** SSH Server Connected! ***
Last login: Thu Jan 13 02:01:06 2011
from 192.168.1.9
[~ $:]
If the host key for the SSH server is not added
to your $HOME/.ssh/known_hosts file, the
clientwillthrowthefollowingerror:
*** Caught exception: type
‘exceptions.TypeError’: unbound
method missing_host_key() must be
called with WarningPolicy instance
as first argument (got SSHClient
instance instead)
This means that the client cannot verify the
authenticity of the server you are connected
to. To add the host key to known_hosts, you
can use the ssh command. It is important
to remember that this is not the ideal way to
add the host key; instead you should use ssh-
keygen. But for simplicity’s sake we are using
thesshclient.
kunal@ubuntu-vm-kdeo:~/.ssh$ ssh
luduser@192.168.1.2
The authenticity of host
‘192.168.1.2 (192.168.1.2)’ can’t be
established.
RSA key fingerprint is be:01:76:6a:b
9:bb:69:64:e3:dc:37:00:a4:36:33:d1.
Are you sure you want to continue
connecting (yes/no)? yes
Warning: Permanently added
‘192.168.1.2’ (RSA) to the list of
known hosts.
So now you’ve seen how easy it can be to
carry out the complex sysadmin tasks using
Python’sversatilelanguage.
As is the case with all Python coding, the
code that is presented here can easily be
adoptedintoyourGUIapplication(withsoftware
such as PyGTK or PyQt) or a web application
(usingaframeworksuchasDjangoorGrok).
106 The Python Book
108 Build tic-tac-toe with Kivy
Program noughts and crosses
112 Create two-step
authentication
Use Twilio for safe authentication
116 Program a Space
Invaders clone
Make the basic Pivaders game
120 Add animation and sound
Enhance your Pivaders game
124 Make a visual novel game
Use Python to make a storytelling game
128 Pygame Zero
Turn your ideas into games
Create
124
108
112
“You’llbesurprisedbythe
diversityofwhatyoucan
makewithPython”
Python
with
The Python Book 107
116
108 The Python Book
Create with Python
EaseintotheworkingsofKivybycreatingthepen-and-paperclassic
injustover100linesofPython...
Buildtic-tac-toewithKivy
Kivy is a highly cross-platform graphical
framework for Python, designed for the
creation of innovative user interfaces like
multitouch apps. Its applications can run not
onlyonthetraditionaldesktopplatformsofLinux,
OSXandWindows,butalsoAndroidandiOS,plus
devicesliketheRaspberryPi.
That means you can develop cross-platform
apps using Python libraries such as Requests,
SQLAlchemy or even NumPy. You can even
access native mobile APIs straight from Python
using some of Kivy’s sister projects. Another
great feature is the Cython-optimised OpenGL
graphicspipeline,allowingadvancedGPUeffects
eventhoughthebasicPythonAPIisverysimple.
Kivy is a set of Python/Cython modules that
can easily be installed via pip, but you’ll need a
fewdependencies.ItusesPygameasarendering
backend (though its API is not exposed), Cython
for compilation of the speedy graphics compiler
internals, and GStreamer for multimedia. These
should all be available through your distro’s
repositories,orviapipwhereapplicable.
With these dependencies satisfied, you
should be able install Kivy with the normal pip
incantation. The current version is 1.8.0, and the
same codebase supports both python2 and
python3. The code in this tutorial is also version-
agnostic,runninginpython2.7andpython3.3.
pip install kivy
If you have any problems with pip, you can use
easy_instalvia easy_install kivy.
There are also packages or repositories available
for several popular distros. You can find more

QTheclassic‘HelloWorld!’inKivyGUIform,
usingthebuilt-inLabelwidget
information on Kivy’s website. A kivy application is
startedbyinstantiatingandrunningan‘App’class.
This is what initialises our pp’s window, interfaces
with the OS, and provides an entry point for the
creation of our GUI. We can start by making the
simplestKivyapppossible:
from kivy.app import App
class TicTacToeApp(App):
pass
if __name__ == “__main__”:
TicTacToeApp().run()
Youcanalreadyrunthis,yourappwillstartupand
you’llgetaplainblackwindow.Exciting!
We can build our own GUI out of Kivy widgets.
Each is a simple graphics element with some
specific behaviour of its own ranging from
standard GUI functionality (eg the Button, Label
or TextInput), to those that impose positioning on
their child widgets (eg the BoxLayout, FloatLayout
or GridLayout), to those abstracting a more
involved task like interacting with hardware (eg
the FileChooser, Camera or VideoPlayer). Most
importantly, Kivy’s widgets are designed to be
easily combined - rather than including a widget
foreveryneedimaginable,widgetsarekeptsimple
but are easy to join to invent new interfaces. We’ll
seesomeofthatinthistutorial.
Since ‘Hello World!’ is basically compulsory in
any programming tutorial, let’s get it over with by
usingasimple‘Label’widgettodisplaythetext:
from kivy.uix.label import Label
We’ll display the ‘Label’ by returning it as our app’s
rootwidget.Everyapphasasinglerootwidget,the
top level of its widget tree, and it will automatically
be sized to fill the window. We’ll see later how to
constructafullGUIbyaddingmorewidgetsforthis
one, but for now it’s enough to set the root widget
byaddinganewmethodtothe‘App’:
def build(self):
return Label(text=’Hello World!’
,
font_size=100,
color=0, 1, 0, 1)) # (r
, g, b, a)
The ‘build’ method is called when the ‘App’ is run,
and whatever widget is returned automatically
becomes the root widget of that App’. In our case
that’s a Label, and we’ve set several properties -
the ‘text’, ‘font_size’ and ‘color’. All widgets have
different properties controlling aspects of their
behaviour, which can be dynamically updated to
alter their appearance later, though here we set
themjustonceuponinstantiation.
Note that these properties are not just Python
attributes but instead Kivy properties. These are
accessed like normal attributes but provide extra
functionality by hooking into Kivy’s event system.
We’ll see examples of creating properties shortly,
andyoushoulddothesameifyouwanttouseyour
variableswith Kivy’seventorbindingfunctionality.
That’s all you need to show some simple text,
so run the program again to check that this does
work.Youcanexperimentwiththeparametersifit’s
unclearwhatanyofthemaredoing.
Ourownwidget:tic-tac-toe
Since Kivy doesn’t have a tic-tac-toe widget, we’ll
have to make our own! It’s natural to create a new
widgetclasstocontainthisbehaviour:
from kivy.uix.gridlayout import GridLayout
class TicTacToeGrid(GridLayout):
pass
Now this obviously doesn’t do anything yet,
except that it inherits all the behaviour of the
Kivy GridLayout widget - that is, we’ll need to
tell it how many columns to have, but then it will
Youcandevelop
cross-platform
appsusingvarious
Pythonlibraries
The Python Book 109
Create with Python

QAtic-tac-toegridnowacceptinginput,addingaOorXalternately
the former, creating a rule for the ‘TicTacToeGrid’
widget by declaring that every ‘TicTacToeGrid’
instantiatedshouldhaveits‘cols’propertysetto3.
We’ll use some more kv language features later,
but for now let’s go back to Python to create the
buttonsthatwillbetheentriesinourtic-tac-toegrid.
from kivy.uix.button import Button
from kivy.properties import ListProperty
class GridEntry(Button):
coords = ListProperty([0, 0])
This inherits from Kivy’s ‘Button’ widget, which
interacts with mouse or touch input, dispatching
events when interactions toggle it. We can hook
into these events to call our own functions when a
user presses the button, and can set the button’s
‘text’ property to display the ‘X’ or ‘O’. We also
createdanewKivypropertyforourwidget,‘coords’
– we’ll show how this is useful later on. It’s almost
identical to making a normal Python attribute by
writing‘self.coords=[0,0]’in‘GridEntry.__init__’.
As with the ‘TicTacToeGrid’, we’ll style our new
class with kv language, but this time we get to see
amoreinterestingfeature.
GridEntry:
font_size: self
.height
As before, this syntax defines a rule for how a
‘GridEntry’ widget should be constructed, this
time setting the ‘font_size’ property that controls
the size of the text in the button’s label. The extra
magic is that kv language automatically detects
thatwe’vereferencedtheButton’sownheightand
will create a binding to update this relationship
– when a ‘GridEntry’ widget’s height changes, its
‘font_size’ will change so the text fits perfectly.
We could have made these bindings straight
from Python (another usage of the ‘bind’ method
used later on), but that’s rarely as convenient as
referencingthepropertywewanttobindto.
Let’s now populate our ‘TicTacToeGrid’ with
‘GridEntry’ widgets (Fig.01). This introduces a
few new concepts: When we instantiated our
‘GridEntry’ widgets, we were able to set their
‘coords’ property by simply passing it in as a
kwarg.Thisisaminorfeaturethatisautomatically
handledbyKivyproperties.
We used the ‘bind’ method to call the
grid’s ‘button_pressed’ method whenever
the `GridEntry` widget dispatches an
‘on_release’ event. This is automatically
handled by its ‘Button’ superclass, and
will occur whenever a user presses, then
releases a ‘GridEntry’ button. We could also
bind to ‘on_press’, which is dispatched when the
button is first clicked, or to any Kivy property of
the button, which is dispatched dynamically
wheneverthepropertyismodified.
We added each ‘GridEntry’ widget to our ‘Grid’
via the ‘add_widget’ method. That means each
one is a child widget of the ‘TicTacToeGrid’, and
so it will display them and knows it should
automatically arrange them into a grid with the
numberofcolumnswesetearlier.
Nowallwehavetodoisreplaceourrootwidget
(returned from ‘App.build’) with a ‘TicTacToeGrid’
andwecanseewhatourapplookslike.
automatically arrange any child widgets to fit
nicelywithasmanyrowsasnecessary.Tic-tac-toe
requiresthreecolumnsandninechildren.
Here we introduce the Kivy language (kv), a
special domain-specific language for making
rules describing Kivy widget trees. It’s very simple
but removes a lot of necessary boilerplate for
manipulatingtheGUIwithPythoncode-asaloose
analogy you might think of it as the HTML/CSS to
Python’s JavaScript. Python gives us the dynamic
powertodoanything,butallthatpowergetsinthe
way if we just want to declare the basic structure
of our GUI. Note that you never need kv language,
youcanalwaysdothesamethinginPythonalone,
but the rest of the example may show why Kivy
programmersusuallyliketousekv.
Kivy comes with all the tools needed to use kv
language;thesimplestwayistowriteitinafilewith
anamebasedonourAppclass.Thatis,weshould
placethefollowinginafilenamed‘tictactoe.kv’:
TicTacToeGrid:
cols: 3 # Number of columns
This is the basic syntax of kv language; for each
widget type we may write a rule defining its
behaviour, including setting its properties and
adding child widgets. This example demonstrates
Kivycomes
withallthetools
neededtousekv
language
110 The Python Book
Create with Python
def build(self):
return TicTacToeGrid()
# Replaces the previous label
With this complete you can run your main Python
file again and enjoy your new program. All being
well, the single Label is replaced by a grid of
nine buttons, each of which you can click (it will
automatically change colour) and release (you’ll
see the printed output information from our
binding). We could customise the appearance by
modifying other properties of the Button, but for
nowwe’llleavethemastheyare.
Hasanyonewonyet?
We’llwanttokeeptrackofthestateoftheboardto
check if anyone has won, which we can do with a
couplemoreKivyproperties:
from kivy.properties import
(ListProperty, NumericProperty)
class TicTacToeGrid(GridLayout):
status = ListProperty([0, 0, 0,
0, 0, 0,
0, 0, 0])
current_player = NumericProperty(1)
This adds an internal status list representing who
has played where, and a number to represent the
current player (1 for ‘O’, -1 for ‘X’). By placing these
numbers in our status list, we’ll know if somebody
winsbecausethesumofarow,columnordiagonal
will be +-3. Now we can update our graphical grid
whenamoveisplayed(Fig.02).
You can run your app again to see exactly what
this did, and you’ll find that clicking each button
now places an ‘O’ or ‘X’ as well as a coloured
background depending on whose turn it is to
play. Not only that, but you can only play one
move in each button thanks to our status
arraykeepingtrackofexistingmoves.
This is enough to play the game but there’s one
vital element missing... a big pop-up telling you
when you’ve won! Before we can do that, we need
toaddsomecodetocheckifthegameisover.
Kivy properties have another useful feature
here, whenever they change they automatically
call an ‘on_propertyname’ method if it exists
and dispatch a corresponding event in Kivy’s
event system. That makes it very easy to write
code that will run when a property changes,
both in Python and kv language. In our case
we can use it to check the status list every time
it is updated, doing something special if a player
hasfilledacolumn,rowordiagonal.
def on_status(self
, instance, new_value):
status = new_value
# Sum each row, column and diagonal.
# Could be shorter
, but let’s be extra
# clear what’s going on
sums = [sum(status[0:3]), # rows
sum(status[3:6]),
sum(status[6:9]),
sum(status[0:
:3]), # columns
sum(status[1:
:3]),
sum(status[2:
:3]),
sum(status[
:
:4]), # diagonals
sum(status[2:-2:2])]
# Sums can only be +-3 if one player
# filled the whole line
if 3 in sums:
print(‘Os win!’)
elif -3 in sums:
print(‘Xs win!’)
elif 0 not in self
.status: # Grid full
print(‘Draw!’)
This covers the basic detection of a won or drawn
board, but itonlyprintsthe result tostdout. At this
stage we probably want to reset the board so that
the players can try again, along with displaying a
graphicalindicatoroftheresult(Fig.03).
Finally, we can modify the `on_status` method
to both reset the board and display the winner
ina‘ModalView’widget.
from kivy.uix.modalview import ModalView
This is a pop-up widget that draws itself on top of
everything else rather than as part of the normal
widget tree. It also automatically closes when the
userclicksortapsoutsideit.
winner = None
if -3 in sums:
winner = ‘Xs win!’
elif 3 in sums:
winner = ‘Os win!’
elif 0 not in self
.status:
winner = ‘Draw.
.
.nobody wins!’
if winner:
popup = ModalView(size_hint=0.75, 0.5))
victory_label = Label(text=winner
,

QThegamewithfinaladditions,makingthegridsquareandextendingtheinterface
The Python Book 111
Create with Python
Tryswappingoutthedifferentwidget
typestoseehowotherwidgetsbehave
font_size=50)
popup.add_widget(victory_label)
popup.bind(on_dismiss=self
.reset)
popup.open()
This mostly uses the same ideas we already
covered, adding the ‘Label’ widget to the
‘ModalView’ then letting the ‘ModalView’ take
care of drawing itself and its children on top of
everything else. We also use another binding; this
time to ‘on_dismiss’, which is an event dispatched
by the ‘ModalView’ when it is closed. Finally, we
made use of the ‘size_hint’ property common
to all widgets, which in this case is used to set
the ‘ModalView’ size proportional to the window
– while a ‘ModalView’ is open you can resize
the window to see it dynamically resize, always
maintainingtheseproportions.Thisisanothertrick
madepossiblebyabindingwiththe‘size_hint’Kivy
property,thistimemanagedinternallybyKivy.
That’s it, a finished program! We can now
not only play tic-tac-toe, but our program
automatically tells us when somebody has won,
and resets the board so we can play again. Simply
runyourprogramandenjoyhoursoffun!
Timetoexperiment
This has been a quick tour through some of Kivy’s
features, but hopefully it demonstrates how
to think about building a Kivy application. Our
programs are built from individual Kivy widgets,
interacting by having Python code run when their
properties change (eg our ‘on_status’ method)
or when they dispatch events (eg ‘Button’ ‘on_
release’). We also briefly saw kv language and
experienced how it can automatically create
bindingsbetweenproperties.
You can find a copy of the full program on
FileSilo, which you can reference to check you’ve
followed everything correctly. We’ve also added
an extra widget, the ‘Interface’, with a structure
coded entirely in kv language that demonstrates
how to add child widgets this way. You can test
it by uncommenting the ‘return Interface()’ line
in ‘TicTacToeGrid.build’. It doesn’t do anything
fundamentally different to what we already
covered, but it does make extensive use of kv
language’s binding ability to automatically update
a label showing the current player, and to resize
the TicTacToeGrid so that it is always square to
fit within its parent. You can play with all these
settings to see exactly how it fits together, or try
thingslikeswappingoutthedifferentwidgettypes
toseehowotherwidgetsbehave.
class TicTacToeGrid(GridLayout):
def __init__(self
, *args, **kwargs):
super(TicTacToeGrid, self).__init__(*args, **kwargs)
for row in range(3):
for column in range(3):
grid_entry = GridEntry(
coords=(row, column))
grid_entry.bind(on_release=self
.button_pressed)
self
.add_widget(grid_entry)
def button_pressed(self
, instance):
# Print output just to see what’s going on
print(‘{} button clicked!’
.format(instance.coords))
Fig 01
# Note the *args parameter! It’s important later when we make a binding
# to reset, which automatically passes an argument that we don’t care about
def reset(self
, *args):
self
.status = [0 for _ in range(9)]
# self
.children is a list containing all child widgets
for child in self
.children:
child.text = ‘’
child.background_color = (1, 1, 1, 1)
self
.current_player = 1
Fig 03
def button_pressed(self
, button):
# Create player symbol and colour lookups
player = {1: ‘O’
, -1: ‘X’}
colours = {1: (1, 0, 0, 1), -1: (0, 1, 0, 1)} # (r
, g, b, a)
row, column = button.coords # The pressed button is automatically
# passed as an argument
# Convert 2D grid coordinates to 1D status index
status_index = 3*row + column
already_played = self
.status[status_index]
# If nobody has played here yet, make a new move
if not already_played:
self
.status[status_index] = self
.current_player
button.text = {1: ‘O’
, -1: ‘X’}[self
.current_player]
button.background_color = colours[self
.current_player]
self
.current_player *= -1 # Switch current player
Fig 02
Codeon
FileSilo
112 The Python Book
Create with Python
02Addcredit
Just like a mobile phone operator,
Twilio is not a free service – although it is very
inexpensive. In order to continue, we’ll need
to add a card and some funds to our newly
created Twilio account. On the main page of the
dashboard, you’ll see a big blue dialog asking
to upgrade your trial account; click through
and follow the instructions to add a card and
the amount of credit you would like to use. The
minimum amount of $20 (around £10 GBP) will
be more than plenty for this and other projects.
Once that’s done, you’re almost ready to start
sending text messages – but first head back
over to the Twilio dashboard and copy your
account SID and auth token down somewhere,
you’llneedthosealittlelater.
Increasesecurityinaccessto your webservices
by buildinga simpletwo-stepauthentication
withTwilio’sSMSAPIsto helpyou
Resources
Python2.7+
Flask0.10.0:
flask.pocoo.org/
FlaskGithub:
github.com/mitsuhiko/flask
ATwilioaccount:
twilio.com
Twilio’sPythonREST
APIHelperLibrary:
github.com/twilio/twilio-python/zipball/master
MySQLDB:
mysql-python.sourceforge.net
Createatwo-step
authenticationwithTwilio
01 Get a Twilio account and
phone number
Signing up to Twilio is pretty easy. First, head
over to http://twilio.com and click the ‘Signup’
button. At this point, the sign-up process
doesn’t really differ from any other service,
but after you’ve entered an email address and
password you’ll be asked for a phone number.
Given the nature of Twilio’s API, it makes sense
for them to ask whether we’re human, and
having them text us is a good way to confirm
that. Hey, it’s a two-step authentication, which
is exactly what we’re working towards.
You can enter any number you have access
to, be it a landline or mobile, to confirm who
you are, but at this point we suggest you
authenticate using a phone that can accept
SMS (instead of a landline). Having entered your
number, you’ll receive a text to authenticate
your phone – enter it and you’ll be presented
with a Twilio phone number. This is your Twilio
phone number and you’ll be using it to send and
receive our authentication texts.
Telephony is one of the most versatile
technologies in our households. Despite being
invented over 100 years ago, we still use the
same basic infrastructure that once only carried
the voices of people to deliver a rich multitude of
media content at incredible speeds. As is often
the case with wonderful things, they can often be
complextoo–andyetphonesaremoreimportant
now to our daily lives than ever. So, what can we
dotoleveragesomeofthatversatiletechnology?
Well, for starters we can use an API. Twilio
has created a RESTful API that removes a great
deal of that complexity of telephony so that we
can write apps and services that are able to
deliver and receive both phone calls and SMS
using various endpoints and services. Neat! In
this tutorial, we’re going to look at using Twilio
to help us create the basic flow for a two-step
authentication system for logging into a service.
We’re also going to be using Flask to help us
create our routes and generate our pages, but
littleofFlask’sdetailwillbecoveredhere.

QTheTwiliointerfaceiskeptniceandsimple–nounnecessarycomplicationshere
The Python Book 113
Create with Python
import MySQLdb
from flask import Flask, redirect, request, session, render_template
from twilio.rest import TwilioRestClient as twilio
import string, random, time
db = MySQLdb.connect(host=127.0.0.1, user=SQLUSER,
passwd=SQLPASS, db=two-step, port=3306)
expirationLength = 300
account_sid = YOUR ACCOUNT SID
auth_token = YOUR ACCOUNT AUTH TOKEN
client = twilio(account_sid, auth_token)
@app.route('/')
def index():
return index page
@app.route('/login', methods=['GET'])
def login():
return login page
@app.route('/check-user', methods=['POST'])
def checkUser():
return check user page
@app.route('/logout')
def logout():
return logout page
@app.route('/verify', methods=['GET'])
def twoStep():
return verify page
@app.route('/check-code', methods=['POST'])
def checkCode():
return check code page
if __name__ == '__main__':
app.secret_key = 'R4nDOMCRypt0gr4ph1cK3yf0R5355i0N'
app.run(host='0.0.0.0', debug=True)
Fig 01
03InstalltheTwilioHelperLibrary
andMySQLDB
The Twilio helper library is a fantastic piece of
codethatletsyoujumpstraightintosendingand
handling text messages in no time at all. There
areacoupleofwaystoinstallthelibrary:youcan
useeitherPIPorEasy_Install,likeso
$ pip install twilio
$ easy_install twilio
Or you can download the source code for the
helper library and run the ‘setup.py’ file. It
really is as simple as that. Now, for storing the
verification tokens we’re going to use a MySQL
database. To get Python talking to our SQL
server, we’ll use the Python module MySQLDB,
thepackageforwhichyoucangrablikeso…
apt-get install python-mysqldb
In the tutorial resources we have included an
SQL dump with the table structure. Import it
into a database of your choosing. Assuming
everything so far has gone swimmingly, you can
create a new project folder/environment and
addanewfile‘server.py’.
04Serversetup
Open the ‘server.py’ file for editing. The
firstthingwe'regoingtodoisimportthelibraries
we need for our authentication flow, create the
endpoints for our server and assign some of the
variablesneededtorunourFlaskserver.(Fig 01)
You may have noticed the account_sid and
auth_token variable we’ve set after the import
statements. We’ll use these with our Twilio
client so we can interact with Twilio and our
mobile phones. These settings can be found
on the Twilio account dashboard, right below
the header. We’ve also connected to our SQL
database, so make sure your SQL server is
running before you fire up the app, otherwise
you’ll have an error thrown. Save, now if you run
your ‘server.py’ file, you should be able to access
theindexpageofyourserverat127.0.0.1:5000/.
05Serverlogic
If you’ve hit all of your server endpoints
already, so far all you will see are the strings we
returned at the end of endpoint declarations.
These are not all that good-looking, so let’s
add some Flask templates to pretty things
up a little. The focus of this tutorial is not on
the intricacies of Flask and as such, included
on the DVD is a folder called ‘templates’ and
another called ‘static’; copy them both to the
root of your current project folder and amend
your endpoints as in Fig 02. If you revisit the
pages again, things might seem a little out of
whack at the moment, but don’t worry about
that for the time being. It’s mostly because
we’ve not put together the server logic to help
the templates figure out what to do.
Let’s deal with the ‘/’ path first. All we’re
doing here is checking the state of our session
cookies and effecting how the index.html
page renders according to that state. If the
user isn’t logged in, we’ll give them a link to the
login page, if the user is logged in but hasn’t
verified, then we’ll give them a link to the
code verification page. Before we deliver the
template we need to check that our session has
its particular variables set, otherwise we’ll end
up getting KeyErrors.
@app.route(‘/’)
def index():
checkSessionState()
return render_template(‘index.
html’)
def checkSessionState():
114 The Python Book
Create with Python
try:
session[‘verified’] == True
except KeyError:
session[‘verified’] = ’’
try:
session[‘loggedin’] == True
except KeyError:
session[‘loggedin’] = ’’
try:
session[‘user’] == True
except KeyError:
session[‘user’] = ’’
result = cur.fetchone()
returnedPassword = result[2]
returnedPhoneNumber = result[3]
We can then build an SQL statement using cur.
execute().Noticethe%s;thiswillbereplacedwith
the value passed through in the next variable. We
execute the statement with cur.fetchone(), which
will get us the first row that the query returns – if
there is no result we’ll get None and we can then
return the user to the login page with an error
message. Let’s assume we’ve requested a valid
user–we’llnextcheckthatthepasswordassigned
tothatuseristhesameastheonesubmitted.Ifso,
we’ll generate the validation code to send to the
user, which we’ll store in the verification table of
ourdatabaseuntilit’susedorexpires.We’llneedto
createanewcursortoinserttheverificationcode
intoourtable.Afterwe’veexecutedthestatement
we need to commit the changes to the database,
wedothiswithdb.commit() –we’llthenaddthe
resultsofthequerytooursessionsowecancheck
againstthemlater.(Fig03)
that will make up the body of our message,
the number that we want to send it to and the
number that we want to send it from. When
inputting the number that we want to send it
to, it’s best to use the +CountryCode type of
phone number to avoid any ambiguity about
the intended recipient. The number that we’re
sending from is our Twilio number; you can
use any Twilio number you have assigned to
your account, so long as it has credit. As soon
as we execute that code, the message will be
sent and your SMS will go straight through to
the telephone. The SID is the unique identifier
for the message/call sent; receiving it means
the message has been executed successfully.
After that, we can redirect our user to the
verification page with return redirect(‘/
verify’) at the end of /check-user.
06Loggingin
The first step in two-step authentication
is logging in with a traditional username/email
and password. Access your database and create
anewuserwiththefollowingquery:
INSERT INTO users (username, password,
phonenumber) VALUES (‘A USERNAME', ‘A
PASSWORD', ‘+44YOURUSERSPHONENUMBER')
For the purposes of this tutorial, the password
is plain text – but we implore you, when you’re
implementing passwords in a live environment,
makesurethatyouhashthem.Still,fornowwe’re
goingtouseaplaintextpassword.Ourlogin.html
template has a form that’s going to POST itself
to check-user; here we'll check the validity of
the credentials and then trigger the verification
if needed. So we’re going to use the MySQLDB
moduletogetdetailsfromourdatabase.
In order to query our database we need to
create a cursor from which to execute our MySQL
statements.Wedothiswithcur = db.cursor():
@app.route(‘/check-user',
methods=[‘POST'])
def checkUser():
#session.clear()
if request.method == ‘POST':
#print request.form['username']
cur = db.cursor()
cur.execute(SELECT * FROM
users WHERE username = %s,
(request.form[‘username'],))
07Sendtheverificationcode
Now that we believe we’re dealing with
a valid user, it’s time for the second step of our
two-step process. On the line after where we
stored a variable in our session, we make a call
to sendVerificationCode (VERIFICATION
CODE, USER PHONE NUMBER) and pass
through the code we want to send to our user
and the user’s phone number. So what does
that function actually look like? It must be
big, long and complicated because it deals
with the telecommunications network, right?
Wrong. It’s actually incredibly simple to send
an SMS with Twilio. In fact, part of the inherent
beauty of Twilio lies in its simplicity. To send a
text, all we have to do is:
def sendVerificationCode(code,
number):
text = client.messages.create(
body=“Your verification code
is: + code,
to=number,
from_=“+YOURTWILIONUMBER
)
return text.sid
Using the client variable we used to instantiate
the Twilio REST module, we can access the
messages class and execute the create
method. All we need to pass through is the text
08Checkverificationcode
At this point the user will have received
a text message with something along the lines
of ‘Your verification code is: 12cd56’ and will
be presented with the verification page. If, at
this point, they choose to browse around our
site, they won’t be able to access anything that
we don't want them to. Still, we’ll know that
they’ve logged in, so if they head back to the
verification page, we can just let them input
their code. Once they submit their code, it will
be sent to the /check-code endpoint.
Just like before when we checked for our
user’s validity, we’re going to attempt to retrieve
the verification code and check it. (Fig 04)
First we’re simply going to retrieve the code
and check the user it has assigned to it. If that
user assigned to the code matches the user
in our session, then we can be certain that
the right person is logging in with the right
code – if not we can redirect them accordingly.
Assuming the code is correct, we need to
check it’s still valid. Back in Step 6, we created
an expiration time that was five minutes in the
future from when the code was generated. If it’s
been more than five minutes (or whatever time
you’ve set on it) then we’re going to consider it
invalid, delete the code from our table and then
log out our user so they can start over, like so.
elif time.time()  expirationTime:
expirySQL = db.cursor()
expirySQL.execute(DELETE FROM
verification WHERE code=%s,
(codeToCheck,))
The Python Book 115
Create with Python
expirySQL.close()
session['loggedin'] == False
return redirect('/logout')
If we manage to pass the tests so far, then
we’ve two-step verified our user – hooray!
Surprisingly easy, eh? Before we give our user
free reign around our service, we still want
to get rid of that token – we don’t need it any
more and we don’t want to risk someone else
using it maliciously in the future.
else:
delSql = db.cursor()
delSql.execute(DELETE FROM
verification WHERE code=%s,
(codeToCheck,))
delSql.close()
db.commit()
session['verified'] = True
return redirect('/')
else:
return redirect('/
verify?error=true')
And that’s it! Now we redirect our user to
wherever we want them to be at the end of the
process. In this instance we’re sending them
back to our index page, which will render a
success message and give the user a link to
log out whenever they like – but they could be
redirectedtotheiruserpage,andsoon.
09Conclusion
In every web-based service, security
is king. Users entrust more and more personal
data and trends to services every day and it’s
the responsibility of those services to maintain
the privacy of that data as best they can. It’s no
wonder that services such as Amazon, Google
and Facebook have all implemented two-
step verification across their services. With
two-step authentication, a user can tie their
account to one of the most personal things they
own: their phone. With services like Twilio and
some simple code, they contain people’s keys –
or at least a part of them.
@app.route('/')
def index():
return render_template('index.html')
@app.route('/login', methods=['GET'])
def login():
return render_template('login.html')
@app.route('/check-user', methods=['POST'])
def checkUser():
return check user page
@app.route('/logout')
def logout():
return logout page
@app.route('/verify', methods=['GET'])
def twoStep():
return render_template('verify.html')
@app.route('/check-code', methods=['POST'])
def checkCode():
return check code page
Fig 02
verficationCode = generateVerificationCode(size=6)
ins = db.cursor()
expiration = int(time.time() + expirationLength)
sql = INSERT INTO verification (code, expiration, username) VALUES ('%s',
'%s', '%s') % (verficationCode, expiration, request.form['username'])
ins.execute(sql)
ins.close()
db.commit()
session['user'] = request.form['username']
session['loggedin'] = True
Fig 03
@app.route('/check-code', methods=['POST'])
def checkCode():
if request.method == 'POST':
codeToCheck = request.form['code']
if not 'user' in session:
return redirect('/login')
else:
cur = db.cursor()
cur.execute(SELECT * FROM verification WHERE code = %s, (codeToCheck,))
result = cur.fetchone()
cur.close()
if result != None:
returnedUser = result[3]
expirationTime = int(result[2])
if returnedUser != session['user']:
return redirect('/verify?error=true')
Fig 04
116 The Python Book
Create with Python
WriteyourownRasPishooterin300linesofPython
Partone:Programa
SpaceInvadersclone
meander their way down the screen
towards you, it’s your job to pick them
off while dodging their random fire.
When one wave is conquered, another
faster, more aggressive wave appears.
We’ve tried to use many features of
Pygame, which is designed to make
the creation of games and interactive
applications easier. We’ve extensively
used the Sprite class, which saves
dozens of lines of extra code in making
collision detection simple and updating
the screen and its many actors a
single-line command.
We hope you agree that this is an
exciting game to play and a great
tool to learn more about Python and
Pygame, but our sensory system is far
from overloaded here. Don’t worry, as
that will be covered in the next tutorial,
adding animation and sound effects to
our game to give it the spit and polish
any self-respecting Space Invaders-
inspired shooter demands…
01Settingupdependencies
If you’re looking to get a better understanding of
programming games with Python and Pygame, we strongly
recommend you copy the Pivaders code in this tutorial into your
ownprogram.It’sgreatpracticeandgivesyouachancetotweak
elements of the game to suit you, be it a different ship image,
changingthedifficultyorthewaysthealienwavesbehave.Ifyou
just want to play the game, that’s easily achieved too, though.
Either way, the game’s only dependency is Pygame, which (if it
isn’talready)canbeinstalledfromtheterminalbytyping:
sudo apt-get install python-pygame
02Downloadingtheproject
For Pivaders we’ve used Git, a brilliant form of version
control used to safely store the game files and retain historical
versionsofyourcode.GitshouldalreadybeinstalledonyourPi;if
not,youcanacquireitbytyping:
sudo apt-get install git
As well as acting as caretaker for your code, Git enables you
to clone copies of other people’s projects so you can work on
them, or just use them. To clone Pivaders, go to your home
folder in the terminal (cd ~), make a directory for the project
(mkdir pivaders),enterthedirectory(cd pivaders)andtype:
git pull https://github.com/russb78/pivaders.git
When you’re learning to program in
a new language or trying to master
a new module, experimenting with a
familiar and relatively simply project
is a very useful exercise to help
expand your understanding of the
tools you’re using. Our Space Invaders
clone is one such example that lends
itself perfectly to Python and the
Pygame module – it’s a simple game
with almost universally understood
rules and logic. While the Invaders
Resources
Raspbian:www.raspberrypi.org/
downloads
Python:www.python.org/doc
Pygame:www.pygame.org/docs
The Python Book 117
Create with Python
#!/usr/bin/env python2
import pygame, random
BLACK = (0, 0, 0)
BLUE = (0, 0, 255)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
ALIEN_SIZE = (30, 40)
ALIEN_SPACER = 20
BARRIER_ROW = 10
BARRIER_COLUMN = 4
BULLET_SIZE = (5, 10)
MISSILE_SIZE = (5, 5)
BLOCK_SIZE = (10, 10)
RES = (800, 600)
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.size = (60, 55)
self.rect = self.image.get_rect()
self.rect.x = (RES[0] / 2) - (self.size[0] / 2)
self.rect.y = 520
self.travel = 7
self.speed = 350
self.time = pygame.time.get_ticks()
def update(self):
self.rect.x += GameState.vector * self.travel
if self.rect.x  0:
self.rect.x = 0
elif self.rect.x  RES[0] - self.size[0]:
self.rect.x = RES[0] - self.size[0]
class Alien(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.size = (ALIEN_SIZE)
self.rect = self.image.get_rect()
self.has_moved = [0, 0]
self.vector = [1, 1]
self.travel = [(ALIEN_SIZE[0] - 7), ALIEN_SPACER]
self.speed = 700
self.time = pygame.time.get_ticks()
def update(self):
if GameState.alien_time - self.time  self.speed:
if self.has_moved[0]  12:
self.rect.x += self.vector[0] * self.travel[0]
self.has_moved[0] +=1
else:
if not self.has_moved[1]:
self.rect.y += self.vector[1] * self.travel[1]
self.vector[0] *= -1
self.has_moved = [0, 0]
self.speed -= 20
if self.speed = 100:
self.speed = 100
self.time = GameState.alien_time
class Ammo(pygame.sprite.Sprite):
def __init__(self, color, (width, height)):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([width, height])
self.image.fill(color)
self.rect = self.image.get_rect()
self.speed = 0
self.vector = 0
def update(self):
self.rect.y += self.vector * self.speed
if self.rect.y  0 or self.rect.y  RES[1]:
self.kill()
class Block(pygame.sprite.Sprite):
def __init__(self, color, (width, height)):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([width, height])
self.image.fill(color)
self.rect = self.image.get_rect()
class GameState:
pass
class Game(object):
def __init__(self):
pygame.init()
pygame.font.init()
self.clock = pygame.time.Clock()
self.game_font = pygame.font.Font(
‘data/Orbitracer.ttf’, 28)
self.intro_font = pygame.font.Font(
‘data/Orbitracer.ttf’, 72)
self.screen = pygame.display.set_mode([RES[0], RES[1]])
self.time = pygame.time.get_ticks()
self.refresh_rate = 20
self.rounds_won = 0
self.level_up = 50
self.score = 0
self.lives = 2
self.player_group = pygame.sprite.Group()
self.alien_group = pygame.sprite.Group()
self.bullet_group = pygame.sprite.Group()
self.missile_group = pygame.sprite.Group()
self.barrier_group = pygame.sprite.Group()
self.all_sprite_list = pygame.sprite.Group()
self.intro_screen = pygame.image.load(
‘data/start_screen.jpg’).convert()
self.background = pygame.image.load(
‘data/Space-Background.jpg’).convert()
pygame.display.set_caption(‘Pivaders - ESC to exit’)
pygame.mouse.set_visible(False)
Player.image = pygame.image.load(
‘data/ship.png’).convert()
Player.image.set_colorkey(BLACK)
Alien.image = pygame.image.load(
‘data/Spaceship16.png’).convert()
Alien.image.set_colorkey(WHITE)
GameState.end_game = False
GameState.start_screen = True
GameState.vector = 0
GameState.shoot_bullet = False
def control(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
GameState.start_screen = False
GameState.end_game = True
if event.type == pygame.KEYDOWN 
and event.key == pygame.K_ESCAPE:
if GameState.start_screen:
GameState.start_screen = False
GameState.end_game = True
self.kill_all()
else:
GameState.start_screen = True
self.keys = pygame.key.get_pressed()
if self.keys[pygame.K_LEFT]:
GameState.vector = -1
elif self.keys[pygame.K_RIGHT]:
GameState.vector = 1
else:
GameState.vector = 0
if self.keys[pygame.K_SPACE]:
if GameState.start_screen:
GameState.start_screen = False
self.lives = 2
self.score = 0
self.make_player()
self.make_defenses()
self.alien_wave(0)
else:
GameState.shoot_bullet = True
def splash_screen(self):
while GameState.start_screen:
self.kill_all()
self.screen.blit(self.intro_screen, [0, 0])
self.screen.blit(self.intro_font.render(
“PIVADERS”, 1, WHITE), (265, 120))
self.screen.blit(self.game_font.render(
“PRESS SPACE TO PLAY”, 1, WHITE), (274, 191))
GroupsThis
longlistofgroups
we’recreatingare
essentiallysets.Each
timewecreateone
oftheseitems,it’s
addedtothesetso
itcanbetestedfor
collisionsanddrawn
withease.
Cleanclode
Havingallthemost
regularlyused
globalvariables
clearlylabelled
heremakesour
codelateroneasier
toread.Also,ifwe
wanttochangethe
sizeofsomething,
weonlyneedtodo
ithereanditwill
workeverywhere.
ControlTaking
careofkeyboard
inputisthecontrol
method.Itchecks
forkeyeventsand
actsaccordingly
dependingwhether
we’reonthestart
screenorplaying
thegame.
Rainbullets
TheAmmoclassis
shortandsweet.
Weonlyneedafew
initialisingattributes
andtheupdate
methodchecksifit’s
stillonthescreen.If
not,it’sdestroyed.
Get
thecode:
bit.ly/
11k5f2x
118 The Python Book
Create with Python
04Creatingyourownclone
Once you’ve racked up a good
high score (anything over 2,000 points is
respectable) and got to know our simple
implementation, you’ll get more from
following along with and exploring the
code and our brief explanations of what’s
going on. For those who want to make
their own project, create a new project
folder and use either IDLE or Leafpad (or
perhaps install Geany) to create and save
a.pyfileofyourown.
05Globalvariablestuples
Once we’ve imported the
modules we need for the project,
there’s quite a long list of variables in
block capitals. The capitals denote that
these variables are constants (or global
variables). These are important numbers
thatneverchange–theyrepresentthings
referred to regularly in the code, like
colours, block sizes and resolution. You’ll
also notice that colours and sizes hold
multiple numbers in braces – these are
tuples. You could use square brackets (to
make them lists), but we use tuples here
since they’re immutable, which means
06Classes–part1
A class is essentially a blueprint
for an object you’d like to make. In the
case of our player, it contains all the
required info, from which you can make
multiple copies (we create a player
instance in the make_player() method
halfway through the project). The great
thing about the classes in Pivaders is
that they inherit lots of capabilities and
shortcuts from Pygame’s Sprite class,
as denoted by the pygame.sprite.Sprite
found within the braces of the first line
of the class. You can read the docs to
learn more about the Sprite class via
www.pygame.org/docs/ref/sprite.html.
07Classes–part2
In Pivader’s classes, besides
creating the required attributes – these
are simply variables in classes – for the
object (be it a player, an alien, some
ammo or a block), you’ll also notice all
the classes have an update() method
apart from the Block class (a method is
a function within a class). The update()
method is called in every loop through
the main game (we’ve called ours
main_loop()) and simply asks the
iteration of the class we’ve created to
move. In the case of a bullet from the
Ammo class, we’re asking it to move
down the screen. If it goes off either the
top or bottom of the screen, we destroy
it (since we don’t need it any more).
08Ammo
What’s most interesting about
classes, though, is that you can use one
class to create lots of different things.
09Thegame
Our final class is called Game. This is where all the main
functionality of the game itself comes in, but remember, so far
this is still just a list of ingredients – nothing can actually happen
until a ‘Game’ object is created (right at the bottom of the code).
The Game class is where the central mass of the game resides,
so we initialise Pygame, set the imagery for our protagonist
and extraterrestrial antagonist and create some GameState
attributes that we use to control key aspects of external classes,
like changing the player’s vector (direction) and deciding if we
needtoreturntothestartscreen,amongotherthings.
10Themainloop
There are a lot of methods (class functions) in the Game
class, and each is designed to control a particular aspect of
eithersettingupthegameorthegameplayitself.Theactuallogic
that dictates what happens within any one round of the game is
actuallycontainedinthemain_loop()methodrightatthebottom
of the pivaders.py script and is the key to unlocking exactly what
variables and functions you need for your game. Starting at
the top of main_loop() and working line-by-line down to its last
line, you can see exactly what’s being evaluated 20 times every
secondwhenyou’replayingthegame.
12Mainloopkeylogic–part2
Once collisions have been calculated, we need to see if
the game is still meant to continue. We do so with is_dead() and
defenses_breached() – if either of these methods returns true,
we know we need to return to the start screen. On the other
hand, we also need to check to see if we’ve killed all the aliens,
from within win_round(). Assuming we’re not dead, but the
aliens are, we know we can call the next_round() method, which
creates a fresh batch of aliens and increases their speed around
the screen. Finally, we refresh the screen so everything that’s
been moved, shot or killed can be updated or removed from the
screen. Remember, the main loop happens 20 times a second –
so the fact we don’t call for the screen to update right at the end
oftheloopisofnoconsequence.
11Mainloopkeylogic–part1
Firstly the game checks that the end_game attribute is
false – if it’s true, the entire loop in main_loop() is skipped and
we go straight to pygame.quit(), exiting the game. This flag is set
to true only if the player closes the game window or presses the
Esc key when on the start_screen. Assuming end_game and
start_screen are false, the main loop can start proper, with the
control()method,whichcheckstoseeifthelocationoftheplayer
needstochange.Nextweattempttomakeanenemymissileand
we use the random module to limit the number of missiles that
can be created. Next we call the update() method for each and
every actor on the screen using a simple for loop. This makes
sureeveryone’suptodateandmovedbeforewecheckcollisions
incalc_collisions().
03TestingPivaders
With Pygame installed and the
project cloned to your machine (you can
also find the .zip on FileSilo – simply
unpack it and copy it to your home
directory to use it), you can take it for a
quick test drive to make sure everything’s
set up properly. All you need to do is type
python pivaders.py from within the
pivaders directory in the terminal to get
started. You can start the game with the
space bar, shoot with the same button
and simply use the left and right arrows
on your keyboard to move your ship left
andright.
you can’t reassign individual items within
them. Perfect for constants, which aren’t
designedtochangeanyway.
You could, for example, have a pet class. From that class you
could create a cat (that meows) and a dog (that barks). They’re
different in many ways, but they’re both furry and have four
legs, so can be created from the same parent class. We’ve
done exactly that with our Ammo class, using it to create both
the player bullets and the alien missiles. They’re different
colours and they shoot in opposite directions, but they’re
fundamentally one and the same. This saves us creating extra
unnecessary code and ensures consistent behaviour between
objectswecreate.
Weusedwidelyavailableopensource
artandfontstomakethegame
The Python Book 119
Create with Python
DeadoraliveProbablytwoofthemost
importantquestionsareansweredhere–is
theplayerdeadordidyouwintheround?
StartthegameTheverylastthing
wedoiscreateaGameobjectandcallthe
mainloop.Besidesourconstants,thisis
theonlycodethatsitsoutsideaclass.
pygame.display.flip()
self.control()
def make_player(self):
self.player = Player()
self.player_group.add(self.player)
self.all_sprite_list.add(self.player)
def refresh_screen(self):
self.all_sprite_list.draw(self.screen)
self.refresh_scores()
pygame.display.flip()
self.screen.blit(self.background, [0, 0])
self.clock.tick(self.refresh_rate)
def refresh_scores(self):
self.screen.blit(self.game_font.render(
“SCORE “ + str(self.score), 1, WHITE), (10, 8))
self.screen.blit(self.game_font.render(
“LIVES “ + str(self.lives + 1), 1, RED), (355, 575))
def alien_wave(self, speed):
for column in range(BARRIER_COLUMN):
for row in range(BARRIER_ROW):
alien = Alien()
alien.rect.y = 65 + (column * (
ALIEN_SIZE[1] + ALIEN_SPACER))
alien.rect.x = ALIEN_SPACER + (
row * (ALIEN_SIZE[0] + ALIEN_SPACER))
self.alien_group.add(alien)
self.all_sprite_list.add(alien)
alien.speed -= speed
def make_bullet(self):
if GameState.game_time - self.player.time  self.player.speed:
bullet = Ammo(BLUE, BULLET_SIZE)
bullet.vector = -1
bullet.speed = 26
bullet.rect.x = self.player.rect.x + 28
bullet.rect.y = self.player.rect.y
self.bullet_group.add(bullet)
self.all_sprite_list.add(bullet)
self.player.time = GameState.game_time
GameState.shoot_bullet = False
def make_missile(self):
if len(self.alien_group):
shoot = random.random()
if shoot = 0.05:
shooter = random.choice([
alien for alien in self.alien_group])
missile = Ammo(RED, MISSILE_SIZE)
missile.vector = 1
missile.rect.x = shooter.rect.x + 15
missile.rect.y = shooter.rect.y + 40
missile.speed = 10
self.missile_group.add(missile)
self.all_sprite_list.add(missile)
def make_barrier(self, columns, rows, spacer):
for column in range(columns):
for row in range(rows):
barrier = Block(WHITE, (BLOCK_SIZE))
barrier.rect.x = 55 + (200 * spacer) + (row * 10)
barrier.rect.y = 450 + (column * 10)
self.barrier_group.add(barrier)
self.all_sprite_list.add(barrier)
def make_defenses(self):
for spacing, spacing in enumerate(xrange(4)):
self.make_barrier(3, 9, spacing)
def kill_all(self):
for items in [self.bullet_group, self.player_group,
self.alien_group, self.missile_group, self.barrier_group]:
for i in items:
i.kill()
def is_dead(self):
if self.lives  0:
self.screen.blit(self.game_font.render(
“The war is lost! You scored: “ + str(
self.score), 1, RED), (250, 15))
self.rounds_won = 0
self.refresh_screen()
pygame.time.delay(3000)
return True
def win_round(self):
if len(self.alien_group)  1:
self.rounds_won += 1
self.screen.blit(self.game_font.render(
“You won round “ + str(self.rounds_won) +
“ but the battle rages on”, 1, RED), (200, 15))
self.refresh_screen()
pygame.time.delay(3000)
return True
def defenses_breached(self):
for alien in self.alien_group:
if alien.rect.y  410:
self.screen.blit(self.game_font.render(
“The aliens have breached Earth defenses!”,
1, RED), (180, 15))
self.refresh_screen()
pygame.time.delay(3000)
return True
def calc_collisions(self):
pygame.sprite.groupcollide(
self.missile_group, self.barrier_group, True, True)
pygame.sprite.groupcollide(
self.bullet_group, self.barrier_group, True, True)
if pygame.sprite.groupcollide(
self.bullet_group, self.alien_group, True, True):
self.score += 10
if pygame.sprite.groupcollide(
self.player_group, self.missile_group, False, True):
self.lives -= 1
def next_round(self):
for actor in [self.missile_group,
self.barrier_group, self.bullet_group]:
for i in actor:
i.kill()
self.alien_wave(self.level_up)
self.make_defenses()
self.level_up += 50
def main_loop(self):
while not GameState.end_game:
while not GameState.start_screen:
GameState.game_time = pygame.time.get_ticks()
GameState.alien_time = pygame.time.get_ticks()
self.control()
self.make_missile()
for actor in [self.player_group, self.bullet_group,
self.alien_group, self.missile_group]:
for i in actor:
i.update()
if GameState.shoot_bullet:
self.make_bullet()
self.calc_collisions()
if self.is_dead() or self.defenses_breached():
GameState.start_screen = True
if self.win_round():
self.next_round()
self.refresh_screen()
self.splash_screen()
pygame.quit()
if __name__ == ‘__main__’:
pv = Game()
pv.main_loop()
Refreshing
thescreenYou
needtocarefully
considerthewayin
whichyouupdate
thescreen.Blitting
thebackground
betweenactor
movementsisvital
forcleananimation.
MainloopThis
isthebusinessend
ofourapplication.
Thisloopexecutes
20timesasecond.It
needstobelogical
andeasyforanother
codertounderstand.
Guns’n’ammo
Bulletsandmissiles
usethesameparent
class.Wechangea
fewkeyattributes
originallyinitialised
tocreatethe
behaviourweneed;
egthevectorforeach
isopposite.
Get
thecode:
bit.ly/
11k5f2x
Aclassisessentiallyablueprint
120 The Python Book
Create with Python
AfterwritingaSpaceInvaderscloneinjust300linesofPython,
nowweexpandittoincludeanimationandsound
Parttwo:Addanimationand
soundtoPivaders
that goal would likely have been overshot at least twofold.
Pygame’s ability to group, manage and detect collisions
thanks to the Sprite class really made a great difference
to our project, not just in terms of length but in simplicity.
If you missed the first part of the project, you can find the
v0.1 code listing on GitHub via git.io/cBVTBg, while you can
find version v0.2, including all the images, music and sound
effects we used, over at git.io/8QsK-w.
Even working within the clearly defined framework
Pygame offers, there are still a thousand ways we could
have approached adding animation and sound. We could
have created any one of a dozen classes to create and
manage containers of individual images, or read in a sprite
sheet (a single image full of smaller, separate images)
which we could then draw (or blit) to the screen. For the
sake of simplicity and performance, we integrated a few
animation methods into our Game class and opted to use a
sprite sheet. Not only does it make it very easy to draw to the
screen, but it also keeps the asset count under control and
keeps performance levels up, which is especially important
for the Raspberry Pi.
01Settingupdependencies
As we recommended with
the last tutorial, you’ll get much more
from the exercise if you download
the code (git.io/8QsK-w) and use it
for reference as you create your own
animations and sound for your Pygame
projects. Regardless of whether you
just want to simply preview and play or
walk-through the code to get a better
understanding of basic game creation,
you’re still going to need to satisfy
some basic dependencies. The two key
requirements here are Pygame and Git,
both of which are installed by default
on up-to-date Raspbian installations.
If you’re unsure if you have them,
though, type the following at the
command line:
sudo apt-get install python-
pygame git
We had great fun creating our basic
Space Invaders clone, Pivaders,
for the previous tutorial. One of the
key challenges with the project was
keeping it to a manageable size – just
300 lines of Python. Without the use
of Pygame’s strong set of features,
Resources
Raspbian:www.raspberrypi.org
/downloads
Python: www.python.org/doc
Pygame: www.pygame.org/docs
Artassets: opengameart.org
The Python Book 121
Create with Python
class Game(object):
def __init__(self):
pygame.init()
pygame.font.init()
self.clock = pygame.time.Clock()
self.game_font = pygame.font.Font(
‘data/Orbitracer
.ttf’
, 28)
self.intro_font = pygame.font.Font(
‘data/Orbitracer
.ttf’
, 72)
self.screen = pygame.display.set_mode([RES[0], RES[1]])
self.time = pygame.time.get_ticks()
self.refresh_rate = 20; self.rounds_won = 0
self.level_up = 50; self.score = 0
self.lives = 2
self.player_group = pygame.sprite.Group()
self.alien_group = pygame.sprite.Group()
self.bullet_group = pygame.sprite.Group()
self.missile_group = pygame.sprite.Group()
self.barrier_group = pygame.sprite.Group()
self.all_sprite_list = pygame.sprite.Group()
self.intro_screen = pygame.image.load(
‘data/graphics/start_screen.jpg’).convert()
self.background = pygame.image.load(
‘data/graphics/Space-Background.jpg’).convert()
pygame.display.set_caption(‘Pivaders - ESC to exit’)
pygame.mouse.set_visible(False)
Alien.image = pygame.image.load(
‘data/graphics/Spaceship16.png’).convert()
Alien.image.set_colorkey(WHITE)
self.ani_pos = 5 # 11 images of ship
self.ship_sheet = pygame.image.load(
‘data/graphics/ship_sheet_final.png’).convert_alpha()
Player
.image = self.ship_sheet.subsurface(
self.ani_pos*64, 0, 64, 61)
self.animate_right = False
self.animate_left = False
self.explosion_sheet = pygame.image.load(
‘data/graphics/explosion_new1.png’).convert_alpha()
self.explosion_image = self.explosion_sheet.subsurface(0, 0,
79, 96)
self.alien_explosion_sheet = pygame.image.load(
‘data/graphics/alien_explosion.png’)
self.alien_explode_graphics = self.alien_explosion_sheet.
subsurface(0, 0, 94, 96)
self.explode = False
self.explode_pos = 0; self.alien_explode = False
self.alien_explode_pos = 0
pygame.mixer
.music.load(‘data/sound/10_Arpanauts.ogg’)
pygame.mixer
.music.play(-1)
pygame.mixer
.music.set_volume(0.7)
self.bullet_fx = pygame.mixer
.Sound(
‘data/sound/medetix__pc-bitcrushed-lazer-beam.ogg’)
self.explosion_fx = pygame.mixer
.Sound(
‘data/sound/timgormly__8-bit-explosion.ogg’)
self.explosion_fx.set_volume(0.5)
self.explodey_alien = []
GameState.end_game = False
GameState.start_screen = True
GameState.vector = 0
GameState.shoot_bullet = False
def control(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
GameState.start_screen = False
GameState.end_game = True
if event.type == pygame.KEYDOWN 
and event.key == pygame.K_ESCAPE:
if GameState.start_screen:
GameState.start_screen = False
GameState.end_game = True
self.kill_all()
else:
GameState.start_screen = True
self.keys = pygame.key.get_pressed()
if self.keys[pygame.K_LEFT]:
GameState.vector = -1
self.animate_left = True
self.animate_right = False
elif self.keys[pygame.K_RIGHT]:
GameState.vector = 1
self.animate_right = True
self.animate_left = False
else:
GameState.vector = 0
self.animate_right = False
self.animate_left = False
if self.keys[pygame.K_SPACE]:
if GameState.start_screen:
GameState.start_screen = False
self.lives = 2
self.score = 0
self.make_player()
self.make_defenses()
self.alien_wave(0)
else:
GameState.shoot_bullet = True
self.bullet_fx.play()
def animate_player(self):
if self.animate_right:
if self.ani_pos  10:
Player
.image = self.ship_sheet.subsurface(
self.ani_pos*64, 0, 64, 61)
self.ani_pos += 1
else:
if self.ani_pos  5:
self.ani_pos -= 1
Player
.image = self.ship_sheet.subsurface(
self.ani_pos*64, 0, 64, 61)
if self.animate_left:
if self.ani_pos  0:
self.ani_pos -= 1
Player
.image = self.ship_sheet.subsurface(
self.ani_pos*64, 0, 64, 61)
else:
if self.ani_pos  5:
Player
.image = self.ship_sheet.subsurface(
self.ani_pos*64, 0, 64, 61)
self.ani_pos += 1
def player_explosion(self):
if self.explode:
if self.explode_pos  8:
self.explosion_image = self.explosion_sheet.
subsurface(0, self.explode_pos*96, 79, 96)
self.explode_pos += 1
self.screen.blit(self.explosion_image, [self.player
.
rect.x -10, self.player
.rect.y - 30])
else:
self.explode = False
self.explode_pos = 0
def alien_explosion(self):
if self.alien_explode:
if self.alien_explode_pos  9:
self.alien_explode_graphics = self.alien_explosion_
sheet.subsurface(0, self.alien_explode_pos*96, 94, 96)
self.alien_explode_pos += 1
self.screen.blit(self.alien_explode_graphics,
[int(self. explodey_alien[0]) - 50 , int(self.explodey_alien[1]) - 60])
else:
self.alien_explode = False
self.alien_explode_pos = 0
self.explodey_alien = []
def splash_screen(self):
while GameState.start_screen:
self.kill_all()
self.screen.blit(self.intro_screen, [0, 0])
self.screen.blit(self.intro_font.render(
“PIVADERS”, 1, WHITE), (265, 120))
self.screen.blit(self.game_font.render(
“PRESS SPACE TO PLAY”, 1, WHITE), (274, 191))
pygame.display.flip()
self.control()
self.clock.tick(self.refresh_rate / 2)
def make_player(self):
self.player = Player()
Pivaders.py listing from line 86 (continued on next page)
ship_sheet
Wesettheplayer
imagetobeequalto
onesmallsegment
ofthespritesheetby
usingthe‘ani_pos’
variable.Changethe
variabletochange
thepicture
SetflagsWe’ve
added‘animate_left’
and‘animate_right’
Booleanflagsto
thecontrolmethod.
Whenthey’retrue,
theactualanimation
codeiscalledviaa
separatemethod
fx.play()
Havingalready
loadedthesound
effectwewantwhen
weshoot,wenow
justneedtocallit
whenwepressthe
spacebar
Get
thecode:
bit.ly/
1xPvY1F
122 The Python Book
Create with Python
AboveTheFreesoundsiteisagoodplacetofindfreeandopensoundeffectsforprojects
04Animationsound
Compared with the game from
last month’s tutorial, you’ll see it’s now
a much more dynamic project. The
protagonist ship now leans into the turns
as you change direction and corrects
itself when you either press the opposite
direction or lift your finger off the button.
When you shoot an alien ship, it explodes
05Findingimagestoanimate
Beforewecanprogramanything,
it’s wise to have assets set up in a way we
can use them. As mentioned, we’ve opted
to use sprite sheets; these can be found
online or created with GIMP with a little
practice. Essentially they’re a mosaic
made up of individual ‘frames’ of equally
sized and spaced images representing
each frame. Find ready-made examples
atopengameart.org,asusedhere.
06Tweakingassets
While many of the assets on
sites like opengameart.org can be used
as is, you may want to import them into
an image-editing application like GIMP
to configure them to suit your needs – as
we did with our ship sheet asset to help
us keep the code simple. We started with
thecentralshipspriteandcentreditintoa
new window. We set the size and width of
theframeandthencopy-pastedtheother
frames either side of it. We ended up with
11 frames of exactly the same size and
width in a single document. Pixel-perfect
precision on size and width is key, so we
can just multiply it to find the next frame.
07Loadingthespritesheet
Since we’re inheriting from the
SpriteclasstocreateourPlayerclass,we
can easily alter how the player looks on
screen by changing Player.image. First,
we need to load our ship sprite sheet with
pygame.image.load(). Since we made our
sheet with a transparent background,
we can append .convert_alpha() to the
08Animationflags
Downthelistintheinitialisingcodeforthe Gameclass,
we set two flags for player animation: self.animate_left and
self.animate_right. In the Control method of our Game class,
we use these to ‘flag’ when we want animations to work using
Boolean values. It allows us to ‘automatically’ animate the
player sprite back to its resting state (otherwise the ship will
continuetolookasifit’sflyingleftwhenithasstopped).
end of the line so the ship frames render correctly (without any
background). We then use subsurface to set the initial Player.
image to the middle ship sprite on the sheet. This is set by self.
ani_pos, which has an initial value of 5. Changing this value will
altertheshipimagedrawntothescreen:‘0’woulddrawitleaning
fullyleft,‘11’fullytotheright.
09Theanimationmethod
These flags pop up again in the core animation code for
the player: animate_player() within the Game class. Here we
use nested if statements to control the animation and physically
set the player image accordingly. Essentially it states that if the
animate_right flag is True and if the current animation position
is different to what we want, we incrementally increase the
ani_posvariableandsettheplayer’simageaccordingly.TheElse
statement then animates the ship sprite back to its resting state
andthesamelogicisthenappliedintheoppositedirection.
10Animatingexplosions
The player_explosion() and alien_explosion() methods
thatcomeaftertheplayeranimationblockintheGameclassare
similar but simpler executions of essentially the same thing. As
we only need to run through the same predefined set of frames
(this time vertically), we only need to see if the self.explode and
self.alien_explode flags are True before we increment the
variables that change the image displayed. As the sprite sheet is
vertical, the variables alien_explode_pos and explosion_image
aresettoadifferentpartofsubsurfacethanbefore.
12Usingsoundeffects
Loading and using sounds is similar to how we do so for
images in Pygame. First we load the sound effect using a simple
assignment.Forthelaserbeam,theinitialisationlookslikethis:
self
.bullet_fx = pygame.mixer
.Sound(
‘data/sound/medetix__pc-bitcrushed-lazer-beam.ogg’)
Then we simply trigger the sound effect at the appropriate time.
In the case of the laser, we want it to play whenever we press the
space bar to shoot, so we place it in the Game class’s Control
method,straightafterweraisetheshoot_bulletflag.
If you’re struggling to find free and open sound effects, we
recommendwww.freesound.org.
11Addingmusictoyourproject
Pygamemakesiteasytoaddamusicalscoretoaproject.
Justobtainasuitablepieceofmusicinyourpreferredformat(we
found ours via freemusicarchive.org) and load it using the Mixer
Pygame class. As it’s already been initialised via pygame.init(),
wecangoaheadandloadthemusicwiththiscode:
pygame.mixer
.music.load(‘data/sound/10_Arpanauts.ogg’)
pygame.mixer
.music.play(-1)
pygame.mixer
.music.set_volume(0.7)
The music.play(-1) requests that the music should start with the
appandcontinuetoloopuntilitquits.Ifwereplaced-1with5,the
musicwouldloopfivetimesbeforeending.Learnmoreaboutthe
Mixerclassviawww.pygame.org/docs/ref/mixer.html.
03Navigatingtheproject
The project is laid out quite
simply across a few subfolders. Within
pivaders sits a licence, readme and a
second pivaders folder. This contains
the main game file, pivaders.py, which
launches the application. Within the
data folder you’ll find subfolders for both
graphics and sound assets, as well as the
font we’ve used for the title screen and
scores. To take pivaders for a test-drive,
simply enter the pivaders subdirectory
(cd pivaders/pivaders)andtype:
python pivaders.py
Use the arrow keys to steer left and right
and the space bar to shoot. You can quit
to the main screen with the Esc key and
pressitagaintoexitthegamecompletely.
02Downloadingpivaders
Git is a superb version control
solution that helps programmers safely
store their code and associated files.
Not only does it help you retain a full
history of changes, it means you can
‘clone’ entire projects to use and work
on from places like github.com. Toclone
the version of the project we created for
this tutorial, go to your home folder from
thecommandline(cd ~)andtype:
git pull https://github.com/
russb78/pivaders.git
This will create a folder called
pivaders – go inside (cd pivaders) and
take a look around.
with several frames of animation and
should you take fire, a smaller explosion
occurs on your ship. Music, lasers and
explosion sound effects also accompany
theanimationsastheyhappen.
The Python Book 123
Create with Python
self.player_group.add(self.player)
self.all_sprite_list.add(self.player)
def refresh_screen(self):
self.all_sprite_list.draw(self.screen)
self.animate_player()
self.player_explosion()
self.alien_explosion()
self.refresh_scores()
pygame.display.flip()
self.screen.blit(self.background, [0, 0])
self.clock.tick(self.refresh_rate)
def refresh_scores(self):
self.screen.blit(self.game_font.render(
“SCORE “ + str(self.score), 1, WHITE), (10, 8))
self.screen.blit(self.game_font.render(
“LIVES “ + str(self.lives + 1), 1, RED), (355, 575))
def alien_wave(self, speed):
for column in range(BARRIER_COLUMN):
for row in range(BARRIER_ROW):
alien = Alien()
alien.rect.y = 65 + (column * (
ALIEN_SIZE[1] + ALIEN_SPACER))
alien.rect.x = ALIEN_SPACER + (
row * (ALIEN_SIZE[0] + ALIEN_SPACER))
self.alien_group.add(alien)
self.all_sprite_list.add(alien)
alien.speed -= speed
def make_bullet(self):
if GameState.game_time - self.player
.time  self.player
.speed:
bullet = Ammo(BLUE, BULLET_SIZE)
bullet.vector = -1
bullet.speed = 26
bullet.rect.x = self.player
.rect.x + 28
bullet.rect.y = self.player
.rect.y
self.bullet_group.add(bullet)
self.all_sprite_list.add(bullet)
self.player
.time = GameState.game_time
GameState.shoot_bullet = False
def make_missile(self):
if len(self.alien_group):
shoot = random.random()
if shoot = 0.05:
shooter = random.choice([
alien for alien in self.alien_group])
missile = Ammo(RED, MISSILE_SIZE)
missile.vector = 1
missile.rect.x = shooter
.rect.x + 15
missile.rect.y = shooter
.rect.y + 40
missile.speed = 10
self.missile_group.add(missile)
self.all_sprite_list.add(missile)
def make_barrier(self, columns, rows, spacer):
for column in range(columns):
for row in range(rows):
barrier = Block(WHITE, (BLOCK_SIZE))
barrier
.rect.x = 55 + (200 * spacer) + (row * 10)
barrier
.rect.y = 450 + (column * 10)
self.barrier_group.add(barrier)
self.all_sprite_list.add(barrier)
def make_defenses(self):
for spacing, spacing in enumerate(xrange(4)):
self.make_barrier(3, 9, spacing)
def kill_all(self):
for items in [self.bullet_group, self.player_group,
self.alien_group, self.missile_group, self.barrier_group]:
for i in items:
i.kill()
def is_dead(self):
if self.lives  0:
self.screen.blit(self.game_font.render(
“The war is lost! You scored: “ + str(
self.score), 1, RED), (250, 15))
self.rounds_won = 0
self.refresh_screen()
self.level_up = 50
self.explode = False
self.alien_explode = False
pygame.time.delay(3000)
return True
def defenses_breached(self):
for alien in self.alien_group:
if alien.rect.y  410:
self.screen.blit(self.game_font.render(
“The aliens have breached Earth defenses!”,
1, RED), (180, 15))
self.refresh_screen()
self.level_up = 50
self.explode = False
self.alien_explode = False
pygame.time.delay(3000)
return True
def win_round(self):
if len(self.alien_group)  1:
self.rounds_won += 1
self.screen.blit(self.game_font.render(
“You won round “ + str(self.rounds_won) +
“ but the battle rages on”, 1, RED), (200, 15))
self.refresh_screen()
pygame.time.delay(3000)
return True
def next_round(self):
self.explode = False
self.alien_explode = False
for actor in [self.missile_group,
self.barrier_group, self.bullet_group]:
for i in actor:
i.kill()
self.alien_wave(self.level_up)
self.make_defenses()
self.level_up += 50
def calc_collisions(self):
pygame.sprite.groupcollide(
self.missile_group, self.barrier_group, True, True)
pygame.sprite.groupcollide(
self.bullet_group, self.barrier_group, True, True)
for z in pygame.sprite.groupcollide(
self.bullet_group, self.alien_group, True, True):
self.alien_explode = True
self.explodey_alien.append(z.rect.x)
self.explodey_alien.append(z.rect.y)
self.score += 10
self.explosion_fx.play()
if pygame.sprite.groupcollide(
self.player_group, self.missile_group, False, True):
self.lives -= 1
self.explode = True
self.explosion_fx.play()
def main_loop(self):
while not GameState.end_game:
while not GameState.start_screen:
GameState.game_time = pygame.time.get_ticks()
GameState.alien_time = pygame.time.get_ticks()
self.control()
self.make_missile()
self.calc_collisions()
self.refresh_screen()
if self.is_dead() or self.defenses_breached():
GameState.start_screen = True
for actor in [self.player_group, self.bullet_group,
self.alien_group, self.missile_group]:
for i in actor:
i.update()
if GameState.shoot_bullet:
self.make_bullet()
if self.win_round():
self.next_round()
self.splash_screen()
pygame.quit()
if __name__ == ‘__main__’
:
pv = Game()
pv.main_loop()
Get
thecode:
bit.ly/
1xPvY1F
124 The Python Book
Create with Python
Bridgethegapbetweenbooksandvideogamesby creating an
interactivenovelor choose-your-own-adventurewithPython
andPygame
Resources
Python2:
www.python.org/
Pygame:
pygame.org/download.shtml
IDLEPythonIDE
Gameassets
CodefromFileSilo(optional)
Makeavisualnovel
gamewithPython
Most people look for a compelling story in
modern videogames, and those that don’t
have one are appearing less and less. A great
way to tell a pure story is through the genre
of visual novels, and you can make one fairly
simply in Python. These interactive novels are
an extremely popular form of entertainment in
Japan, and usually work by having the player
clickthroughastoryandmakedecisionsasthey
go along in order to experience different plot
pointsandendings.

QChangescenestoaddmoredepthtothestory,andallowthegametohavedecisionsandroutes
In Python, this is a relatively simple project
to create, but with the addition of the Pygame
module we can make it easier still, and even
more expandable for the future. Pygame adds
better support for positioning the images and
text, creating display windows and using mouse
and keyboard inputs, thereby simplifying the
codingprocess.
We’ll be coding this in standard Python 2, so
makesuretorunitinIDLE2andnotIDLE3while
youarewriting,testingandcoding.
The Python Book 125
Create with Python
02GetthePygamecode
Next we need to download the code
for Pygame direct from the source. Still in the
terminal,youcandothisbytypingin:
$ hg clone https://bitbucket.org/pygame/
pygame
Which will download it to the folder ‘pygame’.
Move to that using CD pygame in the terminal so
wecancontinuebuildingit.
03BuildthePygamemodule
To install it, we need to do it in two
steps. First we need to prepare the code to
installusingtheterminalwith:
$ python setup.py build
Once that’s finished you can then actually install
itwith:
$ sudo python setup.py install
Thiswon’ttaketoolong.
04Installinotherways
If the above doesn’t work (or is a bit
daunting) you can check the website for binary
and executable files that will work on other
operating systems and Linux distros. Head to
http://pygame.org/download.shtml to get the
files you need for your specific system, including
Windows and OS X. The rest of the tutorial will
workinanyOS.
01 Get Pygame dependencies
The best way to install Pygame for your
system is to compile it. To do this you need to
first install the right dependencies. Open up
the terminal and install the following packages,
which in Ubuntu looks like:
$ sudo apt-get install mercurial
python-dev python-numpy libav-tools
libsdl-image1.2-dev libsdl-mixer1.2-
dev libsdl-ttf2.0-dev libsmpeg-
dev libsdl1.2-dev libportmidi-dev
libswscale-dev libavformat-dev
libavcodec-dev
05Getthevisualnovelfiles
We’ve uploaded the code to FileSilo,
and here we’re going to walk you through what
we’ve done to make it work. Download the files
for the visual novel and unzip them. The two
files we care about for the moment are the
visualnovel.py and script.py python files – this
is where all the important code is.
06Understandthescriptfile
For the moment the script file is small
and literally just holds the script for the game.
It’s made up of events for the visual novel to
move between, line by line, by splitting it up into
scenes.Thisincludesthelocationofeachline,the
character,theactuallineitselfandinformationon
how the game flows. These are matrices with the
informationin,andarecompletelycustomisable.
126 The Python Book
Create with Python
09Addvariablesandassets
We add a mixture of information we
need to run the novel. We define the size of the
display screen to use (1000 pixels wide and 563
high), along with some RGB colours for the code
to use. We’re also telling Pygame what font to
use and how large for certain sections and also
loading images for the game.
10Startthegame
Create a display for the game. Pygame
works by constantly updating the display with
new information. To show how this works, the
menu function adds elements to the display
(which we’ve titled screen), such as filling it
with colour, adding shapes and using blit to add
images or in this case text. Once you’ve created
a buffer of changes to the screen, you update it
with the flip() function.
12Startthestory
Our start_game function is called
when the mouse clicks the right position and
we prepare the game, getting the characters,
locations and progression through the game
script. The rest of this function uses this info to
pull in data from the script to make the game
flow properly.
07Howthescriptrelates
In our game, the code pulls in
elements from the script file as it goes. We'll
explain how that works later, but this also
allows us to implement decisions later on to
change which direction the game might take
you in.
08Startingthemaingame
We don’t need many modules for the
current state of the visual novel. Here we’ve
imported the new Pygame module, our script
as a module and the time module for aesthetic
reasons – we’re going to have the code pause in
bits rather than just instantly change scenes to
the next line. We also initialise Pygame with a
simple pygame.init()
13Firstscreen
The first screen is handled differently,
and acts to get every element up on the
interface before we continue – it makes the
code take a little less time to process as we
11Seethemouse
As we’ve created the button as a
rectangle and now an image on the menu, we
need to recognise when the mouse is hovering
over it to know when the button is clicked. First
we have to use event.get() to see the mouse
in general, then we look for the position with
get_pos(). After that, we wait for it to click, see
where it clicked (using the co-ordinates of the
rectangle) and make a decision after that.
Thecodepullsinelementsfrom
thescriptfileasitgoes,allowingusto
implementdecisionslateron
The Python Book 127
Create with Python
15Thestartingfunction
We finish our code bit with a simple
function that starts off the entire game. This is
just to encapsulate the entire code and allows us
toadddifferentwaysofturningitoffinthefuture.
IDLE when running the file will load everything
up and then run the game() function at the end
– this is similar to how you can add a __main__
functionattheendwhichwillstartthecodeinthe
commandline.
feel more interactive. This would not require
much more code than the if statements, and
it would also be a good way for you to look into
adding graphical buttons to click and use the
collide function.
17Movetheassets
Currently the code has the script-
specific assets in the main visualnovel file.
These can be moved to the script, allowing
you to make the visualnovel file much more
modular so that can you have multiple scripts
with different assets to load at startup.
14Addvariablesandassets
Similarly to the way that our original
startup code works, our next if statement and
iteration checks to see what is different on the
next line, and if it moves to a different scene
function. It will also change anything that is
different without filling up the buffer more
than needed. Where we’ve made no change is
labelled with a 0 in the scripts.
16Expandyourcode
The code written is very expandable,
allowing you to add decisions that are logged
to take you to different scenes (or routes in
visual novel terminology) and make your game
begin. The getattr allows us to use the string/
integer associated with our place in the story
and call upon the relevant scene function from
the script file. We then use an if statement
with an iterative function to successively add
screen element to give the illusion that it’s
building up the first screen. We finish it by
advancing the progression value.
Thecodehereisveryexpandable,
allowingyoutoadddecisionsthattake
youtodifferentscenes
128 The Python Book
Create with Python
Games are a great way of understanding a
language:youhaveagoaltoworktowards,and
eachfeatureyouaddbringsmorefun.However,
games need libraries and modules for graphics
and other essential games features. While the
Pygame library made it relatively easy to make
gamesinPython, it stillbringsinboilerplatecode
thatyouneedbeforeyougetstarted–barriersto
youoryourkidsgettingstartedincoding.
Pygame Zero deals with all of this boilerplate
code for you, aiming to get you coding games
instantly. Pg0 (as we’ll abbreviate it) makes
sensible assumptions about what you’ll need for
agame–fromthesizeofthewindowtoimporting
the game library – so that you can get straight
downtocodingyourideas.
Pg0’s creator, Daniel Pope, told us that the
library “grew out of talking to teachers at Pycon
UK’s education track, and trying to understand
that they need to get immediate results and
break lessons into bite-size fragments, in order
to keep a whole class up to speed.”
To give you an idea of what’s involved here,
we’ll build up a simple game from a Pong-
type bat and ball through to smashing blocks
Breakout-style. The project will illustrate what
can be done with very little effort. Pg0 is in
early development but still offers a great start
– and is now included on the Pi in the Raspbian
Jessie image.
We’ll look at installation on other platforms,
but first let’s see what magic it can perform.
SCORE
0001200
LIVES BREAKOUT
Resources
PygameZero:
pygame-zero.readthedocs.org
Pygame:
pygame.org/download.shtml
Pip
pip-installer.org
Python3.2orlater
www.python.org/
CodefromFileSilo(optional)
PygameZero
PygameZerocutsouttheboilerplatetoturnyourideasintogames
instantly,andwe’llshowyouhow
The Python Book 129
Create with Python
01Zeroeffort
Although game writing is not easy, getting started
certainly is. If you’ve got Raspbian Jessie installed on your Pi,
you’rereadytogo.Openaterminalandtype:
touch example.py
pgzrun example.py
Andyou’llseeanemptygamewindowopen(Ctrl+Qwillclosethe
window).Yes,it’sthateasytogetstarted!
04NoPi?
You don’t even need a Raspberry Pi to install Pygame
Zero – just install the Pygame library, then use pip to install
Pygame Zero. Instructions vary by distro, but a good place to
start is the documentation: bit.ly/1GYznUB.
03OlderRaspbian
If you’re still running Raspbian Wheezy, you’ll need to run
thefollowingstepstoinstallPygameZero:
sudo apt-get update
sudo apt-get install python3-setuptools python3-pip
sudo pip-3.2 install pgzero
05Intro.py
That default black square of 800 by 600 pixels we
saw in Step 1 can now be overridden manually. For example,
we can use the following code to replace it with an oversized
gold brick, in a sly nod to Breakout:
WIDTH = 1000
HEIGHT = 100
def draw():
screen.fill((205, 130, 0))
That colour tuple takes RGB values, so you can quickly
get colours off a cheatsheet; screen is built into Pg0 for
the window display, with methods available for all sorts of
different sprites…
02Python3
If you haven’t got Raspbian Jessie, chances are you’ll
haveneitherPg0norPygameinstalled.ThePython’spippackage
installer will take care of grabbing Pg0 for you, but the preceding
steps vary by distro. One thing you will need is Python 3.2 (or
newer). If you’ve been sticking with Python 2.x in your coding
(perhaps because it’s used in a tutorial you’re following), make
Pg0yourchanceforagentleupgradetoPython3.
RightBreakoutis
aclassicarcade
gamethatcan
bereimaginedin
PygameZero
Insituationswhere
Pygameisused
boilerplateandall
withyoungpeople,
greatresultscan
alsobeachieved
(seeBryson
Payne’sbook),
butPygameand
Pg0,despitetheir
useaspowerful
educationaltools,
arealsogoodfor
creatinggamesfor
codersnomatter
whatstageof
learningtheyareat.
Greatgamesareall
aboutthegameplay,
drivenbypowerful
imaginations
generatingimages,
animations,sounds
andjourneys
throughgame
worlds.Good
frameworksopen
upthiscreative
activitytopeople
whoarenot
traditionallearners
ofprogramming,
whichisanarea
wherePythonhas
longexcelled.
Young
andold
Codeon
FileSilo
130 The Python Book
Create with Python
09Mousemove
We want to move the bat, and the mouse is closer to an
arcadepaddlethanthearrowkeys.Addthefollowing:
def on_mouse_move(pos):
x, y = pos
bat.center = (x, bat.center[1])
Usepgzruntotestthatyouhaveascreen,batandmovement.
08Batty
You can think of Breakout as essentially being a
moving bat – that is, you’re hitting a moving ball in order to
knock out blocks. The bat is a rectangle, and Pygame’s Rect
objects store and manipulate rectangular areas – we use
Rect((left, top), (width, height)), before which we define the bat
colour and then call upon the draw function to put the bat on
thescreen,usingthescreenfunction.
W = 800
H = 600
RED = 200, 0, 0
bat = Rect((W/2, 0.96 * H), (150, 15))
def draw():
screen.clear()
screen.draw.filled_rect(bat, RED)
07BreakoutviaPong
While the Pi is something of a tribute to 1980s 8-bit
computers, Breakout comes from the 1970s and is a direct
descendant of the early arcade classic Pong. We’ll follow the
route from Pong to Breakout (which historically involved Apple
founders Steve Wozniak and Steve Jobs) in the steps to creating
our code, leaving you with the option of developing the Pong
elements into a proper game, as well as refining the finished
Breakoutclone.
06Sprite
The intro example from the Pg0 docs expands on that
with the Actor class, which will automatically load the named
sprite (Pg0 will hunt around for a .jpg or .png in a subdirectory
calledimages).
alien = Actor(‘alien’)
alien.pos = 100, 56
WIDTH = 500
HEIGHT = alien.height + 20
def draw():
screen.clear()
alien.draw()
You can download the alien from the Pg0 documentation (bit.
ly/1Sm5lM7) and try out the animation shown there, but we’re
takingadifferentapproachinourgame.
Right Thebat
andballcome
first–they’rethe
cornerstonesof
PongandBreakout
DavidAmes,who
usesPg0toteach
youngerchildren
tocodeatevents
acrosstheUK,
toldus:“Onething
toavoidwhenit
comestoteaching
kidsisObject
Orientation.”OOP
(object-oriented
programming)is
partlyabstracted
awaybyPg0,butit
can’tbeignored.
Perhapsthebest
approachisusing
Pg0andsome
simplecodeto
start,thendropping
inapieceofOO
whenit’sneededto
solveaparticular
problem.
WiththeCodeClub
agegroup–about
eighttoeleven–
feedinginformation
tosolvepractical
problemsworks
well.Itcanwork
withadults,too–
butthere’salways
someonewho’s
readaheadand
hasafewtricky
questions.
Program
objects
The Python Book 131
Create with Python
## Breakout type game to demonstrate Pygame Zero library
## Based originally upon Tim Viner’s London Python Dojo
## demonstration
## Licensed under MIT License - see file COPYING
from collections import namedtuple
import pygame
import sys
import time
W = 804
H = 600
RED = 200, 0, 0
WHITE = 200,200,200
GOLD = 205,145,0
ball = Rect((W/2, H/2), (30, 30))
Direction = namedtuple(‘Direction’, ‘x y’)
ball_dir = Direction(5, -5)
bat = Rect((W/2, 0.96 * H), (120, 15))
class Block(Rect):
def __init__(self, colour, rect):
Rect.__init__(self, rect)
self.colour = colour
blocks = []
for n_block in range(24):
block = Block(GOLD, ((((n_block % 8)* 100) + 2, ((n_block //
8) * 25) + 2), (96, 23)))
blocks.append(block)
def draw_blocks():
for block in blocks:
screen.draw.filled_rect(block, block.colour)
def draw():
screen.clear()
screen.draw.filled_rect(ball, WHITE)
screen.draw.filled_rect(bat, RED)
draw_blocks()
def on_mouse_move(pos):
x, y = pos
bat.center = (x, bat.center[1])
def on_mouse_down():
global ball_dir
ball_dir = Direction(ball_dir.x * 1.5, ball_dir.y * 1.5)
Fullcodelisting
Togettheballtomoveweneed
todefinemove(ball)foreachcase
wheretheballmeetsawall
10Squareball
In properly retro graphics-style, we define a square
ball too – another rectangle, essentially, with the (30, 30) size
makingitthatsubsetofrectanglesthatwecallasquare.
We’re doing this because Rect is another built-in in Pg0. If we
wanted a circular ball, we’d have to define a class and then use
Pygame’s draw.filled_circle(pos, radius, (r, g, b)) - but Rect we
cancalldirectly.Simplyadd:
WHITE = 200,200,200
ball = Rect((W/2, H/2), (30, 30))
…totheinitialvariableassignments,and:
screen.draw.filled_rect(ball, WHITE)
…tothedef draw()block.
12defmove(ball)
To get the ball to move within the screen we need to
define move(ball) for each case where the ball meets a wall.
For this we use if statements to reverse the ball’s direction at
eachoftheboundaries.Refertothefullcodelistingonpage67.
Note the hardcoded value of 781 for the width of screen,
minus the width of ball – it’s okay to hardcode values in
early versions of code, but it’s the kind of thing that will need
changing if your project expands. For example, a resizable
screenwouldneedavalueofW - 30.
13Absolutevalues
You might expect multiplying y by minus one to work for
reversingthedirectionoftheballwhenithitsthebat:
ball_dir = Direction(ball_dir
.x, -1 * ball_dir
.y)
… but you actually need to use abs, which removes any minus
signs,thenminus:
ball_dir = Direction(ball_dir
.x, - abs(ball_dir
.y))
Try it without in the finished code and see if you get some
strangebehaviour.Yourhomeworkistoworkoutwhy.
11Action!
Now let’s make the ball move. Download the tutorial
resources in FileSilo.co.uk and then add the code inside the
‘move.py’ file to assign movement and velocity. Change the 5 in
ball_dir = Direction(5, -5) if you want the ball slower or faster,
as your processor (and dexterity) demands – but it’s hard to
tell now because the ball goes straight off the screen! Pg0 will
call the update() function you define once per frame, giving the
illusionofsmooth(ish)scrollingifyou’renotrunningmuchelse.
132 The Python Book
Create with Python
19Drawingblocks
Draw_blocks() is added to def draw() after defining:
def draw_blocks():
for block in blocks:
screen.draw.filled_rect(block, block.colour)
20Blockbashing
All that remains with the blocks is to expand def
move(ball) – to destroy a block when the ball hits it.
to_kill = ball.collidelist(blocks)
if to_kill = 0:
sounds.block.play()
ball_dir = Direction(ball_dir
.x, abs(ball_dir
.y))
blocks.pop(to_kill)
17Goingforgold
CreateaBlockclass:
class Block(Rect):
def __init__(self, colour
, rect):
Rect.__init__(self, rect)
self.colour = colour
…andpickanicecolourforyourblocks:
GOLD = 205,145,0
18Lineuptheblocks
Thisbuildsanarrayof24blocks,threerowsofeight:
blocks = []
for n_block in range(24):
block = Block(GOLD, ((((n_block % 8)* 100) + 2,
((n_block // 8) * 25) + 2), (96, 23)))
blocks.append(block)
16Buildingblocks
There are many ways of defining blocks and distributing
them onto the screen. In Tom Viner’s team’s version, from
the London Python Dojo – which was the code that originally
inspired this author to give this a go – the blocks are sized in
relationtonumberacrossthescreen,thus:
N_BLOCKS = 8
BLOCK_W = W / N_BLOCKS
BLOCK_H = BLOCK_W / 4
BLOCK_COLOURS = RED, GREEN, BLUE
Using multicoloured blocks which are then built into an
array means that blocks can join without needing a border.
With its defining variables in terms of screen width, it’s good
sustainable code, which will be easy to amend for different
screensizes–seegithub.com/tomviner/breakout.
However, the array of colour bricks in a single row is not
enough for a full game screen, so we’re going to build our array
fromhard-codedvalues…
15Blockhead!
If you’re not very familiar with the ancient computer
game Breakout, then:
apt-get install lbreakout2
… and have a play. Now, we haven’t set our sights on building
something quite so ambitious in just these six pages, but we
do need blocks.
14Sounds
Also upon bat collision, sounds.blip.play() looks in
the sounds subdirectory for a sound file called blip. You can
download the sounds (and finished code) from FileSilo.co.uk.
Actually, now we think about it, ignore the previous
comment about homework – your real homework is to turn
what we’ve written so far into a proper game of Pong! But first
let’s finish turning it into Breakout!
RightTomViner’s
arrayofblocks
negatestheneedfor
borderedrectangles
There’sanew
versionofPg0in
development–it
mayevenbeout
asyoureadthis.
Pg0creatorDaniel
Popetellsus“atone
generationAPIisin
theworks,”andthat
atthePg0PyConUK
sprint,“wefinished
Actorrotation.”
Contributionsare
welcome–notonly
tothePg0code,but
moreexamplesare
needednotjustto
showwhatcanbe
done,buttogive
teacherstoolsto
enthusechildren
aboutthecreative
actofprogramming.
Pg0hasalso
inspiredGPIO
Zero,tomake
GPIOprogramming
easieronthe
RaspberryPi,with
rapiddevelopment
occurringonthis
newlibraryaswego
topress.
Pg0+1
The Python Book 133
Create with Python
22Scoredraw
Taking advantage of some of Pygame Zero’s quickstart
features, we’ve a working game in around 60 lines of code.
From here, there’s more Pg0 to explore, but a look into Pygame
unmediatedbythePg0wrapperisyournextstepbutone.
First refactor the code; there’s plenty of room for improvement
– see the example ‘breakout-refactored.py’ which is included in
your tutorial resources. Try adding scoring, the most significant
absence in the game. You could try using a global variable and
writing the score to the terminal with print(), or instead use
screen.blit to put it on the game screen. Future versions of Pg0
might do more for easy score keeping.
23Classofninelives
For adding lives, more layers, and an easier life-keeping
score, you may be better defining the class GameClass and
enclosing much of the changes you wish to persist within it,
such as self.score and self.level. You’ll find a lot of Pygame code
online doing this, but you can also find Pg0 examples, such as the
excellent pi_lander example by Tim Martin: github.com/timboe/
pi_lander.
21Gameover
Lastly, we need to allow for the possibility of successfully
destroying all blocks.
if not blocks:
sounds.win.play()
sounds.win.play()
print(“Winner!”)
time.sleep(1)
sys.exit()
24Don’tstophere
This piece is aimed at beginners, so don’t expect to
understand everything! Change the code and see what works,
borrow code from elsewhere to add in, and read even more code.
Keep doing that, then try a project of your own – and let us know
how you get on.
def move(ball):
global ball_dir
ball.move_ip(ball_dir)
if ball.x  781 or ball.x = 0:
ball_dir = Direction(-1 * ball_dir.x, ball_dir.y)
if ball.y = 0:
ball_dir = Direction(ball_dir.x, abs(ball_dir.y))
if ball.colliderect(bat):
sounds.blip.play()
ball_dir = Direction(ball_dir.x, - abs(ball_dir.y))
to_kill = ball.collidelist(blocks)
if to_kill = 0:
sounds.block.play()
ball_dir = Direction(ball_dir.x, abs(ball_dir.y))
blocks.pop(to_kill)
if not blocks:
sounds.win.play()
sounds.win.play()
print(“Winner!”)
time.sleep(1)
sys.exit()
if ball.y  H:
sounds.die.play()
print(“Loser!”)
time.sleep(1)
sys.exit()
def update():
move(ball)
Fullcodelisting(cont.)
LeftTestyourgame
onceit’sfinished
–thentestother
people’sBreakout
gamestoseehow
thecodediffers
development
134 The Python Book
154
Web
136 Develop with Python
Why Python is perfect for the web
142 Creating dynamic templates
Use Flask and Jinja2 to their full potential
146 Build your own blog
Begin developing your blog
150 Deliver content to your blog
Add content to your site
154 Enhance your blog
Complete your blog with add-ons
“Pythonisaversatilelanguage,
perfectformakingwebsites”
142 136
The Python Book 135
Web development
136 The Python Book
Don’tbefooledintothinkingPythonisarestrictivelanguageor
incompatiblewiththemodernweb.Exploreoptionsforbuilding
Pythonwebappsandexperiencerapidapplicationdevelopment
Web development
The Python Book 137
Django djangoproject.com
GOOD FOR: Large database-driven web apps with multiuser support
and sites that need to have heavily customisable admin interfaces
Django contains a lot of impressive features, all in the name of interfaces
and modules. These include autowiring, admin interfaces and database
migration management tools by default for all of your projects and
applications. Django will help to enable rapid application development for
enterprise-level projects, whilst also enabling a clear modular reuseable
approachtocodeusingsubapplications.
Werkzeug
werkzeug.pocoo.org
GOOD FOR: API creation, interacting with
databases and following strict URL routes
whilst managing HTTP utilitie
Werkzeug is the underlying framework for
Flask and other Python frameworks. It provides
a unique set of tools that will enable you to
perform URL routing processes as well as
request and response objects, and it also
includesapowerfuldebugger.
Tornado tornadoweb.org
GOOD FOR: Web socket interaction and long polling due to its
ability to scale to manage vast numbers of connections
Tornado is a networking library that works as a nonblocking web server
and web application framework. It’s known for its high performance and
scalability and was initially developed for friendfeed, which was a real-
time chat system that aggregated several social media sites. It closed
down in April 2015 as its user numbers had declined steadily, but Tornado
remainsasactiveandusefulasever.
Flask flask.pocoo.org
GOOD FOR: Creating full-featured RESTful APIs. Its ability to manage
multiple routes and methods is very impressive
Flask’s aim is to provide a set of commonly used components such as
URL routing and templates. Flask will also work on controlling the request
and response objects, all-in-all this means it is lightweight but is still a
powerfulmicroframework.
PyramiD pylonsproject.org
GOOD FOR: Highly extensible and adaptable to any project
requirement. Not a lightweight system either
Heavily focused on documentation, Pyramid brings all the much needed
basic support for most regular tasks. Pyramid is open source and
also provides a great deal of extensibility – it comes with the powerful
WerkzeugDebuggertoo.
Frameworks
Why?
Let’s take a look at some of the frameworks
available when developing Python web applications
First released in 1991, companies
like Google and NASA have been
using Python for years
Thanks to the introduction of the Web Server
Gateway Interface (WSGI) in 2003, developing
Python web apps for general web servers
became a viable solution as opposed to
restrictingthemto custom solutions.
Python executables and installers are
widely available from the official Python site at
www.python.org.
Mac OS X users can also benefit greatly
from using Homebrew to install and manage
their Python versions. Whilst OS X comes
bundled with a version of Python, it has some
potential drawbacks. Updating your OS may
clear out any downloaded packages, and
Apple’s implementation of the library differs
greatly from the official release. Installing
using Homebrew helps you to keep up to date
and also means you get the Python package
managerpipincluded.
Once Python is installed the first package to
download should be virtualenv using ‘pip install
virtualenv’,which enablesyou to createproject-
specific shell environments. You can run
projects on separate versions of Python with
separateproject-specificpackagesinstalled.
Check out the detailed Hitchhiker’s Guide
to Python for more information: docs.python-
guide.org/en/latest.
Web development
138 The Python Book
Create an API Let us explore the Flask microframework and build a
simple yet powerful RESTful API with minimal code
01InstallFlask
Create a new directory inside of
which your project will live. Open a Terminal
window and navigate to be inside your new
directory. Create a new virtual environment
for this project, placed inside a new directory
called ‘venv’, and activate it. Once inside
the new virtual shell, proceed to installing
Flask using the ‘pip install Flask’ command.
virtualenv venv
. venv/bin/activate
pip install Flask
04ConnecttoDatabase
With the database path defined,
we need a way to create connection to the
database for the application to obtain data.
Create a new method called ‘connet_db’ to
manage this for us. As a method we can call it
when we set up a prerequest hook shortly. This
will return a new open connection using the
database details set in the configuration object.
def connect_db():
return sqlite3.connect(app.
config[‘DATABASE’])
07PopulatetheDatabase
To populate the database you
can now run the init_db inside an active
python shell. To do so enter a shell by typing
‘python’ inside your environment, and then
running the command below. Alternatively,
you can use the sqlite3 command and
pipe the schema.sql file into the database.
# Importing the database using the
init_db method
python
 from index import init_db
 init_db()
# Piping the schema using SQLite3
sqlite3 /tmp/api.db  schema.sql
02CreateIndex
Create a new file in the root of the
project location called ‘index.py’. The sample API
will use a SQLite database, so we need to import
that module for use in the application. We’ll
also import some core components from the
Flask module to handle request management
and response formatting as well as some other
functions. The minimum import for a Flask
applicationisFlaskitself.
import sqlite3
from flask import Flask, request, g,
redirect, url_for
, render_template,
abort, jsonify
05DatabaseSchema
Our SQLite database will only contain
one table. Create a new file called ‘schema.sql’
in the root of the project directory. This fill will
contain the SQL commands required to create
the table and populate it with some sample
bootstrappeddata.
drop table if exists posts;
create table posts (
id integer primary key autoincrement,
title text not null,
text text not null
);
insert into posts (title, text) values
(‘First Entry’
, ‘This is some text’);
insert into posts (title, text) values
(‘Second Entry’
, ‘This is some more text’);
insert into posts (title, text) values
(‘Third Entry’
, ‘This is some more text
(again)’);
08RequestDBConnection
With the database created and
populated we need to be able to ensure
we have an open connection and close it
accordingly when finished. Flask has some
decorator methods to help us achieve this.
The before_request() method will establish
the connection and stores it in the g object
for use throughout the request cycle. We
can then close the connection after the
cycle using the teardown_request() method.
@app.before_request
def before_request():
g.db = connect_db();
03DeclareConfig
For a small application we can declare
configuration options as upper-casename value
pairsinsidethemainmodule,whichwe’lldonow.
Here we can define the path and name of the
SQLite database and also set the Flask debug
output to True for development work. Initialise
the Flask application to a namespace and then
import the config values set directly above it.
We then run the application. All routes must be
placedabovetheselasttwolines.
# Config
DATABASE = ‘
/tmp/api.db’
DEBUG = True
app = Flask(__name__)
app.config.from_object(__name__)
# Add methods and routes here
if __name__ == ‘__main__’
:
app.run()
06InstantiatetheDatabase
To populate the database with the new
table and any associated data, we will need to
import and apply the schema to the database.
Add a new module import at the top of the
project file to obtain the ‘contextlib.closing()’
method. What we will do next is create a method
that will initialise the database by reading the
contents of schema.sql and executing it against
theopendatabase.
from contextlib import closing
def init_db():
with closing(connect_db()) as db:
with app.open_resource(‘schema.sql’
,
mode=’r’) as f:
db.cursor().executescript(f.read())
db.commit()
“World-renowned image sharing service
Instagram and social pin board Pinterest
have also implemented Python as part of
their web stack, opting for Django”
The Python Book 139
Python in
the wild
Interested in Python development?
You’d be in good company with big
names currently using it
10TemplateOutput
Flask expects templates to be available
within the templates directory in the root of
the project, so make sure that you create that
directory now. Next, add a new file called
‘show_posts.html’. The dynamic values are
managed using Jinja2 template syntax, the
default templating engine for Flask applications.
Save this file in the templates directory.
ul class=posts
{% for post in posts %}
lih2{{ post.title }}/h2{{ post.
text|safe }}
{% else %}
liSorry, no post matches your
request.
{% endfor %}
/ul
13Runtheapplication
To run your Flask application, navigate
using the active Terminal window into the
root of the project. Ensuring you are in an
active virtual environment Python shell,
enter the command to run the main index
file. The built-in server will start and the site
will be accessible in the browser on default
port local address http://127.0.0.1:5000.
python index.py
11MakeanAPIResponse
To create an API response we can
define a new route with a specific API endpoint.
Once again, we query the database for all
posts. The data is then returned as JSON,
using the JSONify method to do so. We can
add specific values such as post count and
a custom message if you wish, as well as the
actual posts variable, formatted as JSON.
@app.route(‘
/api/v1/posts/’
,
methods=[‘GET’])
def show_entries():
cur = g.db.execute(‘select title, text
from posts order by id desc’)
14APIJSONOutput
The root of the application will render
the template we previously created. Multiple
routes can be generated to create a rich web
application. Visiting an API-specific URL in
the browser will return the requested data as
cleanly formatted JSON. The ability to define
custom routes like a versioned RESTful endpoint
isincrediblypowerful.
09DisplayPosts
Create your first route so that we can
return and display all available posts. To query
the database we execute a SQL statement
against the stored db connection. The results
are then mapped to values using Python’s
dict method and saved as the posts variable.
To render a template we then call render_
template() and pass in the file name and the
variable to display as the second argument.
Multiple variables can be passed through as a
comma-separatedlist.
@app.route(‘
/’)
def get_posts():
cur = g.db.execute(‘select title, text
from posts order by id desc’)
posts = [dict(title=row[0], text=row[1])
for row in cur
.fetchall()]
return render_template(‘show_posts.
html’
, posts=posts)
12GetaspecificPost
CTo obtain a specific post from the
API we need to create a new route, which will
accept a dynamic value as part of the URI. We
can also choose to use this route for multiple
request methods, which are in this case GET
and DELETE. We can determine the method
by checking the request.method value and
run it against a conditional if/else statement.
@app.route(‘
/api/v1/posts/int:post_id’
,
methods=[‘GET’
, ‘DELETE’])
def single_post(post_id):
method = request.method
if method == ‘GET’
:
cur = g.db.execute(‘select title,
text from posts where id =?’
, [post_id])
posts = [dict(title=row[0],
text=row[1]) for row in cur
.fetchall()]
return jsonify({‘count’
: len(posts),
‘posts’
: posts})
elif method == ‘DELETE’
:
g.db.execute(‘delete from posts
where id = ?’
, [post_id])
return jsonify({‘status’
: ‘Post
deleted’})
@app.teardown_request
def teardown_request(exception):
db = getattr(g, ‘db’
, None)
if db is not None:
db.close()
posts = [dict(title=row[0],
text=row[1]) for row in ur
.fetchall()]
return jsonify({‘count’
: len(posts),
‘posts’
: posts})
Disqus, the popular social interaction
comment service provider, has
been implementing their production
applications in Python for a very long time.
Python’s benefit for the development
team was its ability to scale effectively
and cater for a large number of consumers
whilst also providing an effective
underlying API for internal and external
use. The company are now starting to
run some production apps in Go, but the
majority of code still runs on Python.
World-renowned image sharing service
Instagram and social pin board Pinterest
have also implemented Python as part of
their web stack, opting for Django to assist
with the functionality and ability to cater
for the many thousands of content views
andrequestsmadetotheirservices.
Mozilla, Atlassian’s Bitbucket
repository service, and popular satire site
The Onion have all been noted as using
Django for their products.
Web development
140 The Python Book
Django application
development Django is a full Python web-app framework
with impressive command-line tools
01CreateVirtualEnvironment
Create a new directory for your project
and navigate inside it using a new Terminal
window. Create a new virtual environment for
this project, opting to use the latest Python 3.
Your Python 3 location may vary, so be sure to
set the correct path for the binary package.
virtualenv -p /usr/local/bin/python3 venv
01Generatethemodel
Open blog/models.py and create the
first model class, providing the property names
and types for each. You can dig deeper into
field types via the docs here: bit.ly/1yln1kn.
Once complete, open myblog/settings.py and
add the blog app to the list of allowed installed
applications so that the project will load it.
# blog/models.py
class Post(models.Model):
title = models.CharField(max_
length=200)
text = models.TextField()
# myblog/settings.py
INSTALLED_APPS = (‘django.contrib.admin’
,
..., ‘django.contrib.staticfiles’
, ‘blog’)
04InitialMigration
Navigate into the project directory via
the Terminal window. Some of the installed
apps included in the project generation require
databasetables.
Using the helper. run a migration command to
create all of these automatically. The Terminal
window will keep you informed of all of your
progress and what has been applied from the
migration.
cd myblog
python manage.py migrate
02ActivateandInstall
Using your Terminal window,
activate the virtual environment to start the
project-specific shell. VirtualEnv has a local
version of the Python package manager
pip installed, so it’s fairly straight forward
to run the command to install Django.
. venv/bin.activate
pip install Django
02DataMigration
Any creation of models or changes to
data need to be migrated. To do so we need
to make migration files from the model data,
which generate sequentially numbered files.
Then we run a specific migration to generate
the required SQL and the final migrate
command performs the database execution.
python manage.py makemigrations blog
python manage.py sqlmigrate blog 0001
python manage.py migrate
05CreateApp
Each Django project is made up of
at least one application or module. Run the
startapp command to create a new blog app
module, which will generate the required
code adjacent to the main project structure.
python manage.py startapp blog
03CreateCoreProject
The Django install contains some
incredibly useful command-line tools, which
will help you to run a number of repetitive and
difficult tasks. Let’s use one of them to create a
fresh project structure for us. Run the django-
admin.pyscriptwiththenameoftheprojectthat
youwantcreated.
django-admin.py startproject myblog
Installing
Django
Database
models
 migration
The installation of Django is
relatively easy once you have
python installed. See for yourself
as we build a simple app here
Django’s ability to manage the
migration and maintenance of
database schema and project
models is very impressive
Web development
The Python Book 141
01CreateAdminUser
Django makes content administration
incredibly easy and has an admin section
available in a default project as standard
at http://127.0.0.1:8000/admin. To log in you
need to create a superuser account. Run
the associated command and specify user
details as required to then proceed and log in.
python manage.py createsuperuser
04CreateaView
With the admin interface accepting
new submissions for our post class we’ll create
a view page to display them. Open blog/views.
py and import the Post class from the models.
Create a method to obtain all posts from the
databaseandoutputthemasastring.
from django.http import HttpResponse
from blog.models import Post
def index(request):
post_list = Post.objects.order_by(‘-id’)
[
:5]
output = ‘br /’
.join([p.title for p
in post_list])
return HttpResponse(output)
02Switchonblogmanagement
Having logged in to the administration
interface you will be greeted with features to
manage users and group roles and privileges,
which alone are very powerful and provided for
you by Django. There is not yet, however, any
access to manage our blog posts so let’s turn
thaton.
05ManagetheURLs
Create ‘blog/urls.py’ and add the code
to import the views that were just made in the
module and the accompanying URL patterns.
Open myblog/urls.py and add the URL function
call to implement a new URL for the app to
display the view. Visit http://127.0.0.1:5000/
blog in your browser to render the new view.
# blog/urls.py
from django.conf.urls import patterns,url
from blog import views
urlpatterns = patterns(‘’
,
url(r’^$’
, views.index, name=’index’),
)
# myblog/urls.py
urlpatterns = patterns(‘’
,
url(r’^blog/’
, include(‘blog.urls’)),
url(r’^admin/’
, include(admin.site.urls)),
)
03EnableAdminManagement
To enable our module and associated
models to be managed through the admin
interface, we need to register them with the
admin module. Open blog/admin.py and then go
on to import and register the models in turn (we
only have one of these currently though). Save
the file and refresh the admin site to see the
poststhatarenowavailabletomanage.
from django.contrib import admin
# Register your models here.
Autowiring the
admin interface
Hosting
Python
apps
Admin sections can be
problematic in their own right.
Django provides an extensible
admin interface for you
Admin sections can be
problematic in their own right.
Django provides an extensible
admin interface for you
Herokuheroku.com
This app is perhaps one of the most well-
known cloud hosting providers. Their stack
server environments support a number
of core web app languages including
Python as standard. Their unique Toolbelt
command-line features and integration
with Git repositories, as well as being
incredibly quick and easy to scale and
improve performance, makes them an
obvious choice. A free account will let you
run a Python web app on one dyno instance
withoutanycost.
PythonAnywhere
www.pythonanywhere.com
Another hosted option, and one created
specifically for Python applications in
general is Python Anywhere. The free
basic option plan has enough weight and
power behind it to get you up and running
with a Python web app without having to
scale, but as soon as your project gains
traction, you can switch plans and boost
yourplansperformance.
It offers an incredibly impressive range
of modules as standard, available to import
into your application immediately to get you
started, including Django and Flask should
youneedthem.
Using the
dev server
Django ships with a very helpful built-in
development server, which will help you
out by autocompiling and reloading
after you have completed all of your file
changes. All you have to do to start
the server is to run the ‘python
manage.py runserver’ command from
your Terminal window within
the project directory.
142 The Python Book
Web development
CreateadynamicwebpagewithTwitterandFlask’srendering
engine,Jinja2
Creatingdynamictemplates
withFlask,Jinja2andTwitter
Python and Flask are a great combination
when you’re looking to handle the Twitter
OAuth process and build requests to obtain
tokens. We’ve used Twitter here because of
the large amount of easily digestible data
available on it. However, since Twitter adheres
to the standards set out by OAuth 1.0, the code
we’ve used to sign and build requests can
be modified to work with any third-party API
using the same standard without a great deal
of work. For years PHP has been a mainstay
of template generation, but now with well-
documented frameworks such as Flask,
Sinatra and Handlebars, the ability to use
powerful scripting languages greatly improves
our ability to make great web services. Here,
we’re going to use Python, Flask and its
templating engine to display tweets. Flask
comes with the super-nifty Jinja2 templating
engine, If you’re familiar with Node.js or front-
end JavaScript, the syntax will look very
similar to the Handlebars rendering engine.
But, before we dive into that, we need to
organise some of the example code that we’re
using for this.
Resources
Python2.7+
Flask0.10.0:flask.pocoo.org
FlaskGitHub:
github.com/mitsuhiko/flask
ATwitteraccount
Yourfavouritetexteditor
CodedownloadedfromFileSilo
01Rearrangingourcode
Server code can get messy and
unmaintainable quickly, so the first thing we’re
going to do is move our helper functions to
another file and import them into our project,
much like you would a module. This way, it will
be clear which functions are our server logic
and endpoints and which are generic Python
functions. Open the TwitterAuthentication file
downloaded from FileSilo (stored under Twitter
OAuth files) and locate the getParameters,
sign_request and create_oauth_headers
functions. Cut and paste them into a new file
called helpers.py in the root of your project
folder. At the top of this file we want to import
some libraries.
import urllib, collections, hmac,
binascii, time, random, string
from hashlib import sha1
Now we can head back over to server.py and
import the helper functions back into our
project. We do this by simply calling import
02server.pymodules
With a lot of the modules needed in this
project having been moved to helpers.py, we
can now remove most of them from server.py.
If we amend our first import statement to be…
import urllib2, time, random, json
…our project will continue to function as it did
before. Note the addition of the json module:

QThetemplateusesalooptogeneratealistofTwittertweets
helpers. Because Python is smart, It will look
in the current directory for a helpers.py file
before it looks for a system module. Now every
function included in helpers.py is accessible
to our project. All we need to do to call them is
prepend our the methods we called before with
helper.function_name and it will execute. For
sign_request, we’ll need to pass our
oauth_secret and consumer_secret for each
request rather than accessing it from the
session.Adjustthefunctiondeclarationlikeso:
def sign_request(parameters, method,
baseURL, consumer_secret, oauth_secret):
The Python Book 143
Web development

QTheBSD-licensedFlaskiseasytosetup
anduse–checkoutthewebsiteformoreinfo
we’ll be using that later as we start handling
Twitter data.
Having Flask use a rendering engine is
super-simple. Flask comes packaged with
the Jinja2 template rendering engine, so we’ve
nothing to install – we just need to import the
package into the project. We can do this by
adding render_template to the end of our from
flask import […]statement.
03Ourfirsttemplate
Now that we have a rendering engine,
we need to create some templates for it to
use. In the root of our project’s folder, create
a new folder called templates. Whenever
we try to render a template, Flask will look in
this folder for the template specified. To get
to grips with templating, we’ll rewrite some
of our authentication logic to use a template,
rather than manually requesting endpoints. In
templates, create an index.html file. You can
treat this HTML file like any other – included in
the resources for this tutorial is an index.html
that includes all of the necessary head tags and
!DOCTYPEdeclarationsforthisfile.
04Renderingourtemplate
In server.py, let’s create a route for ‘/’ to
handletheauthorisationprocess.
@app.route(‘
/’)
def home():
if not ‘oauth_token’ in session:
session.clear()
session[‘oauth_secret’] = ‘’
session[‘oauth_token’] = ‘’
return render_template(‘index.html’)
It’s a simple function: all we want to do is check
whether or not we have an oauth_token already
and create those properties in the Flask session
so we don’t throw an error if we try to access
it erroneously. In order to send our generated
template in response to the request, we return
render_template(‘index.html’).
{% if session[‘oauth_token’] != “” %}
h1Already Authorised/h1
div class=”dialog”
pHello, You’ve authenticated!brLet’s a href=”
/get-tweets”get some tweets/a/p
/div
{% else %}
h1Authorisation required/h1
div class=”dialog”
pWe need to a href=”/authenticate”authenticate/a/p
/div
{% endif %}
Fig 01
05Templatevariables
We can choose to send variables to our
template with render_template(‘index.htm’
,
variableOne=value, variableTwo=Value) but
inthisinstancewedon’tneedtoaseachtemplate
hasaccesstotherequestandsessionvariables.
Codeon
FileSilo
144 The Python Book
Web development
Nowweknowhowtobuildtemplates,
let’sgrabsometweetstodisplay
09Checkingoursession
and buildingour request
Before we start grabbing tweets, we want to
run a quick check to make sure we have the
necessary credentials and if not, redirect the
user back the authorisation flow. We can do
this by having Flask respond to the request
with a redirection header, like so:
if session[‘oauth_token’] == “” or
session[‘oauth_secret’] == “”:
return redirect(rootURL)
Assuming we have all we need, we can start to
build the parameters for our request (Fig 02).
You’ll notice that the nonce value is different
from that in our previous requests. Where the
nonce value in our authenticate and authorise
requests can be any random arrangement of
characters that uniquely identify the request,
for all subsequent requests the nonce needs
to be a 32-character hexadecimal string using
only the characters a-f. If we add the following
function to our helpers.py file, we can quickly
build one for each request.
def nonce(size=32, chars=”abcdef” +
string.digits):
return ‘’
.join(random.choice
(chars) for x in range(size))
10Signingandsendingourrequest
We’ve built our parameters, So let’s
sign our request and then add the signature to
the parameters (Fig 03).
Before we create the authorisation headers,
we need to remove the count and user_id
values from the tweetRequestParams
dictionary, otherwise the signature we just
created won’t be valid for the request. We can
achieve this with the del keyword. Unlike our
token requests, this request is a GET request,
so instead of including the parameters
in the request body, we define them as
query parameters.
?count=tweetRequestParams[‘count’]
user_id=tweetRequestParams[‘user_id’]
11HandlingTwitter’sresponse
Now we’re ready to fire off the request
and we should get a JSON response back
from Twitter. This is where we’ll use the json
module we imported earlier. By using the
json.loads function, we can parse the JSON
into a dictionary that we can access and we’ll
pass through to our tweets.html template.
tweetResponse = json.
loads(httpResponse.read())
return render_template(‘tweets.html’
,
data=tweetResponse)
Whereas before, we accessed the session
to get data into our template, this time
we’re explicitly passing a value through to
our template.
12Displayingourtweets
Let’s create that template now, exactly
the same as index.html but this time, instead of
using a conditional, we’re going to create a loop
togeneratealistoftweetswe’vereceived.
First, we check that we actually received
some data from our request to Twitter. If we’ve
got something to render, we’re ready to work
through it, otherwise we’ll just print that we
didn’tgetanything.
Once again, any template logic that we want
to use to generate our page is included between
08Let’sgetsometweets
Sonowweknowhowtobuildtemplates,
let’s grab some tweets to display. In server.py
defineanewroute,get-tweets,likeso:
@app.route(‘/get-tweets’)
@app.route(‘/get-tweets/count’)
def getTweets(count=0):
You’ll notice that unlike our other authentication
endpoints, we’ve made two declarations.
The first is a standard route definition: it will
07Staticfiles
Pretty much every webpage uses
JavaScript, CSS and images, but where do we
keep them? With Flask we can define a folder
for use with static content. For Flask, we create
a static folder in the root of our project and
access files by calling /static/css/styles.css or
/static/js/core.js.Thetutorialresourcesincludea
CSSfileforstylingthisproject.
Open index.html. All code executed in a Flask
template is contained within {% %}. As this is our
homepage, we want to direct users accordingly,
Solet’scheckifwe’vegotanaccesstoken(Fig01).
Between the ifs and else of the template is
standard HTML. If we want to include some data
– for example, the access token – we can just add
{{ session[‘oauth_token’] }} in the HTML and it
will be rendered in the page. Previously, in our /
authorised endpoint, we would display the OAuth
token that we received from Twitter; however, now
thatwehaveatemplate,wecanredirectourusers
backourrootURLandhaveapagerenderedforus
thatexplainstheprogresswe’vemade.
06Getting lost
(andthenfoundagain)
With every server, some things get misplaced or
peoplegetlost.Sohowdowehandlethis?Rather
than defining a route, we can define a handler
thatdealswithgettinglost.
@app.errorhandler(404)
def fourOhFour(error):
return render_template(‘fourohfour
.html’)
If a page or endpoint is requested and triggers a
404, then the fourOhFour function will be fired. In
this case, we’ll generate a template that tells the
user,butwecouldalsoredirecttoanotherpageor
dumptheerrormessage.
intercept and handle the path get-tweets. The
second lets us define a parameter that we can
use as a value in our getTweets function. By
including count=0 in our function declaration,
we ensure that there will always be a default
value when the function is executed; this way we
don’t have to check the value is present before
we access it. If a value is included in the URL, it
will override the value in the function. The string
inside the variable name determines the
name of the variable. If you want the variable
passed to the function to have a specific type,
you can include a converter with the variable
name. For example, if we wanted to make sure
that count was always an integer instead of a
floatorstring,we’ddefineourroutelikeso:
@app.route(‘/get-tweets/int:count’)
The Python Book 145
Web development
tweetRequestParams = {
“oauth_consumer_key” : consumer_key,
“oauth_nonce” : helpers.nonce(32),
“oauth_signature_method” : “HMAC-SHA1”,
“oauth_timestamp” : int(time.time()),
“oauth_version” : “1.0”,
“oauth_token” : session[‚Äòoauth_token’],
“user_id” : session[‘user_id’],
“count” : str(count)
}
Fig 02
tweetRequest = helpers.sign_request(tweetRequestParams, “GET”,
“https://api.twitter
.com/1.1/statuses/user_timeline.json”, consumer_secret,
session[‘oauth_secret’])
tweetRequestParams[“oauth_signature”] = tweetRequest
makeRequest=urllib2.Request(“https://api.twitter
.com/1.1/statuses/
user_timeline.json?count=” + tweetRequestParams[‘count’] + “user_id=”
+ tweetRequestParams[‘user_id’])
del tweetRequestParams[‘user_id’], tweetRequestParams[‘count’]
makeRequest.add_header(“Authorization”, helpers.create_oauth_
headers(tweetRequestParams))
try:
httpResponse = urllib2.urlopen(makeRequest)
except urllib2.HTTPError
, e:
return e.read()
Fig 03
{% if data %}
ul id=”tweets”
{% for tweet in data %}
li
div class=”image”
img src=”{{ tweet[‘user’][‘profile_image_url_https’]
}}” alt=”User Profile Picture”
/div
div class=”text”
a{{ tweet[‘text’]|forceescape }}/a
/div
/li
{% endfor %}
/ul
{% else %}
pWe didn’t get any tweets :(/p
{% endif %}
Fig 04
13Flaskfilters
Sometimes, when parsing from JSON,
Python can generate erroneous characters
that don’t render particularly well in HTML.
You may notice that after tweet[‘text’] there
is |forceescape, This is an example of a Flask
filter; it allows us to effect the input before we
render – in this case it’s escaping our values
for us. There are many, many different built-
in filters that come included with Flask. Your
advisor recommends a full reading of all the
potential options.
14Wrappingup
That’s pretty much it for templating
with Flask. As we’ve seen, it’s insanely quick
and easy to build and deploy dynamic sites.
Flask is great tool for any Python developer
looking to run a web service. Although we’ve
used Twitter to demonstrate Flask’s power,
all of the techniques described can be used
with any third-party service or database
resource. Flask can work with other rendering
engines, such as Handlebars (which is
superb), but Jinja2 still needs to be present
to run Flask and conflicts can occur between
the two engines. With such great integration
between Flask and Jinja2, it makes little
sense to use another engine outside of very
specific circumstances.
{% %}. This time we’re creating a loop; inside the
loopwe’llbeabletoaccessanypropertywehave
of that object and print it out. In this template
we’re going to create an li element for each
tweet we received and display the user’s profile
pictureandtextofthetweet(Fig 04).
In our template we can access properties
using either dot notation (.) or with square
brackets ([]). They behave largely the same;
the [] notation will check for an attribute on
the dictionary or object defined whereas the
. notation will look for an item with the same
name. If either cannot find the parameter
specified, it will return undefined. If this occurs,
the template will not throw an error, it will simply
print an empty string. Keep this in mind if your
template does not render the expected data:
you’ve probably just mis-defined the property
you’retryingtoaccess.
Unlike traditional Python, we need to
tell the template where the for loop and if/
else statements end, so we do that with
{%endfor %}and{% endif %}.
146 The Python Book
Web development
Djangoisofcourseable
toreadandwritetoSQL
databases,butitneeds
verylittlepriorknowledge
tosucceedindoingso
Djangocomeswitha
lightweightdevelopment
serversoyoucantestall
yourworklocally
Learnhowtousethisextremelypowerful
Python-basedwebframeworktocreatea
completeblogfromscratchinrecordtime
Buildyourownblog
withDjango
Creating your own blog always feels like a
great accomplishment. Sure, you could use the
fantastic WordPress if you need a complete blog
witheveryfeatureyou’deverneedrightnow.And
Tumblr exists for people who just want to write
something,orpostpicturesofcorgisinspace.
You don’t have full control from start to finish
with a prefabricated blog, though, and neither
of these is written in the fantastic Django.
Django is of course based on Python, the object-
orientated programming language designed to
have clearly readable syntax. Due to its Python
base, it’s an incredibly powerful and simple-to-
use language for web development with a vast
arrayofapplications.
So let’s use it to make a blog. In this first
section of the process we will explore how to set
up Django, writing and reading to a database,
creating a front- and back-end, and some
interactionswithHTML.
Resources
PythonSourceCode
www.python.org/download/releases/2.7.2
DjangoSourceCode
www.djangoproject.com/download
UsingHTMLandCSSin
conjunctionwithDjangois
clearandstraightforward;
it’smucheasiertobug-fix
thanPHP
Djangocomeswith
agenericback-end
sitethatissetupin
seconds,andeasily
customisableafterthat
The Python Book 147
Web development
01InstallPython
Django is based on Python, and requires
it to be installed to develop on. Python 2.7 is the
recommended version, and this is installed with
the python package. If you want to check your
version, start the Python shell by typing ‘python’
intotheterminal.
02InstallDjango
Most operating systems will have a
Django package available in the repository, like
python-django in Debian. The Django website
has a list if you have trouble finding it, or you
could build it from source. Make sure you install
version1.3.
03VerifyyourDjango
To make sure Django installed properly,
and that you have the right version, enter the
Python shell by typing ‘python’ and enter
thefollowing:
import django
print django.get_version()
It will return a version number if it has installed
correctly,whichshouldbe1.3.
04Startanewproject
In the terminal, cd to the folder you
want to develop the blog in, and then run the
nextcommand:
django-admin startproject myblog
Here, ‘myblog’ can be replaced by whatever you
wish to name the project, but we’ll use it for the
upcomingexamples.
05Startthedevelopmentserver
Django comes with a lightweight
development server to test out work locally. We
can also use it to check our work, so cd to the
myblogfolderandthenuse:
python manage.py runserver
If all goes well, it should return zero errors. Use
Ctrl+Ctoexittheserver.
06Configurethedatabase
The database settings are kept in the
settings.py file. Open it up with your favourite
editor and go to the Databases section. Change
ENGINEto:
‘ENGINE’: ‘django.db.backends.sqlite3’,
And in NAME, put the absolute path – for
example:
‘NAME’: ‘/home/user/projects/myblog/
sqlite.db’,
Saveandexit.
07Createthedatabase
The database file will be generated by
usingthecommand:
python manage.py syncdb
During the creation, it will ask you to set up a
superuser,whichyoucandonow.
The SQLite database file will be created in
yourmyblogfolder.
148 The Python Book
Web development
Youdon’thavefullcontrolfromstart
tofinishwithaprefabricatedblog–but
youwillwithDjango
08Createyourblog
Nowit’stimetocreateablogappinyour
project.Type:
python manage.py startapp blog
This creates the models file which is where all
your data lives. You can change ‘blog’ to another
name,butwe’lluseitinourexamples.
09Startyourblogmodel
We can now take the first steps in
creating our blog model. Open models.py and
changeitsoitsaysthefollowing:
from django.db import models
class Post(models.Model):
post = models.TextField()
This creates the Post class, which has a
subclassthatcontainsyourblogtext.
10Customiseyourblog
Let’snowexpandtheblogmodelabitso
itresemblesamoreclassicblog:
class Post(models.Model):
post = models.TextField()
title = models.TextField()
author = models.CharField(max_
length=50)
pub_date = models.DateTimeField()
A CharField needs to have a character
limit defined, and DateTimeField holds the
timevalues.
11Installyourapp
Your app needs to be installed to your
project, which is very simple. Open the settings.
py file again, go to the INSTALLED_APPS section
andadd:
‘blog’,
Then run the following to create the database
tables:
python manage.py sql blog
Andfinally:
python manage.py syncdb
12Setuptopost
Now we can create a post and test out
ourcode.Firstthough,enterthePythonshell:
python manage.py shell
Then execute these commands to add all the
necessaryfieldsanddata:
from blog.models import Post
import datetime
13Let’sblog
Create the post. For this example, we
willcallittest_post:
test_post = Post()
Nowlet’saddtheblogcontent:
test_post.post = ‘Hello World!’
test_post.title = ‘First Post’
test_post.author = ‘Me’
test_post.pub_date = datetime.
datetime.now()
Andthensaveitwith:
test_post.save()
14Startthesiteback-end
To create the admin site, edit urls.py
from the myblog directory, and uncomment or
addthefollowinglines:
from django.contrib import admin
admin.autodiscover()
url(r’^admin/’, include(admin.site.
urls)),
Save and exit, then edit settings.py and
uncommentthislinefromINSTALLED_APPS:
‘django.contrib.admin’,
Theadminsiteisnowat127.0.0.1:8000/admin/.
The Python Book 149
Web development
24Afunctionalblog
So there you have it! Navigating to
127.0.0.1:8000/admin/ or 127.0.0.1:8000/myblog/
will show off the fine work you’ve created.
Django is dead easy to use once you know how,
and there are plenty of tweaks you should be
abletomakeafterthistutorial.
15Setuptheadminpage
The admin page has a generic, usable
template, but you need to configure it to view,
edit, create and delete posts. First, create a new
fileadmin.pyintheblogdirectoryandenter:
from blog.models import Post
from django.contrib import admin
admin.site.register(Post)
To have the posts display nicely on the site, edit
models.pyandadd:
class Post (models.Model):
…
def __unicode__(self):
return self.title
Save,andrun:
python manage.py syncdb
The admin page is now usable! You should be
able to see the other posts, and it’s now a lot
easiertoaddmore.
16Activatethefront-end
Open up urls.py from the myblog
directory in your editor and add the following to
theurlpatternssection:
url(r’^myblog/’, ‘blog.urls.index’)),
One of the examples in the file can be
uncommented and edited to this as well. It
pointstoamodelwewillnowcreate.
17Createanotherurlsfile
You need to create another urls file in the
app directory, in our case blog/urls.py. Create it
andaddthefollowing:
from django.template import Context,
loader
from blog.models import Post
from django.http import HttpResponse
def index(request):
post_list = Post.objects.all()
t = loader.get_template(‘blog/
index.html’)
c = Context({
‘post_list’: poll_list,
})
return HttpResponse(t.render(c))
18Startthetemplate
The code we’ve just written looks for a
template that currently doesn’t exist. We first
need to tell Django where templates are to be
lookedforinsettings.py:
TEMPLATE_DIRS = (
‘/home/user/projects/templates’,
)
You can put the template directory wherever you
want,aslongasit’sreferencedhere.
19Writeatemplate
Now to write the site template. In our
example,we’reusingindex.html:
{% for post in post_list %}
{{ post.title }}
{{ post.author }}
{{ post.pub_date }}
{{ post.post }}
{% endfor %}
This needs to be located in a folder with
the same name as your app within the
templatedirectory.
20Viewyourhandiwork
Let’s make sure this worked. Start the
developerserverwith:
python manage.py runserver
Andnavigateto127.0.0.1:8000/myblog/.
It’s not pretty, but you should have
successfully called upon your stored data. We’ll
spendthenextstepstidyingitupabit.
21Formatthefrontpage
Go back into the template file,
index.html,andaddthefollowinghtmltags:
{% for post in post_list %}
h2{{ post.title }}/h2
{{ post.author }} on {{ post.pub_
date }}
p{{ post.post }}/p
{% endfor %}
This is just an example – the post can be in any
orderwithanytags.
22Spruceuptheadminlist
We’ll do this in the admin.py file in our
blog directory; open it in your editor and make
thefollowingchanges:
from blog.models import Post
from django.contrib import admin
class Admin(admin.ModelAdmin):
list_display = [‘title’, ‘author’,
‘pub_date’]
admin.site.register(Post, Admin)
Inthiscase‘list_display’isafixedvariablename.
23Alogicalpostpage
The new post page on the site might
not be in an order you’re comfortable with.
We’ll change that now in admin.py with the
followingadditions:
class Admin(admin.ModelAdmin):
list_display = [‘title’, ‘author’,
‘pub_date’]
fields = [‘title’, ‘pub_date’,
‘author’, ‘post’]
admin.site.register(Post, Admin)
Remembertosave!
Djangoisanincrediblypowerful
andsimple-to-uselanguage
150 The Python Book
Web development
Wecontinuebuildinganawesomeblogusingthepowerful
Djangoframework,andthistutorialisallaboutthefront-end
contentdelivery
Delivercontenttoyourblog
In the last tutorial we began to build the most
basic of blogs, and learned how to use a bit of
Djangointheprocess. We can now set up a new
project, create a database and write basic code
Withminimalextracode,
ourtemplatecandisplay
themontharchivefrom
thesidebar
Djangohasbuilt-incodeto
dealwithpaginationvery
cleanlyandeffectively
Allowyourreadersto
giveyoufeedback,and
moderatetheminthe
adminpanel
WithDjangowecanmake
simplesidebarsthatlist
archivesbymonth
to read and write to the database. All simple
stuff, but of course it’s core to building websites
whereDjangomightbecalledupon.
Here we will give the front end of the site
an overhaul, making it more of the standard
you would expect from a modern blog. This
will include a sidebar, pages, post pages and
the ability to add and moderate comments.
In the process we will learn some more of
the benefits that come with using Django to
developwebsites.
You should keep using Django 1.3 for this
tutorial,aswedidbefore.
Resources
Pythonbase:
http://www.python.org/download/
Djangosource:https://www.
djangoproject.com/download/
The Python Book 151
Web development
08Pleaseturnover
Now we need to add the navigation
links to the blog, so open the index template
forediting:
{% if post_list.has_previous %}
a href=”?list_page={{ post_list.
previous_page_number }}”Newer /a
{% endif %}
{% if post_list.has_next %}
a href=”?list_page={{ post_list.
next_page_number }}” Older/a
{% endif %}
01Newblogorder
We left off last time with the blog
displaying posts in chronological order, which
isn’t very helpful to readers. To correct this,
open up urls.py in the blog folder and edit the
followingline:
post_list = Post.objects.all().order_
by(“-pub-date”)
This makes sure that posts are displayed in
reverseorder(newestfirst).
02Aviewtoapage
You’ll want to be able to link specific
pages, of course, and to do that we first have to
define what goes into these pages in the urls.py
fileintheblogfolder:
def post_page(request, post_id):
post_page = Post.objects.
get(pk=post_id)
return render_to_response(‘blog/
post.html’, {‘post_page’: post_page})
03Cleanupyourcode
You may notice that we used a different
return command to the index definition – this
is a shortcut that makes writing the code a bit
easier.Togetitworking,add:
from django.shortcuts import render_to_
response
We recommend that you edit the index code to
matchpost_page.
04EditURLs
In urls.py in myblog we need to make
some additions and modifications for the
websitetodirecttothepostcorrectly:
url(r’^myblog/$’, ‘blog.urls.index’),
url(r’^myblog/(?Ppost_idd+)/$’,
‘blog.urls.post_page’),
The post_id is the number of the post, which is
auto-generated. The ‘$’ is important to make the
redirectionwork.
05Aposttemplate
We told the post_page to point towards
a template we now need to create. In the same
location as index.html, create post.html with the
followingformattingtoresemblethefrontpage:
h2{{ post_page.title }}/h2
{{ post_page.author }} on {{ post_page.
pub_date }}
p{{ post_page.post }}/p
06Linktothepage
Let’s get these links working from the
mainpage.Openuptheindex.htmlfileandmake
thefollowingchange:
h2a href=/myblog/{{ post.pk }}{{
post.title }}/a/h2
This is a very simple addition using an absolute
link, and requires no fiddling with the views
ormodel.
07Pagination
To get blog posts split up over pages, we
need to make some additions to urls.py in the
blogfolder:
post_list = Post.objects.all().order_
by(“-pub_date”)
paginator = Paginator(post_list, 3)
try: list_page = request.GET.get(“list_
page”, ‘1’)
except ValueError: list_page = 1
post_list = paginator.page(list_page)
return render_to_response(‘blog/index.
html’, {‘post_list’: post_list})
152 The Python Book
Web development
Weneedtobeabletoprocessthe
dataandmetadataintheforms
09Wrongpage
Let’s add a quick bit of code to return
somebody to the previous page if they get the
URLwrong:
from django.core.paginator import
Paginator, EmptyPage, InvalidPage
try:
post_list = paginator.page(list_
page)
except (EmptyPage, InvalidPage):
post_list = paginator.
page(paginator.num_pages)
The last part replaces ‘post_list = paginator.
page(list_page)’.
10Haveyoursay
Everyone has their opinion on the
internet. You can give your readers the ability to
comment,andwe’llstartbyeditingmodels.py:
class Comment(models.Model):
author = models.CharField(max_
length=50)
text = models.TextField()
post = models.ForeignKey(Post)
def __unicode__(self):
return (self.post, self.text)
We’ve made it so they can put their name with
acomment.
11Backtothecomment
We now need to add a small line to the
urls.py file in myblog so the comment can be
postedthensentbacktotheoriginalpage:
url(r’^myblog/add_comment/(d+)/$’,
‘blog.urls.add_comment’),
This URL pattern calls the ID of the page that
you’reon.
12Formacomment
We need to be able to process the
data and metadata in the forms, so let’s add
a class to urls.py in the blog folder with the
followingadditions:
from django.forms import ModelForm
from blog.models import Post, Comment
class CommentForm(ModelForm):
class Meta:
model = Comment
exclude = [‘post’]
13Inthepost
We need to attribute the comments to
the post they’re being made on, so update the
post_pagedefinition:
from django.core.context_processors
import csrf
def post_page(request, post_id):
post_page = Post.objects.
get(pk=post_id)
comments = Comment.objects.
filter(post=post_page)
d = dict(post_page=post_page,
comments=comments, form=CommentForm())
d.update(csrf(request))
return render_to_response(‘blog/
post.html’, d)
The CSRF tag is to prevent cross-site
requestforgery.
14Commenttemplate
Let’s get the post page ready for
commentsbyaddingthistopost.html:
pComments:/p
{% for comment in comments %}
{{ comment.author }}
p{{ comment.text }}/p
{% endfor %}
strongAdd comment/strong
form action=”{% url blog.urls.
add_comment post_page.id %}”
method=”POST”{% csrf_token %}
Name {{ form.author }}
p{{ form.text }}/p
input type=”submit” value=”Submit”
/form
15Defineyourcomments
The final step is defining the comments
inblog/urls.py,andit’sabigone:
def add_comment(request, comment_id):
p = request.POST
if p.has_key(‘text’) and p[‘text’]:
author = ‘Anonymous’
if p[‘author’]: author =
p[‘author’]
comment = Comment(post=Post.
objects.get(pk=comment_id))
cf = CommentForm(p,
instance=comment)
cf.fields[‘author’].required =
False
comment =
cf.save(commit=False)
comment.author = author
comment.save()
return HttpResponseRedirect(reverse
(‘blog.urls.post_page’, args=[comment_
id]))
This ensures text has been entered, and if not
specified author is ‘Anonymous’. Before testing,
runsyncdbsocommenttablescanbecreated.
The Python Book 153
Web development
16Administrate
Like the posts, we can get the
Admin page to see comments. Start editing
blogs/admin.pytogetthisfeatureadded:
from blog.models import Post, Comment
from django.contrib import admin
class PostAdmin(admin.ModelAdmin):
list_display = [‘title’, ‘author’,
‘pub_date’]
fields = [‘title’, ‘pub_date’,
‘author’, ‘post’]
admin.site.register(Post, PostAdmin)
17Comment-specificadminfeatures
Now we can add the comment-specific
adminfeatureswithoutcausinganyclashes:
class CommentAdmin(admin.ModelAdmin):
list_display = [‘text’, ‘author’,
‘post’]
admin.site.register(Comment,
CommentAdmin)
This will show the comments on the admin site,
and you can see the comment, the author and
thepostit’sconnectedto.
18Sidebarbeginnings
Django makes it pretty easy to order
posts by years and months, but first we need to
importsomenewmodelsintoblog/urls.py:
import time
from calendar import month_name
We’re going to define two new functions,
month_timelineandmonth,tomakethesidebar.
19Starttodefinemonth_timeline
First we need to get all the information
fromtheposts:
def month_timeline():
year, month = time.localtime()[:2]
begin = Post.objects.order_by(‘pub_
date’)[0]
month_begin = begin.pub_date.month
year_begin = begin.pub_date.year
month_list = []
The ‘[:2]’ makes sure we only get the time
informationweneed.
20Finishyourdefinition
Now we will order the posts by month
andyearstartingfromourfirstmonth.
for y in range(year, year_begin-1, -1):
start, end = 12, 0
if y == year: start = month
if y == year_begin: end = month_
begin-1
for m in range(start, end, -1):
month_list.append((y, m,
month_name[m]))
return month_list
21Returntoreader
With the list organised, we can now
definemonthsowecandisplayitontheblog:
def month(request, year, month):
post_list = Post.objects.
filter(pub_date__year=year, pub_date__
month=month)
return render_to_response(‘blog/
index.html’, dict(sidebar_list=post_
list, month_list=month_timeline()))
Nowweneedtolinkituptotheindextemplate.
22Finaliseyoursidebardefinition
Edit the return command on the index
functiontoincludethesidebarinformation:
return render_to_response(‘blog/index.
html’, dict(post_list=post_list,
sidebar_list=post_list.object_list,
month_list=month_timeline()))
Then add this line to urls.py in myblog so a
monthpagecanberendered:
url(r’^myblog/month/(d+)/(d+)/$’,
‘blog.urls.month’),
All we need to do now is display the information
onthesite.
24Sidebarfinale
Obviously it’s not at the side right now
– that’s a job for the HTML and CSS. The info is
there, though, and you can manipulate it any
way you want. However, your blog is now a lot
morefriendlytoyourreaders.
23Sidebarontheweb
Go to the index template. First of all,
changethefirstlineofthepostforloopto:
{% for post in sidebar_list %}
Simple enough. Now we need to add the
sidebarinformation:
{% for month in month_list %}
pa href=”{% url blog.urls.month
month.0 month.1 %}”{{ month.2 }}/a/
p
{% endfor %}
154 The Python Book
Web development
01Summarise
On a normal blog we’re going to have
much longer articles. We can generate a
summary of each of these on the index page
templatelikeso:
p{{ post.post|truncatewords:3 }}/p
This automatically takes the first three words
ofthepost–ofcourse,youcanuseanynumber.
Toaddtotheprevioustutorials,we’ll
coversomeofthemoreadvanced
featuresyoucanutilisewiththe
powerofDjango
Enhanceyourblog
withextrafeatures
We’ve been building our Django blog to create
and display posts, allow people to make
comments, and filter posts by month like a
classic blog sidebar. We still have a bit of a
way to go until it looks and behaves more like a
classicblog,though.
In this tutorial, we’re going to add in
summaries, excerpts, categories and finally an
RSS feed. This allows us to look at a few things –
firstly we’ll get a better understanding of cross-
model referencing and how that works in the
admin site. We will also go through how to make
changes to the database, and how Django helps
whencreatinganSQLquery.
Finally, the RSS feed is part of a standard
feed library in Django itself. We will learn how
to import and use it to create a simple list of the
latest entries that click through to the posts. By
the end of the tutorial your Django blog will be
finallyfinished!
Resources
Pythonbase:
http://www.python.org/download/
Djangosource:https://www.
djangoproject.com/download/
03Writeanexcerpt
Towritetheexcerpt,orappendittothe
previousposts,we’llhavetoaddittotheadmin
page.Openupadmin.pyandeditthefields
sectionoftheAdminPostclasstoaddexcerpt:
fields = [‘title’, ‘pub_date’,
‘author’, ‘post’, ‘excerpt’]
02Manualexcerpt
Ifyoudon’twantanautomaticsummary,
wecanaddanexcerptfieldtoourpostmodelso
youcancraftonemanually:
excerpt = models.TextField()
Tolimitthecharactersinyourexcerpt,usea
CharFieldlikeforourauthorsection.
We’regoingtoaddsummaries,
excerptsandanRSSfeed
The Python Book 155
Web development
04Excerptorsummary
Youcanreplacethepostcontentinthe
indextemplatewiththeexcerpt,butwecankeep
itasabackupforiftheexcerptisempty:
{% if post.excerpt %} p{{ post.
excerpt }}/p {% else %} p{{ post.
post|truncatewords:3 }}/p {% endif %}
05Databaseerror
Ifyou’vedecidedtotestthechanges,
06Databasequery
TheoutputwillshowyouwhattheSQL
codeistoaddthemodelstothedatabase.We
wanttoaddtheexcerptfieldspecifically,which
shouldlooksomethinglikethis:
“excerpt” text NOT NULL
Makeanoteofit.
07Altertable
Togetintothedatabaseshellandadd
thefield,run:$ python manage.py dbshell
ThenweneedtouseanALTERTABLEquery:
08Savethechanges
We’veremovedNOTNULLaswe
alreadyhaveentriesthatwon’thaveanexcerpt,
andwanttomakeitsoanautosummarycanbe
made.Savethechangeswith:COMMIT;andthen
exittheshellwith:.quit
09Testitout
Nowwecantestouttheexcerptcode–
createanewpostoreditanexistingonetohave
anexcerpt.Ifyou’vefollowedourstepscorrectly
itshouldwork;ifnot,youmayneedtodoabitof
bugfixing.
Createand
manageparent
andchild
categoriesasa
separatefunction
oftheblog
Learnhowtoalter
thedatabaseto
createpostswith
categories,and
addthemto
otherposts
Haveautomaticsummariesormanually
craftedexcerptsforyourblogposts
Createcustom
RSSfeedsusing
built-inDjango
functions
you’llhavenoticedourwebserverhasstopped
working.Thisisbecausethereisnoexcerpt
columninourdatabase.Thereforeweneedto
addtheexcerptcolumn.Tofindouthow,run:
$ python manage.py sqlall blog
ALTER TABLE “blog_post”.
Andthenenterthecodewenoteddownlikeso:
ADD “excerpt” text;
156 The Python Book
Web development
10Categorymodel
Wecanaddamodelforblogcategories:
class Categories(models.Model): name
= models.CharField(unique=True,
max_length=200) slug = models.
SlugField(unique=True, max_length=100)
parent = models.ForeignKey(‘self’,
blank=True, null=True, related_
name=’child’) def __unicode__(self):
return (self.name)
Thisallowsforparentandchildcategories.
11Administratecategories
Wecanaddittotheadminsiteby
creatingaCategoriessectioninadmin.py:
class CategoriesAdmin(admin.
ModelAdmin): list_display = [‘name’,
‘slug’, ‘parent’] fields = [‘name’,
‘slug’, ‘parent’] admin.site.register
(Categories, CategoriesAdmin)
Beforewecanmakecategories,though,we
needtocreatethedatabasetable:
$ python manage.py syncdb
12Categorisetheposts
Similarlytowhatwedidwiththe
13Databasecategory
Likebefore,we’llfindouttheSQLneeded
toalterthetable:$ python manage.py sqlall
blogWhichforourexamplereturnsasomewhat
differentcodethanbefore:“category_id”
integer NOT NULL REFERENCES “blog_
categories” (“id”)It’sanIDwe’regetting,not
text,fromthecategoriestable.
14Altertable–part2
Againlet’senterthedatabaseshell:
python manage.py dbshellWe’llcontinue
muchlikebefore,butwiththenewcode:ALTER
TABLE “blog_post” ADD “category_id”
integer REFERENCES “blog_categories”
(“id”);Andfinally,tosave:COMMIT;
15Administratecategories–part2
Nowwecangobacktoadmin.pyandaddthenewcategoryfieldstothePostAdminmodel:
list_display = [‘title’, ‘author’, ‘pub_date’, ‘category’] fields = [‘title’,
‘pub_date’, ‘author’, ‘post’, ‘excerpt’, ‘category’]Ourpreviousblogpostswithno
categoryhavedisappeared!Tofixthis,gobacktomodels.pyandmakethischangetothePostmodel:
category = models.ForeignKey(Categories, blank=True, null=True)Sowecannowcreate
categoriesseparately,assignthemtoposts,andviewpostswithoutacategory.
16Categorydisplay
Asoururls.pyintheblogdirectorygets
allthepostfields,totheindextemplatewejust
add:pCategory: {{ post.category }}/
pAndtotheposttemplate:pCategory: {{
post_list.category }}/p
17Categorypage
Firstweneedtodefineourcategoryin
blog/urls.py.ImportCategoriesandthenadd:
def blog_categories(request, category_
id): categories = Categories.objects.
get(pk=category_id)Weneedthe
category_idtocallthecorrespondingposts.
comments,wewanttoaddaForeignKeyto
thePostmodelsowecanattributeaposttoa
category.Addthisline:category = models.
ForeignKey(Categories)
AndmoveCategoriestothetopofmodels.py.
Wecannow
createcategories
separately
The Python Book 157
Web development
24RSSURLs
Thefinalstepisaddingthefeed
URLtourls.py:url(r’^myblog/feed/$’,
BlogFeed()),Andnowyourblogisnowfully
functional.Withabitmoretweakingand
theming,youcangetitonlineandblogaway!
23RSSlinks
Weneedtodefineitem_linkforthe
feedsothatthefeeditemscanlinktotheright
place.WehavetogivethecompleteURLand
thepostIDforitwork:def item_link(self,
post): link = “http://127.0.0.1:8000/
myblog/”+str(post.pk) return link
18Categorydefinition
Finishthedefinitionbyusingtheparent_
idtofilterthecorrectPosts,thenrenderthe
response:category_posts = Post.objects.
filter(category=categories) return
render_to_response(‘blog/categories.
html’, dict(category_posts=category_
posts, categories=categories))
Againwe’recallinganewtemplatethatwe’ll
constructshortly.
19CategoryURLs
We’llcreatetheURLinurls.pyasforthe
postpage,onlyit’llgivetheslugofthecategory
insteadofanIDinthelink:url(r’^myblog/
category/(?Pcategory_idd+/$’, ‘blog.
urls.blog_categories’),
20Categorytemplate
We’llusesomethingsimilartotheIndex
andPosttemplatetocreateacategorypage
template:{% for post in category_posts
%} h2a href=/myblog/{{ post.pk }}{{
post.title }}/a/h2 {{ post.author
}} on {{ post.pub_date }} % if post.
excerpt %} p{{ post.excerpt }}/p {%
else %} p{{ post.post|truncatewords:3
}}/p {% endif %} pCategory: {{
post.category }}/p {% endfor %}
21Categoryclickthrough
Finally,let’smakethecategoriesclick
throughtotherelevantpagebychangingthe
22RSS
Djangohasabuilt-inRSSframework.
Inblog/urls.pyadd:from django.contrib.
syndication.views import Feed class
BlogFeed(Feed): title = “Blog Feed” link
= “/” def items(self): return Post.
objects.order_by(“-pub_date”) def item_
title(self, post): return post.title
categorydisplaytobe:pCategory: a
href=/myblog/category/{{ categories.pk
}}{{ post.category }}/a/pThiscango
onthecategories,postandindextemplate.
Finally,let’smakethecategories
clickthroughtotherelevantpage
Python
158 The Python Book
Use
160 Programming in Python on Raspberry Pi
Learn how to optimise for Pi
164 Turn Ras Pi into a stop motion studio
Create a stop-motion ilm
168 Send an SMS from Raspberry Pi
Combine simple Python code and Twilio
170 Build a complex LED matrix
Build and program this useful light display
withPi
“TheRaspberryPitakesthe
‘Pi’ofitsnamefromPython,
astheofficialPilanguage”
168
The Python Book 159
164 170
160 The Python Book
Use Python with Pi
LearnthebasicsofprogramminginPythonwiththeRaspberry
Pi,layingthefoundationsforallyourfutureprojects
ProgramminginPython
ontheRaspberryPi
This tutorial follows on from the one last
issue: ‘Setting up the Raspberry Pi’, where we
showed you how to prepare your SD card for
use with the Raspberry Pi. The beauty of using
an SD card image is that the operating system is
ready to go and a development environment is
alreadyconfiguredforus.
We’ll be using a lightweight integrated
development environment (IDE) called Geany
to do our Python development. Geany provides
a friendlier interface compared to text-based
editors such as nano and will make it easier to
getintotheswingofthings.
Thistutorialwillcovertopicssuchas:
• basic arithmetic
• comparison operators, for example ‘equal to’
and ‘not equal to’
• control structures, for example loops and if
statements
Bytheend,we’llhaveanadvancedversionofour
‘helloworld’application.Let’sdivestraightin…
Resources
ARaspberryPiwithall
necessaryperipherals
AnSDcardcontainingthe
latestDebianimageforthe
RaspberryPi
http://www.raspberrypi.org/downloads
It’simportanttothinkaboutdata
types.Weconvertthenumberto
decimaltomakesurethatwe
don’tloseanydecimalnumbers
duringarithmetic
Thestoppingconditionforawhileloop
hastobesatisfiedatsomepointinthe
code;otherwisetheloopwillneverend!
It’sgoodpracticetodescribe
whattheprogram’spurposeisat
thetopofthefile.Thiswillhelp
yououtwhenworkingonlarger
projectswithmultiplefiles
Theprintfunctioncanonlyacceptstring
datatypes,soweneedtoconvertany
variableswithanumberdatatypetoastring
beforewecanprintthemtothescreen
The Python Book 161
Use Python with Pi
01Stayingorganised
We don’t want to have messy folders on
our new Pi, so let’s go to the file manager and
organise ourselves. Open the file manager by
clicking the icon next to the menu icon on the
bottom left of the screen. Create a new folder
by right-clicking and selecting NewFolder, then
type a name and click OK. We created a folder
called Python, and inside that created a folder
calledHelloWorldv2.
02StartingGeany
Start Geany by going to the LXDE menu
and going to Programs. From here, select Geany.
Once you’re in the Geany interface, create a new
Python file from a template by selecting ‘New
(with template)main.py’. Delete everything in
this template apart from the first line: #!/usr/
bin/env python. This line is important because it
means you can run the code from the command
line and the Bash shell will know to open it with
thePythoninterpreter.
03Savingyourwork
It’s always a good idea to keep saving
your work with Ctrl+S as you program, because
it would be a shame to lose anything you’ve been
working on. To save your file for the first time,
either press Ctrl+S or go to the File menu and
select Save. Give the file a sensible name and
save it in the tidy folder structure you created
before. It’s a good habit to be well organised
when programming, because it makes things
much easier when your projects become bigger
andmorecomplicated.
04Settingitup
Having detailed comments in your
code is important because it allows you to note
down things you find confusing and document
complex procedures. Ifanother programmerhas
to work with your code in the future, they’ll be
extremely grateful. Start by adding a comment
with a description of what the program will do
and your name. All comment lines start with a
hash (#) and are not interpreted as code by the
Python interpreter. We import the sys library
so we can use the sys.exit function to close the
program later on. We also import everything
from the decimal library because we want to
makeuseofthedecimaltype.
05Variables
A variable is data that is stored in
memory and can be accessed via a name. Our
program is going to start by asking for your
first name, store that in a variable and then
print out a welcome message. We’re going to
add a comment that explains this and create
a variable called firstName. Notice how we’ve
capitalised the first letter of the second word to
make it easier to read. We want the firstName
variable to hold the value returned by a function
called raw_input, that will ask the user for input.
The question is passed into the print function
within brackets, and because this is a string
it is enclosed within quotation marks. A string
type is basically a collection of characters. Note
the extra space we’ve added after the colon
because the user types their input straight after
thisquestion.
06Printingamessage
Now that we have a value in firstName,
we need to output a welcome message to the
screen. We print to the screen in Python using
the print function. The print function is followed
by a pair of brackets which enclose the values
to print. When using the addition operator
with strings, they are joined together. Note
how firstName doesn’t need to be enclosed by
quotation marks because it is the name of a
variable. If it was enclosed in quotation marks,
thetextfirstNamewouldbeoutput.Wefinishoff
by adding a ‘n’ character (new line character) to
ouroutputtoleaveoneblanklinebeforewestart
ournextexample.
Whenusingtheadditionoperator
withstrings,theyarejoinedtogether
162 The Python Book
Use Python with Pi
07Fixingasmallissue
The Debian image that we’re currently
using has a small misconfiguration issue in
Geany. You’ll know if you have this problem by
trying to run your program with either the F5
key or going to the Build menu and selecting
Execute. If the issue is present then nothing
will happen and you’ll see a message saying
‘Could not find terminal: xterm’. Not to worry, it’s
easy to fix. Go to the Edit menu and then select
Preferences. Go to the Tools tab and change the
valueforTerminalfromxtermtolxterminal.
08Testingourprogram
Now we’ve done that part, why not test
it? It’s worth noting that you have to save before
running the program, or anything you’ve done
since you last saved won’t be interpreted by
Python. Run the program by pressing the F5 key.
Input your name by typing it and then pressing
the Enter key. Once you have done this, you’ll see
a welcome message. If the program exits with
thecode0theneverythingwasrunsuccessfully.
PressEntertoclosetheterminal.
09Workingwithnumbers
We’re going to ask the user for a number
by basically repeating the first couple of lines
we did. Once the user gives us a number, we’ll
halve, square and double it. The raw_input
function returns the value that the user input
as a string. A string is a text-based value so
we can’t perform arithmetic on it. The integer
type in Python can only store whole numbers
whereas the decimal type can store numbers
with decimals. We’re going to do something
called a type cast, which basically converts a
10Performingarithmetic
The main arithmetic operators in Python
are + - / *, the latter two being divide and
multiply respectively. We’ve created three new
variablescallednumberHalved,numberDoubled
and numberSquared. Notice that we don’t need
to specify that they should be decimal because
Python gives a type to its variables from the
type of their initial value. The number variable
is a decimal type, so all values returned from
performing arithmetic on that number will also
beofadecimaltype.
11Printingournumbers
Now that we have performed our
arithmetic, we need to print the results using
the print function. The print function only
accepts string values passed to it. This means
that we need to convert each decimal value to
a string using the str() function before they can
be printed. We’re using a print statement with
nothing between the quotation marks to print
one blank line. This works because the print
function always adds a new line at the end of
its output unless told otherwise, so printing an
emptystringjustprintsanewline.
value with one type to another type. We’re going
to convert our number string to a decimal value
because it’s likely that decimals will be involved
if we are halving numbers. If the number was of
anintegertype,anydecimalvalueswouldsimply
be cut off the end, without any rounding. This is
calledtruncation.
Savebeforerunningtheprogram,
oranythingyou’vedonesinceyoulast
savedwon’tbeinterpreted
Theprint
functiononly
acceptsstring
values,soconvert
eachdecimal
valuetoastring
The Python Book 163
Use Python with Pi
12InputvalidationwithWhileloops
andIfstatements
To demonstrate a while loop and if statements,
we will output a question to the user that
requires a yes or no answer. We’re going to ask
them if they want to continue – and for this we
require either a lower-case ‘yes’, or a lower-
case ‘no’. A while loop is a loop that runs until a
condition is met. In this case, we will create a
variable called yesOrNo and the while loop will
run while yesOrNo is false. The yesOrNo variable
will be a Boolean type that can be either True or
False. The variable will be initialised with a value
ofFalse,orthewhileloopwillnotrun.
A while loop has the format ‘while [condition]:’
– where any code that is part of the while loop
needs to be indented in the lines below the
colon. Any code that is not indented will not
be part of the while loop. This is the same for
an if statement. The condition is checked with
the comparison operator ‘==’. A single ‘=’ is an
assignment operator whereas a double equals
is a comparison operator. Another common
comparison operator is ‘!=’ – which means ‘not
equalto’.
We create a variable called ‘result’, which
holds the result of the question, do you want to
continue? We then check this result is valid with
an if statement. Notice the ‘or’ operator which
allows two conditions to be tested. If the user
inputs a correct value then we set yesOrNo to
True, which stops the while loop on the next run.
Otherwise, we output an error message and
the while loop will run again. The user can use
the Ctrl+C command at the terminal to exit the
programatanytime.
13Continueorexit?
Next we will deal with the result
that was stored during the while loop with if
statements. If the user typed ‘yes’ then we
will print ‘Continuing’. Otherwise, we will print
‘Exiting’ and then call the sys.exit function. You
don’t have to do anything else for the program
to continue because it will simply carry on if the
sys.exit function wasn’t called. This code also
shows that the newline character n can be
used anywhere in a string, not just in separate
quotationmarkslikeabove.
14Loopswithnumbers
We’ll be using a while loop that uses a
number and a = (less than or equal to) operator
as its stopping condition. The while loop will be
used to increment the number by 1, printing the
change on each loop until the stopping condition
is met. The count variable allows us to know
exactly how many times we have been through
thewhileloop.
15Incrementingnumberswithaloop
The while loop will run until the count is
6, meaning that it will run for a total of 5 times
because the count begins at 1. On each run, the
while loop increments the number variable and
then prints what is being added to the original
number, followed by the result. Finally, the count
isincremented.
16Finishingoff
The final step is to print that the
program is exiting. This is the last line and we
don’t have to do anything else because Python
simply finishes when there are no more lines
tointerpret.
17Admireyourwork
Now that we’ve finished coding, save any
changes you have made and run your program
withtheF5key.
Thecount
variableletsus
knowexactlyhow
manytimeswe
havebeenthrough
thewhileloop
Use Python with Pi
Build your own animation studio by usingyour
RaspberryPi asastop-motioncamera
TurnyourRaspberryPi
intoastop-motionstudio
What have you done with your Raspberry
Pi camera lately? While it gives us plenty of
new ways to use the Pi, unless you’ve got your
computer set up as a security webcam or you’re
particularly a fan of time-lapse photography, the
chancesarethatyou’veoverlookedthePicamera
moduleforawhile.
If you’re a fan of animation or you simply want
to extend the possibilities of the module, why not
build a stop-motion camera? By using Python
and an external button to capture images,
the Raspberry Pi can be the perfect tool for
animators.
Better still, you can go beyond animating toys
or bits of LEGO and go old school by mounting
the Pi on a rostrum and creating a cartoon. Even
if you can’t buy or build one, you can mount the
stopmotionPicamerawithasmartphonemount
forstability.
01Mountyourstop-motionPi
camera
Before you get started, think about the type of
animation you’re going to be capturing. If you’re
using the traditional top-down method, as used
by classic cartoon animators, then you’ll need a
rostrumtomounttheRaspberryPi.
Alternatively, you may be animating
something on a desk, table or perhaps the
BelowOurhome-made
antennamaylookalittle
rougharoundtheedges,
butitworksgreat!
02Findsomewheretoshoot
For your first attempts at shooting
a stop-motion video, you should use a wide
and uncluttered space. This might be a desk,
a kitchen work surface or even the floor, but it
should be a hard and flat area in most cases
(unless you have need for a bumpy carpeted
environment for your video) to aid with the
creationofyourstop-motionfilm.
As time progresses and your skill develops,
other surfaces can prove useful alternatives, but
keepitsimplefornowandstickwithflatsurfaces
while you get to grips with the art form using the
RaspberryPistop-motioncamera.
03ConnectthePicameramodule
Next you’ll need to connect the Pi
camera module to your Raspberry Pi. All models
have the necessary connector, although where
it is found on the device will depend on the
versionofyourRaspberryPi.
floor, but you’ll need your Pi camera mounted
in a similar way, looking across rather than
down.
Various options are available, such as
smartphone tripods and dashboard mounts.
Most of these should be suitable for securely
mounting your Raspberry Pi.
Resources
Harddrive
OSMC:
osmc.tv/
Homenetwork
AnotherLinuxcomputer,lessthan
eightyearsold
Use Python with Pi
The Python Book 165
The Model A has the Pi-camera connector next to the Ethernet port, as
does the Model B. On the B+ and the Raspberry Pi 2, the connector is in a
similar position, but it’s a little further from the Ethernet port between the
audio-outandHDMIports.
Connecting the camera module can be tricky. Begin with taking your Pi
out of its case or remove the top where possible and disconnect all cables.
Takeprecautionsbeforeremovingthedevicefromitsantistaticbag,asthe
cameramoduleisverysensitivetostaticelectricity.
On the Pi, lift the plastic catch on the connector and slot the camera
module flex into place with the shiny contacts facing away from the
Ethernet port. Once the flex is fully slotted in, push the plastic catch back
intoplace.
sudo apt-get install python-picamera python3-
picamera
sudo idle3
In the Python editor, open FileNew Window and enter the code below,
setting the camera.vflip and camera.hflip as True or False as required.
Save (perhaps as ‘camflip.py’), then press F5 to run the script and view the
correctlyoutputtedimage.
Tosavetime,however,youmighttryrotatingthepositionof
yourcameraorPicameramodule!
import picamera
from time import sleep
with picamera.PiCamera() as camera:
camera.vflip = True
camera.hflip = True
camera.start_preview()
sleep(3)
camera.capture(‘
/home/pi/image2.jpg’)
camera.stop_preview()
04TestyourPicameramodule
After connecting the Pi camera, check that it works by booting the
Raspberry Pi (we’re assuming you’re running Raspbian) and entering this in
thecommandline:
sudo raspi-config
With the keyboard arrows, move down to option five, ‘Enable Camera’, and
tap Enter. In the following screen, hit Enter again to enable the camera
and exit. If you’re not already signed into the GUI, do so now (if you’re in the
command line interface, enter startx to launch the desktop view). Open
theterminalandenter:
raspistill -o image1.jpg
YoucanreviewtheresultingimageinyourHomedirectory.
LeftConsidertheangle
you’llbeshootingfrom
asyouaresettingup
RightWiththecamera
module,ensure the
shinysidefacesaway
fromtheEthernetport
05Straightenouttheimage
With the Pi camera up and running, you may notice that it’s
outputting the image with the axes flipped. We can fix this using Python, so
opentheterminalandenter:
06Setupthebreadboardandbutton
We have two ways to add a button to the Raspberry Pi, but before
proceeding,ensureyouhaveswitchedthecomputeroffanddisconnectedit
fromthemains.Youshouldalsodisconnectanycablesandhardware.
The simplest method of adding a button is to employ a solder-free
breadboard and a single-state pushbutton. Connect the button to the
breadboard with two male-to-female wires running to GPIO pins GND and
17.WithascriptdesignedtodetectactionfromthebuttonontheGPIO,each
frameofyouranimationcanbecapturedwithasinglebuttonpush.
FileSilo.co.uk
166 The Python Book
Use Python with Pi
09Useanappinstead
Don’t fancy using the script? Try this stop-motion
application. Begin by installing the raspicam-extras package
thatincludestheUB4LdrivesforthePi:
wget http://www.linux-projects.org/listing/uv4l_
repo/lrkey.asc  sudo apt-key add ./lrkey.asc
sudo sh -c ‘echo “deb http://www.linux-projects.
org/listing/uv4l_repo/raspbian/ wheezy main”  /
etc/apt/sources.list’
sudo apt-get update
sudo apt-get install uv4l uv4l-raspicam uv4l-
raspicam-extras
Withthatdone,enter:
sudo apt-get install stopmotion
Launch with the stopmotion command to open a GUI with a
live camera for you to line up each shot. This is a more elegant
solutionandcapturedimagescanbestitchedtogetherusingthe
‘Numberofimages’sliderandthecamerabuttonaboveit.
08Stitchtogetheryourstop-motionanimation
The collected images can be cycled through relatively
quickly using a special picture viewing app, but for a true
animationyouwillneedtocompilethemintoonesinglefile.Inthe
terminal,installffmpeg:
sudo apt-get install ffmpeg
After installing, you can then convert your images into a video
clip,asfollows:
ffmpeg -y -f image2 -i /home/pi/Desktop/stop-
motion/frame%03d.jpg -r 24 -vcodec libx264 -profile
high -preset slow /home/pi/Desktop/stop-motion/
animation.mp4
Withthisfilecreated,openwiththecommand:
omxplayer animation.mp4
Thevideowillthenbeplayedinfull-screenmode.
Don’twanttobuild
yourownrostrum?Why
botherwhenacamera
tripodcanbepositioned
asneededandother
items,likesmartphone
suctionholdersand
grips,canbeemployed
toholdyourRaspberryPi
caseandcameramodule
inplace?
Fortop-downanimation,
suction-padsmartphone
holders(availablefor
under£10)thatusea
stickygelforastronger
gripareperfectfor
holdingyourstop-motion
Picameraandattaching
toaflatsurfaceabove
theanimationsubject.
Tripodsand
suction
holders
07Codeforstopmotion
Once satisfied with the results of your Pi
camera,it’stimetoturnitintoastop-motioncamera.
The first step is to type up the code shown below,
which will capture an image of your subject and save
it into a folder called ‘Stop motion’. Each image is
numbered sequentially and they can all be stitched
together once your animation is complete. Save the
codeasanimation.py:
import picamera
from RPi import GPIO
button = 17
GPIO.setmode(GPIO.BCM)
GPIO.setup(button, GPIO.IN, GPIO.PUD_UP)
with picamera.PiCamera() as camera:
camera.start_preview()
frame = 1
while True:
GPIO.wait_for_edge(button, GPIO.
FALLING)
camera.capture(‘
/home/pi/animation/
frame%03d.
jpg’ % frame)
frame += 1
camera.stop_preview()
Then,inanewterminalwindow,enterthefollowing:
sudo python3 animation.py
Press the button to capture each frame, moving the
subjectasneeded.Whenyou’realldone,hitCtrl+Cto
terminatethescript.
Don’twanttobuildyourown
rostrum?Whybotherwhena
cameratripodcanbepositioned
asneeded?
The Python Book 167
Use Python with Pi
10Putitalltogether
Now you have the camera set up, a device for keeping it steady
(whether a DIY rostrum or a tripod), and you’ve constructed a button or
plan to capture each frame via SSH. Your stop-motion Raspberry Pi
camera is finally ready!
By now you’re probably aching to get started, so with your stop-motion
Pi camera ready to use (and close to a power supply), it’s time to start
building your film set. While this might simply be an empty table top,
there might equally be a few props you would like to include.
11Storyboardyourshoot
It’seasytogettiedupwiththeideaofcreatingastop-motioncamera
andforgetallaboutasubjectandhowitwillact.
Youcanavoidanyproblemsherebytakingthetimetocarefullyplanwhat
willhappeninyourfilm:yourstory.Remember,eachsecondofthevideowill
require26frames!
The best way to plan at this level is to simply write up an outline, but
beyondthisyoumayprefertostoryboardinsteadbymakingpencilsketches
tohelpyouprogressthestory.
12Castyourstop-motionshoot
You’ll also need a good idea of what your subject will be; this means
who or what you’re going to be using the stop-motion camera to capture
frames of. Typically, amateur stop-motion films make use of household
objects,toysandchild’splayclay.
The beauty of this kind of animation is that you can use almost anything
that you can get your hands on, from a cup and saucer to an Action Man, as
long as you have a way to support the subject(s) in the positions you wish
themtotakethroughout.
13Stop-motionwithtoys
Ifyoucasttoysasyourstop-motionstars,youwillgetamuchbetter
resultfromsomethingthatisbuilttostanduponitsownthantoysthattend
tositorfallover.
LEGO sets and Minifigs appear in many stop-motion productions on
YouTube.Thisiswithgoodreason,asthey’rereallyeasytoplaceinadesired
position. The construction element of the bricks is also a major attraction.
Another popular option is Transformers toys. These are both good places to
start,butyoushouldaimtodevelopyourownapproachovertime.
14Peopleinstop-motionfilms
It isn’t only inanimate objects that you can include in stop-motion
films. People can feature too! Pop videos such as Peter Gabriel’s 1985
hit Sledgehammer have taken advantage of stop motion (that video was
produced by Aardman Animations, the eventual creators of Wallace and
Gromit)andthetechniquecanbeusedonhumanstocreatesurrealeffects.
Ifyou want your subject to be moving arounda room too, they can appear to
15MakeyourownWallaceandGromit
Known as ‘claymation’, the practice of animating lumps of clay has
been a popular form of animation for years in the UK, but there’s more to
it than just clay. These forms, whether they’re cheese-loving old men or
remarkablycleverdogs,haveawireskeletonthatisusedtokeepmovement
inthedesiredposition.
This makes it much easier to capture the frames efficiently, but for the
best results you should also have several versions of the same figures
available. This is just in case one gets deformed and damaged during
production!
17Takeyourstop-motionstudiotothenextlevel
At the risk of encouraging you to become the next Ivor Wood (creator
of The Wombles, Paddington and Postman Pat, among others), it is possible
to use the Raspberry Pi’s camera module for ambitious projects as well as
smallones.Afterall,thisdevicephotographsinhighresolutionsothereisno
reasonnottoadoptthissetupandincorporateitintoaworkingstop-motion
studiowithaminiatureset.
Sharing your work through YouTube is a great idea too, especially as it will
makeitsimpletoaddasoundtrackusingYouTube’sbrowser-basededitor.
16Fromstopmotiontotimelapse
Similar to stop motion, time lapse is a technique that automatically
captures images on a preset timer. We can use a Python script to control
this, saving the captures in a directory and using ffmpeg to compile them
intoafilm.
However, what you may not want for this project is a mains cable trailing
all over, especially if you’re attempting to capture the movement of the stars
at night or nature activity. We suggest employing a Pi-compatible battery
pack to make your time-lapse Pi camera truly mobile, using SSH to run the
scriptremotely:
import time
import picamera
VIDEO_DAYS = 1
FRAMES_PER_HOUR = 60
FRAMES = FRAMES_PER_HOUR * 24 * VIDEO_DAYS
def capture_frame(frame):
with picamera.PiCamera() as cam:
time.sleep(2)
cam.capture(‘
/home/pi/Desktop/frame%03d.
jpg’ % frame)
# Capture the images
for frame in range(FRAMES):
# Note the time before the capture
start = time.
time()
capture_frame(frame)
# Wait for the next capture. Note that we take into
# account the length of time it took to capture the
# image when calculating the delay
time.sleep(
int(60 * 60 / FRAMES_PER_HOUR) - (time.
time() - start)
)
AboveHere’sthestopmotionprograminaction–it’sasimpleenoughGUItoget
yourheadaroundandgivesyouanicepreviewwindow
befloatingorgliding.Theresultscanbestrange,butusefulifyouknowwhat
youwant.
Use Python with Pi
Create a program that combinesTwilioand simplePython
codeto enableyouto sendanSMS(textmessage)from your
Pitoamobilephone
SendanSMSfromyour
RaspberryPi
Text messaging, or SMS (Short Message
Service), has become a staple of everyday
communication. What began life as a 40 pence
message service is now offered by most tariff
providers as an unlimited service. Twilio, a cloud
communications company, enables you to send
SMS messages for free from your Raspberry Pi to a
mobile phone using just six lines of code.
01SetupyourTwilioaccount
The first step of this project is to register
for a Twilio account and Twilio number. This is free
and will enable you to send an SMS to a registered,
verified phone. Once signed up, you will receive a
verification code via SMS to the registered phone.
When prompted, enter this onto the Twilio site to
authenticate your account and phone. Go to twilio.
com/try-twilioandcreateyouraccount.
02Registerandverifymobilenumbers
Your Twilio account is a trial account (unless
you pay the upgrade fee), which means you can only
send and receive communications from a validated
phone number. Enter the phone number of the
mobile that you want to verify, ensuring that you
select the correct country code. Twilio will text you
a verification code. Enter this code into the website
formandpresssubmit.
LeftWiththismethod,
youcouldgetyour
Pitodropyouatext
whenitfinishes
runningascript
Resources
RaspberryPi
Twilioaccount
168 The Python Book
Use Python with Pi
05Twilioauthentication
Now you are ready to create the SMS program that
will send the text message to your mobile phone. Open your
Python editor and import the Twilio REST libraries (line one,
below). Next, add your AccountSid and Auth Token, replacing
theXwithyours,asyouwillfindonyourdashboard:
from twilio.rest import TwilioRestClient
account_sid = “XXXXXXXXXXXXXXXXXXXXX”
# Enter Yours
auth_token = “XXXXXXXXXXXXXXXXXXXXX”
# Enter Yours
client = TwilioRestClient(account_sid, auth_
token)
03Thedashboard
Once registered and logged in, visit the dashboard
page,whichwilldisplayyourAccountSidandyourAuthToken.
These are both required to use the Twilio REST. Keep these
secure and private, but be sure to make a note of them as you
willneedthemforyourPythonprogramlater.
04Installthesoftware
Boot up your Raspberry Pi and connect it to the
Internet. Before you install the Twilio software, it is worth
updating and upgrading your Pi. In the LX Terminal, type sudo
apt-get update, then sudo apt-get upgrade. Once complete,
type sudo easy_install twilio or sudo pip install twilio to
install the software. (If you need to install pip, type sudo apt-
get install python-pip python-dev, press Enter, then type
sudopipinstall-Upip.)
06Createyourmessage
You will probably want to be able to change your
text messages rather than send the same one. Create a new
variable in your program called message. This will prompt you
toenterthephrasethatyouwanttosendtothemobilephone.
Whentheprogramruns,thisisthemessagethatwillbesent:
message = raw_input(“Please enter your message”)
RESTstandsfor
Representational
StateTransfer.(It
issometimesspelt
“ReST”.)Itrelieson
astateless,client-
server,cacheable
communications
protocol–andin
virtuallyallcases,the
HTTPprotocolisused.
RESTisanarchitecture
stylefordesigning
networkedapplications.
REST
09OtherAPIandcodes
Twilio provides a wide range of API codes and
reference documents to create other communication
programs, such as making phone calls, recording a call, and
retrieving data including caller IDs and call duration. The
API also complements a wide range of languages, including
Ruby,PHP,JavaandNode.js(twilio.com/api).
08Sendthemessage
Now send your message. The code below is not
required, but useful to indicate your message has been sent.
Add the lines and save your program. Ensure your Raspberry
Pi is connected to the Internet and that your mobile is on,
then run your program. You have just texted from your
RaspberryPi!
print message.sid
print “Your message is being sent”
print “Check your phone!”
07Addyournumbers
To send the message, you need to add the code line
below and your two phone numbers. The first number is your
mobile phone number, which is registered and validated with
Twilio (Step 2). The second number is your Twilio account
number, which can be retrieved from your dashboard page
under‘CalltheSandboxnumber’.ChangetheSandboxnumber
toyourcountrylocationandremembertoaddtheinternational
countrycode.
message = client.messages.
create(to=“+44YOURMOBNUMBER”
,
from_=“+44YOURTWILIONUMBER”
, body=message)
AboveYouwillbeabletofindyourAccountSidandyourAuthTokenontheTwiliodashboard
Twilioenablesyoutosend
SMSmessagesforfree
The Python Book 169
Codeon
FileSilo
170 The Python Book
i
LEDMatrixdisplaysystemsfinduseeverywherefromgaudy
kebabshopstoimpressivesteampunk-styledsystems
BuildacomplexLEDmatrix
Driving LEDs in an efficient fashion is a science of its own.
The common availability of single-board computers has put
the necessary technology within reach of everyone.
When dealing with LED displays, two different systems
must be considered. We will focus on traditional matrix-
based systems made up of one or more LEDs. Their
affordable nature makes them ideally suited to classic display
applications: they communicate currency prices, provide
stock-brokers with updates from the trading floor and have
even been used as basic displays for primitive oscilloscopes.
Finally, we will also provide you with an overview of
electronic basics. This tutorial is a bit more advanced than the
ones we usually run in this section of the magazine, and it’s
also worth noting that we’re going to be programming with C
rather than Python. Follow along using the code listing annos.
01ThinkaboutLEDs
Standalone LEDs are primitive – they light up once
current flows through them. Driving a few LEDs is as easy as
connecting them to GPIO pins along with a resistor. Sadly, this
method becomes wasteful once more than a few of them get
involved–driving16diodestiesup16pins.
02Arrangeyourdiodes
Methods were devised to reduce the number of pins
needed. Matrix-based systems are resilient to individual
diode failures, and provide a pin-to-LED ratio of n=(n/2)^2. The
following steps assume a 16x16 LED matrix which is made
up according to Figure A. Since LEDs permit current in only
one direction, you can enable a single LED by bringing the
correspondingpinshighandlow.
Resources
Breadboardwires
16x16LEDMatrix
2x74HC238
2x74HC244
16x220OhmResistor
Sourcecode:FileSilo.co.uk
The Python Book 171
Use Python with Pi
04Separateconcerns
Chip two goes by the name of 74HC244, which is
described as an octal buffer with tri-state capability. Tri-State
outputs can physically disconnect themselves from the bus
line. This permits you to tie their outputs together without
fear of short circuits. As long as all but one chip are in tri-state
mode, no current can flow between high and low output pins.
03HarnesstheMUX
Our LED module has a total of 32 inputs, which
overwhelms older versions of the RPi. The first way to restrict
their number comes in the shape of the 74HC238, a component
described as a 3-to-8 line decoder/demultiplexer. Its function is
describedintheFigureBimageonthenextpage.
AboveTheextendedversionofthisschematicisinsideFileSilo.co.uk
– justsigninanddownload
05Roundthemup
Four GPIO pins control the enabled display ‘line’.
Three pins configure the address which is to be emitted,
while the signal emitted from the fourth pin is connected to
the activity inputs. This ensures that but one IC is active. The
74HC244 ensures that but one of the two groups is active.
06Configurethepins
We used a library from Hussam Al-Hertani’s
Hertaville blog (hertaville.com/2014/07/07/rpimmapgpio).
The first step involves setting output functions. As the GPIOs
are set to outputs, the tri-state feature might connect the
internal state to the output pins of the IC. This could lead to
internal shorting if the output is not turned off.
#include “€œmmapGpio.h”
#include unistd.h
#include stdio.h
GHº

QH3,1$
GHº

QH3,1$
GHº

QH3,1$
GHº

QH3,1$
GHº

QH3,16
GHº

QH3,16
GHº

QH3,1'
GHº

QH3,1'
GHº

QH3,1'
GHº

QH3,1'
GHº

QH3,1'
GHº

QH3,1'
GHº

QH3,1'
GHº

QH3,1'
void VHW$GGUHVV(unsigned FKDU _which, mmapGpio*BZKHUH
{
if(_which
{
BZKHUH-ZULWH3LQ+LJK3,1$
}
else
{
BZKHUH-ZULWH3LQ/RZ3,1$
}
if(_which
{
BZKHUH-ZULWH3LQ+LJK3,1$
}
else
{
BZKHUH-ZULWH3LQ/RZ3,1$
}
if(_which
{
BZKHUH-ZULWH3LQ+LJK3,1$
}
else
{
BZKHUH-ZULWH3LQ/RZ3,1$

The Python Book_ The ultimate guide to coding with Python ( PDFDrive ).pdf

  • 1.
    OVER2HOURS OFVIDEOTUTORIALS Over20 incredible projects The ultimate guideto coding with Python Learn to use Python t1SPHSBNHBNFTt(FUDSFBUJWFXJUI1J Python The NEW
  • 3.
    Welcometo Pythonisanincrediblyversatile,expansivelanguagewhich,duetoitssimilarityto everydaylanguage,issurprisinglyeasytolearnevenforinexperiencedprogrammers.It hasseenahugeincreaseinpopularitysincethereleaseandriseoftheRaspberryPi,for whichPythonistheofficiallyrecognisedprogramminglanguage.Inthisnewedition ofThePythonBook,you’llfindplentyofcreativeprojectstohelpyougettogripswith thecombinationofyourRaspberryPiandPython’spowerfulfunctionality,pluslotsof tutorialsthatfocusonPython’seffectivenessawayfromtheRaspberryPi.You’lllearn allabouthowtocodewithPythonfromastandingstart,withourcomprehensive masterclass,thengoontocompletetutorialsthatwillconsolidateyourskillsandhelp youtobecomefluentinthelanguage.You’lllearnhowtomakePythonworkforyou withtutorialsoncodingwithDjango,Flask,Pygameandevenmoreusefulthird-party frameworks.GetreadytobecomeatruePythonexpertwiththewealthofinformation containedwithinthesepages. Python The
  • 5.
    Imagine Publishing Ltd RichmondHouse 33 Richmond Hill Bournemouth Dorset BH2 6EZ +44 (0) 1202 586200 Website: www.imagine-publishing.co.uk Twitter: @Books_Imagine Facebook: www.facebook.com/ImagineBookazines Publishing Director Aaron Asadi Head of Design Ross Andrews Production Editor Alex Hoskins Senior Art Editor Greg Whitaker Assistant Designer Steve Dacombe Printed by William Gibbons, 26 Planetary Road, Willenhall, West Midlands, WV13 3XT Distributed in the UK, Eire the Rest of the World by Marketforce, 5 Churchill Place, Canary Wharf, London, E14 5HU Tel 0203 787 9060 www.marketforce.co.uk Distributed in Australia by Network Services (a division of Bauer Media Group), Level 21 Civic Tower, 66-68 Goulburn Street, Sydney, New South Wales 2000, Australia Tel +61 2 8667 5288 Disclaimer The publisher cannot accept responsibility for any unsolicited material lost or damaged in the post. All text and layout is the copyright of Imagine Publishing Ltd. Nothing in this bookazine may be reproduced in whole or part without the written permission of the publisher. All copyrights are recognised and used specifically for the purpose of criticism and review. Although the bookazine has endeavoured to ensure all information is correct at time of print, prices and availability may change. This bookazine is fully independent and not affiliated in any way with the companies mentioned herein. The Python Book Second Edition © 2016 Imagine Publishing Ltd ISBN 9781785462382 bookazine series Part of the Python The
  • 6.
    Python essentials 26 Coderock, paper, scissors Put basic coding into action 32 Program a hangman game Use Python to make the classic game 38 Play poker dice Test your luck and your coding 44 Create a graphical interface Add interface to your projects 50 Bring graphics to games Add images to simple games 56 Build an app for Android Make your own app with Kivy 62 Making web apps Use Python to create online apps 66 50 Python tips Essential knowledge for Python users Contents 8 Get started with Python Master the basics the right way Work with Python 74 Python for professionals Use your coding skills at work 82 Make extensions for XBMC Enhance XBMC with this tutorial 88 Scientific computing Get to grips with NumPy 92 Instant messaging Get chatting using Python 98 Replace your shell Use Python for your primary shell 102 Python for system admins How Python helps system administration 6 The Python Book 82 16 50 essential commands The commands you need to know 92 116 124 Create with Python 108 Build tic-tac-toe with Kivy Program noughts and crosses 112 Create two-step authentication Use Twilio for safe authentication 116 Program a Space Invaders clone Make the basic Pivaders game 120 Add animation and sound Enhance your Pivaders game 124 Make a visual novel Program a book-style game 128 Pygame Zero Turn your ideas into games Python The
  • 7.
    “Gettogrips withPython,and masterhighly versatilecode” Web development 136 Developwith Python Why Python is perfect for the web 142 Create dynamic templates Use Jinja, Flask and more 146 Build your own blog Begin developing your blog 150 Deliver content to your blog Add content to your site 154 Enhance your blog Complete your blog with add-ons Use Python with Pi 160 Programming in Python on Raspberry Pi Learn how to optimise for Pi 164 Turn Raspberry Pi into a stop-motion studio Learn how to optimise for Pi 168 Send SMS with Pi Send text messages for free 170 Build an LED Matrix Use Pi to control light sequences 50 Python tips 66 The Python Book 7 170 136
  • 8.
    8 The PythonBook Get started with Python P ython is a great programming language for both beginners and experts. It is designed with code readability in mind, making it an excellent choice for beginners who are still getting used to various programming concepts. The language is popular and has plenty of libraries available, allowing programmers to get a lot done with relatively little code. YoucanmakeallkindsofapplicationsinPython: youcouldusethePygameframeworktowrite simple2Dgames,youcouldusetheGTK librariestocreateawindowedapplication,oryoucould trysomethingalittlemoreambitiouslikeanappsuch ascreatingoneusingPython’sBluetoothandInput librariestocapturetheinputfromaUSBkeyboardand relaytheinputeventstoanAndroidphone. Forthisguide we’regoingtobeusingPython2.xsince thatistheversionthatismostlikelytobeinstalledonyour Linuxdistribution. Inthefollowingtutorials,you’lllearnhowtocreate populargamesusingPythonprogramming.We’llalso showyouhowtoaddsoundandAItothesegames. Alwayswantedtohaveagoat programming?Nomoreexcuses, becausePythonistheperfectwaytogetstarted!
  • 9.
    The Python Book9 Get started with Python
  • 10.
    Get started withPython 10 The Python Book TIP Ifyouwereusingagraphical editorsuchasgedit,then youwouldonlyhavetodo thelaststepofmakingthe fileexecutable.Youshould onlyhavetomarkthefileas executableonce.Youcan freelyeditthefileonceit isexecutable. Interpretedvscompiledlanguages AninterpretedlanguagesuchasPythonisonewherethesource codeisconvertedtomachinecodeandthenexecutedeachtimethe programruns.ThisisdiferentfromacompiledlanguagesuchasC, wherethesourcecodeisonlyconvertedtomachinecodeonce–the resultingmachinecodeisthenexecutedeachtimetheprogramruns. HelloWorld Let’s get stuck in, and what better way than with the programmer’s best friend, the ‘Hello World’ application! Start by opening a terminal. Its current working directory will be your home directory. It’s probably a good idea to make a directory for the files we’ll be creating in this tutorial, rather than having them loose in your home directory. You can create a directory called Python using the command mkdir Python. You’ll then want to change into that directory using the command cdPython. The next step is to create an empty file using the command ‘touch’ followed by the filename. Our expert used the command touch hello_world.py. The final and most important part of setting up the file is making it executable. This allows us to run code inside the hello_world.py file. We do this with the command chmod +x hello_world.py. Now that we have our file set up, we can go ahead and open it up in nano, or any text editor of your choice. Gedit is a great editor with syntax highlighting support that should be available on any distribution. You’ll be able to install it using your package manager ifyou don’t have it already. [liam@liam-laptop ~]$ mkdir Python [liam@liam-laptop ~]$ cd Python/ [liam@liam-laptop Python]$ touch hello_world.py [liam@liam-laptop Python]$ chmod +x hello_world.py [liam@liam-laptop Python]$ nano hello_world.py Our Hello World program is very simple, it only needs two lines. The first line begins with a ‘shebang’ (the symbol #! – also known as a hashbang) followed by the path to the Python interpreter. The program loader uses this line to work out what the rest of the lines need to be interpreted with. If you’re running this in an IDE likeIDLE, you don’t necessarily needto do this. The code that is actually read by the Python interpreter is only a single line. We’re passing the value Hello World to the print function by placing it in brackets immediately after we’ve called the print function. Hello World is enclosed in quotation marks to indicate that it is a literal value and should not be interpreted as source code. As expected, the print function in Python prints any value that getspassedto it from the console. You can save the changes you’ve just made to the file in nano using the key combination Ctrl+O, followed by Enter. Use Ctrl+X toexit nano. #!/usr/bin/env python2 print(“Hello World”) You can run the Hello World program by prefixing its filename with ./ – in this case you’d type: ./hello_world.py. [liam@liam-laptop Python]$ ./hello_world.py Hello World Variablesanddatatypes A variable is a name in source code that is associated with an area in memory that you can use to store data, which is then called upon throughout the code. The data can be one of many types,including: Integer Stores whole numbers Float Storesdecimalnumbers Boolean Can havea value ofTrueorFalse String Storesacollectionofcharacters.“Hello World”is astring As well as these main data types, there are sequence types (technically, a string is a sequence type but is so commonly used we’ve classed it asa main data type): List Containsacollectionofdatainaspecificorder Tuple Contains a collection immutable data in a specific order A tuple would be used for something like a co-ordinate, containing an x and y value stored as a single variable, whereas a list is typically used to store larger collections. The data stored in a tuple is immutable because you aren’t able to change values of individual elements in a tuple. However, you can do so in a list. It will also be useful to know about Python’s dictionary type. A dictionary is a mapped data type. It stores data in key-value pairs. This means that you access values stored in the dictionary using that value’s corresponding key, which is different to how you would do it with a list. In a list, you would access an element of the list using that element’s index (a number representing the element’s position in the list). Let’s work on a program we can use to demonstrate how to use variables and different data types. It’s worth noting at this point that you don’t always have to specify data types in Python. Feel free to create this file in any editor you like. Everything will work just fine as long as you remember to make the file executable. We’re going to call ours variables.py. “A variable is a name in source code that is associated with an area in memory that you can use to store data”
  • 11.
    Get started withPython The following line creates an integer variable called hello_int with the # value of 21. Notice how it doesn’t need to go in quotation marks You could also create the same list in the following way We might as well create a dictionary while we’re at it. Notice how we’ve aligned the colons below to make the code tidy The same principal is true of Boolean values We create a tuple in the following way And a list in this way #!/usr/bin/env python2 # We create a variable by writing the name of the variable we want followed # by an equals sign, which is followed by the value we want to store in the # variable. For example, the following line creates a variable called # hello_str, containing the string Hello World. hello_str =“Hello World” hello_int = 21 hello_bool = True hello_tuple = (21, 32) hello_list = [“Hello,”,“this”,“is”,“a”,“list”] # This list now contains 5 strings. Notice that there are no spaces # between these strings so if you were to join them up so make a sentence # you’d have to add a space between each element. hello_list = list() hello_list.append(“Hello,”) hello_list.append(“this”) hello_list.append(“is”) hello_list.append(“a”) hello_list.append(“list”) # The irst line creates an empty list and the following lines use the append # function of the list type to add elements to the list. This way of using a # list isn’t really very useful when working with strings you know of in # advance, but it can be useful when working with dynamic data such as user # input. This list will overwrite the irst list without any warning as we # are using the same variable name as the previous list. hello_dict = {“irst_name”:“Liam”, “last_name” :“Fraser”, “eye_colour”:“Blue”} # Let’s access some elements inside our collections # We’ll start by changing the value of the last string in our hello_list and # add an exclamation mark to the end. The“list”string is the 5th element # in the list. However, indexes in Python are zero-based, which means the # irst element has an index of 0. print(hello_list[4]) hello_list[4] +=“!” # The above line is the same as hello_list[4] = hello_list[4] +“!” print(hello_list[4]) TIP Atthispoint,it’sworth explainingthatanytextin aPythonfilethatfollows a#characterwillbe ignoredbytheinterpreter. Thisissoyoucanwrite commentsinyourcode. Notice that there will now be two exclamation marks when we print the element “Any text in a Python file that follows a # character will be ignored”
  • 12.
    12 The PythonBook Get started with Python Indentationindetail Aspreviouslymentioned,thelevelofindentation dictateswhichstatementablockofcodebelongs to.IndentationismandatoryinPython,whereasin otherlanguages,setsofbracesareusedtoorganise codeblocks.Forthisreason,itisessentialthatyou useaconsistentindentationstyle.Fourspaces aretypicallyusedtorepresentasinglelevelof indentationinPython.Youcanusetabs,buttabsare notwelldefined,especiallyifyouhappentoopena fileinmorethanoneeditor. Moreabouta Pythonlist APythonlistissimilartoan arrayinotherlanguages.A list(ortuple)inPythoncan containdataofmultiple types,whichisnotusually thecasewitharraysinother languages.Forthisreason, werecommendthatyou onlystoredataofthesame typeinalist.Thisshould almostalwaysbethecase anywayduetothenatureof thewaydatainalistwould beprocessed. print(str(hello_tuple[0])) # We can’t change the value of those elements like we just did with the list # Notice the use of the str function above to explicitly convert the integer # value inside the tuple to a string before printing it. print(hello_dict[“irst_name”] +““ + hello_dict[“last_name”] +“ has“ + hello_dict[“eye_colour”] +“ eyes.”) print(“{0} {1} has {2} eyes.”.format(hello_dict[“irst_name”], hello_dict[“last_name”], hello_dict[“eye_colour”])) Remember that tuples are immutable, although we can access the elements of them like so Let’s create a sentence using the data in our hello_dict A tidier way of doing this would be to use Python’s string formatter Controlstructures In programming, a control structure is any kind of statement that can change the path that the code execution takes. For example, a control structure that decided to end the program if a number was less than 5 would looksomething like this: #!/usr/bin/env python2 import sys # Used for the sys.exit function int_condition = 5 if int_condition 6: sys.exit(“int_condition must be = 6”) else: print(“int_condition was = 6 - continuing”) The path that the code takes will depend on the value of the integer int_condition. The code in the ‘if’ block will only be executed if the condition is true. The import statement is used to load the Python system library; the latter provides the exit function, allowing you to exit the program, printing an error message. Notice that indentation (in this case four spaces per indent) is used to indicate which statement a block of code belongs to. ‘If’ statements are probably the most commonly used control structures. Othercontrol structures include: •For statements, which allow you to iterate over items in collections, or to repeat a piece of code a certain number oftimes; • While statements, a loop that continues while the condition istrue. We’re going to write a program that accepts user input from the user to demonstrate how control structures work. We’re calling it construct.py. The ‘for’ loop is using a local copy of the current value, which means any changes inside the loop won’t make any changes affecting the list. On the other hand however, the ‘while’ loop is directly accessing elements in the list, so you could change the list there should you want to do so. We will talk about variable scope in some more detail later on. The output from the above program is as follows: [liam@liam-laptop Python]$ ./construct.py How many integers? acd You must enter an integer [liam@liam-laptop Python]$ ./construct.py How many integers? 3 Please enter integer 1: t You must enter an integer Please enter integer 1: 5 Please enter integer 2: 2 Please enter integer 3: 6 Using a for loop 5 2 6 Using a while loop 5 2 6 “The ‘for‘ loop uses a local copy, so changes in the loop won’t affect the list”
  • 13.
    The Python Book13 The number of integers we want in the list A list to store the integers These are used to keep track of how many integers we currently have If the above succeeds then isint will be set to true: isint =True #!/usr/bin/env python2 # We’re going to write a program that will ask the user to input an arbitrary # number of integers, store them in a collection, and then demonstrate how the # collection would be used with various control structures. import sys # Used for the sys.exit function target_int = raw_input(“How many integers?“) # By now, the variable target_int contains a string representation of # whatever the user typed.We need to try and convert that to an integer but # be ready to # deal with the error if it’s not. Otherwise the program will # crash. try: target_int = int(target_int) except ValueError: sys.exit(“You must enter an integer”) ints = list() count = 0 # Keep asking for an integer until we have the required number while count target_int: new_int = raw_input(“Please enter integer {0}:“.format(count + 1)) isint = False try: new_int = int(new_int) except: print(“You must enter an integer”) # Only carry on if we have an integer. If not, we’ll loop again # Notice below I use ==, which is diferent from =.The single equals is an # assignment operator whereas the double equals is a comparison operator. if isint == True: # Add the integer to the collection ints.append(new_int) # Increment the count by 1 count += 1 print(“Using a for loop”) for value in ints: print(str(value)) By now, the user has given up or we have a list illed with integers. We can loop through these in a couple of ways. The irst is with a for loop
  • 14.
    14 The PythonBook Get started with Python #!/usr/bin/env python2 # Below is a function called modify_string, which accepts a variable # that will be called original in the scope of the function. Anything # indented with 4 spaces under the function definition is in the # scope. def modify_string(original): original += “ that has been modified.” # At the moment, only the local copy of this string has been modified def modify_string_return(original): original += “ that has been modified.” # However, we can return our local copy to the caller. The function # ends as soon as the return statement is used, regardless of where it # is in the function. return original test_string = “This is a test string” modify_string(test_string) print(test_string) test_string = modify_string_return(test_string) print(test_string) # The function’s return value is stored in the variable test string, # overwriting the original and therefore changing the value that is # printed. We are now outside of the scope of the modify_ string function, as we have reduced the level of indentation The test string won’t be changed in this code However, we can call the function like this TIP Youcandefinedefaults forvariablesifyouwant tobeabletocallthe functionwithoutpassing anyvariablesthroughat all.Youdothisbyputting anequalssignafter thevariablename.For example,youcando: def modify_string (original=” Default String”) # Or with a while loop: print(“Using a while loop”) # We already have the total above, but knowing the len function is very # useful. total = len(ints) count = 0 while count total: print(str(ints[count])) count += 1 Functionsandvariablescope Functions are used in programming to break processes down into smaller chunks. This often makes code much easier to read. Functions can also be reusable if designed in a certain way. Functions can have variables passed to them. Variables in Python are always passed by value, which means that a copy of the variable is passed to the function that is only valid in the scope of the function. Any changes made to the original variable inside the function will be discarded. However, functions can also return values, so this isn’t an issue. Functions are defined with the keyword def, followed by the name of the function. Any variables that can be passed through are put in brackets following the function’s name. Multiple variables are separated by commas. The names given to the variables in these brackets are the ones that they will have in the scope of the function, regardless of what the variable that’s passed to the function is called. Let’s see this in action. Theoutputfromtheprogramoppositeisasfollows: “Functions are used in programming to break processes down in”
  • 15.
    Get started withPython [liam@liam-laptop Python]$ ./functions_and_scope.py This is a test string This is a test string that has been modified. Scope is an important thing to get the hang of, otherwise it can get you into some bad habits. Let’s write a quick program to demonstrate this. It’s going to have a Boolean variable called cont, which will decide if a number will be assigned to a variable in an if statement. However, the variable hasn’t been defined anywhere apart from in the scope of the if statement. We’ll finish off by trying to print the variable. #!/usr/bin/env python2 cont = False if cont: var = 1234 print(var) In the section of code above, Python will convert the integer to a string before printing it. However, it’s always a good idea to explicitly convert things to strings – especially when it comes to concatenating strings together. If you try to use the + operator on a string and an integer, there will be an error because it’s not explicitly clear what needs to happen. The + operator would usually add two integers together. Having said that, Python’s string formatter that we demonstrated earlier is a cleaner way of doing that. Can you see the problem? Var has only been defined in the scope of the if statement. This means that we get a very nasty error when we try to access var. [liam@liam-laptop Python]$ ./scope.py Traceback (most recent call last): File “./scope.py”, line 8, in module print var NameError: name ‘var’ is not defined If cont is set to True, then the variable will be created and we can access it just fine. However, this is a bad way to do things. The correct way is to initialise the variable outside of the scope of the if statement. #!/usr/bin/env python2 cont = False var = 0 if cont: var = 1234 if var != 0: print(var) The variable var is defined in a wider scope than the if statement, and can still be accessed by the if statement. Any changes made to var inside the if statement are changing the variable defined in the larger scope. This example doesn’t really do anything useful apart from illustrate the potential problem, but the worst-case scenario has gone from the program crashing to printing a zero. Even that doesn’t happen because we’ve added an extra construct to test the value of var before printing it. Codingstyle It’s worth taking a little time to talk about coding style. It’s simple to write tidy code. The key is consistency. For example, you should always name your variables in the same manner. It doesn’t matter if you want to use camelCase or use underscores as we have. One crucial thing is to use self-documenting identifiers for variables. You shouldn’t have to guess what a variable does. The other thing that goes with this is to always comment your code. This will help anyone else who reads your code, and yourself in the future. It’s also useful to put a brief summary at the top of a code file describing what the application does, or a part of the application ifit’smadeup ofmultiplefiles. Summary This article should have introduced you to the basics of programming in Python. Hopefully you are getting used to the syntax, indentation and general look and feel of a Python program. The next step is to learn how to come up with a problem that you want to solve, and break it down into small enough steps that you can implement in a programming language. Google, or any other search engine, is very helpful. If you are stuck with anything, or have an error message you can’t work out how to fix, stick it into Google and you should be a lot closer to solving your problem. For example, if we Google ‘play mp3 file with python’, the first link takes us to a Stack Overflow thread with a bunch of useful replies. Don’t be afraid to get stuck in – the real fun of programming is solving problemsone manageablechunk ata time. Happyprogramming! Comparisonoperators The common comparison operators available in Python include: strictlylessthan = lessthanorequal strictlygreaterthan = greaterthanorequal == equal != notequal
  • 16.
    Python has amassive environment of extra modules that can provide functionality in hundreds of different disciplines. However, every programming language has a core set of functionality that everyone should know in order to get useful work done. Python is no different in this regard. Here, we will look at 50 commands that we consider to be essential to programming in Python. Others may pick a slightly differentset,butthislistcontainsthebestofthebest. We will cover all of the basic commands, from importing extra modules at the beginning of a program to returning values to the calling environment at the end. We will also be looking at some commands that are useful in learning about the current session within Python, like the current list of variables that have been definedandhowmemoryisbeingused. BecausethePythonenvironmentinvolvesusingalot of extra modules, we will also look at a few commands that are strictly outside of Python. We will see how to install external modules and how to manage multiple environments for different development projects. Sincethisisgoingtobealistofcommands,thereisthe assumption that you already know the basics of how to use loops and conditional structures. This piece is designed to help you remember commands that you know you’ve seen before, and hopefully introduce you toafewthatyoumaynothaveseenyet. Although we’ve done our best to pack everything you could ever need into 50 tips, Python is such an expansive language that some commands will have been left out. Make some time to learn about the ones thatwedidn’tcoverhere,onceyou’vemasteredthese. PYTHON ESSENTIAL COMMANDS Pythonisknownasavery denselanguage,withlotsof modulescapableofdoing almostanything.Here, wewilllookatthecore essentialsthateveryone needstoknow 16 The Python Book
  • 17.
    50 Python commands Importingmodules The strength of Python is its ability to be extended through modules. The first step in many programs is to import those modules that you need. The simplest import statement is to just call ‘import modulename’. In this case, those functions and objects provided are not in the general namespace. You need to call them using the complete name (modulename.methodname). You can shorten the ‘modulename’ part with the command ‘import modulename as mn’. You can skip this issue completely with the command ‘from modulename import *’ to import everything from the given module. Then you can call those provided capabilities directly. If you only need a few of the provided items, you can import them selectively by replacing the ‘*’ with the methodorobjectnames. Evaluating code Sometimes, you may have chunks of code that are put together programmatically. If these piecesof codeare put togetherasa string, you can execute the result with the command ‘eval(“code_string”)’. Any syntax errors within the code string are reported as exceptions. By default, this code is executed within the current session, using the current globals and locals dictionaries. The ‘eval’ command can also take two other optional parameters, where you can provide a different set of dictionaries for the globals and locals. If there is only one additional parameter, then it is assumed to be a globals dictionary. You can optionally hand in a code objectthatiscreatedwiththecompilecommand insteadofthecodestring.Thereturnvalueofthis commandisNone. An enhanced shell The default interactive shell is provided through the command ‘python’, but is rather limited. An enhanced shell is provided by the command ‘ipython’. It provides a lot of extra functionality to the code developer. A thorough history system is available, giving you access to not only commands from the current session, but also from previous sessions. There are also magic commands that provide enhanced ways of interacting with the current Python session. For more complex interactions, you can create and use macros. You can also easily peek into the memory of the Python session and decompile Python code. You can even create profiles that allow you to handle initialisationstepsthatyoumayneedtodoeverytime youuseiPython. Installing new modules While most of the commands we are looking at are Python commands that are to be executed within a Python session, there are a few essential commands that need to be executed outside of Python. The first of these is pip. Installingamoduleinvolvesdownloadingthesourcecode,andcompilinganyincluded externalcode.Luckily,thereisarepositoryofhundredsofPythonmodulesavailable at http://pypi.python.org. Instead of doing everything manually, you can install a new module by using the command ‘pip install modulename’. This command will also do a dependency check and install any missing modules before installing the one you requested. You may need administrator rights if you want this new module installed in the global library for your computer. On a Linux machine, you would simplyrunthepipcommandwithsudo.Otherwise,youcaninstallittoyour personallibrarydirectorybyaddingthecommandlineoption‘—user’. “Every programming language out there has a core set of functionality that everyone should know in order to get useful work done. Python is no different” 01 Executing a script Importing a module does run the code within the module file, but does it through the module maintenance code within the Python engine. This maintenance code also deals with running initialising code. If you only wish to take a Python script and execute the raw code within the current session, you can use the ‘execfile(“filename.py”)’ command, where the main option is a string containing the Python file to load and execute. By default, any definitions are loaded into the locals and globals of the current session. You can optionally include two extra parameters the execfile command. These two options are both dictionaries, one for a different set of locals and a different set of globals. If you only hand in one dictionary, it is assumed to be a globals dictionary. The return valueofthiscommandisNone. 04 Reloading modules When a module is first imported, any initialisation functions are run at that time. This may involve creating data objects, or initiating connections. But, this is only done the first time within a given session. Importing the same module again won’t re-execute any of the initialisation code. If you want to have this codere-run,youneedtousethereloadcommand.Theformatis‘reload(modulename)’.Somethingtokeep in mind is thatthedictionary from thepreviousimportisn’tdumped, but only written over. This means that any definitions that have changed between the import and the reload are updated correctly. But if you delete a definition, the old one will stick around and still be accessible. There may be other side effects, so alwaysusewithcaution. 02 06 05 03 The Python Book 17
  • 18.
    18 The PythonBook Reductions In many calculations, one of the computations you need to do is a reduction operation. This is where you take some list of values and reduce it down to a single value. In Python, you can use the command ‘reduce(function, iterable)’ to apply the reduction function to each pair of elements in the list. For example, if you apply the summation reduction operation to the list of the first five integers, you would get the result ((((1+2)+3)+4)+5). You can optionally add a third parameter to act as an initialisation term. It is loaded before any elements from the iterable, and is returned as a default if the iterable is actually empty. You can use a lambda function as the function parameter to reduce to keep your code as tight as possible. In this case, remember thatitshouldonlytaketwoinputparameters. Virtualenvs Because of the potential complexity of the Python environment, it is sometimes best to setupacleanenvironmentwithinwhichtoinstall only the modules you need for a given project. In this case, you can use the virtualenv command to initialise such an environment. If you create a directory named ‘ENV’, you can create a new environment with the command ‘virtualenv ENV’. This will create the subdirectories bin, lib and include, and populate them with an initial environment. You can then start using this new environment by sourcing the script ‘ENV/bin/ activate’, which will change several environment variables, such as the PATH. When you are done, you can source the script ‘ENV/bin/deactivate’ to reset your shell’s environment back to its previous condition. In this way, you can have environments that only have the modules you needforagivensetoftasks. Mapping functions A common task that is done in modern programs is to map a given computation to an entire list of elements. Python provides the command ‘map()’ to do just this. Map returns a list of the results of the function applied to each element of an iterable object. Map can actually take more than one function and more than one iterable object. If it is given more than one function, then a list of tuples is returned, with each element of the tuple containing the results from each function. If there is more than one iterable handed in, then map assumes that the functions take more than one input parameter, so it will take them from the given iterables. This has the implicit assumption that the iterables are all of the same size, and that they are all necessary as parametersforthegivenfunction. Loops While not strictly commands, everyone needs to know how to deal with loops. The two main types of loops are a fixed number of iterations loop (for) and a conditional loop (while). In a for loop, you iterate over some sequence of values, pulling them off the list one at a time andputtingtheminatemporaryvariable.Youcontinueuntil either you have processed every element or you have hit a breakcommand.Inawhileloop,youcontinuegoingthrough the loop as long as some test expression evaluates to True. While loops can also be exited early by using the break command, you can also skip pieces of code within either loop by using a continue command to selectively stop this currentiterationandmoveontothenextone. “While not strictly commands, everyone needs to know how to deal with loops. The two main types of loops are a fixed number of iterations loop (for) and a conditional loop (while)” 12 Asserting values At some point, we all need to debug some piece of code we are trying to write. One of the tools useful in this is the concept of an assertion. The assert command takes a Python expression and checks to see if it is true. If so, then execution continues as normal. If it is not true, then an AssertionError is raised. This way, you can check to make sure that invariants within your code stay invariant. By doing so, you can check assumptions made within your code. You can optionally include a second parameter to the assert command. This second parameter is Python expression that is executed if the assertion fails. Usually, this is some type of detailed error message that gets printed out. Or, you may want to include cleanup code that tries torecoverfromthefailedassertion. 07 Filtering Where the command map returns a result for every element in an iterable, filter only returns a result if the function returns a True value. This means that you can create a new list of elements where only the elements that satisfy some condition are used. As an example, if your function checked that the values were numbers between 0 and 10, then it would create a new list with no negative numbers and no numbers above 10. This could be accomplished with a for loop, but this method is much cleaner. If the function provided to filter is ‘None’, then it is assumed to be the identity function. This means that only those elements that evaluate to True are returned as part of the new list. There are iterable versions of filter available in the itertools module. 11 09 08 10
  • 19.
    The Python Book19 50 Python commands Enumerating Sometimes,weneedtolabeltheelements that reside within an iterable object with their indices so that they can be processed at some later point. You could do this by explicitly looping through each of the elements and building an enumerated list. The enumerate command does this in one line. It takes an iterable object and creates a list of tuples as the result. Each tuple has the 0-based index of the element, along with the element itself. You can optionally start the indexing from some other value by including an optional second parameter. As an example, you could enumerate a list of names with the command ‘list(enumerate(names, start=1))’. In this example, we decided to start the indexing at 1 insteadof0. Casting Variables in Python don’t have any type information, and so can be used to store any type of object. The actual data, however, is of one type or another. Many operators, like addition, assume that the input values are of the same type. Very often, the operator you are using is smart enough to make the type of conversion that is needed. If you have the need to explicitly convert your data from one type to another, there are a class of functions that can be used to do this conversion process. The ones you are most likely to use is ‘abs’, ‘bin’, ‘bool’, ‘chr’, ‘complex’, ‘float’, ‘hex’, ‘int’, ‘long’, ‘oct’, and ‘str’. For the number-based conversion functions, there is an order of precedence where some types are a subset of others. For example, integers are “lower” than floats. When converting up, no changes in the ultimate value should happen. When converting down, usually some amount of information is lost. For example, when converting from float to integer, Python truncates the number towardszero. 14 15 How true is a list? In some cases, you may have collected a number of elements within a list that can be evaluated to True or False. For example, maybe you ran a number of possibilities through your computation and have created a list of which ones passed. You can use the command ‘any(list)’ to check to see whether any of the elements within your list are true. If you need to check whether all of the elements are True, you can use the command ‘all(list)’. Both of these commands return a True if the relevant condition is satisfied, and a False if not. They do behave differently if the iterable object is empty, however. The command ‘all’ returns a True if the iterable is empty, whereas the command ‘any’ returns a False when givenanyemptyiterable. 13 What is this? Everything in Python is an object. You can check to see what class this object is an instance of with the command ‘isinstance(object, class)’. This command returns a Boolean value. 16 Is it a subclass? The command ‘issubclass(class1, class2)’ checks to see if class1 is a subclass of class2. If class1 and class2 are the same, this is returned as True. 17 Global objects You can get a dictionary of the global symbol table for the current module with the command ‘globals()’. 18 Local objects You can access an updated dictionary of the current local symbol table by using the command ‘locals()’. 19 Variables The command ‘vars(dict)’ returns writeable elements for an object. If you use ‘vars()’, it behaves like ‘locals()’. 20 Making a global A list of names can be interpreted as globals for the entire code block with the command ‘global names’. 21 Nonlocals In Python 3.X, you can access names from the nearest enclosing scope with the command ‘nonlocal names’ and bind it to the local scope. 22 Raising an exception When you identify an error condition, you can use the ‘raise’ command to throw up an exception. You can include an exception type and a value. 23 Dealing with an exception Exceptions can be caught in a try-except construction. If the code in the try block raises an exception, the code in the except block gets run. 24 Static methods You can create a statis method, similar to that in Java or C++, with the command ‘staticmethod(function_name)’. 25
  • 20.
    20 The PythonBook Printing The most direct way of getting output to the user is with the print command. This will send text out to the console window. If you are using version 2.X of Python, there are a couple of ways you can use the print command. The most common way had been simply call it as ‘print “Some text”’. You can also use print with the same syntax that you would use for any other function. So, the above example would look like ‘print(“Some text”)’. This is the only form available in version 3.X. If you use the function syntax, you can add extra parameters that give you finer control over this output. For example, you can give the parameter ‘file=myfile.txt’ and get the output from the print command being dumped into the given text file. It also will accept any object that has some string representation available. With modules The ‘with’ command provides the ability to wrap a code block with methods defined by a context manager. This can help clean up code and make it easier to read what a given piece of code is supposed to be doing months later. A classic example of using ‘with’ is when dealing with files. You could use something like ‘with open(“myfile. txt”, “r”) as f:’. This will open the file and prepare it for reading. You can then read the file in the code block with ‘data=f.read()’. The best part of doing this is that the file will automatically be closed when the code block is exited, regardless of the reason. So, even if the code block throws an exception, you don’t need to worry about closing the file as part of your exception handler. If you have a more complicated ‘with’ example, you can create a context manager class to helpout. 32 31 Memoryview Sometimes, you need to access the raw data of some object, usually as a buffer of bytes. You can copy this data and put it into a bytearray, for example. But this means that you will be using extra memory, and this might not be an option for large objects. The command ‘memoryview(object_name)’ wraps the object handed in to the command and provides an interface to the raw bytes. It gives access to these bytes an element at a time. In many cases, elements are the size of one byte. But, depending on the object details, you could end up with elements that are larger than that. You can find out the size of an element in bytes with the property ‘itemsize’. Once you have your memory view created, you can accesstheindividualelementsasyouwouldgetelementsfromalist(mem_view[1],forexample). 33 “A classic example of using ‘with’ is when dealing with files. The best part of doing this is that the file will automatically be closed when the code block is exited, regardless of the reason” Ranges You may need a list of numbers, maybe in a ‘for’ loop. The command ‘range()’ can create an iterable list of integers. With one parameter, it goes from 0 to the given number. You can provide an optional start number, as well as a step size. Negative numbers count down. Xranges One problem with ranges is that all of the elements need to be calculated up front and stored in memory. The command ‘xrange()’ takes the same parameters and provides the same result, but only calculates the next element as it is needed. 27 Iterators Iteration is a very Pythonic way of doing things. For objects which are not intrinsically iterable, you can use the command ‘iter(object_ name)’ to essentially wrap your object and provide an iterable interface for use with other functions and operators. 28 Sorted lists You can use the command ‘sorted(list1)’ to sort the elements of a list. You can give it a custom comparison function, and for more complex elements you can include a key function that pulls out a ranking property from each element for comparison. 29 Summing items Above, we saw the general reduction function reduce. A specific type of reduction operation, summation, is common enough to warrant the inclusion of a special case, the command ‘sum(iterable_object)’. You can include a second parameter here that will provide a starting value. 30 26
  • 21.
    The Python Book21 50 Python commands Threads You can do multiple threads of execution within Python. The ‘thread()’ command can create a new thread of execution for you. It follows the same techniques as those for POSIX threads. When you first create a thread, you need to hand in a function name, along with whatever parameters said function needs. One thing to keep in mind is that these threads behave just like POSIX threads. This means that almost everythingistheresponsibilityoftheprogrammer.You needtohandlemutexlocks(withthemethods‘acquire’ and ‘release’), as well as create the original mutexes with the method ‘allocate_lock’. When you are done, youneedto‘exit’thethreadtoensurethatitisproperly cleaned up and no resources get left behind. You also have fine-grained control over the threads, being able tosetthingslikethestacksizefornewthreads. Shelving data While pickling allows you save data and reload it, sometimes you need more structured object permanence in your Python session. With the shelve module, you can create an object store where essentially anything that can be pickled can be stored there. The backend of the storage on the drive can be handled by one of several systems, such as dbm or gdbm.Onceyouhaveopenedashelf,youcanreadand writetoitusingkeyvaluepairs.Whenyouaredone,you need to be sure to explicitly close the shelf so that it is synchronised with the file storage. Because of the way the data may be stored in the backing database, it is besttonotopentherelevantfilesoutsideoftheshelve module in Python. You can also open the shelf with writeback set to True. If so, you can explicitly call the syncmethodtowriteoutcachedchanges. Pickling data There are a few different ways of serialisingmemorywhenyouneedtocheckpoint results to disk. One of these is called pickling. Pickle is actually a complete module, not just a single command. To store data on to the hard drive, you can use the dump method to write the data out. When you want to reload the same data at some other point in the future, you can use the load method to read the data in and unpickle it. One issue with pickle is its speed, or lack of it. There is a second module, cPickle, that provides the same basic functionality. But, since it is written in C, it can be as much as 1000 times faster.Onethingtobeawareofisthatpickledoes not store any class information for an object, but only its instance information. This means that when you unpickle the object, it may have different methods and attributes if the class definitionhaschangedintheinterim. Weak references You sometimes need to have a reference to an object, but still be able to destroy it if needed. A weak reference is one which can be ignored by the garbage collector. If the only references left to n object are weak references, then the garbage collector is allowed to destroy that object and reclaim the space for other uses. This is useful in cases where you have caches or mappings of large datasets that don’t necessarily have to stay in memory. If an object that is weakly referenced ends up being destroyed and you try to access it, it will appear as a None. You can test for this condition and then reload the data if you decide that this is a necessarystep. Yielding In many cases, a function may need to yield the context of execution to some other function.Thisisthecasewithgenerators.Thepreferred method for a generator is that it will only calculate the next value when it is requested through the method ‘next()’. The command ‘yield’ saves the current state of the generator function, and return execution control to the calling function. In this way, the saved state of the generator is reloaded and the generator picks up where it left off in order to calculate the next requested value.Inthisway,youonlyneedtohaveenoughmemory available to store the bare minimum to calculate the next needed value, rather than having to store all of the possiblevaluesinmemoryallatonce. 39 38 Files When dealing with files, you need to create a file object to interact with it. The file command takes a string with the file name and location and creates a file object instance. You can then call the file object methods like ‘open’, ‘read’ and ‘close’, to get data out of the file. If you are doing file processing, you can also use the ‘readline’ method. When opening a file, there is an explicit ‘open()’ command to simplify the process. It takes a string with the file name, and an optional parameter that is a string which defines the mode. The default is to open the file as read-only (‘r’). You can also open it for writing (‘w’) and appending (‘a’).Afteropeningthefile,afileobjectisreturnedsothatyoucanfurtherinteractwithit.Youcanthenread it,writetoit,andfinallycloseit. 34 37 36 35
  • 22.
    22 The PythonBook 50 Python commands Slices While not truly a command, slices are too important a concept not to mention in this list of essential commands. Indexing elements in data structures, like lists, is one of the most common things done in Python. You can select a single element by giving a single index value. More interestingly, you can select a range of elements by giving a start index and an end index, separated by a colon. This gets returned as a new list that you can save in a new variable name. You can even change the step size, allowing you to skip some number of elements. So, you could grab every odd element from the list ‘a’ with the slice ‘a[1::2]’. This starts at index 1, continues until the end, and steps through the index values 2 at a time. Slices can be given negative index values. If you do, then they start from the end of the listandcountbackwards. 43 Inputting data Sometimes, you need to collect input from an end user. The command ‘input()’ can take a prompt string to display to the user, and then wait for the user to type a response. Once theuserisdonetypingandhitstheenterkey,the text is returned to your program. If the readline module was loaded before calling input, then you will have enhanced line editing and history functionality. This command passes the text through eval first, and so may cause uncaught errors. If you have any doubts, you can use the command ‘raw_input()’ to skip this problem. This command simply returns the unchanged string inputted by the user. Again, you can use the readlinemoduletogetenhancedlineediting. 40 Comparing objects There are several ways to compare objects within Python, with several caveats. The first is that you can test two things between objects: equality and identity. If you are testing identity, you are testing to see if two names actually refer to the same instance object. This can be done with the command ‘cmp(obj1, obj2)’. You can also test this condition by using the ‘is’ keyword. For example, ‘obj1 is obj2’. If you are testing for equality, you are testing to see whether the values in the objects referred to by the two names are equal. This test is handled by the operator ‘==’, as in ‘obj1 == obj2’. Testing for equality canbecomecomplexformorecomplicatedobjects. 42 Internal variables Forpeoplecomingfromotherprogramminglanguages,thereisaconceptofhavingcertainvariables or methods be only available internally within an object. In Python, there is no such concept. All elements of an object are accessible. There is a style rule, however, that can mimic this type of behaviour. Any names that start with an underscore are expected to be treated as if they were internal names and to be kept as private to the object.Theyarenothidden,however,andthereisnoexplicitprotectionforthesevariablesormethods.Itisupto theprogrammertohonourtheintentionfromtheauthortheclassandnotalteranyoftheseinternalnames.You arefreetomakethesetypesofchangesifitbecomesnecessary,though. 41
  • 23.
    The Python Book23 50 Python commands “Python is an interpreted language, which means that the source code that you write needs to be compiled into a byte code format. This byte code then gets fed into the actual Python engine” Lambda expressions Since objects, and the names that point to them, are truly different things, you can have objects thathavenoreferencestothem.Oneexampleofthisisthelambdaexpression.Withthis,youcancreate an anonymous function. This allows you use functional programming techniques within Python. The format is the keyword ‘lambda’, followed by a parameter list, then a colon and the function code. For example,youcouldbuildyourownfunctiontosquareanumberwith‘lambdax:x*x’.Youcanthenhavea function that can programmatically create new functions and return them to the calling code. With this capability, you can create function generators to have self-modifying programs. The only limitation is thattheyarelimitedtoasingleexpression,soyoucan’tgenerateverycomplexfunctions. 44 __del__ method When an instance object is about to be destroyed, the __del__ method is called. This gives you the chance to do any kind of cleanup that may be required. This might be closing files, or disconnecting network connections. After this code is completed, the object is finally destroyed and resources are freed. 47 Return values Functions may need to return some value to the calling function. Because essentially no name has a type, this includes functions. So functions can use the ‘return’ command to return any object to the caller. 49 String concatenation We will finish with what most lists start with – string concatenation. The easiest way to build up strings is to use the ‘+’ operator. If you want to include other items, like numbers, you can use the ‘str()’ casting function to convert it to a string object. 50 Exiting your program There are two pseudo-commands available to exit from the Python interpreter: ‘exit()’ and quit()’. They both take an optional parameter which sets the exit code for the process. If you want to exit from a script, you are better off using the exit function from the sys module (‘sys.exit(exit_code)’. 48 __init__ method When you create a new class, you can include a private initialisation method that gets called when a new instance of the class is created. This method is useful when the new object instance needs some data loaded in the new object. 46 Compiling code objects Python is an interpreted language, which means that the source codethatyouwriteneedstobecompiled into a byte code format. This byte code then gets fed into the actual Python engine to step through the instructions. Within your program, you may have the need to take control over the process of converting code to byte code and running the results. Maybe you wish to build your own REPL. The command ‘compile()’ takes a string object that contains a collection of Python code, and returns an object that represents a byte code translation of this code. This new object can then be handed in to either ‘eval()’ or ‘exec()’ to be actually run. You can use the parameter ‘mode=’ to tell compile what kind of code is being compiled. The ‘single’ mode is a single statement, ‘eval’ is a single expression and ‘exec’isawholecodeblock. 45
  • 24.
    Essentials Python 26 Code rock,paper, scissors Put basic coding into action 32 Program a hangman game Use Python to make the classic game 38 Play poker dice Test your luck and your coding 44 Create a graphical interface Add interface to your projects “Gettogripswith Pythonandstart buildingonthe basicswiththese expertguides” 56 24 The Python Book 50 Bring graphics to games Add images to simple games 56 Build an app for Android Make your own app with Kivy 62 Making web apps Use Python to create online apps 66 50 Python tips Essential knowledge for Python users
  • 25.
  • 26.
    26 The PythonBook Python essentials LearnhowtodosomebasicPythoncodingbyfollowing ourbreakdownofasimplerock,paper,scissorsgame Codeagameof rock,paper,scissors This tutorial will guide you through making a rock, paper, scissors game in Python. The codeappliesthelessonsfromthemasterclass– and expands on what was included there – and doesn’trequireanyextraPythonmodulestorun, likePygame. Rock, paper, scissors is the perfect game to show off a little more about what exactly Python can do. Human input, comparisons, random selections and a whole host of loops are used in making a working version of the game. It’s also easy enough to adapt and expand as you see fit, adding rules and results, and even making a rudimentaryAIifyouwish. For this particular tutorial, we also recommend using IDLE. IDLE is a great Python IDE that is easily obtainable in most Linux distributions and is available by default on Raspbian for Raspberry Pi. It helps you by highlighting any problems there might be with your code and allows you to easily run it to make sureit’sworkingproperly. Resources Python2:www.python.org/download IDLE:www.python.org/idle AllowthePythonscript toruninaterminal, andoutsidetheIDE Humaninputintheform ofintegersisusedfor comparingmovesand, ultimately,playingthegame Usedeductionto determineoneof threeoutcomes Loopthecodeover againandstart fromthebeginning Appendtointeger variablestokeeptrack ofscoresandmore
  • 27.
    The Python Book27 Python essentials 01This section imports the extra Python functionswe’llneedforthecode–they’re still parts of the standard Python libraries, just notpartofthedefaultenvironment 02The initial rules of the game are created here.Thethreevariableswe’reusingand their relationship is defined. We also provide a variablesowecankeepscoreofthegames 03We begin the game code by defining the startofeachround.Theendofeachplay session comes back through here, whether we wanttoplayagainornot 04The game is actually contained all in here, asking for the player input, getting the computer input and passing these on to get theresults.Attheendofthat,itthenasksifyou’d liketoplayagain 05Player input is done here. We give the player information on how to play this particular version of the game and then allow their choice to be used in the next step. We also have something in place in case they enter an invalidoption 06There are a few things going on when we showtheresults.First,we’reputtingina delay to add some tension, appending a variable to some printed text, and then comparing what the player and computer did. Through an if statement, we choose what outcome to print, andhowtoupdatethescores 07We now ask for text input on whether or not someone wants to play again. Depending on their response, we go back to the start,orendthegameanddisplaytheresults
  • 28.
    28 The PythonBook Python essentials 01We need to start with the path to the Python interpreter here. This allows us to run the program inside a terminal or otherwise outside of a Python-specific IDE like IDLE. Note that we’re also using Python 2 rather than Python 3 for this particular script, which needs to be specified in the code to make sure it calls upon the correct version fromthesystem. 03We’re setting each move to a specific number so that once a selection is made by the player during the game, it will be equated to that specific variable. This makes the code slightly easier later on, as we won’t need to parse any text for this particular function.Ifyousowish,youcanaddadditional moves,andthiswillstarthere. 05Similar to the way the text names of thevariablesaredefinedandusedonly when needed, the rules are done in such a way that when comparing the results, our variables are momentarily modified. Further down in the code we’ll explain properly what’s happening, but basically after determining whether or not there’s a tie, we’ll see if the computer’s move would have lost to the player move. If the computer move equals the losing throw to the player’s move, you win. 02We’reimportingtwoextramoduleson top of the standard Python code so we can use some extra functions throughout the code. We’ll use the random module to determinewhatmovethecomputerwillthrow, and the time module to pause the running of the code at key points. The time module can also be used to utilise dates and times, either todisplaythemorotherwise. 04Here we specify the rules for the game, and the text representations of each move for the rest of the code. When called upon, our script will print the names of any of the three moves,mainlytotelltheplayerhowthecomputer moved. These names are only equated to these variables when they are needed – this way, the number assigned to each of them is maintained whileit’sneeded. 06Very simply, this creates a variable that can be used throughout the code to keep track of scores. We need to start it at zero now so that it exists, otherwise if we defined it in a function, it would only exist inside that function. The code adds a point to the computer orplayerdependingontheoutcomeoftheround, although we have no scoring for tied games in thisparticularversion. Thebreakdown There are other modules you can import with basic Python. Some of the major ones are shown to the right. There are also many more that are included as standardwith Python. Pythonmodules string Perform common string operations datetime andcalendar Other modules related to time math Advanced mathematical functions json JSON encoder and decoder pydoc Documentation generator and online help system 01 02 03 04 05 06
  • 29.
    The Python Book29 Python essentials 07Here we define the actual beginning of the code, with the function we’ve called ‘start’. It’s quite simple, printing our greeting to the player and then starting a while loop that will allow us to keep playing the game as many times as we wish. The pass statement allows the while loop tostoponcewe’vefinished,andcouldbeusedtoperformanumberofother tasksifsowished.Ifwedostopplayingthegame,thescorefunctionisthen calledupon–we’llgooverwhatthatdoeswhenwegettoit. 09We start the move function off by putting it into a while loop. The whole point of move is to obtain an integer between one and three from the player, so the while loop allows us to account for the player making an unsupported entry. Next, we are setting the player variable to be created from the player’s input with raw_input. We’ve alsoprintedinstructiontexttogoalongwithit.The‘n’we’ve used in the text adds a line break; this way, the instructions appearasalist. 10The try statement is used to clean up code and handleerrorsorotherexceptions.Weparsewhatthe player entered by turning it into an integer using int(). We use the if statement to check if it is either 1, 2, or 3 – if it is, move returns this value back up to the game function. If it throws upaValueError,weuseexcepttodonothing.Itprintsanerror message and the while loop starts again. This will happen until an acceptable move is made. 08We’ve kept the game function fairly simple so we can break down eachstepabitmoreeasilyinthecode.Thisiscalleduponfromthe start function, and first of all determines the player move by calling upon the move function below. Once that’s sorted, it sets the computer move. It uses the random module’s randint function to get an integer between one and three (1, 3). It then passes the player and computer move, stored as integers,ontotheresultfunctionwhichweusetofindtheoutcome. 07 08 09 10 Thecodeinaction
  • 30.
    30 The PythonBook Python essentials 11Theresultfunctiononlytakesthevariables playerandcomputerforthistask,whichis why we set that in result(player, computer). We’re starting off by having a countdown to the result. The printed numbers are self-explanatory, but we’ve also thrown in sleep from the time module we imported. Sleep pauses the execution of the lookupwhatthetextversionofthemoveiscalled from the names we set earlier on, and then to insertthatwhere{0}is. 13Here we’re simply calling the scores we set earlier. Using the global function allows for the variable to be changed and used outside of the variable, especially after we’ve appendedanumbertooneoftheirscores. 15Ifit’snotatie,weneedtokeepchecking, as it could still be a win or a loss. Within the else, we start another if statement. Here, we use the rules list from earlier to see if the losing move to the player’s move is the same as the computer’s. If that’s the case, we print the message saying so, and add one to the player_scorevariablefrombefore. code by the number of seconds in the brackets. We’ve put a one-second pause between counts, thenhalfasecondafterthattoshowtheresults. 12To print out what the computer threw, we’re using string.format(). The {0} in the printed text is where we’re inserting the move, which we have previously defined as numbers. Usingnames[computer],we’retellingthecodeto 14The way we’re checking the result is basicallythroughaprocessofelimination. Our first check is to see if the move the player and computer used were the same, which is the simplest part. We put it in an if statement so that if it’s true, this particular section of the code ends here.Itthenprintsourtiemessageandgoesback tothegamefunctionforthenextstep. 16If we get to this point, the player has lost. We print the losing message, give the computer a point and it immediately ends the resultfunction,returningtothegamefunction. 11 12 13 14 15 16 Thecodeinaction
  • 31.
    The Python Book31 Python essentials 17The next section of game calls upon a play_again function. Like the move function, we have human input, asking the player if they would like to play again via a text message withraw_input,withthesimple‘y/n’suggestionin anattempttoelicitanexpectedresponse. 19Ifwedon’tgetanexpectedresponse,we will assume the player does not want to play again. We’ll print a goodbye message, and that will end this function. This will also cause the game function to move onto the next section andnotrestart. 18Giving users an option of y/n like we have should expect a response in kind. The if statement checks to see if any of our defined positiveresponseshavebeenentered.AsPython doesn’t differentiate between upper or lower case, we’ve made sure that it accepts both y and Y.Ifthisisthecase,itreturnsapositiveresponse togame,whichwillstartitagain. 20Going back to the start function, after gamefinisheswemoveontotheresults. This section calls the scores, which are integers, andthenprintsthemindividuallyafterthenames of the players. This is the end of the script, as far as the player is concerned. Currently, the code won’t permanently save the scores, but you can havePythonwriteittoafiletokeepifyouwish. 21The final part allows for the script to be used in two ways. Firstly, we can execute it in the command line and it will work fine. Secondly, we can import this into another Python script, perhaps if you wanted to add it as a game to a collection. This way, it won’t execute thecodewhenbeingimported. IFalsohastheELIF(elseif)operator,whichcan be used in place of the second IF statement we employed. It’s usually used to keep code clean,butperformsthesamefunction. ELIF 17 18 19 20 21 Thecodeinaction
  • 32.
    32 The PythonBook Python essentials ThissectionimportstheextraPython functions we’ll need for the code – they’re still parts of the standard Python libraries, just not part of the defaultenvironment We’re again providing variables so we can keep score of the games played, andthey’reupdatedeachround Our very basic graphics involve ASCII art of the game’s stages, printed out aftereveryturn LearnhowtodosomemorePython codingbyfollowingourbreakdownofa simpleHangmangame Programa gameof Hangman One of the best ways to get to know Python is by building lots of simple projects so you can understand a bit more about the programming language. This time round, we’re looking at Hangman, a multi-round game relying on if and while loops and dealing with strings of text in multiple ways. We’ll be using some of the techniquesweimplementedlasttimeaswell,so wecanbuilduponthem. Hangman still doesn’t require the Pygame set of modules, but it’s a little more advanced than rock-paper-scissors. We’re playing around with a lot more variables this time. However, we’re still looking at comparisons, random selections and human input, along with splitting up a string, editing a list and even displaying rudimentary graphics. You should continue to use IDLE for these tutorials. As we’ve mentioned before, its built- in debugging tools are simple yet effective and it can be used on any Linux system, as well as the Raspberry Pi. Codelisting #!/usr/bin/env python2 from random import * player_score = 0 computer_score = 0 def hangedman(hangman): graphic = [ “”” +-------+ | | | | | ============== “””, “”” +-------+ | | | O | | | =============== “””, “”” “””, “”” +-------+ | | | O | -|- | / | =============== “””] print graphic[hangman] return Resources Python2:www.python.org/download IDLE:www.python.org/idle
  • 33.
    The Python Book33 Python essentials The actual game starts here, with a while loop to let you continually play the game until you decide otherwise, thenending the program The game rules are decided here, as well as the setup for the word and keeping track of tries and incorrectanswers Each round of the game is played here, asking for aninput,thentellingyouifyouwerecorrectornot. It prints out the graphic and changes any variables that need to be updated, especially incorrect and correctguesses After each round, the code checks if you’ve won or lostyet–thewinconditionbeingthatyouguessed theword,orlosingifyou’ve made sixguesses The human input for the game takes the letter and turns it into something the code can use. It’s verified in the previous block of code and then referred back to if you’ve entered an unsupported or already used character The same class as last time, which allows you to selectwhether or not you wishto play again Upon quitting the game, scores are given for the durationoftheplaysession.Wealsoendthescript withtheif__name__code like before def start(): print “Let’s play a game of Linux Hangman.” while game(): pass scores() def game(): dictionary = [“gnu”,”kernel”,”linux”,”mageia”,”penguin”,”ubuntu”] word = choice(dictionary) word_length = len(word) clue = word_length * [“_”] tries = 6 letters_tried = “” guesses = 0 letters_right = 0 letters_wrong = 0 global computer_score, player_score while (letters_wrong != tries) and (“”.join(clue) != word): letter=guess_letter() if len(letter)==1 and letter .isalpha(): if letters_tried.find(letter) != -1: print “You’ve already picked”, letter else: letters_tried = letters_tried + letter first_index=word.find(letter) if first_index == -1: letters_wrong +=1 print “Sorry,”,letter ,”isn’t what we’re looking for .” else: print”Congratulations,”,letter ,”is correct.” for i in range(word_length): if letter == word[i]: clue[i] = letter else: print “Choose another .” hangedman(letters_wrong) print “ “.join(clue) print “Guesses: “, letters_tried if letters_wrong == tries: print “Game Over .” print “The word was”,word computer_score += 1 break if “”.join(clue) == word: print “You Win!” print “The word was”,word player_score += 1 break return play_again() def guess_letter(): print letter = raw_input(“Take a guess at our mystery word:”) letter .strip() letter .lower() print return letter def play_again(): answer = raw_input(“Would you like to play again? y/n: “) if answer in (“y”, “Y”, “yes”, “Yes”, “Of course!”): return answer else: print “Thank you very much for playing our game. See you next time!” def scores(): global player_score, computer_score print “HIGH SCORES” print “Player: “, player_score print “Computer: “, computer_score if __name__ == ‘__main__’: start() Codehighlighting IDLEautomaticallyhighlightsthecodetomake reading your work that bit easier. It also allows youtochangethesecoloursandhighlightingin IDLE’s Preferences, in case you’re colour blind or are just used to a different colour scheme in general. Codelistingcontinued
  • 34.
    34 The PythonBook Python essentials IseeASCII Here’s a close-up of the seven stages we’ve used for Hangman’s graphics. You can change them yourself, but you need to make sure the quote marks are all in the correct place so that the art is considered a text string to be printedout. #!/usr/bin/env python2 from random import * player_score = 0 computer_score = 0 def hangedman(hangman): graphic = [ “”” +-------+ | | | | | ============== “””, “”” def start(): print “Let’s play a game of Linux Hangman.” while game(): pass scores() 01 02 03 04 05 01We begin by using this line to enter the path to the Python interpreter. This allows us to run the program inside a terminal or otherwise outside of a Python-specific IDE like IDLE. Note that we’re also using Python 2 for this particular script, as it is installed by default on most Linux systems and will thereforeensurecompatibility. 02We’re importing the ‘random’ module slightly differently this time, importing the actual names of the functions from random rather than just the module itself. This allows us to use the functions without having syntax like random.function. The asterisk imports all the functions from random, although you can switch that for specific names of any of random’s functions. We’ll be using the random functiontoselectawordfortheplayertoguess. 03Very simply, this creates a variable that can be used throughout the code to keep track of scores. We need to start it at zero now so that it exists; otherwise if we defined it in a function, it would only exist inside that function. The code adds a point to the computer or player depending on the outcome of the round. 04Our simple graphics consist of a series of ASCII hanging man stages. We’re storing these in a function as a list of separate string objects so we can call upon them by passing on the number of incorrect guesses to it. There are seven graphics in all, like in the pen-and-paper version. We also include the print command with the function, so when it’s called it will completely handle the selection and display of the hanging man, with the first one being printed after the firstletterisguessed. 05Here we define the actual beginning of the code, with the function we’ve called ‘start’. It’s quite simple, printing our greeting to the player and then starting a while loop that will allow us to keep playing the game as many times as we wish. The pass statement allows the while loop to stop once we’ve finished, and could be used to perform a number “”” +-------+ | | | | | ============== “””, “”” +-------+ | | | O | | | =============== “””, “”” +-------+ | | | O | | | | =============== “””, “”” +-------+ | O | -| | | | =============== “””, “”” +-------+ | | | O | -|- | | =============== “””, “”” +-------+ | | | O | -|- | / | =============== “””, “”” +-------+ | | | O | -|- | / | =============== “””] Therules Although we’ve moved some of the rules to the ‘game’ function this month, you can always put them back here and call upon them using the global variable, as we would do with the scores. For thewords,youcouldalsocreatea separatefileandimportthemlike therandom module.
  • 35.
    The Python Book35 Python essentials of other tasks if so wished. If we do stop playing the game, the score function is then called upon –we’ll go overwhatthatdoeswhenwegettoit. 06We have put a majority of the game code in the ‘game’ function this time around, as there’s not as much that needs to be split up. You can split it up further if you wish, using the style of code from last issue, if it would make the code cleaner for you or help you understand the building blocks a bitmore. 07The first four lines quickly set up the word for the player to guess. We’ve got a small selection of words in a list here. However, these can be imported via HTML or expanded upon. Choice is used to select a random element from the list, which comes from the random module we imported. Finally, we ascertain how long the string is of the word to guess, and then create the clue variable with a number of underscores of that length. This is used to display the wordasyoubuilditupfromguesses. 08We start to set up the rules and the individual variables to keep track of during the game. There can only be six incorrect guesses before the hanging man is fully drawn, or in our case displayed, so we set the tries variable to six. We’ll keep track of the letters through letters_tried to make sure that not only will the player know, but also the code for when def game(): dictionary = [“gnu”,”kernel”,”linux”,”mageia”,”penguin”,”ubuntu”] word = choice(dictionary) word_length = len(word) clue = word_length * [“_”] tries = 6 letters_tried = “” guesses = 0 letters_right = 0 letters_wrong = 0 global computer_score, player_score while (letters_wrong != tries) and (“”.join(clue) != word): letter=guess_letter() if len(letter)==1 and letter .isalpha(): if letters_tried.find(letter) != -1: print “You’ve already picked”, letter it’s checking against letters already played. Finally, we create empty variables for the number of guesses made, letters correct and letters incorrect, to make the code slightly easier. We also import the global scoreshere. 09We’re starting a while loop to perform the player selection and check the status of the game. This loop continues until the player wins or loses. It starts by checking if all the tries have been used up by seeing if letters_wrong is not equal to tries. As each try will only add one point to wrong, it will never go above six. It then concatenates ‘clue’ and sees if it’s the same as the word the computer selected. If both these statementsaretrue,itgoesontothenextturn. 10We call upon the function we’re using to input a letter and give it the variable ‘letter’. We check what it returns by first of all making sure it’s only a single letter, with len(letter), then by using isalpha to see if it’s one of the 26 letters of the alphabet. If these conditions are satisfied, we start a new if statement to make sure it’s a new guess, and tell the player if it’s already been chosen so they can start again. If all this is acceptable, we move on to the next section of the code to see if it’s a correct guess or not. 06 08 09 10 07 Indentations While IDLE will keep track of the indentsinthecode,ifyou’reusing atexteditortowritesomePython, you’ll have to make sure you’re using them correctly. Python is very sensitive to whether or not indents are used correctly, and it doesaid in readability aswell.
  • 36.
    36 The PythonBook Python essentials 11 12 13 14 16 15 11If it’s a new letter that we find acceptable, the first thing we do is add it to the list of letters tried. This is done simply by adding the strings together. We then use the find command to search the word string for the letter entered, which will then return a number of the placement of the letter in the string. If it doesn’t find the letter, it returns a -1 value, which we use in the next if statement to see if the first_index variable is -1. If so, it adds one to the number of letters_wrong and then prints a message to let theplayerknowthatitwasanincorrectguess. 12If we’ve got this far and the letter is not incorrect, than we can only assume it is correct. Through this simple process of elimination, we first print out a message to let theplayerknowthatthey’vebeensuccessfuland thenmakearecordofit. 13We’re going to start a small loop here so we can update the clue with the correct letter we’ve added. We use the range function to tell the code how many times we wish to iterate over the clue by using the word_length variable. We then check to see which letter in the word has been guessed correctly and change that specific part of the clue to be that letter so it can be printed out for the player to see, and for us to checkwhetherornotthegameisover. 14We end the original if statement by telling the player to choose again if they did not enter a supported input. Before we go on to the next round of choices, we print out the hanging else: letters_tried = letters_tried + letter first_index=word.find(letter) if first_index == -1: letters_wrong +=1 print “Sorry,” ,letter ,”isn’t what we’re looking for .” else: print”Congratulations,” ,letter ,”is correct.” for i in range(word_length): if letter == word[i]: clue[i] = letter else: print “Choose another .” hangedman(letters_wrong) print “ “. join(clue) print “Guesses: “, letters_tried if letters_wrong == tries: print “Game Over .” print “The word was” ,word computer_score += 1 break if “” . join(clue) == word: print “You Win!” print “The word was” ,word player_score += 1 break return play_again() man graphic as it stands, by calling the graphic in the list that corresponds to the number of incorrect guesses that have been made. We then print how the clue currently looks, with a space in between each character, and then print the numberofguessesthathavebeenmade. 15Here we check to see if the game is over again, first of all comparing the letters_wrong to the number of tries. If that’s true, we print a message that the game has endedandrevealthemysteryofthehiddenword. We increase the computer’s score and break the loop. The next loop checks to see if the full clue concatenatedisthesameastheoriginalword–if that’sthecase,weprintthewinmessage,thefull wordandaddonepointtotheplayerscorebefore breaking the loop again. This can also be done withifsandelifstoavoidusingbreaks. Continuation This code is still part of the game function we started on the previous page, so make sure your indentations are in alignment if you’renotusinganIDE.Ifyouplan to split this code up, we’d suggest starting with the word selection andresults.
  • 37.
    The Python Book37 Python essentials def guess_letter(): print letter = raw_input(“Take a guess at our mystery word:”) letter .strip() letter .lower() print return letter def play_again(): answer = raw_input(“Would you like to play again? y/n: “) if answer in (“y”, “Y”, “yes”, “Yes”, “Of course!”): return answer else: print “Thank you very much for playing our game. See you next time!” def scores(): global player_score, computer_score print “HIGH SCORES” print “Player: “, player_score print “Computer: “, computer_score if __name__ == ‘__main__’: start() 17 18 21 19 20 22 16We end the entire game function loop by calling upon return again, which we will thenpassallthewayuptothestartfunctiononce it’sfinished. 17The human input function first of all prints out a raw_input message. Once the player enters the letter, the function parses it to be used with the rest of the code. Firstly, strip is used to remove any white space from the input given, as we’ve not given it any extra parameters. We then convert it into lower-case letters, as Python will not be able to correctly compare an upper-case character with a lower-case alternative. We then print the selection for the record and return it up to the game function. 18The last part of the game function is to ask the player if they wish to try again. The play_again function takes a human input with a simple message and then analyses the input so it knows what to send back. 19Giving users an option of y/n like we have should expect a response in kind. The if statement checks to see if any of our defined positive responses have been entered. As Python doesn’t differentiate between upper or lower case, we’ve made sure it accepts both y and Y. If this is the case, it returns a positive response to game, which will start it again. 20If we don’t get an expected response, we will assume the player does not want to play again. We’ll print a goodbye message and that will end this function. This will also cause the start function to move onto the next section and not restart. 21Going all the way back to the start function, after game finishes we move onto the results. This section is quite simple – it calls the scores, which are integers, and then prints them individually after the names of the players. This is the end of the script, as far as the player is concerned. Currently, the code will not permanently save the scores, but you can have Python write it to a file to keep if you wish. 22The final part of the code allows for the script to be used in two ways. Firstly, we can execute it in the command line and it will work fine. Secondly, we can import this into another Python script, perhaps if you wanted to add it as a game to a collection. This way, it will not execute the code when being imported. Homework Now that you’ve finished with the code, why not make your own changes? Increase the word count; create different, selectable word categories; or even let people guess the full word. You have all the tools to do this in the currentcodeandlast month’stutorial.
  • 38.
    38 The PythonBook Python essentials TheStart Here we’re doing some minor setups so we can get our code to run with some extra modules not includedwiththe basics TheRules We’re setting names for each dice roll so they can be properly identified to the player – much more interesting than numbers TheScore Again we’ve got some basic variables set up so we cankeepscoreofthe gamesifwe want to TheScript The game is handled here, passing the player onto thenextfunctiontoactuallyplay,andhandlingthe endofthesession aswell TheGame We access the full game loop via here, and the function that allows us to play again if we’re so inclined TheThrow The initial hand is dealt, so to speak, at the start of the throws function. This function handles all the decision making in the game, while passing off the dicerollstoanother function TheHand We’ve also got a special function so we can inform the player exactly whatstyle ofhandthey have TheDecision There are two rounds in this version of poker dice, and you can select how many dice you wish to re-roll in this small while loop that makes sure you’realso usinga correct number #!/usr/bin/env python2 import random from itertools import groupby nine = 1 ten = 2 jack = 3 queen = 4 king = 5 ace = 6 names = { nine: “9”, ten: “10”, jack: “J”, queen: “Q”, king: “K”, ace: “A” } player_score = 0 computer_score = 0 def start(): print “Let’s play a game of Linux Poker Dice.” while game(): pass scores() def game(): print “The computer will help you throw your 5 dice” throws() return play_again() def throws(): roll_number = 5 dice = roll(roll_number) dice.sort() for i in range(len(dice)): print “Dice”,i + 1,”:”,names[dice[i]] result = hand(dice) print “You currently have”, result while True: rerolls = input(“How many dice do you want to throw again? “) try: if rerolls in (1,2,3,4,5): break except ValueError: pass print “Oops! I didn’t understand that. Please enter 1, 2, 3, 4 or 5.” Codelisting Putonyourpokerfaceandgetreadytogambleasyouhone yourprogrammingskillwithabitofpokerdice PlaypokerdiceusingPython So you’ve learnt how to program tic-tac-toe and guessed your way to victory at hangman. Now it’s time to head to Las Vegas and play our cardsright.Orinthiscase,virtualdice,andmore like Reno as we continue with our Python game tutorialsandintroduceyoutosomepokerdice. We’re again using some of the lessons we’ve already learnt, including random number generation, list creation and modification, human input, rule setting, scoring and more. But we’ll also be adding some new skills in this tutorial.Namely,we’llbecreatingandappending lists with random numbers, and using functions multiple times in one block of code to cut down onbloat. Again, we recommend using IDLE, and we’re using Python 2 to ensure compatibility with a wider variety of distros, including the Raspberry Pi. So, we hope luck is a lady for you and that the odds are ever in your favour – just keep those fingers crossed that you don’t roll a snake eyes (wearecodinginPython,afterall)! Resources Python2:www.python.org/download IDLE:www.python.org/idle
  • 39.
    The Python Book39 Python essentials TheRe-roll We’re doing the second set of rolls and starting the end of the game here by calling on the same function as before, but we’re also aware that choosing no re-rollsmeans the endofthe game TheDice Here we’re finding out which dice the player wants to re-roll, and also making sure that they enter a valid number. Just so they know they’re doing something, weprint something after every turn SecondHand We change and display the new dice hand to end the game. Again, we make sure to tell the player what theactualhandtheyhaveis TheRolls The function we reuse to roll our virtual six dice using a simple while loop. This allows us to keep thecodebase smaller TheAnalysis There are eight possible types of hands in poker dice, and we can use a bit of logic to work out all but one of them without checking against all 7,776 outcomes – in fact, we only specifically have to checkfortwo TheQuestion Our simple ‘play again’ function that parses player input so wecan restart or end the script TheEnd Scores are displayed at the end of the script, and the very final part allows us to import this into otherPythonscriptsasa module EXTRAFUNCTIONS Splitting up actions into functions makes it easier to not only perform them multiple times, but reduce the amount of code. On larger projects,thiscanaidwithspeed. if rerolls == 0: print “You finish with”, result else: roll_number = rerolls dice_rerolls = roll(roll_number) dice_changes = range(rerolls) print “Enter the number of a dice to reroll: “ iterations = 0 while iterations rerolls: iterations = iterations + 1 while True: selection = input(“”) try: if selection in (1,2,3,4,5): break except ValueError: pass print “Oops! I didn’t understand that. Please enter 1, 2, 3, 4 or 5.” dice_changes[iterations-1] = selection-1 print “You have changed dice”, selection iterations = 0 while iterations rerolls: iterations = iterations + 1 replacement = dice_rerolls[iterations-1] dice[dice_changes[iterations-1]] = replacement dice.sort() for i in range(len(dice)): print “Dice”,i + 1,”:”,names[dice[i]] result = hand(dice) print “You finish with”, result def roll(roll_number): numbers = range(1,7) dice = range(roll_number) iterations = 0 while iterations roll_number: iterations = iterations + 1 dice[iterations-1] = random.choice(numbers) return dice def hand(dice): dice_hand = [len(list(group)) for key, group in groupby(dice)] dice_hand.sort(reverse=True) straight1 = [1,2,3,4,5] straight2 = [2,3,4,5,6] if dice == straight1 or dice == straight2: return “a straight!” elif dice_hand[0] == 5: return “five of a kind!” elif dice_hand[0] == 4: return “four of a kind!” elif dice_hand[0] == 3: if dice_hand[1] == 2: return “a full house!” else: return “three of a kind.” elif dice_hand[0] == 2: if dice_hand[1] == 2: return “two pair.” else: return “one pair.” else: return “a high card.” def play_again(): answer = raw_input(“Would you like to play again? y/n: “) if answer in (“y”, “Y”, “yes”, “Yes”, “Of course!”): return answer else: print “Thank you very much for playing our game. See you next time!” def scores(): global player_score, computer_score print “HIGH SCORES” print “Player: “, player_score print “Computer: “, computer_score if __name__ == ‘__main__’: start() Codelistingcontinued
  • 40.
    40 The PythonBook Python essentials it later with a different number that the player chooses. We get five random numbers in a list returnedfrom thefunction,andwe order itusing sort to make it a bit more readable for the player andalsolateronforthehandfunction. 08Dicedisplay We print out each dice, numbering them so the player knows which dice is which, and also giving it the name we set at the start of the script. We’re doing this with a loop that repeats itself the number of times as the dice list is long using the range(len(dice)) argument. The i is increased each turn, and it prints out that specificnumberofthedicelist. 09Currenthand We want to find the type of hand the player has multiple times during the game, so set a specific function to find out. We pass the series ofdicewehaveontothisfunction,andprint. 10Throwagain Before we can throw the dice for the second round, we need to know which dice the #!/usr/bin/env python2 import random from itertools import groupby nine = 1 ten = 2 jack = 3 queen = 4 king = 5 ace = 6 names = { nine: “9”, ten: “10”, jack: “J”, queen: “Q”, king: “K”, ace: “A” } player_score = 0 computer_score = 0 def start(): print “Let’s play a game of Linux Poker Dice.” while game(): pass scores() def game(): print “The computer will help you throw your 5 dice” throws() return play_again() 01 02 03 04 06 05 01Begin As before, we use this line to enter the path to the Python interpreter. This allows us to run the program inside a terminal or otherwise outside of a Python-specific IDE like IDLE. Note thatwe’realsousingPython2forthisscript. 02Importing Aswellasimportingtherandommodule for our dice throws, we need to get the groupby function so we can order the dice in a way that is more readable and also easier for analysis when tellingtheplayerwhathandtheyhave. 03Cards While we’re using random numbers for the dice rolls, unless we assign the correct cards to each number, the player won’t know what they’ve rolled and what constitutes a better hand. We set each card to a number and then equatewhattheseshouldbeprintedoutas. 04Scores As usual, we have the empty scores for the player and computer so we can update these as we go. While it’s not specifically used in this version of the code, it’s easy enough to expand on it and add your own simple computer roll, or limited AI for both rolls. 05Start We’re starting the interactive part of the code with the ‘start’ function. It prints a greeting to the player, then starts a while loop that’ll allow us to replay the game as many times as we wish. The pass statement allows the while loop to stop once we’ve finished. If we do stop playing the game,thescorefunctionisthencalledupon. 06Game Like our Rock, Paper, Scissors code, def game pawns the rest of the game onto other functions, with its main function allowing us to keep repeating the game by passing the player through to the play_again function. 07Throws For our first throw, we want to have five random dice. We’ve set a variable here to pass on to our throwing function, allowing us to reuse RECYCLING There are a few variables that have duplicates throughout the code – while we’ve been careful to make sure they work where we want them to, it’s not the best code conduct. The names of the variables don’t specifically matter – it’s just best to label them in a way you understand for bug fixing and otherstoread.
  • 41.
    The Python Book41 Python essentials def throws(): roll_number = 5 dice = roll(roll_number) dice.sort() for i in range(len(dice)): print “Dice”,i + 1,”:”,names[dice[i]] result = hand(dice) print “You currently have”, result while True: rerolls = input(“How many dice do you want to throw again? “) try: if rerolls in (1,2,3,4,5): break except ValueError: pass print “Oops! I didn’t understand that. Please enter 1, 2, 3, 4 or 5.” if rerolls == 0: print “You finish with”, result else: roll_number = rerolls dice_rerolls = roll(roll_number) dice_changes = range(rerolls) print “Enter the number of a dice to reroll: “ iterations = 0 while iterations rerolls: iterations = iterations + 1 while True: selection = input(“”) try: if selection in (1,2,3,4,5): break except ValueError: pass print “Oops! I didn’t understand that. Please enter 1, 2, 3, 4 or 5.” dice_changes[iterations-1] = selection-1 print “You have changed dice”, selection playerwantstorollagain.Westartthisbyasking them how many re-rolls they want to do, which allows us to create a custom while loop to ask the user which dice to change that iterates the correctnumberoftimes. We also have to make sure it’s a number within the scope of the game, which is why we check using the try function, and print out a message which tells the user if and how they arewrong. 11Stick One of the things we’ve been trying to do in these tutorials is point out how logic can cut down on a lot of coding by simply doing process 07 08 10 11 12 13 09 INDENTATIONS Watch the indentations again as we split the else function. The following page’s code is on the same level as roll roll_number, dice_rerolls and dice_changes in thecode. WHITESPACE The big if function at the end of throws doesn’t have many line breaks between sections – you canaddtheseasmuchasyouwant to break up the code into smaller chunksvisually,aidingdebugging. of eliminations or following flow charts. If the userwantstore-rollzerotimes,thenthatmeans they’re happy with their hand, and it must be the end of the game. We print a message to indicate thisanddisplaytheirhandagain. 12There-rolls Here’s where we start the second roll and the end of the game, using a long else to the if statement we just started. We first of all make sure to set our variables – updating roll_number to pass onto the roll function with the re-roll number the user set, and creating the list that’s the exact length of the new set of rolls we wish to usethankstorange(rerolls). 13Parse We ask the player to enter the numbers of the dice they wish to re-roll. By setting an iterations variable, we can have the while loop last the same number of times as we want re- rolls by comparing it to the reroll variable itself. We check each input to make sure it’s a number thatcanbeused,andaddthevalidchoicestothe dice_changes list. We use iterations-1 here as Pythonlistsbeginat0ratherthan1.Wealsoprint out a short message so the player knows the selectionwassuccessful.
  • 42.
    42 The PythonBook Python essentials 14New dice We’re resetting and reusing the iterations variabletoperformasimilarwhilelooptoupdate the rolls we’ve done to the original dice variable. The main part of this while loop is using the iterations-1 variable to find the number from dice_changes list, and using that to change that specific integer in the dice list with the number from the replacement list. So if the first item on the dice_changes list is two, then the second item on the dices list is changed to the number wewanttoreplaceitwith. 15Sorting We’re ending the throw function in basically the same way we ended the first throw. First of all, we re-sort the dice list so that all the numbers are in ascending order. Then we print out the final cards that the dice correspond to, before again passing it onto the hand function so that we can fully determine the hand that the player has. We print out this result and that ends thefunction,sendingthewholethingbacktothe gamefunctiontoaskifyouwanttoplayagain. 16Dicerolling The roll function is used twice in the code for both times that we roll the dice. Being able to use the same code multiple times means we can cut down on bloat in the rest of the script, allowing it to run a little faster, as we’ve explained. It also means in this case that we can use it again if you want to change the game to threerounds,ormodifyitforrealpoker. 17Numberofrolls We begin the whole thing by bringing over the roll_number variable into the function – this is because while in the original roll it will always be five, the second roll could between one and the full five dice. We create a list with the number of entries we need for each roll, and again set an iterations variable for the upcoming whileloop. 18Remember Much like the while loops in the rest of the code so far, we’re keeping it going until iterations is the same as roll_number. Each entry in the dice list is replaced with a random number using the random.choice function and keeping it in the range of the numbers variable, which is one to six for each side of the dice. After this is done, we return the dice variable to the throw function that makesupthemajorityofthegame. 19Handanalysis While not technically a hand of cards, the poker terminology still applies. We start in this function by setting up a few things. The first part uses the groupby function we imported – this is used in this case to count the numbers that make up the dice variable. If there are three twos,afourandafive,itwillreturn[3,1,1].We’re using this to ascertain what kind of hand the player has. As the output of this groupby won’t be in any specific order, weuse thesortfunction again to sort it; however, this time we use the reverse=TRUE argument to make the analysis easieragain. 20Straights Straights and high cards are odd ones out in poker dice, as they do not rely on being able to count any repetitions in the cards. There are, however, only two hands that create a straight in poker dice, so we have created two lists here that contain them. We can then check first to see if the dice make these hands, and then if all other checks fail, it has to be a high card. 2 1Yourhand While seemingly lengthy, this a fairly simple if statement. As we stated before, we check to see if it’s one of the two straight hands. As there are no flushes or royal straight flushes inpokerdice,wedon’thavetoworryaboutthose. We then check to see if the first item in the list is five, which can only result in five of a kind; similarly, if the first item is four then the hand mustbefourofakind.Ifthefirstnumberisthree, thenitcanbeeitherafullhouseorthreeofakind, iterations = 0 while iterations rerolls: iterations = iterations + 1 replacement = dice_rerolls[iterations-1] dice[dice_changes[iterations-1]] = replacement dice.sort() for i in range(len(dice)): print “Dice”,i + 1,”:”,names[dice[i]] result = hand(dice) print “You finish with”, result def roll(roll_number): numbers = range(1,7) dice = range(roll_number) iterations = 0 while iterations roll_number: iterations = iterations + 1 dice[iterations-1] = random.choice(numbers) return dice 14 15 16 18 17 HIGHERORLOWER Which hand is best? What are the odds of getting certain hands in the game? Some of the answers are surprising, as the poker hands they’re based on trump the differing odds the dice produce. We’ve ranked hands from highest to lowest. FiveofaKind .................6/7776 FourofaKind ............150/7776 FullHouse .................300/7776 Straight......................240/7776 ThreeofaKind ........1200/7776 TwoPairs .................1800/7776 OnePair ...................3600/7776 HighCard ...................480/7776
  • 43.
    The Python Book43 Python essentials def hand(dice): dice_hand = [len(list(group)) for key, group in groupby(dice)] dice_hand.sort(reverse=True) straight1 = [1,2,3,4,5] straight2 = [2,3,4,5,6] if dice == straight1 or dice == straight2: return “a straight!” elif dice_hand[0] == 5: return “five of a kind!” elif dice_hand[0] == 4: return “four of a kind!” elif dice_hand[0] == 3: if dice_hand[1] == 2: return “a full house!” else: return “three of a kind.” elif dice_hand[0] == 2: if dice_hand[1] == 2: return “two pair .” else: return “one pair .” else: return “a high card.” def play_again(): answer = raw_input(“Would you like to play again? y/n: “) if answer in (“y”, “Y”, “yes”, “Yes”, “Of course!”): return answer else: print “Thank you very much for playing our game. See you next time!” def scores(): global player_score, computer_score print “HIGH SCORES” print “Player: “, player_score print “Computer: “, computer_score if __name__ == ‘__main__’ : start() 19 20 21 22 23 24 HOMEWORK There is currently no scoring in place for this version of the game. Try adding a computer player, or create a rule set that requires a certain hand or higher. You could even makeittwo-player. so we nest an if statement. Again, we do this for pairs, where that could be one or two pairs. If all else fails then, by a process of elimination, it can only be a high card. We give each outcome a text stringtosendbacktothethrowfunctionsothatit canbeprinted. 22Playagain As before, we ask the player for raw input with the text offering another game. Instead of parsing it, we assume the player will choose a specified yes response based on the text, and if none of these versions is received, we print out themessagethankingthemforplayingthegame. Thisendsthegamefunction. 23Finalscores Going all the way back to the start function, after the game finishes we move onto the results. This section is quite simple – it calls the scores, which are integers, and then prints them individually after the names of the players. This is the end of the script, as far as the player is concerned. Currently, the code will not permanently save the scores, but you can have Pythonwriteittoafiletokeepifyouwish. 24Modules The final part of the code allows for the script to be used in two ways. Firstly, we can execute it in the command line and it will work just fine. Secondly, we can import this into another Python script, perhaps if you wanted to add it as a game to a collection. This last piece of code will prevent our script being executed when imported by another module – it will only do so whenbeingrundirectly. TEXTEDITORS Instead of the IDE we’ve suggested, you should also try coding in a text editor. Some of them are a little more lightweight and format code similar to the way the IDE does, separating functions and strings by colours etc. Some of the ones we’d recommend are the classic gedit, a popular text editor from GNOME desktops; Geany, which has a few IDE-esque features written into it; TEA, a multifunctioning text editor and project manager; and Jedit, a text editor that lives in the command line for minimum resource usage. These can also be used with multiple programminglanguages,soyoucangetused tothem with Python,thenmaketheswitch.
  • 44.
    44 The PythonBook Python essentials Thestart Herewe’re doing someminor setup, including getting a new modulethathelpsuscreatea simplegraphical interface Theimports We’re importing the three games wecreated in past issuesso wecancalluponor use them Thewindow Createagraphicalwindow andgive it a name sowe can add some functions toit Theframe Define the dimensionsofthe windowand givea rough guide to placement ofthe objects within Thewelcome Print a messagein the windowand place it in a specific orientation. Thisworks a littledifferently to print Thebutton The focus of this month’s tutorial is making Rock-Paper- Scissors work in a graphical interface, so we’re calling a new function we’re creating Theinterface Creatingandformattingbuttonstostart the other twotutorial games in the command lineor shell Theexit Here we create a button that quits the window and ends the script. We’ve also placed it specifically at the bottom of the window Theloop The mainloop allowsthe main windowto continueto work and beupdated without exiting the program unlessspecified #!/usr/bin/env python2 #Linux User Developer presents: Mega Microgrames Collection from Tkinter import * import rockpaperscissors import hangman import pokerdice root = Tk() root.title (“Linux User Developer’s Mega Microgames Collection”) mainframe = Frame(root, height = 200, width = 500) mainframe.pack_propagate(0) mainframe.pack(padx = 5, pady = 5) intro = Label(mainframe, text = “””Welcome to Linux User Developers Mega Microgames Collection. Please select one of the following games to play: “””) intro.pack(side = TOP) rps_button = Button(mainframe, text = “Rock, Paper, Scissors”, command = rockpaperscissors.gui) rps_button.pack() hm_button = Button(mainframe, text = “Hangman”, command = hangman.start) hm_button.pack() pd_button = Button(mainframe, text = “Poker Dice”, command = pokerdice.start) pd_button.pack() exit_button = Button(mainframe, text = “Quit”, command = root.destroy) exit_button.pack(side = BOTTOM) root.mainloop() MainInterfaceCodeListing BringeverythingtogetherwithaPythonGUIandtakethenext stepinprogrammingyourownsoftware Createagraphicalinterface forPythongames The three basic games we have made in Python so far have all run in the command line or via IDLE, a Python IDE. While this allowed us to show off different ways to use Python code, we haven’t actually shown you how to present it yet. In this tutorial, we will take all three games and put them all into one neatly unified graphicalinterface. To this end, we’ll be making use of the small line of code we added at the bottom of each previous tutorial so we can import them as modules into our main graphical script. We’ll also modify the existing code to add some graphical elements. To do all this we’ll be using Tkinter, a default module available in Python that allows you to create windows and frames with fairly simple code. All you need for this tutorial is an up-to-date copy of Python, from your distro’s repository or the website, and the IDLE development environment. This will also work great on Raspberry Pi distros, such as Raspbian. Resources Python2:www.python.org/download IDLE:www.python.org/idle
  • 45.
    The Python Book45 Python essentials Newimports ImportnewmodulesthatallowustocreatetheGUI part of Rock, Paper, Scissors, as well as removing themoduleswenolonger need Newinterface Our new main function allows us to call the majorityofthegamescriptwhentherps_buttonis pressed. This contains the game components and thegraphicalcomponents Newstart We’ve changed the start function so that it no longergoestothescorefunctionafterit’sfinished. We’vealsoremovedthescorefunction,aswetrack thatdifferentlysoit can be displayedproperly Newgame We’ve changed the game function so that it now takes the input from our graphical interface. We use a new variable to do this that works with the GUI,otherwiseitworksroughlythesameasbefore Newresults The result function remains largely unchanged, only now it sends the outcome message to a variable we use for the interface, and generally usesthenewGUI’svariables Newwindow Wecreatethegamewindowwithaslightlydifferent method due to already having a ‘mainloop’ root window. We’re also giving it a name so you can identify it properly Newvariables Ournewvariablesaresetupsotheycaninteractwith boththegamecodeandtheinterfacecodeproperly. We’ve also made sure to have a default selection for theplayersothatthecoderunsproperly Newframe Determine the size and layout of the window for the game using a slightly different method than before. We’ve also allowed for elements to be anchored in certain positions around the window Newchoice Here we place radio buttons in a specific configuration in the window, giving the user the choice of three moves. This is then passed along to thevariableandusedbythegamecode Newmove Here we allow for the computer’s move to be displayedunderthe‘Computer’ label Newbutton Pressing the Play button we’ve put here runs the game script, prints out the scores and finally a messagebasedonthe outcome Newending We’ve changed this so that the main script begins withguinowratherthan the start function #!/usr/bin/env python2 # Linux User Developer presents: Rock, Paper, Scissors: The Video Game: The Module from Tkinter import * from ttk import * import random def gui(): rock = 1 paper = 2 scissors = 3 names = { rock: “Rock”, paper: “Paper”, scissors: “Scissors” } rules = { rock: scissors, paper: rock, scissors: paper } def start(): while game(): pass def game(): player = player_choice.get() computer = random.randint(1, 3) computer_choice.set(names[computer]) result(player, computer) def result(player, computer): new_score = 0 if player == computer: result_set.set(“Tie game.”) else: if rules[player] == computer: result_set.set(“Your victory has been assured.”) new_score = player_score.get() new_score += 1 player_score.set(new_score) else: result_set.set(“The computer laughs as you realise you have been defeated.”) new_score = computer_score.get() new_score += 1 computer_score.set(new_score) rps_window = Toplevel() rps_window.title (“Rock, Paper, Scissors”) player_choice = IntVar() computer_choice = StringVar() result_set = StringVar() player_choice.set(1) player_score = IntVar() computer_score = IntVar() rps_frame = Frame(rps_window, padding = ‘3 3 12 12’ , width = 300) rps_frame.grid(column=0, row = 0, sticky=(N,W,E,S)) rps_frame.columnconfigure(0, weight=1) rps_frame.rowconfigure(0,weight=1) Label(rps_frame, text=’Player’).grid(column=1, row = 1, sticky = W) Radiobutton(rps_frame, text =’Rock’ , variable = player_choice, value = 1).grid(column=1, row=2, sticky=W) Radiobutton(rps_frame, text =’Paper’ , variable = player_choice, value = 2).grid(column=1, row=3, sticky=W) Radiobutton(rps_frame, text =’Scissors’ , variable = player_choice, value = 3).grid(column=1, row=4, sticky=W) Label(rps_frame, text=’Computer’).grid(column=3, row = 1, sticky = W) Label(rps_frame, textvariable = computer_choice).grid(column=3, row=3, sticky = W) Button(rps_frame, text=”Play”, command = start).grid(column = 2, row = 2) Label(rps_frame, text = “Score”).grid(column = 1, row = 5, sticky = W) Label(rps_frame, textvariable = player_score).grid(column = 1, row = 6, sticky = W) Label(rps_frame, text = “Score”).grid(column = 3, row = 5, sticky = W) Label(rps_frame, textvariable = computer_score).grid(column = 3, row = 6, sticky = W) Label(rps_frame, textvariable = result_set).grid(column = 2, row = 7) if __name__ == ‘__main__’: gui() ModifiedRPSCodeListing
  • 46.
    46 The PythonBook Python essentials 01 02 03 04 07 08 09 06 05 01Firstline We use this line to enter the path to the Python interpreter. This lets us run the program inside a terminal or otherwise outside of a Python-specific IDE like IDLE. Note that we’re alsousingPython2forthisparticularscript. 06Introductions We create the intro variable as a label that lives in the main frame. We give it text to introduce the interface, using the triple quote marks to have it go across multiple lines and format better. We then use pack to display it, and tellTkintertoputitatthetopoftheinterface. 07Rock,Paper,Scissors We create a button for the Rock, Paper, Scissors game using the Button function. We attach to it the main frame, give it a label using 02Importgraphics Tkinter is the graphical interface we’re using and while it’s a standard Python function, you’llneedtoimportthemodulesoyoucanuseit. We’ve used the ‘from [module] import *’ method so that we can use the functions from it without havingtoaddTkinteratthebeginning. 04Rootwindow Using the Tk() function creates the window we’re going to be placing everything into. We’ve decided to call it root for now; however, you can call it anything you like, as long as you’re consistent with it. We’ve also named it using the title command from Tkinter and a string of text. 05Mainframe The first line has us set the variable mainframe as a Frame in the interface. We’ve attached it to root, the main window, and given 03Importgames We’re importing the modules for the three games. We added the line at the bottom MAINWINDOW The main interface window that this code creates is fairly basic, but contains the functions we require. The window exit button will do the same job as the Quit button, and the Hangman and Poker Dice buttons run the old scripts in the Python shell. #!/usr/bin/env python2 #Linux User Developer presents: Mega Microgrames Collection from Tkinter import * import rockpaperscissors import hangman import pokerdice root = Tk() root.title (“Linux User Developer’s Mega Microgames Collection”) mainframe = Frame(root, height = 200, width = 500) mainframe.pack_propagate(0) mainframe.pack(padx = 5, pady = 5) intro = Label(mainframe, text = “””Welcome to Linux User Developers Mega Microgames Collection. Please select one of the following games to play: “””) intro.pack(side = TOP) rps_button = Button(mainframe, text = “Rock, Paper , Scissors”, command = rockpaperscissors.gui) rps_button.pack() hm_button = Button(mainframe, text = “Hangman”, command = hangman.start) hm_button.pack() pd_button = Button(mainframe, text = “Poker Dice”, command = pokerdice.start) pd_button.pack() exit_button = Button(mainframe, text = “Quit”, command = root.destroy) exit_button.pack(side = BOTTOM) root.mainloop() it a minimum height and width in pixels. We use pack_propogate to create the window, and then make sure it’s the size that we’ve defined. We’ve then used pack to pad the borders, allowing the contents of the window to not touch the sides of it. of each script so we can do this. To make sure to differentiate the functions in each game, we will have to specify [module].[function] so there are no errors in the code.
  • 47.
    The Python Book47 Python essentials #!/usr/bin/env python2 # Linux User Developer presents: Rock, Paper , Scissors: The Video Game: The Module from Tkinter import * from ttk import * import random def gui(): rock = 1 paper = 2 scissors = 3 names = { rock: “Rock”, paper: “Paper”, scissors: “Scissors” } rules = { rock: scissors, paper: rock, scissors: paper } def start(): while game(): pass def game(): player = player_choice.get() computer = random.randint(1, 3) computer_choice.set(names[computer]) result(player , computer) 10 11 12 13 14 09Breaktheloop The exit button works similarly to the other buttons we’ve created, but instead it uses the command root.destroy. This ends the loop that we’ve created with root.mainloop(), which allows the interface code to continue looping, allowing us to continually use it. We place the exit button at the bottom of the window with ‘side = BOTTOM’. 12Gamevariables The variables are staying the same so that we can do the same comparisons we made in the original code. We’ve put them into the function itself so that they don’t affect the other imported code into the main interface – and so that when calling just this function, we don’t need to use global to bring them in. 14Gamefunction The game function has had a few modifications to make sure it works with the interface. First of all, the player variable is retried using get() on the special variable we’ve created to contain the player choice. We do a similar thing for the computer, using ‘set’ to change the variable in our interface- friendly computer_choice value. We still use the name variable to set the text that goes into computer_choice. This then passes the player and computer variables along in the same way we did before. 13Startfunction We’ve removed the part that calls the score function from the start function, as we have the interface handle the scoring now. It still calls upon the game function, though, putting it into a loop so it can be used continuously. This function is called by the interface to begin the game by setting a computer move and then comparing it to the player’s choice. 10Gamecode Nothingmuchhaschangedinthestartof this code, other than a few import changes. The code for running it in the command line is still PYTHONSHELL Our other code will run in the shell or via a command line in the same way as before when the buttons arepressed. 08Othergames For the other two games, the code is mostlythesame;however,wecalluponthestart function in both of them. In the final interface, this will cause the games to run in the shell or commandlineasthey’vebeenrunningbefore. 11Gameinterface One of the biggest changes we’re making to this script is having it all contained in one function, ‘def gui’. The interface code needs to be put into a function, otherwise it will be run during import. While we’ve chosen to put the entirety of the code in a function, you can also try just having the graphical interface code in one. All our variables are kept in here so that they still work properly. there, and with a few modifications the code will run independently of the main interface. We’ve removed the time module, as we no longer need it, and imported not only the Tkinter module, but the ttk module. The ttk module allows us to arrange the GUI in a grid, which will be slightly easiertouseandunderstand. text that appears on the button, and then have it run a command. In this case, we use the modified rockpapershotgun.py code that has a gui function, hence rockpapershotgun.py. We then use pack to place it in the window
  • 48.
    48 The PythonBook Python essentials def result(player , computer): new_score = 0 if player == computer: result_set.set(“Tie game.”) else: if rules[player] == computer: result_set.set(“Your victory has been assured.”) new_score = player_score.get() new_score += 1 player_score.set(new_score) else: result_set.set(“The computer laughs as you realise you have been defeated.”) new_score = computer_score.get() new_score += 1 computer_score.set(new_score) rps_window = Toplevel() rps_window.title (“Rock, Paper , Scissors”) player_choice = IntVar() computer_choice = StringVar() result_set = StringVar() player_choice.set(1) player_score = IntVar() computer_score = IntVar() 15 16 17 19 20 18 GAMEWINDOW In its default state, the game window will have rock selected and no message will be displayed. Once the player makes a move, the message will be displayed at the bottom and the computer’s move will be printed. There’s no quit button on this menu, but clicking the window exit will bring you back to the main interface. 15Resultfunction The result function still takes the same two variables as before, which we set in the game function. While technically we can use the variables set up for the interface, these are not pure integers and can cause an error if not handled correctly. With that in mind, we’ve created an empty new_score variable that we can use to effectively clean the interface value beforeaddingitbackintoit. 20Interfacevariables Here is the reason we had to call and change the variables in a different manner. For Tkinter, we need to let the interface know whether or not a variable is an integer or a text value. IntVar and StringVar allow for these respectively. We’ve also set the player_choice variable to be one, which we have already set as thechoiceforrock.Thismeanstherewillatleast be a default choice when the game is started, anditwon’tcauseanerror. 21Gameframe We’ve created the frame for our interface items slightly differently. Instead of using the pack command in the main interface, we’re using grid to make sure they’re orientated in such a way that makes sense for the user. Padding does just that, setting up values to make sure the items in the frame don’t touch the edge of the window. Using the .grid command, we then create this frame. The row and column variables allow for rows and columns to be included in the structure of 16Tie The logic for determining the result is the same as before. We first do the easy check – whether or not the numeric value for the player andcomputervariableisthesame.Whatchanges this time is that, instead of printing the text, we send the “Tie game” message to our result variableusingthesetfunctionfromTkinter. 18Lose This part of the overall if statement works in the same way as before, by assuming that if it isn’t a tie or a win, it’s a loss. Like the new version of the win code, it then uses set to change the message that will be displayed to the player, and calls upon and changes the computer score by putting it through the new_score variable. 19New window As the original window is part of the mainloop, we cannot have the window be created using Tk() like in the main interface code. As this window is coming off it, though, we instead create it using Toplevel(). This allows the window to run separately and on top of the main window. We’ve also given 17Win The if statement continues by seeing if the player has won. Like before, we use the rules we set to make the comparison for the code to it a name, which will not change the main window’s name in the process. make. We set the result_set like we did in the tie game, with a different message to the user. Finally, we set the new_score variable to be the current player score, using the get function to obtain it, plus one to the score, and then use set again to put it back into the player_score variable. We can’t use += with the player_score variable,asitisnotastandardvariable.
  • 49.
    The Python Book49 Python essentials rps_frame = Frame(rps_window, padding = ‘3 3 12 12’ , width = 300) rps_frame.grid(column=0, row = 0, sticky=(N,W,E,S)) rps_frame.columnconfigure(0, weight=1) rps_frame.rowconfigure(0,weight=1) Label(rps_frame, text=’Player’).grid(column=1, row = 1, sticky = W) Radiobutton(rps_frame, text =’Rock’ , variable = player_choice, value = 1).grid(column=1, row=2, sticky=W) Radiobutton(rps_frame, text =’Paper’ , variable = player_choice, value = 2).grid(column=1, row=3, sticky=W) Radiobutton(rps_frame, text =’Scissors’ , variable = player_choice, value = 3).grid(column=1, row=4, sticky=W) Label(rps_frame, text=’Computer’).grid(column=3, row = 1, sticky = W) Label(rps_frame, textvariable = computer_choice).grid(column=3, row=3, sticky = W) Button(rps_frame, text=”Play”, command = start).grid(column = 2, row = 2) Label(rps_frame, text = “Score”).grid(column = 1, row = 5, sticky = W) Label(rps_frame, textvariable = player_score).grid(column = 1, row = 6, sticky = W) Label(rps_frame, text = “Score”).grid(column = 3, row = 5, sticky = W) Label(rps_frame, textvariable = computer_score).grid(column = 3, row = 6, sticky = W) Label(rps_frame, textvariable = result_set).grid(column = 2, row = 7) if __name__ == ‘__main__’: gui() 21 22 23 23 24 25 22Player’schoice We create a label for the player’s move and assign it to a grid location, on the first row, on the first column. We also justify it to the left using ‘sticky = W’. We then add the radio buttons for the player’s move, each on the same column but the following row down. We give each choice a name, then assign it to the player_choice variable. We then make each choice have a numerical value that corresponds to the moves we’vedeterminedinthefirstsetofrules. 23Computer’smove We display the computer move here. First of all, we label what this is and then create 24PressPlay The running of the code all hinges on the Play button. It’s very simple: we put it in the row between the Player and Computer move as part of our three-column system; and it runs the start functionusing the command option.Due to the loop of the interface, we can keep pressing this without needing to be asked to play again. Simply exiting the window will go back to the main interface window as well, meaning we do notneedaspecificquitbutton. 25Running score We have two sets of scores to display – one for the player and the other for the 26End game The final part of the code allows for the script to be used by the main window, and also allows for it to run on its own when used in the command line or shell. You’ll need to perform some modifications to make it run on its own, such as making it the mainloop and not a Toplevel window. However, it will run just fine from both without the need to be launched from themaininterface. the window, and the sticky allows us to justify items with specific directions – in this case top, left, right and bottom justification. Finally, we then make sure each column and row is treated equally by giving them the same weighting, and starting from zero. a second label to display the actual move. We do this by adding the textvariable option to Label, and using the computer_choice variable we updated earlier in the game function. This merely prints the text from the names list and justifiesthistotheleft. computer. We label these the same way we’ve done with labelling the Player and Computer move, having them on a lower row but still in the relevant columns. Below that, we use the textvariable option again to get the numerical score we assigned to the separate score variable. Finally, we create another label to displaythemessageforthegame’soutcome
  • 50.
    50 The PythonBook Python essentials Completeyourtrioofgameswithagraphicalinterfaceforthe hangmanandpokerdicecode Bringgraphicstosimple Pythongames We have now created a simple selector for the trio of Python games we made previously. This interface was able to launch a GUI for our rock, paper, scissors game, and run the other two in the terminal. Now, we’re going to convert the hangman and poker dice codes to work in a similar way to rock, paper, scissors. The trick with hangman comes in allowing for a different type of input, text, and the ability to have multiple rounds of the game. Tkinter allows for text entry, and we rely a lot less on ‘while’ loops to play the game in its entirety. Poker Dice needs to keep the dice analysis code, and the option to change specific dice using checkboxes. We’ll be modifying a large amount of the original code to fit in with the new graphical scheme. This mainly involves cutting specific parts and having the Tkinter-specific code handle these itself. The code listings on these pages include the modified code – we’ll discuss the graphical part on the following pages. Resources Python2:www.python.org/download IDLE:www.python.org/idle 1Imported Herewe’re doing the sameminorsetup, including getting theTkinter module that helps us create asimplegraphical interface 2Words We’re keeping ourvariablesthat determine the word to guessheresoitcan be easily accessed anywhere in the code 3Function Likelasttime,we’re putting the majority of our originalcodeinto a newfunction,gui 4Analysis Weselecttheword andanalyseitbefore continuingon with the restofthecode 5Graphics Thehangedman functionislargely unchanged,albeitwith newcode todisplay ourASCIIgraphicson the interface 6Guesses Wecheckthe number ofmistakesmade,and calltheguess_letter function tocheck the letter entered from Tkinter import * from ttk import * from random import * word = 0 word_length = 0 clue = 0 def gui(): global word, word_length, clue dictionary = [“gnu”,”kernel”,”linux”,”magei a”,”penguin”,”ubuntu”] word = choice(dictionary) word_length = len(word) clue = word_length * [“_”] tries = 6 def hangedman(hangman): graphic = [ “”” +-------+ | | | O | -|- | / | =============== “””] graphic_set = graphic[hangman] hm_graphic.set(graphic_set) def game(): letters_wrong = incorrect_guesses.get() letter=guess_letter() first_index=word.find(letter) if first_index == -1: letters_wrong +=1 incorrect_guesses.set(letters_ wrong) else: for i in range(word_length): if letter == word[i]: clue[i] = letter hangedman(letters_wrong) clue_set = “ “.join(clue) word_output.set(clue_set) if letters_wrong == tries: result_text = “Game Over. The word was “ + word result_set.set(result_text) new_score = computer_score.get() new_score += 1 computer_score.set(new_score) if “”.join(clue) == word: result_text = “You Win! The word was “ + word result_set.set(result_text) new_score = player_score.get() new_score += 1 player_score.set(new_score) def guess_letter(): letter = letter_guess.get() letter.strip() letter.lower() return letter def reset_game(): global word, word_length, clue incorrect_guesses.set(0) hangedman(0) result_set.set(“”) letter_guess.set(“”) word = choice(dictionary) word_length = len(word) clue = word_length * [“_”] new_clue = “ “.join(clue) word_output.set(new_clue) if __name__ == ‘__main__’: gui() 01 02 03 04 05 06 HangmanCodeListing
  • 51.
    The Python Book51 Python essentials 1Moreimports We’ve added the new imported modulesweneedto make Tkinter workandkeeptherest the same 2Dicelist Thelistthatholdsthe dice iskept outside the main function so that it canbeaccessedeverywhere 3Rolls Samegoesfortherollfunction. It doesn’tspecificallyneed to be inside the gui functionanyway 4Decisions The checkboxes in the graphical codewe’re going to createlater will giveusnumberswecan analyse for thecode.Weretrieve thesenumbers andcheckthemtofindoutwhich dicetheuser wishestore-roll 5Hands Finally,ourhandanalysisfunction isthe lastpartofthe originalcode thatiskeptoutsidethe guifunction. Boththis and the abovefunction passthe necessary details back upthe chain to thenbeaddedinto thenewgraphicalelementsofthe newinterface 6Nodice Ifnodicehavebeenselectedto re-roll, the hand output ischanged to showa finalmessage 7Re-roll Thispartisalmostthe same as before–anewsetofdice are rolled and then inserted into the list ofdice likebefore,thenre-sortedtomake the handanalysiseasier 8Morefunctions The new gui functionisthe main changetothePoker Dice code, and asbeforeincludesthe Tkinter elementsandotherpartsofthe original code 9Gamestart Asimplefunctionthat we can use to activatethere-rollsofthedice 10Newhand Thenewdicearenamed,analysed, andeverythingisthen set for the gui to display the finaloutcome 11Reset Likewiththehangman code,we haveafunctiontoreset allthe variables,allowingyoutostart the gameagain from Tkinter import * from ttk import * import random from itertools import groupby dice = 0 def roll(roll_number): numbers = range(1,7) dice = range(roll_number) iterations = 0 while iterations roll_number: iterations = iterations + 1 dice[iterations-1] = random. choice(numbers) return dice def hand(dice): dice_hand = [len(list(group)) for key, group in groupby(dice)] dice_hand.sort(reverse=True) straight1 = [1,2,3,4,5] straight2 = [2,3,4,5,6] if dice == straight1 or dice == straight2: return “a straight!” elif dice_hand[0] == 5: return “five of a kind!” elif dice_hand[0] == 4: return “four of a kind!” elif dice_hand[0] == 3: if dice_hand[1] == 2: return “a full house!” else: return “three of a kind.” elif dice_hand[0] == 2: if dice_hand[1] == 2: return “two pair.” else: return “one pair.” else: return “a high card.” def gui(): global dice dice = roll(5) dice.sort() nine = 1 ten = 2 jack = 3 queen = 4 king = 5 ace = 6 names = { nine: “9”, ten: “10”, jack: “J”, queen: “Q”, king: “K”, ace: “A” } result = “You have “ + hand(dice) def game(): throws() def throws(): global dice dice1_check = dice1.get() dice2_check = dice2.get() dice3_check = dice3.get() dice4_check = dice4.get() dice5_check = dice5.get() dice_rerolls = [dice1_check, dice2_check, dice3_check, dice4_check, dice5_check] for i in range(len(dice_rerolls)): if 0 in dice_rerolls: dice_rerolls.remove(0) if len(dice_rerolls) == 0: result = “You finish with “ + hand(dice) hand_output.set(result) else: roll_number = len(dice_rerolls) number_rerolls = roll(roll_num- ber) dice_changes = range(len(dice_ rerolls)) iterations = 0 while iterations roll_number: iterations = iterations + 1 dice_changes[iterations-1] = number_rerolls[iterations-1] iterations = 0 while iterations roll_number: iterations = iterations + 1 replacement = number_ rerolls[iterations-1] dice[dice_ changes[iterations-1]] = replacement dice.sort() new_dice_list = [0,0,0,0,0] for i in range(len(dice)): new_dice_list[i] = names[dice[i]] final_dice = “ “.join(new_dice_ list) dice_output.set(final_dice) final_result = “You finish with “ + hand(dice) hand_output.set(final_result) def reset_game(): global dice dice = roll(5) dice.sort() for i in range(len(dice)): empty_dice[i] = names[dice[i]] first_dice = “ “.join(empty_dice) dice_output.set(first_dice) result = “You have “ + hand(dice) hand_output.set(result) if __name__ == ‘__main__’: gui() 01 02 03 04 05 11 10 09 08 07 06 PokerDiceCodeListing
  • 52.
    52 The PythonBook Python essentials 01 02 03 01Firstlines As usual, we start off each program with the code that lets us run it in the command line, followed by importing the necessary modules: random, to determine the word to use; Tkinter, for the majority of the graphical code; and 06Gamesbegin All the analysis of the letter we’ve entered is done in this function. To that end, we start by obtaining the incorrect guesses so far from the variable we’ve set up so the interface can access it if we want it to. The letter from the entry field in the interface is then obtained and cleaned up so it can be used with the rest of thecode. 07Checktheletter This section of the code is again largely unchanged–theletteristakenandcomparedto the word with find to see if it matches with one of the letters. The if statement then adds one to the incorrect guess variable, or updates the clue variabletoaddtheletterintherightspot. 09Updatescores Exactly as before, we check to see if the player has won or lost yet. In the event of either, a message is displayed to signify this, and the winsandlossesscoreisupdatedusingset. 02Globalvariables We have kept these three variables outside of the gui function so they can be 04Randomword We bring in the three variables with global so we can modify them throughout the code, and then set the word. As before, a random item from the list of words is selected with choice, the length is ascertained, and the cluetodisplayisset. 08Updateinterface These three lines set the graphic for this round, join the current clue together as a string, and then set it on the variable for the interface toread. 03Graphicalfunction We’re putting all the working code into the gui function so it can be activated from the main interface. This means we can import the Hangman code into the interface without the game window popping up, and only run it when weactivatetheguifunctionfromhere. 05Thehangedman The main difference this time for the Hangman graphics is that instead of printing these out, we’re going to display them in the interface. When the function is called and the graphic selected, it’s placed in the variable we’ve set up in the interface code that we’re using to displaytheresult. ttk, for the grid code we’ll be using to align the differentelements. accessedatallpointsinthecode.Python2does not allow you to call upon global variables when you’re in a nested function, whereas in Python 3 thiscouldhavegoneintotheguifunction. 09 08 07 06 05 04 #!/usr/bin/env python2 from Tkinter import * from ttk import * from random import * word = 0 word_length = 0 clue = 0 def gui(): global word, word_length, clue dictionary = [“gnu”,”kernel”,”linux”,”mageia”,”penguin”,”ubuntu”] word = choice(dictionary) word_length = len(word) clue = word_length * [“_”] tries = 6 def hangedman(hangman): graphic = [ “”” +-------+ | | | O | -|- | / | =============== “””] graphic_set = graphic[hangman] hm_graphic.set(graphic_set) def game(): letters_wrong = incorrect_guesses.get() letter=guess_letter() first_index=word.find(letter) if first_index == -1: letters_wrong +=1 incorrect_guesses.set(letters_wrong) else: for i in range(word_length): if letter == word[i]: clue[i] = letter hangedman(letters_wrong) clue_set = “ “.join(clue) word_output.set(clue_set) if letters_wrong == tries: result_text = “Game Over. The word was “ + word result_set.set(result_text) new_score = computer_score.get() new_score += 1 computer_score.set(new_score) if “”.join(clue) == word: result_text = “You Win! The word was “ + word result_set.set(result_text) new_score = player_score.get() new_score += 1 player_score.set(new_score) YOULOSE When you’ve run out of guesses, the game stops. From here, you can also reset the game to play againifyouwish.
  • 53.
    The Python Book53 Python essentials 10 11 15 12 16 13 14 12Interfacevariables Tkinter only works with specific variables – we’ve created all the ones we need or can use here. IntVars take integers, while StringVars take strings. We’ve used get and set throughout the restofthecodewiththesetogetandsetvalues. 14CluetoHangman These labels are fairly straightforward – we’re either giving them fixed text, or telling them to use a specific textvariable so they can beupdatedasweplaythegame. 13Framedwindow The frame is set up the same way as last time. We pad the frame from the edge of the window, set a grid, give it sticky points at compass points, and allow for setting objects withspecificrowandcolumnpoints. 16Resultsandreset The rest of the code is similar to what we’ve done already: labels to display fixed text and the scores/result text that change. The button that activates the reset function is also put at the bottom here. The final two lines allow ustoimportthemoduleintotheinterfacecode. 10Sanitiseinput The guess_letter function purely gets the letter from the player input variable, strips it of any formatting, makes it lower case, and then returns it back to the game function. This is so thelettercanbeusedproperly. 11New window We use the Toplevel command from Tkinter like last month to separate the loops of the main interface and game window. We then usetitletocallitHangman. 15Textentry Entry here sets a text box we will add the letters to. The exportselection option makes it so selecting the letter won’t immediately copy it to the clipboard, and the textvariable selection is where the code stores the letter added. The button activates the game function, analysing thelettertheplayerentered. Theframeisset upasbefore def guess_letter(): letter = letter_guess.get() letter.strip() letter.lower() return letter def reset_game(): global word, word_length, clue incorrect_guesses.set(0) hangedman(0) result_set.set(“”) letter_guess.set(“”) word = choice(dictionary) word_length = len(word) clue = word_length * [“_”] new_clue = “ “.join(clue) word_output.set(new_clue) hm_window = Toplevel() hm_window.title (“Hangman”) incorrect_guesses = IntVar() incorrect_guesses.set(0) player_score = IntVar() computer_score = IntVar() result_set = StringVar() letter_guess = StringVar() word_output = StringVar() hm_graphic = StringVar() hm_frame = Frame(hm_window, padding = ‘3 3 12 12’, width = 300) hm_frame.grid(column=0, row = 0, sticky=(N,W,E,S)) hm_frame.columnconfigure(0, weight=1) hm_frame.rowconfigure(0,weight=1) Label(hm_frame, textvariable = hm_graphic).grid(column=2, row = 1) Label(hm_frame, text=’Word’).grid(column=2, row = 2) Label(hm_frame, textvariable = word_output).grid(column=2, row = 3) Label(hm_frame, text=’Enter a letter’).grid(column=2, row = 4) hm_entry = Entry(hm_frame, exportselection = 0, textvariable = letter_guess).grid(column = 2, row = 5) hm_entry_button = Button(hm_frame, text = “Guess”, command = game).grid(column = 2, row = 6) Label(hm_frame, text = “Wins”).grid(column = 1, row = 7, sticky = W) Label(hm_frame, textvariable = player_score).grid(column = 1, row = 8, sticky = W) Label(hm_frame, text = “Losses”).grid(column = 3, row = 7, sticky = W) Label(hm_frame, textvariable = computer_score).grid(column = 3, row = 8, sticky = W) Label(hm_frame, textvariable = result_set).grid(column = 2, row = 9) replay_button = Button(hm_frame, text = “Reset”, command = reset_game).grid(column = 2, row = 10) if __name__ == ‘__main__’: gui() THEHANGMANGUI Press the updated Hangman button to launch a new window. Here we have the initial graphic, wordclueandentryfortheplayer to interact with. The scores are set to zero, and no result message is displayed as no games havebeen played yet. ORIGINALINTERFACE You’ll also need the interface code from last issue, which already works with the modified Rock, Paper, Scissors code. The way it was left off means it won’t work with the new code, so you’ll have to change the command in each button from [game].start to [game].gui.
  • 54.
    54 The PythonBook Python essentials 17 19 24 22 20 18 21 23 22Firstroll As the window opens, we immediately make the first roll. This is then sorted, each number is attributed to a card, and then the result is created to be displayed in the main window. This is similar to how it worked before, but instead it’s now entered into the StringVars fortheinterfacetowardstheendofthescript 23Startgame When we activate the button that starts game, it immediately sends us to the rest of the code. This would also work if you had the button go to the throws function instead; however, you canaddotherfunctionstothispartifyouwish. 24Diceselection The first thing we do is find out what checkboxes have been ticked by the player. We then put these in a list so we can change out the correct dice numbers. We’ve also brought in dice so we can check against that what the current dicerollsare. 20Handofdice Like roll, nothing has changed for the hand function. It’s simply now placed outside the gui function for the exact same reasons. It also means that you can easily import this functionintoanotherscriptifyouwish. 21GUIstart As we’ve mentioned last month and in the Hangman code, we put all the GUI code into afunctionsothatwecancallonitwhenwewant to. In this case, pressing the Poker Dice button on the main interface activates pokerdice.gui, whichisthisfunction. 18Outsidedice For Poker Dice, there’s only one variable to show at any one time, the dice. Again, due to the nested functions, and because we’re using Python2,weneedtocallitwithglobalfromhere tomakesurethegamecanberesetproperly. 19Dicerolls The roll function has been removed from the gui function so as not to create any code errors with some of its variables. It can be easily called within the nested functions. It hasn’t changedatallfromtheoriginalcode. 17Startover The usual array of command-line compatibility and module importing here. The groupby function is specifically imported here fordiceanalysis. #!/usr/bin/env python2 from Tkinter import * from ttk import * import random from itertools import groupby dice = 0 def roll(roll_number): numbers = range(1,7) dice = range(roll_number) iterations = 0 while iterations roll_number: iterations = iterations + 1 dice[iterations-1] = random.choice(numbers) return dice def hand(dice): dice_hand = [len(list(group)) for key, group in groupby(dice)] dice_hand.sort(reverse=True) straight1 = [1,2,3,4,5] straight2 = [2,3,4,5,6] if dice == straight1 or dice == straight2: return “a straight!” elif dice_hand[0] == 5: return “five of a kind!” elif dice_hand[0] == 4: return “four of a kind!” elif dice_hand[0] == 3: if dice_hand[1] == 2: return “a full house!” else: return “three of a kind.” elif dice_hand[0] == 2: if dice_hand[1] == 2: return “two pair.” else: return “one pair.” else: return “a high card.” def gui(): global dice dice = roll(5) dice.sort() nine = 1 ten = 2 jack = 3 queen = 4 king = 5 ace = 6 names = { nine: “9”, ten: “10”, jack: “J”, queen: “Q”, king: “K”, ace: “A” } result = “You have “ + hand(dice) def game(): throws() def throws(): global dice dice1_check = dice1.get() dice2_check = dice2.get() dice3_check = dice3.get() dice4_check = dice4.get() dice5_check = dice5.get() dice_rerolls = [dice1_check, dice2_check, dice3_check, dice4_ check, dice5_check] THEPOKERDICEGUI Two things are being printed out on the initial window. The first set of dice, ordered in the way we did last time, and the current hand. The checkboxes activate a specific number that is used when re-rolling dice with the Rerollbutton. EXTRAGAMEFUNCTIONS We mentioned that the game function doesn’t necessarily need to be used right now. You can either clean up the code and remove it, or add extra functions, such as being able to choose a random new selection of dice, or making it two-player. Experimentwith whatyouwanttodo!
  • 55.
    The Python Book55 Python essentials 25Dicetore-roll If a checkbox isn’t selected, we have it set to give a zero value. We want to remove these from the list so that the correct dice are changed, so we use the for loop to check each part of the list, and then use the remove functionwhentheelementdoesequalzero. 26Earlyfinish If no dice have been selected to re-roll, the list will contain all 0s, which will then be removed. The length of this list will then also be zero,meaningwecanusethattoendthegameif theplayerhitsRerollwithoutselectinganydice. 27New dice This else function works roughly the same as before. We start by getting the necessary information for how many dice to roll, andalisttoputthere-rolls.Wethenrollasmany newdiceasweneedwiththefirstwhileloop 28Gameover We use the same kind of while loop to replace the new numbers into the original list, much like last time. Then the dice are re-sorted, analysed, joined as a string and then set into the interface’s variable. The final hand message is alsocreateandset. 29Graphicalvariables As we’re rolling the dice as soon as we launch the game, but the interface code doesn’t start until the end, you can see that after creating the necessary variables, we also then set them. Of note, the dice have to be made into a string separately with the for loop before addingtothevariable. 30Checkbuttons The main new addition to this code is the check buttons with Checkbutton. You can set an on and off value, with default off being 0. We’ve made it so that the check buttons return the same number as the dice they’re changing, which we explained how we used earlier in the code. The variable option sets whatever the outcomeistothespecificTkintervariable. 25 26 27 28 29 30 Thecheck buttonsarenew for i in range(len(dice_rerolls)): if 0 in dice_rerolls: dice_rerolls.remove(0) if len(dice_rerolls) == 0: result = “You finish with “ + hand(dice) hand_output.set(result) else: roll_number = len(dice_rerolls) number_rerolls = roll(roll_number) dice_changes = range(len(dice_rerolls)) iterations = 0 while iterations roll_number: iterations = iterations + 1 dice_changes[iterations-1] = number_rerolls[iterations-1] iterations = 0 while iterations roll_number: iterations = iterations + 1 replacement = number_rerolls[iterations-1] dice[dice_changes[iterations-1]] = replacement dice.sort() new_dice_list = [0,0,0,0,0] for i in range(len(dice)): new_dice_list[i] = names[dice[i]] final_dice = “ “.join(new_dice_list) dice_output.set(final_dice) final_result = “You finish with “ + hand(dice) hand_output.set(final_result) def reset_game(): global dice dice = roll(5) dice.sort() for i in range(len(dice)): empty_dice[i] = names[dice[i]] first_dice = “ “.join(empty_dice) dice_output.set(first_dice) result = “You have “ + hand(dice) hand_output.set(result) pd_window = Toplevel() pd_window.title (“Poker Dice”) dice_output = StringVar() empty_dice = [0,0,0,0,0] for i in range(len(dice)): empty_dice[i] = names[dice[i]] first_dice = “ “.join(empty_dice) dice_output.set(first_dice) hand_output = StringVar() hand_output.set(result) dice1 = IntVar() dice2 = IntVar() dice3 = IntVar() dice4 = IntVar() dice5 = IntVar() result_set = StringVar() player_score = IntVar() computer_score = IntVar() pd_frame = Frame(pd_window, padding = ‘3 3 12 12’, width = 300) pd_frame.grid(column=0, row = 0, sticky=(N,W,E,S)) pd_frame.columnconfigure(0, weight=1) pd_frame.rowconfigure(0,weight=1) Label(pd_frame, text=’Dice’).grid(column=3, row = 1) Label(pd_frame, textvariable = dice_output).grid(column=3, row = 2) Label(pd_frame, textvariable = hand_output).grid(column=3, row = 3) Label(pd_frame, text=’Dice to Reroll?’).grid(column=3, row = 4) reroll1 = Checkbutton(pd_frame, text = “1”, variable = dice1, onvalue = 1, offvalue = 0).grid(column=1, row = 5) reroll2 = Checkbutton(pd_frame, text = “2”, variable = dice2, onvalue = 2, offvalue = 0).grid(column=2, row = 5) reroll3 = Checkbutton(pd_frame, text = “3”, variable = dice3, onvalue = 3, offvalue = 0).grid(column=3, row = 5) reroll4 = Checkbutton(pd_frame, text = “4”, variable = dice4, onvalue = 4, offvalue = 0).grid(column=4, row = 5) reroll5 = Checkbutton(pd_frame, text = “5”, variable = dice5, onvalue = 5, offvalue = 0).grid(column=5, row = 5) pd_reroll_button = Button(pd_frame, text = “Reroll”, command = game).grid(column = 3, row = 6) replay_button = Button(pd_frame, text = “Reset”, command = reset_game).grid(column = 3, row = 7) if __name__ == ‘__main__’: gui() ONEWINDOW Thewaywe’vemadetheseTkinter interfaces is to have the games launch in a separate window. You can have them all running in one window, though, by replacing the labels and buttons of the original interface by putting them as different functions or classes. Make sure to add a quit button to the games that lets you go back to the main page.
  • 56.
    56 The PythonBook Python essentials Buildanappfor AndroidwithPython MasterKivy,theexcellentcross-platformapplication frameworktomakeyourfirstAndroidapp… The great thing about Kivy is there are loads of directions we could take it in to do some pretty fancy things. But, we're going to make a beeline for one of Kivy's coolest features - the ability it affords you to easily run your programs on Android. We'll approach this by first showing how to make a new app, this time a dynamic Breakout- to be drawn anywhere on your screen and on any widget type. Beforewecandoanyofthiswe'llneedaclass for each kind of game object, which we’re going to pre-populate with some of the properties that we'll need later to control them. Remember from last time, Kivy properties are special attributes declared at class level, which (among other things) can be modified via kv language and dispatch events when they are modified (Fig. 02). The Game class will be one big widget containing the entire game. We've specifically made it a subclass of FloatLayout because this special layout is able to position and size its children in proportion to its own position and size – so no matter where we run it or how we resize the window, it will place all the game objects appropriately. Next we can use Kivy's graphics instructions to draw various shapes on our widgets. We'll just demonstrate simple rectangles to show their locations, though there are many more advanced options you might like to investigate. In a Python file we can apply any instruction by declaring it on the canvas of any widget, an example of which is shown in Fig. 03. This would draw a red rectangle with the same position and size as the player at its moment of instantiation – but this has a Before anything else, let's throw together a basic Kivy app (Fig. 01). We've pre-imported the widget types we'll be using, which this time are just three: the basic Widget with no special behaviour, the ModalView with a pop-up behaviour as used last time, and the FloatLayout as we will explaine later. Kivy has many other pre-built widgets for creating GUIs, but this time we’re going to focus on drawing the whole GUI from scratch using Kivy's graphics instructions. These comprise either vertex instructions to create shapes (including rectangles, lines, meshes, and so on) or contextual graphics changes (such as translation, rotation, scaling, etc), and are able QHerewe'vedrawnallthesimplegraphics forourgame…nowwejusthavetomake theshapesactuallydosomething! style game. We'll then be able to compile this straight to an Android APK that you can use just likeanyother. Of course, once you have mastered the basic techniques you aren't limited to using any particular kind of app, as even on Android you can make use of all your favourite Python librariestomakeanysortofprogramyoulike. Once you've mastered Kivy, your imagination is the only limit. If you're pretty new to Kivy, don't worry, we won't assume that you have any pre-existing knowledge. As long as you have mastered some of the Python tutorials in this book so far, and so have a fairly good understanding of the language, you shouldn’t haveanyproblemsfollowingalong.
  • 57.
    The Python Book57 Python essentials from kivy.app import App from kivy.uix.widget import Widget from kivy.uix.floatlayout import FloatLayout from kivy.uix.modalview import ModalView __version__ = '0.1' # Used later during Android compilation class BreakoutApp(App): pass BreakoutApp().run() Fig. 01 from kivy.graphics.context_instructions import Color from kivy.graphics.vertex_instructions import Rectangle class Player(Widget): def __init__(self , **kwargs): super(Player , self).__init__(**kwargs) with self .canvas: Color(1, 0, 0, 1) # r , g, b, a - red Rectangle(pos=self .pos, size=self .size) # or without the with syntax, self .canvas.add(... ) Fig. 03 from kivy.properties import (ListProperty, NumericProperty, ObjectProperty, StringProperty) class Game(FloatLayout): # Will contain everything blocks = ListProperty([]) player = ObjectProperty() # The game's Player instance ball = ObjectProperty() # The game's Ball instance class Player(Widget): # A moving paddle position = NumericProperty(0.5) direction = StringProperty('none') class Ball(Widget): # A bouncing ball # pos_hints are for proportional positioning, see below pos_hint_x = NumericProperty(0.5) pos_hint_y = NumericProperty(0.3) proper_size = NumericProperty(0. ) velocity = ListProperty([0.1, 0.5]) class Block(Widget): # Each coloured block to destroy colour = ListProperty([1, 0, 0]) Fig. 02 problem, unfortunately, as the drawing is static. When we later move the player widget, the red rectangle will stay in the same place, and the widget will be invisible when it is in its real position. We could fix this by keeping references to our canvas instructions and repeatedly updating their properties to track the player, but there's actually an easier way to do all of this - we can use the Kivy language we introduced last time. It has a special syntax for drawing on the widget canvas, which we can use to draw each of our widget shapes: Player: canvas: Color: rgba: 1, 1, 1, 1 Rectangle: pos: self.pos size: self.size Ball: canvas: Color: rgb: 1, 0.55, 0 Rectangle: pos: self.pos size: self.size Block: canvas: Color: rgb: self.colour # A property we predefined above Rectangle: pos: self.pos size: self.size Color: rgb: 0.1, 0.1, 0.1 Line: rectangle: [self.x, self.y, self.width, self.height] The canvas declaration is special, underneath it we can write any canvas instructions we like. Don't get confused, canvas is not a widget and nor are graphics instructions like Line. This is just a special syntax that is unique to the canvas. Instructions all have different properties that can be set, like the pos and size of the rectangle, and you can check the Kivy documentation online for all the possibilities. The biggest advantage is that although we still declare simple canvas instructions, kv language is able to detect what Kivy properties we have referred to and automatically track them, so when they are updated (the widget moves or is resized) the canvas instructions move to follow! Onceyouhavethebasictechniques, youaren’tlimitedtooneapp…your imaginationistheonlylimit
  • 58.
    58 The PythonBook Python essentials You probably noticed we had one of the Block’s ‘Color’ instructions refer to its colour property. This means that we can change the property any time to update the colour of the block, or in this case to give each block arandomcolour(Fig.04). Now that each of our widgets has a graphical representation, let’s now tell our Game where to place them, so that we can start up the app and actuallyseesomethingthere. class Game(FloatLayout): def setup_blocks(self): for y_jump in range(5): for x_jump in range(10): block = Block(pos_hint={ 'x' : 0.05 + 0.09*x_jump, 'y' : 0.05 + 0.09*y_jump}) self .blocks.append(block) self .add_widget(block) class BreakoutApp(App): def build(self): g = Game() g.setup_blocks() return g Here we create the widgets we want then use add_widgettoaddthemtothegraphicstree.Our root widget on the screen is an instance of Game andeveryblockisaddedtothattobedisplayed. The only new thing is that every Block has been given a pos_hint. All widgets have this special property, and it is used by FloatLayouts like our Game to set their positionproportionatetothelayout. The dictionary is able to handle various parameters, but in this case ‘x’and ‘y’ give x and y Block position as a relative fraction of the parentwidthandheight. You can run the app now, and this time it will add 50 blocks to the Game before displaying it on the screen. Each should have one of the three possible random colours and be positioned in a grid, but you'll now notice their sizes haven't been manually set so they all overlap. We can fix this by setting their size_hint properties – and let's also take this opportunity to do the same for the otherwidgets aswell(Fig.05). This takes care of keeping all our game widgets positioned and sized in proportion to the Game containing them. Notice that the Player and Ball use references to the properties we set earlier, so we'll be able to move them by just setting these properties and letting kv language automatically update theirpositions. The Ball also uses an extra property to remain square rather than rectangular, just because the alternative would likely look a littlebitodd. We've now almost finished the basic graphics of our app! All that remains is to add aBallandaPlayerwidgettotheGame. Game: ball: the_ball player: the_player Ball: id: the_ball Player: id: the_player You can run the game again now, and should be able to see all the graphics working properly. Nothing moves yet, but thanks to the FloatLayout everything should remain in proportion if you resize the game/window. Now we just have to add the game mechanics. For a game like this you usually want to run some update function many times per second, updating the widget positions and carrying out game logic – in this case collisions with the ball (Fig. 06). The Clock can schedule any function at any time, either once or repeatedly. A function scheduled at interval automatically receives the timesinceitslastcall(dthere),whichwe'vepassed through to the ball and player via the references we created in kv language. It's good practice to scale the update (eg ball distance moved) by this dt, so things remain stable even if something interrupts the clock and updates don't meet theregular1/60syouwant. At this point we have also added the first steps toward handling keyboard input, by binding to the kivy Window to call a method of the Player every time a key is pressed. We can then finish offthePlayerclassbyaddingthiskeyhandleralong withtouch/mouseinput. class Player(Widget): def on_touch_down(self , touch): self .direction = ( 'right' if touch.x self .parent. center_x else 'left') def on_touch_up(self , touch): self .direction = 'none' def on_key_down(self , keypress, scancode, *args): if scancode == 275: self .direction = 'right' elif scancode == 276: self .direction = 'left' else: self .direction = 'none' def on_key_up(self , *args): self .direction = 'none' def update(self , dt): dir_dict = {'right' : 1, 'left' : -1, QRunningtheappshowsourcolouredblocksonthe screen…buttheyalloverlap!Wecanfixthateasily
  • 59.
    The Python Book59 Python essentials 'none' : 0} self .position += (0.5 * dt * dir_ dict[self .direction]) These on_touch_ functions are Kivy's general method for interacting with touch or mouse input, they are automatically called when the input is detected and you can do anything you like in response to the touches you receive. In this case we set the Player's direction property in response to either keyboard and touch/mouse input, and use this direction to move the Player when its updatemethodiscalled.Wecanalsoaddtheright behaviourfortheball(Fig.07). This makes the ball bounce off every wall by forcing its velocity to point back into the Game, as well as bouncing from the player paddle – but with an extra kick just to let the ball speed change. It doesn't yet handle any interaction with the blocks or any win/lose conditions, but it does try to call Game.lose() if the ball hits the bottom of the player's screen, solet'snowaddinsomegameendcodetohandle allofthis(Fig.08).AndthenaddthecodeinFig.09 toyour'breakout.kv'file. This should fully handle the loss or win, opening a pop-up with an appropriate message and providing a button to try again. Finally, we have to handle destroying blocks when the ballhitsthem(Fig.10). Thisfullycoverstheselastconditions,checking collision via Kivy's built-in collide_widget method that compares their bounding boxes (pos and size). The bounce direction will depend on how far the ball has penetrated, as this will tell us how it firstcollidedwiththeBlock. So there we have it, you can run the code to play your simple Breakout game. Obviously it's very simple right now, but hopefully you can see lots of different ways to add whatever extra behaviour you like – you could add different types of blocks and power-ups, a lives system, more sophisticated paddle/ball interaction, or even build a full game interface with a menu and settingsscreenaswell. We’rejustgoingtofinishshowingonecoolthing that you can already do – compile your game for Android!GenerallyspeakingyoucantakeanyKivy app and turn it straight into an Android APK that will run on any of your Android devices. You can even access the normal Android API to access hardware or OS features such as vibration, sensorsornativenotifications. We'll build for Android using the Buildozer tool, and a Kivy sister project wrapping other build tools to create packages on different systems. This takes care of downloading and running the Android build tools (SDK, NDK, etc) and Kivy's Python-for-AndroidtoolsthatcreatetheAPK. import random class Block(Widget): def __init__(self , **kwargs): super(Block, self).__init__(**kwargs) self .colour = random.choice([ (0.78, 0.28, 0), )0.28, 0.63, 0.28), )0.25, 0.28, 0.78)]) Fig. 04 from kivy.clock import Clock from kivy.core.window import Window from kivy.utils import platform class Game(FloatLayout): def update(self , dt): self .ball.update(dt) # Not defined yet self .player .update(dt) # Not defined yet def start(self , *args): Clock.schedule_interval(self .update, 1./60. ) def stop(self): Clock.unschedule(self .update) def reset(self): for block in self .blocks: self .remove_widget(block) self .blocks = [] self .setup_blocks() self .ball.velocity = [random.random(), 0.5] self .player .position = 0.5 class BreakoutApp(App): def build(self): g = Game() if platform() != 'android' : Window.bind(on_key_down=g.player .on_key_down) Window.bind(on_key_up=g.player .on_key_up) g.reset() Clock.schedule_once(g.start, 0) return g Fig. 06 Block: size_hint: 0.09, 0.05 # ... canvas part Player: size_hint: 0.1, 0.025 pos_hint: {'x' : self .position, 'y' : 0.1} # ... canvas part Ball: pos_hint: {'x' : self .pos_hint_x, 'y' : self .pos_hint_y} size_hint: None, None proper_size: min(0.03*self .parent.height, 0.03*self .parent.width) size: self .proper_size, self .proper_size # ... canvas part Fig. 05
  • 60.
    60 The PythonBook Python essentials Here you will be needing some basic dependencies, which can be installed with ease just by using your distro's normal repositories. The main ones to use are OpenJDK7, zlib, an up-to-date Cython, and Git. If you are using a 64-bit distro you will also be in need of 32-bit compatibility libraries for zlib, libstdc++,aswellaslibgcc.Youcanthengoonand downloadandinstallBuildozer: git clone git://github.com/kivy/buildozer cd buildozer sudo python2.7 setup.py install When you’re done with that part you can then go on and navigate to your Kivyapp,andyou’llhavetonamethemaincodefile ‘main.py’, this is the access point that the Android APKwillexpect.Then: buildozer init This creates a ‘buildozer.spec’ file, a settings file containing all the information that Buildozer needs to create your APK, from the name and version to thespecificAndroidbuildoptions.Wesuggestthat you check through the whole file just to see what's available but most of the default settings will be fine,theonlythingwesuggestchangingis(Fig.11). There are various other options you will often want to set, but none are really all that vital right now, so you’re able to immediately tell Buildozer to buildyourAPKandgetgoing! buildozer android debug This will take some time, so be patient and it will workoutfine.Whenyoufirstrunit,itwilldownload both the Android SDK and NDK, which are large (at least hundreds of megabytes) but vital to the build. It will also take time to build these and to compile the Python components of your APK. A lot ofthisonlyneedstobedoneonce, asfuturebuilds will take a couple of minutes if you change the buildozer.spec, or just a few seconds if you've only changedyourcode. The APK produced is a debug APK, and you can install and use it but there are extra steps if you wanttofullydigitallysignitsothatitcanbeposted on the Play store. This isn't hard, and Buildozer can do some of the work, but you can check the documentationonlineforfulldetails. Assuming everything goes fine (it should!), your Android APK will be in a newly created 'bin' directory with the name ‘KivyBreakout-0.1-debug. apk’. You can send it to your phone any way you like (eg email), though you may need to enable application installation from unknown sources in yourSettingsbeforeyoucaninstallit. class Ball(Widget) def update(self , dt): self .pos_hint_x += self .velocity[0] * dt self .pos_hint_y += self .velocity[1] * dt if self .right self .parent.right: # Bounce from right self .velocity[0] = -1 * abs(self .velocity[0]) if self .x self .parent.x: # Bounce from left self .velocity[0] = abs(self .velocity[0]) if self .top self .parent.top: # Bounce from top self .velocity[1] = -1 * abs(self .velocity[1]) if self .y self .parent.y: # Lose at bottom self .parent.lose() # Not implemented yet self .bounce_from_player(self .parent.player) def bounce_from_player(self , player): if self .collide_widget(player): self .velocity[1] = abs(self .velocity[1]) self .velocity[0] += ( 0.1 * ((self .center_x - player .center_x) / player .width)) Fig. 07 class GameEndPopup(ModalView): message = StringProperty() game = ObjectProperty() class Game(Widget): def lose(self): self .stop() GameEndPopup(message='[color=#ff0000]You lose![/color]' , game=self).open() def win(self): # Not called yet, but we'll need it later self .stop() GameEndPopup(message='[color=#00ff00]You win![/color]' , game=self).open() Fig. 08 GameEndPopup: size_hint: 0.8, 0.8 auto_dismiss: False # Don't close if player clicks outside BoxLayout: orientation: 'vertical' Label: text: root.message font_size: 60 markup: True halign: 'center' Button: size_hint_y: None height: sp(80) text: 'Play again?' font_size: 60 on_release: root.game.start(); root.dismiss() Fig. 09
  • 61.
    self .parent.do_layout() self .parent.destroy_blocks(self) class Game(FloatLayout): def destroy_blocks(self ,ball): for i, block in enumerate(self .blocks): if ball.collide_widget(block): y_overlap = ( ball.top - block.y if ball.velocity[1] 0 else block.top - ball.y) / block.size_hint_y x_overlap = ( ball.right - block.x if ball.velocity[0] 0 else block.right - ball.x) / block.size_hint_x if x_overlap y_overlap: ball.velocity[0] *= -1 else: ball.velocity[1] *= -1 self .remove_widget(block) self .blocks.pop(i) if len(self .blocks) == 0: self .win() return # Only remove at most 1 block per frame Fig. 10 title = Kivy Breakout # Displayed in your app drawer package.name = breakout # Just a unique identifying string, # along with the package.domain fullscreen = 0 # This will mean the navbar is not covered log_level = 2 # Not vital, but this will print a lot more debug # information and may be useful if something # goes wrong Fig. 11 PuttingyourAPK onthePlayStore Findouthowtodigitallysignarelease APKanduploadittoanappstoreof yourchoice 1 BuildandsignareleaseAPK First we have to begin by creating a personal digital key, then using it to digitally sign a special release version of the APK. Run these commands, and follow the instructions they then giveyou. ## Create your personal digital key ## You can choose your own ## keystore name, alias, and passwords. $ keytool -genkey -v -keystore test- release-key.keystore -alias test-alias -keyalg RSA -keysize 2048 -validity 10000 ## Compile your app in release mode $ buildozer android release ## Sign the APK with your new key $ jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore ./test-release-key.keystore ./bin/KivyBreakout-0.1-release- unsigned.apk test-alias ## Align the APK zip file $ ~/ .buildozer/android/platform/android- sdk-21/tools/zipalign -v 4 ./bin/KivyBreakout-0.1-release- unsigned.apk ./bin/KivyBreakout-0.1-release.apk 3 Uploadyourapptothestore Click 'Add new application' to submit your app the store, including uploading your APK and adding description text. When everything is ready, simply click Publish, and it should take just a few hoursforyourapptogolive! 2 SignupasaGoogle PlayDeveloper Visit https://play.google.com/ apps/publish/signup, and follow the instructions. You'll need to pay a one-off $25 charge, but then you can uploadasmanyappsasyoulike. Python essentials QYourgameshouldrunonanymodernAndroiddevice…you canevenbuildareleaseversionandpublishtoanappstore!
  • 62.
    62 The PythonBook Python essentials Pythonprovidesquickandeasywaytobuild applications,includingwebapps.Readontofindout howtouseittobuildafeature-completewebapp MakingwebappswithPython 02ConfiguringtheDjangoproject settings Before we start working on the application, let’s configure the Django project as per our requirements. Edit ludIssueTracker/settings.py as follows (onlypartsrequiringmodificationareshown): Database Settings: We will be using SQLite3 asourdatabasesystem. NOTE: Red text indicates new code or updatedcode. ‘default’: { ‘ENGINE’: ‘django. db.backends.sqlite3’, ‘NAME’: ‘ludsite.db3, Pathsettings Django requires an absolute path for directory settings. But we want to be able to pass in the relative directory references. In order to do that we will add a helper Python function. Insert the followingcodeatthetopofthesettings.pyfile: import os def getabspath(*x): return os.path.join(os.path. abspath(os.path.dirname(__file__)), *x) Nowyoucanupdatethepathoptions: @code TEMPLATE_DIRS = ( getabspath(‘templates’) ) MEDIA_ROOT = getabspath(‘media’) Python is known for its simplicity and capabilities. At this point it is so advanced that there is nothing you cannot do with Python, and conquering the web is one of the possibilities. When you are using Python for web developmentyougetaccess toahuge catalogue of modules and community support – make the mostofthem. Web development in Python can be done in many different ways, right from using the plain old CGI modules to utilising fully groomed web frameworks. Using the latter is the most popular method of building web applications with Python, since it allows you to build applications without worrying about all that low-level implementation stuff. There are many web frameworks available for Python, such as Django, TurboGears and Web2Py. For this tutorial we will be using our current preferred option,Django. 01CreatingtheDjangoProject magazineissuetracker The django-admin.py file is used to create new Django projects. Let’s create one for our issue trackerproject… In Django, a project represents the site and its settings. An application, on the other hand, represents a specific feature of the site, like blogging or tagging. The benefit of this approach is that your Django application becomes portable and can be integrated with other Djangositeswithverylittleeffort. $ django-admin.py startproject ludIssueTracker A project directory will be created. This will also act as the root of your development web server that comes with Django. Under the project directoryyouwillfindthefollowingitems… manage.py: Python script to work with your project. ludIssueTracker: A python package (a directory with __init__.py file) for your project. This package contains your project’s settings and configurationdata. ludIssueTracker/settings.py: This file contains alltheconfigurationoptionsfortheproject. ludIssueTracker/urls.py: This file contains variousURLmappings. wsgi.py: An entry-point for WSGI-compatible web servers to serve your project. Only useful when you are deploying your project. For this tutorialwewon’tbeneedingit. Resources Python2.7: https://www.python.org/download/releases/2.7/ Djangoversion1.4: https://www.djangoproject.com/
  • 63.
    The Python Book63 Python essentials 03Creatingludissuesapp In this step we will create the primary app for our site, called ludissues. To do that, we willusethemanage.pyscript: $ python manage.py startapp ludissues We will need to enable this app in the config file aswell: INSTALLED_APPS = ( ............. 'django.contrib.admin', ‘ludissues’, ) 04Creatingthedatamodel This is the part where we define the data model for our app. Please see the inline commentstounderstandwhatishappening. Fromdjango.dbimportmodels: # We are importing the user authentication module so that we use the built # in authentication model in this app from django.contrib.auth.models import User # We would also create an admin interface for our app from django.contrib import admin # A Tuple to hold the multi choice char fields. # First represents the field name the second one repersents the display name ISSUE_STATUS_CHOICES = ( MEDIA_URL = ‘/media/’ Nowwewillneedtoenabletheadmininterface forourDjangosite.ThisisaneatfeatureofDjango which allows the automatic creation of an admin interfaceofthesitebasedonthedatamodel.The admin interface can be used to add and manage contentforaDjangosite. Uncommentthefollowingline: INSTALLED_APPS = ( ‘django.contrib.auth’, ‘django.contrib.contenttypes’, ‘django.contrib.sessions’, ‘django.contrib.sites’, ‘django.contrib.messages’, ‘django.contrib.staticfiles’, ‘django.contrib.admin’, # ‘django.contrib.admindocs’, ) ('new', 'New'), ('accepted','Accepted'), ('reviewed','Reviewed'), ('started','Started'), ('closed','Closed'), ) class Issue(models.Model): # owner will be a foreign key to the User model which is already built-in Django owner = models.ForeignKey(User,n ull=True,blank=True) # multichoice with defaulting to new status = models.CharField(max_ length=25,choices=ISSUE_STATUS_ CHOICES,default='new') summary = models.TextField() # date time field which will be set to the date time when the record is created opened_on = models. DateTimeField('date opened', auto_ now_add=True) modified_on = models. DateTimeField('date modified', auto_ now=True) def name(self): return self.summary. split('n',1)[0] # Admin front end for the app. We are also configuring some of the # built in attributes for the admin interface on # how to display the list, how it will be sorted # what are the search fields etc. class IssueAdmin(admin.ModelAdmin): date_hierarchy = 'opened_on' list_filter = ('status','owner') list_display = ('id','name','sta tus','owner','modified_on') search_fields = ['description','status'] # register our site with the Django admin interface admin.site. register(Issue,IssueAdmin) To have the created data model reflected in the database,runthefollowingcommand: $ python manage.py syncdb You’llbealsoaskedtocreateasuperuserforit: You just installed Django's auth system, which means you don't have any superusers defined. Would you like to create one now? (yes/no): yes 05Enablingtheadminsite The admin site is already enabled, but we need to enable it in the urls.py file – this contains the regex-based URL mapping from modeltoview.Updatetheurls.pyfileasfollows: from django.conf.urls import patterns, include, url from django.contrib import admin admin.autodiscover() urlpatterns = patterns(‘’, url(r’^admin/’, include(admin. site.urls)), ) 06StartingtheDjangowebserver Django includes a built-in web server which is very handy to debug and test Django applications. Let’s start it to see how our admin interfaceworks… Tostartthewebserver: $ python manage.py runserver If you do not have any errors in your code, the server should be available on port 8000. To launch the admin interface, navigate your browsertohttp://localhost:8000/admin. You will be asked to log in here. Enter the username and password that you created while syncingthedatabase. After logging in, you will notice that all the apps installedinyourprojectareavailablehere.Weare onlyinterestedintheAuthandLudIssuesapp. You can click the +Add to add a record. Click the Add button next to Users and add a few users to the site. Once you have the users inside the system, youcannowaddafewissuestothesystem. Q Adminloginscreen
  • 64.
    64 The PythonBook Python essentials 07Creatingthepublicuserinterface forludissues At this point, the admin interface is working. But we need a way to display the data that we have added using the admin interface. But there is no publicinterface.Let’screateitnow. We will have to begin by editing the main urls.py(ludIssueTracker/urls.py). urlpatterns = patterns(‘’, (r’^’,include(‘ludissues. urls’)), (r’^admin/’, include(admin.site. urls)), ) This ensures that all the requests will be processedbyludissues.urlsfirst. 08Creatingludissues.url Create a urls.py file in the app directory (ludissues/urls.py)withthefollowingcontent: from django.conf.urls import patterns, include, url # use ludissues model from models import ludissues # dictionary with all the objects in ludissues info = { ‘queryset’:ludissues.objects. all(), } # To save us writing lots of python code # we are using the list_detail generic view #list detail is the name of view we are using urlpatterns = patterns(‘django. views.generic.list_detail’, #issue-list and issue-detail are the 09Settinguptemplateandmedia directories In this step we will create the template and media directories. We have already mentioned thetemplatedirectoryas TEMPLATE_DIRS = ( getabspath(‘templates’) ) Which translates to ludIssueTracker/ ludIssueTracker/templates/. Since we will be accessing the templates from the ludissues app, the complete directory path would be ludIssueTracker/ludIssueTracker/templates/ ludissues. Create these folders in your projectfolder. Also, create the directory ludIssueTracker/ ludIssueTracker/media/ for holding the CSS file. Copy the style.css file from the resources directoryofthecodefolder. Toservefilesfromthisfolderweneedtomake it available publicly. To do that, open settings.py and add the following lines in ludIssueTracker/ ludIssueTracker/urls.py: from django.conf.urls import patterns, include, url from django.conf import settings # Uncomment the next two lines to enable the admin: from django.contrib import admin admin.autodiscover() urlpatterns = patterns(‘’, (r’^’,include(‘ludissues. urls’)), (r’^admin/’, include(admin.site. Q Adminhomepage Q The‘Addissue’menu Q Thelistviewforissues Click the Add button next to Issues. Here you will notice that you can enter Owner, Status and Summary for the issue. But what about the opened_on and modified_on field that we defined while modelling the app? They are not here because they are not supposed to be entered by the user. opened_on will automatically set to the date time it is created and modified_on will automatically set to the datetimeonwhichanissueismodified. Another cool thing is that the owner field is automaticallypopulatedwithalltheusersinside thesite. We have defined our list view to show ID, name, status, owner and ‘modified on’ in the model. You can get to this view by navigating to http://localhost:8000/admin/ludissues/issue/. template names #which will be looked in the default template #directories url(r’^$’,’object_ list’,info,name=’issue-list’), url(r’^(?Pobject_ idd+)/$’,’object_ detail’,info,name=’issue-detail’), ) To display an issue list and details, we are using a Django feature called generic views. In this case we are using views called list and details. This allow us to create an issue list view and issue detail view. These views are then applied using the issue_list.html and issue_detail.html template. In the following steps we will create the template files.
  • 65.
    The Python Book65 Python essentials 10Creatingthetemplatefiles Templates will be loaded from the ludIssueTracker/ludIssueTracker/templates directory. In Django, we start with the ludIssueTracker/ludIssueTracker/templates/ base.html template. Think of it as the master templatewhichcanbeinheritedbyslaveones. ludIssueTracker/ludIssueTracker/templates/ base.html !DOCTYPE html PUBLIC “-//W3C//DTD XHTML Strict//EN” “ HYPERLINK “http://www.w3.org/ TR/xhtml1/DTD/xhtml1-strict.dtd” http://www.w3.org/TR/xhtml1/DTD/ xhtml1-strict.dtd” html head title{% block title %}{% endblock %}LUD Issues/title link rel=”stylesheet” href=”{{ MEDIA_URL }}style.css” type=”text/css” media=”screen” / /head body div id=”hd” h1LUD Issue Tracker/span/h1 /div div id=”mn” ul lia href=”{% url issue-list %}” class=”sel”View Issues/a/li lia href=”/admin/”Admin Site/a/li /ul /div div id=”bd” {% block content %} {% endblock %} /div /body /html {{variablename}}representsaDjangovariable. (% block title %} represents blocks. Contents of a block are evaluated by Django and are displayed. These blocks can be replaced by the childtemplates. Q ThemagazineIssueTrackerinaction–listofissues urls)), (r’^media/ (?Ppath.*)$’,’django.views.static. serve’, {‘document_root’:settings. MEDIA_ROOT}) ) Now we need to create the issue_list.html template. This template is responsible for displayingalltheissuesavailableinthesystem. ludIssueTracker/ludIssueTracker/templates/ ludissues/issue_list.html {% extends ‘base.html’ %} {% block title %}View Issues - {% endblock %} {% block content %} table cellspacing=”0” class=”column-options” tr thIssue/th thDescription/th thStatus/th thOwner/th /tr {% for issue in object_list %} tr tda href=”{% url issue- detail issue.id %}”{{ issue.id }}/ a/td tda href=”{% url issue- detail issue.id %}”{{ issue.name }}/a/td td{{ issue.status }}/td td{{ issue.owner}}/td /tr {% endfor %} /table {% endblock %} Here we are inheriting the base.html file that we created earlier. {% for issue in object_list %} runs on the object sent by the urls.py. Then we are iterating on the object_list for issue.id and issue.name. Now we will create issue_detail.html. This template is responsible for displaying the detail viewofacase. ludIssueTracker/ludIssueTracker/templates/ ludissues/issue_detail.html {% extends ‘base.html’ %} {% block title %}Issue #{{ object.id }} - {% endblock %} {% block content %} h2Issue #{{ object.id }} span{{ object.status }}/span/h2 div class=”issue” h2Information/h2 div class=”date” p class=”cr”Opened {{ object.opened_on }} ago/p p class=”up”Last modified {{ object.modified_on }} ago/p /div div class=”clear”nbsp;/div div class=”block w49 right” p class=”ass title”Owner/ p p class=”ass”{{ object. owner }}/p /div div class=”clear”nbsp;/div div class=”block” p class=”des title”Summary/p p class=”des”{{ object. summary }}/p /div /div {% endblock %} And that’s everything! The issue tracker app is now complete and ready to use. You can now point your browser at localhost:8000 to start usingtheapp.
  • 66.
    66 The PythonBook Python essentials Pythonisaprogramminglanguagethatletsyouworkmorequicklyand integrateyoursystemsmoreeffectively.Today,Pythonisoneofthemost popularprogramminglanguagesintheopensourcespace.Lookaround andyouwillfinditrunningeverywhere,fromvariousconfigurationtools toXMLparsing.Hereisthecollectionof50gemstomakeyourPython experienceworthwhile… Basics 1.RunningPythonscripts On most of the UNIX systems, you can run Pythonscriptsfromthecommandline. $ python mypyprog.py 2.RunningPython programsfrom Pythoninterpreter The Python interactive interpreter makes it easy to try your first steps in programming and using all Python commands. You just issue each command at the command prompt (), one by one,andtheanswerisimmediate. Python interpreter can be started by issuing thecommand: $ python kunal@ubuntu:~$ python Python 2.6.2 (release26-maint, Apr 19 2009, 01:56:41) [GCC 4.3.3] on linux2 Type “help”, “copyright”, “credits” or “license” for more information. type commands here Inthisarticle,allthecodestartingatthe symbolismeanttobegivenatthe Pythonprompt. It is also important to remember that Python takes tabs very seriously – so if you are receiving any error that mentions tabs, correct thetabspacing. 3.Dynamictyping In Java, C++, and other statically typed languages, you must specify the data type of the function return value and each function argument. On the other hand, Python is a dynamically typed language. In Python you never have to explicitly specify the data type of anything. Based on what value you assign, Python will keep track of the data typeinternally. 50 Python tips
  • 67.
    The Python Book67 Python essentials x,y = my_function.minmax(25, 6.3) 9.Moduledefinednames Example: The built-in function ‘dir()’ can be used to find out which names a module defines. It returns a sortedlistofstrings. import time dir(time) [‘__doc__’, ‘__file__’, ‘__name__’, ‘__package__’, ‘accept2dyear’, ‘altzone’, ‘asctime’, ‘clock’, ‘ctime’, ‘daylight’, ‘gmtime’, ‘localtime’, ‘mktime’, ‘sleep’, ‘strftime’, ‘strptime’, ‘struct_ time’, ‘time’, ‘timezone’, ‘tzname’, ‘tzset’] 10.Moduleinternal documentation You can see the internal documentation (if available) of a module name by looking at .__doc__. Example: import time print time.clock.__doc__ clock() - floating point number This example returns the CPU time or real time since the start of the process or since the first call to clock(). This has as much precision as the systemrecords. 11.Passingarguments toaPythonscript Pythonletsyouaccesswhateveryouhavepassed to a script while calling it. The ‘command line’ contentisstoredinthesys.argvlist. import sys print sys.argv 12.Loadingmodulesor commandsatstartup You can load predefined modules or commands at the startup of any Python script by using the environment variable $PYTHONSTARTUP. You can set environment variable $PYTHONSTARTUP to a file which contains the instructions load necessary modulesorcommands. 13.Convertingastring todateobject You can use the function ‘DateTime’ to convert a stringtoadateobject. Example: from DateTime import DateTime dateobj = DateTime(string) 14.Convertingalist toastringfordisplay You can convert a list to string in either of the followingways. 1stmethod: mylist = [‘spam’, ‘ham’, ‘eggs’] print ‘, ‘.join(mylist) spam, ham, eggs 2ndmethod: print ‘n’.join(mylist) spam ham eggs 15.Tabcompletion inPythoninterpreter You can achieve auto completion inside Python interpreterbyaddingtheselinestoyour.pythonrc file(oryourfileforPythontoreadonstartup): import rlcompleter, readline readline.parse_and_bind(‘tab: complete’) This will make Python complete partially typed function, method and variable names when you presstheTabkey. 16.Python documentationtool Youcanpopupagraphicalinterfaceforsearching thePythondocumentationusingthecommand: $ pydoc -g Youwillneedpython-tkpackageforthistowork. 17.Python documentationserver You can start an HTTP server on the given port on thelocalmachine.Thiswillgiveyouanice-looking access to all Python documentation, including third-partymoduledocumentation. $ pydoc -p portNumber 18.Pythondevelopment software There are plenty of tools to help with Python development.Hereareafewimportantones: IDLE: The Python built-in IDE, with autocompletion, function signature popup help, andfileediting. IPython: Another enhanced Python shell with tab-completionandotherfeatures. Eric3: A GUI Python IDE with autocompletion, classbrowser,built-inshellanddebugger. WingIDE: Commercial Python IDE with free licence available to open-source developerseverywhere. 4.Pythonstatements Python uses carriage returns to separate statements, and a colon and indentation to separate code blocks. Most of the compiled programming languages, such as C and C++, use semicolons to separate statements and curly bracketstoseparatecodeblocks. 5. ==and=operators Python uses ‘==’ for comparison and ‘=’ for assignment. Python does not support inline assignment, so there’s no chance of accidentally assigning the value when you actually want to compareit. 6.Concatenatingstrings Youcanuse‘+’toconcatenatestrings. print ‘kun’+’al’ kunal 7.The__init__method The __init__ method is run as soon as an object of a class is instantiated. The method is useful to do any initialization you want to do with your object. The __init__ method is analogous to a constructor in C++,C#orJava. Example: class Person: def __init__(self, name): self.name = name def sayHi(self): print ‘Hello, my name is’, self.name p = Person(‘Kunal’) p.sayHi() Output: [~/src/python $:] python initmethod.py Hello, my name is Kunal 8.Modules To keep your programs manageable as they grow in size, you may want to break them up into several files. Python allows you to put multiple function definitions into a file and use them as a modulethatcanbeimportedintootherscriptsand programs.Thesefilesmusthavea.pyextension. Example: # file my_function.py def minmax(a,b): if a = b: min, max = a, b else: min, max = b, a return min, max Module Usage import my_function
  • 68.
    68 The PythonBook Python essentials 19.Executingfunctions atthetimeofPython interpretertermination You can use ‘atexit’ module to execute functions atthetimeofPythoninterpretertermination. Example: def sum(): print(4+5) def message(): print(“Executing Now”) import atexit atexit.register(sum) atexit.register(message) Output: Executing Now 9 20.Convertingfrominteger tobinary,hexadecimal andoctal Python provides easy-to-use functions – bin(), hex() and oct() – to convert from integer to binary, decimalandoctalformatrespectively. Example: bin(24) ‘0b11000’ hex(24) ‘0x18’ oct(24) ‘030’ 21.Convertingany charsettoUTF-8 You can use the following function to convert any charsettoUTF-8. data.decode(“input_charset_here”). encode(‘utf-8’) 22.Removing duplicatesfromlists If you want to remove duplicates from a list, just put every element into a dict as a key (for example with ‘none’ as value) and then check dict.keys(). from operator import setitem def distinct(l): d = {} map(setitem, (d,)*len(l), l, []) return d.keys() 23.Do-whileloops Since Python has no do-while or do-until loop constructs (yet), you can use the following methodtoachievesimilarresults: while True: do_something() if condition(): break 24.Detectingsystem platform To execute platform-specific functions, it is very usefultodetecttheplatformonwhichthePython interpreter is running. You can use ‘sys.platform’ tofindoutthecurrentplatform. Example: OnUbuntuLinux import sys sys.platform ‘linux2’ OnMacOSXSnowLeopard import sys sys.platform ‘darwin’ 25.Disablingandenabling garbagecollection Sometimes you may want to enable or disable the garbage collector at runtime. You can use the ‘gc’ module to enable or disable the garbagecollection. Example: import gc gc.enable built-in function enable gc.disable built-in function disable 26.UsingC-basedmodules forbetterperformance Many Python modules ship with counterpart C modules. Using these C modules will give a significant performance boost in complex applications. Example: cPickle instead of Pickle, cStringIO instead of StringIO . 27.Calculatingmaximum, minimumandsum outofanylistoriterable Youcanusethefollowingbuilt-infunctions. max:Returnsthelargestelementinthelist. min:Returnsthesmallestelementinthelist. sum: This function returns the sum of all elements in the list. It accepts an optional second argument: the value to start with when summing(defaultsto0). 28.Representing fractionalnumbers Fraction instance can be created using the followingconstructor: Fraction([numerator [,denominator]]) 29.Performing mathoperations The ‘math’ module provides a plethora of mathematical functions. These work on integer and float numbers, except complex numbers. For complex numbers, a separate module is used,called‘cmath’. Forexample: math.acos(x): Return arc cosine of x. math.cos(x): Returns cosine of x. math.factorial(x) : Returns x factorial. 30.Workingwitharrays The ‘array’ module provides an efficient way to use arrays in your programs. The ‘array’ module definesthefollowingtype: array(typecode [, initializer]) Once you have created an array object, say myarray,youcanapplyabunchofmethodstoit. Hereareafewimportantones: myarray.count(x): Returns the number of occurrences of x in a. myarray.extend(x): Appends x at the end of the array. myarray.reverse(): Reverse the order of the array. 31.Sortingitems The ‘bisect’ module makes it very easy to keep lists in any possible order. You can use the following functions to order lists. bisect.insort(list, item [, low [, high]]) Inserts item into list in sorted order. If item is already in the list, the new entry is inserted to therightofanyexistingentries. bisect.insort_left(list, item [, low [, high]]) Inserts item into list in sorted order. If item is already in the list, the new entry is inserted to theleftofanyexistingentries. Built-in modules
  • 69.
    The Python Book69 Python essentials 32.Usingregular expression-based search The ‘re’ module makes it very easy to use regxp- based searches. You can use the function ‘re.search()’ with a regexp-based expression. Checkouttheexamplebelow. Example: import re s = “Kunal is a bad boy” if re.search(“K”, s): print “Match!” # char literal ... Match! if re.search(“[@A-Z]”, s): print “Match!” # char class ... # match either at-sign or capital letter Match! if re.search(“d”, s): print “Match!” # digits class ... 33.Workingwithbzip2(.bz2) compressionformat You can use the module ‘bz2’ to read and write datausingthebzip2compressionalgorithm. bz2.compress() : For bz2 compression bz2.decompress() : For bz2 decompression Example: # File: bz2-example.py import bz2 MESSAGE = “Kunal is a bad boy” compressed_message = bz2. compress(MESSAGE) decompressed_message = bz2. decompress(compressed_message) print “original:”, repr(MESSAGE) print “compressed message:”, repr(compressed_message) print “decompressed message:”, repr(decompressed_message) Output: [~/src/python $:] python bz2- example.py original: ‘Kunal is a bad boy’ compressed message: ‘BZh91AYSYxc4 x0fGx98x00x00x02x15x80@x00 x00x084%x8a x00”x00x0cx84r x03Cxa2xb0xd6sxa5xb3x19x00 xf8xbbx92)xc2x84x86 zxc0’ decompressed message: ‘Kunal is a bad boy’ 34.UsingSQLitedatabase withPython SQLiteisfastbecomingaverypopularembedded database because of its zero configuration needed, and superior levels of performance. You can use the module ‘sqlite3’ in order to work with SQLitedatabases. Example: import sqlite3 connection = sqlite.connect(‘test. db’) curs = connection.cursor() curs.execute(‘’’create table item ... (id integer primary key, itemno text unique, ... scancode text, descr text, price real)’’’) sqlite3.Cursor object at 0x1004a2b30 35.Workingwithzipfiles You can use the module ‘zipfile’ to work with zipfiles. zipfile.ZipFile(filename [, mode [, compression [,allowZip64]]]) Open a zip file, where the file can be either a path toafile(astring)orafile-likeobject. zipfile.close()¶ Closethearchivefile.Youmustcall‘close()’before exiting your program or essential records will not bewritten. zipfile.extract(member[, path[, pwd]]) Extract a member from the archive to the current working directory; ‘member’ must be its full name (or a zipinfo object). Its file information is extracted as accurately as possible. ‘path’ specifies a different directory to extract to. ‘member’ can be a filename or a zipinfo object. ‘pwd’isthepasswordusedforencryptedfiles. 36.UsingUNIX-style wildcardstosearch forfilenames You can use the module ‘glob’ to find all the pathnames matching a pattern according to the rules used by the UNIX shell. *, ?, and character rangesexpressedwith[]willbe matched. Example: import glob glob.glob(‘./[0-9].*’) [‘./1.gif’, ‘./2.txt’] glob.glob(‘*.gif’) [‘1.gif’, ‘card.gif’] glob.glob(‘?.gif’) [‘1.gif’] 37.Performingbasicfile operations(copy,delete andrename) You can use the module ‘shutil’ to perform basic file operation at a high level. This module works with your regular files and so will not work with special files like named pipes, block devices, and soon. shutil.copy(src,dst) Copiesthefilesrctothefileordirectorydst. shutil.copymode(src,dst) Copiesthefilepermissionsfromsrctodst. shutil.move(src,dst) Movesafileordirectorytodst. shutil.copytree(src, dst, symlinks [,ignore]]) Recursivelycopyanentiredirectoryatsrc. shutil.rmtree(path [, ignore_errors [, onerror]]) Deletesanentiredirectory. 38.ExecutingUNIX commandsfromPython You can use module commands to execute UNIX commands. This is not available in Python3 – insteadyouneedtousethemodule‘subprocess’. Example: import commands commands.getoutput(‘ls’) ‘bz2-example.pyntest.py’ 39.Readingenvironment variables You can use the module ‘os’ to gather operating- system-specificinformation: Example: import os os.path module ‘posixpath’ from ‘/usr/lib/python2.6/posixpath. pyc’ os.environ {‘LANG’: ‘en_ IN’, ‘TERM’: ‘xterm-color’, ‘SHELL’: ‘/bin/bash’, ‘LESSCLOSE’: ‘/usr/bin/lesspipe %s %s’, ‘XDG_SESSION_COOKIE’: ‘925c4644597c791c704656354adf56d6- 1257673132.347986-1177792325’, ‘SHLVL’: ‘1’, ‘SSH_TTY’: ‘/dev/ pts/2’, ‘PWD’: ‘/home/kunal’, ‘LESSOPEN’: ‘| /usr/bin lesspipe ......} os.name ‘posix’ os.linesep ‘n’
  • 70.
    70 The PythonBook Python essentials 40.Sendingemail You can use the module ‘smtplib’ to send email using an SMTP (Simple Mail Transfer Protocol) clientinterface. smtplib.SMTP([host [, port]]) Example(sendanemailusingGoogleMail SMTPserver): import smtplib # Use your own to and from email address fromaddr = ‘kunaldeo@gmail.com’ toaddrs = ‘toemail@gmail.com’ msg = ‘I am a Python geek. Here is the proof.!’ # Credentials # Use your own Google Mail credentials while running the program username = ‘kunaldeo@gmail.com’ password = ‘xxxxxxxx’ # The actual mail send server = smtplib.SMTP(‘smtp.gmail. com:587’) # Google Mail uses secure connection for SMTP connections server.starttls() server.login(username,password) server.sendmail(fromaddr, toaddrs, msg) server.quit() 41.Accessing FTPserver ‘ftplib’ is a fully fledged client FTP module for Python. To establish an FTP connection, you canusethefollowingfunction: ftplib.FTP([host [, user [, passwd [, acct [, timeout]]]]]) Example: host = “ftp.redhat.com” username = “anonymous” password = “kunaldeo@gmail.com” import ftplib import urllib2 ftp_serv = ftplib. FTP(host,username,password) # Download the file u = urllib2.urlopen (“ftp:// ftp.redhat.com/pub/redhat/linux/ README”) # Print the file contents print (u.read()) Output: [~/src/python $:] python ftpclient.py OlderversionsofRedHatLinuxhavebeenmoved to the following location: ftp://archive.download. redhat.com/pub/redhat/linux/ 42.Launchingawebpage withthedefaultweb browser The ‘webbrowser’ module provides a convenient way to launch webpages using the default webbrowser. Example(launchgoogle.co.ukwithsystem’s defaultwebbrowser): import webbrowser webbrowser.open(‘http://google. co.uk’) True 43.Creatingsecurehashes The ‘hashlib’ module supports a plethora of secure hash algorithms including SHA1, SHA224, SHA256,SHA384,SHA512andMD5. Example(createhexdigestofthegiventext): import hashlib # sha1 Digest hashlib.sha1(“MI6 Classified Information 007”).hexdigest() ‘e224b1543f229cc0cb935a1eb9593 18ba1b20c85’ # sha224 Digest hashlib.sha224(“MI6 Classified Information 007”).hexdigest() ‘3d01e2f741000b0224084482f905e9b7b97 7a59b480990ea8355e2c0’ # sha256 Digest hashlib.sha256(“MI6 Classified Information 007”).hexdigest() ‘2fdde5733f5d47b672fcb39725991c89 b2550707cbf4c6403e fdb33b1c19825e’ # sha384 Digest hashlib.sha384(“MI6 Classified Information 007”).hexdigest() ‘5c4914160f03dfbd19e14d3ec1e74bd8b99 dc192edc138aaf7682800982488daaf540be 9e0e50fc3d3a65c8b6353572d’ # sha512 Digest hashlib.sha512(“MI6 Classified Information 007”).hexdigest() ‘a704ac3dbef6e8234578482a31d5ad29d25 2c822d1f4973f49b850222edcc0a29bb89077 8aea807a0a48ee4ff8bb18566140667fbaf7 3a1dc1ff192febc713d2’ # MD5 Digest hashlib.md5(“MI6 Classified Information 007”).hexdigest() ‘8e2f1c52ac146f1a999a670c826f7126’ 44.Seedingrandom numbers You can use the module ‘random’ to generate a wide variety of random numbers. The most used one is ‘random.seed([x])’. It initialises the basic random number generator. If x is omitted or None, current system time is used; current system time is also used to initialise the generatorwhenthemoduleisfirstimported. 45.WorkingwithCSV (comma-separated values)files CSVfilesareverypopularfordataexchangeover theweb.Usingthemodule‘csv’,youcanreadand writeCSVfiles. Example: import csv # write stocks data as comma- separated values writer = csv.writer(open(‘stocks. csv’, ‘wb’, buffering=0)) writer.writerows([ (‘GOOG’, ‘Google, Inc.’, 505.24, 0.47, 0.09), (‘YHOO’, ‘Yahoo! Inc.’, 27.38, 0.33, 1.22), (‘CNET’, ‘CNET Networks, Inc.’, 8.62, -0.13, -1.49) ]) # read stocks data, print status messages stocks = csv.reader(open(‘stocks. csv’, ‘rb’)) status_labels = {-1: ‘down’, 0: ‘unchanged’, 1: ‘up’} for ticker, name, price, change, pct in stocks: status = status_ labels[cmp(float(change), 0.0)] print ‘%s is %s (%s%%)’ % (name, status, pct) 46.Installingthird- partymodulesusing setuptools ‘setuptools’ is a Python package which lets you download, build, install, upgrade and uninstall packages very easily. To use ‘setuptools’ you will need to install from your distribution’s package manager. After installation you can use the command ‘easy_install’ to perform Python package management tasks.
  • 71.
    The Python Book71 Python essentials Example(installingsimplejsonusing setuptools): kunal@ubuntu:~$ sudo easy_install simplejson Searching for simplejson Reading http://pypi.python.org/simple/ simplejson/ Reading http://undefined.org/ python/#simplejson Best match: simplejson 2.0.9 Downloading http://pypi.python. org/packages/source/s/simplejson/ simplejson-2.0.9.tar.gz#md5=af5e67a39c a3408563411d357e6d5e47 Processing simplejson-2.0.9.tar.gz Running simplejson-2.0.9/setup.py -q bdist_egg --dist-dir /tmp/easy_ install-FiyfNL/simplejson-2.0.9/egg- dist-tmp-3YwsGV Adding simplejson 2.0.9 to easy- install.pth file Installed /usr/local/lib/python2.6/ dist-packages/simplejson-2.0.9-py2.6- linux-i686.egg Processing dependencies for simplejson Finished processing dependencies for simplejson 47.Loggingtosystemlog Youcanusethemodule‘syslog’towritetosystem log. ‘syslog’ acts as an interface to UNIX syslog libraryroutines. Example: import syslog syslog.syslog(‘mygeekapp: started logging’) for a in [‘a’, ‘b’, ‘c’]: b = ‘mygeekapp: I found letter ‘+a syslog.syslog(b) syslog.syslog(‘mygeekapp: the script goes to sleep now, bye,bye!’) Output: $ python mylog.py $ tail -f /var/log/messages Nov 8 17:14:34 ubuntu -- MARK -- Nov 8 17:22:34 ubuntu python: mygeekapp: started logging Nov 8 17:22:34 ubuntu python: mygeekapp: I found letter a Nov 8 17:22:34 ubuntu python: mygeekapp: I found letter b Nov 8 17:22:34 ubuntu python: mygeekapp: I found letter c Nov 8 17:22:34 ubuntu python: mygeekapp: the script goes to sleep now, bye,bye! 48.GeneratingPDF documents ‘ReportLab’ is a very popular module for PDF generationfromPython. PerformthefollowingstepstoinstallReportLab $ wget http://www.reportlab.org/ftp/ ReportLab_2_3.tar.gz $ tar xvfz ReportLab_2_3.tar.gz $ cd ReportLab_2_3 $ sudo python setup.py install Forasuccessfulinstallation,youshouldseea similarmessage: ############SUMMARY INFO########### ################################### #Attempting install of _rl_accel, sgmlop pyHnj #extensions from ‘/home/kunal/python/ ReportLab_2_3/src/rl_addons/rl_accel’ ################################### #Attempting install of _renderPM #extensions from ‘/home/kunal/python/ ReportLab_2_3/src/rl_addons/renderPM’ # installing with freetype version 21 ################################### Example: from reportlab.pdfgen.canvas import Canvas # Select the canvas of letter page size from reportlab.lib.pagesizes import letter pdf = Canvas(“bond.pdf”, pagesize = letter) # import units from reportlab.lib.units import cm, mm, inch, pica pdf.setFont(“Courier”, 60) pdf.setFillColorRGB(1, 0, 0) pdf.drawCentredString(letter[0] / 2, inch * 6, “MI6 CLASSIFIED”) pdf.setFont(“Courier”, 40) pdf.drawCentredString(letter[0] / 2, inch * 5, “For 007’s Eyes Only”) # Close the drawing for current page pdf.showPage() # Save the pdf page pdf.save() Output: @image:pdf.png @title: PDF Output 49.UsingTwitterAPI You can connect to Twitter using the ‘Python- Twitter’module. Performthefollowingstepstoinstall Python-Twitter: $ wget http://python-twitter. googlecode.com/files/python-twitter- 0.6.tar.gz $ tar xvfz python-twitter* $ cd python-twitter* $ sudo python setup.py install Example(fetchingfollowerslist): import twitter # Use you own twitter account here mytwi = twitter.Api(username=’kunald eo’,password=’xxxxxx’) friends = mytwi.GetFriends() print [u.name for u in friends] [u’Matt Legend Gemmell’, u’jono wells’, u’The MDN Big Blog’, u’Manish Mandal’, u’iH8sn0w’, u’IndianVideoGamer.com’, u’FakeAaron Hillegass’, u’ChaosCode’, u’nileshp’, u’Frank Jennings’,..’] 50.DoingYahoo!news search You can use the Yahoo! search SDK to access Yahoo!searchAPIsfromPython. Performthefollowingstepstoinstallit: $wget http://developer.yahoo.com/ download/files/yws-2.12.zip $ unzip yws* $ cd yws*/Python/pYsearch*/ $ sudo python setup.py install Example: # Importing news search API from yahoo.search.news import NewsSearch srch = NewsSearch(‘YahooDemo’, query=’London’) # Fetch Results info = srch.parse_results() info.total_results_available 41640 info.total_results_returned 10 for result in info.results: ... print “’%s’, from %s” % (result[‘Title’], result[‘NewsSource’]) ... ‘Afghan Handover to Be Planned at London Conference, Brown Says’, from Bloomberg ................. Third-partymodules
  • 72.
    72 The PythonBook “WithPython,youcantweak andrealiseyouridealsystemset-up” 74 Python for professionals Put your skills to professional use 82 Extensions for XBMC Enhance XBMC with this tutorial 88 Scientific computing Get to grips with NumPy 92 Instant messaging Get chatting using Python 98 Replace your shell Use Python for your primary shell 102 Python for system admins How Python helps system administration 92 Python Work with
  • 73.
    The Python Book73 82 88 74
  • 74.
    74 The PythonBook Work with Python Pythonisrelieduponbywebdevelopers,engineersand academicresearchersacrosstheworld.Here’showtoputyour Pythonskillstoprofessionaluse PYTHON FOR PROFESSIONALS
  • 75.
    The Python Book75 Work with Python Systemadministration System administration tasks are some of the most annoying things that you need to deal with when you have to maintain your own system. Because of this, system administrators have constantly been trying to find ways to automate these types of tasks to maximise their time.Theystartedwithbasicshellscripts,andthenmoved on to various scripting languages. For a long time, Perl had been the language of choice for developing these types of maintenance tools. However, Python is now growing in popularity as the language to use. It has reached the point where most Linux distributions have a Python interpreter included in order to run system scripts, so you shouldn’t haveanyexcusefornotwritingyourownscripts. Because you will be doing a lot system level work, you will have most need of a couple of key Python modules. The first module is ‘os’. This module provides the bulk of the interfaces to interacting with the underlying system. The usual first step is to look at the environment your script is running in to see what information might exist there to help guide your script. The following code gives you a mapping object where you can interact with the environmentvariablesactiverightnow: import os os.environ You can get a list of the available environment variables with the function “os.environs.keys()”, and then access individual variables with “os.environs[key]”. These environment variables are used when you spawn a subprocess, as well. So you will want to change values, like the PATH or the current working directory, in order for you to run these subprocesses correctly. While there is a “putenv” function that edits these values for you, it unfortunately does not exist on all systems. So the better way to do this is to edit the values directly within the environsmapping. Another category of tasks you may want to automate is when working with files. For example, you can get the currentworkingdirectorywithcodelike cwd = os.getcwd() Youcanthengetalistofthefilesinthisdirectorywith os.listdir(cwd) You can move around the file system with the function “os.chdir(new_path)”. Once you’ve found the file you are interested in, you can open it with “os.open()” and open it for reading, writing and/or appending. You can then read or write to it with the functions “os.read()” and “os.write()”. Oncedone,youcanclosethefilewith“os.close()”. Left Pythonscripts enableyoutoinstruct andinteractwithyour operatingsystem GetthemostoutofPythoninhandlingalloftheday-to-day upkeepthatkeepsyoursystemhealthy RunningsubprocessesfromPython The underlying philosophy of Unix is to build small, specialised programs that do one job extremely well. You then chain these together to build more complex behaviours. There is no reason why you shouldn’t use the same philosophy within your Python scripts. There are several utility programs available to use with very little work on your part. The older way of handling this was through using functions like “popen()” and “spawnl()” from the os module, but a better way of running other programs is by using the subprocess module instead. You can then launch a program, like ls, by using: import subprocess subprocess.run([‘ls’ , ‘-l’]) This provides you with a long file listing for the current directory. The function “run()” was introduced in Python 3.5 and is the suggested way of handling this. If you have an older version, or if you require more control than that, then you can employ the underlying “popen()” function that we mentioned earlier instead. If you want to get the output, you can use the following: cmd_output = subprocess.run([‘ls’ , ‘-l’], stdout=subprocess.PIPE) The variable “cmd_output” is a CompletedProcess object that containsthereturncodeandastringholdingthestdoutoutput. SYSTEMADMINISTRATION BASH,PERL,PYTHON OPERATINGSYSTEM CPU FILES/IO Onceyouhaveyourscript allwrittenup,youmaywant toschedulethemtorun automaticallywithoutyour intervention.OnUnixsystems, youcanhavecronrunyour scriptonwhateverschedule isnecessary.Theutility “crontab-l”liststhecurrent contentsofyourcronfile,and “crontab-e”letsyoueditthe scheduledjobsthatyouwant crontorun. Scheduling withcron
  • 76.
    76 The PythonBook Work with Python With the content and the bulk of the computing hosted on a server, a web application can better guarantee a consistent experience for the end user. The popular Django framework provides a very complete environment of plugins and works on the DRY principle (Don’t Repeat Yourself). Because of this, you should be able to build your web application very quickly. Since Django is built on Python, you should be able to install it with “sudo pip install Django”. Most distributions should have a package for Django, too. Depending on what you want to do with your app, you may need to install a database like MySQL or postgresqltostoreyourapplicationdata. There are Django utilities available to automatically generateastartingpointforyournewproject’scode: django-admin startproject newsite This command creates a file named “manage.py” and a subdirectory named “newsite”. The file “manage.py” contains several utility functions you can use to administer your new application. The newly created subdirectory contains the files “__init__.py”, “settings.py”, “urls.py” and “wsgi.py”. These files and the subdirectory they reside in comprise a Python package that gets loaded when your web site is started up. The core configuration for your site can be found in the file “settings.py”. The URL declarations, basically a table of contents for your site, are stored in the file “urls.py”. The file “wsgi.py” contains an entry point for WSGI-compatiblewebservers. Once your application is done, it should be hosted on a properly configured and hardened web server. But, this is inconvenient if you are in the process of developing your web application. To help you out, Django has a web server built into the framework. You can start it up by changing directory to the “newsite” project directory and running the followingcommand: Left Pythoninterpreterswork withyourdatabasestopowera webserver Bottom TheModel-View- Controllerarchitectureisoften usedforUIs python manage.py runserver This will start up a server listening to port 8000 on your local machine. Because this built in server is designed to be used for development, it automatically reloads your Python code for each request. This means that you don’t needtorestarttheservertoseeyourcodechanges. All of these steps get you to a working project. You are now ready to start developing your applications. Within the “newsite”subdirectory,youcantype: python manage.py startapp newapp This will create a new subdirectory named “newapp”, with the files “models.py”, “tests.py” and “views.py”, among others.Thesimplestpossibleviewconsistsofthecode: from django.http import HttpResponse def index(request): return HttpResponse(“Hello world”) This isn’t enough to make it available, however. You will also need to create a URLconf for the view. If the file “urls.py”doesn’texistyet,createitandthenaddthecode: from django.conf.urls import url from . Import views urlpatterns = [ url(r’^$’ , views.index, name=‘index’), ] The last step is to get the URL registered within your project.Youcandothiswiththecode from django.conf.urls import include, url from django.contrib import admin urlpatterns = [ url(r’^newapp/’ , include(‘newapp. urls’) ), Webdevelopment Pythonhas several frameworksavailable forallofyour variouswebdevelopmenttasks. Wewilllook at someofthe morepopularones Whenyoustartdeveloping yourownapplications,you maybeginadescentinto dependencyhell.Several Pythonpackagesdepend onotherPythonpackages. Thisisitsstrength,butalso itsweakness.Luckily,you havevirtualenvavailable tohelptamethisjungle. Youcancreatenewvirtual environmentsforeachofyour projects.Thankfullywiththis, youcanbesuretocaptureall ofthedependenciesforyour ownpackage. Virtual environments USER WEBSERVER DATABASE PYTHON INTERPRETER MODEL USER VIEW CONTROLLER Manipulates Sees Updates Uses
  • 77.
    The Python Book77 Work with Python url(r’^admin’ , admin.site.urls), ] This needs to be put in the “urls.py” file for the main project. You can now pull up your newly created applicationwiththeURLhttp://localhost:8000/newapp/. The last part of many applications is the database side. The actual connection details to the database, like the username and password, are contained in the file “settings.py”. This connection information is used for all of the applications that exist within the same project. You can create the core database tables for your site with thiscommand: python manage.py migrate For your own applications, you can define the data model you need within the file “models.py”. Once the data model is created, you can add your application to the INSTALLED_APPS section of the “settings.py” so that django knows to include it in any database activity. You initializeitwith: python manage.py makemigrations newapp Once these migrations have been created, you need to applythemtothedatabasebyusingthecommand: python manage.py migrate Any time you make changes to your model, you will need to runthemakemigrationsandmigratestepsagain. Once you have your application finished, you can make the move to the final hosting server. Don’t forget to check the available code within the Django framework before puttingtoomuchworkintodevelopingyourowncode. UsingthePyCharmIDE THEEDITORPANE Themaineditorpanecanbeconfiguredtomatchyour ownstyle,orthestyleofoneoftheothermaineditors, likeemacs.Ithandlessyntaxhighlighting,andeven displayserrorlocations THEPROJECTPANE Thispaneisthecentrallocationforyourproject.Allof yourfilesandlibrariesarelocatedhere.Right-clicking inthepanebringsupadrop-downmenuwhereyoucan addnewfilesorlibraries,rununittests,orevenstartup adebugger THESTATUSBARE PyCharmdoesalotofworkbehindthescenes. Thestatusbarhelpsyoukeeptrackofallofthese backgroundprocesses OtherPython frameworks WhileDjangoisoneofthemostpopularframeworksaround fordoingwebdevelopment,itisbynomeanstheonlyone around.Thereareseveralothersavailablethatmayproveto beabetterfitforparticularproblemdomains.Forexample, ifyouarelookingforareallyself-containedframework,you couldlookatweb2py.Everythingyouneedtobeabletohave acompletesystem,fromdatabasestowebserverstoa ticketingsystem,areincludedaspartoftheframework.Itis soself-containedthatitcanevenrunfromaUSBdrive Ifyouneedevenlessofaframework,thereareseveral mini-frameworksthatareavailable.Forexample,CherryPy isapurelyPythonicmulti-threadedwebserverthatyou canembedwithinyourownapplication.Thisisactually theserverincludedwithTurboGearsandweb2py.Areally popularmicroframeworkisaprojectcalledflask.Itincludes integratedunittestingsupport,jinja2templatingandRESTful requestdispatching. Oneoftheoldestframeworksaroundiszope,nowupto version3.ThislatestversionwasrenamedBlueBream.Zope isfairlylow-level,however.Youmaybemoreinterestedin lookingatsomeoftheotherframeworksthatarebuilton topofwhatisprovidedbyzope.Forexample,pyramidisa veryfast,easytouseframeworkthatfocusesonthemost essentialfunctionsrequiredbymostwebapplications.To thisend,itprovidestemplating,theservingofstaticcontent, mappingofURLstocode,amongotherfunctions.Ithandles thiswhileprovidingtoolsforapplicationsecurity. Ifyouarelookingforsomeideas,thereareseveralopen sourceprojectsthathavebeenbuiltusingtheseframeworks, fromblogs,toforumstoticketingsystems.Theseprojectscan providesomebest-practiceswhenyougotoconstructyour ownapplication. Whenyouareinthemiddleof developingyourapplication, youmayneedtohaveseveral differentterminalwindows openinordertohaveacode editoropen,amonitoronthe server,andpotentiallytesting output.Ifyouaredoingthison yourownmachine,thisisn’tan issue.But,ifyouareworking remotely,youshouldlookinto usingtmux.Itcanprovidea muchmorerobustterminal environmentforyou. Terminal development environments
  • 78.
    78 The PythonBook Work with Python Python has become one of the key languages used in science. There is a huge number of packages available to handle almost any task that you may have and, importantly, Python knows what it isn’t good at. To deal with this, Python has been designed to easily incorporate code from C or FORTRAN. This way, you can offload any heavycomputationstomoreefficientcode. The core package of most of the scientific code available is numpy. One of the problems in Python is that the object oriented nature of the language is the source of its inefficiencies. With no strict types, Python always needs to check parameters on every operation. Numpy provides a new datatype, the array, which helps solve some of these issues. Arrays can only hold one type of object, and because Python knows this it can use some optimisations to speed things up to almost what you can get from writing your code directly in C or FORTRAN. The classic example of the difference is the for loop. Lets say youwantedtoscaleavectorbysomevalue,somethinglike a*b.InregularPython,thiswouldlooklike for elem in b: c.append(a * elem) Innumpy,thiswouldlooklike: a*b Left Thenumpypackage makesitsimpletovisualise yourdata So, not only is it faster, it is also written in a shorter, clearer form. Along with the new datatype, numpy provides overloaded forms of all of the operators that are of most use, like multiplication or division. It also provides optimised versions of several functions, like the trig functions,totakeadvantageofthisnewdatatype. The largest package available, that is built on top of numpy, is scipy. Scipy provides sub-sections in several areas of science. Each of these sub-sections need to be imported individually after importing the main scipy package. For example, if you are doing work with Computationalscience Pythonisfast becomingthe go-to languagefor computationalscience Spyder,theIDEforscientists THEEDITORPANE Thispaneiswhereyoucanopenandedityoursource files.Abovethispanearebuttonstoallowyoutosimply runthecode,orrunitunderadebugger.Underthe debugger,youcansetbreakpointsandstepthrough eachlineofcodeindividually VARIABLEEXPLORER Thevariableexplorerpaneletsyouaccessallofthe datastructureswithinthecurrentPythoninterpreter. Youneedtoactuallyrunyourcodeforanythingtoshow uphere IPYTHONCONSOLE Theconsolewindowletsyouinteractdirectlywiththe underlyinginterpreterthatwillbeusedwhenyoutryand runyourcode Oneofthereallypowerful partsofIpython(orjupyter) isthatitisbuiltwithaclient/ servermodel.Thismeansthat itisrelativelyeasytosetup multiplemachinestoactas aserverpool.Youcanthen farmoutmultipletasksto theseothermachinestoget evenmoreworkdone.While thisdoesn’trunanyparticular functioninparallel,itdoeslet yourunlongerfunctionsinthe backgroundwhileyouworkon somethingelse. Parallel Python
  • 79.
    The Python Book79 Work with Python Above Theabilitytogenerate complexplotsisessential Above JupyterNotebookisawebapplication thatisused forcreatingandsharingdocumentsthatcontainlivecode andequations differential equations, you can use the “integrate” section tosolvethemwithcodethatlookslike import scipy import scipy.integrate result = scipy.integrate.quad(lambda x: sin(x), 0, 4.5) Differential equations crop up in almost every scientific field. You can do statistical analysis with the “stats” section. If you want to do some signal processing, you can use the “signal” section and the “fftpack” section. This packageisdefinitelythefirststopforanyonewantingtodo anyscientificprocessing. Once you have collected your data, you usually need to graph it, in order to get a visual impression of patterns within it. The primary package you can use for this is matplotlib. If you have ever used the graphics package in R before, the core design of matplotlib has borrowed quite a few ideas. There are two categories of functions for graphing, low-level and high-level. High-level functions try to take care of as many of the menial tasks, like creating a plot window, drawing axes, selecting a coordinate system, as possible. The low-level functions give you control over almost every part of a plot, from drawing individual pixels to controlling every aspect of the plot window. It also borrowed the idea of drawing graphs into a memory based window. This means that it can draw graphs while running onacluster. If you need to do symbolic math, you may be more used to using something like Mathematica or Maple. Luckily, you have sympy that can be used to do many of the same things. You can use Python to do symbolic calculus, or to solve algebraic equations. The one weird part of sympy is that you need to use the “symbols()” function to tell sympy Interactivescience withjupyter Foralotofscientificproblems,youneedtoplaywithyour datainaninteractiveway.Theoriginalwayyouwoulddo thiswastousetheIpythonwebnotebook.Thisprojecthas sincebeenrenamedJupyter.Forthosewhohaveuseda programlikeMathematicaorMaple,theinterfaceshould seemveryfamiliar.Jupyterstartsaserverprocess,by defaultonport8888,andthenwillopenawebbrowser whereyoucanopenaworksheet.Likemostotherprograms ofthistype,theentriesruninchronologicalorder,notin theorderthattheyhappenontheworksheet.Thiscan beabitconfusingatfirst,butitmeansthatifyougoto editanearlierentry,allofthefollowingentriesneedtobe re-executedmanuallyinordertopropagatethatchange throughtherestofthecomputations. Jupyterhassupportforprettyprintingmathwithin theproducedwebpage.Youcanalsomixdocumentation blocksandcodeblockswithinthesamepage.Thismeans thatyoucanuseittoproduceverypowerfuleducational material,wherestudentscanreadaboutsometechnique, andthenactuallyrunitandseeitinaction.Bydefault, Jupyterwillalsoembedmatplotlibplotswithinthesame worksheetasaresultssection,soyoucanseeagraphof somedataalongwiththecodethatgeneratedit.Thisis hugeinthegrowingneedforreproduciblescience.Youcan alwaysgobackandseehowanyanalysiswasdoneandbe abletoreproduceanyresultatall. Sometimesyouneedasmuch speedasyourarecapableof pushingonyourhardware.In thesecases,youalwayshave theoptionofusingCython. ThisletsyoutakeCcodefrom someotherproject,which hasprobablyalreadybeen optimised,anduseitwithin yourownPythonprogram.In scientificprogramming,you arelikelytohaveaccessto codethathasbeenworked onfordecadesandishighly specialised.Thereisnoneed toredothedevelopment effortthathasgoneintoit. Theneedfor speed whatvariablesarevalidtobeconsideredinyourequations. You can then start doing manipulations using these registeredvariables. You may have large amounts of data that you need to work with and analyze. If so, you can use the pandas package to help deal with that. Pandas has support for several different file formats, like CSV files, Excel spreadsheets or HDF5. You can merge and join datasets, or do slicing or subsetting. In order to get the best performance out of the code, the heaviestlifting is done by CythoncodethatincorporatesfunctionswritteninC.Quite a few ideas on how to manipulate your data was borrowed fromhowthingsaredoneinR. You now have no reason not to start using Python for your scientific work. You should be able to use it for almost anyproblemthatcomesup!
  • 80.
    80 The PythonBook Work with Python ROS–RobotOperatingSystem While you could simply write some code that runs on a standard computer and a standard Linux distribution, this is usually not optimal when trying to handle all of the data processing that a robot needs when dealing with events in realtime. When you reach this point, you may need to look at a dedicated operating system – the Robot Operating System (ROS). ROS is designed to provide the same type of interface between running code the computer hardware it is running on, with the lowest possible overhead. One of the really powerful features of ROS is that it is designed to facilitate communication between different processes running on the computer, or potentially over multiple computers connected over some type of network. Instead of each process being a silo that is protected from all other processes, ROS is more of a graph of processes with messages being passed between them all. Because ROS is a complete operating system, rather than a library, it is wrong to think that you can use it in your Python code. It is better to think that you can write Python code that can be used in ROS. The fundamental design is to be as agnostic as possible. This means that interfaces to your code should be clean and not particularly care where they running or who is talking to them. Then, it can be used within the graph of processes running within ROS. There are standard libraries available that allow you to do coordinate transformations, useful for figuring out where sensors or limbs are in space. There is a library available for creating preemptible tasks for data processing, and another for creating and managing the types of messages that can be handed around the various processes. For extremely time-sensitive tasks, there is a plugin library that allows you to write a C++ plugin that can be loaded within ROS packages. Robotics is the most direct way that your code can interact with the world around you. It can read actual sensor information and move real actuators and get real workdone. The first thing your robot needs is the ability to sense the world around it. The one sense that we as humans feel is most useful is sight. With web cameras being so cheap and easy to connect to hardware, vision is easy to give to your robot. The real problem is how to interpret this data. Luckily, you can use the OpenCV project to do just that. It is a vision package that can provide simple image gathering and processing, to extremely complex functions like face recognition and extraction of 3D objects. You can identify and track objects moving through your field of view. You can also use OpenCV to give you robot some reasoning capabilities, too. OpenCV includes a set of functions for machine learning, where you can do statistical classificationordataclustering,anduseittofeeddecision treesorevenneuralnetworks. Another important sense that you may want to use is sound. The jasper project is one that is developing a complete voice control system. This project would give you the structure you need to give your robot the ability to listen for and respond to your verbal commands. The project has gotten to the point where you can give it a command and the voice recognition software can translate this into text. You then need to build a mapping of what pieces of text correspond to what commandstoexecute. There are lots of other sensors you could have, but this begins to leave the realm of store-bought hardware. Most other sensors, like temperature, pressure, orientation or location, need specialised hardware that needs to be interfaced to the computer brain for your robot. This Roboticsandelectronics Roboticsisthemostdirect interfacebetweenyour codeand therealworldaround you Whilewehaven’tdiscussed whatkindofcomputertouse foryourroboticsproject,you shouldconsiderthefamous RaspberryPi.Thistiny computershouldbesmall enoughtofitintoalmost anyrobotstructurethatyou mightbebuilding.Sinceitis alreadyrunningLinuxand Python,youshouldbeable tosimplycopyyourcode developmentworktothePi. ItalsoincludesitsownIObus sothatyoucanhaveitread it’sownsensors. RaspberryPi IncontrasttotheRaspberry Pi,whichrunsafullOSfrom itsSDcard,theArduino boardsaremicrocontrollers ratherthancomplete computers.Insteadof runninganOS,theArduino platformexecutescodethat isinterpretedbyitsfirmware. Itismainlyusedtointerface withhardwaresuchasmotors andservos,sensors,and devicessuchasLEDs,and isincrediblycapableinthis regard.Arduinosarewidely usedinroboticsprojects andcanbeapowerful complementtothePi. Arduino
  • 81.
    The Python Book81 Work with Python means it is time to get your soldering iron out. As for reading the data in, this is most often done over a basic serial connection. You can then use the pySerial module to connecttotheserialportandreaddataofftheconnection. Youcanuse: import serial to load the module and start communicating with your sensor. The problem is that this is a very low-level way to communicate. You, as the programmer, are responsible for all of the details. This includes communication speed, byte size, flow control; basically everything. So this will definitely be an area of your code where you should plan on spending somedebuggingtime. Now that you have all of this data coming in, what will you do with it? You need to be able to move actuators out in the world and have real effects. This could be motors for wheels or tracks, levers to shift objects, or potentially complete limbs, like arms or legs. While you could try and drive these types of electronic devices directly from the output ports of your computer, there usually isn’t enough current available to provide the necessary power. So, you will need to have some off-board brains capable of handling the supplying of power to these devices. One of themostpopularcandidatesforthistaskistheArduino. Forlow-levelwork,checkoutArduinos THEMAINEDITOR Youhaveaccesstoalargenumberoflibraries, andsupportforalargenumberofversionsofthe Arduinoboards.ThecodeisessentiallyC,soPython programmersshouldn’tbetoofaroutoftheirdepths OUTPUTWINDOW Thispanecontainsoutputfromvarioustasks.This mightbecompilingthesourcecode,oruploadingitto theArduinoboardbeingusedinyourproject THESTATUSBAR Thestatusbarremindsyouwhichtypeofboardyour arecurrentlyprogrammingfor,aswellaswhichportthe ArduinoIDEthinksitison.Alwaysverifythisinformation beforetryingtouploadyourcontrolprogramtothe boardinquestion Forroboticswork,youmay needtorunsomecodetruly inparallel,onmultipleCPUs. PythoncurrentlyhastheGIL, whichmeansthatthereisa fundamentalbottleneckbuilt intotheinterpreter.Oneway aroundthisistoactuallyrun multiplePythoninterpreters, oneforeachthreadof execution.Theotheroption istomovefromCpythonto eitherJythonorIronPython,as neitherhasaGIL. Bypassing theGIL Luckily, the Arduino is designed to connect to the serial port of your computer, so you can simply use pySerial to talk to it. You can send commands to code that you have written and uploaded to the Arduino to handle the actual manipulations of the various actuators. The Arduino can talk back, however. This means that you can read feedback data to see what effect your movements have had. Did you end up turning your wheels as far as you wanted to? This means that you could also use the Arduino as an interface between your sensors and the computer, thus simplifying your Python code even more. There are loads of add-on modules available, too, that might be able toprovidethesensingcapabilitiesthatyourequirestraight out of the box. There are also several models of Arduino, so you may be able to find a specialised model that best fits yourneeds. Now that you have all of this data coming in and the ability to act out in the real world, the last step is giving your robot some brains. This is where the state of the art unfortunately does not live up to the fantasy of R2-D2 or C-3P0. Most of your actual innovative coding work will likely take place in this section of the robot. The general term for this is artificial intelligence. There are several projects currently underway that you could use as a starting point to giving your robot some real reasoning capability,likeSimpleAIorPyBrain.
  • 82.
    82 The PythonBook Work with Python Python is the world’s most popular easy-to-use open source language. Learn how to use it to build your own features for XBMC, the world’s favourite FOSS media centre Makeextensionsfor XBMCwithPython XBMC is perhaps the most important thing that has ever happened in the open source media centre space. It started its life on the original Xbox videogames console and since then it has become the de facto software for multimedia aficionados. It also has been forked into many other successful media centre applications such as Boxee and Plex. XBMC has ultimately grown into a very powerful open source application with a solid community behind it. It supports almost all major platforms, including different hardware architectures. It is available for Linux, Windows, MacOSX,Android,iOSandRaspberryPi. In these pages we will learn to build extensions for XBMC. Extensions are a way of adding features to XBMC without having to learn the core of XBMC or alter that core in any way. One additional advantage is that XBMC uses Python as its scripting language, and this can be also used to build the extensions. This really helps new developers get involved in the project since Python is easy to learn compared to languages likeC/C++(fromwhichthecoreofXBMCismade). XBMC supports various types of extensions (or Add-ons): Plugins, Programs and Skins. Plugins add features to XBMC. Depending on the type of feature, a plug-in will appear in the relevant media section of XBMC. For example, a YouTube plug-in would appear in the Videos section. Scripts/Programs are like mini-applications for XBMC. They appear in the Programs section. Skins are important since XBMC is a completely customisable application – you can change Resources XBMC: www.xbmc.org/download Python2.7x PythonIDE(optional) CodeonFileSilo Listof installed plug-ins Currentmedia selection Rating(onlyavailablefor hostedplug-ins) Configure launcher Openschangelog fortheplug-in Localised descriptionstring the look and feel of just about every facet of thepackage. Depending upon which category your extension fits, you will have to create the extensiondirectoryaccordingly.Forexample… Plug-ins: plugin.audio.ludaudi:Anaudioplug-in plugin.video.ludvidi:Avideoplug-in script.xxx.xxx:Aprogram In this tutorial we will build an XBMC plug-in called LUD Entertainer. This plug-in will provide a nice way to watch videos from Reddit from within XBMC.Ourplug-inwillshowvariouscontentsuch as trailers and documentaries from Reddit. We’ll also allow our users to add their own Subreddit. Each video can then be categorised as Hot, New, Top, Controversial etc. With this plug-in we will demonstrate how easy it is hook into XBMC’s built-in method to achieve a very high-quality userexperience. Due to space limitations,we aren’t able to print the full code here. We recommend downloading thecompletecodefromFileSilo.
  • 83.
    The Python Book83 Work with Python 01Preparingthedirectorystructure As we have mentioned previously, each XBMC extension type follows a certain directory naming convention. In this case we are building a video plug-in, so the plug-in directory name would be plugin.video.ludlent. But that’s just the root directory name – we will need several other foldersandfilesaswell. The following describes the directory structure of LUDLinuxEntertainer: plugin.video.ludent–RootPlugindirectory |--addon.xml |--changelog.txt |--default.py |--icon.png |--LICENSE.txt |--README `--resources |--lib `--settings.xml 02Creatingaddon.xml An addon.xml file needs to be created in therootoftheextensiondirectory.Theaddon.xml file contains the primary metadata from a XBMC extension. It contains overview, credits, version information and dependenciesinformation about theextension. The root element of addon.xml is the addon element.Itisdefinedas: addon id=plugin.video. ludent name=LUD HSW Viewer version=0.0.1 provider- name=LUDK rest of the content is placed here /addon Here, id is the identifier for the plug-in, so it should be unique among all the XBMC extensions, and id is also used for the directory name; version tells XBMC the extension version number, which helps in its ability to deliver automatic updates – XBMC follows the Major.Minor.Patchversioningconvention;nameis theEnglishtitleoftheplug-in. Note:Steps3to5coverentriesthatneedtobe addedwithintheaddon.xmlfile. 03Addingdependencyinformation Dependency inside an extension is managedusingtherequireselement. requires import addon=xbmc.python version=2.1.0/ import addon=plugin.video. youtube version=3.0.0/ import addon=plugin.video.vimeo version=2.3.0/ import addon=plugin.video. dailymotion_com version=1.0.0/ /requires Intheabovecodewehaveaddedadependency to a library called xbmc.python version 2.1. Currently it is added as a mandatory dependency. To make the dependency optional you will need to add optional=true; eg import addon=kunal.special version=0.1.0 optional=true / In the above example we have added core dependency xbmc.python to 2.1.0 because it’s the version shipped with XBMC version Frodo 12.0 and 12.1 . If you were to add xbmc.python to2.0thenitwouldonlyworkinXBMCEden11.0 andnotinthelatestversion. For the current version of XBMC 12.1, the following versions of core XBMC components areshipped: xbmc.python2.1.0 xbmc.gui4.0.0 xbmc.json6.0.0 xbmc.metadata2.1.0 xbmc.addon12.0.0 Inadditiontoxbmc.pythonwearealsoadding some third-party plug-ins as dependencies, such as plugin.video.youtube. These plug-ins will be installed automatically when we install plugin.video.ludent. 04Settinguptheproviderand entrypoint Our extension is supposed to provide the video content for XBMC. In order to convey that, we havetosetupthefollowingelement: extension point=xbmc.python. pluginsource library=default. py providesvideo/provides /extension Here, the library attribute sets up the plug-in entry point. In this example default.py will be executed when the user activates the plug-in. The provides elements sets up the media type it provides. This also gets reflected in the placement of the plug-in. Since ours is a video plug-in, it will show up in the Videos section ofXBMC. 05Settingupplug-inmetadata Metadataabouttheplug-inisprovidedin extensionpoint=xbmc.addon.metadata.The followingaretheimportantelements… platform: Most of the time, XBMC extensions are cross-platform compatible. However, if you depend on the native platform library that is only available on certain platforms then you will need to set the supported platforms here. Accepted values for the platform are: all, linux, osx, osx32, osx64, ios (Apple iOS) , windx (Windows DirectX), wingl(WindowsOpenGL)andandroid. summary lang=en: This gives a brief description of the plug-in. Our example sets the language attribute as English, but you can use otherlanguagestoo. description: A detailed description of the plug-in. website:Webpagewheretheplug-inishosted. source: Source code repository URL. If you are hosting your plug-in on GitHub, you can mention therepositoryURLhere. forum:DiscussionforumURLforyourplug-in. email: Author email. You can directly type email or use a bot-friendly email address like max at domaindotcom. 06Settingchangelog,icon,fanart andlicence We need a few additional files in the plug-in directory… changelog.txt: You should list the changes made to your plug-in between releases. The changelog isvisiblefromtheXBMCUI. Anexamplechangelog: 0.0.1 -InitialRelease 0.0.2 -FixedVideoBufferingIssue icon.png: This will represent the plug-in in the XBMC UI. It needs to be a non-transparent PNG fileofsize256x256. fanart.jpg (optional): The fanart.jpg is rendered in the background if a user selects the plug-in in XBMC. The art needs to be rendered in HDTV formats, so its size can range from 1280x720 (720p)uptothemaximum1920x1080(1080p).
  • 84.
    84 The PythonBook Work with Python License.txt: This file contains the licence of the distributed plug-in. The XBMC project recommends the use of the Creative Commons Attribution-ShareAlike 3.0 licence for skins, and GPL 2.0 for add-ons. However, most of the copyleftlicencescanbeused. Note: For the purpose of packaging, extensions/ add-ons/themes/plug-insarethesame. 07Providingsettingsfortheplug-in Settings can be provided by the file resources/settings.xml.Thesearegreatforuser- configurableoptions. Partial:resources/settings.xml settings category label=30109 setting id=filter type=bool label=30101 default=false/ setting type=sep / setting id=showAll type=bool label=30106 default=false/ setting id=showUnwatched type=bool label=30107 default=true/ setting id=showUnfinished type=bool label=30108 default=false/ setting type=sep / setting id=forceViewMode type=bool label=30102 default=true/ setting id=viewMode type=number label=30103 default=504/ /category category label=30110 setting id=cat_hot type=bool label=30002 default=true/ setting id=cat_new type=bool label=30003 default=true/ /category /settings Here, label defines the language id string which will then be used to display the label. id defines the name which will be used for programmatic access. type defines the data type you want to collect; it also affects the UI which will be displayed for the element. default defines the default value for the setting. You should always useadefaultvaluewhereverpossibletoprovidea betteruserexperience. The following are a few important settings typesthatyoucanuse… text:Usedforbasicstringinputs. ipaddress:Usedtocollectinternetaddresses. number: Allows you enter a number. XBMC will also provide an on-screen numeric keyboard for theinput. slider: This provides an elegant way to collect integer, float and percentage values. You can get theslidersettinginthefollowingformat: setting label=21223 type=slider id=sideinput default=10 range=1,1,10 option=int / Intheaboveexamplewearecreatingasliderwith min range 1, max range 10 and step as 1. In the option field we are stating the data type we are interested in – we can also set option to float orpercent. bool: Provides bool selection in the form of on oroff. file: Provides a way to input file paths. XBMC will provideafilebrowsertomaketheselectionoffile. If you are looking to make selection for a specific type of file you can use audio, video, image or executableinsteadoffile. folder:Providesawaytobrowseforafolder… Example: setting label=12001 type=folder id=folder source=auto option=writeable/ Here, source sets the start location for the folder, while option sets the write parameter for theapplication. sep lsep: sep is used to draw a horizontal line in the setting dialog; lsep is used for drawing a horizontal line with text. They do not collect any input but are there for building better user interfaceelements… setting label=21212 type=lsep / 08Languagesupport Language support is provided in the form of the strings.xml file located in resources/languages/[language name]. This approach is very similar to many large software projects, including Android, where static strings areneverused. resource/language/english/string.xml example: ?xml version=1.0 encoding=utf-8 standalone=yes? strings string id=30001Add subreddit/ string string id=30002Hot/string string id=30003New/string string id=30004Top/string string id=30005Controversial/ string string id=30006Hour/string string id=30007Day/string string id=30008Week/string string id=30009Month/string string id=30010Year/string /strings As you may have seen in the settings.xml example, all the labels are referring to string ids. You can have many other languages as well. Depending upon the language XBMC is running in, the correct language file will be loadedautomatically. Post XBMC Frodo (12.1), strings.xml will be deprecated. Post Frodo, XBMC will be moved to a GNU gettext-based translation system; gettext uses PO files. You can use a tool called xbmc-xml2po to convert strings.xml into equivalentPOfiles. 09Buildingdefault.py Since our plug-in is small, it will all be contained inside default.py. If you are developing a more complex add-on then you can create supporting files in the same directory. If your library depends upon third-party libraries, you have two ways to go about it. You can either place the third-party libraries into the resources/lib folder; or bundle the library itself into a plug-in, then add that plug-in as the dependency in the addon.xmlfile. Our plug-in works with reddit.tv. This is the website from Reddit which contains trending videos shared by its readers. Videos posted on Reddit are actually sourced from YouTube, Vimeo andDailymotion. We will be starting off default.py using the followingimports: import urllib import urllib2 … import xbmcplugin
  • 85.
    The Python Book85 Work with Python import xbmcgui import xbmcaddon Apart from xbmcplugin, xbmcgui and xbmcaddon, the rest are all standard Python libraries which are available on PyPI (Python Package Index) via pip. You will not need to install any library yourself since the Python runtime for XBMChasallthecomponentsbuiltin. urllib and urllib2 help in HTTP communication. socket is used for network I/O; re is used for regular expression matching; sqlite3 is the Python module for accessing an SQLite embedded database; xbmcplugin, xbmcgui and xbmcaddoncontaintheXBMC-specificroutine. 10Initialising During the initialisation process, we will be reading various settings from settings.xml. Settingscanbereadinthefollowingway: addon = xbmcaddon.Addon() filterRating = int(addon. getSetting(filterRating)) filterVoteThreshold = int(addon.getS etting(filterVoteThreshold)) Inordertoreadsettingsoftypeboolyouwillneed todosomethinglike: filter = addon.getSetting(filter) == true We are also setting the main URL, plug-in handle andtheuseragentforit: pluginhandle = int(sys.argv[1]) urlMain = http://www.reddit.com userAgent = Mozilla/5.0 (Windows NT 6.2; WOW64; rv:22.0) Gecko/20100101 Firefox/22.0 opener = urllib2.build_opener() opener.addheaders = [(‘User-Agent’, userAgent)] 11Readinglocalisedstrings As mentioned, XBMC uses strings.xml to serve up the text. In order to read those strings, youwillneedtousegetLocalizedString. translation = addon. getLocalizedString translation(30002) In this example, translation(30002) will return the string Hot when it is running in an Englishenvironment. 12Buildinghelperfunctions In this step we will look at some of the importanthelperfunctions. getDbPath(): This returns the location of the SQLite database file for videos. XBMC stores library and playback information in SQLite DB files. There are separate databases for videos and music, located inside the .xbmc/userdata/ Database folder. We are concerned with the videosDB.Itisprefixedwith‘MyVideos’… def getDbPath(): path = xbmc. translatePath(special://userdata/ Database) files = os.listdir(path) latest = for file in files: if file[:8] == ‘MyVideos’ and file[-3:] == ‘.db’: if file latest: latest = file return os.path.join(path, latest) getPlayCount(url): Once we have the database location, we can get the play count using a simple SQL query. The MyVideo database contains a table called files, which keeps a record of all the video files played in XBMC by filename.InthiscaseitwillbeURL. dbPath = getDbPath() conn = sqlite3.connect(dbPath) c = conn.cursor() def getPlayCount(url): c.execute(‘SELECT playCount FROM files WHERE strFilename=?’, [url]) result = c.fetchone() if result: result = result[0] if result: return int(result) return 0 return -1 Theabovetableisanexampleofafilestable. addSubreddit(): Our plug-in allows users to add their own Subreddit. This function takes the Subreddit input from the user, then saves it in thesubredditsfileinsidetheaddondatafolder. Thefollowingsetsthesubredditsfilelocation: subredditsFile = xbmc. translatePath(special://profile/ addon_data/+addonID+/subreddits) this translates into .xbmc/userdata/ addon_data/plugin.video.ludent/ subreddits def addSubreddit(): keyboard = xbmc.Keyboard(‘’, translation(30001)) keyboard.doModal() if keyboard.isConfirmed() and keyboard.getText(): subreddit = keyboard. getText() fh = open(subredditsFile, ‘a’) fh.write(subreddit+’n’) fh.close() This function also demonstrates how to take a text input from the user. Here we are calling the Keyboard function with a text title. Once it detects the keyboard, it writes the input in the subredditsfilewithanewlinecharacter. getYoutubeUrl(id): When we locate a YouTube URL to play, we pass it on to the YouTube plug-in (plugin.video.youtube) to handle the playback. To doso,weneedtocallitinacertainformat… def getYoutubeUrl(id): url = plugin://plugin. video.youtube/?path=/root/ videoaction=play_videovideoid= + id return url idFile idPath strFilename playCount lastPlayed dateAdded 1 1 plugin://plugin. 2013-08-0623:47 2 2 plugin://plugin. 1 2013-08-0722:42 3 2 plugin://plugin. 1 2013-08-0800:09 4 2 plugin://plugin. 1 2013-08-0800:55 5 2 plugin://plugin. 1 2013-08-0800:58
  • 86.
    86 The PythonBook Work with Python SimilarlyforVimeo: def getVimeoUrl(id): url = plugin://plugin.video. vimeo/?path=/root/videoaction=play_ videovideoid= + id return url AndforDailymotion: def getDailyMotionUrl(id): url = plugin://plugin.video. dailymotion_com/?url= + id + mode=playVideo return url Once we have the video URL resolved into the respectiveplug-in,playingitisverysimple: def playVideo(url): listitem = xbmcgui. ListItem(path=url) xbmcplugin. setResolvedUrl(pluginhandle, True, listitem) 13Populatingplug-incontentlisting xbmcplugin contains various routines for handling the content listing inside the plug-ins UI. The first step is to create directory entries which can be selected from the XBMC UI. For this we will use a function called xbmcplugin.addDirectoryItem. For our convenience we will be abstracting addDirectoryItem to suit it to our purpose, so that we can set name, URL, mode, icon image andtypeeasily. def addDir(name, url, mode, iconimage, type=): u = sys.argv[0]+?url=+urllib. quote_plus(url)+mode=+str(mode)+ type=+str(type) ok = True liz = xbmcgui.ListItem(name, iconImage=DefaultFolder.png, thumbnailImage=iconimage) liz.setInfo(type=Video, infoLabels={Title: name}) ok = xbmcplugin. addDirectoryItem(handle=int(sys. argv[1]), url=u, listitem=liz, isFolder=True) return ok On the same lines, we can build a function to placelinksaswell… def addLink(name, url, mode, iconimage, description, date): u = sys.argv[0]+?url=+urllib. quote_plus(url)+mode=+str(mode) ok = True liz = xbmcgui.ListItem(name, iconImage=DefaultVideo.png, thumbnailImage=iconimage) liz.setInfo(type=Video, infoLabels={Title: name, Plot: description, Aired: date}) liz.setProperty(‘IsPlayable’, ‘true’) ok = xbmcplugin. addDirectoryItem(handle=int(sys. argv[1]), url=u, listitem=liz) return ok Based on the abstractions we have just created, we can create the base functions which will populate the content. But before we do that, let’s first understand how Reddit works. Most of the Reddit content filters are provided through something called Subreddits. This allows you to view discussions related to a particular topic. In our plug-in we are interested in showing videos; we also want to show trailers, documentaries etc. We access these using Subreddits. For example, for trailers it would be reddit.com/r/ trailers. For domains we can use /domain; for example, to get all the YouTube videos posted on Reddit, we will call reddit.com/domain/ youtube.com. Now you may ask what is the guarantee that this Subreddit will only list videos? The answer is that it may not. For that reason we scrape the site ourselves to find videos.Moreonthisinthenextstep. The first base function we’ll define is index(). Thisiscalledwhentheuserstartstheplug-in. def index(): defaultEntries = [videos, trailers, documentaries, music] entries = defaultEntries[:] if os.path. exists(subredditsFile): fh = open(subredditsFile, ‘r’) content = fh.read() fh.close() spl = content.split(‘n’) for i in range(0, len(spl), 1): if spl[i]: subreddit = spl[i] entries. append(subreddit) entries.sort() for entry in entries: if entry in defaultEntries: addDir(entry.title(), r/+entry, ‘listSorting’, ) else: addDirR(entry.title(), r/+entry, ‘listSorting’, ) addDir([ Vimeo.com ], domain/vimeo.com, ‘listSorting’, ) addDir([ Youtu.be ], domain/ youtu.be, ‘listSorting’, ) addDir([ Youtube.com ], domain/youtube.com, ‘listSorting’, ) addDir([ Dailymotion.com ], domain/dailymotion.com, ‘listSorting’, ) addDir([B]- +translation(30001)+ -[/B], , ‘addSubreddit’, ) xbmcplugin. endOfDirectory(pluginhandle) Here, the penultimate entry makes a call to addSubreddit. listSorting takes care of sorting out the data based on criteria such as Hot, New etc. It also calls in Reddit’s JSON function, whichreturnsniceeasy-to-parseJSONdata. We have created a settings entry for all the sorting criteria. Based on what is set, we go aheadandbuildoutthesortedlist. def listSorting(subreddit): if cat_hot: addDir(translation(30002), urlMain+/+subreddit+/hot/. json?limit=100, ‘listVideos’, ) if cat_new: addDir(translation(30003), urlMain+/+subreddit+/new/. json?limit=100, ‘listVideos’, ) if cat_top_d: addDir(translation(30004)+: +translation(30007), urlMain+/+subreddit+/ top/.json?limit=100t=day, ‘listVideos’, ) xbmcplugin. endOfDirectory(pluginhandle)
  • 87.
    The Python Book87 Work with Python In the code listed to the left here, we are opening the URL, then – based on regular expression matches – we are discovering the location title, description, date, ups, downs and rating. We are also locating video thumbnails and then passing them on to XBMC. Later in the code, we also try to match the URL to a video provider. With our plug-in we are supporting YouTube, Vimeo and Dailymotion. If this is detected successfully, we call the helper functions to locate the XBMC plug- in based playback URL. During this whole parsing process, if any exception is raised, the whole loop is ignored and the next JSON item is parsed. 15Installing running theadd-on You can install the add-on using one of thefollowing two methods: • You can copy the plug-in directory to .xbmc/addons. • You can install the plug-in from the zip file. To do so, compress the add-on folder into a zip file using the command: $ zip -r plugin.video.ludent.zip plugin.video.ludent To install the plug-in from the zip file, open XBMC, go to System then Add-ons, then click ‘Install from zip file’. The benefit of installing from a zip file is that XBMC will automatically try to install all the dependent plug-ins as well. Once you have the plug-in installed, you can run it by going to the Videos Add-ons section of XBMC, selecting Get More… and then clicking on LUD Reddit Viewer. You can access the settings dialog of the plug-in by right-clicking the LUD Reddit Viewer, then selecting ‘Add-on settings’. So, you have seen how robust and powerful XBMC’s extension system is. In this example, we were able to leverage the full power of Python (including those magical regular expression matches) from within XBMC. XBMC itself also offers a robust UI framework, which provides a very professional look for our add-on. As powerful as it may seem, we have only built a video plug-in. XBMC’s extension system also provides a framework for building fully fledged programs (called Programs). We will coverthisinalaterissue. 14Populatingtheepisodeview(listingvideos) At this point we have the URL in hand, which returns JSON data; now we need to extract the dataoutofitwhichwillmakesensetous. By looking at the JSON data, you can see there’s a lot of interesting information present here. For example, url is set to youtube.com/watch?v=n4rTztvVx8E; title is set to ‘The Counselor – Official Trailer’. There also many other bits of data that we will use, such as ups, downs, num_comments, thumbnail_urlandsoon.Inordertofilteroutthedatathatweneed,wewilluseregularexpressions. There is one more thing to note: since we are not presenting directories any more but are ready to placecontent,wehavetosetthexbmcplugin.setContenttoepisodesmode. def listVideos(url): currentUrl = url xbmcplugin.setContent(pluginhandle, episodes) content = opener .open(url).read() spl = content.split(‘content’) for i in range(1, len(spl), 1): entry = spl[i] try: match = re.compile(‘title: ( .+?)’ , re.DOTALL).findall(entry) title = match[0].replace(amp; , ) match = re.compile(‘description: ( .+?)’ , re.DOTALL). findall(entry) description = match[0] match = re.compile(‘created_utc: ( .+?), ’ , re.DOTALL).findall(entry) downs = int(match[0].replace(} , )) rating = int(ups*100/(ups+downs)) if filter and (ups+downs) filterVoteThreshold and rating filterRating: continue title = title+ (+str(rating)+%) match = re.compile(‘num_comments: ( .+?), ’ , re.DOTALL). findall(entry) comments = match[0] description = dateTime+ | +str(ups+downs)+ votes: +str(rating)+% Up | +comments+ commentsn+description match = re.compile(‘thumbnail_url: ( .+?)’ , re.DOTALL). findall(entry) thumb = match[0] matchYoutube = re.compile(‘url: http://www.youtube.com/ watch?v=( .+?)’ , re.DOTALL).findall(entry) matchVimeo = re.compile(‘url: http://vimeo.com/( .+?)’ , re.DOTALL).findall(entry) url = if matchYoutube: url = getYoutubeUrl(matchYoutube[0]) elif matchVimeo: url = getVimeoUrl(matchVimeo[0].replace(# , )) if url: addLink(title, url, ‘playVideo’ , thumb, description, date) except: pass match = re.compile(‘after: ( .+?)’ , re.DOTALL).findall(entry) xbmcplugin.endOfDirectory(pluginhandle) if forceViewMode: xbmc.executebuiltin(‘Container .SetViewMode(‘+viewMode+’)’)
  • 88.
    88 The PythonBook Work with Python Powerfulcalculationswith NumPy,SciPyandMatplotlib Scientificcomputing withNumPy NumPy is the primary Python package for performing scientific computing. It has a powerful N-dimensional array object, tools for integrating C/C++ and Fortran code, linear algebra, Fourier transform, and random number capabilities, among other things. NumPy also supports broadcasting, which is a clever way for universal functions to deal in a meaningful way with inputs that do not have exactly the same form. Apart from its capabilities, the other advantage of NumPy is that it can be integrated into Python programs. In other words, you may get your data from a database, the output of another program, an external file or an HTML pageandthenprocessitusingNumPy. This article will show you how to install NumPy, make calculations, plot data, read and write external files, and it will introduce you to some Matplotlib and SciPy packages that work wellwithNumPy. NumPy also works with Pygame, a Python package for creating games, though explaining itsuseisbeyondofthescopeofthisarticle. It is considered good practice to try the various NumPy commands inside the Python shellbeforeputtingthemintoPythonprograms. The examples in this article are using either PythonshelloriPython. AsimplePython programfor PolynomialFitting! APythonscript thatusesSciPyto processanimage Matplotlib generatedoutput Findinghelp iseasy Resources NumPy: www.numpy.org SciPy: www.scipy.org Matplotlib: www.matplotlib.org 01 Installing NumPy Most Linux distributions have a ready-to-install package you can use. After installation, you can find out the NumPy version youareusingbyexecutingthefollowing: $ python Python 2.7 .3 (default, Mar 13 2014, 11:03:55) [GCC 4.7 .2] on linux2 Type help , copyright , credits or license for more information. numpy.version.version
  • 89.
    The Python Book89 Work with Python Traceback (most recent call last): File stdin , line 1, in module NameError: name 'numpy' is not defined import numpy numpy.version.version '1.6.2' Not only have you found the NumPy version but you also know that NumPy is properly installed. 02AboutNumPy Despite its simplistic name, NumPy is a powerful Python package that is mainly for working with arrays and matrices. There are many ways to create an array but the simplest is by using the array() function: oneD = array([1,2,3,4]) The aforementioned command creates a one-dimensional array. If you want to create a two-dimensional array, you can use the array() functionasfollows: twoD = array([ [1,2,3], ... [3,3,3], ... [-1,-0.5,4], ... [0,1,0]] ) Youcanalsocreatearrayswithmoredimensions. 03Making simple calculations using NumPy Given an array named myArray, you can find the minimum and maximum values in it by executing the following commands: myArray.min() myArray.max() Should you wish to find the mean value of all array elements, run the next command: myArray.mean() Similarly, you can find the median of the array by running the following command: median(myArray) The median value of a set is an element that divides the data set into two subsets (left and right subsets) with the same number of elements. If the data set has an odd number of elements, then the median is part of the data set. On the other side, if the data set has an even number of elements, then the median is the mean value of the two centre elements of the sorted data set. 04UsingarrayswithNumPy NumPy not only embraces the indexing methods used in typical Python for strings and lists but also extends them. If you want to select a given element from an array, you can use the followingnotation: twoD[1,2] You can also select a part of an array (a slice) usingthefollowingnotation: twoD[ :1,1:3] Finally, you can convert an array into a Python listusingthetolist()function. 05Readingfiles Imaginethatyouhavejustextracted informationfromanApachelogfileusingAWKand youwanttoprocessthetextfileusingNumPy. The following AWK code finds out the total numberofrequestsperhour: $ cat access.log | cut -d[ -f2 | cut -d] -f1 | awk -F: '{print $2} ' | sort -n | uniq -c | awk '{print $2, $1} ' timeN.txt The format of the text file (timeN.txt) with the data is the following: 00 191 01 225 02 121 03 104 Reading the timeN.txt file and assigning it to a new array variable can be done as follows: aa = np.loadtxt(timeN.txt) 06Writingtofiles Writing variables to a file is largely similar to reading a file. If you have an array variable named aa1, you can easily save its contents into a file called aa1.txt by using the followingcommand: In [17]: np.savetxt(aa1.txt, aa1) As you can easily imagine, you can read the contents of aa1.txt later by using the loadtxt()function. 07Commonfunctions NumPy supports many numerical and statistical functions. When you apply a function to an array, the function is automatically applied toallarrayelements. When working with matrices, you can find the inverse of a matrix AA by typing “AA.I”. You can also find its eigenvalues by typing “np.linalg. eigvals(AA)” and its eigenvector by typing “np. linalg.eig(BB)”. 08Workingwithmatrices A special subtype of a two-dimensional NumPy array is a matrix. A matrix is like an array except that matrix multiplication replaces element-by-element multiplication. Matrices are generated using the matrix (or mat) function asfollows: In [2]: AA = np.mat('0 1 1; 1 1 1; 1 1 1 ') You can add two matrices named AA and BB by typing AA + BB. Similarly, you can multiply them bytypingAA*BB. 03Makingsimple calculations
  • 90.
    90 The PythonBook Work with Python SciPyisbuiltontopofNumPy andismoreadvanced 09PlottingwithMatplotlib The first move you should make is to install Matplotlib. As you can see, Matplotlib has manydependenciesthatyoushouldalsoinstall. The first thing you will learn is how to plot a polynomial function. The necessary commands for plotting the 3x^2-x+1 polynomial are the following: import numpy as np import matplotlib.pyplot as plt myPoly = np.poly1d(np.array([3, -1, 1]). astype(float)) x = np.linspace(-5, 5, 100) y = myPoly(x) plt.xlabel('x values') plt.ylabel('f(x) values') xticks = np.arange(-5, 5, 10) yticks = np.arange(0, 100, 10) plt.xticks(xticks) plt.yticks(yticks) plt.grid(True) plt.plot(x,y) The variable that holds the polynomial is myPoly. The range of values that will be plotted for x is defined using “x = np.linspace(-5, 5, 100)”. The other important variable is y, which calculates and holds the values of f(x) for each x value. It is important that you start ipython using the “ipython --pylab=qt” parameters in order to see the output on your screen. If you are interested in plotting polynomial functions, you should experiment more, as NumPy can also calculate the derivatives of a function and plot multiple functions in the same output. 10AboutSciPy SciPy is built on top of NumPy and is more advanced than NumPy. It supports numerical integration, optimisations, signal processing, image and audio processing, and statistics. The example in Fig. 01 (to the left) uses a small part of the scipy.stats package that isaboutstatistics. The example uses two statistics distributions and may be difficult to understand even if you know mathematics, but it is presented in order togiveyouabettertasteofSciPycommands. 11UsingSciPyforimageprocessing Now we will show you how to process and transform a PNG image using SciPy. The most important part of the code is the following line: In [36]: from scipy.stats import poisson, lognorm In [37]: mySh = 10; In [38]: myMu = 10; In [39]: ln = lognorm(mySh) In [40]: p = poisson(myMu) In [41]: ln.rvs((10,)) Out[41]: array([ 9.29393114e-02, 1.15957068e+01, 9.78411983e+01, 8.26370734e-07, 5.64451441e-03, 4.61744055e-09, 4.98471222e-06, 1.45947948e+02, 9.25502852e-06, 5.87353720e-02]) In [42]: p.rvs((10,)) Out[42]: array([12, 11, 9, 9, 9, 10, 9, 4, 13, 8]) In [43]: ln.pdf(3) Out[43]: 0.013218067177522842 Fig 01 09Plottingwith Matplotlib
  • 91.
    The Python Book91 Work with Python ProcessandtransformaPNG imageusingSciPy 12Otherusefulfunctions It is very useful to be able to find out the data type of the elements in an array; it can be done using the dtype() function. Similarly, the ndim() function returns the number of dimensions of an array. When reading data from external files, you can save their data columns into separate variables using the following way: In [10]: aa1,aa2 = np.loadtxt(timeN.txt, usecols=(0,1), unpack=True) The aforementioned command saves column 1 into variable aa1 and column 2 into variable aa2. The “unpack=True” allows the data to be assigned to two different variables. Please note that the numbering of columns starts with 0. 14ArraybroadcastinginNumPy To close, we will talk more about array broadcasting because it is a very useful characteristic. First, you should know that array broadcasting has a rule: in order for two arrays to be considered for array broadcasting, “the size of the trailing axes for both arrays in an operation must either be the same size or one of them must be one.” Put simply, array broadcasting allows NumPy to “change” the dimensions of an array by filling it with data in order to be able to do calculations with another array. Nevertheless, you cannot stretch both dimensions of an array to do your job. 13Fittingtopolynomials The NumPy polyfit() function tries to fit a set of data points to a polynomial. The data was found from the timeN.txt file, created earlier in this article. The Python script uses a fifth degree polynomial, but if you want to use a different degree instead then you only have to change the following line: coefficients = np.polyfit(aa1, aa2, 5) image = np.array(Image.open('SA.png'). convert('L ')) This line allows you to read a usual PNG file and convert it into a NumPy array for additional processing. The program will also separate the output into four parts and displays a different image for each of these four parts. 11UsingSciPyfor imageprocessing 13Fittingto Polynomials
  • 92.
    92 The PythonBook Work with Python Howtoprogramboththeclient,completewithaGUI,and serverofasimpleinstantmessengerinPython InstantmessagingwithPython He’re we’ll be implementing an instant messenger in Python with a client-server architecture. This means each client connects to the server, which relays any message that one client sends to all other clients. The server will also notify the other clients when someone joinsorleavestheserver.Theinstantmessenger can work anywhere a TCP socket can: on the same computer with the loopback interface, across various computers on a LAN, or even over the internet if you were to configure your router correctly. However, our messages aren’t encrypted,sowewouldn’trecommendthat. Writing an instant messenger is an interesting technical problem that covers a bunch of areas that you may not have come across while programmingbefore: • We’ll be employing sockets, which are used totransmitdataacrossnetworks. • We’ll also be using threading, which allows a programtodomultiplethingsatonce. • We’ll cover the basics of writing a simple graphical user interface with GTK, as well as howtointeractwiththatfromadifferentthread. • Finally, we’ll be touching on the use of regularexpressionstoeasilyanalyseandextract datafromstrings. Before getting started, you’ll need to have a Python2.x interpreter installed, as well as the PyGTK bindings and the Python2 GObject bindings. The chances are that if you have a system with a fair amount of software on it, you will already have these packages, so it may be easier to wait and see if you’re missing any libraries when you attempt to import them. All of the above packages are commonly used, so you should be able to install them using your distro’s packagemanager. Resources Acomputer–runningyourfavouriteLinux distribution Internetconnection–toaccess documentation Python2.x,PyGTKandGObject– packagesinstalled Theservernotifies allclientswhena newclientjoins Eachmessage hasatimestamp prefixedtoit Similarly,theserver notifiesallclients whenaclientleaves Aclientcandetect whentheserver exitswithout crashingorhanging
  • 93.
    The Python Book93 Work with Python 01Theserver The server will do the following jobs: • Listen for new clients • Notify all clients when a new client joins • Notify all clients when a client leaves • Receive and deliver messages to all clients We’re going to write the server side of the instant messenger first, as the client requires it. There will be two code files, so it’s a good idea to make a folder to keep them inside. You can create an empty file with the command touch [filename], and mark that file as executable using chmod +x [filename]. This file is now ready to edit in your favourite editor. [liam@liam-laptop Python]$ mkdir Python-IM [liam@liam-laptop Python]$ cd Python-IM/ [liam@liam-laptop Python-IM]$ touch IM-Server.py [liam@liam-laptop Python-IM]$ chmod +x IM-Server.py 02Startingoff As usual, we need to start off with the line that tells the program loader what it needs to interpret the rest of the file with. In your advisor’s case, that line is: #!/usr/bin/env python2. On your system, it may need to be changed to #!/usr/bin/env/ python2.6 or #!/usr/ bin/env python2.7 After that, we’ve written a short comment about what the application does, and imported the required libraries. We’ve already mentioned what the threading and socket libraries are for. The re library is used for searching strings with regular expressions. The signal library is used for dealing with signals that will kill the program, such as SIGINT. SIGINT is sent when Ctrl+C is pressed. We handle these signals so that the program can tell the clients that it’s exiting rather than dying unexpectedly. The sys library is used to exit the program. Finally, the timelibraryisusedtoputasensiblelimitonhow frequentlythebodyofwhileloopsexecute. #!/usr/bin/env python2 # The server side of an instant messaging application. Written as part of a Linux User Developer tutorial by Liam Fraser in 2013. import threading 03TheServerclass TheServerclassisthemainclassofour instant messenger server. The initialiser of this class accepts a port number to start listening for clients on. It then creates a socket, binds the socket to the specified port on all interfaces, and then starts to listen on that port. You can optionally include an IP address in the tuple that contains the port. Passing in a blank string like wehavedonecausesittolistenonallinterfaces. The value of 1 passed to the listen function specifies the maximum number of queued connections we can accept. This shouldn’t be a problem as we’re not expecting a bunch of clientstoconnectatexactlythesametime. Now that we have a socket, we’ll create an empty array that will be later used to store a collection of client sockets that we can echo messages to. The final part is to tell the signal library to run the self.signal_handler function, which we have yet to write, when a SIGINT or SIGTERM is sent to the application so that we cantidyupnicely. class Server(): def __init__(self, port): # Create a socket and bind it to a port self.listener = socket. socket(socket.AF_INET, socket.SOCK_ STREAM) self.listener.bind((‘’, port)) self.listener.listen(1) print “Listening on port {0}”.format(port) # Used to store all of the client sockets we have, for echoing to them self.client_sockets = [] # Run the function self.signal_ handler when Ctrl+C is pressed signal.signal(signal.SIGINT, self.signal_handler) signal.signal(signal. SIGTERM, self.signal_handler) 04Theserver’smainloop The server’s main loop essentially accepts new connections from clients, adds that client’s socket to the collection of import socket import re import signal import sys import time Threading:docs.python.org/2/library/ threading.html Sockets: docs.python.org/2/library/ socket.html Regular expressions:docs.python. org/2/library/re.html Thesignalhandler:docs.python.org/ 2/library/signal.html PyGTK: www.pygtk.org/ pygtk2reference GObject: www.pygtk.org/ pygtk2reference/gobject-functions.html Useful documentation sockets and then starts an instance of the ClientListener class, which we have yet to write, in a new thread. Sometimes, defining interfaces you are going to call before you’ve written them is good, because it can give an overview of how the program will work without worrying about the details. Note that we’re printing information as we go along, to make debugging easier should we need to do it. Sleeping at the end of the loop is useful to make sure the while loop can’t run quickly enough to hang the machine. However, this is unlikely to happen as the line that accepts new connections is blocking, which means that the program waits for a connection before moving on from that line. For this reason, we need to enclose the line in a try block, so that we can catch the socket error and exit when we can no longer accept connections. This will usually be whenwe’veclosedthesocketduringtheprocess ofquittingtheprogram. def run(self): while True: # Listen for clients, and create a ClientThread for each new client print “Listening for more clients” try: (client_socket, client_address) = self.listener. accept() except socket.error: sys.exit(“Could not
  • 94.
    94 The PythonBook Work with Python 09Tidyingup We need to have a function to tidy up the thread. We’ll call this either when the client sends us a blank string (indicating that it’s stopped listening on the socket) or sends us the string “QUIT”. When this happens, we’ll echo to every client that the user has quit. def quit(self): # Tidy up and end the thread self.listening = False self.socket.close() self.server.remove_ socket(self.socket) self.server.echo({0} has quit.n.format(self.username)) 05Theechofunction We need a function that can be called from a client’s thread to echo a message to each client. This function is pretty simple. The most important part is that sending data to sockets is in a try block, which means that we can handle the exception if the operation fails, rather than havingtheprogramcrash. def echo(self, data): # Send a message to each socket in self.client_socket print echoing: {0}. format(data) for socket in self.client_ sockets: # Try and echo to all clients try: socket.sendall(data) except socket.error: print Unable to send message 06FinishingtheServerclass The remainder of the Server class is taken up with a couple of simple functions; one to remove a socket from the collection of sockets, which doesn’t need an explanation, and the signal_handler function that we talked about in the initialiser of the class. This function stops listening for new connections, and unbinds the socket from the port it was listening on. Finally, we send a message to each client to let them know that we are exiting. The signal will continue to close the program as expected once thesignal_handlerfunctionhasended. def remove_socket(self, socket): 07Theclientthread The class that is used to deal with each client inherits the Thread class. This means that the class can be created, then started with client_thread.start(). At this point, the code in the run function of the class will be run in the background and the main loop of the Server classwillcontinuetoacceptnewconnections. We have to start by initialising the Thread base class, using the super keyword. You may have noticed that when we created a new instance of theClientListenerclassintheserver’smainloop, we passed through the server’s self variable. We dothisbecauseit’sbetterforeachinstanceofthe ClientListener class to have its own reference to the server, rather than using the global one that we’llcreatelatertoactuallystarttheapplication. class ClientListener(threading. Thread): def __init__(self, server, socket, address): # Initialise the Thread base class super(ClientListener, self).__init__() # Store the values that have been passed to the constructor self.server = server self.address = address self.socket = socket self.listening = True self.username = No Username 08Theclientthread’sloop The loop that runs in the client thread is pretty similar to the one in the server. It keeps listening for data while self.listening is true, and passes any data it gets to a handle_msg function that we will write shortly. The value passed to the socket.recv function is the size of 10Handlingmessages There are three possible messages our clientscansend: • QUIT • USERNAME user • Arbitrary string to be echoed to all clients The client will also send a bunch of empty messages if the socket has been closed, so we will end their thread if that happens. The code should be pretty self-explanatory apart from the regular expression part. If someone sends the USERNAME message, then the server tells every client that a new user has joined. This is tested with a regular expression. ^ indicates the start of the string, $ indicates the end, and the brackets containing .* extract whatever comes after“USERNAME”. # Remove the specified socket from the client_sockets list self.client_sockets. remove(socket) def signal_handler(self, signal, frame): # Run when Ctrl+C is pressed print Tidying up # Stop listening for new connections self.listener.close() # Let each client know we are quitting self.echo(QUIT) accept any more connections”) self.client_sockets. append(client_socket) print “Starting client thread for {0}”.format(client_ address) client_thread = ClientListener(self, client_socket, client_address) client_thread.start() time.sleep(0.1) thebuffertousewhilereceivingdata. def run(self): # The thread's loop to receive and process messages while self.listening: data = try: data = self.socket. recv(1024) except socket.error: Unable to recieve data self.handle_msg(data) time.sleep(0.1) # The while loop has ended print Ending client thread for {0}.format(self.address)
  • 95.
    Work with Python 11Startingtheserver Thecode that actually starts the Server class is as follows. Note that you are probably best picking a high-numbered port as you need toberoottoopenports1024. if __name__ == __main__: # Start a server on port 59091 server = Server(59091) server.run() 13Theclientgraphicaluserinterface The user interface of the client isn’t the main focus of the tutorial, and won’t be explained in as much detail as the rest of the code. However, the code should be fairly straightforward to read and we have provided links to documentation that will help. Our MainWindow class inherits the gtk Window class, so we need to start by initialising that using the super keyword. Then we create the controls that will go on the window, connect any events they have to functions, and finally lay out the controls how we want. The destroy event is raised when the program is closed, and the other events should be obvious. GTK uses a packing layout, in which you use Vboxes and Hboxes to lay out the controls. V and H stand for vertical and horizontal. These controls essentially let you split a window up almost like a table, and will automatically decide the size of the controls depending on the size of the application. GTK doesn’t come with a control to enter basic information, such as the server’s IP address, port and your chosen username, so we’ve made a function called ask_for_info, which creates a message box, adds a text box to it and then retrieves the results. We’ve done this because it’s simpler and uses less code than creating a new window to accept the information. 12Theclient Create a new file for the client as we did for the server and open it in your favourite editor. The client requires the same imports as the server, as well as the gtk, gobject and datetime libraries. One important thing we need to do is to tell GObject that we’ll be using threading, so we can call functions from other threads and have the main window, which is running in the main GTKthread,update. class MainWindow(gtk.Window): def __init__(self): # Initialise base gtk window class super(MainWindow, self).__ init__() # Create controls self.set_title(IM Client) vbox = gtk.VBox() hbox = gtk.HBox() self.username_label = gtk. Label() self.text_entry = gtk. Entry() send_button = gtk. Button(Send) self.text_buffer = gtk. TextBuffer() text_view = gtk. TextView(self.text_buffer) # Connect events self.connect(destroy, self.graceful_quit) send_button. connect(clicked, self.send_ message) # Activate event when user presses Enter self.text_entry. connect(activate, self.send_ message) # Do layout vbox.pack_start(text_view) hbox.pack_start(self. username_label, expand = False) WeneedtotellGObjectthatwe’llbe usingthreading def handle_msg(self, data): # Print and then process the message we’ve just recieved print {0} sent: {1}. format(self.address, data) # Use regular expressions to test for a message like USERNAME liam username_result = re.search('^USERNAME (.*)$', data) if username_result: self.username = username_result.group(1) self.server.echo({0} has joined.n.format(self. username)) elif data == QUIT: # If the client has sent quit then close this thread self.quit() elif data == : # The socket at the other end is probably closed self.quit() else: # It's a normal message so echo it to everyone self.server.echo(data) #!/usr/bin/env python2 # The client side of an instant messaging application. Written as part of a Linux User Developer tutorial by Liam Fraser in 2013. import threading import gtk import gobject import socket import re import time import datetime # Tell gobject to expect calls from multiple threads gobject.threads_init()
  • 96.
    Work with Python 14Configuringtheclient Thiscode is run after we’ve added the controls to the main window, and asks the user for input. Currently, the application will exit if the user enters an incorrect server address or port; butthisisn’taproductionsystem,sothat’sfine. def configure(self): # Performs the steps to connect to the server # Show a dialog box asking for server address followed by a port server = self.ask_for_ info(server_address:port) # Regex that crudely matches an IP address and a port number regex = re.search('^(d+. d+.d+.d+):(d+)$', server) address = regex.group(1). strip() port = regex.group(2). strip() # Ask for a username self.username = self.ask_ for_info(username) self.username_label.set_ text(self.username) # Attempt to connect to the server and then start listening self.network = Networking(self, self.username, address, int(port)) self.network.listen() Theserverisgoingtoechothe messagetoeachclient 15TheremainderofMainWindow The rest of the MainWindow class has plenty of comments to explain itself, as follows. One thing to note is that when a client sends a message, it doesn’t display it in the text view straight away. The server is going to echo the message to each client, so the client simply displays its own message when the server echoes it back. This means that you can tell if the server is not receiving your messages when youdon’tseeamessagethatyousend. def add_text(self, new_text): # Add text to the text view text_with_timestamp = {0} {1}.format(datetime.datetime.now(), new_text) # Get the position of the end of the text buffer, so we know where to insert new text end_itr = self.text_buffer. get_end_iter() # Add new text at the end of the buffer self.text_buffer.insert(end_ itr, text_with_timestamp) def send_message(self, widget): # Clear the text entry and send the message to the server # We don't need to display it as it will be echoed back to each client, including us. new_text = self.text_entry. get_text() self.text_entry.set_text() message = {0} says: {1}n. format(self.username, new_text) self.network.send(message) def graceful_quit(self, widget): # When the application is closed, tell GTK to quit, then tell the server we are quitting and tidy up the network gtk.main_quit() self.network.send(QUIT) self.network.tidy_up() hbox.pack_start(self.text_ entry) hbox.pack_end(send_button, expand = False) vbox.pack_end(hbox, expand = False) # Show ourselves self.add(vbox) self.show_all() # Go through the configuration process self.configure() def ask_for_info(self, question): # Shows a message box with a text entry and returns the response dialog = gtk. MessageDialog(parent = self, type = gtk.MESSAGE_QUESTION, flags = gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, buttons = gtk.BUTTONS_OK_CANCEL, message_format = question) entry = gtk.Entry() entry.show() dialog.vbox.pack_end(entry) response = dialog.run() response_text = entry. get_text() dialog.destroy() if response == gtk.RESPONSE_ OK: return response_text else: return None
  • 97.
    Work with Python ThePython Book 97 17Runningafunctionasathread The listener function above will be run as a thread. This is trivial to do. Enabling the daemon option on the thread means that it will dieifthemainthreadunexpectedlyends. def listen(self): # Start the listening thread self.listen_thread = threading.Thread(target=self. listener) # Stop the child thread from keeping the application open self.listen_thread.daemon = True self.listen_thread.start() 18FinishingtheNetworkingclass Again, most of this code is similar to the code in the server’s Networking class. One 19Startingtheclient The main window is started by initialising an instance of the class. Notice that we don’t need to store anything that is returned. We then starttheGTKthreadbycallinggtk.main(). if __name__ == __main__: # Create an instance of the main window and start the gtk main loop MainWindow() gtk.main() 21That’sit! So, it’s not perfect and could be a little more robust in terms of error handling, but we have a working instant messenger server that can accept multiple clients and relay messages between them. More importantly, we have learned a bunch of new concepts and methods ofworking. 20Tryingitout You’ll want a few terminals: one to start the server, and some to run clients. Once you’ve started the server, open an instance of the client and enter 127.0.0.1:port, where ‘port’ is the port you decided to use. The server will print the port it’s listening on to make this easy. Then enter a username and click OK. Here is an example output from the server with two clients. You can use the client over a network by replacing 127.0.0.1 with the IP address of the server. You may have to let the port through your computer’sfirewallifit’snotworking. [liam@liam-laptop Python]$ ./IM- Server.py Listening on port 59091 Listening for more clients Starting client thread for ('127.0.0.1', 38726) ('127.0.0.1', 38726) sent: USERNAME client1 echoing: client1 has joined. Listening for more clients Starting client thread for ('127.0.0.1', 38739) ('127.0.0.1', 38739) sent: USERNAME client2 echoing: client2 has joined. Listening for more clients ('127.0.0.1', 38739) sent: client2 says: Hi echoing: client2 says: Hi ('127.0.0.1', 38726) sent: client1 says: Hi echoing: client1 says: Hi ('127.0.0.1', 38726) sent: QUIT echoing: client1 has quit. Ending client thread for ('127.0.0.1', 38726) ^CTidying up echoing: QUIT Could not accept any more connections ('127.0.0.1', 38739) sent: echoing: client2 has quit. Ending client thread for ('127.0.0.1', 38739) difference is that we want to add some things to the text view of our window. We do this by using the idle_add function of GObject. This allows us to call a function that will update the window runninginthemainthreadwhenitisnotbusy. def send(self, message): # Send a message to the server print Sending: {0}. format(message) try: self.socket. sendall(message) except socket.error: print Unable to send message def tidy_up(self): # We'll be tidying up if either we are quitting or the server is quitting self.listening = False self.socket.close() # We won't see this if it's us that's quitting as the window will be gone shortly gobject.idle_add(self. window.add_text, Server has quit.n) def handle_msg(self, data): if data == QUIT: # Server is quitting self.tidy_up() elif data == : # Server has probably closed unexpectedly self.tidy_up() else: # Tell the GTK thread to add some text when it's ready gobject.idle_add(self. window.add_text, data) 16Theclient’sNetworkingclass Much of the client’s Networking class is similar to that of the server’s. One difference is that the class doesn’t inherit the Thread class – wejuststartoneofitsfunctionsasathread. class Networking(): def __init__(self, window, username, server, port): # Set up the networking class self.window = window self.socket = socket. socket(socket.AF_INET, socket.SOCK_ STREAM) self.socket.connect((server, port)) self.listening = True # Tell the server that a new user has joined self.send(USERNAME {0}. format(username)) def listener(self): # A function run as a thread that listens for new messages while self.listening: data = try: data = self.socket. recv(1024) except socket.error: Unable to recieve data self.handle_msg(data) # Don't need the while loop to be ridiculously fast time.sleep(0.1)
  • 98.
    98 The PythonBook Work with Python Pythonisagreatprogramminglanguage,butdidyouknowthat itisevencapableofreplacingyourprimaryshell(command-line interface)?Here,weexplainall… We all use shell on a daily basis. For most of us, shell is the gateway into our Linux system. For years and even today, Bash has beenthedefaultshellforLinux.Butitisgettinga bitlonginthetooth. No need to be offended: we still believe Bash is the best shell out there when compared to some other UNIX shells such as Korn Shell (KSH),CShell(CSH)orevenTCSH. This tutorial is not about Bash being incapable, but it is about how to breathe completely new life into the shell to do old things conveniently and new things which were previously not possible, even by a long shot. So, withoutfurtherdelay,let’sjumpin. While the Python programming language may require you to write longer commands to accomplish a task (due to the way Python’s modules are organised), this is not something to be particularly concerned about. You can easily write aliases to the equivalent of the Bash command that you intend to replace. Most of the time there will be more than one way to do a thing, but you will need to decide which way worksbestforyou. Python provides support for executing system commands directly (via the os or subprocess module), but where possible we will focus on Python-native implementations, as thisallowsustodevelopportablecode. Replaceyourshell withPython YouwillrequireaversionofPythoninstalledon yoursystem.Thegoodnewsisyoudon’thaveto doanythingtogetitinstalled.MostoftheLinux distributionsalreadyship with eitherPython 2.6or Python2.7 Resources
  • 99.
    The Python Book99 Work with Python Youcaneasilywritealiasestothe equivalentoftheBashcommandthat youintendtoreplace SECTION1:Completingbasicshell tasksinPython 1.Filemanagement The Python module shutil provides support for file and directory operations. It provides support for file attributes, directory copying, archiving etc.Let’slookatsomeofitsimportantfunctions. shutil module copy (src,dst): Copy the src file to the destination directory. In this mode permissions bits are copied but metadata is not copied. copy2 (src,dst): Same as copy() but also copies the metadata. copytree(src, dst[, symlinks=False[, ignore=None]]): This is similar to ‘cp -r’, it allows you to copy an entire directory. ignore_patterns (*patterns): ignore_ patterns is an interesting function that can be used as a callable for copytree(), it allows you to ignore files and directories specified by the glob-style patterns. rmtree(path[, ignore_errors[, onerror]]): rmtree() is used to delete an entire directory. move(src,dst): Similar to mv command it allows you to recessively move a file or directory to a new location. Example: from shutil import copytree, ignore_ patterns copytree(source, destination, ignore=ignore_patterns(‘*.pyc’, ‘tmp*’)) make_archive(base_name, format[, root_ dir[, base_dir[, verbose[, dry_run[, owner[, group[, logger]]]]]]] : Think of this as a replacement for tar, zip, bzip etc. make_archive() creates an archive file in the given format such as zip, bztar, tar , gztar. Archive support can be extended via Python modules. Example from shutil import make_archive import os archive_name = os.path. expanduser(os.path.join(‘~’, ‘ludarchive’)) root_dir = os.path.expanduser(os. path.join(‘~’, ‘.ssh’)) make_archive(archive_name, ‘gztar’, root_dir) ‘/Users/kunal/ludarchive.tar.gz’ 2.Interfacingoperatingsystem subprocesses Python provides two modules to interface with the OS and to manage processes, called os and subprocess. These modules allow you to interact with the core operating system shell and let you work with the environment, processes,usersandfiledescriptors. The subprocess module was introduced to support better management of subprocesses (part of which already exists in the os module) in Python and is aimed to replace os.system, os.spawn*, os.popen, popen2.* and commands.*modules. osmodule environ: environment represents the OS environmentvariablesinastringobject. example: import os os.environ {‘VERSIONER_PYTHON_PREFER_32_BIT’: ‘no’, ‘LC_CTYPE’: ‘UTF-8’, ‘TERM_ PROGRAM_VERSION’: ‘297’, ‘LOGNAME’: ‘kunaldeo’, ‘USER’: ‘kunaldeo’, ‘PATH’: ‘/System/Library/Frameworks/Python. framework/Versions/2.7/bin:/Users/ kunaldeo/narwhal/bin:/opt/local/sbin:/ usr/local/bin:/usr/bin:/bin:/usr/sbin:/ sbin:/usr/local/bin:/usr/X11/bin:/opt/ local/bin:/Applications/MOTODEV_Studio_ For_Android_2.0.0_x86/android_sdk/ tools:/Applications/MOTODEV_Studio_For_ Android_2.0.0_x86/android_sdk/platform- tools:/Volumes/CyanogenModWorkspace/ bin’, ‘HOME’: ‘/Users/kunaldeo’, ‘PS1’: ‘[e[0;32m]u[e[m] [e[1;34m]w[e[m] [e[1;32m]$[e[m] [e[1;37m]’, ‘NARWHAL_ENGINE’: ‘jsc’, ‘DISPLAY’: ‘/tmp/launch-s2LUfa/ org.x:0’, ‘TERM_PROGRAM’: ‘Apple_ Terminal’, ‘TERM’: ‘xterm-color’, ‘Apple_PubSub_Socket_Render’: ‘/tmp/ launch-kDul5P/Render’, ‘VERSIONER_ PYTHON_VERSION’: ‘2.7’, ‘SHLVL’: ‘1’, ‘SECURITYSESSIONID’: ‘186a5’, ‘ANDROID_SDK’: ‘/Applications/MOTODEV_ Studio_For_Android_2.0.0_x86/android_ sdk’,’_’: ‘/System/Library/Frameworks/ Python.framework/Versions/2.7/bin/ python’, ‘TERM_SESSION_ID’: ‘ACFE2492- BB5C-418E-8D4F-84E9CF63B506’, ‘SSH_ AUTH_SOCK’: ‘/tmp/launch-dj6Mk4/ Listeners’, ‘SHELL’: ‘/bin/bash’, ‘TMPDIR’: ‘/var/folders/6s/pgknm8b118 737mb8psz8x4z80000gn/T/’, ‘LSCOLORS’: ‘ExFxCxDxBxegedabagacad’, ‘CLICOLOR’: ‘1’, ‘__CF_USER_TEXT_ENCODING’: ‘0x1F5:0:0’, ‘PWD’: ‘/Users/kunaldeo’, ‘COMMAND_MODE’: ‘unix2003’} You can also find out the value for an environmentvalue: os.environ[‘HOME’] ‘/Users/kunaldeo’ putenv(varname,value) : Adds or sets an environment variable with the given variable name and value. getuid() : Return the current process’s user id. getlogin() : Returns the username of currently logged in user getpid(pid) : Returns the process group id of given pid. When used without any parameters it simply returns the current process id. getcwd() : Return the path of the current working directory. chdir(path) : Change the current working directory to the given path.
  • 100.
    100 The PythonBook Work with Python listdir(path) : Similar to ls, returns a list with the content of directories and file available on the given path. Example: os.listdir(“/home/homer”) [‘.gnome2’, ‘.pulse’, ‘.gconf’, ‘.gconfd’, ‘.beagle’, ‘.gnome2_ private’, ‘.gksu.lock’, ‘Public’, ‘.ICEauthority’, ‘.bash_history’, ‘.compiz’, ‘.gvfs’, ‘.update- notifier’, ‘.cache’, ‘Desktop’, ‘Videos’, ‘.profile’, ‘.config’, ‘.esd_auth’, ‘.viminfo’, ‘.sudo_ as_admin_successful’, ‘mbox’, ‘.xsession-errors’, ‘.bashrc’, ‘Music’, ‘.dbus’, ‘.local’, ‘.gstreamer-0.10’, ‘Documents’, ‘.gtk-bookmarks’, ‘Downloads’, ‘Pictures’, ‘.pulse- cookie’, ‘.nautilus’, ‘examples. desktop’, ‘Templates’, ‘.bash_logout’] mkdir(path[, mode]) : Creates a directory with the given path with the numeric code mode. The default mode is 0777. makedirs(path[, mode]) : Creates given path (inclusive of all its directories) recursively. The default mode is 0777. : Example: import os path = “/home/kunal/greatdir” os.makedirs( path, 0755 ); rename (old,new) : The file or directory “old” is renamed to “new” If “new” is a directory, an error will be raised. On Unix and Linux, if “new” exists and is a file, it will be replaced silently if the user has permission to do so. renames (old,new) : Similar to rename but also creates any directories recessively if necessary. rmdir(path) : Remove directory from the path mentioned. If the path already has files you will need to use shutil. rmdtree() subprocess: call(*popenargs, **kwargs) : Runs the command with arguments. On process completion it returns the returncode attribute. Example: import subprocess print subprocess.call([“ls”,”-l”]) total 3684688 drwx------+ 5 kunaldeo staff 170 Aug 19 01:37 Desktop drwx------+ 10 kunaldeo staff 340 Jul 26 08:30 Documents drwx------+ 50 kunaldeo staff 1700 Aug 19 12:50 Downloads drwx------@ 127 kunaldeo staff 4318 Aug 19 01:43 Dropbox drwx------@ 42 kunaldeo staff 1428 Aug 12 15:17 Library drwx------@ 3 kunaldeo staff 102 Jul 3 23:23 Movies drwx------+ 4 kunaldeo staff 136 Jul 6 08:32 Music drwx------+ 5 kunaldeo staff 170 Aug 12 11:26 Pictures drwxr-xr-x+ 5 kunaldeo staff 170 Jul 3 23:23 Public -rwxr-xr-x 1 kunaldeo staff 1886555648 Aug 16 21:02 androidsdk.tar drwxr-xr-x 5 kunaldeo staff 170 Aug 16 21:05 sdk drwxr-xr-x 19 kunaldeo staff 646 Aug 19 01:47 src -rw-r--r-- 1 root staff 367 Aug 16 20:36 umbrella0.log STD_INPUT_HANDLE: The standard input device.Initially,thisistheconsoleinputbuffer. STD_OUTPUT_HANDLE: The standard output device. Initially, this is the active console screenbuffer. STD_ERROR_HANDLE: The standard error device. Initially, this is the active console screenbuffer. SECTION2:IPython:aready-made Pythonsystemshellreplacement In section 1 we have introduced you to the Python modules which allow you to do system shell-related tasks very easily using vanilla Python. Using the same features, you can build a fully featured shell and remove a lot of Python boilerplate code along the way. However, if you are kind of person who wants everything ready-made, you are in luck. IPython provides a powerful and interactive Python shell which you can use as your primary shell. IPython supports Python 2.6 to 2.7 and 3.1 to 3.2 . It supports two type of Python shells: Terminal based and Qtbased. Just to reiterate, IPython is purely implemented in Python and provides a 100% Python- compliant shell interface, so everything you have learnt in section 1 can be run inside IPythonwithoutanyproblems. IPython is already available in most Linux distributions. Search your distro’s repositories to look for it. In case you are not able to find it, you canalsoinstallitusingeasy_installorPyPI. IPython provides a lot of interesting features whichmakesitagreatshellreplacement… Tab completion: Tab completion provides an excellent way to explore any Python object that you are working with. It also helps you to avoid makingtypos. Example: In [3]: import o {hit tab} objc opcode operator optparse os os2emxpath In [3]: import os In [4]: os.p {hit tab} os.pardir os.pathconf_names os.popen os.popen4 os.path os.pathsep os.popen2 os.putenv os.pathconf os.pipe os.popen3 Built In Object Explorer: You can add ‘?’ after any Python object to view its details such as Type, Base Class, String Form, Namespace, File and Docstring. Example: In [28]: os.path? Type: module Base Class: type ‘module’ String Form:module ‘posixpath’ from ‘/System/Library/Frameworks/Python. framework/Versions/2.7/lib/python2.7/ posixpath.pyc’ Namespace: Interactive File: /System/Library/Frameworks/
  • 101.
    The Python Book101 Work with Python As you can see, it’s easy to tailor Python for all your shell environment needs. Python modules like os, subprocess and shutil are available at your disposal to do just about everything you need using Python. IPython turns this whole experience into an even more complete package. You get to do everything a standard Python shell does and with much more convenient features. IPython’s magic functions really do provide a magical Python shell experience. So next time you open a Bash session, think again: why settle for goldwhen platinum is a stepaway? IPythonalsocomeswithitsown Qt-basedconsole Python.framework/Versions/2.7/lib/ python2.7/posixpath.py Docstring: Common operations on POSIX pathnames. Instead of importing this module directly, import os and refer to this module as os.path. The ‘os.path’ name is an alias for this module on POSIX systems; on other systems (eg Mac, Windows),os.pathprovidesthesameoperations in a manner specific to that platform, and is an aliastoanothermodule(egmacpath,ntpath). Some of this can actually be useful on non- POSIX systems too, eg for manipulation of the pathnamecomponentofURLs. You can also use double question marks (??) to viewthesourcecodefortherelevantobject. Magic functions: IPython comes with a set of predefined ‘magic functions’ that you can call with a command-line-style syntax. IPython ‘magic’ commands are conventionally prefaced by %, but if the flag %automagic is set to on, then you can call magic commands without the preceding%. To view a list of available magic functions, you can use ‘magic function %lsmagic’. Magic functions include functions that work with code such as %run, %edit, %macro, %recall etc; functions that affect shell such as %colors, %xmode, %autoindent etc; and other functions such as %reset, %timeit, %paste etc. Most of the cool features of IPython are powered using magicfunctions. Example: In [45]: %lsmagic Available magic functions: %alias %autocall %autoindent %automagic %bookmark %cd %colors %cpaste %debug %dhist %dirs %doctest_mode %ed %edit %env %gui %hist %history %install_default_ config %install_profiles %load_ext %loadpy %logoff %logon %logstart %logstate %logstop %lsmagic %macro %magic %page %paste %pastebin %pdb %pdef %pdoc %pfile %pinfo %pinfo2 %popd %pprint %precision %profile %prun %psearch %psource %pushd %pwd %pycat %pylab %quickref %recall %rehashx %reload_ext %rep %rerun %reset %reset_selective %run %save %sc %sx %tb %time %timeit %unalias %unload_ext %who %who_ls %whos %xdel %xmode Automagic is OFF, % prefix IS needed for magic functions To view help on any Magic Function, call ‘%somemagic?’toreaditsdocstring. Python script execution and runtime code editing: You can use %run to run any Python script. You can also control-run the Python script with pdb debugger using -d, or pdn profiler using -p. You can also edit a Python script using the %edit command. %edit will open the given Python script in the editor definedbythe$EDITORenvironmentvariable. Shell command support: If you are in the mood to just run a shell command, you can do it very easilybyprefixingthecommandwith!. Example: In [5]: !ps PID TTY TIME CMD 4508 ttys000 0:00.07 -bash 84275 ttys001 0:00.03 -bash 17958 ttys002 0:00.18 -bash In [8]: !clang prog.c -o prog prog.c:2:1: warning: type specifier missing, defaults to ‘int’ [-Wimplicit- int] main() ^~~~ 1 warning generated. Qt console : IPython also comes with its own Qt-based console. This provides a number of features that are only available in a GUI, such as inline figures, multiline editing with syntax highlighting,andgraphicalcalltips. YoucanstarttheQtconsolewith: $ ipython qtconsole If you get errors related to missing modules, makesurethatyouhaveinstalledthedependent packages, such as PyQt, pygments, pyexpect andZeroMQ. QIPythonQtconsolewithGUIcapabilities
  • 102.
    102 The PythonBook Work with Python LearnhowPythoncanhelpinsystemadministrationasitdaresto replacetheusualshellscripting… Pythonforsystem administrators Parsingconfigurationfiles Configurationfilesprovideawayforapplications to store various settings. In order to write a script that allows you to modify settings of a particular application, you should be able to parse the configuration file of the application. In this section we learn how to parse INI-style configuration files. Although old, the INI file format is very popular with much modern open sourcesoftware,suchasPHPandMySQL. Excerptforphp.iniconfigurationfile: [PHP] engine = On zend.ze1_compatibility_mode = Off short_open_tag = On asp_tags = Off precision = 14 y2k_compliance = On output_buffering = 4096 ;output_handler = zlib.output_compression = Off [MySQL] ; Allow or prevent persistent links. mysql.allow_persistent = On mysql.max_persistent = 20 mysql.max_links = -1 mysql.default_port = 3306 mysql.default_socket = mysql.default_host = localhost mysql.connect_timeout = 60 mysql.trace_mode = Off Python provides a built-in module called ConfigParser (known as configparser in Python 3.0).Youcanusethismoduletoparseandcreate configurationfiles. @code:writeconfig.py @description: The following demonstrates addingMySQLsectiontothephp.inifile. @warning: Do not use this script with the actual php.ini file, as it’s not designed to handleallaspectsofacompletephp.inifile. import ConfigParser config = ConfigParser. RawConfigParser() config.add_section(‘MySQL’) config.set(‘MySQL’,’mysql.trace_ mode’,’Off’) config.set(‘MySQL’,’mysql.connect_ timeout’,’60’) config.set(‘MySQL’,’mysql.default_ host’,’localhost’) config.set(‘MySQL’,’mysql.default_ port’,’3306’) config.set(‘MySQL’,’mysql.allow_ persistent’, ‘On’ ) config.set(‘MySQL’,’mysql.max_ persistent’,’20’) with open(‘php.ini’, ‘ap’) as configfile: config.write(configfile) Output:php.ini [MySQL] mysql.max_persistent = 20 mysql.allow_persistent = On mysql.default_port = 3306 mysql.default_host = localhost mysql.trace_mode = Off mysql.connect_timeout = 60 @code:parseconfig.py @description: Parsing and updatingthe config file import ConfigParser config = ConfigParser.ConfigParser() config.read(‘php.ini’) # Print config values print config.get(‘MySQL’,’mysql. System administration is an important part of our computing environment. It does not matter whether you are managing systems at your work our home. Linux, being a UNIX-based operating system, already has everything a system administrator needs, such as the world-class shells(notjustonebutmany,includingBash,csh, zsh etc), handy tools, and many other features which make the Linux system an administrator’s dream. So why do we need Python when Linux already has everything built-in? Being a dynamic scripting language, Python is very easy to read and learn. That’s just not us saying that, but many Linux distributions actually use Python in core administrative parts. For example, Red Hat (and Fedora) system setup tool Anaconda is written in Python (read this line again, got the snakeconnection?).Also,toolslikeGNUMailman, CompizConfig Settings Manager (CCSM) and hundreds of tiny GUI and non-GUI configuration tools are written using Python. Python does not limit you on the choice of user interface to follow –youcanbuildcommand-line,GUIandwebapps using Python. This way, it has got covered almost allthepossibleinterfaces. Here we will look into executing sysadmin- relatedtasksusingPython. Python-develPython development libraries, required for compiling third-party Python module setuptoolssetuptools allows you to download, build, install, upgrade, and uninstall Python packages with ease Resources Note This is written for the Python 2.X series, as it is still the most popular and default Python distribution across all the platforms (including all Linux distros, BSDsandMacOSX).
  • 103.
    The Python Book103 Work with Python default_host’) print config.get(‘MySQL’,’mysql. default_port’) config.remove_option(‘MySQL’,’mysql. trace_mode’) with open(‘php.ini’, ‘wb’) as configfile: config.write(configfile) ParsingJSONdata JSON (also known as JavaScript Object Notation) is a lightweight modern data- interchange format. JSON is an open standard under ECMA-262. It is a text format and is completely language-independent. JSON is also used as the configuration file format for modern applications such as Mozilla Firefox and Google Chrome. JSON is also very popular with modern web services such as Facebook, Twitter, Amazon EC2 etc. In this section we will use the Python module ‘simplejson’ to access Yahoo Search (using the Yahoo Web Services API), which outputs JSONdata. To use this section, you should have the following: 1.Pythonmodule:simplejson. Note: You can install Python modules using the command ‘easy_install module name’. This command assumes that you have a working internetconnection. 2. Yahoo App ID: The Yahoo App ID can be created from https://developer.apps.yahoo. com/dashboard/createKey.html. The Yahoo App ID will be generated on the next page. See thescreenshotbelowfordetails. QGeneratingtheYahooAppID simplejson is very easy to use. In the following example we will use the capability of mapping JSON data structures directly to Python data types. This gives us direct access to the JSON datawithoutdevelopinganyXMLparsingcode. JSONPYTHONDATAMAPPING JSON Python object dict array list string unicode number(int) int,long number(real) float TRUE TRUE FALSE FALSE null None For this section we will use the simplejson. load function, which allows us to deserialise a JSONobjectintoaPythonobject. @code:LUDSearch.py import simplejson, urllib APP_ID = ‘xxxxxxxx’ # Change this to your APP ID SEARCH_BASE = ‘http://search. yahooapis.com/WebSearchService/V1/ webSearch’ class YahooSearchError(Exception): pass def search(query, results=20, start=1, **kwargs): kwargs.update({ ‘appid’: APP_ID, ‘query’: query, ‘results’: results, ‘start’: start, ‘output’: ‘json’ }) url = SEARCH_BASE + ‘?’ + urllib.urlencode(kwargs) result = simplejson.load(urllib. urlopen(url)) if ‘Error’ in result: # An error occurred; raise an exception raise YahooSearchError, result[‘Error’] return result[‘ResultSet’] Let’s use the above code from the Python shell to see how it works. Change to the directory where you have saved the LUDYSearch.py and openaPythonshell. @code: Python Shell Output. Lines starting with‘’indicateinput execfile(“LUDYSearch.py”) results = search(‘Linux User and Developer’) results[‘totalResultsAvailable’] 123000000 results[‘totalResultsReturned’] 20 items = results[‘Result’] for Result in items: ... print Result[‘Title’],Result[‘Url’] ... Linux User http://www.linuxuser. co.uk/ Linux User and Developer - Wikipedia, the free encyclopedia http://en.wikipedia.org/wiki/Linux_ User_and_Developer Linux User amp;amp; Developer | Linux User http://www.linuxuser. co.uk/tag/linux-user-developer/ Gatheringsystem information One of the important jobs of a system administrator is gathering system information. In this section we will use the SIGAR (System Information Gatherer And Reporter) API to demonstrate how we can gather system information using Python. SIGAR is a very complete API and it can provide lot of information,includingthefollowing: 1. System memory, swap, CPU, load average, uptime,logins. 2. Per-process memory, CPU, credential info, state,arguments,environment,openfiles. 3.Filesystemdetectionandmetrics. 4. Network interface detection, configuration infoandmetrics. 5.TCPandUDPconnectiontables. 6.Networkroutetable. InstallingSIGAR The first step is to build and install SIGAR. SIGAR is hosted at GitHub, so make sure that you have Git installed in your system. Then perform the following steps to install SIGAR and its Pythonbindings: $ git clone git://github.com/ hyperic/sigar.git sigar.git $ cd sigar.git/bindings/python $ sudo python setup.py install Pythondoesn’t limityourchoice ofinterface
  • 104.
    104 The PythonBook Work with Python At the end you should see a output similar to thefollowing: Writing /usr/local/lib/python2.6/ dist-packages/pysigar-0.1.egg-info SIGAR is a very easy-to-use library and can be usedtogetinformationonalmosteveryaspectof asystem.Thenextexampleshowsyouhow. The following code shows the memory and the filesysteminformation @code:PySysInfo.py import os import sigar sg = sigar.open() mem = sg.mem() swap = sg.swap() fslist = sg.file_system_list() print “==========Memory Information==============” print “tTotaltUsedtFree” print “Mem:t”, (mem.total() / 1024), (mem.used() / 1024), (mem.free() / 1024) print “Swap:t”, (swap.total() / 1024), (swap.used() / 1024), (swap.free() / 1024) print “RAM:t”, mem.ram(), “MB” print “==========File System Information===============” def format_size(size): return sigar.format_size(size * 1024) print ‘FilesystemtSizetUsed tAvailtUse%tMounted ontTypen’ for fs in fslist: dir_name = fs.dir_name() usage = sg.file_system_ usage(dir_name) total = usage.total() used = total - usage.free() avail = usage.avail() pct = usage.use_percent() * 100 if pct == 0.0: pct = ‘-’ print fs.dev_name(), format_ size(total), format_size(used), format_size(avail), pct, dir_name, fs.sys_type_ name(), ‘/’, fs.type_name() @Output ==========Memory Information============== Total Used Free Mem: 8388608 6061884 2326724 Swap: 131072 16048 115024 RAM: 8192 MB ==========File System Information=============== Filesystem Size Used Avail Use% Mounted on Type /dev/disk0s2 300G 175G 124G 59.0 / hfs / local devfs 191K 191K 0 - /dev devfs / none AccessingSecureShell (SSH)services SSH (Secure Shell) is a modern replacement for an old remote shell system called Telnet. It allows data to be exchanged using a secure channel between two networked devices. System administrators frequently use SSH to administrate networked systems. In addition to providing remote shell, SSH is also used for secure file transfer (using SSH File Transfer Protocol, or SFTP) and remote X server forwarding (allows you to use SSH clients as X server). In this section we will learn how to use the SSH protocol from Python using a Python module called paramiko, which implements the SSH2 protocolforPython. paramiko can be installed using the following steps: $ git clone https://github.com/robey/ paramiko.git $ cd paramiko $ sudo python setup.py install To the core of paramiko is the SSHClient class. This class wraps L{Transport},L{Channel},andL{SFTPClient} to handle most of the aspects of SSH. You can use SSHClientas: client = SSHClient() client.load_system_host_keys() client.connect(‘some.host.com’) stdin, stdout, stderr = client.exec_ command(‘dir’) The following example demonstrates a full SSH clientwrittenusingtheparamikomodule. @code:PySSHClient.py import base64, getpass, os, socket, sys, socket, traceback import paramiko import interactive # setup logging paramiko.util.log_to_file(‘demo_simple. log’) # get hostname username = ‘’ if len(sys.argv) 1: hostname = sys.argv[1] if hostname.find(‘@’) = 0: username, hostname = hostname. Note If you are confused with the tab spacing of thecode,lookforthecodefilesonFileSilo. split(‘@’) else: hostname = raw_input(‘Hostname: ‘) if len(hostname) == 0: print ‘*** Hostname required.’ sys.exit(1) port = 22 if hostname.find(‘:’) = 0: hostname, portstr = hostname. split(‘:’) port = int(portstr) # get username if username == ‘’: default_username = getpass. getuser() username = raw_input(‘Username [%s]: ‘ % default_username) if len(username) == 0: username = default_username password = getpass.getpass(‘Password for %s@%s: ‘ % (username, hostname)) # now, connect and use paramiko Client to negotiate SSH2 across the connection try: client = paramiko.SSHClient() client.load_system_host_keys() client.set_missing_host_key_ policy(paramiko.WarningPolicy) print ‘*** Connecting...’ client.connect(hostname, port, username, password) chan = client.invoke_shell() print repr(client.get_transport()) print ‘*** SSH Server Connected! ***’ print interactive.interactive_ shell(chan) chan.close() client.close() except Exception, e: print ‘*** Caught exception: %s: %s’ % (e.__class__, e) traceback.print_exc() try: client.close() except: pass sys.exit(1) To run this code you will also need a custom Python class interactive.py which implements
  • 105.
    The Python Book105 Work with Python Administrators are comfortable with running raw scripts by hand, but end-users are not. So if you are writing a script that is supposed to be used by common users, it is a good idea to create a user- friendly interface on top of the script. This way end-users can run the scripts just like any other application. To demonstrate this, we will create a simple GRUB configuration tool which allows userstoselectdefaultbootentryandthetimeout. We will be creating a TUI (text user interface) application and will use the Python module ‘snack’ to facilitate this (not to be confused with thePythonaudiolibrary,tksnack). Thisappconsistsoftwofiles… grub.py: GRUB Config File (grub.conf) Parser (available on FileSilo). It implements two main functions, readBootDB() and writeBootFile(), which are responsible for reading and writing the GRUBconfigurationfile. grub_tui.py: Text user interface file for manipulating the GRUB configuration file using thefunctionsavailableingrub.py. @code:grub_tui.py import sys from snack import * from grub import (readBootDB, writeBootFile) def main(entry_ value=’1’,kernels=[]): try: (default_value, entry_ value, kernels)=readBootDB() except: print sys.stderr, (“Error reading /boot/grub/grub. conf.”) sys.exit(10) screen=SnackScreen() while True: g=GridForm(screen, (“Boot configuration”),1,5) if len(kernels)0 : li=Listbox(height=len(kernels), width=20, returnExit=1) for i, x in enumerate(kernels): li.append(x,i) g.add(li, 0, 0) li.setCurrent(default_value) bb = ButtonBar(screen, WritingauserinterfaceusingPython Learnhowtocreateauser-friendlyinterfaceusingPython (((“Ok”), “ok”), ((“Cancel”), “cancel”))) e=Entry(3, str(entry_ value)) l=Label((“Timeout (in seconds):”)) gg=Grid(2,1) gg.setField(l,0,0) gg.setField(e,1,0) g.add(Label(‘’),0,1) g.add(gg,0,2) g.add(Label(‘’),0,3) g.add(bb,0,4,growx=1) result = g.runOnce() if bb.buttonPressed(result) == ‘cancel’: screen.finish() sys.exit(0) else: entry_value = e.value() try : c = int(entry_ value) break except ValueError: continue writeBootFile(c, li.current()) screen.finish() if __name__== ‘__main__’: main() Start the tool using the sudo command (as it reads the grub. conf file) $ sudo grub_tui.py the interactive shell for the SSH session. Look for this file on FileSilo and copy it into the same folderwhereyouhavecreatedPySSHClient.py. @code_Output kunal@ubuntu-vm-kdeo:~/src/paramiko/ demos$ python demo_simple.py Hostname: 192.168.1.2 Username [kunal]: luduser Password for luduser@192.168.1.2: *** Connecting... paramiko.Transport at 0xb76201acL (cipher aes128-ctr, 128 bits) (active; 1 open channel(s)) *** SSH Server Connected! *** Last login: Thu Jan 13 02:01:06 2011 from 192.168.1.9 [~ $:] If the host key for the SSH server is not added to your $HOME/.ssh/known_hosts file, the clientwillthrowthefollowingerror: *** Caught exception: type ‘exceptions.TypeError’: unbound method missing_host_key() must be called with WarningPolicy instance as first argument (got SSHClient instance instead) This means that the client cannot verify the authenticity of the server you are connected to. To add the host key to known_hosts, you can use the ssh command. It is important to remember that this is not the ideal way to add the host key; instead you should use ssh- keygen. But for simplicity’s sake we are using thesshclient. kunal@ubuntu-vm-kdeo:~/.ssh$ ssh luduser@192.168.1.2 The authenticity of host ‘192.168.1.2 (192.168.1.2)’ can’t be established. RSA key fingerprint is be:01:76:6a:b 9:bb:69:64:e3:dc:37:00:a4:36:33:d1. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added ‘192.168.1.2’ (RSA) to the list of known hosts. So now you’ve seen how easy it can be to carry out the complex sysadmin tasks using Python’sversatilelanguage. As is the case with all Python coding, the code that is presented here can easily be adoptedintoyourGUIapplication(withsoftware such as PyGTK or PyQt) or a web application (usingaframeworksuchasDjangoorGrok).
  • 106.
    106 The PythonBook 108 Build tic-tac-toe with Kivy Program noughts and crosses 112 Create two-step authentication Use Twilio for safe authentication 116 Program a Space Invaders clone Make the basic Pivaders game 120 Add animation and sound Enhance your Pivaders game 124 Make a visual novel game Use Python to make a storytelling game 128 Pygame Zero Turn your ideas into games Create 124 108 112 “You’llbesurprisedbythe diversityofwhatyoucan makewithPython” Python with
  • 107.
  • 108.
    108 The PythonBook Create with Python EaseintotheworkingsofKivybycreatingthepen-and-paperclassic injustover100linesofPython... Buildtic-tac-toewithKivy Kivy is a highly cross-platform graphical framework for Python, designed for the creation of innovative user interfaces like multitouch apps. Its applications can run not onlyonthetraditionaldesktopplatformsofLinux, OSXandWindows,butalsoAndroidandiOS,plus devicesliketheRaspberryPi. That means you can develop cross-platform apps using Python libraries such as Requests, SQLAlchemy or even NumPy. You can even access native mobile APIs straight from Python using some of Kivy’s sister projects. Another great feature is the Cython-optimised OpenGL graphicspipeline,allowingadvancedGPUeffects eventhoughthebasicPythonAPIisverysimple. Kivy is a set of Python/Cython modules that can easily be installed via pip, but you’ll need a fewdependencies.ItusesPygameasarendering backend (though its API is not exposed), Cython for compilation of the speedy graphics compiler internals, and GStreamer for multimedia. These should all be available through your distro’s repositories,orviapipwhereapplicable. With these dependencies satisfied, you should be able install Kivy with the normal pip incantation. The current version is 1.8.0, and the same codebase supports both python2 and python3. The code in this tutorial is also version- agnostic,runninginpython2.7andpython3.3. pip install kivy If you have any problems with pip, you can use easy_instalvia easy_install kivy. There are also packages or repositories available for several popular distros. You can find more QTheclassic‘HelloWorld!’inKivyGUIform, usingthebuilt-inLabelwidget information on Kivy’s website. A kivy application is startedbyinstantiatingandrunningan‘App’class. This is what initialises our pp’s window, interfaces with the OS, and provides an entry point for the creation of our GUI. We can start by making the simplestKivyapppossible: from kivy.app import App class TicTacToeApp(App): pass if __name__ == “__main__”: TicTacToeApp().run() Youcanalreadyrunthis,yourappwillstartupand you’llgetaplainblackwindow.Exciting! We can build our own GUI out of Kivy widgets. Each is a simple graphics element with some specific behaviour of its own ranging from standard GUI functionality (eg the Button, Label or TextInput), to those that impose positioning on their child widgets (eg the BoxLayout, FloatLayout or GridLayout), to those abstracting a more involved task like interacting with hardware (eg the FileChooser, Camera or VideoPlayer). Most importantly, Kivy’s widgets are designed to be easily combined - rather than including a widget foreveryneedimaginable,widgetsarekeptsimple but are easy to join to invent new interfaces. We’ll seesomeofthatinthistutorial. Since ‘Hello World!’ is basically compulsory in any programming tutorial, let’s get it over with by usingasimple‘Label’widgettodisplaythetext: from kivy.uix.label import Label We’ll display the ‘Label’ by returning it as our app’s rootwidget.Everyapphasasinglerootwidget,the top level of its widget tree, and it will automatically be sized to fill the window. We’ll see later how to constructafullGUIbyaddingmorewidgetsforthis one, but for now it’s enough to set the root widget byaddinganewmethodtothe‘App’: def build(self): return Label(text=’Hello World!’ , font_size=100, color=0, 1, 0, 1)) # (r , g, b, a) The ‘build’ method is called when the ‘App’ is run, and whatever widget is returned automatically becomes the root widget of that App’. In our case that’s a Label, and we’ve set several properties - the ‘text’, ‘font_size’ and ‘color’. All widgets have different properties controlling aspects of their behaviour, which can be dynamically updated to alter their appearance later, though here we set themjustonceuponinstantiation. Note that these properties are not just Python attributes but instead Kivy properties. These are accessed like normal attributes but provide extra functionality by hooking into Kivy’s event system. We’ll see examples of creating properties shortly, andyoushoulddothesameifyouwanttouseyour variableswith Kivy’seventorbindingfunctionality. That’s all you need to show some simple text, so run the program again to check that this does work.Youcanexperimentwiththeparametersifit’s unclearwhatanyofthemaredoing. Ourownwidget:tic-tac-toe Since Kivy doesn’t have a tic-tac-toe widget, we’ll have to make our own! It’s natural to create a new widgetclasstocontainthisbehaviour: from kivy.uix.gridlayout import GridLayout class TicTacToeGrid(GridLayout): pass Now this obviously doesn’t do anything yet, except that it inherits all the behaviour of the Kivy GridLayout widget - that is, we’ll need to tell it how many columns to have, but then it will Youcandevelop cross-platform appsusingvarious Pythonlibraries
  • 109.
    The Python Book109 Create with Python QAtic-tac-toegridnowacceptinginput,addingaOorXalternately the former, creating a rule for the ‘TicTacToeGrid’ widget by declaring that every ‘TicTacToeGrid’ instantiatedshouldhaveits‘cols’propertysetto3. We’ll use some more kv language features later, but for now let’s go back to Python to create the buttonsthatwillbetheentriesinourtic-tac-toegrid. from kivy.uix.button import Button from kivy.properties import ListProperty class GridEntry(Button): coords = ListProperty([0, 0]) This inherits from Kivy’s ‘Button’ widget, which interacts with mouse or touch input, dispatching events when interactions toggle it. We can hook into these events to call our own functions when a user presses the button, and can set the button’s ‘text’ property to display the ‘X’ or ‘O’. We also createdanewKivypropertyforourwidget,‘coords’ – we’ll show how this is useful later on. It’s almost identical to making a normal Python attribute by writing‘self.coords=[0,0]’in‘GridEntry.__init__’. As with the ‘TicTacToeGrid’, we’ll style our new class with kv language, but this time we get to see amoreinterestingfeature. GridEntry: font_size: self .height As before, this syntax defines a rule for how a ‘GridEntry’ widget should be constructed, this time setting the ‘font_size’ property that controls the size of the text in the button’s label. The extra magic is that kv language automatically detects thatwe’vereferencedtheButton’sownheightand will create a binding to update this relationship – when a ‘GridEntry’ widget’s height changes, its ‘font_size’ will change so the text fits perfectly. We could have made these bindings straight from Python (another usage of the ‘bind’ method used later on), but that’s rarely as convenient as referencingthepropertywewanttobindto. Let’s now populate our ‘TicTacToeGrid’ with ‘GridEntry’ widgets (Fig.01). This introduces a few new concepts: When we instantiated our ‘GridEntry’ widgets, we were able to set their ‘coords’ property by simply passing it in as a kwarg.Thisisaminorfeaturethatisautomatically handledbyKivyproperties. We used the ‘bind’ method to call the grid’s ‘button_pressed’ method whenever the `GridEntry` widget dispatches an ‘on_release’ event. This is automatically handled by its ‘Button’ superclass, and will occur whenever a user presses, then releases a ‘GridEntry’ button. We could also bind to ‘on_press’, which is dispatched when the button is first clicked, or to any Kivy property of the button, which is dispatched dynamically wheneverthepropertyismodified. We added each ‘GridEntry’ widget to our ‘Grid’ via the ‘add_widget’ method. That means each one is a child widget of the ‘TicTacToeGrid’, and so it will display them and knows it should automatically arrange them into a grid with the numberofcolumnswesetearlier. Nowallwehavetodoisreplaceourrootwidget (returned from ‘App.build’) with a ‘TicTacToeGrid’ andwecanseewhatourapplookslike. automatically arrange any child widgets to fit nicelywithasmanyrowsasnecessary.Tic-tac-toe requiresthreecolumnsandninechildren. Here we introduce the Kivy language (kv), a special domain-specific language for making rules describing Kivy widget trees. It’s very simple but removes a lot of necessary boilerplate for manipulatingtheGUIwithPythoncode-asaloose analogy you might think of it as the HTML/CSS to Python’s JavaScript. Python gives us the dynamic powertodoanything,butallthatpowergetsinthe way if we just want to declare the basic structure of our GUI. Note that you never need kv language, youcanalwaysdothesamethinginPythonalone, but the rest of the example may show why Kivy programmersusuallyliketousekv. Kivy comes with all the tools needed to use kv language;thesimplestwayistowriteitinafilewith anamebasedonourAppclass.Thatis,weshould placethefollowinginafilenamed‘tictactoe.kv’: TicTacToeGrid: cols: 3 # Number of columns This is the basic syntax of kv language; for each widget type we may write a rule defining its behaviour, including setting its properties and adding child widgets. This example demonstrates Kivycomes withallthetools neededtousekv language
  • 110.
    110 The PythonBook Create with Python def build(self): return TicTacToeGrid() # Replaces the previous label With this complete you can run your main Python file again and enjoy your new program. All being well, the single Label is replaced by a grid of nine buttons, each of which you can click (it will automatically change colour) and release (you’ll see the printed output information from our binding). We could customise the appearance by modifying other properties of the Button, but for nowwe’llleavethemastheyare. Hasanyonewonyet? We’llwanttokeeptrackofthestateoftheboardto check if anyone has won, which we can do with a couplemoreKivyproperties: from kivy.properties import (ListProperty, NumericProperty) class TicTacToeGrid(GridLayout): status = ListProperty([0, 0, 0, 0, 0, 0, 0, 0, 0]) current_player = NumericProperty(1) This adds an internal status list representing who has played where, and a number to represent the current player (1 for ‘O’, -1 for ‘X’). By placing these numbers in our status list, we’ll know if somebody winsbecausethesumofarow,columnordiagonal will be +-3. Now we can update our graphical grid whenamoveisplayed(Fig.02). You can run your app again to see exactly what this did, and you’ll find that clicking each button now places an ‘O’ or ‘X’ as well as a coloured background depending on whose turn it is to play. Not only that, but you can only play one move in each button thanks to our status arraykeepingtrackofexistingmoves. This is enough to play the game but there’s one vital element missing... a big pop-up telling you when you’ve won! Before we can do that, we need toaddsomecodetocheckifthegameisover. Kivy properties have another useful feature here, whenever they change they automatically call an ‘on_propertyname’ method if it exists and dispatch a corresponding event in Kivy’s event system. That makes it very easy to write code that will run when a property changes, both in Python and kv language. In our case we can use it to check the status list every time it is updated, doing something special if a player hasfilledacolumn,rowordiagonal. def on_status(self , instance, new_value): status = new_value # Sum each row, column and diagonal. # Could be shorter , but let’s be extra # clear what’s going on sums = [sum(status[0:3]), # rows sum(status[3:6]), sum(status[6:9]), sum(status[0: :3]), # columns sum(status[1: :3]), sum(status[2: :3]), sum(status[ : :4]), # diagonals sum(status[2:-2:2])] # Sums can only be +-3 if one player # filled the whole line if 3 in sums: print(‘Os win!’) elif -3 in sums: print(‘Xs win!’) elif 0 not in self .status: # Grid full print(‘Draw!’) This covers the basic detection of a won or drawn board, but itonlyprintsthe result tostdout. At this stage we probably want to reset the board so that the players can try again, along with displaying a graphicalindicatoroftheresult(Fig.03). Finally, we can modify the `on_status` method to both reset the board and display the winner ina‘ModalView’widget. from kivy.uix.modalview import ModalView This is a pop-up widget that draws itself on top of everything else rather than as part of the normal widget tree. It also automatically closes when the userclicksortapsoutsideit. winner = None if -3 in sums: winner = ‘Xs win!’ elif 3 in sums: winner = ‘Os win!’ elif 0 not in self .status: winner = ‘Draw. . .nobody wins!’ if winner: popup = ModalView(size_hint=0.75, 0.5)) victory_label = Label(text=winner , QThegamewithfinaladditions,makingthegridsquareandextendingtheinterface
  • 111.
    The Python Book111 Create with Python Tryswappingoutthedifferentwidget typestoseehowotherwidgetsbehave font_size=50) popup.add_widget(victory_label) popup.bind(on_dismiss=self .reset) popup.open() This mostly uses the same ideas we already covered, adding the ‘Label’ widget to the ‘ModalView’ then letting the ‘ModalView’ take care of drawing itself and its children on top of everything else. We also use another binding; this time to ‘on_dismiss’, which is an event dispatched by the ‘ModalView’ when it is closed. Finally, we made use of the ‘size_hint’ property common to all widgets, which in this case is used to set the ‘ModalView’ size proportional to the window – while a ‘ModalView’ is open you can resize the window to see it dynamically resize, always maintainingtheseproportions.Thisisanothertrick madepossiblebyabindingwiththe‘size_hint’Kivy property,thistimemanagedinternallybyKivy. That’s it, a finished program! We can now not only play tic-tac-toe, but our program automatically tells us when somebody has won, and resets the board so we can play again. Simply runyourprogramandenjoyhoursoffun! Timetoexperiment This has been a quick tour through some of Kivy’s features, but hopefully it demonstrates how to think about building a Kivy application. Our programs are built from individual Kivy widgets, interacting by having Python code run when their properties change (eg our ‘on_status’ method) or when they dispatch events (eg ‘Button’ ‘on_ release’). We also briefly saw kv language and experienced how it can automatically create bindingsbetweenproperties. You can find a copy of the full program on FileSilo, which you can reference to check you’ve followed everything correctly. We’ve also added an extra widget, the ‘Interface’, with a structure coded entirely in kv language that demonstrates how to add child widgets this way. You can test it by uncommenting the ‘return Interface()’ line in ‘TicTacToeGrid.build’. It doesn’t do anything fundamentally different to what we already covered, but it does make extensive use of kv language’s binding ability to automatically update a label showing the current player, and to resize the TicTacToeGrid so that it is always square to fit within its parent. You can play with all these settings to see exactly how it fits together, or try thingslikeswappingoutthedifferentwidgettypes toseehowotherwidgetsbehave. class TicTacToeGrid(GridLayout): def __init__(self , *args, **kwargs): super(TicTacToeGrid, self).__init__(*args, **kwargs) for row in range(3): for column in range(3): grid_entry = GridEntry( coords=(row, column)) grid_entry.bind(on_release=self .button_pressed) self .add_widget(grid_entry) def button_pressed(self , instance): # Print output just to see what’s going on print(‘{} button clicked!’ .format(instance.coords)) Fig 01 # Note the *args parameter! It’s important later when we make a binding # to reset, which automatically passes an argument that we don’t care about def reset(self , *args): self .status = [0 for _ in range(9)] # self .children is a list containing all child widgets for child in self .children: child.text = ‘’ child.background_color = (1, 1, 1, 1) self .current_player = 1 Fig 03 def button_pressed(self , button): # Create player symbol and colour lookups player = {1: ‘O’ , -1: ‘X’} colours = {1: (1, 0, 0, 1), -1: (0, 1, 0, 1)} # (r , g, b, a) row, column = button.coords # The pressed button is automatically # passed as an argument # Convert 2D grid coordinates to 1D status index status_index = 3*row + column already_played = self .status[status_index] # If nobody has played here yet, make a new move if not already_played: self .status[status_index] = self .current_player button.text = {1: ‘O’ , -1: ‘X’}[self .current_player] button.background_color = colours[self .current_player] self .current_player *= -1 # Switch current player Fig 02 Codeon FileSilo
  • 112.
    112 The PythonBook Create with Python 02Addcredit Just like a mobile phone operator, Twilio is not a free service – although it is very inexpensive. In order to continue, we’ll need to add a card and some funds to our newly created Twilio account. On the main page of the dashboard, you’ll see a big blue dialog asking to upgrade your trial account; click through and follow the instructions to add a card and the amount of credit you would like to use. The minimum amount of $20 (around £10 GBP) will be more than plenty for this and other projects. Once that’s done, you’re almost ready to start sending text messages – but first head back over to the Twilio dashboard and copy your account SID and auth token down somewhere, you’llneedthosealittlelater. Increasesecurityinaccessto your webservices by buildinga simpletwo-stepauthentication withTwilio’sSMSAPIsto helpyou Resources Python2.7+ Flask0.10.0: flask.pocoo.org/ FlaskGithub: github.com/mitsuhiko/flask ATwilioaccount: twilio.com Twilio’sPythonREST APIHelperLibrary: github.com/twilio/twilio-python/zipball/master MySQLDB: mysql-python.sourceforge.net Createatwo-step authenticationwithTwilio 01 Get a Twilio account and phone number Signing up to Twilio is pretty easy. First, head over to http://twilio.com and click the ‘Signup’ button. At this point, the sign-up process doesn’t really differ from any other service, but after you’ve entered an email address and password you’ll be asked for a phone number. Given the nature of Twilio’s API, it makes sense for them to ask whether we’re human, and having them text us is a good way to confirm that. Hey, it’s a two-step authentication, which is exactly what we’re working towards. You can enter any number you have access to, be it a landline or mobile, to confirm who you are, but at this point we suggest you authenticate using a phone that can accept SMS (instead of a landline). Having entered your number, you’ll receive a text to authenticate your phone – enter it and you’ll be presented with a Twilio phone number. This is your Twilio phone number and you’ll be using it to send and receive our authentication texts. Telephony is one of the most versatile technologies in our households. Despite being invented over 100 years ago, we still use the same basic infrastructure that once only carried the voices of people to deliver a rich multitude of media content at incredible speeds. As is often the case with wonderful things, they can often be complextoo–andyetphonesaremoreimportant now to our daily lives than ever. So, what can we dotoleveragesomeofthatversatiletechnology? Well, for starters we can use an API. Twilio has created a RESTful API that removes a great deal of that complexity of telephony so that we can write apps and services that are able to deliver and receive both phone calls and SMS using various endpoints and services. Neat! In this tutorial, we’re going to look at using Twilio to help us create the basic flow for a two-step authentication system for logging into a service. We’re also going to be using Flask to help us create our routes and generate our pages, but littleofFlask’sdetailwillbecoveredhere. QTheTwiliointerfaceiskeptniceandsimple–nounnecessarycomplicationshere
  • 113.
    The Python Book113 Create with Python import MySQLdb from flask import Flask, redirect, request, session, render_template from twilio.rest import TwilioRestClient as twilio import string, random, time db = MySQLdb.connect(host=127.0.0.1, user=SQLUSER, passwd=SQLPASS, db=two-step, port=3306) expirationLength = 300 account_sid = YOUR ACCOUNT SID auth_token = YOUR ACCOUNT AUTH TOKEN client = twilio(account_sid, auth_token) @app.route('/') def index(): return index page @app.route('/login', methods=['GET']) def login(): return login page @app.route('/check-user', methods=['POST']) def checkUser(): return check user page @app.route('/logout') def logout(): return logout page @app.route('/verify', methods=['GET']) def twoStep(): return verify page @app.route('/check-code', methods=['POST']) def checkCode(): return check code page if __name__ == '__main__': app.secret_key = 'R4nDOMCRypt0gr4ph1cK3yf0R5355i0N' app.run(host='0.0.0.0', debug=True) Fig 01 03InstalltheTwilioHelperLibrary andMySQLDB The Twilio helper library is a fantastic piece of codethatletsyoujumpstraightintosendingand handling text messages in no time at all. There areacoupleofwaystoinstallthelibrary:youcan useeitherPIPorEasy_Install,likeso $ pip install twilio $ easy_install twilio Or you can download the source code for the helper library and run the ‘setup.py’ file. It really is as simple as that. Now, for storing the verification tokens we’re going to use a MySQL database. To get Python talking to our SQL server, we’ll use the Python module MySQLDB, thepackageforwhichyoucangrablikeso… apt-get install python-mysqldb In the tutorial resources we have included an SQL dump with the table structure. Import it into a database of your choosing. Assuming everything so far has gone swimmingly, you can create a new project folder/environment and addanewfile‘server.py’. 04Serversetup Open the ‘server.py’ file for editing. The firstthingwe'regoingtodoisimportthelibraries we need for our authentication flow, create the endpoints for our server and assign some of the variablesneededtorunourFlaskserver.(Fig 01) You may have noticed the account_sid and auth_token variable we’ve set after the import statements. We’ll use these with our Twilio client so we can interact with Twilio and our mobile phones. These settings can be found on the Twilio account dashboard, right below the header. We’ve also connected to our SQL database, so make sure your SQL server is running before you fire up the app, otherwise you’ll have an error thrown. Save, now if you run your ‘server.py’ file, you should be able to access theindexpageofyourserverat127.0.0.1:5000/. 05Serverlogic If you’ve hit all of your server endpoints already, so far all you will see are the strings we returned at the end of endpoint declarations. These are not all that good-looking, so let’s add some Flask templates to pretty things up a little. The focus of this tutorial is not on the intricacies of Flask and as such, included on the DVD is a folder called ‘templates’ and another called ‘static’; copy them both to the root of your current project folder and amend your endpoints as in Fig 02. If you revisit the pages again, things might seem a little out of whack at the moment, but don’t worry about that for the time being. It’s mostly because we’ve not put together the server logic to help the templates figure out what to do. Let’s deal with the ‘/’ path first. All we’re doing here is checking the state of our session cookies and effecting how the index.html page renders according to that state. If the user isn’t logged in, we’ll give them a link to the login page, if the user is logged in but hasn’t verified, then we’ll give them a link to the code verification page. Before we deliver the template we need to check that our session has its particular variables set, otherwise we’ll end up getting KeyErrors. @app.route(‘/’) def index(): checkSessionState() return render_template(‘index. html’) def checkSessionState():
  • 114.
    114 The PythonBook Create with Python try: session[‘verified’] == True except KeyError: session[‘verified’] = ’’ try: session[‘loggedin’] == True except KeyError: session[‘loggedin’] = ’’ try: session[‘user’] == True except KeyError: session[‘user’] = ’’ result = cur.fetchone() returnedPassword = result[2] returnedPhoneNumber = result[3] We can then build an SQL statement using cur. execute().Noticethe%s;thiswillbereplacedwith the value passed through in the next variable. We execute the statement with cur.fetchone(), which will get us the first row that the query returns – if there is no result we’ll get None and we can then return the user to the login page with an error message. Let’s assume we’ve requested a valid user–we’llnextcheckthatthepasswordassigned tothatuseristhesameastheonesubmitted.Ifso, we’ll generate the validation code to send to the user, which we’ll store in the verification table of ourdatabaseuntilit’susedorexpires.We’llneedto createanewcursortoinserttheverificationcode intoourtable.Afterwe’veexecutedthestatement we need to commit the changes to the database, wedothiswithdb.commit() –we’llthenaddthe resultsofthequerytooursessionsowecancheck againstthemlater.(Fig03) that will make up the body of our message, the number that we want to send it to and the number that we want to send it from. When inputting the number that we want to send it to, it’s best to use the +CountryCode type of phone number to avoid any ambiguity about the intended recipient. The number that we’re sending from is our Twilio number; you can use any Twilio number you have assigned to your account, so long as it has credit. As soon as we execute that code, the message will be sent and your SMS will go straight through to the telephone. The SID is the unique identifier for the message/call sent; receiving it means the message has been executed successfully. After that, we can redirect our user to the verification page with return redirect(‘/ verify’) at the end of /check-user. 06Loggingin The first step in two-step authentication is logging in with a traditional username/email and password. Access your database and create anewuserwiththefollowingquery: INSERT INTO users (username, password, phonenumber) VALUES (‘A USERNAME', ‘A PASSWORD', ‘+44YOURUSERSPHONENUMBER') For the purposes of this tutorial, the password is plain text – but we implore you, when you’re implementing passwords in a live environment, makesurethatyouhashthem.Still,fornowwe’re goingtouseaplaintextpassword.Ourlogin.html template has a form that’s going to POST itself to check-user; here we'll check the validity of the credentials and then trigger the verification if needed. So we’re going to use the MySQLDB moduletogetdetailsfromourdatabase. In order to query our database we need to create a cursor from which to execute our MySQL statements.Wedothiswithcur = db.cursor(): @app.route(‘/check-user', methods=[‘POST']) def checkUser(): #session.clear() if request.method == ‘POST': #print request.form['username'] cur = db.cursor() cur.execute(SELECT * FROM users WHERE username = %s, (request.form[‘username'],)) 07Sendtheverificationcode Now that we believe we’re dealing with a valid user, it’s time for the second step of our two-step process. On the line after where we stored a variable in our session, we make a call to sendVerificationCode (VERIFICATION CODE, USER PHONE NUMBER) and pass through the code we want to send to our user and the user’s phone number. So what does that function actually look like? It must be big, long and complicated because it deals with the telecommunications network, right? Wrong. It’s actually incredibly simple to send an SMS with Twilio. In fact, part of the inherent beauty of Twilio lies in its simplicity. To send a text, all we have to do is: def sendVerificationCode(code, number): text = client.messages.create( body=“Your verification code is: + code, to=number, from_=“+YOURTWILIONUMBER ) return text.sid Using the client variable we used to instantiate the Twilio REST module, we can access the messages class and execute the create method. All we need to pass through is the text 08Checkverificationcode At this point the user will have received a text message with something along the lines of ‘Your verification code is: 12cd56’ and will be presented with the verification page. If, at this point, they choose to browse around our site, they won’t be able to access anything that we don't want them to. Still, we’ll know that they’ve logged in, so if they head back to the verification page, we can just let them input their code. Once they submit their code, it will be sent to the /check-code endpoint. Just like before when we checked for our user’s validity, we’re going to attempt to retrieve the verification code and check it. (Fig 04) First we’re simply going to retrieve the code and check the user it has assigned to it. If that user assigned to the code matches the user in our session, then we can be certain that the right person is logging in with the right code – if not we can redirect them accordingly. Assuming the code is correct, we need to check it’s still valid. Back in Step 6, we created an expiration time that was five minutes in the future from when the code was generated. If it’s been more than five minutes (or whatever time you’ve set on it) then we’re going to consider it invalid, delete the code from our table and then log out our user so they can start over, like so. elif time.time() expirationTime: expirySQL = db.cursor() expirySQL.execute(DELETE FROM verification WHERE code=%s, (codeToCheck,))
  • 115.
    The Python Book115 Create with Python expirySQL.close() session['loggedin'] == False return redirect('/logout') If we manage to pass the tests so far, then we’ve two-step verified our user – hooray! Surprisingly easy, eh? Before we give our user free reign around our service, we still want to get rid of that token – we don’t need it any more and we don’t want to risk someone else using it maliciously in the future. else: delSql = db.cursor() delSql.execute(DELETE FROM verification WHERE code=%s, (codeToCheck,)) delSql.close() db.commit() session['verified'] = True return redirect('/') else: return redirect('/ verify?error=true') And that’s it! Now we redirect our user to wherever we want them to be at the end of the process. In this instance we’re sending them back to our index page, which will render a success message and give the user a link to log out whenever they like – but they could be redirectedtotheiruserpage,andsoon. 09Conclusion In every web-based service, security is king. Users entrust more and more personal data and trends to services every day and it’s the responsibility of those services to maintain the privacy of that data as best they can. It’s no wonder that services such as Amazon, Google and Facebook have all implemented two- step verification across their services. With two-step authentication, a user can tie their account to one of the most personal things they own: their phone. With services like Twilio and some simple code, they contain people’s keys – or at least a part of them. @app.route('/') def index(): return render_template('index.html') @app.route('/login', methods=['GET']) def login(): return render_template('login.html') @app.route('/check-user', methods=['POST']) def checkUser(): return check user page @app.route('/logout') def logout(): return logout page @app.route('/verify', methods=['GET']) def twoStep(): return render_template('verify.html') @app.route('/check-code', methods=['POST']) def checkCode(): return check code page Fig 02 verficationCode = generateVerificationCode(size=6) ins = db.cursor() expiration = int(time.time() + expirationLength) sql = INSERT INTO verification (code, expiration, username) VALUES ('%s', '%s', '%s') % (verficationCode, expiration, request.form['username']) ins.execute(sql) ins.close() db.commit() session['user'] = request.form['username'] session['loggedin'] = True Fig 03 @app.route('/check-code', methods=['POST']) def checkCode(): if request.method == 'POST': codeToCheck = request.form['code'] if not 'user' in session: return redirect('/login') else: cur = db.cursor() cur.execute(SELECT * FROM verification WHERE code = %s, (codeToCheck,)) result = cur.fetchone() cur.close() if result != None: returnedUser = result[3] expirationTime = int(result[2]) if returnedUser != session['user']: return redirect('/verify?error=true') Fig 04
  • 116.
    116 The PythonBook Create with Python WriteyourownRasPishooterin300linesofPython Partone:Programa SpaceInvadersclone meander their way down the screen towards you, it’s your job to pick them off while dodging their random fire. When one wave is conquered, another faster, more aggressive wave appears. We’ve tried to use many features of Pygame, which is designed to make the creation of games and interactive applications easier. We’ve extensively used the Sprite class, which saves dozens of lines of extra code in making collision detection simple and updating the screen and its many actors a single-line command. We hope you agree that this is an exciting game to play and a great tool to learn more about Python and Pygame, but our sensory system is far from overloaded here. Don’t worry, as that will be covered in the next tutorial, adding animation and sound effects to our game to give it the spit and polish any self-respecting Space Invaders- inspired shooter demands… 01Settingupdependencies If you’re looking to get a better understanding of programming games with Python and Pygame, we strongly recommend you copy the Pivaders code in this tutorial into your ownprogram.It’sgreatpracticeandgivesyouachancetotweak elements of the game to suit you, be it a different ship image, changingthedifficultyorthewaysthealienwavesbehave.Ifyou just want to play the game, that’s easily achieved too, though. Either way, the game’s only dependency is Pygame, which (if it isn’talready)canbeinstalledfromtheterminalbytyping: sudo apt-get install python-pygame 02Downloadingtheproject For Pivaders we’ve used Git, a brilliant form of version control used to safely store the game files and retain historical versionsofyourcode.GitshouldalreadybeinstalledonyourPi;if not,youcanacquireitbytyping: sudo apt-get install git As well as acting as caretaker for your code, Git enables you to clone copies of other people’s projects so you can work on them, or just use them. To clone Pivaders, go to your home folder in the terminal (cd ~), make a directory for the project (mkdir pivaders),enterthedirectory(cd pivaders)andtype: git pull https://github.com/russb78/pivaders.git When you’re learning to program in a new language or trying to master a new module, experimenting with a familiar and relatively simply project is a very useful exercise to help expand your understanding of the tools you’re using. Our Space Invaders clone is one such example that lends itself perfectly to Python and the Pygame module – it’s a simple game with almost universally understood rules and logic. While the Invaders Resources Raspbian:www.raspberrypi.org/ downloads Python:www.python.org/doc Pygame:www.pygame.org/docs
  • 117.
    The Python Book117 Create with Python #!/usr/bin/env python2 import pygame, random BLACK = (0, 0, 0) BLUE = (0, 0, 255) WHITE = (255, 255, 255) RED = (255, 0, 0) ALIEN_SIZE = (30, 40) ALIEN_SPACER = 20 BARRIER_ROW = 10 BARRIER_COLUMN = 4 BULLET_SIZE = (5, 10) MISSILE_SIZE = (5, 5) BLOCK_SIZE = (10, 10) RES = (800, 600) class Player(pygame.sprite.Sprite): def __init__(self): pygame.sprite.Sprite.__init__(self) self.size = (60, 55) self.rect = self.image.get_rect() self.rect.x = (RES[0] / 2) - (self.size[0] / 2) self.rect.y = 520 self.travel = 7 self.speed = 350 self.time = pygame.time.get_ticks() def update(self): self.rect.x += GameState.vector * self.travel if self.rect.x 0: self.rect.x = 0 elif self.rect.x RES[0] - self.size[0]: self.rect.x = RES[0] - self.size[0] class Alien(pygame.sprite.Sprite): def __init__(self): pygame.sprite.Sprite.__init__(self) self.size = (ALIEN_SIZE) self.rect = self.image.get_rect() self.has_moved = [0, 0] self.vector = [1, 1] self.travel = [(ALIEN_SIZE[0] - 7), ALIEN_SPACER] self.speed = 700 self.time = pygame.time.get_ticks() def update(self): if GameState.alien_time - self.time self.speed: if self.has_moved[0] 12: self.rect.x += self.vector[0] * self.travel[0] self.has_moved[0] +=1 else: if not self.has_moved[1]: self.rect.y += self.vector[1] * self.travel[1] self.vector[0] *= -1 self.has_moved = [0, 0] self.speed -= 20 if self.speed = 100: self.speed = 100 self.time = GameState.alien_time class Ammo(pygame.sprite.Sprite): def __init__(self, color, (width, height)): pygame.sprite.Sprite.__init__(self) self.image = pygame.Surface([width, height]) self.image.fill(color) self.rect = self.image.get_rect() self.speed = 0 self.vector = 0 def update(self): self.rect.y += self.vector * self.speed if self.rect.y 0 or self.rect.y RES[1]: self.kill() class Block(pygame.sprite.Sprite): def __init__(self, color, (width, height)): pygame.sprite.Sprite.__init__(self) self.image = pygame.Surface([width, height]) self.image.fill(color) self.rect = self.image.get_rect() class GameState: pass class Game(object): def __init__(self): pygame.init() pygame.font.init() self.clock = pygame.time.Clock() self.game_font = pygame.font.Font( ‘data/Orbitracer.ttf’, 28) self.intro_font = pygame.font.Font( ‘data/Orbitracer.ttf’, 72) self.screen = pygame.display.set_mode([RES[0], RES[1]]) self.time = pygame.time.get_ticks() self.refresh_rate = 20 self.rounds_won = 0 self.level_up = 50 self.score = 0 self.lives = 2 self.player_group = pygame.sprite.Group() self.alien_group = pygame.sprite.Group() self.bullet_group = pygame.sprite.Group() self.missile_group = pygame.sprite.Group() self.barrier_group = pygame.sprite.Group() self.all_sprite_list = pygame.sprite.Group() self.intro_screen = pygame.image.load( ‘data/start_screen.jpg’).convert() self.background = pygame.image.load( ‘data/Space-Background.jpg’).convert() pygame.display.set_caption(‘Pivaders - ESC to exit’) pygame.mouse.set_visible(False) Player.image = pygame.image.load( ‘data/ship.png’).convert() Player.image.set_colorkey(BLACK) Alien.image = pygame.image.load( ‘data/Spaceship16.png’).convert() Alien.image.set_colorkey(WHITE) GameState.end_game = False GameState.start_screen = True GameState.vector = 0 GameState.shoot_bullet = False def control(self): for event in pygame.event.get(): if event.type == pygame.QUIT: GameState.start_screen = False GameState.end_game = True if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE: if GameState.start_screen: GameState.start_screen = False GameState.end_game = True self.kill_all() else: GameState.start_screen = True self.keys = pygame.key.get_pressed() if self.keys[pygame.K_LEFT]: GameState.vector = -1 elif self.keys[pygame.K_RIGHT]: GameState.vector = 1 else: GameState.vector = 0 if self.keys[pygame.K_SPACE]: if GameState.start_screen: GameState.start_screen = False self.lives = 2 self.score = 0 self.make_player() self.make_defenses() self.alien_wave(0) else: GameState.shoot_bullet = True def splash_screen(self): while GameState.start_screen: self.kill_all() self.screen.blit(self.intro_screen, [0, 0]) self.screen.blit(self.intro_font.render( “PIVADERS”, 1, WHITE), (265, 120)) self.screen.blit(self.game_font.render( “PRESS SPACE TO PLAY”, 1, WHITE), (274, 191)) GroupsThis longlistofgroups we’recreatingare essentiallysets.Each timewecreateone oftheseitems,it’s addedtothesetso itcanbetestedfor collisionsanddrawn withease. Cleanclode Havingallthemost regularlyused globalvariables clearlylabelled heremakesour codelateroneasier toread.Also,ifwe wanttochangethe sizeofsomething, weonlyneedtodo ithereanditwill workeverywhere. ControlTaking careofkeyboard inputisthecontrol method.Itchecks forkeyeventsand actsaccordingly dependingwhether we’reonthestart screenorplaying thegame. Rainbullets TheAmmoclassis shortandsweet. Weonlyneedafew initialisingattributes andtheupdate methodchecksifit’s stillonthescreen.If not,it’sdestroyed. Get thecode: bit.ly/ 11k5f2x
  • 118.
    118 The PythonBook Create with Python 04Creatingyourownclone Once you’ve racked up a good high score (anything over 2,000 points is respectable) and got to know our simple implementation, you’ll get more from following along with and exploring the code and our brief explanations of what’s going on. For those who want to make their own project, create a new project folder and use either IDLE or Leafpad (or perhaps install Geany) to create and save a.pyfileofyourown. 05Globalvariablestuples Once we’ve imported the modules we need for the project, there’s quite a long list of variables in block capitals. The capitals denote that these variables are constants (or global variables). These are important numbers thatneverchange–theyrepresentthings referred to regularly in the code, like colours, block sizes and resolution. You’ll also notice that colours and sizes hold multiple numbers in braces – these are tuples. You could use square brackets (to make them lists), but we use tuples here since they’re immutable, which means 06Classes–part1 A class is essentially a blueprint for an object you’d like to make. In the case of our player, it contains all the required info, from which you can make multiple copies (we create a player instance in the make_player() method halfway through the project). The great thing about the classes in Pivaders is that they inherit lots of capabilities and shortcuts from Pygame’s Sprite class, as denoted by the pygame.sprite.Sprite found within the braces of the first line of the class. You can read the docs to learn more about the Sprite class via www.pygame.org/docs/ref/sprite.html. 07Classes–part2 In Pivader’s classes, besides creating the required attributes – these are simply variables in classes – for the object (be it a player, an alien, some ammo or a block), you’ll also notice all the classes have an update() method apart from the Block class (a method is a function within a class). The update() method is called in every loop through the main game (we’ve called ours main_loop()) and simply asks the iteration of the class we’ve created to move. In the case of a bullet from the Ammo class, we’re asking it to move down the screen. If it goes off either the top or bottom of the screen, we destroy it (since we don’t need it any more). 08Ammo What’s most interesting about classes, though, is that you can use one class to create lots of different things. 09Thegame Our final class is called Game. This is where all the main functionality of the game itself comes in, but remember, so far this is still just a list of ingredients – nothing can actually happen until a ‘Game’ object is created (right at the bottom of the code). The Game class is where the central mass of the game resides, so we initialise Pygame, set the imagery for our protagonist and extraterrestrial antagonist and create some GameState attributes that we use to control key aspects of external classes, like changing the player’s vector (direction) and deciding if we needtoreturntothestartscreen,amongotherthings. 10Themainloop There are a lot of methods (class functions) in the Game class, and each is designed to control a particular aspect of eithersettingupthegameorthegameplayitself.Theactuallogic that dictates what happens within any one round of the game is actuallycontainedinthemain_loop()methodrightatthebottom of the pivaders.py script and is the key to unlocking exactly what variables and functions you need for your game. Starting at the top of main_loop() and working line-by-line down to its last line, you can see exactly what’s being evaluated 20 times every secondwhenyou’replayingthegame. 12Mainloopkeylogic–part2 Once collisions have been calculated, we need to see if the game is still meant to continue. We do so with is_dead() and defenses_breached() – if either of these methods returns true, we know we need to return to the start screen. On the other hand, we also need to check to see if we’ve killed all the aliens, from within win_round(). Assuming we’re not dead, but the aliens are, we know we can call the next_round() method, which creates a fresh batch of aliens and increases their speed around the screen. Finally, we refresh the screen so everything that’s been moved, shot or killed can be updated or removed from the screen. Remember, the main loop happens 20 times a second – so the fact we don’t call for the screen to update right at the end oftheloopisofnoconsequence. 11Mainloopkeylogic–part1 Firstly the game checks that the end_game attribute is false – if it’s true, the entire loop in main_loop() is skipped and we go straight to pygame.quit(), exiting the game. This flag is set to true only if the player closes the game window or presses the Esc key when on the start_screen. Assuming end_game and start_screen are false, the main loop can start proper, with the control()method,whichcheckstoseeifthelocationoftheplayer needstochange.Nextweattempttomakeanenemymissileand we use the random module to limit the number of missiles that can be created. Next we call the update() method for each and every actor on the screen using a simple for loop. This makes sureeveryone’suptodateandmovedbeforewecheckcollisions incalc_collisions(). 03TestingPivaders With Pygame installed and the project cloned to your machine (you can also find the .zip on FileSilo – simply unpack it and copy it to your home directory to use it), you can take it for a quick test drive to make sure everything’s set up properly. All you need to do is type python pivaders.py from within the pivaders directory in the terminal to get started. You can start the game with the space bar, shoot with the same button and simply use the left and right arrows on your keyboard to move your ship left andright. you can’t reassign individual items within them. Perfect for constants, which aren’t designedtochangeanyway. You could, for example, have a pet class. From that class you could create a cat (that meows) and a dog (that barks). They’re different in many ways, but they’re both furry and have four legs, so can be created from the same parent class. We’ve done exactly that with our Ammo class, using it to create both the player bullets and the alien missiles. They’re different colours and they shoot in opposite directions, but they’re fundamentally one and the same. This saves us creating extra unnecessary code and ensures consistent behaviour between objectswecreate. Weusedwidelyavailableopensource artandfontstomakethegame
  • 119.
    The Python Book119 Create with Python DeadoraliveProbablytwoofthemost importantquestionsareansweredhere–is theplayerdeadordidyouwintheround? StartthegameTheverylastthing wedoiscreateaGameobjectandcallthe mainloop.Besidesourconstants,thisis theonlycodethatsitsoutsideaclass. pygame.display.flip() self.control() def make_player(self): self.player = Player() self.player_group.add(self.player) self.all_sprite_list.add(self.player) def refresh_screen(self): self.all_sprite_list.draw(self.screen) self.refresh_scores() pygame.display.flip() self.screen.blit(self.background, [0, 0]) self.clock.tick(self.refresh_rate) def refresh_scores(self): self.screen.blit(self.game_font.render( “SCORE “ + str(self.score), 1, WHITE), (10, 8)) self.screen.blit(self.game_font.render( “LIVES “ + str(self.lives + 1), 1, RED), (355, 575)) def alien_wave(self, speed): for column in range(BARRIER_COLUMN): for row in range(BARRIER_ROW): alien = Alien() alien.rect.y = 65 + (column * ( ALIEN_SIZE[1] + ALIEN_SPACER)) alien.rect.x = ALIEN_SPACER + ( row * (ALIEN_SIZE[0] + ALIEN_SPACER)) self.alien_group.add(alien) self.all_sprite_list.add(alien) alien.speed -= speed def make_bullet(self): if GameState.game_time - self.player.time self.player.speed: bullet = Ammo(BLUE, BULLET_SIZE) bullet.vector = -1 bullet.speed = 26 bullet.rect.x = self.player.rect.x + 28 bullet.rect.y = self.player.rect.y self.bullet_group.add(bullet) self.all_sprite_list.add(bullet) self.player.time = GameState.game_time GameState.shoot_bullet = False def make_missile(self): if len(self.alien_group): shoot = random.random() if shoot = 0.05: shooter = random.choice([ alien for alien in self.alien_group]) missile = Ammo(RED, MISSILE_SIZE) missile.vector = 1 missile.rect.x = shooter.rect.x + 15 missile.rect.y = shooter.rect.y + 40 missile.speed = 10 self.missile_group.add(missile) self.all_sprite_list.add(missile) def make_barrier(self, columns, rows, spacer): for column in range(columns): for row in range(rows): barrier = Block(WHITE, (BLOCK_SIZE)) barrier.rect.x = 55 + (200 * spacer) + (row * 10) barrier.rect.y = 450 + (column * 10) self.barrier_group.add(barrier) self.all_sprite_list.add(barrier) def make_defenses(self): for spacing, spacing in enumerate(xrange(4)): self.make_barrier(3, 9, spacing) def kill_all(self): for items in [self.bullet_group, self.player_group, self.alien_group, self.missile_group, self.barrier_group]: for i in items: i.kill() def is_dead(self): if self.lives 0: self.screen.blit(self.game_font.render( “The war is lost! You scored: “ + str( self.score), 1, RED), (250, 15)) self.rounds_won = 0 self.refresh_screen() pygame.time.delay(3000) return True def win_round(self): if len(self.alien_group) 1: self.rounds_won += 1 self.screen.blit(self.game_font.render( “You won round “ + str(self.rounds_won) + “ but the battle rages on”, 1, RED), (200, 15)) self.refresh_screen() pygame.time.delay(3000) return True def defenses_breached(self): for alien in self.alien_group: if alien.rect.y 410: self.screen.blit(self.game_font.render( “The aliens have breached Earth defenses!”, 1, RED), (180, 15)) self.refresh_screen() pygame.time.delay(3000) return True def calc_collisions(self): pygame.sprite.groupcollide( self.missile_group, self.barrier_group, True, True) pygame.sprite.groupcollide( self.bullet_group, self.barrier_group, True, True) if pygame.sprite.groupcollide( self.bullet_group, self.alien_group, True, True): self.score += 10 if pygame.sprite.groupcollide( self.player_group, self.missile_group, False, True): self.lives -= 1 def next_round(self): for actor in [self.missile_group, self.barrier_group, self.bullet_group]: for i in actor: i.kill() self.alien_wave(self.level_up) self.make_defenses() self.level_up += 50 def main_loop(self): while not GameState.end_game: while not GameState.start_screen: GameState.game_time = pygame.time.get_ticks() GameState.alien_time = pygame.time.get_ticks() self.control() self.make_missile() for actor in [self.player_group, self.bullet_group, self.alien_group, self.missile_group]: for i in actor: i.update() if GameState.shoot_bullet: self.make_bullet() self.calc_collisions() if self.is_dead() or self.defenses_breached(): GameState.start_screen = True if self.win_round(): self.next_round() self.refresh_screen() self.splash_screen() pygame.quit() if __name__ == ‘__main__’: pv = Game() pv.main_loop() Refreshing thescreenYou needtocarefully considerthewayin whichyouupdate thescreen.Blitting thebackground betweenactor movementsisvital forcleananimation. MainloopThis isthebusinessend ofourapplication. Thisloopexecutes 20timesasecond.It needstobelogical andeasyforanother codertounderstand. Guns’n’ammo Bulletsandmissiles usethesameparent class.Wechangea fewkeyattributes originallyinitialised tocreatethe behaviourweneed; egthevectorforeach isopposite. Get thecode: bit.ly/ 11k5f2x Aclassisessentiallyablueprint
  • 120.
    120 The PythonBook Create with Python AfterwritingaSpaceInvaderscloneinjust300linesofPython, nowweexpandittoincludeanimationandsound Parttwo:Addanimationand soundtoPivaders that goal would likely have been overshot at least twofold. Pygame’s ability to group, manage and detect collisions thanks to the Sprite class really made a great difference to our project, not just in terms of length but in simplicity. If you missed the first part of the project, you can find the v0.1 code listing on GitHub via git.io/cBVTBg, while you can find version v0.2, including all the images, music and sound effects we used, over at git.io/8QsK-w. Even working within the clearly defined framework Pygame offers, there are still a thousand ways we could have approached adding animation and sound. We could have created any one of a dozen classes to create and manage containers of individual images, or read in a sprite sheet (a single image full of smaller, separate images) which we could then draw (or blit) to the screen. For the sake of simplicity and performance, we integrated a few animation methods into our Game class and opted to use a sprite sheet. Not only does it make it very easy to draw to the screen, but it also keeps the asset count under control and keeps performance levels up, which is especially important for the Raspberry Pi. 01Settingupdependencies As we recommended with the last tutorial, you’ll get much more from the exercise if you download the code (git.io/8QsK-w) and use it for reference as you create your own animations and sound for your Pygame projects. Regardless of whether you just want to simply preview and play or walk-through the code to get a better understanding of basic game creation, you’re still going to need to satisfy some basic dependencies. The two key requirements here are Pygame and Git, both of which are installed by default on up-to-date Raspbian installations. If you’re unsure if you have them, though, type the following at the command line: sudo apt-get install python- pygame git We had great fun creating our basic Space Invaders clone, Pivaders, for the previous tutorial. One of the key challenges with the project was keeping it to a manageable size – just 300 lines of Python. Without the use of Pygame’s strong set of features, Resources Raspbian:www.raspberrypi.org /downloads Python: www.python.org/doc Pygame: www.pygame.org/docs Artassets: opengameart.org
  • 121.
    The Python Book121 Create with Python class Game(object): def __init__(self): pygame.init() pygame.font.init() self.clock = pygame.time.Clock() self.game_font = pygame.font.Font( ‘data/Orbitracer .ttf’ , 28) self.intro_font = pygame.font.Font( ‘data/Orbitracer .ttf’ , 72) self.screen = pygame.display.set_mode([RES[0], RES[1]]) self.time = pygame.time.get_ticks() self.refresh_rate = 20; self.rounds_won = 0 self.level_up = 50; self.score = 0 self.lives = 2 self.player_group = pygame.sprite.Group() self.alien_group = pygame.sprite.Group() self.bullet_group = pygame.sprite.Group() self.missile_group = pygame.sprite.Group() self.barrier_group = pygame.sprite.Group() self.all_sprite_list = pygame.sprite.Group() self.intro_screen = pygame.image.load( ‘data/graphics/start_screen.jpg’).convert() self.background = pygame.image.load( ‘data/graphics/Space-Background.jpg’).convert() pygame.display.set_caption(‘Pivaders - ESC to exit’) pygame.mouse.set_visible(False) Alien.image = pygame.image.load( ‘data/graphics/Spaceship16.png’).convert() Alien.image.set_colorkey(WHITE) self.ani_pos = 5 # 11 images of ship self.ship_sheet = pygame.image.load( ‘data/graphics/ship_sheet_final.png’).convert_alpha() Player .image = self.ship_sheet.subsurface( self.ani_pos*64, 0, 64, 61) self.animate_right = False self.animate_left = False self.explosion_sheet = pygame.image.load( ‘data/graphics/explosion_new1.png’).convert_alpha() self.explosion_image = self.explosion_sheet.subsurface(0, 0, 79, 96) self.alien_explosion_sheet = pygame.image.load( ‘data/graphics/alien_explosion.png’) self.alien_explode_graphics = self.alien_explosion_sheet. subsurface(0, 0, 94, 96) self.explode = False self.explode_pos = 0; self.alien_explode = False self.alien_explode_pos = 0 pygame.mixer .music.load(‘data/sound/10_Arpanauts.ogg’) pygame.mixer .music.play(-1) pygame.mixer .music.set_volume(0.7) self.bullet_fx = pygame.mixer .Sound( ‘data/sound/medetix__pc-bitcrushed-lazer-beam.ogg’) self.explosion_fx = pygame.mixer .Sound( ‘data/sound/timgormly__8-bit-explosion.ogg’) self.explosion_fx.set_volume(0.5) self.explodey_alien = [] GameState.end_game = False GameState.start_screen = True GameState.vector = 0 GameState.shoot_bullet = False def control(self): for event in pygame.event.get(): if event.type == pygame.QUIT: GameState.start_screen = False GameState.end_game = True if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE: if GameState.start_screen: GameState.start_screen = False GameState.end_game = True self.kill_all() else: GameState.start_screen = True self.keys = pygame.key.get_pressed() if self.keys[pygame.K_LEFT]: GameState.vector = -1 self.animate_left = True self.animate_right = False elif self.keys[pygame.K_RIGHT]: GameState.vector = 1 self.animate_right = True self.animate_left = False else: GameState.vector = 0 self.animate_right = False self.animate_left = False if self.keys[pygame.K_SPACE]: if GameState.start_screen: GameState.start_screen = False self.lives = 2 self.score = 0 self.make_player() self.make_defenses() self.alien_wave(0) else: GameState.shoot_bullet = True self.bullet_fx.play() def animate_player(self): if self.animate_right: if self.ani_pos 10: Player .image = self.ship_sheet.subsurface( self.ani_pos*64, 0, 64, 61) self.ani_pos += 1 else: if self.ani_pos 5: self.ani_pos -= 1 Player .image = self.ship_sheet.subsurface( self.ani_pos*64, 0, 64, 61) if self.animate_left: if self.ani_pos 0: self.ani_pos -= 1 Player .image = self.ship_sheet.subsurface( self.ani_pos*64, 0, 64, 61) else: if self.ani_pos 5: Player .image = self.ship_sheet.subsurface( self.ani_pos*64, 0, 64, 61) self.ani_pos += 1 def player_explosion(self): if self.explode: if self.explode_pos 8: self.explosion_image = self.explosion_sheet. subsurface(0, self.explode_pos*96, 79, 96) self.explode_pos += 1 self.screen.blit(self.explosion_image, [self.player . rect.x -10, self.player .rect.y - 30]) else: self.explode = False self.explode_pos = 0 def alien_explosion(self): if self.alien_explode: if self.alien_explode_pos 9: self.alien_explode_graphics = self.alien_explosion_ sheet.subsurface(0, self.alien_explode_pos*96, 94, 96) self.alien_explode_pos += 1 self.screen.blit(self.alien_explode_graphics, [int(self. explodey_alien[0]) - 50 , int(self.explodey_alien[1]) - 60]) else: self.alien_explode = False self.alien_explode_pos = 0 self.explodey_alien = [] def splash_screen(self): while GameState.start_screen: self.kill_all() self.screen.blit(self.intro_screen, [0, 0]) self.screen.blit(self.intro_font.render( “PIVADERS”, 1, WHITE), (265, 120)) self.screen.blit(self.game_font.render( “PRESS SPACE TO PLAY”, 1, WHITE), (274, 191)) pygame.display.flip() self.control() self.clock.tick(self.refresh_rate / 2) def make_player(self): self.player = Player() Pivaders.py listing from line 86 (continued on next page) ship_sheet Wesettheplayer imagetobeequalto onesmallsegment ofthespritesheetby usingthe‘ani_pos’ variable.Changethe variabletochange thepicture SetflagsWe’ve added‘animate_left’ and‘animate_right’ Booleanflagsto thecontrolmethod. Whenthey’retrue, theactualanimation codeiscalledviaa separatemethod fx.play() Havingalready loadedthesound effectwewantwhen weshoot,wenow justneedtocallit whenwepressthe spacebar Get thecode: bit.ly/ 1xPvY1F
  • 122.
    122 The PythonBook Create with Python AboveTheFreesoundsiteisagoodplacetofindfreeandopensoundeffectsforprojects 04Animationsound Compared with the game from last month’s tutorial, you’ll see it’s now a much more dynamic project. The protagonist ship now leans into the turns as you change direction and corrects itself when you either press the opposite direction or lift your finger off the button. When you shoot an alien ship, it explodes 05Findingimagestoanimate Beforewecanprogramanything, it’s wise to have assets set up in a way we can use them. As mentioned, we’ve opted to use sprite sheets; these can be found online or created with GIMP with a little practice. Essentially they’re a mosaic made up of individual ‘frames’ of equally sized and spaced images representing each frame. Find ready-made examples atopengameart.org,asusedhere. 06Tweakingassets While many of the assets on sites like opengameart.org can be used as is, you may want to import them into an image-editing application like GIMP to configure them to suit your needs – as we did with our ship sheet asset to help us keep the code simple. We started with thecentralshipspriteandcentreditintoa new window. We set the size and width of theframeandthencopy-pastedtheother frames either side of it. We ended up with 11 frames of exactly the same size and width in a single document. Pixel-perfect precision on size and width is key, so we can just multiply it to find the next frame. 07Loadingthespritesheet Since we’re inheriting from the SpriteclasstocreateourPlayerclass,we can easily alter how the player looks on screen by changing Player.image. First, we need to load our ship sprite sheet with pygame.image.load(). Since we made our sheet with a transparent background, we can append .convert_alpha() to the 08Animationflags Downthelistintheinitialisingcodeforthe Gameclass, we set two flags for player animation: self.animate_left and self.animate_right. In the Control method of our Game class, we use these to ‘flag’ when we want animations to work using Boolean values. It allows us to ‘automatically’ animate the player sprite back to its resting state (otherwise the ship will continuetolookasifit’sflyingleftwhenithasstopped). end of the line so the ship frames render correctly (without any background). We then use subsurface to set the initial Player. image to the middle ship sprite on the sheet. This is set by self. ani_pos, which has an initial value of 5. Changing this value will altertheshipimagedrawntothescreen:‘0’woulddrawitleaning fullyleft,‘11’fullytotheright. 09Theanimationmethod These flags pop up again in the core animation code for the player: animate_player() within the Game class. Here we use nested if statements to control the animation and physically set the player image accordingly. Essentially it states that if the animate_right flag is True and if the current animation position is different to what we want, we incrementally increase the ani_posvariableandsettheplayer’simageaccordingly.TheElse statement then animates the ship sprite back to its resting state andthesamelogicisthenappliedintheoppositedirection. 10Animatingexplosions The player_explosion() and alien_explosion() methods thatcomeaftertheplayeranimationblockintheGameclassare similar but simpler executions of essentially the same thing. As we only need to run through the same predefined set of frames (this time vertically), we only need to see if the self.explode and self.alien_explode flags are True before we increment the variables that change the image displayed. As the sprite sheet is vertical, the variables alien_explode_pos and explosion_image aresettoadifferentpartofsubsurfacethanbefore. 12Usingsoundeffects Loading and using sounds is similar to how we do so for images in Pygame. First we load the sound effect using a simple assignment.Forthelaserbeam,theinitialisationlookslikethis: self .bullet_fx = pygame.mixer .Sound( ‘data/sound/medetix__pc-bitcrushed-lazer-beam.ogg’) Then we simply trigger the sound effect at the appropriate time. In the case of the laser, we want it to play whenever we press the space bar to shoot, so we place it in the Game class’s Control method,straightafterweraisetheshoot_bulletflag. If you’re struggling to find free and open sound effects, we recommendwww.freesound.org. 11Addingmusictoyourproject Pygamemakesiteasytoaddamusicalscoretoaproject. Justobtainasuitablepieceofmusicinyourpreferredformat(we found ours via freemusicarchive.org) and load it using the Mixer Pygame class. As it’s already been initialised via pygame.init(), wecangoaheadandloadthemusicwiththiscode: pygame.mixer .music.load(‘data/sound/10_Arpanauts.ogg’) pygame.mixer .music.play(-1) pygame.mixer .music.set_volume(0.7) The music.play(-1) requests that the music should start with the appandcontinuetoloopuntilitquits.Ifwereplaced-1with5,the musicwouldloopfivetimesbeforeending.Learnmoreaboutthe Mixerclassviawww.pygame.org/docs/ref/mixer.html. 03Navigatingtheproject The project is laid out quite simply across a few subfolders. Within pivaders sits a licence, readme and a second pivaders folder. This contains the main game file, pivaders.py, which launches the application. Within the data folder you’ll find subfolders for both graphics and sound assets, as well as the font we’ve used for the title screen and scores. To take pivaders for a test-drive, simply enter the pivaders subdirectory (cd pivaders/pivaders)andtype: python pivaders.py Use the arrow keys to steer left and right and the space bar to shoot. You can quit to the main screen with the Esc key and pressitagaintoexitthegamecompletely. 02Downloadingpivaders Git is a superb version control solution that helps programmers safely store their code and associated files. Not only does it help you retain a full history of changes, it means you can ‘clone’ entire projects to use and work on from places like github.com. Toclone the version of the project we created for this tutorial, go to your home folder from thecommandline(cd ~)andtype: git pull https://github.com/ russb78/pivaders.git This will create a folder called pivaders – go inside (cd pivaders) and take a look around. with several frames of animation and should you take fire, a smaller explosion occurs on your ship. Music, lasers and explosion sound effects also accompany theanimationsastheyhappen.
  • 123.
    The Python Book123 Create with Python self.player_group.add(self.player) self.all_sprite_list.add(self.player) def refresh_screen(self): self.all_sprite_list.draw(self.screen) self.animate_player() self.player_explosion() self.alien_explosion() self.refresh_scores() pygame.display.flip() self.screen.blit(self.background, [0, 0]) self.clock.tick(self.refresh_rate) def refresh_scores(self): self.screen.blit(self.game_font.render( “SCORE “ + str(self.score), 1, WHITE), (10, 8)) self.screen.blit(self.game_font.render( “LIVES “ + str(self.lives + 1), 1, RED), (355, 575)) def alien_wave(self, speed): for column in range(BARRIER_COLUMN): for row in range(BARRIER_ROW): alien = Alien() alien.rect.y = 65 + (column * ( ALIEN_SIZE[1] + ALIEN_SPACER)) alien.rect.x = ALIEN_SPACER + ( row * (ALIEN_SIZE[0] + ALIEN_SPACER)) self.alien_group.add(alien) self.all_sprite_list.add(alien) alien.speed -= speed def make_bullet(self): if GameState.game_time - self.player .time self.player .speed: bullet = Ammo(BLUE, BULLET_SIZE) bullet.vector = -1 bullet.speed = 26 bullet.rect.x = self.player .rect.x + 28 bullet.rect.y = self.player .rect.y self.bullet_group.add(bullet) self.all_sprite_list.add(bullet) self.player .time = GameState.game_time GameState.shoot_bullet = False def make_missile(self): if len(self.alien_group): shoot = random.random() if shoot = 0.05: shooter = random.choice([ alien for alien in self.alien_group]) missile = Ammo(RED, MISSILE_SIZE) missile.vector = 1 missile.rect.x = shooter .rect.x + 15 missile.rect.y = shooter .rect.y + 40 missile.speed = 10 self.missile_group.add(missile) self.all_sprite_list.add(missile) def make_barrier(self, columns, rows, spacer): for column in range(columns): for row in range(rows): barrier = Block(WHITE, (BLOCK_SIZE)) barrier .rect.x = 55 + (200 * spacer) + (row * 10) barrier .rect.y = 450 + (column * 10) self.barrier_group.add(barrier) self.all_sprite_list.add(barrier) def make_defenses(self): for spacing, spacing in enumerate(xrange(4)): self.make_barrier(3, 9, spacing) def kill_all(self): for items in [self.bullet_group, self.player_group, self.alien_group, self.missile_group, self.barrier_group]: for i in items: i.kill() def is_dead(self): if self.lives 0: self.screen.blit(self.game_font.render( “The war is lost! You scored: “ + str( self.score), 1, RED), (250, 15)) self.rounds_won = 0 self.refresh_screen() self.level_up = 50 self.explode = False self.alien_explode = False pygame.time.delay(3000) return True def defenses_breached(self): for alien in self.alien_group: if alien.rect.y 410: self.screen.blit(self.game_font.render( “The aliens have breached Earth defenses!”, 1, RED), (180, 15)) self.refresh_screen() self.level_up = 50 self.explode = False self.alien_explode = False pygame.time.delay(3000) return True def win_round(self): if len(self.alien_group) 1: self.rounds_won += 1 self.screen.blit(self.game_font.render( “You won round “ + str(self.rounds_won) + “ but the battle rages on”, 1, RED), (200, 15)) self.refresh_screen() pygame.time.delay(3000) return True def next_round(self): self.explode = False self.alien_explode = False for actor in [self.missile_group, self.barrier_group, self.bullet_group]: for i in actor: i.kill() self.alien_wave(self.level_up) self.make_defenses() self.level_up += 50 def calc_collisions(self): pygame.sprite.groupcollide( self.missile_group, self.barrier_group, True, True) pygame.sprite.groupcollide( self.bullet_group, self.barrier_group, True, True) for z in pygame.sprite.groupcollide( self.bullet_group, self.alien_group, True, True): self.alien_explode = True self.explodey_alien.append(z.rect.x) self.explodey_alien.append(z.rect.y) self.score += 10 self.explosion_fx.play() if pygame.sprite.groupcollide( self.player_group, self.missile_group, False, True): self.lives -= 1 self.explode = True self.explosion_fx.play() def main_loop(self): while not GameState.end_game: while not GameState.start_screen: GameState.game_time = pygame.time.get_ticks() GameState.alien_time = pygame.time.get_ticks() self.control() self.make_missile() self.calc_collisions() self.refresh_screen() if self.is_dead() or self.defenses_breached(): GameState.start_screen = True for actor in [self.player_group, self.bullet_group, self.alien_group, self.missile_group]: for i in actor: i.update() if GameState.shoot_bullet: self.make_bullet() if self.win_round(): self.next_round() self.splash_screen() pygame.quit() if __name__ == ‘__main__’ : pv = Game() pv.main_loop() Get thecode: bit.ly/ 1xPvY1F
  • 124.
    124 The PythonBook Create with Python Bridgethegapbetweenbooksandvideogamesby creating an interactivenovelor choose-your-own-adventurewithPython andPygame Resources Python2: www.python.org/ Pygame: pygame.org/download.shtml IDLEPythonIDE Gameassets CodefromFileSilo(optional) Makeavisualnovel gamewithPython Most people look for a compelling story in modern videogames, and those that don’t have one are appearing less and less. A great way to tell a pure story is through the genre of visual novels, and you can make one fairly simply in Python. These interactive novels are an extremely popular form of entertainment in Japan, and usually work by having the player clickthroughastoryandmakedecisionsasthey go along in order to experience different plot pointsandendings. QChangescenestoaddmoredepthtothestory,andallowthegametohavedecisionsandroutes In Python, this is a relatively simple project to create, but with the addition of the Pygame module we can make it easier still, and even more expandable for the future. Pygame adds better support for positioning the images and text, creating display windows and using mouse and keyboard inputs, thereby simplifying the codingprocess. We’ll be coding this in standard Python 2, so makesuretorunitinIDLE2andnotIDLE3while youarewriting,testingandcoding.
  • 125.
    The Python Book125 Create with Python 02GetthePygamecode Next we need to download the code for Pygame direct from the source. Still in the terminal,youcandothisbytypingin: $ hg clone https://bitbucket.org/pygame/ pygame Which will download it to the folder ‘pygame’. Move to that using CD pygame in the terminal so wecancontinuebuildingit. 03BuildthePygamemodule To install it, we need to do it in two steps. First we need to prepare the code to installusingtheterminalwith: $ python setup.py build Once that’s finished you can then actually install itwith: $ sudo python setup.py install Thiswon’ttaketoolong. 04Installinotherways If the above doesn’t work (or is a bit daunting) you can check the website for binary and executable files that will work on other operating systems and Linux distros. Head to http://pygame.org/download.shtml to get the files you need for your specific system, including Windows and OS X. The rest of the tutorial will workinanyOS. 01 Get Pygame dependencies The best way to install Pygame for your system is to compile it. To do this you need to first install the right dependencies. Open up the terminal and install the following packages, which in Ubuntu looks like: $ sudo apt-get install mercurial python-dev python-numpy libav-tools libsdl-image1.2-dev libsdl-mixer1.2- dev libsdl-ttf2.0-dev libsmpeg- dev libsdl1.2-dev libportmidi-dev libswscale-dev libavformat-dev libavcodec-dev 05Getthevisualnovelfiles We’ve uploaded the code to FileSilo, and here we’re going to walk you through what we’ve done to make it work. Download the files for the visual novel and unzip them. The two files we care about for the moment are the visualnovel.py and script.py python files – this is where all the important code is. 06Understandthescriptfile For the moment the script file is small and literally just holds the script for the game. It’s made up of events for the visual novel to move between, line by line, by splitting it up into scenes.Thisincludesthelocationofeachline,the character,theactuallineitselfandinformationon how the game flows. These are matrices with the informationin,andarecompletelycustomisable.
  • 126.
    126 The PythonBook Create with Python 09Addvariablesandassets We add a mixture of information we need to run the novel. We define the size of the display screen to use (1000 pixels wide and 563 high), along with some RGB colours for the code to use. We’re also telling Pygame what font to use and how large for certain sections and also loading images for the game. 10Startthegame Create a display for the game. Pygame works by constantly updating the display with new information. To show how this works, the menu function adds elements to the display (which we’ve titled screen), such as filling it with colour, adding shapes and using blit to add images or in this case text. Once you’ve created a buffer of changes to the screen, you update it with the flip() function. 12Startthestory Our start_game function is called when the mouse clicks the right position and we prepare the game, getting the characters, locations and progression through the game script. The rest of this function uses this info to pull in data from the script to make the game flow properly. 07Howthescriptrelates In our game, the code pulls in elements from the script file as it goes. We'll explain how that works later, but this also allows us to implement decisions later on to change which direction the game might take you in. 08Startingthemaingame We don’t need many modules for the current state of the visual novel. Here we’ve imported the new Pygame module, our script as a module and the time module for aesthetic reasons – we’re going to have the code pause in bits rather than just instantly change scenes to the next line. We also initialise Pygame with a simple pygame.init() 13Firstscreen The first screen is handled differently, and acts to get every element up on the interface before we continue – it makes the code take a little less time to process as we 11Seethemouse As we’ve created the button as a rectangle and now an image on the menu, we need to recognise when the mouse is hovering over it to know when the button is clicked. First we have to use event.get() to see the mouse in general, then we look for the position with get_pos(). After that, we wait for it to click, see where it clicked (using the co-ordinates of the rectangle) and make a decision after that. Thecodepullsinelementsfrom thescriptfileasitgoes,allowingusto implementdecisionslateron
  • 127.
    The Python Book127 Create with Python 15Thestartingfunction We finish our code bit with a simple function that starts off the entire game. This is just to encapsulate the entire code and allows us toadddifferentwaysofturningitoffinthefuture. IDLE when running the file will load everything up and then run the game() function at the end – this is similar to how you can add a __main__ functionattheendwhichwillstartthecodeinthe commandline. feel more interactive. This would not require much more code than the if statements, and it would also be a good way for you to look into adding graphical buttons to click and use the collide function. 17Movetheassets Currently the code has the script- specific assets in the main visualnovel file. These can be moved to the script, allowing you to make the visualnovel file much more modular so that can you have multiple scripts with different assets to load at startup. 14Addvariablesandassets Similarly to the way that our original startup code works, our next if statement and iteration checks to see what is different on the next line, and if it moves to a different scene function. It will also change anything that is different without filling up the buffer more than needed. Where we’ve made no change is labelled with a 0 in the scripts. 16Expandyourcode The code written is very expandable, allowing you to add decisions that are logged to take you to different scenes (or routes in visual novel terminology) and make your game begin. The getattr allows us to use the string/ integer associated with our place in the story and call upon the relevant scene function from the script file. We then use an if statement with an iterative function to successively add screen element to give the illusion that it’s building up the first screen. We finish it by advancing the progression value. Thecodehereisveryexpandable, allowingyoutoadddecisionsthattake youtodifferentscenes
  • 128.
    128 The PythonBook Create with Python Games are a great way of understanding a language:youhaveagoaltoworktowards,and eachfeatureyouaddbringsmorefun.However, games need libraries and modules for graphics and other essential games features. While the Pygame library made it relatively easy to make gamesinPython, it stillbringsinboilerplatecode thatyouneedbeforeyougetstarted–barriersto youoryourkidsgettingstartedincoding. Pygame Zero deals with all of this boilerplate code for you, aiming to get you coding games instantly. Pg0 (as we’ll abbreviate it) makes sensible assumptions about what you’ll need for agame–fromthesizeofthewindowtoimporting the game library – so that you can get straight downtocodingyourideas. Pg0’s creator, Daniel Pope, told us that the library “grew out of talking to teachers at Pycon UK’s education track, and trying to understand that they need to get immediate results and break lessons into bite-size fragments, in order to keep a whole class up to speed.” To give you an idea of what’s involved here, we’ll build up a simple game from a Pong- type bat and ball through to smashing blocks Breakout-style. The project will illustrate what can be done with very little effort. Pg0 is in early development but still offers a great start – and is now included on the Pi in the Raspbian Jessie image. We’ll look at installation on other platforms, but first let’s see what magic it can perform. SCORE 0001200 LIVES BREAKOUT Resources PygameZero: pygame-zero.readthedocs.org Pygame: pygame.org/download.shtml Pip pip-installer.org Python3.2orlater www.python.org/ CodefromFileSilo(optional) PygameZero PygameZerocutsouttheboilerplatetoturnyourideasintogames instantly,andwe’llshowyouhow
  • 129.
    The Python Book129 Create with Python 01Zeroeffort Although game writing is not easy, getting started certainly is. If you’ve got Raspbian Jessie installed on your Pi, you’rereadytogo.Openaterminalandtype: touch example.py pgzrun example.py Andyou’llseeanemptygamewindowopen(Ctrl+Qwillclosethe window).Yes,it’sthateasytogetstarted! 04NoPi? You don’t even need a Raspberry Pi to install Pygame Zero – just install the Pygame library, then use pip to install Pygame Zero. Instructions vary by distro, but a good place to start is the documentation: bit.ly/1GYznUB. 03OlderRaspbian If you’re still running Raspbian Wheezy, you’ll need to run thefollowingstepstoinstallPygameZero: sudo apt-get update sudo apt-get install python3-setuptools python3-pip sudo pip-3.2 install pgzero 05Intro.py That default black square of 800 by 600 pixels we saw in Step 1 can now be overridden manually. For example, we can use the following code to replace it with an oversized gold brick, in a sly nod to Breakout: WIDTH = 1000 HEIGHT = 100 def draw(): screen.fill((205, 130, 0)) That colour tuple takes RGB values, so you can quickly get colours off a cheatsheet; screen is built into Pg0 for the window display, with methods available for all sorts of different sprites… 02Python3 If you haven’t got Raspbian Jessie, chances are you’ll haveneitherPg0norPygameinstalled.ThePython’spippackage installer will take care of grabbing Pg0 for you, but the preceding steps vary by distro. One thing you will need is Python 3.2 (or newer). If you’ve been sticking with Python 2.x in your coding (perhaps because it’s used in a tutorial you’re following), make Pg0yourchanceforagentleupgradetoPython3. RightBreakoutis aclassicarcade gamethatcan bereimaginedin PygameZero Insituationswhere Pygameisused boilerplateandall withyoungpeople, greatresultscan alsobeachieved (seeBryson Payne’sbook), butPygameand Pg0,despitetheir useaspowerful educationaltools, arealsogoodfor creatinggamesfor codersnomatter whatstageof learningtheyareat. Greatgamesareall aboutthegameplay, drivenbypowerful imaginations generatingimages, animations,sounds andjourneys throughgame worlds.Good frameworksopen upthiscreative activitytopeople whoarenot traditionallearners ofprogramming, whichisanarea wherePythonhas longexcelled. Young andold Codeon FileSilo
  • 130.
    130 The PythonBook Create with Python 09Mousemove We want to move the bat, and the mouse is closer to an arcadepaddlethanthearrowkeys.Addthefollowing: def on_mouse_move(pos): x, y = pos bat.center = (x, bat.center[1]) Usepgzruntotestthatyouhaveascreen,batandmovement. 08Batty You can think of Breakout as essentially being a moving bat – that is, you’re hitting a moving ball in order to knock out blocks. The bat is a rectangle, and Pygame’s Rect objects store and manipulate rectangular areas – we use Rect((left, top), (width, height)), before which we define the bat colour and then call upon the draw function to put the bat on thescreen,usingthescreenfunction. W = 800 H = 600 RED = 200, 0, 0 bat = Rect((W/2, 0.96 * H), (150, 15)) def draw(): screen.clear() screen.draw.filled_rect(bat, RED) 07BreakoutviaPong While the Pi is something of a tribute to 1980s 8-bit computers, Breakout comes from the 1970s and is a direct descendant of the early arcade classic Pong. We’ll follow the route from Pong to Breakout (which historically involved Apple founders Steve Wozniak and Steve Jobs) in the steps to creating our code, leaving you with the option of developing the Pong elements into a proper game, as well as refining the finished Breakoutclone. 06Sprite The intro example from the Pg0 docs expands on that with the Actor class, which will automatically load the named sprite (Pg0 will hunt around for a .jpg or .png in a subdirectory calledimages). alien = Actor(‘alien’) alien.pos = 100, 56 WIDTH = 500 HEIGHT = alien.height + 20 def draw(): screen.clear() alien.draw() You can download the alien from the Pg0 documentation (bit. ly/1Sm5lM7) and try out the animation shown there, but we’re takingadifferentapproachinourgame. Right Thebat andballcome first–they’rethe cornerstonesof PongandBreakout DavidAmes,who usesPg0toteach youngerchildren tocodeatevents acrosstheUK, toldus:“Onething toavoidwhenit comestoteaching kidsisObject Orientation.”OOP (object-oriented programming)is partlyabstracted awaybyPg0,butit can’tbeignored. Perhapsthebest approachisusing Pg0andsome simplecodeto start,thendropping inapieceofOO whenit’sneededto solveaparticular problem. WiththeCodeClub agegroup–about eighttoeleven– feedinginformation tosolvepractical problemsworks well.Itcanwork withadults,too– butthere’salways someonewho’s readaheadand hasafewtricky questions. Program objects
  • 131.
    The Python Book131 Create with Python ## Breakout type game to demonstrate Pygame Zero library ## Based originally upon Tim Viner’s London Python Dojo ## demonstration ## Licensed under MIT License - see file COPYING from collections import namedtuple import pygame import sys import time W = 804 H = 600 RED = 200, 0, 0 WHITE = 200,200,200 GOLD = 205,145,0 ball = Rect((W/2, H/2), (30, 30)) Direction = namedtuple(‘Direction’, ‘x y’) ball_dir = Direction(5, -5) bat = Rect((W/2, 0.96 * H), (120, 15)) class Block(Rect): def __init__(self, colour, rect): Rect.__init__(self, rect) self.colour = colour blocks = [] for n_block in range(24): block = Block(GOLD, ((((n_block % 8)* 100) + 2, ((n_block // 8) * 25) + 2), (96, 23))) blocks.append(block) def draw_blocks(): for block in blocks: screen.draw.filled_rect(block, block.colour) def draw(): screen.clear() screen.draw.filled_rect(ball, WHITE) screen.draw.filled_rect(bat, RED) draw_blocks() def on_mouse_move(pos): x, y = pos bat.center = (x, bat.center[1]) def on_mouse_down(): global ball_dir ball_dir = Direction(ball_dir.x * 1.5, ball_dir.y * 1.5) Fullcodelisting Togettheballtomoveweneed todefinemove(ball)foreachcase wheretheballmeetsawall 10Squareball In properly retro graphics-style, we define a square ball too – another rectangle, essentially, with the (30, 30) size makingitthatsubsetofrectanglesthatwecallasquare. We’re doing this because Rect is another built-in in Pg0. If we wanted a circular ball, we’d have to define a class and then use Pygame’s draw.filled_circle(pos, radius, (r, g, b)) - but Rect we cancalldirectly.Simplyadd: WHITE = 200,200,200 ball = Rect((W/2, H/2), (30, 30)) …totheinitialvariableassignments,and: screen.draw.filled_rect(ball, WHITE) …tothedef draw()block. 12defmove(ball) To get the ball to move within the screen we need to define move(ball) for each case where the ball meets a wall. For this we use if statements to reverse the ball’s direction at eachoftheboundaries.Refertothefullcodelistingonpage67. Note the hardcoded value of 781 for the width of screen, minus the width of ball – it’s okay to hardcode values in early versions of code, but it’s the kind of thing that will need changing if your project expands. For example, a resizable screenwouldneedavalueofW - 30. 13Absolutevalues You might expect multiplying y by minus one to work for reversingthedirectionoftheballwhenithitsthebat: ball_dir = Direction(ball_dir .x, -1 * ball_dir .y) … but you actually need to use abs, which removes any minus signs,thenminus: ball_dir = Direction(ball_dir .x, - abs(ball_dir .y)) Try it without in the finished code and see if you get some strangebehaviour.Yourhomeworkistoworkoutwhy. 11Action! Now let’s make the ball move. Download the tutorial resources in FileSilo.co.uk and then add the code inside the ‘move.py’ file to assign movement and velocity. Change the 5 in ball_dir = Direction(5, -5) if you want the ball slower or faster, as your processor (and dexterity) demands – but it’s hard to tell now because the ball goes straight off the screen! Pg0 will call the update() function you define once per frame, giving the illusionofsmooth(ish)scrollingifyou’renotrunningmuchelse.
  • 132.
    132 The PythonBook Create with Python 19Drawingblocks Draw_blocks() is added to def draw() after defining: def draw_blocks(): for block in blocks: screen.draw.filled_rect(block, block.colour) 20Blockbashing All that remains with the blocks is to expand def move(ball) – to destroy a block when the ball hits it. to_kill = ball.collidelist(blocks) if to_kill = 0: sounds.block.play() ball_dir = Direction(ball_dir .x, abs(ball_dir .y)) blocks.pop(to_kill) 17Goingforgold CreateaBlockclass: class Block(Rect): def __init__(self, colour , rect): Rect.__init__(self, rect) self.colour = colour …andpickanicecolourforyourblocks: GOLD = 205,145,0 18Lineuptheblocks Thisbuildsanarrayof24blocks,threerowsofeight: blocks = [] for n_block in range(24): block = Block(GOLD, ((((n_block % 8)* 100) + 2, ((n_block // 8) * 25) + 2), (96, 23))) blocks.append(block) 16Buildingblocks There are many ways of defining blocks and distributing them onto the screen. In Tom Viner’s team’s version, from the London Python Dojo – which was the code that originally inspired this author to give this a go – the blocks are sized in relationtonumberacrossthescreen,thus: N_BLOCKS = 8 BLOCK_W = W / N_BLOCKS BLOCK_H = BLOCK_W / 4 BLOCK_COLOURS = RED, GREEN, BLUE Using multicoloured blocks which are then built into an array means that blocks can join without needing a border. With its defining variables in terms of screen width, it’s good sustainable code, which will be easy to amend for different screensizes–seegithub.com/tomviner/breakout. However, the array of colour bricks in a single row is not enough for a full game screen, so we’re going to build our array fromhard-codedvalues… 15Blockhead! If you’re not very familiar with the ancient computer game Breakout, then: apt-get install lbreakout2 … and have a play. Now, we haven’t set our sights on building something quite so ambitious in just these six pages, but we do need blocks. 14Sounds Also upon bat collision, sounds.blip.play() looks in the sounds subdirectory for a sound file called blip. You can download the sounds (and finished code) from FileSilo.co.uk. Actually, now we think about it, ignore the previous comment about homework – your real homework is to turn what we’ve written so far into a proper game of Pong! But first let’s finish turning it into Breakout! RightTomViner’s arrayofblocks negatestheneedfor borderedrectangles There’sanew versionofPg0in development–it mayevenbeout asyoureadthis. Pg0creatorDaniel Popetellsus“atone generationAPIisin theworks,”andthat atthePg0PyConUK sprint,“wefinished Actorrotation.” Contributionsare welcome–notonly tothePg0code,but moreexamplesare needednotjustto showwhatcanbe done,buttogive teacherstoolsto enthusechildren aboutthecreative actofprogramming. Pg0hasalso inspiredGPIO Zero,tomake GPIOprogramming easieronthe RaspberryPi,with rapiddevelopment occurringonthis newlibraryaswego topress. Pg0+1
  • 133.
    The Python Book133 Create with Python 22Scoredraw Taking advantage of some of Pygame Zero’s quickstart features, we’ve a working game in around 60 lines of code. From here, there’s more Pg0 to explore, but a look into Pygame unmediatedbythePg0wrapperisyournextstepbutone. First refactor the code; there’s plenty of room for improvement – see the example ‘breakout-refactored.py’ which is included in your tutorial resources. Try adding scoring, the most significant absence in the game. You could try using a global variable and writing the score to the terminal with print(), or instead use screen.blit to put it on the game screen. Future versions of Pg0 might do more for easy score keeping. 23Classofninelives For adding lives, more layers, and an easier life-keeping score, you may be better defining the class GameClass and enclosing much of the changes you wish to persist within it, such as self.score and self.level. You’ll find a lot of Pygame code online doing this, but you can also find Pg0 examples, such as the excellent pi_lander example by Tim Martin: github.com/timboe/ pi_lander. 21Gameover Lastly, we need to allow for the possibility of successfully destroying all blocks. if not blocks: sounds.win.play() sounds.win.play() print(“Winner!”) time.sleep(1) sys.exit() 24Don’tstophere This piece is aimed at beginners, so don’t expect to understand everything! Change the code and see what works, borrow code from elsewhere to add in, and read even more code. Keep doing that, then try a project of your own – and let us know how you get on. def move(ball): global ball_dir ball.move_ip(ball_dir) if ball.x 781 or ball.x = 0: ball_dir = Direction(-1 * ball_dir.x, ball_dir.y) if ball.y = 0: ball_dir = Direction(ball_dir.x, abs(ball_dir.y)) if ball.colliderect(bat): sounds.blip.play() ball_dir = Direction(ball_dir.x, - abs(ball_dir.y)) to_kill = ball.collidelist(blocks) if to_kill = 0: sounds.block.play() ball_dir = Direction(ball_dir.x, abs(ball_dir.y)) blocks.pop(to_kill) if not blocks: sounds.win.play() sounds.win.play() print(“Winner!”) time.sleep(1) sys.exit() if ball.y H: sounds.die.play() print(“Loser!”) time.sleep(1) sys.exit() def update(): move(ball) Fullcodelisting(cont.) LeftTestyourgame onceit’sfinished –thentestother people’sBreakout gamestoseehow thecodediffers
  • 134.
    development 134 The PythonBook 154 Web 136 Develop with Python Why Python is perfect for the web 142 Creating dynamic templates Use Flask and Jinja2 to their full potential 146 Build your own blog Begin developing your blog 150 Deliver content to your blog Add content to your site 154 Enhance your blog Complete your blog with add-ons “Pythonisaversatilelanguage, perfectformakingwebsites” 142 136
  • 135.
  • 136.
    Web development 136 ThePython Book Don’tbefooledintothinkingPythonisarestrictivelanguageor incompatiblewiththemodernweb.Exploreoptionsforbuilding Pythonwebappsandexperiencerapidapplicationdevelopment
  • 137.
    Web development The PythonBook 137 Django djangoproject.com GOOD FOR: Large database-driven web apps with multiuser support and sites that need to have heavily customisable admin interfaces Django contains a lot of impressive features, all in the name of interfaces and modules. These include autowiring, admin interfaces and database migration management tools by default for all of your projects and applications. Django will help to enable rapid application development for enterprise-level projects, whilst also enabling a clear modular reuseable approachtocodeusingsubapplications. Werkzeug werkzeug.pocoo.org GOOD FOR: API creation, interacting with databases and following strict URL routes whilst managing HTTP utilitie Werkzeug is the underlying framework for Flask and other Python frameworks. It provides a unique set of tools that will enable you to perform URL routing processes as well as request and response objects, and it also includesapowerfuldebugger. Tornado tornadoweb.org GOOD FOR: Web socket interaction and long polling due to its ability to scale to manage vast numbers of connections Tornado is a networking library that works as a nonblocking web server and web application framework. It’s known for its high performance and scalability and was initially developed for friendfeed, which was a real- time chat system that aggregated several social media sites. It closed down in April 2015 as its user numbers had declined steadily, but Tornado remainsasactiveandusefulasever. Flask flask.pocoo.org GOOD FOR: Creating full-featured RESTful APIs. Its ability to manage multiple routes and methods is very impressive Flask’s aim is to provide a set of commonly used components such as URL routing and templates. Flask will also work on controlling the request and response objects, all-in-all this means it is lightweight but is still a powerfulmicroframework. PyramiD pylonsproject.org GOOD FOR: Highly extensible and adaptable to any project requirement. Not a lightweight system either Heavily focused on documentation, Pyramid brings all the much needed basic support for most regular tasks. Pyramid is open source and also provides a great deal of extensibility – it comes with the powerful WerkzeugDebuggertoo. Frameworks Why? Let’s take a look at some of the frameworks available when developing Python web applications First released in 1991, companies like Google and NASA have been using Python for years Thanks to the introduction of the Web Server Gateway Interface (WSGI) in 2003, developing Python web apps for general web servers became a viable solution as opposed to restrictingthemto custom solutions. Python executables and installers are widely available from the official Python site at www.python.org. Mac OS X users can also benefit greatly from using Homebrew to install and manage their Python versions. Whilst OS X comes bundled with a version of Python, it has some potential drawbacks. Updating your OS may clear out any downloaded packages, and Apple’s implementation of the library differs greatly from the official release. Installing using Homebrew helps you to keep up to date and also means you get the Python package managerpipincluded. Once Python is installed the first package to download should be virtualenv using ‘pip install virtualenv’,which enablesyou to createproject- specific shell environments. You can run projects on separate versions of Python with separateproject-specificpackagesinstalled. Check out the detailed Hitchhiker’s Guide to Python for more information: docs.python- guide.org/en/latest.
  • 138.
    Web development 138 ThePython Book Create an API Let us explore the Flask microframework and build a simple yet powerful RESTful API with minimal code 01InstallFlask Create a new directory inside of which your project will live. Open a Terminal window and navigate to be inside your new directory. Create a new virtual environment for this project, placed inside a new directory called ‘venv’, and activate it. Once inside the new virtual shell, proceed to installing Flask using the ‘pip install Flask’ command. virtualenv venv . venv/bin/activate pip install Flask 04ConnecttoDatabase With the database path defined, we need a way to create connection to the database for the application to obtain data. Create a new method called ‘connet_db’ to manage this for us. As a method we can call it when we set up a prerequest hook shortly. This will return a new open connection using the database details set in the configuration object. def connect_db(): return sqlite3.connect(app. config[‘DATABASE’]) 07PopulatetheDatabase To populate the database you can now run the init_db inside an active python shell. To do so enter a shell by typing ‘python’ inside your environment, and then running the command below. Alternatively, you can use the sqlite3 command and pipe the schema.sql file into the database. # Importing the database using the init_db method python from index import init_db init_db() # Piping the schema using SQLite3 sqlite3 /tmp/api.db schema.sql 02CreateIndex Create a new file in the root of the project location called ‘index.py’. The sample API will use a SQLite database, so we need to import that module for use in the application. We’ll also import some core components from the Flask module to handle request management and response formatting as well as some other functions. The minimum import for a Flask applicationisFlaskitself. import sqlite3 from flask import Flask, request, g, redirect, url_for , render_template, abort, jsonify 05DatabaseSchema Our SQLite database will only contain one table. Create a new file called ‘schema.sql’ in the root of the project directory. This fill will contain the SQL commands required to create the table and populate it with some sample bootstrappeddata. drop table if exists posts; create table posts ( id integer primary key autoincrement, title text not null, text text not null ); insert into posts (title, text) values (‘First Entry’ , ‘This is some text’); insert into posts (title, text) values (‘Second Entry’ , ‘This is some more text’); insert into posts (title, text) values (‘Third Entry’ , ‘This is some more text (again)’); 08RequestDBConnection With the database created and populated we need to be able to ensure we have an open connection and close it accordingly when finished. Flask has some decorator methods to help us achieve this. The before_request() method will establish the connection and stores it in the g object for use throughout the request cycle. We can then close the connection after the cycle using the teardown_request() method. @app.before_request def before_request(): g.db = connect_db(); 03DeclareConfig For a small application we can declare configuration options as upper-casename value pairsinsidethemainmodule,whichwe’lldonow. Here we can define the path and name of the SQLite database and also set the Flask debug output to True for development work. Initialise the Flask application to a namespace and then import the config values set directly above it. We then run the application. All routes must be placedabovetheselasttwolines. # Config DATABASE = ‘ /tmp/api.db’ DEBUG = True app = Flask(__name__) app.config.from_object(__name__) # Add methods and routes here if __name__ == ‘__main__’ : app.run() 06InstantiatetheDatabase To populate the database with the new table and any associated data, we will need to import and apply the schema to the database. Add a new module import at the top of the project file to obtain the ‘contextlib.closing()’ method. What we will do next is create a method that will initialise the database by reading the contents of schema.sql and executing it against theopendatabase. from contextlib import closing def init_db(): with closing(connect_db()) as db: with app.open_resource(‘schema.sql’ , mode=’r’) as f: db.cursor().executescript(f.read()) db.commit() “World-renowned image sharing service Instagram and social pin board Pinterest have also implemented Python as part of their web stack, opting for Django”
  • 139.
    The Python Book139 Python in the wild Interested in Python development? You’d be in good company with big names currently using it 10TemplateOutput Flask expects templates to be available within the templates directory in the root of the project, so make sure that you create that directory now. Next, add a new file called ‘show_posts.html’. The dynamic values are managed using Jinja2 template syntax, the default templating engine for Flask applications. Save this file in the templates directory. ul class=posts {% for post in posts %} lih2{{ post.title }}/h2{{ post. text|safe }} {% else %} liSorry, no post matches your request. {% endfor %} /ul 13Runtheapplication To run your Flask application, navigate using the active Terminal window into the root of the project. Ensuring you are in an active virtual environment Python shell, enter the command to run the main index file. The built-in server will start and the site will be accessible in the browser on default port local address http://127.0.0.1:5000. python index.py 11MakeanAPIResponse To create an API response we can define a new route with a specific API endpoint. Once again, we query the database for all posts. The data is then returned as JSON, using the JSONify method to do so. We can add specific values such as post count and a custom message if you wish, as well as the actual posts variable, formatted as JSON. @app.route(‘ /api/v1/posts/’ , methods=[‘GET’]) def show_entries(): cur = g.db.execute(‘select title, text from posts order by id desc’) 14APIJSONOutput The root of the application will render the template we previously created. Multiple routes can be generated to create a rich web application. Visiting an API-specific URL in the browser will return the requested data as cleanly formatted JSON. The ability to define custom routes like a versioned RESTful endpoint isincrediblypowerful. 09DisplayPosts Create your first route so that we can return and display all available posts. To query the database we execute a SQL statement against the stored db connection. The results are then mapped to values using Python’s dict method and saved as the posts variable. To render a template we then call render_ template() and pass in the file name and the variable to display as the second argument. Multiple variables can be passed through as a comma-separatedlist. @app.route(‘ /’) def get_posts(): cur = g.db.execute(‘select title, text from posts order by id desc’) posts = [dict(title=row[0], text=row[1]) for row in cur .fetchall()] return render_template(‘show_posts. html’ , posts=posts) 12GetaspecificPost CTo obtain a specific post from the API we need to create a new route, which will accept a dynamic value as part of the URI. We can also choose to use this route for multiple request methods, which are in this case GET and DELETE. We can determine the method by checking the request.method value and run it against a conditional if/else statement. @app.route(‘ /api/v1/posts/int:post_id’ , methods=[‘GET’ , ‘DELETE’]) def single_post(post_id): method = request.method if method == ‘GET’ : cur = g.db.execute(‘select title, text from posts where id =?’ , [post_id]) posts = [dict(title=row[0], text=row[1]) for row in cur .fetchall()] return jsonify({‘count’ : len(posts), ‘posts’ : posts}) elif method == ‘DELETE’ : g.db.execute(‘delete from posts where id = ?’ , [post_id]) return jsonify({‘status’ : ‘Post deleted’}) @app.teardown_request def teardown_request(exception): db = getattr(g, ‘db’ , None) if db is not None: db.close() posts = [dict(title=row[0], text=row[1]) for row in ur .fetchall()] return jsonify({‘count’ : len(posts), ‘posts’ : posts}) Disqus, the popular social interaction comment service provider, has been implementing their production applications in Python for a very long time. Python’s benefit for the development team was its ability to scale effectively and cater for a large number of consumers whilst also providing an effective underlying API for internal and external use. The company are now starting to run some production apps in Go, but the majority of code still runs on Python. World-renowned image sharing service Instagram and social pin board Pinterest have also implemented Python as part of their web stack, opting for Django to assist with the functionality and ability to cater for the many thousands of content views andrequestsmadetotheirservices. Mozilla, Atlassian’s Bitbucket repository service, and popular satire site The Onion have all been noted as using Django for their products.
  • 140.
    Web development 140 ThePython Book Django application development Django is a full Python web-app framework with impressive command-line tools 01CreateVirtualEnvironment Create a new directory for your project and navigate inside it using a new Terminal window. Create a new virtual environment for this project, opting to use the latest Python 3. Your Python 3 location may vary, so be sure to set the correct path for the binary package. virtualenv -p /usr/local/bin/python3 venv 01Generatethemodel Open blog/models.py and create the first model class, providing the property names and types for each. You can dig deeper into field types via the docs here: bit.ly/1yln1kn. Once complete, open myblog/settings.py and add the blog app to the list of allowed installed applications so that the project will load it. # blog/models.py class Post(models.Model): title = models.CharField(max_ length=200) text = models.TextField() # myblog/settings.py INSTALLED_APPS = (‘django.contrib.admin’ , ..., ‘django.contrib.staticfiles’ , ‘blog’) 04InitialMigration Navigate into the project directory via the Terminal window. Some of the installed apps included in the project generation require databasetables. Using the helper. run a migration command to create all of these automatically. The Terminal window will keep you informed of all of your progress and what has been applied from the migration. cd myblog python manage.py migrate 02ActivateandInstall Using your Terminal window, activate the virtual environment to start the project-specific shell. VirtualEnv has a local version of the Python package manager pip installed, so it’s fairly straight forward to run the command to install Django. . venv/bin.activate pip install Django 02DataMigration Any creation of models or changes to data need to be migrated. To do so we need to make migration files from the model data, which generate sequentially numbered files. Then we run a specific migration to generate the required SQL and the final migrate command performs the database execution. python manage.py makemigrations blog python manage.py sqlmigrate blog 0001 python manage.py migrate 05CreateApp Each Django project is made up of at least one application or module. Run the startapp command to create a new blog app module, which will generate the required code adjacent to the main project structure. python manage.py startapp blog 03CreateCoreProject The Django install contains some incredibly useful command-line tools, which will help you to run a number of repetitive and difficult tasks. Let’s use one of them to create a fresh project structure for us. Run the django- admin.pyscriptwiththenameoftheprojectthat youwantcreated. django-admin.py startproject myblog Installing Django Database models migration The installation of Django is relatively easy once you have python installed. See for yourself as we build a simple app here Django’s ability to manage the migration and maintenance of database schema and project models is very impressive
  • 141.
    Web development The PythonBook 141 01CreateAdminUser Django makes content administration incredibly easy and has an admin section available in a default project as standard at http://127.0.0.1:8000/admin. To log in you need to create a superuser account. Run the associated command and specify user details as required to then proceed and log in. python manage.py createsuperuser 04CreateaView With the admin interface accepting new submissions for our post class we’ll create a view page to display them. Open blog/views. py and import the Post class from the models. Create a method to obtain all posts from the databaseandoutputthemasastring. from django.http import HttpResponse from blog.models import Post def index(request): post_list = Post.objects.order_by(‘-id’) [ :5] output = ‘br /’ .join([p.title for p in post_list]) return HttpResponse(output) 02Switchonblogmanagement Having logged in to the administration interface you will be greeted with features to manage users and group roles and privileges, which alone are very powerful and provided for you by Django. There is not yet, however, any access to manage our blog posts so let’s turn thaton. 05ManagetheURLs Create ‘blog/urls.py’ and add the code to import the views that were just made in the module and the accompanying URL patterns. Open myblog/urls.py and add the URL function call to implement a new URL for the app to display the view. Visit http://127.0.0.1:5000/ blog in your browser to render the new view. # blog/urls.py from django.conf.urls import patterns,url from blog import views urlpatterns = patterns(‘’ , url(r’^$’ , views.index, name=’index’), ) # myblog/urls.py urlpatterns = patterns(‘’ , url(r’^blog/’ , include(‘blog.urls’)), url(r’^admin/’ , include(admin.site.urls)), ) 03EnableAdminManagement To enable our module and associated models to be managed through the admin interface, we need to register them with the admin module. Open blog/admin.py and then go on to import and register the models in turn (we only have one of these currently though). Save the file and refresh the admin site to see the poststhatarenowavailabletomanage. from django.contrib import admin # Register your models here. Autowiring the admin interface Hosting Python apps Admin sections can be problematic in their own right. Django provides an extensible admin interface for you Admin sections can be problematic in their own right. Django provides an extensible admin interface for you Herokuheroku.com This app is perhaps one of the most well- known cloud hosting providers. Their stack server environments support a number of core web app languages including Python as standard. Their unique Toolbelt command-line features and integration with Git repositories, as well as being incredibly quick and easy to scale and improve performance, makes them an obvious choice. A free account will let you run a Python web app on one dyno instance withoutanycost. PythonAnywhere www.pythonanywhere.com Another hosted option, and one created specifically for Python applications in general is Python Anywhere. The free basic option plan has enough weight and power behind it to get you up and running with a Python web app without having to scale, but as soon as your project gains traction, you can switch plans and boost yourplansperformance. It offers an incredibly impressive range of modules as standard, available to import into your application immediately to get you started, including Django and Flask should youneedthem. Using the dev server Django ships with a very helpful built-in development server, which will help you out by autocompiling and reloading after you have completed all of your file changes. All you have to do to start the server is to run the ‘python manage.py runserver’ command from your Terminal window within the project directory.
  • 142.
    142 The PythonBook Web development CreateadynamicwebpagewithTwitterandFlask’srendering engine,Jinja2 Creatingdynamictemplates withFlask,Jinja2andTwitter Python and Flask are a great combination when you’re looking to handle the Twitter OAuth process and build requests to obtain tokens. We’ve used Twitter here because of the large amount of easily digestible data available on it. However, since Twitter adheres to the standards set out by OAuth 1.0, the code we’ve used to sign and build requests can be modified to work with any third-party API using the same standard without a great deal of work. For years PHP has been a mainstay of template generation, but now with well- documented frameworks such as Flask, Sinatra and Handlebars, the ability to use powerful scripting languages greatly improves our ability to make great web services. Here, we’re going to use Python, Flask and its templating engine to display tweets. Flask comes with the super-nifty Jinja2 templating engine, If you’re familiar with Node.js or front- end JavaScript, the syntax will look very similar to the Handlebars rendering engine. But, before we dive into that, we need to organise some of the example code that we’re using for this. Resources Python2.7+ Flask0.10.0:flask.pocoo.org FlaskGitHub: github.com/mitsuhiko/flask ATwitteraccount Yourfavouritetexteditor CodedownloadedfromFileSilo 01Rearrangingourcode Server code can get messy and unmaintainable quickly, so the first thing we’re going to do is move our helper functions to another file and import them into our project, much like you would a module. This way, it will be clear which functions are our server logic and endpoints and which are generic Python functions. Open the TwitterAuthentication file downloaded from FileSilo (stored under Twitter OAuth files) and locate the getParameters, sign_request and create_oauth_headers functions. Cut and paste them into a new file called helpers.py in the root of your project folder. At the top of this file we want to import some libraries. import urllib, collections, hmac, binascii, time, random, string from hashlib import sha1 Now we can head back over to server.py and import the helper functions back into our project. We do this by simply calling import 02server.pymodules With a lot of the modules needed in this project having been moved to helpers.py, we can now remove most of them from server.py. If we amend our first import statement to be… import urllib2, time, random, json …our project will continue to function as it did before. Note the addition of the json module: QThetemplateusesalooptogeneratealistofTwittertweets helpers. Because Python is smart, It will look in the current directory for a helpers.py file before it looks for a system module. Now every function included in helpers.py is accessible to our project. All we need to do to call them is prepend our the methods we called before with helper.function_name and it will execute. For sign_request, we’ll need to pass our oauth_secret and consumer_secret for each request rather than accessing it from the session.Adjustthefunctiondeclarationlikeso: def sign_request(parameters, method, baseURL, consumer_secret, oauth_secret):
  • 143.
    The Python Book143 Web development QTheBSD-licensedFlaskiseasytosetup anduse–checkoutthewebsiteformoreinfo we’ll be using that later as we start handling Twitter data. Having Flask use a rendering engine is super-simple. Flask comes packaged with the Jinja2 template rendering engine, so we’ve nothing to install – we just need to import the package into the project. We can do this by adding render_template to the end of our from flask import […]statement. 03Ourfirsttemplate Now that we have a rendering engine, we need to create some templates for it to use. In the root of our project’s folder, create a new folder called templates. Whenever we try to render a template, Flask will look in this folder for the template specified. To get to grips with templating, we’ll rewrite some of our authentication logic to use a template, rather than manually requesting endpoints. In templates, create an index.html file. You can treat this HTML file like any other – included in the resources for this tutorial is an index.html that includes all of the necessary head tags and !DOCTYPEdeclarationsforthisfile. 04Renderingourtemplate In server.py, let’s create a route for ‘/’ to handletheauthorisationprocess. @app.route(‘ /’) def home(): if not ‘oauth_token’ in session: session.clear() session[‘oauth_secret’] = ‘’ session[‘oauth_token’] = ‘’ return render_template(‘index.html’) It’s a simple function: all we want to do is check whether or not we have an oauth_token already and create those properties in the Flask session so we don’t throw an error if we try to access it erroneously. In order to send our generated template in response to the request, we return render_template(‘index.html’). {% if session[‘oauth_token’] != “” %} h1Already Authorised/h1 div class=”dialog” pHello, You’ve authenticated!brLet’s a href=” /get-tweets”get some tweets/a/p /div {% else %} h1Authorisation required/h1 div class=”dialog” pWe need to a href=”/authenticate”authenticate/a/p /div {% endif %} Fig 01 05Templatevariables We can choose to send variables to our template with render_template(‘index.htm’ , variableOne=value, variableTwo=Value) but inthisinstancewedon’tneedtoaseachtemplate hasaccesstotherequestandsessionvariables. Codeon FileSilo
  • 144.
    144 The PythonBook Web development Nowweknowhowtobuildtemplates, let’sgrabsometweetstodisplay 09Checkingoursession and buildingour request Before we start grabbing tweets, we want to run a quick check to make sure we have the necessary credentials and if not, redirect the user back the authorisation flow. We can do this by having Flask respond to the request with a redirection header, like so: if session[‘oauth_token’] == “” or session[‘oauth_secret’] == “”: return redirect(rootURL) Assuming we have all we need, we can start to build the parameters for our request (Fig 02). You’ll notice that the nonce value is different from that in our previous requests. Where the nonce value in our authenticate and authorise requests can be any random arrangement of characters that uniquely identify the request, for all subsequent requests the nonce needs to be a 32-character hexadecimal string using only the characters a-f. If we add the following function to our helpers.py file, we can quickly build one for each request. def nonce(size=32, chars=”abcdef” + string.digits): return ‘’ .join(random.choice (chars) for x in range(size)) 10Signingandsendingourrequest We’ve built our parameters, So let’s sign our request and then add the signature to the parameters (Fig 03). Before we create the authorisation headers, we need to remove the count and user_id values from the tweetRequestParams dictionary, otherwise the signature we just created won’t be valid for the request. We can achieve this with the del keyword. Unlike our token requests, this request is a GET request, so instead of including the parameters in the request body, we define them as query parameters. ?count=tweetRequestParams[‘count’] user_id=tweetRequestParams[‘user_id’] 11HandlingTwitter’sresponse Now we’re ready to fire off the request and we should get a JSON response back from Twitter. This is where we’ll use the json module we imported earlier. By using the json.loads function, we can parse the JSON into a dictionary that we can access and we’ll pass through to our tweets.html template. tweetResponse = json. loads(httpResponse.read()) return render_template(‘tweets.html’ , data=tweetResponse) Whereas before, we accessed the session to get data into our template, this time we’re explicitly passing a value through to our template. 12Displayingourtweets Let’s create that template now, exactly the same as index.html but this time, instead of using a conditional, we’re going to create a loop togeneratealistoftweetswe’vereceived. First, we check that we actually received some data from our request to Twitter. If we’ve got something to render, we’re ready to work through it, otherwise we’ll just print that we didn’tgetanything. Once again, any template logic that we want to use to generate our page is included between 08Let’sgetsometweets Sonowweknowhowtobuildtemplates, let’s grab some tweets to display. In server.py defineanewroute,get-tweets,likeso: @app.route(‘/get-tweets’) @app.route(‘/get-tweets/count’) def getTweets(count=0): You’ll notice that unlike our other authentication endpoints, we’ve made two declarations. The first is a standard route definition: it will 07Staticfiles Pretty much every webpage uses JavaScript, CSS and images, but where do we keep them? With Flask we can define a folder for use with static content. For Flask, we create a static folder in the root of our project and access files by calling /static/css/styles.css or /static/js/core.js.Thetutorialresourcesincludea CSSfileforstylingthisproject. Open index.html. All code executed in a Flask template is contained within {% %}. As this is our homepage, we want to direct users accordingly, Solet’scheckifwe’vegotanaccesstoken(Fig01). Between the ifs and else of the template is standard HTML. If we want to include some data – for example, the access token – we can just add {{ session[‘oauth_token’] }} in the HTML and it will be rendered in the page. Previously, in our / authorised endpoint, we would display the OAuth token that we received from Twitter; however, now thatwehaveatemplate,wecanredirectourusers backourrootURLandhaveapagerenderedforus thatexplainstheprogresswe’vemade. 06Getting lost (andthenfoundagain) With every server, some things get misplaced or peoplegetlost.Sohowdowehandlethis?Rather than defining a route, we can define a handler thatdealswithgettinglost. @app.errorhandler(404) def fourOhFour(error): return render_template(‘fourohfour .html’) If a page or endpoint is requested and triggers a 404, then the fourOhFour function will be fired. In this case, we’ll generate a template that tells the user,butwecouldalsoredirecttoanotherpageor dumptheerrormessage. intercept and handle the path get-tweets. The second lets us define a parameter that we can use as a value in our getTweets function. By including count=0 in our function declaration, we ensure that there will always be a default value when the function is executed; this way we don’t have to check the value is present before we access it. If a value is included in the URL, it will override the value in the function. The string inside the variable name determines the name of the variable. If you want the variable passed to the function to have a specific type, you can include a converter with the variable name. For example, if we wanted to make sure that count was always an integer instead of a floatorstring,we’ddefineourroutelikeso: @app.route(‘/get-tweets/int:count’)
  • 145.
    The Python Book145 Web development tweetRequestParams = { “oauth_consumer_key” : consumer_key, “oauth_nonce” : helpers.nonce(32), “oauth_signature_method” : “HMAC-SHA1”, “oauth_timestamp” : int(time.time()), “oauth_version” : “1.0”, “oauth_token” : session[‚Äòoauth_token’], “user_id” : session[‘user_id’], “count” : str(count) } Fig 02 tweetRequest = helpers.sign_request(tweetRequestParams, “GET”, “https://api.twitter .com/1.1/statuses/user_timeline.json”, consumer_secret, session[‘oauth_secret’]) tweetRequestParams[“oauth_signature”] = tweetRequest makeRequest=urllib2.Request(“https://api.twitter .com/1.1/statuses/ user_timeline.json?count=” + tweetRequestParams[‘count’] + “user_id=” + tweetRequestParams[‘user_id’]) del tweetRequestParams[‘user_id’], tweetRequestParams[‘count’] makeRequest.add_header(“Authorization”, helpers.create_oauth_ headers(tweetRequestParams)) try: httpResponse = urllib2.urlopen(makeRequest) except urllib2.HTTPError , e: return e.read() Fig 03 {% if data %} ul id=”tweets” {% for tweet in data %} li div class=”image” img src=”{{ tweet[‘user’][‘profile_image_url_https’] }}” alt=”User Profile Picture” /div div class=”text” a{{ tweet[‘text’]|forceescape }}/a /div /li {% endfor %} /ul {% else %} pWe didn’t get any tweets :(/p {% endif %} Fig 04 13Flaskfilters Sometimes, when parsing from JSON, Python can generate erroneous characters that don’t render particularly well in HTML. You may notice that after tweet[‘text’] there is |forceescape, This is an example of a Flask filter; it allows us to effect the input before we render – in this case it’s escaping our values for us. There are many, many different built- in filters that come included with Flask. Your advisor recommends a full reading of all the potential options. 14Wrappingup That’s pretty much it for templating with Flask. As we’ve seen, it’s insanely quick and easy to build and deploy dynamic sites. Flask is great tool for any Python developer looking to run a web service. Although we’ve used Twitter to demonstrate Flask’s power, all of the techniques described can be used with any third-party service or database resource. Flask can work with other rendering engines, such as Handlebars (which is superb), but Jinja2 still needs to be present to run Flask and conflicts can occur between the two engines. With such great integration between Flask and Jinja2, it makes little sense to use another engine outside of very specific circumstances. {% %}. This time we’re creating a loop; inside the loopwe’llbeabletoaccessanypropertywehave of that object and print it out. In this template we’re going to create an li element for each tweet we received and display the user’s profile pictureandtextofthetweet(Fig 04). In our template we can access properties using either dot notation (.) or with square brackets ([]). They behave largely the same; the [] notation will check for an attribute on the dictionary or object defined whereas the . notation will look for an item with the same name. If either cannot find the parameter specified, it will return undefined. If this occurs, the template will not throw an error, it will simply print an empty string. Keep this in mind if your template does not render the expected data: you’ve probably just mis-defined the property you’retryingtoaccess. Unlike traditional Python, we need to tell the template where the for loop and if/ else statements end, so we do that with {%endfor %}and{% endif %}.
  • 146.
    146 The PythonBook Web development Djangoisofcourseable toreadandwritetoSQL databases,butitneeds verylittlepriorknowledge tosucceedindoingso Djangocomeswitha lightweightdevelopment serversoyoucantestall yourworklocally Learnhowtousethisextremelypowerful Python-basedwebframeworktocreatea completeblogfromscratchinrecordtime Buildyourownblog withDjango Creating your own blog always feels like a great accomplishment. Sure, you could use the fantastic WordPress if you need a complete blog witheveryfeatureyou’deverneedrightnow.And Tumblr exists for people who just want to write something,orpostpicturesofcorgisinspace. You don’t have full control from start to finish with a prefabricated blog, though, and neither of these is written in the fantastic Django. Django is of course based on Python, the object- orientated programming language designed to have clearly readable syntax. Due to its Python base, it’s an incredibly powerful and simple-to- use language for web development with a vast arrayofapplications. So let’s use it to make a blog. In this first section of the process we will explore how to set up Django, writing and reading to a database, creating a front- and back-end, and some interactionswithHTML. Resources PythonSourceCode www.python.org/download/releases/2.7.2 DjangoSourceCode www.djangoproject.com/download UsingHTMLandCSSin conjunctionwithDjangois clearandstraightforward; it’smucheasiertobug-fix thanPHP Djangocomeswith agenericback-end sitethatissetupin seconds,andeasily customisableafterthat
  • 147.
    The Python Book147 Web development 01InstallPython Django is based on Python, and requires it to be installed to develop on. Python 2.7 is the recommended version, and this is installed with the python package. If you want to check your version, start the Python shell by typing ‘python’ intotheterminal. 02InstallDjango Most operating systems will have a Django package available in the repository, like python-django in Debian. The Django website has a list if you have trouble finding it, or you could build it from source. Make sure you install version1.3. 03VerifyyourDjango To make sure Django installed properly, and that you have the right version, enter the Python shell by typing ‘python’ and enter thefollowing: import django print django.get_version() It will return a version number if it has installed correctly,whichshouldbe1.3. 04Startanewproject In the terminal, cd to the folder you want to develop the blog in, and then run the nextcommand: django-admin startproject myblog Here, ‘myblog’ can be replaced by whatever you wish to name the project, but we’ll use it for the upcomingexamples. 05Startthedevelopmentserver Django comes with a lightweight development server to test out work locally. We can also use it to check our work, so cd to the myblogfolderandthenuse: python manage.py runserver If all goes well, it should return zero errors. Use Ctrl+Ctoexittheserver. 06Configurethedatabase The database settings are kept in the settings.py file. Open it up with your favourite editor and go to the Databases section. Change ENGINEto: ‘ENGINE’: ‘django.db.backends.sqlite3’, And in NAME, put the absolute path – for example: ‘NAME’: ‘/home/user/projects/myblog/ sqlite.db’, Saveandexit. 07Createthedatabase The database file will be generated by usingthecommand: python manage.py syncdb During the creation, it will ask you to set up a superuser,whichyoucandonow. The SQLite database file will be created in yourmyblogfolder.
  • 148.
    148 The PythonBook Web development Youdon’thavefullcontrolfromstart tofinishwithaprefabricatedblog–but youwillwithDjango 08Createyourblog Nowit’stimetocreateablogappinyour project.Type: python manage.py startapp blog This creates the models file which is where all your data lives. You can change ‘blog’ to another name,butwe’lluseitinourexamples. 09Startyourblogmodel We can now take the first steps in creating our blog model. Open models.py and changeitsoitsaysthefollowing: from django.db import models class Post(models.Model): post = models.TextField() This creates the Post class, which has a subclassthatcontainsyourblogtext. 10Customiseyourblog Let’snowexpandtheblogmodelabitso itresemblesamoreclassicblog: class Post(models.Model): post = models.TextField() title = models.TextField() author = models.CharField(max_ length=50) pub_date = models.DateTimeField() A CharField needs to have a character limit defined, and DateTimeField holds the timevalues. 11Installyourapp Your app needs to be installed to your project, which is very simple. Open the settings. py file again, go to the INSTALLED_APPS section andadd: ‘blog’, Then run the following to create the database tables: python manage.py sql blog Andfinally: python manage.py syncdb 12Setuptopost Now we can create a post and test out ourcode.Firstthough,enterthePythonshell: python manage.py shell Then execute these commands to add all the necessaryfieldsanddata: from blog.models import Post import datetime 13Let’sblog Create the post. For this example, we willcallittest_post: test_post = Post() Nowlet’saddtheblogcontent: test_post.post = ‘Hello World!’ test_post.title = ‘First Post’ test_post.author = ‘Me’ test_post.pub_date = datetime. datetime.now() Andthensaveitwith: test_post.save() 14Startthesiteback-end To create the admin site, edit urls.py from the myblog directory, and uncomment or addthefollowinglines: from django.contrib import admin admin.autodiscover() url(r’^admin/’, include(admin.site. urls)), Save and exit, then edit settings.py and uncommentthislinefromINSTALLED_APPS: ‘django.contrib.admin’, Theadminsiteisnowat127.0.0.1:8000/admin/.
  • 149.
    The Python Book149 Web development 24Afunctionalblog So there you have it! Navigating to 127.0.0.1:8000/admin/ or 127.0.0.1:8000/myblog/ will show off the fine work you’ve created. Django is dead easy to use once you know how, and there are plenty of tweaks you should be abletomakeafterthistutorial. 15Setuptheadminpage The admin page has a generic, usable template, but you need to configure it to view, edit, create and delete posts. First, create a new fileadmin.pyintheblogdirectoryandenter: from blog.models import Post from django.contrib import admin admin.site.register(Post) To have the posts display nicely on the site, edit models.pyandadd: class Post (models.Model): … def __unicode__(self): return self.title Save,andrun: python manage.py syncdb The admin page is now usable! You should be able to see the other posts, and it’s now a lot easiertoaddmore. 16Activatethefront-end Open up urls.py from the myblog directory in your editor and add the following to theurlpatternssection: url(r’^myblog/’, ‘blog.urls.index’)), One of the examples in the file can be uncommented and edited to this as well. It pointstoamodelwewillnowcreate. 17Createanotherurlsfile You need to create another urls file in the app directory, in our case blog/urls.py. Create it andaddthefollowing: from django.template import Context, loader from blog.models import Post from django.http import HttpResponse def index(request): post_list = Post.objects.all() t = loader.get_template(‘blog/ index.html’) c = Context({ ‘post_list’: poll_list, }) return HttpResponse(t.render(c)) 18Startthetemplate The code we’ve just written looks for a template that currently doesn’t exist. We first need to tell Django where templates are to be lookedforinsettings.py: TEMPLATE_DIRS = ( ‘/home/user/projects/templates’, ) You can put the template directory wherever you want,aslongasit’sreferencedhere. 19Writeatemplate Now to write the site template. In our example,we’reusingindex.html: {% for post in post_list %} {{ post.title }} {{ post.author }} {{ post.pub_date }} {{ post.post }} {% endfor %} This needs to be located in a folder with the same name as your app within the templatedirectory. 20Viewyourhandiwork Let’s make sure this worked. Start the developerserverwith: python manage.py runserver Andnavigateto127.0.0.1:8000/myblog/. It’s not pretty, but you should have successfully called upon your stored data. We’ll spendthenextstepstidyingitupabit. 21Formatthefrontpage Go back into the template file, index.html,andaddthefollowinghtmltags: {% for post in post_list %} h2{{ post.title }}/h2 {{ post.author }} on {{ post.pub_ date }} p{{ post.post }}/p {% endfor %} This is just an example – the post can be in any orderwithanytags. 22Spruceuptheadminlist We’ll do this in the admin.py file in our blog directory; open it in your editor and make thefollowingchanges: from blog.models import Post from django.contrib import admin class Admin(admin.ModelAdmin): list_display = [‘title’, ‘author’, ‘pub_date’] admin.site.register(Post, Admin) Inthiscase‘list_display’isafixedvariablename. 23Alogicalpostpage The new post page on the site might not be in an order you’re comfortable with. We’ll change that now in admin.py with the followingadditions: class Admin(admin.ModelAdmin): list_display = [‘title’, ‘author’, ‘pub_date’] fields = [‘title’, ‘pub_date’, ‘author’, ‘post’] admin.site.register(Post, Admin) Remembertosave! Djangoisanincrediblypowerful andsimple-to-uselanguage
  • 150.
    150 The PythonBook Web development Wecontinuebuildinganawesomeblogusingthepowerful Djangoframework,andthistutorialisallaboutthefront-end contentdelivery Delivercontenttoyourblog In the last tutorial we began to build the most basic of blogs, and learned how to use a bit of Djangointheprocess. We can now set up a new project, create a database and write basic code Withminimalextracode, ourtemplatecandisplay themontharchivefrom thesidebar Djangohasbuilt-incodeto dealwithpaginationvery cleanlyandeffectively Allowyourreadersto giveyoufeedback,and moderatetheminthe adminpanel WithDjangowecanmake simplesidebarsthatlist archivesbymonth to read and write to the database. All simple stuff, but of course it’s core to building websites whereDjangomightbecalledupon. Here we will give the front end of the site an overhaul, making it more of the standard you would expect from a modern blog. This will include a sidebar, pages, post pages and the ability to add and moderate comments. In the process we will learn some more of the benefits that come with using Django to developwebsites. You should keep using Django 1.3 for this tutorial,aswedidbefore. Resources Pythonbase: http://www.python.org/download/ Djangosource:https://www. djangoproject.com/download/
  • 151.
    The Python Book151 Web development 08Pleaseturnover Now we need to add the navigation links to the blog, so open the index template forediting: {% if post_list.has_previous %} a href=”?list_page={{ post_list. previous_page_number }}”Newer /a {% endif %} {% if post_list.has_next %} a href=”?list_page={{ post_list. next_page_number }}” Older/a {% endif %} 01Newblogorder We left off last time with the blog displaying posts in chronological order, which isn’t very helpful to readers. To correct this, open up urls.py in the blog folder and edit the followingline: post_list = Post.objects.all().order_ by(“-pub-date”) This makes sure that posts are displayed in reverseorder(newestfirst). 02Aviewtoapage You’ll want to be able to link specific pages, of course, and to do that we first have to define what goes into these pages in the urls.py fileintheblogfolder: def post_page(request, post_id): post_page = Post.objects. get(pk=post_id) return render_to_response(‘blog/ post.html’, {‘post_page’: post_page}) 03Cleanupyourcode You may notice that we used a different return command to the index definition – this is a shortcut that makes writing the code a bit easier.Togetitworking,add: from django.shortcuts import render_to_ response We recommend that you edit the index code to matchpost_page. 04EditURLs In urls.py in myblog we need to make some additions and modifications for the websitetodirecttothepostcorrectly: url(r’^myblog/$’, ‘blog.urls.index’), url(r’^myblog/(?Ppost_idd+)/$’, ‘blog.urls.post_page’), The post_id is the number of the post, which is auto-generated. The ‘$’ is important to make the redirectionwork. 05Aposttemplate We told the post_page to point towards a template we now need to create. In the same location as index.html, create post.html with the followingformattingtoresemblethefrontpage: h2{{ post_page.title }}/h2 {{ post_page.author }} on {{ post_page. pub_date }} p{{ post_page.post }}/p 06Linktothepage Let’s get these links working from the mainpage.Openuptheindex.htmlfileandmake thefollowingchange: h2a href=/myblog/{{ post.pk }}{{ post.title }}/a/h2 This is a very simple addition using an absolute link, and requires no fiddling with the views ormodel. 07Pagination To get blog posts split up over pages, we need to make some additions to urls.py in the blogfolder: post_list = Post.objects.all().order_ by(“-pub_date”) paginator = Paginator(post_list, 3) try: list_page = request.GET.get(“list_ page”, ‘1’) except ValueError: list_page = 1 post_list = paginator.page(list_page) return render_to_response(‘blog/index. html’, {‘post_list’: post_list})
  • 152.
    152 The PythonBook Web development Weneedtobeabletoprocessthe dataandmetadataintheforms 09Wrongpage Let’s add a quick bit of code to return somebody to the previous page if they get the URLwrong: from django.core.paginator import Paginator, EmptyPage, InvalidPage try: post_list = paginator.page(list_ page) except (EmptyPage, InvalidPage): post_list = paginator. page(paginator.num_pages) The last part replaces ‘post_list = paginator. page(list_page)’. 10Haveyoursay Everyone has their opinion on the internet. You can give your readers the ability to comment,andwe’llstartbyeditingmodels.py: class Comment(models.Model): author = models.CharField(max_ length=50) text = models.TextField() post = models.ForeignKey(Post) def __unicode__(self): return (self.post, self.text) We’ve made it so they can put their name with acomment. 11Backtothecomment We now need to add a small line to the urls.py file in myblog so the comment can be postedthensentbacktotheoriginalpage: url(r’^myblog/add_comment/(d+)/$’, ‘blog.urls.add_comment’), This URL pattern calls the ID of the page that you’reon. 12Formacomment We need to be able to process the data and metadata in the forms, so let’s add a class to urls.py in the blog folder with the followingadditions: from django.forms import ModelForm from blog.models import Post, Comment class CommentForm(ModelForm): class Meta: model = Comment exclude = [‘post’] 13Inthepost We need to attribute the comments to the post they’re being made on, so update the post_pagedefinition: from django.core.context_processors import csrf def post_page(request, post_id): post_page = Post.objects. get(pk=post_id) comments = Comment.objects. filter(post=post_page) d = dict(post_page=post_page, comments=comments, form=CommentForm()) d.update(csrf(request)) return render_to_response(‘blog/ post.html’, d) The CSRF tag is to prevent cross-site requestforgery. 14Commenttemplate Let’s get the post page ready for commentsbyaddingthistopost.html: pComments:/p {% for comment in comments %} {{ comment.author }} p{{ comment.text }}/p {% endfor %} strongAdd comment/strong form action=”{% url blog.urls. add_comment post_page.id %}” method=”POST”{% csrf_token %} Name {{ form.author }} p{{ form.text }}/p input type=”submit” value=”Submit” /form 15Defineyourcomments The final step is defining the comments inblog/urls.py,andit’sabigone: def add_comment(request, comment_id): p = request.POST if p.has_key(‘text’) and p[‘text’]: author = ‘Anonymous’ if p[‘author’]: author = p[‘author’] comment = Comment(post=Post. objects.get(pk=comment_id)) cf = CommentForm(p, instance=comment) cf.fields[‘author’].required = False comment = cf.save(commit=False) comment.author = author comment.save() return HttpResponseRedirect(reverse (‘blog.urls.post_page’, args=[comment_ id])) This ensures text has been entered, and if not specified author is ‘Anonymous’. Before testing, runsyncdbsocommenttablescanbecreated.
  • 153.
    The Python Book153 Web development 16Administrate Like the posts, we can get the Admin page to see comments. Start editing blogs/admin.pytogetthisfeatureadded: from blog.models import Post, Comment from django.contrib import admin class PostAdmin(admin.ModelAdmin): list_display = [‘title’, ‘author’, ‘pub_date’] fields = [‘title’, ‘pub_date’, ‘author’, ‘post’] admin.site.register(Post, PostAdmin) 17Comment-specificadminfeatures Now we can add the comment-specific adminfeatureswithoutcausinganyclashes: class CommentAdmin(admin.ModelAdmin): list_display = [‘text’, ‘author’, ‘post’] admin.site.register(Comment, CommentAdmin) This will show the comments on the admin site, and you can see the comment, the author and thepostit’sconnectedto. 18Sidebarbeginnings Django makes it pretty easy to order posts by years and months, but first we need to importsomenewmodelsintoblog/urls.py: import time from calendar import month_name We’re going to define two new functions, month_timelineandmonth,tomakethesidebar. 19Starttodefinemonth_timeline First we need to get all the information fromtheposts: def month_timeline(): year, month = time.localtime()[:2] begin = Post.objects.order_by(‘pub_ date’)[0] month_begin = begin.pub_date.month year_begin = begin.pub_date.year month_list = [] The ‘[:2]’ makes sure we only get the time informationweneed. 20Finishyourdefinition Now we will order the posts by month andyearstartingfromourfirstmonth. for y in range(year, year_begin-1, -1): start, end = 12, 0 if y == year: start = month if y == year_begin: end = month_ begin-1 for m in range(start, end, -1): month_list.append((y, m, month_name[m])) return month_list 21Returntoreader With the list organised, we can now definemonthsowecandisplayitontheblog: def month(request, year, month): post_list = Post.objects. filter(pub_date__year=year, pub_date__ month=month) return render_to_response(‘blog/ index.html’, dict(sidebar_list=post_ list, month_list=month_timeline())) Nowweneedtolinkituptotheindextemplate. 22Finaliseyoursidebardefinition Edit the return command on the index functiontoincludethesidebarinformation: return render_to_response(‘blog/index. html’, dict(post_list=post_list, sidebar_list=post_list.object_list, month_list=month_timeline())) Then add this line to urls.py in myblog so a monthpagecanberendered: url(r’^myblog/month/(d+)/(d+)/$’, ‘blog.urls.month’), All we need to do now is display the information onthesite. 24Sidebarfinale Obviously it’s not at the side right now – that’s a job for the HTML and CSS. The info is there, though, and you can manipulate it any way you want. However, your blog is now a lot morefriendlytoyourreaders. 23Sidebarontheweb Go to the index template. First of all, changethefirstlineofthepostforloopto: {% for post in sidebar_list %} Simple enough. Now we need to add the sidebarinformation: {% for month in month_list %} pa href=”{% url blog.urls.month month.0 month.1 %}”{{ month.2 }}/a/ p {% endfor %}
  • 154.
    154 The PythonBook Web development 01Summarise On a normal blog we’re going to have much longer articles. We can generate a summary of each of these on the index page templatelikeso: p{{ post.post|truncatewords:3 }}/p This automatically takes the first three words ofthepost–ofcourse,youcanuseanynumber. Toaddtotheprevioustutorials,we’ll coversomeofthemoreadvanced featuresyoucanutilisewiththe powerofDjango Enhanceyourblog withextrafeatures We’ve been building our Django blog to create and display posts, allow people to make comments, and filter posts by month like a classic blog sidebar. We still have a bit of a way to go until it looks and behaves more like a classicblog,though. In this tutorial, we’re going to add in summaries, excerpts, categories and finally an RSS feed. This allows us to look at a few things – firstly we’ll get a better understanding of cross- model referencing and how that works in the admin site. We will also go through how to make changes to the database, and how Django helps whencreatinganSQLquery. Finally, the RSS feed is part of a standard feed library in Django itself. We will learn how to import and use it to create a simple list of the latest entries that click through to the posts. By the end of the tutorial your Django blog will be finallyfinished! Resources Pythonbase: http://www.python.org/download/ Djangosource:https://www. djangoproject.com/download/ 03Writeanexcerpt Towritetheexcerpt,orappendittothe previousposts,we’llhavetoaddittotheadmin page.Openupadmin.pyandeditthefields sectionoftheAdminPostclasstoaddexcerpt: fields = [‘title’, ‘pub_date’, ‘author’, ‘post’, ‘excerpt’] 02Manualexcerpt Ifyoudon’twantanautomaticsummary, wecanaddanexcerptfieldtoourpostmodelso youcancraftonemanually: excerpt = models.TextField() Tolimitthecharactersinyourexcerpt,usea CharFieldlikeforourauthorsection. We’regoingtoaddsummaries, excerptsandanRSSfeed
  • 155.
    The Python Book155 Web development 04Excerptorsummary Youcanreplacethepostcontentinthe indextemplatewiththeexcerpt,butwecankeep itasabackupforiftheexcerptisempty: {% if post.excerpt %} p{{ post. excerpt }}/p {% else %} p{{ post. post|truncatewords:3 }}/p {% endif %} 05Databaseerror Ifyou’vedecidedtotestthechanges, 06Databasequery TheoutputwillshowyouwhattheSQL codeistoaddthemodelstothedatabase.We wanttoaddtheexcerptfieldspecifically,which shouldlooksomethinglikethis: “excerpt” text NOT NULL Makeanoteofit. 07Altertable Togetintothedatabaseshellandadd thefield,run:$ python manage.py dbshell ThenweneedtouseanALTERTABLEquery: 08Savethechanges We’veremovedNOTNULLaswe alreadyhaveentriesthatwon’thaveanexcerpt, andwanttomakeitsoanautosummarycanbe made.Savethechangeswith:COMMIT;andthen exittheshellwith:.quit 09Testitout Nowwecantestouttheexcerptcode– createanewpostoreditanexistingonetohave anexcerpt.Ifyou’vefollowedourstepscorrectly itshouldwork;ifnot,youmayneedtodoabitof bugfixing. Createand manageparent andchild categoriesasa separatefunction oftheblog Learnhowtoalter thedatabaseto createpostswith categories,and addthemto otherposts Haveautomaticsummariesormanually craftedexcerptsforyourblogposts Createcustom RSSfeedsusing built-inDjango functions you’llhavenoticedourwebserverhasstopped working.Thisisbecausethereisnoexcerpt columninourdatabase.Thereforeweneedto addtheexcerptcolumn.Tofindouthow,run: $ python manage.py sqlall blog ALTER TABLE “blog_post”. Andthenenterthecodewenoteddownlikeso: ADD “excerpt” text;
  • 156.
    156 The PythonBook Web development 10Categorymodel Wecanaddamodelforblogcategories: class Categories(models.Model): name = models.CharField(unique=True, max_length=200) slug = models. SlugField(unique=True, max_length=100) parent = models.ForeignKey(‘self’, blank=True, null=True, related_ name=’child’) def __unicode__(self): return (self.name) Thisallowsforparentandchildcategories. 11Administratecategories Wecanaddittotheadminsiteby creatingaCategoriessectioninadmin.py: class CategoriesAdmin(admin. ModelAdmin): list_display = [‘name’, ‘slug’, ‘parent’] fields = [‘name’, ‘slug’, ‘parent’] admin.site.register (Categories, CategoriesAdmin) Beforewecanmakecategories,though,we needtocreatethedatabasetable: $ python manage.py syncdb 12Categorisetheposts Similarlytowhatwedidwiththe 13Databasecategory Likebefore,we’llfindouttheSQLneeded toalterthetable:$ python manage.py sqlall blogWhichforourexamplereturnsasomewhat differentcodethanbefore:“category_id” integer NOT NULL REFERENCES “blog_ categories” (“id”)It’sanIDwe’regetting,not text,fromthecategoriestable. 14Altertable–part2 Againlet’senterthedatabaseshell: python manage.py dbshellWe’llcontinue muchlikebefore,butwiththenewcode:ALTER TABLE “blog_post” ADD “category_id” integer REFERENCES “blog_categories” (“id”);Andfinally,tosave:COMMIT; 15Administratecategories–part2 Nowwecangobacktoadmin.pyandaddthenewcategoryfieldstothePostAdminmodel: list_display = [‘title’, ‘author’, ‘pub_date’, ‘category’] fields = [‘title’, ‘pub_date’, ‘author’, ‘post’, ‘excerpt’, ‘category’]Ourpreviousblogpostswithno categoryhavedisappeared!Tofixthis,gobacktomodels.pyandmakethischangetothePostmodel: category = models.ForeignKey(Categories, blank=True, null=True)Sowecannowcreate categoriesseparately,assignthemtoposts,andviewpostswithoutacategory. 16Categorydisplay Asoururls.pyintheblogdirectorygets allthepostfields,totheindextemplatewejust add:pCategory: {{ post.category }}/ pAndtotheposttemplate:pCategory: {{ post_list.category }}/p 17Categorypage Firstweneedtodefineourcategoryin blog/urls.py.ImportCategoriesandthenadd: def blog_categories(request, category_ id): categories = Categories.objects. get(pk=category_id)Weneedthe category_idtocallthecorrespondingposts. comments,wewanttoaddaForeignKeyto thePostmodelsowecanattributeaposttoa category.Addthisline:category = models. ForeignKey(Categories) AndmoveCategoriestothetopofmodels.py. Wecannow createcategories separately
  • 157.
    The Python Book157 Web development 24RSSURLs Thefinalstepisaddingthefeed URLtourls.py:url(r’^myblog/feed/$’, BlogFeed()),Andnowyourblogisnowfully functional.Withabitmoretweakingand theming,youcangetitonlineandblogaway! 23RSSlinks Weneedtodefineitem_linkforthe feedsothatthefeeditemscanlinktotheright place.WehavetogivethecompleteURLand thepostIDforitwork:def item_link(self, post): link = “http://127.0.0.1:8000/ myblog/”+str(post.pk) return link 18Categorydefinition Finishthedefinitionbyusingtheparent_ idtofilterthecorrectPosts,thenrenderthe response:category_posts = Post.objects. filter(category=categories) return render_to_response(‘blog/categories. html’, dict(category_posts=category_ posts, categories=categories)) Againwe’recallinganewtemplatethatwe’ll constructshortly. 19CategoryURLs We’llcreatetheURLinurls.pyasforthe postpage,onlyit’llgivetheslugofthecategory insteadofanIDinthelink:url(r’^myblog/ category/(?Pcategory_idd+/$’, ‘blog. urls.blog_categories’), 20Categorytemplate We’llusesomethingsimilartotheIndex andPosttemplatetocreateacategorypage template:{% for post in category_posts %} h2a href=/myblog/{{ post.pk }}{{ post.title }}/a/h2 {{ post.author }} on {{ post.pub_date }} % if post. excerpt %} p{{ post.excerpt }}/p {% else %} p{{ post.post|truncatewords:3 }}/p {% endif %} pCategory: {{ post.category }}/p {% endfor %} 21Categoryclickthrough Finally,let’smakethecategoriesclick throughtotherelevantpagebychangingthe 22RSS Djangohasabuilt-inRSSframework. Inblog/urls.pyadd:from django.contrib. syndication.views import Feed class BlogFeed(Feed): title = “Blog Feed” link = “/” def items(self): return Post. objects.order_by(“-pub_date”) def item_ title(self, post): return post.title categorydisplaytobe:pCategory: a href=/myblog/category/{{ categories.pk }}{{ post.category }}/a/pThiscango onthecategories,postandindextemplate. Finally,let’smakethecategories clickthroughtotherelevantpage
  • 158.
    Python 158 The PythonBook Use 160 Programming in Python on Raspberry Pi Learn how to optimise for Pi 164 Turn Ras Pi into a stop motion studio Create a stop-motion ilm 168 Send an SMS from Raspberry Pi Combine simple Python code and Twilio 170 Build a complex LED matrix Build and program this useful light display withPi “TheRaspberryPitakesthe ‘Pi’ofitsnamefromPython, astheofficialPilanguage” 168
  • 159.
    The Python Book159 164 170
  • 160.
    160 The PythonBook Use Python with Pi LearnthebasicsofprogramminginPythonwiththeRaspberry Pi,layingthefoundationsforallyourfutureprojects ProgramminginPython ontheRaspberryPi This tutorial follows on from the one last issue: ‘Setting up the Raspberry Pi’, where we showed you how to prepare your SD card for use with the Raspberry Pi. The beauty of using an SD card image is that the operating system is ready to go and a development environment is alreadyconfiguredforus. We’ll be using a lightweight integrated development environment (IDE) called Geany to do our Python development. Geany provides a friendlier interface compared to text-based editors such as nano and will make it easier to getintotheswingofthings. Thistutorialwillcovertopicssuchas: • basic arithmetic • comparison operators, for example ‘equal to’ and ‘not equal to’ • control structures, for example loops and if statements Bytheend,we’llhaveanadvancedversionofour ‘helloworld’application.Let’sdivestraightin… Resources ARaspberryPiwithall necessaryperipherals AnSDcardcontainingthe latestDebianimageforthe RaspberryPi http://www.raspberrypi.org/downloads It’simportanttothinkaboutdata types.Weconvertthenumberto decimaltomakesurethatwe don’tloseanydecimalnumbers duringarithmetic Thestoppingconditionforawhileloop hastobesatisfiedatsomepointinthe code;otherwisetheloopwillneverend! It’sgoodpracticetodescribe whattheprogram’spurposeisat thetopofthefile.Thiswillhelp yououtwhenworkingonlarger projectswithmultiplefiles Theprintfunctioncanonlyacceptstring datatypes,soweneedtoconvertany variableswithanumberdatatypetoastring beforewecanprintthemtothescreen
  • 161.
    The Python Book161 Use Python with Pi 01Stayingorganised We don’t want to have messy folders on our new Pi, so let’s go to the file manager and organise ourselves. Open the file manager by clicking the icon next to the menu icon on the bottom left of the screen. Create a new folder by right-clicking and selecting NewFolder, then type a name and click OK. We created a folder called Python, and inside that created a folder calledHelloWorldv2. 02StartingGeany Start Geany by going to the LXDE menu and going to Programs. From here, select Geany. Once you’re in the Geany interface, create a new Python file from a template by selecting ‘New (with template)main.py’. Delete everything in this template apart from the first line: #!/usr/ bin/env python. This line is important because it means you can run the code from the command line and the Bash shell will know to open it with thePythoninterpreter. 03Savingyourwork It’s always a good idea to keep saving your work with Ctrl+S as you program, because it would be a shame to lose anything you’ve been working on. To save your file for the first time, either press Ctrl+S or go to the File menu and select Save. Give the file a sensible name and save it in the tidy folder structure you created before. It’s a good habit to be well organised when programming, because it makes things much easier when your projects become bigger andmorecomplicated. 04Settingitup Having detailed comments in your code is important because it allows you to note down things you find confusing and document complex procedures. Ifanother programmerhas to work with your code in the future, they’ll be extremely grateful. Start by adding a comment with a description of what the program will do and your name. All comment lines start with a hash (#) and are not interpreted as code by the Python interpreter. We import the sys library so we can use the sys.exit function to close the program later on. We also import everything from the decimal library because we want to makeuseofthedecimaltype. 05Variables A variable is data that is stored in memory and can be accessed via a name. Our program is going to start by asking for your first name, store that in a variable and then print out a welcome message. We’re going to add a comment that explains this and create a variable called firstName. Notice how we’ve capitalised the first letter of the second word to make it easier to read. We want the firstName variable to hold the value returned by a function called raw_input, that will ask the user for input. The question is passed into the print function within brackets, and because this is a string it is enclosed within quotation marks. A string type is basically a collection of characters. Note the extra space we’ve added after the colon because the user types their input straight after thisquestion. 06Printingamessage Now that we have a value in firstName, we need to output a welcome message to the screen. We print to the screen in Python using the print function. The print function is followed by a pair of brackets which enclose the values to print. When using the addition operator with strings, they are joined together. Note how firstName doesn’t need to be enclosed by quotation marks because it is the name of a variable. If it was enclosed in quotation marks, thetextfirstNamewouldbeoutput.Wefinishoff by adding a ‘n’ character (new line character) to ouroutputtoleaveoneblanklinebeforewestart ournextexample. Whenusingtheadditionoperator withstrings,theyarejoinedtogether
  • 162.
    162 The PythonBook Use Python with Pi 07Fixingasmallissue The Debian image that we’re currently using has a small misconfiguration issue in Geany. You’ll know if you have this problem by trying to run your program with either the F5 key or going to the Build menu and selecting Execute. If the issue is present then nothing will happen and you’ll see a message saying ‘Could not find terminal: xterm’. Not to worry, it’s easy to fix. Go to the Edit menu and then select Preferences. Go to the Tools tab and change the valueforTerminalfromxtermtolxterminal. 08Testingourprogram Now we’ve done that part, why not test it? It’s worth noting that you have to save before running the program, or anything you’ve done since you last saved won’t be interpreted by Python. Run the program by pressing the F5 key. Input your name by typing it and then pressing the Enter key. Once you have done this, you’ll see a welcome message. If the program exits with thecode0theneverythingwasrunsuccessfully. PressEntertoclosetheterminal. 09Workingwithnumbers We’re going to ask the user for a number by basically repeating the first couple of lines we did. Once the user gives us a number, we’ll halve, square and double it. The raw_input function returns the value that the user input as a string. A string is a text-based value so we can’t perform arithmetic on it. The integer type in Python can only store whole numbers whereas the decimal type can store numbers with decimals. We’re going to do something called a type cast, which basically converts a 10Performingarithmetic The main arithmetic operators in Python are + - / *, the latter two being divide and multiply respectively. We’ve created three new variablescallednumberHalved,numberDoubled and numberSquared. Notice that we don’t need to specify that they should be decimal because Python gives a type to its variables from the type of their initial value. The number variable is a decimal type, so all values returned from performing arithmetic on that number will also beofadecimaltype. 11Printingournumbers Now that we have performed our arithmetic, we need to print the results using the print function. The print function only accepts string values passed to it. This means that we need to convert each decimal value to a string using the str() function before they can be printed. We’re using a print statement with nothing between the quotation marks to print one blank line. This works because the print function always adds a new line at the end of its output unless told otherwise, so printing an emptystringjustprintsanewline. value with one type to another type. We’re going to convert our number string to a decimal value because it’s likely that decimals will be involved if we are halving numbers. If the number was of anintegertype,anydecimalvalueswouldsimply be cut off the end, without any rounding. This is calledtruncation. Savebeforerunningtheprogram, oranythingyou’vedonesinceyoulast savedwon’tbeinterpreted Theprint functiononly acceptsstring values,soconvert eachdecimal valuetoastring
  • 163.
    The Python Book163 Use Python with Pi 12InputvalidationwithWhileloops andIfstatements To demonstrate a while loop and if statements, we will output a question to the user that requires a yes or no answer. We’re going to ask them if they want to continue – and for this we require either a lower-case ‘yes’, or a lower- case ‘no’. A while loop is a loop that runs until a condition is met. In this case, we will create a variable called yesOrNo and the while loop will run while yesOrNo is false. The yesOrNo variable will be a Boolean type that can be either True or False. The variable will be initialised with a value ofFalse,orthewhileloopwillnotrun. A while loop has the format ‘while [condition]:’ – where any code that is part of the while loop needs to be indented in the lines below the colon. Any code that is not indented will not be part of the while loop. This is the same for an if statement. The condition is checked with the comparison operator ‘==’. A single ‘=’ is an assignment operator whereas a double equals is a comparison operator. Another common comparison operator is ‘!=’ – which means ‘not equalto’. We create a variable called ‘result’, which holds the result of the question, do you want to continue? We then check this result is valid with an if statement. Notice the ‘or’ operator which allows two conditions to be tested. If the user inputs a correct value then we set yesOrNo to True, which stops the while loop on the next run. Otherwise, we output an error message and the while loop will run again. The user can use the Ctrl+C command at the terminal to exit the programatanytime. 13Continueorexit? Next we will deal with the result that was stored during the while loop with if statements. If the user typed ‘yes’ then we will print ‘Continuing’. Otherwise, we will print ‘Exiting’ and then call the sys.exit function. You don’t have to do anything else for the program to continue because it will simply carry on if the sys.exit function wasn’t called. This code also shows that the newline character n can be used anywhere in a string, not just in separate quotationmarkslikeabove. 14Loopswithnumbers We’ll be using a while loop that uses a number and a = (less than or equal to) operator as its stopping condition. The while loop will be used to increment the number by 1, printing the change on each loop until the stopping condition is met. The count variable allows us to know exactly how many times we have been through thewhileloop. 15Incrementingnumberswithaloop The while loop will run until the count is 6, meaning that it will run for a total of 5 times because the count begins at 1. On each run, the while loop increments the number variable and then prints what is being added to the original number, followed by the result. Finally, the count isincremented. 16Finishingoff The final step is to print that the program is exiting. This is the last line and we don’t have to do anything else because Python simply finishes when there are no more lines tointerpret. 17Admireyourwork Now that we’ve finished coding, save any changes you have made and run your program withtheF5key. Thecount variableletsus knowexactlyhow manytimeswe havebeenthrough thewhileloop
  • 164.
    Use Python withPi Build your own animation studio by usingyour RaspberryPi asastop-motioncamera TurnyourRaspberryPi intoastop-motionstudio What have you done with your Raspberry Pi camera lately? While it gives us plenty of new ways to use the Pi, unless you’ve got your computer set up as a security webcam or you’re particularly a fan of time-lapse photography, the chancesarethatyou’veoverlookedthePicamera moduleforawhile. If you’re a fan of animation or you simply want to extend the possibilities of the module, why not build a stop-motion camera? By using Python and an external button to capture images, the Raspberry Pi can be the perfect tool for animators. Better still, you can go beyond animating toys or bits of LEGO and go old school by mounting the Pi on a rostrum and creating a cartoon. Even if you can’t buy or build one, you can mount the stopmotionPicamerawithasmartphonemount forstability. 01Mountyourstop-motionPi camera Before you get started, think about the type of animation you’re going to be capturing. If you’re using the traditional top-down method, as used by classic cartoon animators, then you’ll need a rostrumtomounttheRaspberryPi. Alternatively, you may be animating something on a desk, table or perhaps the BelowOurhome-made antennamaylookalittle rougharoundtheedges, butitworksgreat! 02Findsomewheretoshoot For your first attempts at shooting a stop-motion video, you should use a wide and uncluttered space. This might be a desk, a kitchen work surface or even the floor, but it should be a hard and flat area in most cases (unless you have need for a bumpy carpeted environment for your video) to aid with the creationofyourstop-motionfilm. As time progresses and your skill develops, other surfaces can prove useful alternatives, but keepitsimplefornowandstickwithflatsurfaces while you get to grips with the art form using the RaspberryPistop-motioncamera. 03ConnectthePicameramodule Next you’ll need to connect the Pi camera module to your Raspberry Pi. All models have the necessary connector, although where it is found on the device will depend on the versionofyourRaspberryPi. floor, but you’ll need your Pi camera mounted in a similar way, looking across rather than down. Various options are available, such as smartphone tripods and dashboard mounts. Most of these should be suitable for securely mounting your Raspberry Pi. Resources Harddrive OSMC: osmc.tv/ Homenetwork AnotherLinuxcomputer,lessthan eightyearsold
  • 165.
    Use Python withPi The Python Book 165 The Model A has the Pi-camera connector next to the Ethernet port, as does the Model B. On the B+ and the Raspberry Pi 2, the connector is in a similar position, but it’s a little further from the Ethernet port between the audio-outandHDMIports. Connecting the camera module can be tricky. Begin with taking your Pi out of its case or remove the top where possible and disconnect all cables. Takeprecautionsbeforeremovingthedevicefromitsantistaticbag,asthe cameramoduleisverysensitivetostaticelectricity. On the Pi, lift the plastic catch on the connector and slot the camera module flex into place with the shiny contacts facing away from the Ethernet port. Once the flex is fully slotted in, push the plastic catch back intoplace. sudo apt-get install python-picamera python3- picamera sudo idle3 In the Python editor, open FileNew Window and enter the code below, setting the camera.vflip and camera.hflip as True or False as required. Save (perhaps as ‘camflip.py’), then press F5 to run the script and view the correctlyoutputtedimage. Tosavetime,however,youmighttryrotatingthepositionof yourcameraorPicameramodule! import picamera from time import sleep with picamera.PiCamera() as camera: camera.vflip = True camera.hflip = True camera.start_preview() sleep(3) camera.capture(‘ /home/pi/image2.jpg’) camera.stop_preview() 04TestyourPicameramodule After connecting the Pi camera, check that it works by booting the Raspberry Pi (we’re assuming you’re running Raspbian) and entering this in thecommandline: sudo raspi-config With the keyboard arrows, move down to option five, ‘Enable Camera’, and tap Enter. In the following screen, hit Enter again to enable the camera and exit. If you’re not already signed into the GUI, do so now (if you’re in the command line interface, enter startx to launch the desktop view). Open theterminalandenter: raspistill -o image1.jpg YoucanreviewtheresultingimageinyourHomedirectory. LeftConsidertheangle you’llbeshootingfrom asyouaresettingup RightWiththecamera module,ensure the shinysidefacesaway fromtheEthernetport 05Straightenouttheimage With the Pi camera up and running, you may notice that it’s outputting the image with the axes flipped. We can fix this using Python, so opentheterminalandenter: 06Setupthebreadboardandbutton We have two ways to add a button to the Raspberry Pi, but before proceeding,ensureyouhaveswitchedthecomputeroffanddisconnectedit fromthemains.Youshouldalsodisconnectanycablesandhardware. The simplest method of adding a button is to employ a solder-free breadboard and a single-state pushbutton. Connect the button to the breadboard with two male-to-female wires running to GPIO pins GND and 17.WithascriptdesignedtodetectactionfromthebuttonontheGPIO,each frameofyouranimationcanbecapturedwithasinglebuttonpush. FileSilo.co.uk
  • 166.
    166 The PythonBook Use Python with Pi 09Useanappinstead Don’t fancy using the script? Try this stop-motion application. Begin by installing the raspicam-extras package thatincludestheUB4LdrivesforthePi: wget http://www.linux-projects.org/listing/uv4l_ repo/lrkey.asc sudo apt-key add ./lrkey.asc sudo sh -c ‘echo “deb http://www.linux-projects. org/listing/uv4l_repo/raspbian/ wheezy main” / etc/apt/sources.list’ sudo apt-get update sudo apt-get install uv4l uv4l-raspicam uv4l- raspicam-extras Withthatdone,enter: sudo apt-get install stopmotion Launch with the stopmotion command to open a GUI with a live camera for you to line up each shot. This is a more elegant solutionandcapturedimagescanbestitchedtogetherusingthe ‘Numberofimages’sliderandthecamerabuttonaboveit. 08Stitchtogetheryourstop-motionanimation The collected images can be cycled through relatively quickly using a special picture viewing app, but for a true animationyouwillneedtocompilethemintoonesinglefile.Inthe terminal,installffmpeg: sudo apt-get install ffmpeg After installing, you can then convert your images into a video clip,asfollows: ffmpeg -y -f image2 -i /home/pi/Desktop/stop- motion/frame%03d.jpg -r 24 -vcodec libx264 -profile high -preset slow /home/pi/Desktop/stop-motion/ animation.mp4 Withthisfilecreated,openwiththecommand: omxplayer animation.mp4 Thevideowillthenbeplayedinfull-screenmode. Don’twanttobuild yourownrostrum?Why botherwhenacamera tripodcanbepositioned asneededandother items,likesmartphone suctionholdersand grips,canbeemployed toholdyourRaspberryPi caseandcameramodule inplace? Fortop-downanimation, suction-padsmartphone holders(availablefor under£10)thatusea stickygelforastronger gripareperfectfor holdingyourstop-motion Picameraandattaching toaflatsurfaceabove theanimationsubject. Tripodsand suction holders 07Codeforstopmotion Once satisfied with the results of your Pi camera,it’stimetoturnitintoastop-motioncamera. The first step is to type up the code shown below, which will capture an image of your subject and save it into a folder called ‘Stop motion’. Each image is numbered sequentially and they can all be stitched together once your animation is complete. Save the codeasanimation.py: import picamera from RPi import GPIO button = 17 GPIO.setmode(GPIO.BCM) GPIO.setup(button, GPIO.IN, GPIO.PUD_UP) with picamera.PiCamera() as camera: camera.start_preview() frame = 1 while True: GPIO.wait_for_edge(button, GPIO. FALLING) camera.capture(‘ /home/pi/animation/ frame%03d. jpg’ % frame) frame += 1 camera.stop_preview() Then,inanewterminalwindow,enterthefollowing: sudo python3 animation.py Press the button to capture each frame, moving the subjectasneeded.Whenyou’realldone,hitCtrl+Cto terminatethescript. Don’twanttobuildyourown rostrum?Whybotherwhena cameratripodcanbepositioned asneeded?
  • 167.
    The Python Book167 Use Python with Pi 10Putitalltogether Now you have the camera set up, a device for keeping it steady (whether a DIY rostrum or a tripod), and you’ve constructed a button or plan to capture each frame via SSH. Your stop-motion Raspberry Pi camera is finally ready! By now you’re probably aching to get started, so with your stop-motion Pi camera ready to use (and close to a power supply), it’s time to start building your film set. While this might simply be an empty table top, there might equally be a few props you would like to include. 11Storyboardyourshoot It’seasytogettiedupwiththeideaofcreatingastop-motioncamera andforgetallaboutasubjectandhowitwillact. Youcanavoidanyproblemsherebytakingthetimetocarefullyplanwhat willhappeninyourfilm:yourstory.Remember,eachsecondofthevideowill require26frames! The best way to plan at this level is to simply write up an outline, but beyondthisyoumayprefertostoryboardinsteadbymakingpencilsketches tohelpyouprogressthestory. 12Castyourstop-motionshoot You’ll also need a good idea of what your subject will be; this means who or what you’re going to be using the stop-motion camera to capture frames of. Typically, amateur stop-motion films make use of household objects,toysandchild’splayclay. The beauty of this kind of animation is that you can use almost anything that you can get your hands on, from a cup and saucer to an Action Man, as long as you have a way to support the subject(s) in the positions you wish themtotakethroughout. 13Stop-motionwithtoys Ifyoucasttoysasyourstop-motionstars,youwillgetamuchbetter resultfromsomethingthatisbuilttostanduponitsownthantoysthattend tositorfallover. LEGO sets and Minifigs appear in many stop-motion productions on YouTube.Thisiswithgoodreason,asthey’rereallyeasytoplaceinadesired position. The construction element of the bricks is also a major attraction. Another popular option is Transformers toys. These are both good places to start,butyoushouldaimtodevelopyourownapproachovertime. 14Peopleinstop-motionfilms It isn’t only inanimate objects that you can include in stop-motion films. People can feature too! Pop videos such as Peter Gabriel’s 1985 hit Sledgehammer have taken advantage of stop motion (that video was produced by Aardman Animations, the eventual creators of Wallace and Gromit)andthetechniquecanbeusedonhumanstocreatesurrealeffects. Ifyou want your subject to be moving arounda room too, they can appear to 15MakeyourownWallaceandGromit Known as ‘claymation’, the practice of animating lumps of clay has been a popular form of animation for years in the UK, but there’s more to it than just clay. These forms, whether they’re cheese-loving old men or remarkablycleverdogs,haveawireskeletonthatisusedtokeepmovement inthedesiredposition. This makes it much easier to capture the frames efficiently, but for the best results you should also have several versions of the same figures available. This is just in case one gets deformed and damaged during production! 17Takeyourstop-motionstudiotothenextlevel At the risk of encouraging you to become the next Ivor Wood (creator of The Wombles, Paddington and Postman Pat, among others), it is possible to use the Raspberry Pi’s camera module for ambitious projects as well as smallones.Afterall,thisdevicephotographsinhighresolutionsothereisno reasonnottoadoptthissetupandincorporateitintoaworkingstop-motion studiowithaminiatureset. Sharing your work through YouTube is a great idea too, especially as it will makeitsimpletoaddasoundtrackusingYouTube’sbrowser-basededitor. 16Fromstopmotiontotimelapse Similar to stop motion, time lapse is a technique that automatically captures images on a preset timer. We can use a Python script to control this, saving the captures in a directory and using ffmpeg to compile them intoafilm. However, what you may not want for this project is a mains cable trailing all over, especially if you’re attempting to capture the movement of the stars at night or nature activity. We suggest employing a Pi-compatible battery pack to make your time-lapse Pi camera truly mobile, using SSH to run the scriptremotely: import time import picamera VIDEO_DAYS = 1 FRAMES_PER_HOUR = 60 FRAMES = FRAMES_PER_HOUR * 24 * VIDEO_DAYS def capture_frame(frame): with picamera.PiCamera() as cam: time.sleep(2) cam.capture(‘ /home/pi/Desktop/frame%03d. jpg’ % frame) # Capture the images for frame in range(FRAMES): # Note the time before the capture start = time. time() capture_frame(frame) # Wait for the next capture. Note that we take into # account the length of time it took to capture the # image when calculating the delay time.sleep( int(60 * 60 / FRAMES_PER_HOUR) - (time. time() - start) ) AboveHere’sthestopmotionprograminaction–it’sasimpleenoughGUItoget yourheadaroundandgivesyouanicepreviewwindow befloatingorgliding.Theresultscanbestrange,butusefulifyouknowwhat youwant.
  • 168.
    Use Python withPi Create a program that combinesTwilioand simplePython codeto enableyouto sendanSMS(textmessage)from your Pitoamobilephone SendanSMSfromyour RaspberryPi Text messaging, or SMS (Short Message Service), has become a staple of everyday communication. What began life as a 40 pence message service is now offered by most tariff providers as an unlimited service. Twilio, a cloud communications company, enables you to send SMS messages for free from your Raspberry Pi to a mobile phone using just six lines of code. 01SetupyourTwilioaccount The first step of this project is to register for a Twilio account and Twilio number. This is free and will enable you to send an SMS to a registered, verified phone. Once signed up, you will receive a verification code via SMS to the registered phone. When prompted, enter this onto the Twilio site to authenticate your account and phone. Go to twilio. com/try-twilioandcreateyouraccount. 02Registerandverifymobilenumbers Your Twilio account is a trial account (unless you pay the upgrade fee), which means you can only send and receive communications from a validated phone number. Enter the phone number of the mobile that you want to verify, ensuring that you select the correct country code. Twilio will text you a verification code. Enter this code into the website formandpresssubmit. LeftWiththismethod, youcouldgetyour Pitodropyouatext whenitfinishes runningascript Resources RaspberryPi Twilioaccount 168 The Python Book
  • 169.
    Use Python withPi 05Twilioauthentication Now you are ready to create the SMS program that will send the text message to your mobile phone. Open your Python editor and import the Twilio REST libraries (line one, below). Next, add your AccountSid and Auth Token, replacing theXwithyours,asyouwillfindonyourdashboard: from twilio.rest import TwilioRestClient account_sid = “XXXXXXXXXXXXXXXXXXXXX” # Enter Yours auth_token = “XXXXXXXXXXXXXXXXXXXXX” # Enter Yours client = TwilioRestClient(account_sid, auth_ token) 03Thedashboard Once registered and logged in, visit the dashboard page,whichwilldisplayyourAccountSidandyourAuthToken. These are both required to use the Twilio REST. Keep these secure and private, but be sure to make a note of them as you willneedthemforyourPythonprogramlater. 04Installthesoftware Boot up your Raspberry Pi and connect it to the Internet. Before you install the Twilio software, it is worth updating and upgrading your Pi. In the LX Terminal, type sudo apt-get update, then sudo apt-get upgrade. Once complete, type sudo easy_install twilio or sudo pip install twilio to install the software. (If you need to install pip, type sudo apt- get install python-pip python-dev, press Enter, then type sudopipinstall-Upip.) 06Createyourmessage You will probably want to be able to change your text messages rather than send the same one. Create a new variable in your program called message. This will prompt you toenterthephrasethatyouwanttosendtothemobilephone. Whentheprogramruns,thisisthemessagethatwillbesent: message = raw_input(“Please enter your message”) RESTstandsfor Representational StateTransfer.(It issometimesspelt “ReST”.)Itrelieson astateless,client- server,cacheable communications protocol–andin virtuallyallcases,the HTTPprotocolisused. RESTisanarchitecture stylefordesigning networkedapplications. REST 09OtherAPIandcodes Twilio provides a wide range of API codes and reference documents to create other communication programs, such as making phone calls, recording a call, and retrieving data including caller IDs and call duration. The API also complements a wide range of languages, including Ruby,PHP,JavaandNode.js(twilio.com/api). 08Sendthemessage Now send your message. The code below is not required, but useful to indicate your message has been sent. Add the lines and save your program. Ensure your Raspberry Pi is connected to the Internet and that your mobile is on, then run your program. You have just texted from your RaspberryPi! print message.sid print “Your message is being sent” print “Check your phone!” 07Addyournumbers To send the message, you need to add the code line below and your two phone numbers. The first number is your mobile phone number, which is registered and validated with Twilio (Step 2). The second number is your Twilio account number, which can be retrieved from your dashboard page under‘CalltheSandboxnumber’.ChangetheSandboxnumber toyourcountrylocationandremembertoaddtheinternational countrycode. message = client.messages. create(to=“+44YOURMOBNUMBER” , from_=“+44YOURTWILIONUMBER” , body=message) AboveYouwillbeabletofindyourAccountSidandyourAuthTokenontheTwiliodashboard Twilioenablesyoutosend SMSmessagesforfree The Python Book 169 Codeon FileSilo
  • 170.
    170 The PythonBook i LEDMatrixdisplaysystemsfinduseeverywherefromgaudy kebabshopstoimpressivesteampunk-styledsystems BuildacomplexLEDmatrix Driving LEDs in an efficient fashion is a science of its own. The common availability of single-board computers has put the necessary technology within reach of everyone. When dealing with LED displays, two different systems must be considered. We will focus on traditional matrix- based systems made up of one or more LEDs. Their affordable nature makes them ideally suited to classic display applications: they communicate currency prices, provide stock-brokers with updates from the trading floor and have even been used as basic displays for primitive oscilloscopes. Finally, we will also provide you with an overview of electronic basics. This tutorial is a bit more advanced than the ones we usually run in this section of the magazine, and it’s also worth noting that we’re going to be programming with C rather than Python. Follow along using the code listing annos. 01ThinkaboutLEDs Standalone LEDs are primitive – they light up once current flows through them. Driving a few LEDs is as easy as connecting them to GPIO pins along with a resistor. Sadly, this method becomes wasteful once more than a few of them get involved–driving16diodestiesup16pins. 02Arrangeyourdiodes Methods were devised to reduce the number of pins needed. Matrix-based systems are resilient to individual diode failures, and provide a pin-to-LED ratio of n=(n/2)^2. The following steps assume a 16x16 LED matrix which is made up according to Figure A. Since LEDs permit current in only one direction, you can enable a single LED by bringing the correspondingpinshighandlow. Resources Breadboardwires 16x16LEDMatrix 2x74HC238 2x74HC244 16x220OhmResistor Sourcecode:FileSilo.co.uk
  • 171.
    The Python Book171 Use Python with Pi 04Separateconcerns Chip two goes by the name of 74HC244, which is described as an octal buffer with tri-state capability. Tri-State outputs can physically disconnect themselves from the bus line. This permits you to tie their outputs together without fear of short circuits. As long as all but one chip are in tri-state mode, no current can flow between high and low output pins. 03HarnesstheMUX Our LED module has a total of 32 inputs, which overwhelms older versions of the RPi. The first way to restrict their number comes in the shape of the 74HC238, a component described as a 3-to-8 line decoder/demultiplexer. Its function is describedintheFigureBimageonthenextpage. AboveTheextendedversionofthisschematicisinsideFileSilo.co.uk – justsigninanddownload 05Roundthemup Four GPIO pins control the enabled display ‘line’. Three pins configure the address which is to be emitted, while the signal emitted from the fourth pin is connected to the activity inputs. This ensures that but one IC is active. The 74HC244 ensures that but one of the two groups is active. 06Configurethepins We used a library from Hussam Al-Hertani’s Hertaville blog (hertaville.com/2014/07/07/rpimmapgpio). The first step involves setting output functions. As the GPIOs are set to outputs, the tri-state feature might connect the internal state to the output pins of the IC. This could lead to internal shorting if the output is not turned off. #include “€œmmapGpio.h” #include unistd.h #include stdio.h GHº QH3,1$ GHº QH3,1$ GHº QH3,1$ GHº QH3,1$ GHº QH3,16 GHº QH3,16 GHº QH3,1' GHº QH3,1' GHº QH3,1' GHº QH3,1' GHº QH3,1' GHº QH3,1' GHº QH3,1' GHº QH3,1' void VHW$GGUHVV(unsigned FKDU _which, mmapGpio*BZKHUH
  • 172.
  • 173.
  • 174.
  • 175.
  • 176.
  • 177.
  • 178.
  • 179.
  • 180.
  • 181.
  • 182.
  • 183.
  • 184.
    } } void VDIHO6HW5RZ(unsigned FKDU_which, mmapGpio*BZKHUH
  • 185.
  • 186.
  • 187.
  • 188.
  • 189.
    } else { Full code listing Step07 Step 12 Step 08 Figure A OurLEDmodelhasa totalof32inputs,which overwhelmsolder versionsoftheRPi
  • 190.
    172 The PythonBook Use Python with Pi 08Selectarow Inthe74HC244,wefirstdisablebothunitsandproceed to turning on the one which is needed. This sequence prevents ghostingduringtheswitchingprocess. 09Dothemainloop The outer part of the loop consists of logic that manages the addressing of the individual rows. Our program must flash the individual LED groups one after another using thebuildingblocksdescribedinthenextstep. 10Completetheloop Writing out data is accomplished in a sequence of three commands. We select the row, configure the column and then write out the data bits that are to be displayed. A small pause is observed in order to give the LEDs some time to ‘burn into’ theviewer’seyes. _where-writePinLow(PINCS1); } } void setData(unsigned char _which, mmapGpio* _where) { if(_which1) { _where-writePinHigh(PIND0); } else { _where-writePinLow(PIND0); } if(_which2) { _where-writePinHigh(PIND1); } else { _where-writePinLow(PIND1); } if(_which4) { _where-writePinHigh(PIND2); } else { _where-writePinLow(PIND2); } if(_which8) { _where-writePinHigh(PIND3); } else { _where-writePinLow(PIND3); } if(_which16) { _where-writePinHigh(PIND4); } else { _where-writePinLow(PIND4); } if(_which32) { _where-writePinHigh(PIND5); } else { _where-writePinLow(PIND5); } if(_which64) { _where-writePinHigh(PIND6); } else { _where-writePinLow(PIND6); } if(_which128) { _where-writePinHigh(PIND7); } else Full code listing 07PowertheMUX Create a convenience function taking an address ranging from zero to 15. It is converted into pin outputs for our 3-to-8-demultiplexer. The effect of this is that all but one of the sixteen rows is to be supplied with energy. Step 08 Step 11 Figure B AboveDigitalLEDmatriceslikethisonegiveyoufarmorecontrolover eachindividual‘pixel’inthedisplay TwoversionsofLED stripsareoffered. ‘Primitive’onesare basedonanalogue technology.Init,an entirestripofdiodes hasthecolourset bythethreeinput pins.Systemssuch asthemega-display shownintheleft- handimagerequire theuseofthe digitalversion.They arebasedonthe conceptoftheshift register.Yoursystem inputsindividual colourvalueswhich arethenpushedon alongthestrip. LED stripes
  • 191.
    The Python Book173 Use Python with Pi 11Energycontrol LEDslightupifcurrentflowsthroughthem.SetDatapulls the pins of the 74HC244 low to ensure that the energy supplied fromthe74HC238canflowthroughthediode. 12AvoidGPIOtrouble The Raspberry Pi Foundation has a tendency to change the layout of the expansion header regularly, a habit which professional manufacturers of process computers abhor. It’s recommended to handle the mapping between pins and functions via a set of defines. Our code is optimised for a Rev2 Raspberry Pi with a ‘short’ header – 40-pin variants will require readjustments making sure the physical pin numbers correspondtothelogicalGPIOnumbers. 13Addexampledata Test the code by setting the datastore to a value of your choice. Setting 64 to all fields will disable one row in each part of the display. 14Kickitoff Check all connections between the planar and the single-board computer, and proceed to starting the compiled app. Don’t forget to use the sudo command – direct memory access is restricted to root in order to prevent apps from causing havoc in the physical memory. Users are accustomed to this, so requiring them to put a sudo in front of the command doesn’t cause concern. 15Noticeaflicker Sharp-eyed readers will notice an occasional flicker where one line appears brighter than the others. This is caused by the stalling of the program – if the kernel does other work, the switching routine can’t run. We could solve this problem by using a real-time Linux kernel. { _where-writePinLow(PIND7); } } int main(void) { mmapGpio rpiGpio; //Set outputs rpiGpio.setPinDir(PINA0,mmapGpio::OUTPUT); rpiGpio.setPinDir(PINA1,mmapGpio::OUTPUT); rpiGpio.setPinDir(PINA2,mmapGpio::OUTPUT); rpiGpio.setPinDir(PINA3,mmapGpio::OUTPUT); //TURN OFF ASAP! rpiGpio.setPinDir(PINCS0,mmapGpio::OUTPUT); rpiGpio.writePinHigh(PINCS0); //TURN OFF ASAP! rpiGpio.setPinDir(PINCS1,mmapGpio::OUTPUT); rpiGpio.writePinHigh(PINCS1); rpiGpio.setPinDir(PIND0,mmapGpio::OUTPUT); rpiGpio.setPinDir(PIND1,mmapGpio::OUTPUT); rpiGpio.setPinDir(PIND2,mmapGpio::OUTPUT); rpiGpio.setPinDir(PIND3,mmapGpio::OUTPUT); rpiGpio.setPinDir(PIND4,mmapGpio::OUTPUT); rpiGpio.setPinDir(PIND5,mmapGpio::OUTPUT); rpiGpio.setPinDir(PIND6,mmapGpio::OUTPUT); rpiGpio.setPinDir(PIND7,mmapGpio::OUTPUT); unsigned char dataStore[2][16]; for(int j=0;j2;j++) { for(int k=0;k16;k++) { dataStore[j][k]=64; } } int blockCounter=0; int rowCounter=0; while(1) { blockCounter++; if(blockCounter==16) { if(rowCounter==0) { blockCounter=0; rowCounter=1; } else { blockCounter=0; rowCounter=0; } } safelySetRow(rowCounter, rpiGpio); setAddress(blockCounter, rpiGpio); setData(dataStore[rowCounter][blockCounter], rpiGpio); usleep(50); } return 0; } Full code listing Step 06 Step 09 Step 13 Step 10 AboveThisisthefullschematicoftheLEDmatrixthatwe’reworking withhere(youcanalsoviewitatitsfullsizeonFileSIlo) Fullcode schematics FileSilo.co.uk
  • 192.
    Try 3issues forjust £5* *This ofer entitlesnew UK direct debit subscribers to receive their first three issues for £5. After these issues, subscribers will then pay £25.15 every six issues. Subscribers can cancel this subscription at any time. New subscriptions will start from the next available issue. Ofer code ZGGZINE must be quoted to receive this special subscriptions price. Direct debit guarantee available on request.This ofer will expire 31 January 2017. **This is an US subscription ofer.The USA issue rate is based on an annual subscription price of £65 for 13 issues which is equivalent to $102 at the time of writing compared with the newsstand price of $16.99 for 13 issues being $220.87.Your subscription will start from the next available issue. This ofer expires 31 January 2017. S p e c i a l t r i a l o f f e r Exclusiveofferfornew Enjoyed thisbook?
  • 193.
  • 194.
    YOUR FREE RESOURCES Login to filesilo.co.uk/bks-864 and download your great resources NOW! YOUR BONUS RESOURCES ON FILESILOWITHTHIS BOOKAZINE,FREEAND EXCLUSIVE FOR THE PYTHON BOOK READERS,YOU’LLFINDA WEALTH OFRESOURCES, INCLUDINGTHE FOLLOWING… •Awalkthrough video on writing good- quality code with Python from the very beginning •Aseries of tutorials on making a PiSnake game with Raspberry Pi and Python •Aguide to using GUI with GTK • Everything you need to complete the tutorials in this book and become a Python expert Allthetutorialfilesyou’llneed Hoursof freevideotutorials Inspirationalprojects PACKEDWITHBRILLIANT DIGITALCONTENT,AVAILABLE ANYTIME,ONDEMAND filesilo.co.uk/bks-864 EVERYTHING YOUNEED TOBUILDON THEAWESOME SKILLS INTHIS BOOKAZINE ENHANCE YOUR PYTHON SKILLS 176 The Python Book
  • 195.
    FILESILO –THE HOMEOFPRO RESOURCES Arapidlygrowinglibrary Updatedcontinuallywithcoolresources Letsyoukeepyourdownloadsorganised Browseandaccessyourcontentfromanywhere Nomoretorndiscpagestoruinyourmagazines Nomorebrokendiscs Printsubscribersgetallthecontent Digitalmagazineownersgetallthecontenttoo! Eachissue’scontentisfreewithyourmagazine Secureonlineaccesstoyourfreeresources Discover your free online assets Themostpopulardownloadsareshownin thecarouselhere,socheckoutwhatyour fellowreadersareenjoying Whetherit’sprogrammingtutorialsor videoworkshops,categoriesmakeiteasy toidentifythecontentyou’relookingfor Findoutmoreaboutouronlinestores,and usefulFAQs,suchasourcookieand privacypoliciesandcontactdetails Ifyou’relookingforaparticulartypeof content,likesoftwareorvideotutorials, usethefiltersheretorefineyoursearch Seekeydetailsforeachresource includingnumberofviewsand downloads,andthecommunityrating Discoverourfantasticsistermagazines andthewealthofcontentandinformation thattheyprovide ThefirsttimeyouuseFileSilo,you’llneedto register.Afterthat,youcanuseyouremail addressandpasswordtologin ThisisthenewFileSilositethatreplaces yourdisc.You’llfinditbyvisitingthelinkon thefollowingpage The Python Book 177
  • 196.
    HOW TO USE EVERYTHINGYOU NEED TO KNOW ABOUT ACCESSINGYOURNEWDIGITALREPOSITORY Havingtroublewithanyofthetechniquesinthisbookazine’stutorials?Don’tknow howtomakethebestuseofyourfreeresources?Wanttohaveyourworkcritiqued bythoseintheknow?ThenwhynotvisittheLinuxUserDeveloperandImagine BookazinesFacebookpagesforallyourquestions,concernsandqualms.Thereisa friendlycommunityoffellowLinuxandOpenSourceenthusiastswaitingtohelpyou out,aswellasregularpostsandupdatesfromtheteambehindLinuxUser Developermagazine.Likeustodayandstartchatting! facebook.com/ImagineBookazines NEEDHELPWITH THETUTORIALS? ToaccessFileSilo,pleasevisitfilesilo.co.uk/bks-864 01Followthe on-screen instructionstocreatean accountwithoursecure FileSilosystem,loginand unlockthebookazineby answeringa simplequestion aboutit.Youcan nowaccessthe contentforfree atanytime. 02Onceyouhave loggedin,youare freetoexplorethewealthof contentavailableon FileSilo,fromgreatvideo tutorialsandonlineguides tosuperbdownloadable resources.Andthemore bookazinesyoupurchase, themoreyourinstantly accessiblecollectionof digitalcontent willgrow. 03Youcanaccess FileSiloonany desktop,tabletor smartphonedeviceusing anypopularbrowser(such asSafari,FirefoxorGoogle Chrome).However,we recommendthatyouusea desktoptodownload content,asyoumaynotbe abletodownloadfilesto yourphoneortablet. 04Ifyouhaveany problemswith accessingcontenton FileSilo,orwiththe registrationprocess,takea lookattheFAQsonlineor emailfilesilohelp@ imagine-publishing.co.uk. facebook.com/LinuxUserUK 178 The Python Book
  • 198.
    The ultimate guideto coding with Python PutPythontowork Supercharge your system and make life easier with handy coding tutorials UsePythonwithRaspberryPi Work on any Raspberry Pi model using its officially recognised language Gettogripswiththebasics Learn Python the right way and complete basic projects with our simple guides OVER2HOURS OFVIDEOTUTORIALS Python The 250 essentialtips inside