{% block title %}{% endblock %}LUD Issues (2024)

Table of Contents
Issue #{{ object. id }} {{ object . status }} Information Opened {{ object. opened_on }} ago Last modified {{ object .modified_on }} ago Owner P> {{ object, owner }} Summary {{ object, summary }} {% 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. The Python Book 65 V ^Python essentials Python is a programming language that lets you work more quickly and integrate your systems more effectively. Today, Python is one of the most popular programming languages in the open source space. Look around and you will find it running everywhere, from various configuration tools to XML parsing. Here is the collection of 50 gems to make your Python experience worthwhile. . . Basics T Running Python scripts On most of the UNIX systems, you can run Python scripts from the command line. $ python mypyprog.py 2. Running Python programs from Python interpreter 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, and the answer is immediate. Python interpreter can be started by issuing the command: $ 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. »> In this article, all the code starting at the »> symbol is meant to be given at the Python prompt. It is also important to remember that Python takes tabs very seriously - so if you are receiving any error that mentions tabs, correct the tab spacing. 3^ Dynamic typing 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 type internally. 66 The Python Book Python essentials ^Python statements 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 brackets to separate code blocks. 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 compare it. 6. Concatenating strings You can use “+’ to concatenate strings. »> print ‘kun’+’al’ kunal TJhe 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# or Java. 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 module that can be imported into other scripts and programs. These files must have a. py extension. 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 x,y = my_function .minmax(25, 6.3) 9. Module defined names Example: The built-in function ‘dir()’ can be used to find out which names a module defines. It returns a sorted list of strings. »> 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. Module internal 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 system records. 1 1 . Passing arguments to a Python script Python lets you access whatever you have passed to a script while calling it. The ‘command line’ content is stored in the sys.argv list, import sys print sys.argv 12. Loading modules or commands at startup 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 modules or commands . 13. Converting a string to date object You can use the function ‘DateTime’ to convert a string to a date object. Example: from DateTime import DateTime dateobj = DateTime(string) 14. Converting a list to a string for display You can convert a list to string in either of the following ways. 1st method: »> mylist = [‘spam’, ‘ham’, ‘eggs’] »> print ‘, ‘ . join(mylist) spam, ham, eggs 2nd method: »> print ‘\n’ . join(mylist) spam ham eggs 15. Tab completion in Python interpreter You can achieve auto completion inside Python interpreter by adding these lines to your .pythonrc file (or your file for Python to read on startup): import rlcompleter, readline readline . parse_and_bind( ‘ tab : complete ’ ) This will make Python complete partially typed function, method and variable names when you press the Tab key. 16. Python documentation tool You can pop up a graphical interface for searching the Python documentation usingthe command: $ pydoc -g You will need python-tk package for this to work. 17 Python documentation server You can start an HTTP server on the given port on the local machine. This will give you a nice-looking access to all Python documentation, including third-party module documentation. $ pydoc -p 18, Python development software There are plenty of tools to help with Python development. Here are a few important ones: IDLE: The Python built-in IDE, with autocompletion, function signature popup help, and file editing. IPythonj Another enhanced Python shell with tab-completion and other features. Eric3: A GUI Python IDE with autocompletion, class browser, built-in shell and debugger. WingIDE: Commercial Python IDE with free licence available to open-source developers everywhere. » ► The Python Book 67 ^Python essentials Built-in modules 19. Executing functions at the time of Python interpreter termination You can use ‘atexit’ module to execute functions at the time of Python interpreter termination. Example: def sum() : print(4+5) def message() : print(“Executing Now”) import atexit atexit. register(sum) atexit. register(message) Output: Executing Now 9 20. Converting from integer to binary, hexadecimal and octal Python provides easy-to-use functions - bin(), hex() and oct() - to convert from integer to binary, decimal and octal format respectively. Example: »> bin(24) ‘0bll000’ »> hex(24) ‘0x18’ »> oct (24) ‘030’ 21. Converting any charset to UTF-8 You can use the following function to convert any charset to UTF-8. data . decode(“input_charset_here”) . encode( ‘utf-8’ ) 22. Removing duplicates from lists If you want to remove duplicates from a list, just put every element into a diet 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), 1, []) return d.keysQ 23. Do-while loops Since Python has no do-while or do-until loop constructs (yet), you can use the following method to achieve similar results: while True: do_something() if condition() : break 24. Detecting system platform To execute platform-specific functions, it is very useful to detect the platform on which the Python interpreter is running. You can use ‘sys. platform’ to find out the current platform. Example: OnUbuntu Linux »> import sys »> sys. platform ‘1100x2’ On Mac OS X Snow Leopard »> import sys »> sys. platform ‘darwin’ 25. Disabling and enabling garbage collection Sometimes you may want to enable or disable the garbage collector at runtime. You can use the ‘go’ module to enable or disable the garbage c ollection. Example: »> import gc »> gc. enable »> gc. disable 26. Using C-based modules for better performance 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, cStringlO instead of StringlO . 27. Calculating maximum, minimum and sum out of any list or iterable You can use the following built-in functions, max: Returns the largest element in the list, min: Returns the smallest element in the list. 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 (defaults to 0). 28. Representing fractional numbers Fraction instance can be created using the following constructor: Fraction ([numerator [, denominator]]) 29. Performing math operations 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’. For example: math.acos(x) : Return arc cosine of x. math.cos(x): Returns cosine of x. math. factorial(x) : Returns x factorial . 30 . Working with arrays The ‘array’ module provides an efficient way to use arrays in your programs. The ‘array’ module defines the following type: array(typecode [, initializer]) Once you have created an array object, say myarray, you can apply a bunch of methods to it. Flere are a few important ones: 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 . Sorting items 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 the right of any existing entries. 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 the left of any existing entries. 68 The Python Book Python essentials^! 32. Using regular expression-based search The ‘re’ module makes it very easy to use regxp- based searches. You can use the function ‘re.searchO’ with a regexp-based expression. Check out the example below. 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. Working with bzip2 (.bz2) compression format You can use the module ‘bz2’ to read and write data usingthe bzip2 compression algorithm. 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: ‘BZh91AY&SY\xc4\ x0fG\x98\x00\x00\x02\xl5\x80@\x00\ x00\x084%\x8a \x00”\x00\x0c\x84\r\ x03C\xa2\xb0\xd6s\xa5\xb3\xl9\x00\ xf8\xbb\x92)\xc2\x84\x86 z<\xc0’ decompressed message: ‘Kunal is a bad boy’ 34. Using SQLite database with Python SQLite is fast becoming a very popular embedded database because of its zero configuration needed, and superior levels of performance. You can use the module ‘sqlite3’ in order to work with SQLite databases. 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) ’ ’ ’ ) 35. Working with zip files 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 to a file (a string) or a file-like object. zipfile. close()H Close the archive file. You must call ‘closeO’ before exiting your program or essential records will not be written. 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’ is the password used for encrypted files. 36. Using UNIX-style wildcards to search for filenames 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 ranges expressed with [] will be matched. Example: »> import glob »> glob.glob( ‘ . / [0-9] .*’ ) [‘./l.gif’, ‘ . /2 . txt ’ ] »> glob. glob(‘*. gif ’) [‘l.gif’, ‘card.gif’] »> glob. glob(‘?. gif ’) ['l.gif'] 37. Performing basic file operations (copy, delete and rename) 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) Copies the file src to the file or directory dst. shutil . copymode (src, dst) Copies the file permissions from src to dst. shutil. move (src, dst) Moves a file or directory to dst. shutil . copytree(src, dst, symlinks [, ignore]]) Recursively copy an entire directory at src. shutil . rmtree(path [, ignore_errors [, onerror]]) Deletes an entire directory. 38. Executing UNIX commands from Python You can use module commands to execute UNIX commands. This is not available in Python 3 - instead you need to use the module ‘subprocess’. Example: »> import commands »> commands. getoutput(‘ Is’) ‘ bz2-example . py\ntest . py ’ 39. Reading environment variables You can use the module ‘os’ to gather operating- system-specific information: Example: »> import os »> os. path os. name » »> os.linesep HHr The Python Book 69 ^Python essentials 40. Sending email You can use the module ‘smtplib’ to send email using an SMTP (Simple Mail Transfer Protocol) client interface. smtplib. SMTP([host [, port]]) Example (send an email using Google Mail SMTP server): 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 FTP server ‘ftplib’ is a fully fledged client FTP module for Python. To establish an FTP connection, you can usethefollowingfunction: 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 Older versions of Red Flat Linux have been moved to the following location: ftp://archive.download. redhat.com/pub/redhat/linux/ 42. Launching a webpage with the default web browser The ‘webbrowser’ module provides a convenient way to launch webpages using the default web browser. Example (launch google.co.uk with system’s default web browser): »> import webbrowser »> webbrowser . open ( ‘ http : //google . co.uk’) True 43. Creating secure hashes The ‘hashlib’ module supports a plethora of secure hash algorithms including SFHA1 , SFIA224, SFI A256, SF IA384, SFHA51 2 and MD5. Example (create hex digest of the given text): »> import hashlib # shal Digest »> hashlib. shal(“MI6 Classified Information 007”) . hexdigest() ‘ e224bl543f 229cc0cb935aleb9593 18balb20c85’ # sha224 Digest »> hashlib. sha224(“MI6 Classified Information 007”) . hexdigest() ‘3d01e2f741000b0224084482f905e9b7b97 7a59b480990ea8355e2c0 ’ # sha256 Digest »> hashlib. sha256(“MI6 Classified Information 007”) . hexdigest() ‘2fdde5733f5d47b672fcb39725991c89 b2550707cbf4c6403e fdb33blcl9825e ’ # sha384 Digest »> hashlib. sha384(“MI6 Classified Information 007”) . hexdigest() ‘5c4914160f03dfbdl9el4d3ecle74bd8b99 dcl92edcl38aaf7682800982488daaf540be 9e0e50fc3d3a65c8b6353572d ’ # sha512 Digest »> hashlib. sha512(“MI6 Classified Information 007”) . hexdigest() ‘a704ac3dbef6e8234578482a31d5ad29d25 2c822dlf4973f49b850222edcc0a29bb89077 8aea807a0a48ee4ff8bbl8566140667fbaf7 3aldclff 192febc713d2 ’ # MD5 Digest »> hashlib. md5(“MI6 Classified Information 007”) . hexdigest() ‘ 8e2f Ic52acl46f Ia999a670c826f 7126 ’ 44. Seeding random 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 generator when the module is first imported. 45. Working with CSV (comma-separated values) files CSV files are very popular for data exchange over the web. Using the module ‘csv’, you can read and write CSV files. 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, pet in stocks: status = status. labels[cmp(float(change) , 0.0)] print ‘%s is %s (%s%%) ’ % (name, status, pet) 46. Installing third- party modules using 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 ‘easyjnstall’ to perform Python package management tasks. 70 The Python Book Python essentials^! Example (installing simplejson using setuptools): kunal@ubuntu:~$ sudo easy_install simplejson Searching for simplejson Reading http : // pypi . python . org/simple/ simplejson/ Reading http://undefined.org/ python/#simple j son Best match: simplejson 2.0.9 Downloading http: //pypi .python, org/packages/source/s/simplejson/ simple j son-2 . 0 . 9 . tar . gz#md5=af 5e67a39c a3408563411d357e6d5e47 Processing simple json-2 .0.9. tar . gz Running simple json-2 . 0 . 9/setup . py -q bdist_egg — dist-dir /tmp/easy_ install-FiyfNL/simple json-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/simple json-2 . 0 . 9-py2 . 6- linux-i686.egg Processing dependencies for simplejson Finished processing dependencies for simplejson 47. Logging to system log You can use the module ‘syslog’ to write to system log. ‘syslog’ acts as an interface to UNIX syslog library routines. 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! Third-party modules 48. Generating PDF documents ‘ReportLab’ is a very popular module for PDF generation from Python. Perform the following steps to install ReportLab $ 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 For a successful installation, you should see a similar message: It It It It It It It It It It It It I II l/vl \ I _L I 1 1 \JII II It II It It 1 1 II II It II iTTT' T TT i' I I i f TTT T fl //'7T7T If 1 1 I TlT 1 1 l lj lTT I I ItlTT r I I II Ti l l II I tnlt II II II II II II 11 II II II II II II 11 II 11 II II II II II II 11 II II II II II II II II 11 II II II #Attempting install of _rl_accel, sgmlop & pyHnj #extensions from ‘/home/kunal/python/ ReportLab_2_3/src/rl_addons/rl_accel’ h mm m i/T/Tr^ it it T in itit. i f f t it if ii ii ii ii ii ii ii ii ii ii ii ii ii ii ii ii ii ii it ii ii ii ii ii ii ii ii ii ii ii ii ii ii ii ii #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(l, 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. Using Twitter API You can connect to Twitter using the ‘Python- Twitter’ module. Perform the following steps to install Python-Twitter: $ wget http: //python-twitter, googlecode . com/f iles/python-twitter- 0.6. tar.gz $ tar xvfz python-twitter* $ cd python-twitter* $ sudo python setup. py install Example (fetching followers list): »> 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’iFI8sn0w’ , u’ IndianVideoGamer.com’ , u’FakeAaron Hillegass’, u ’ ChaosCode ’ , u’nileshp’, u’Frank Jennings’,..’] 50. Doing Yahoo! news search You can use the Yahoo! search SDK to access Yahoo! search APIs from Python. Perform the following steps to install it: $wget http : //developer . yahoo . com/ download/f iles/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 Flandover to Be Planned at London Conference, Brown Says’, from Bloomberg M The Python Book 71 2013-04-21 1427:55.333252 client 2 has joined: 2013-04*21 1427:59.363522 ciient2 says: Hi 2013-04-21 14:28:09.799543 client 1 says Hi 2013*04-21 14:28:19 703654 dientl has quit, 013-04-21 14:28:26727603 Server has quit. 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 "With Python, you can tweak and realise your ideal system set-up" 72 The Python Book JA kspnOuttcruLI Afdumo i:1.D.5idrijZ 4 I Sm«c>i N«to ud QDD 82 88 Jtvil&i anpwt. »iilH totNi Mtul toi.Ni fc*S r- mini iwl pin 1 1 ; s ft# wslt t* • rmm t'f< 1*4 c*i* f!« 'twll te Mt iKf }«lH«£4ftk Hluhn* (Wf} it to 0*1*11 toluilli t* ih* wml *iarilu r 1h* ciPtiLil * IftrhfUrt tl to'l»j fl4 & pip ct it* jwwiutow* iw it in to»‘*q r>A Ii4t pin 9f ihi pcitoliwiKr ps 19 iW tod fftv*d ® !#•*** t id frg* iifitql Pin ft *■ i*totod riUH 3 r« 2®r Mf tto 0 SOU a* fto tHlt M*** 1 .! Cto J4 %» to N& Lit Selur // ItaU fto*llnfv «"1 «Wf! I tot » II flto «i // tt &• P4.*l urt*d ■ - Mt»i’iu|iiitoik 4 m- ■ « J ml tn! BfittflUHrmin * H . |“[II + ■ - ■ ■ ■ •’ Conivguf* Ikttoltotw Unmittll nuHiito* CMgt^ Addon trifrrmatian LUDfledd it Viewer Media sources Author LUO Vwtiti 0,0 J Rating LllDEni £x*m( Dei r. Dpt iuii LuD Ei ample Addon Id deKWrfiStJ ate ) MdartSenpliuDt 41i^# W-9*i la* t til p*i»*i-Mi|>lBiUfe r‘Q PIC**0f! llltl... ftOlto Wildwm w*rid#n- ¥ %tm n«vdln| iT-iit infvtm*. io«i.. . sotoe 1H* toUttolng ntrB pjckagtt mill l» inulUn; bit fanli-lyx |i4i>»|||b-M l 1 rp pg e t taiy-l , ft pylMlMlievtil p^tftOfl-^L PVtUon-f U0«I Pftlwi-^ pyLMn-HtptQftlth-Qltl python- .pypar E PfttoHh luggeiied jUChrytt? bU-ftttoi pytbfm-fli-eiUo fflrth&ft-gfftJ-dK pylTnsn-g; pyttiVJl^nf t^i n tmwf-*iCltiritdr pylhgir -triple thit roiiswing mu pacing** *iti tm mtiUwii bit ffthtt-Lfr Ubflirtpdv Hdry-1,# pyiiwa-oiiBvtiii pythati-fli pyt n-wi-fllwi*; python-p PftMjimtplntlll pjftimp-utplBtUHlU pytnofi-p i uE^fid*d f V nr.lv IfliiiUp* t ic rvnvi and * f* Htst4 to fit lf«4 wft of iroHlw^ An*n mi op*r»tlo* # 31, 3 Kb idflitloril dll* ip Did yuu Vim to £dn1 i flirt [T/nll T Celt I nit ps/jPf ip. ji .flriitriH arg/dftil*n/ rf-ot-S /■/■** Ift Get. ‘I Ml ps//f f p j 9 .Or&iin , grg/dfiftiiH/ ^my/iuLn tttM ft? hfti 6*1 ! 4. ii i ( p : / / f t f . ui« .{JfSir.g tg/det 1 iln f #9f^/tolh tuts http://f rp.MinfiioHii+Di^iUtftUnf oM-try/uin Gftsft htlpi/yflp,ul^E^Un.C!ri/4eDli!i^ Nbtfj/W 1* G4t.? Ml ps //ftp .«■ ,OirD lan „ pr^/dkOJ. rf^try/iiiln m bittf http://fTp.aE.oroiin.dri/dtoiinA «titrry/aitn G«ti4 http ;//ftp.oi,d«ftiifi.Qri/dttft l*n/ >#*aty/o«in 61 6* l :1ft htlfti //ftp. ue . dvblflii.pi'py'drblin/ *fM«iy/«ai/i The Python Book 73 ^Work with Python PYTHON FOR PROFESSIONALS Python is relied upon by web developers, engineers and academic researchers across the world. Here’s how to put your Python skills to professional use 74 The Python Book Work with Python System administration Get the most out of Python in handling all of the day-to-day upkeep that keeps your system healthy 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. They started with basic shell scripts, and then moved on to various scripting languages. For a long time, Perl had been the language of choice for developing these types of maintenance tools. Plowever, 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 have any excuse for not writingyour own scripts. 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 environment variables active right now: SYSTEM ADMINISTRATION BASH, PERL, PYTHON Left Python scripts enable you to instruct and interact with your operating system OPERATING SYSTEM v / \ CPU FILES/10 way to do this is to edit the values directly within the environs mapping. Another category of tasks you may want to automate is when working with files. For example, you can get the current working directory with code like import os os. environ You can get a list of the available environment variables with the function “os.environs.keysO”, and then access individual variables with “os.environs[keyj”. These environment variables are used when you spawn a subprocess, as well. So you will want to change values, like the PATFI 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 cwd = os.getcwd() You can then get a list of the files in this directory with 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.openO” and open it for reading, writing and/or appending. You can then read or write to it with the functions “os.readO” and “os.writeO”. Once done, you can close the file with “os.closeO”. Running subprocesses from Python 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 “popenO” and “spawnlO” 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 Is, by using: import subprocess subprocess. runflYls’, ‘-1’]) 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 “popenO” function that we mentioned earlier instead. If you want to get the output, you can use the following: cmd_output = subprocess. run([‘ls’, ‘-1’], stdout=subprocess.PIPE) The variable “cmd_output” is a Completed Process object that contains the return code and a string holding the stdout output. I J Scheduling with cron Once you have your script all written up, you may want to schedule them to run automatically without your intervention. On Unix systems, you can have cron run your script on whatever schedule is necessary. The utility “crontab -1” lists the current contents of your cron file, and “crontab -e” lets you edit the scheduled jobs that you want cron to run. The Python Book 75 ^Work with Python Web development Python has several frameworks available for all of your various web development tasks. We will look at some of the more popular ones 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 postgresql to store your application data. There are Django utilities available to automatically generate a starting point for your new project’s code: 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-compatible web servers. 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 following command: 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 need to restart the server to see your code changes. All of these steps get you to a working project. You are now ready to start developing your applications. Within the “newsite” subdirectory, you can type: 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. The simplest possible view consists of the code: from django.http import HttpResponse f 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’t exist yet, create it and then add the code: from django.conf.urls import url from . Import views urlpatterns = [ url(r ,A $’, views. index, name=‘index’), ] The last step is to get the URL registered within your project. You can do this with the code from django.conf.urls import include, url from django.contrib import admin urlpatterns = [ url(r ,A newapp/’, include(‘newapp. urls’)), Left Python interpreters work with your databases to power a Webserver Bottom The Model-View- Controller architecture is often used for Uls Virtual environments When you start developing your own applications, you may begin a descent into dependency hell. Several Python packages depend on other Python packages. This is its strength, but also its weakness. Luckily, you have virtualenv available to help tame thisjungle. You can create new virtual environments for each of your projects. Thankfully with this, you can be sure to capture all of the dependencies for your own package. 76 The Python Book Work with Python^ Using the PyCharm IDE * -■•■•.p- P* ■ KVCh#rr%teirrtihc*NlT Ldi?ifr" T t 0 ■ £»♦*■ E-* 1 tt**t*" C/rt* v v?i a**- r yNlUH a|k T+Kl tv 1 0 i * r - 4 indn ■ THE EDITOR PANE * <***! P< liffft -■■■ j.pj The main editor pane can be configured to match your own style, orthe style of one of the other main editors, ► ii iMpnn y&roi like emacs. It handles syntax highlighting, and even displays error locations THE PROJECT PANE This pane is the central location foryour project. All of your files and libraries are located here. Right-clicking in the pane brings up a drop-down menu where you can add new files or libraries, run unit tests, or even start up a debugger THE STATUS BARE Tool Window? Quick Access k«p b+iewle- acctntodl wtmVwt cfccl fTOTi tt] Ifi&fcH tort MtrtkHrt iHJfctHW Hit”* PyCharm does a lot of work behind the scenes. The status bar helps you keep track of all of these background processes ^1 11 n TPMl#4 f-BU WHi* iw* ^ h«mi *wj id** f fewhPh* M itnpVf^'V f fh# rj-mv,*** *4*1 ■ >.» UTJ urlCr^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 application with the URL http://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 this command: python manage. py migrate Terminal development environments When you are in the middle of developingyour application, you may need to have several different terminal windows open in order to have a code editor open, a monitor on the server, and potentially testing output. If you are doingthis on your own machine, this isn’t an issue. But, if you are working remotely, you should look into usingtmux. It can provide a much more robust terminal environment for you. 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 I NSTALLED_APPS section of the “settings. py” so that django knows to include it in any database activity. You initialize it with: python manage. py makemigrations newapp Once these migrations have been created, you need to apply them to the database by using the command: python manage. py migrate Any time you make changes to your model, you will need to run the makemigrations and migrate steps again. 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 puttingtoo much work into developingyour own code. Other Python frameworks While Django is one of the most popular frameworks around for doing web development, it is by no means the only one arou nd. There are several others available that may prove to be a better fit for particular problem domains. For example, if you are looking for a really self-contained framework, you could lookatweb2py. Everything you need to be able to have a complete system, from databases to web servers to a ticketi ng system, are included as part of the framework. It is so self-contained that it can even run from a USB drive If you need even less of a framework, there are several mini-frameworks that are available. For example, CherryPy is a purely Pythonic multi-threaded web serverthatyou can embed within your own application. This is actually the server included with TurboGears and web2py. A really popular microframework is a project called flask. It includes integrated unit testing support, jinja2templating and RESTful request dispatching. One of the oldest frameworks around is zope, now up to version 3. This latest version was renamed BlueBream. Zope is fairly low-level, however. You may be more interested in looking at some of the other frameworks that are built on top of what is provided by zope. For example, pyramid is a very fast, easy to use framework that focuses on the most essential functions required by most web applications. To this end, it provides templating, the serving of static content, mappingof URLs to code, amongotherfunctions. It handles this while providing tools for application security. If you are looking for some ideas, there are several open source projects that have been built using these frameworks, from blogs, to forums to ticketing systems. These projects can provide some best-practices when you go to construct your own application. The Python Book 77 ^Work with Python Computational science Python is fast becoming the go-to language for computational science 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 heavy computations to more efficient code. 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 you wanted to scale a vector by some value, something like a*b. In regular Python, this would look like for elem in b: c.append(a * elem) In numpy, this would look like: a*b 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, to take advantage of this new datatype. 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 Left The numpy package makes it simple to visualise your data Parallel Python One of the really powerful parts of Ipython (or jupyter) is that it is built with a client/ server model. This means that it is relatively easy to setup multiple machines to act as a server pool. You can then farm out multiple tasks to these other machines to get even more work done. While this doesn’t run any particular function in parallel, it does let you run longer functions in the background while you work on somethingelse. Spyder, the IDE for scientists □ ' U U ► * t * ^ ^ N , * Lr*Y> ■ Bifl At* #■ #« V+i«* -# f ■ ■ CSABEiv P^ tfcap i wammtr (Ad tjt l# flti&dfq irf* ■ IjAf 11 CafeppiL 1 78 The Python Book Work with Python ^ 00 + Above The ability to generate complex plots is essential differential equations, you can use the “integrate” section to solve them with code that looks like import scipy import scipy. integrate result = scipy. integrate. quad( x: sin(x), 0, 4.5) The need for speed Sometimes you need as much speed as your are capable of pushing on your hardware. In these cases, you always have the option of using Cython. This lets you take C code from some other project, which has probably already been optimised, and use it within your own Python program. In scientific programming, you are likely to have access to code that has been worked on for decades and is highly specialised. There is no need to redo the development effort that has gone into it. 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 package is definitely the first stop for anyone wanting to do any scientific processing. 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 on a cluster. 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 “symbolsQ” function to tell sympy Interactive science withjupyter Fora lot of scientific problems, you need to play with your data in an interactive way. The original way you would do this was to use the Ipython web notebook. This project has since been renamed Jupyter. Forthosewhohaveuseda program like Mathematica or Maple, the interface should seem very familiar. Jupyter starts a server process, by default on port 8888, and then will open a web browser where you can open a worksheet. Like most other programs of this type, the entries run in chronological order, notin the order thatthey happen on the worksheet. This can be a bit confusing at first, but it means that if you go to edit an earlier entry, all of the following entries need to be re-executed manually in order to propagate that change through the rest of the computations. Jupyter has support for pretty printing math within the produced web page. You can also mix documentation blocks and code blocks within the same page. This means that you can use it to produce very powerful educational material, where students can read about some technique, and then actually run it and see it in action. By default, Jupyter will also embed matplotlib plots within the same worksheet as a results section, so you can see a graph of some data along with the code that generated it. This is huge in the growing need for reproducible science. You can always go back and see how any analysis was done and be able to reproduce any result at all. Above Jupyter Notebook is a web application that is used for creating and sharing documents that contain live code and equations what variables are valid to be considered in your equations. You can then start doing manipulations using these registered variables. 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 heaviest lifting is done by Cython code that incorporates functions written in C. Quite a few ideas on how to manipulate your data was borrowed from how things are done in R. You now have no reason not to start using Python for your scientific work. You should be able to use it for almost any problem that comes up! The Python Book 79 Work with Python Robotics and electronics 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 work done. 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 Robotics is the most direct interface between your code and the real world around you for machine learning, where you can do statistical classification or data clustering, and use it to feed decision trees or even neural networks. 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 commands to execute. 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 Arduino In contrast to the Raspberry Pi, which runs a full OS from its SD card, the Arduino boards are microcontrollers rather than complete computers. Instead of runningan OS, the Arduino platform executes code that is interpreted by its firmware. It is mainly used to interface with hardware such as motors and servos, sensors, and devices such as LEDs, and is incredibly capable in this regard. Arduinos are widely used in robotics projects and can be a powerful complementtothe Pi. Raspberry Pi While we haven’t discussed what kind of computerto use for your robotics project, you should considerthe famous Raspberry Pi. This tiny computer should be small enough to fit into almost any robot structure that you might be building. Since it is already running Linuxand Python, you should be able to simply copy your code development work to the Pi. It also includes its own 10 bus so that you can have it read it’s own sensors. ROS - Robot Operating System 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. 80 The Python Book Work with Python^ For low-level work, check out Arduinos * AnaloglnOutSerial | Arduino 2:1 .0.S+dP&g £-4 File Edit Sketch Tools Help T AfialoQlnOut serial Analog input, analog output, serial output * Reacts an analog input pin ? naps the result to a range frou 0 to 255 and uses the result to set the pulsevidth lodulation [fwO of an output pin, Also prints the results to the serial aemitor. The circuit; * potentiometer connected: to analog pin G. Center pin of the poientioaeier goes to the analog pm side pins of the potentiometer go to +5V and ground * LEX' connected fro* digital pin 9 to ground created 29 c^c. 2we ■odifisd 9 Apr 1312 bf Toi lgo« This example code is in the public donain /,/ These constants von't change // to the pins used; const int analog inPin - AG: • Analog input pin that 1 const int analoqOutPin - 9; •/ Analog output pin that t THE MAIN EDITOR You have access to a large number of libraries, and support fora large number of versions of the Arduino boards. The code is essentially C, so Python programmers shouldn’t be too far out of their depths OUTPUT WIN DOW This pane contains output from various tasks. This might be compilingthe source code, or uploading it to the Arduino board being used in your project 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 connect to the serial port and read data off the connection. You can use: 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 some debugging time. 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 the most popular candidates for this task is the Arduino. 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 to provide the sensing capabilities that you require straight out of the box. There are also several models of Arduino, so you may be able to find a specialised model that best fits your needs. 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, like SimpleAl or PyBrain. Bypassing the GIL For robotics work, you may need to run some code truly in parallel, on multiple CPUs. Python currently has the GIL, which meansthatthereisa fundamental bottleneck built i nto the i nterpreter. One way around this is to actually run multiple Python interpreters, one for each thread of execution. The other option is to move from Cpython to either Jython or IronPython, as neither has a GIL. The Python Book 81 i^Work with Python f Vnfcw W-0P4 4 13 AM Current media selection k LUDMdrtV^tf IUO * 1 « 001 r, lUDEAlEaMfle Configure launcher Rating (only available for hosted plug-ins) Localised description string Make extensions for Opens changelog forthe plug-in XBMC 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 Resources XBMC: www.xbmc.org/download Python 2.7x Python IDE (optional) Code on FileSilo 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, Mac OS X, Android, iOS and Raspberry Pi. 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 like C/C++ (from which the core of XBMC is made). 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 the look and feel of just about every facet of the package. Depending upon which category your extension fits, you will have to create the extension directory accordingly. For example. . . Plug-ins: plugin. audio.ludaudi: An audio plug-in plugin.video.ludvidi: Avideo plug-in script.xxx.xxx: A program 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. Our plug-in will show various content such 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 user experience. Due to space limitations, we aren’t able to print the full code here. We recommend downloading the complete code from FileSilo. 82 The Python Book Work with Python^ Preparing the directory structure 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 folders and files as well. The following describes the directory structure of LUD Linux Entertainer: plugin.video.ludent - Root Plugin directory |— addon.xml l-changelog.txt |— default.py |— icon. png |- LICENSE.txt |- README resources I- lib settings.xml Creating addon.xml An addon.xml file needs to be created in the root of the extension directory. The addon.xml file contains the primary metadata from a XBMC extension. It contains overview, credits, version information and dependencies information about the extension. The root element of addon.xml is the element. It is defined as: cimport addon=”plugin .video, youtube” version=”3 . 0. 0”/> I n the above code we have added a dependency 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 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 to 2.0 then it would only work in XBMC Eden 1 1 .0 and not in the latest version. For the current version of XBMC 12.1, the following versions of core XBMC components are shipped: xbmc.python 2.1.0 xbmc. gui 4.0.0 xbmc.json 6.0.0 xbmc. metadata 2.1 .0 xbmc. addon 12.0.0 In addition to xbmc.python we are also adding 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. Setting up the provider and entry point Our extension is supposed to provide the video content for XBMC. In order to convey that, we have to set up the following element: : 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 (Windows OpenGL) and android. : This gives a brief description of the plug-in. Our example sets the language attribute as English, but you can use other languages too. : A detailed description of the plug-in. : Webpage where the plug-in is hosted. : Source code repository URL. If you are hosting your plug-in on GitHub, you can mention the repository URL here. : Discussion forum URLforyour plug-in. : Author email. You can directly type email or use a bot-friendly email address like max at domain dotcom. Setting changelog, icon, fanart and licence 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 is visible fromtheXBMCUI. An example changelog: 0 . 0.1 - Initial Release 0 . 0.2 - Fixed Video Buffering Issue icon. png: This will represent the plug-in in the XBMC Ul. It needs to be a non-transparent PNG file of size 256x256. 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) up to the maximum 1 920x1 080 (1 080p). » The Python Book 83 ^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 copyleft licences can be used. Note: For the purpose of packaging, extensions/ add-ons/themes/plug-ins are the same. Providing settings for the plug-in Settings can be provided by the file resources/settings.xml. These are great for user- configurable options. Partial: resources/settings.xml ccategory label=”30109”> *!■ froi* nu«py luport * nyArray - imyl »* nyftrray.Hi ni| 3 -IBS >» nyflrrflVi.MKU 2M lyAffllyi.MaPij |i 24.333333313333312 »> riyArrfty rriBfl IftriH Traceha^k Cnost r-ecerlT call liS-th File "■cstdirii *’ 1 ' r line in smcdule* AttribkCeError : 1 njmpy . ndsrray 1 object has no iti rlbute ’median 1 >»> nyArrBy.ncdlBpn Trace back fnost recent call lastjs File t line 1* in ^nodule* AttrlbiiteError: 1 n jepy. ndarray 1 object has no a*tt rlbute ‘iiedian* »> ayArray P Bedian(3 Trace back C nest recent call Uitb File “<5tdin>", line 1* in ™dule> AttnbiuteError; , minpy.fid«rrsy 4 object has no attribute ‘median 1 >» iiyArray .med lanieyAr rayj Traceback (most recent call Intli File , line l, in ^t«ddule> AttribiateError: 4 njnpy,ndarray 1 ebject has no attribute ’median* 03 Making simple calculations Traceback (most recent call last): File ””, line 1, in 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. About NumPy 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 usingthe arrayO function: »> oneD = array([l,2,3,4]) The aforementioned command creates a one-dimensional array. If you want to create a two-dimensional array, you can use the arrayO function as follows: »> twoD = array([ [1,2,3], [3,3,3], [- 1 ,- 0 . 5 , 4 ], [ 0 , 1 , 0 ]] ) You can also create arrays with more dimensions. Making 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.maxO 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. Using arrays with NumPy 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 following notation: »> twoD[l,2] You can also select a part of an array (a slice) using the following notation: »> twoD[:l,l:3] Finally, you can convert an array into a Python list using the tolistO function. Readingfiles Imagine that you have just extracted information from an Apache log file using AWK and you want to process the text file using NumPy. The following AWK code finds out the total number of requests per hour: $ cat access.log | cut -d[ -f2 | cut -d] -fl | 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'') Writing to files Writing variables to a file is largely similar to reading a file. If you have an array variable named aal, you can easily save its contents into a file called aal.txt by using the following command: In [17]: np.savetxt(''aal.txt'', aal) As you can easily imagine, you can read the contents of aal.txt later by using the loadtxtO function. Common functions NumPy supports many numerical and statistical functions. When you apply a function to an array, the function is automatically applied to all array elements. 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)”. Working with matrices 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 usingthe matrix (or mat) function as follows: 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. » The Python Book 89 Work with Python f$h - 90*45 ■ QQPlotting with Matplotlib ml'uxilc — lUO/var - Wjhj nnYfMil:^* g.pt-gc-t imtiU pythan-Bstplotltb Reading package Lists.,, Done Building dependency tree Redding stale info rut torn < . Pone the following e*tri packages will be instolledt bit fcnti’lyn gi rln i-gUb-2, & libgireppsitorY-l*^! Libgt4de2-0 python-ealro python -da leu til python gl python -glade 2 python-gobject python-gob )eet-a python-gtkz python-malplatUfr-dftta python-pyparsing python-tk python-ti Suggested packages; ftlt-deso python-gi-cairo pytbo#i-gH2-dac python -gob ject-z-dbg dvipng ipythen python -conf igobj python -exeele rat or p?th&n-*atplotUb-tfoc python -”4 (4 python- traits pyt han-wsgtkZ. fl tenllwe-est r»-ut its (ex live- latex-extra tlx The fallowing NEW packages will be installed: bit Tqnts-lyx girl, 2-ylib*’2„ G libgireposltory-l .0-1 libqlade2-t python-cairn python -da teu til python -gi python-glade I python -gob jeet python-gobjecT-2 pythor-glkZ python ir.at plot Lib python matplotlib- data python pyparsing pythan th python -tz 9 upgraded, 17 newly installed, O to remove and G not upgraded. Need to get It. 4 HD of archives. After this operation, 31. j MB of additional disk space will be used, Uoi ypu want tn rnot inue [r/n | ? Y Get: i n 1 1 p ; // f t p . us , debt a n . g f g/dep i an / wfoeezy/B®in bit 3beJG 4 2,4z-4-2 (1 + 654 K0l Get: 2 http://f Ip, us .deblan- org/deblan/ vheejy/aalri fonts-ly* all 2 ,0,3-3 (16? kh] Get: 3 http;//ftp,u5pdebidn,Drg/dcbian/ wfieezy/aain libqi repos it sjry-l, 4-1 aad64 1.32,1-1 Si 07 kB) Get: 4 bt tp://f tp. os .debtan . o rq/deb ian/ wheeiy/noin girl. 2-g lib-2, 0 aw36* 1.32.1-1 C171 kB] Gets 5 httpf//ftp,u9,debi*n, org/debian/ wheery/Bain UbgladeS-B aind64 ii2,6.4-l (89- & kftt Get: 6 http://f|p,oi.debijn,orq/debian/ wheery/Bain python-ctlro aadfifl 1.8,3-1+02 (84,2 kB] Get: 7 http://fip.of .debijn.gfg/de&ian/ wtievzy/naln pythan-dateutil all l,5+#fig<4,l [Sl+3 kB? Get: a rmpi//ftp>ui .dehlan.Qrg/tieblan/ whecry/Bain pythnn-gl avd64 3. 2,2-2 I lie kB] Get: 9 http:/yf(p.us.drbldn,Qrg/debian/ wheezy/Min python-gdb]ect-2 flBdfrf 2,21,6-18 (555 k fll Get: 10 hi ip ; //f cp. us. deb Ian. dr g/debian/ wbeely/Baln pytbOn-|tk2 amM* 2,24.l-3*bl (1,815 k 8J Get: 11 http47/ftp^ uifrom 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 . Interfacing operating system & 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, users and file descriptors. 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. os module environ: environment represents the OS environment variables in a string object. 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 . f ramework/Versions/2 . 7/bin : /Users/ kunaldeo/narwhal/bin : /opt/local/sbin : / usr/local/bin : /usr/bin : /bin : /usr/sbin : / sbin : /usr/local/bin : /usr/Xll/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[l ; 34m\\]\\w\\[\\e[m\\] \\ [\\e[l ; 32m\\]\\$\\[\\e[m\\] \\ [\\e[l;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 . f ramework/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/pgknm8bll8 737mb8psz8x4z80000gn/T/ ’ , ‘ LSCOLORS ’ : ‘ ExFxCxDxBxegedabagacad ’ , ‘ CLICOLOR ’ : ‘ 1 ’ , ‘ CF.USER.TEXT.ENCODING ’ : ‘0xlF5: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. The Python Book 99 ^Work with Python « listdir(path) : Similar to Is, returns a list with the content of directories and file available on the given path. command with arguments. On process completion it returns the returncode attribute. 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 Qt based. Example: »> os.listdir(‘7home/homer”) [ ‘ . gnome2 ’ , ‘ . pulse ’ , ‘ . gconf ’ , ‘ . gconfd ’ , ‘ . beagle ’ , ‘ . gnome2_ private’, ‘ .gksu. lock’ , ‘Public’, ‘ . ICEauthority ’ , ‘ . bash_history’ , ‘.compiz’, ‘.gvfs’, ‘.update- notif ier ’ , ‘ . cache ’ , ‘ Desktop ’ , ‘Videos’, ‘.profile’, ‘.config’, ‘.esd_auth’, ‘.viminfo’, ‘.sudo_ as_admin_successf ul ’ , ‘ 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 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, this is the console input buffer. STD_OUTPUT_HANDLE: The standard output device. Initially, this is the active console screen buffer. STD_ERROR_HANDLE: The standard error device. Initially, this is the active console screen buffer. 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 IPython without any problems. 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 can also install it using easyjnstall or PyPI. IPython provides a lot of interesting features which makes it a great shell replacement. . . 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 os.pardir os . popen os. path os . popen2 os.pathconf os.popen3 {hit tab} os.pathconf_names os.popen4 os.pathsep os.putenv os. pipe 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. SECTION 2: IPython: a ready-made Python system shell replacement 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 Example: In [28]: os. path? Type: module Base Class: String Form: Namespace: Interactive File: /System/Library/Frameworks/ 100 The Python Book Work with Python U I Python also comes with its own Qt-based console You can start the Qt console with: $ ipython qtconsole If you get errors related to missing modules, make sure that you have installed the dependent packages, such as PyQt, pygments, pyexpect and ZeroMQ. Python . f ramework/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. path provides the same operations in a manner specific to that platform, and is an alias to another module (eg macpath, ntpath). %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 Some of this can actually be useful on non- POSIX systems too, eg for manipulation of the pathname component of URLs. You can also use double question marks (??) to view the source code for the relevant object. Magic functions: I Python 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 magic functions. 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 To view help on any Magic Function, call ‘%somemagic?’ to read its docstring. 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 defined bythe$EDITOR environment variable. Shell command support: If you are in the mood to just run a shell command, you can do it very easily by prefixingthe command with ! . 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() A 1 warning generated. Qt console : I Python 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, and graphical calltips . 1 i v h & ■ IPython Qt console with GUI capabilities 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 gold when platinum is a step away? The Python Book 101 fcwork with Python Python for system administrators Learn how Python can help in system administration as it dares to replace the usual shell scripting. . . Resources Python-devel Python development libraries, required for compiling third-party Python module setuptools setuptools allows you to download, build, install, upgrade, and uninstall Python packages with ease 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 (not just one but many, including Bash, 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 snake connection?). Also, tools like GNU Mailman, 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 - you can build command-line, GUI and web apps using Python. This way, it has got covered almost all the possible interfaces. Here we will look into executing sysadmin- related tasks using Python. Parsing configuration files Configuration files provide a way for applications 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 source software, such as PHP and MySQL. lExcerptforphp.ini configuration file:| [PHP] engine = On zend. zel_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). You can use this module to parse and create configuration files. @code: writeconf ig.py ■ ©description: The f< allowing demonstrates! adding MySQL section to the php.ini file, ©warning: Do not use this script wil ■ th i actual php.ini file, as it’s not designed handle all aspects of a complete php.ini file. 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^B^ ^S^^^^™ ©description: Parsing and updating the confij m import ConfigParser config = ConfigParser . ConfigParser() config. read ( ‘php. ini’) # Print config values print config. get(‘MySQL’ , ’mysql. 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, BSDsand Mac OS X). 102 The Python Book Work with Python^ default_host ’ ) print config.get(‘MySQL’ , ’mysql. default_port ’ ) conf ig . remove_option ( ‘ MySQL ’ , ’ mysql . trace_mode’ ) with open( ‘php. ini ’ , ‘wb’) as configfile : conf ig. write (conf igfile) Parsing JSON data 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 JSON data. ■ fo use this section, you should have the 1. Python module: simplejson. Note: You can install Python modules using the command ‘easyjnstall Cmodule name>’. This command assumes that you have a working internet connection. | 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 the screenshot below for details. ■ Generating the Yahoo App ID 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 data without developing any XML parsing code. JSON PYTHON DATA MAPPING JSON Python object diet 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 JSON object into a Python object. import simplejson, APP_ID = ‘xxxxxxxx your APP ID urllib ’ # Change this to SEARCH_BASE = ‘ http : //search . yahooapis . com/WebSearchService/Vl/ webSearch ’ class YahooSearchError(Exception) : pass def search(query , results=20, start=l, **kwargs) : kwargs. update({ ‘appid’: APP_ID, ‘query’ : query, ‘ results’ : results, ‘ start’ : start, ‘output’: ‘json’ }) url = SEARCH_BASE + ‘?’ + url lib. 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 open a Python shell. @code: Python Shell Output. Lines startinj with *>»’ indicate input^ »> 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; Developer | Linux User http: //www. linuxuser . co.uk/ tag/ linux- user-developer / Gathering system 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, g IGAR is a very complete API and it can provide lot of [information, including the followini 1.| System memory, swap, CPU, load average, uptime, logins. | Per-process memory, CPU, credential info, state, arguments, environment, open files. | File system detection and metrics. ( Network interface detection, configuration info and metrics. |TCP and UDP connection tables. | Network route table. Installing SIGAR 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 Python bindings: $ git clone git : //github. com/ hyperic/sigar.git sigar.git $ cd sigar. git/bindings/python $ sudo python setup. py install i Python doesn’t limit your choice of interface The Python Book 103 ^Work with Python At the end yo u should see a output similar to| the following:®- ’ ' Writing /usr/local/lib/python2 . 6/ dist-packages/pysigar-0. 1 . egg-info SIGAR is a very easy-to-use library and can be used to get information on almost every aspect of a system. The next example shows you how. The following code sho ws the memory and the] file system informati on @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 “\tTotal\tllsed\tFree” 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 ‘ Filesystem\tSize\tUsed\ tAvail\tUse%\tMounted on\tType\n ’ 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() pet = usage. use_percent() * 100 if pet == 0.0: pet = ‘ - ’ print fs.dev_name() , format. size(total), format_size(used) , format.size(avail) ,\ pet, dir.name, fs.sys_type_ name(), ‘/’, fs. type_name() temwrani ==========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 Accessing Secure Shell (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 protocol for Python. param iko can be installed using the followinj 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}, [-{Channel}, and L{SFTPClient} to handle most of the aspects of SSH . You can use split(‘@’) else: hostname = raw_input( ‘Flostname: ‘) if len(hostname) == 0: print ‘*** Flostname required.’ sys.exit(l) 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. SSFIClient() client. load_system_host_keys() client . set_missing_host_key_ pol icy (paramiko . WarningPolicy) print ‘*** Connecting...’ client. connect (hostname, port, username, password) chan = client . invoke_shell() print repr (client. get_transport()) print ‘*** SSH Server Connected! kkk 1 SSHClient as: client = SSFIClient() client. load_system_host_keys() client . connect ( ‘some. host . com’ ) stdin, stdout, stderr = client. exec_ command( ‘dir ’ ) The following example demonstrates a full SSH client written using the paramiko module. import base64, getpass, os, socket, sys, socket, traceback import paramiko import interactive # setup logging paramiko . util . log_to_f ile( ‘demo_simple . log’) # get hostname username = ‘ ’ if len(sys.argv) > 1: hostname = sys.argv[l] if hostname. find( ‘@’ ) >= 0: username, hostname = hostname. 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(l) To run this code you will also need a custom Python class interactive. py which implements Note If you are confused with the tab spacing of L the code, lookforthe codefiles on FileSilo. A 104 The Python Book Work with Python the interactive shell for the SSH session. Look for this file on FileSilo and copy it into the same folder where you have created PySSFIClient.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. . . *** 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 youjy $HOME/.ssh/known_host s file, the client will throw the following errorj *** Caught exception: : 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 the ssh client. 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 : dl . 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’s versatile language. As is the case with all Python coding, the code that is presented here can easily be adopted into your GUI application (with software such as PyGTK or PyQt) or a web application (using a framework such as Django or Grok). Writing a user interface using Python Learn howto create a user-friendly interface using 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 users to select default boot entry and the timeout. 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 the Python audio library, tksnack). — This app consists of two files. . . grub.py: GRUB Config File (grub.conf) Parser (available on FileSilo). It implements two main functions, readBootDBO and writeBootFileO, which are responsible for reading and writing the GRUB configuration file. Text user interface file for manipulating the GRUB configuration file using the functions available in grub.py. (((“Ok”), “ok”), ((“Cancel”), ‘cancel”)^^ e=Entry (3, st r(entry_ yalue)) l=Label((“Timeout (in seconds) : ”)) The Python Book 105 108 Build tic-tac-toe with Kivy Program noughts and crosses 112 Create two-step authentication UseTwilio 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 PygameZero Turn your ideas into games Here’s scene two! 108 |° X 0 0 X "You'll be surprised by the diversity of what you can make with Python" 106 The Python Book The Python Book 107 ^Create with Python Build tic-tac-toe with Kivy Ease into the workings of Kivy by creating the pen-and-paper classic in just over 100 lines of Python... 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 only on the traditional desktop platforms of Linux, OS X and Windows, but also Android and iOS, plus devices like the Raspberry Pi. 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 graphics pipeline, allowing advanced GPU effects even though the basic Python API is very simple. Kivy is a set of Python/Cython modules that can easily be installed via pip, but you’ll need a few dependencies. It uses Pygame as a rendering 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, or via pip where applicable. 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, running in python2.7 and python3.3. pip install kivy If you have any problems with pip, you can use easyjnstalvia easy_install kivy. There are also packages or repositories available for several popular distros. You can find more information on Kivy’s website. A kivy application is started by instantiating and running an 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 simplest Kivy app possible: from kivy. app import App TicTacToeApp(App) : pass if name == “ main T icTacToeAppO . r un () You can already run this, your app will start up and you’ll get a plain black window. 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 Textlnput), 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 for every need imaginable, widgets are kept simple but are easy to join to invent new interfaces. We’ll see some of that in this tutorial. Since ‘Hello World!’ is basically compulsory in any programming tutorial, let’s get it over with by using a simple ‘Label’ widgetto display the text: from kivy. uix. label import Label You can develop cross-platform apps using various Python libraries We’ll display the ‘Label’ by returning it as our app’s root widget. Every app has a single root widget, the top level of its widget tree, and it will automatically be sized to fill the window. We’ll see later how to construct a full GUI by adding more widgets for this one, but for now it’s enough to set the root widget by adding a new method to the ‘App’: if build(self): return Label(text=’ Hello World!’, font_size=100, Hello World! ■ The classic ‘Hello World!’ in Kivy GUI form, using the built-in Label widget color=0, 1, 0, 1)) 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 them just once upon instantiation. 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, and you should do the same if you want to use your variables with Kivy’s event or bi ndi ng f u nctionality. That’s all you need to show some simple text, so run the program again to check that this does work. You can experiment with the parameters if it’s unclear what any of them are doing. Our own widget: 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 widget class to contain this behaviour: from kivy. uix. gridlayout import GridLayout 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 108 The Python Book Create with Python 0 0 X 0 X ■ Atic-tac-toe grid now accepting input, adding a 0 or X alternately automatically arrange any child widgets to fit nicely with as many rows as necessary. Tic-tac-toe requires three columns and nine children. 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 manipulatingthe GUI with Python code - as a loose analogy you might think of it as the HTML/CSS to Python’s JavaScript. Python gives us the dynamic power to do anything, but all that power gets in the way if we just want to declare the basic structure of our GUI. Note that you never need kv language, you can always do the same thing in Python alone, but the rest of the example may show why Kivy programmers usually like to use kv. Kivy comes with all the tools needed to use kv language; the simplest way is to write it in a file with a name based on our App class. That is, we should place the following in a file named ‘tictactoe.kv’: : cols: 3 1 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 the former, creating a rule for the ‘TicTacToeGrid’ widget by declaring that every ‘TicTacToeGrid’ instantiated should have its ‘cols’ property set to 3. We’ll use some more kv language features later, but for now let’s go back to Python to create the buttons that will be the entries in ourtic-tac-toe grid. from kivy. uix. button import Button from kivy. properties import ListProperty 5S 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 created a new Kivy property for our widget, ‘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 a more interesting feature. : font_size: self. height J Kivy comes with all the tools needed to use kv language 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 that we’ve referenced the Button’s own height and 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 referencing the property we want to bind to. 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. This is a minor feature that is automatically handled by Kivy properties. 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 wheneverthe property is modified. 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 number of columns we set earlier. Now all we have to do is replace our root widget (returned from ‘App. build’) with a ‘TicTacToeGrid’ and we can see what our app looks like. » ►- The Python Book 109 ^Create with Python « A The game with final additions, makingthe grid square and extendingthe interface if 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 now we’ll leave them as they are. Has anyone won yet? We’ll want to keep track of the state of the board to check if anyone has won, which we can do with a couple more Kivy properties: from kivy. properties import *-i (ListProperty, NumericProperty) TicTacToeGrid(GridLayout) : status = ListProperty([0, 0, 0, 0, 0, G, 0 , 0 , 01 ) current_player = NumericProperty(l) 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 wins because the sum of a row, column or diagonal will be +-3. Now we can update our graphical grid when a move is played (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 ‘0’ 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 array keeping track of existing moves. 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 to add some code to check if the game is over. 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 has filled a column, row or diagonal. 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 = [ (status[0:3]), (status [3: 6]), (status[6:9]), (status[0::3]), (status[l::3]), (status[2::3]), (status[:: ]), (status [2: -2: 2])] # Sums can only be +-3 if one player # filled the whole line if 3 in sums: print(‘0s win!’) elif -3 in sums: print(‘Xs win!’) elif 0 not in self, status: print(‘Draw!’) This covers the basic detection of a won or drawn board, but it only prints the result to stdout. At this stage we probably want to reset the board so that the players can try again, along with displaying a graphical indicator of the result (Fig. 03). Finally, we can modify the on_status" method to both reset the board and display the winner in a ‘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 user clicks or taps outside it. 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, 110 The Python Book Create with Python 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 maintainingthese proportions. This is another trick made possible by a binding with the ‘size_hint’ Kivy property, this time managed internally by Kivy. 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 run your program and enjoy hours of fun! Time to experiment 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 bindings between properties. 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 InterfaceO’ line in ‘TicTacToeG rid. 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 things like swapping out the different widget types to see how other widgets behave. i Try swapping out the different widget types to see how other widgets behave TicTacToeGrid(GridLayout): (self, *args, **kwargs): (TicTacToeGrid, self). init (*args, **kwargs) for row in rang* (3): for column in (3): grid_entry = GridEntry( coords=(row, column)) grid_entry. bind(on_release=self. button_pressed) self. add_widget(grid_entry) if button_pressed(self, instance): # Print output just to see what’s going on print(‘{} button clicked! ’.format(instance. coords)) Fig 01 if 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)} row, column = button. coords # passed as an argument Fig 02 # Convert 2D grid coordinates to ID 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, cur rent_player button. text = {1: ‘O’, -1: ‘X’}[self.current_player] button. background_color = colours[self.current_player] self. cur rent_player *= -1 # Note the *args parameter! It’s important later when we make a binding Fig 03 # to reset, which automatically passes an argument that we don’t care about reset(self, *args): self, status = [0 for _ in (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, cur rent_player = 1 The Python Book 111 ^Create with Python Create a two-step authentication with Twilio Increase security in access to your web services by building a simple two-step authentication with Twilio’s SMS APIs to help you Resources Python 2.7+ Flask 0.10.0: flask.pocoo.org/ Flask Github: github.com/mitsuhiko/flask ATwilio account: twilio.com Twilio’s Python REST API Helper Library: github.com/twilio/twilio-python/zipbaU/master MySQLDB: mysql-python.sourceforge.net Stoics -'••Vizirs. TeM^iC m j " iH 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 - and yet phones are more important now to our daily lives than ever. So, what can we do to leverage some of that versatile technology? 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 little of Flask’s detail will be covered here. SM5 AfUltytHTK Todftyl &MS MilMjM l*i m 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 worki ng 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. Add credit 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’ll need those a little later. The Twilio interface is kept nice and simple - no unnecessary complications here 112 The Python Book Create with Python Install the Twilio Helper Library and MySQLDB The Twilio helper library is a fantastic piece of code that lets you jump straight into sendi ng and handling text messages in no time at all. There are a couple of ways to install the library: you can use either PIP or EasyJnstall, like so import MySQLdb from flask import Flask, redirect, request, session, render_template from twilio. rest import TwilioRestClient 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 = client = twilio(account_sid, auth_token) @app.route( / ) f index() : return "index page" @app.route( /login 1 , methods=[ 'GET ' ]) f login() : return "login page" @app.route( /check-user', methods=[ POST']) f checkUser() : return "check user page" @app.route( /logout') f logout() : return "logout page" @app.route( /verify', methods=[ ' GET' ]) f twoStep() : return "verify page" @app.route( /check-code', methods=[ POST']) f checkCode(): return "check code page" if name == ' main ': app. secret_key = ' R4nD0MCRypt( app. run(host='0. 0.0.0 , debug=True) $ 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, the package for which you can grab like so. . . 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 add a new file ‘server.py’. Server setup Open the ‘server.py’ file for editing. The first thing we're going to do is import the libraries we need for our authentication flow, create the endpoints for our server and assign some of the variables needed to run our Flask server. (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 the index page of your server at 1 27.0.0.1 :5000/. Server logic 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 7’ 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( / ) f index(): checkSessionState() return render_template( 1 ’) f checkSessionStateQ : » > The Python Book 113 « try: session[ /erified’] == True except KeyError: session[ /erified’] = try: session[ Loggedin’] == True except KeyError: session[ Loggedin , ] = try: session[ jser ] == True except KeyError: session[ jser ] = Logging in The first step in two-step authentication is logging in with a traditional username/email and password. Access your database and create a new user with the following query: INSERT INTO users (username, password, phonenumber) VALUES (‘A USERNAME', ‘A *-i PASSWORD', 1 +44Y0URUSERSPH0NENUMBER ) For the purposes of this tutorial, the password is plain text - but we implore you, when you’re implementing passwords in a live environment, make sure that you hash them. Still, for now we’re goingto use a plain text password. Our login.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 module to get details from our database. In order to query our database we need to create a cursor from which to execute our MySQL statements. We do this with cur = db.cursor(): @app.route( /check-user’, methods=[ POST']) f checkUser() : #session.clear() if request .method == POST': #print request . form[ 'username ' ] cur = db.cursor() cur.execute( '"SELECT * FROM users WHERE username = %s"" , (request . form[ ],)) result = cur . fetchone() returnedPassword = results ] returnedPhoneNumber = result[ ] We can then build an SQL statement using cur. execute(). Notice the %s; this will be replaced with the value passed through in the next variable. We execute the statement with cur.fetchoneO, 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’ll next checkthatthe password assigned to that user is the same as the one submitted. If so, we’ll generate the validation code to send to the user, which we’ll store in the verification table of ourdatabase until it’s used or expires. We’ll need to create a new cursor to insert the verification code into our table. After we’ve executed the statement we need to commit the changes to the database, we do this with db. commit() - we’ll then add the results of the query to our session so we can check against them later. (Fig 03) Send the verification code 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: f sendVerificationCode(code, number) : text = client. messages. create( body=‘Your verification code is: " — + code, to=number , f rom_=“+Y0URTWILI0NUMBER" ) 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 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(V verify’) at the end of /check-user. Check verification code 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 goingto 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.timeO > expirationTime : expirySQL = db cursor () expirySQL . execute( w verification WHERE code=%s"” , (codeToCheck, )) 114 The Python Book Create with Python expirySQL. close() session[ ] == 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( M verification WHERE code=%s'"’ , — i (codeToCheck, )) delSql . close() db commit() session[ 'verified ’ ] = True return redirect('/ ) else : return redirect('/ e ' ) 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 redirected to their user page, and so on. Conclusion Vv 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( ' / ) if index(): return render_template( ' index. html ) @app. route ( login', methods=[ ]) if login() : return render_template( ' login . html ) @app. route( ' /check-user ' , methods=[ ' POST ]) if checkllser() : return "check user page" @app . route( ' /logout ' ) if logout() : return "logout page" @app. route( '/verify ' , methods=[ 'GET ' ]) f twoStep() : return render_template( ) @app. route( ' /check-code ' , methods=[ ' POST ]) if checkCode(): return "check code page" verficationCode = generateVerificationCode(size=6) ins = db.cursor() expiration = (time.time() + expirationLength) sql = "INSERT INTO ver % (verficationCode, expiration, request. form[ jsername ]) ins.execute(sql) ins.close() db. commit() sessionE ' user ] = request. form[ 'username ] sessionE ' loggedin ] = True @app. route( '/check-code' , methods=E ' POST ]) if checkCode(): if request .method == 'POST': codeToCheck = request. formE 'code ] if not 'user' in session: return redirect( /login') else: cur = db.cursor() cur.execute( "SELECT * FROM verification WHERE code = %s’ M , (codeToCheck,)) result = cur . fetchone() cur.close() if result != None: returnedllser = resultE3] expirationTime = (resultE2]) if returnedllser != sessionE ' user ]: return redirect( 1 /verify?error=true ’ ) The Python Book 115 Create with Python Part one: Program a Space Invaders clone Write your own RasPi shooter in 300 lines of Python Resources Raspbian: www.raspberrypi.org/ downloads Python I www.python.org/doc Pygame I www.pygame.org/docs 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 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... Setting up dependencies w I 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 own program. It’s great practice and gives you a chance to tweak elements of the game to suit you, be it a different ship image, changing the difficulty or the ways the alien waves behave. If you 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’t already) can be installed from the terminal by typing: sudo apt-get install python-pygame Downloading the project For Pivaders we’ve used Git, a brilliant form of version control used to safely store the game files and retain historical versions of your code. Git should already be installed on your Pi; if not, you can acquire it by typing: 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), enter the directory (cd pivaders) and type: git pull https://github.com/russb78/pivaders.git 116 The Python Book Create with Python 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) Clean clode Havingall the most regularly used global variables clearly labelled here makes our code later on easier to read. Also, if we want to change the size of something, we only need to do it here and it will Player(pygame. sprite. Sprite): work everywhere. (self): ■■■■■■■■ pygame. sprite. Sprite. (self) self.size = (60, 55) self.rect = self.image.get_rect() self.rect.x = (RESC0] / 2) (self.size[0] / 2) self.rect.y = 520 self.travel = 7 self, speed = 350 self.time = pygame. time. get_ticks() 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] Alien (pygame. sprite. Sprite): (self): pygame. sprite. Sprite. (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() 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[l]: self.rect.y += self.vector[l] * self.travel[l] self.vector[0] *= -1 self.has_moved = [0, 0] self.speed -= 20 if self.speed <= 100: self.speed = 100 self.time = GameState. alien_time >s Ammo(pygame. sprite. Sprite): (self, color, (width, height)): pygame. sprite. Sprite. (self) self.image = pygame. Surface([width, height]) self, image. fill(color) self.rect = self.image. get_rect() self.speed = 0 self.vector = 0 update(self): self.rect.y += self.vector * self.speed if self.rect.y < 0 or self.rect.y > RES[1]: self.kill() Block(pygame. sprite. Sprite): (self, color, (width, height)): pygame. sprite. Sprite. (self) self.image = pygame. Surface([width, height]) self, image. fill(color) self.rect = self.image. get_rect() Rain bullets The Ammo class is short and sweet. We only need a few initialising attributes and the update method checks if it’s still on the screen. If not, it’s destroyed. GameState: pass Game( ): (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[(], RES[1]]) self.time = pygame. time. get_ticks() self, ref resh_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() collisions and drawn self.intro_screen = pygame. image. load( with ease, ‘data/sta rt_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/Spaceshipl6.png’).convert() Alien. image. set_colorkey (WHITE) GameState. end_game = False GameState. start_screen = True GameState. vector = 0 GameState. shoot_bullet = False Groups This long list of groups we’re creating are essentially sets. Each time we create one of these items, it’s added to the set so it can be tested for if 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 Control Taking care of keyboard input is the control method. It checks for key events and acts accordingly dependingwhether we’re on the start screen or playing the game. if splash_screen(self): while GameState. start_screen: self.kill_all() self.screen. blit(self.intro_screen, [0, 0]) self, screen, blit (self, int ro_font. render ( “PIVADERS”, 1, WHITE), (265, 120)) self, screen. blit(self. game_font. render ( “PRESS SPACE TO PLAY”, 1, WHITE), (274, 191)) The Python Book 117 Testing Pi vaders 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 and right. Creating your own done 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.pyfile of your own. Global variables & tuples 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 that never change - they represent things 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 you can’t reassign individual items within them. Perfect for constants, which aren’t designed to change anyway. Classes - part 1 wU 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. Classes - part 2 w # 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 updateO method apart from the Block class (a method is a function within a class). The updateO 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). OQ Ammo wO What’s most interesting about classes, though, is that you can use one class to create lots of different things. - m PlUflDERS PRESS SPACE TO PL BY We used widely available open source Jf, 1 art and fonts to make the game IH r‘ X 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 objects we create. The game \ J w 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 need to return to the start screen, among other things. The main loop I \ J There are a lot of methods (class functions) in the Game class, and each is designed to control a particular aspect of either setting up the game or the gameplay itself. The actual logic that dictates what happens within any one round of the game is actually contained inthemain_loop() method right at the bottom 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 second when you’re playingthe game. Main loop key logic - part 1 I 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.quitO, 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 controlO method, which checks to see if the location of the player needs to change. Next we attempt to make an enemy missile and we use the random module to limit the number of missiles that can be created. Next we call the updateO method for each and every actor on the screen using a simple for loop. This makes sure everyone’s up to date and moved before we check collisions incalc_collisions(). Main loop key logic - part 2 I ^ 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 of the loop is of no consequence. 118 The Python Book Create with Python l A class is essentially a blueprint Dead or alive Probably two of the most important questions are answered here - is the player dead or did you win the round? pygame. display. flip() self.control() ? make_player(self): self, player = Player() self. player_group. add (self, player) self. all_sprite_list.add(self. player) r 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) refresh_scores(self): self, screen, blit (self. game_font. render ( “SCORE “ + (self. score), 1, WHITE), (10, 8)) self, screen. blit(self.game_font.render( “LIVES “ + ;tr (self.lives + 1), 1, RED), (355, 575)) if alien_wave(self, speed): for column in (BARRIER.COLUMN): for row in (BARRIER.ROW): alien = Alien() alien. rect.y = 65 + (column * ( ALIEN_SIZE[1] + ALIEN_SPACER)) alien. rect.x = ALIEN.SPACER + ( row * (ALIEN_SIZE[ ] + ALIEN_SPACER)) self.alien_group.add(alien) self.all_sprite_list.add(alien) alien. speed -= speed Refreshing the screen you need to carefully considerthe way in which you update the screen. Blitting the background between actor movements is vital for clean animation. if make_bullet(self): if GameState.game_time - self. player. time > 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 if make_missile(self): if (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) self, player, speed: Guns ’n’ ammo Bullets and missiles use the same parent class. We change a few key attributes originally initialised to create the behaviour we need; eg the vector for each is opposite. ‘ f make_barrier(self, columns, rows, spacer): for column in range(columns): for row in (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) i make_defenses(self): for spacing, spacing in ( (4)): self.make_barrier(3, 9, spacing) if 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() is_dead(self): if self.lives < 0: self, screen, bl it (self. game_font. render ( “The war is lost! You scored: “ + str ( self. score), 1, RED), (250, 15)) self.rounds_won = 0 self, ref resh_screen() pygame. time. delay (3000) return True f win_round(self): if len(self.alien_group) < 1: self.rounds_won += 1 self, screen, blit (self. game_font. render ( “You won round “ + (self.rounds_won) + “ but the battle rages on”, 1, RED), (200, 15)) self. refresh_screen() pygame. time. delay (3000) return True if 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, ref resh_screen() pygame. time. delay(3000) return True if 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 next_round(self): for actor in [self.missile_group, self. bar rier_group, self.bullet_group]: for i in actor: i.kill() self.alien_wave(self.level_up) self.make_defenses() self.level_up += 50 Main loop This is the business end of our application. This loop executes 20 times a second. It needs to be logical and easy for another coderto understand. 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. ref resh_screen() self.splash_screen() pygame. quit() if name == pv = Game() pv.main_loop() Start the game The very last thing we do is create a Game object and call the main loop. Besides our constants, this is the only code that sits outside a class. The Python Book 119 Create with Python Part two: Add animation and sound to Pivaders After writing a Space Invaders clone in just 300 lines of Python, now we expand it to include animation and sound Resources Raspbian: www.raspberrypi.org /downloads Python: www.python.org/doc Pygame: www.pygame.org/docs Art assets: opengameart.org 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, 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 vO.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 forthe Raspberry Pi. Setting up dependencies w I 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 120 The Python Book Create with Python Pivaders.py listing from line 86 (continued on next page) RES[1]]) ship_sheet We set the player image to be equal to one small segment of the sprite sheet by usingthe‘ani_pos’ variable. Change the variable to change the picture Game( ): (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[] self, time = pygame.time.get_ticks() self, ref resh_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 . j pg ’ ) . convert () pygame. display.set_caption(‘Pivaders - ESC to exit’) pygame. mouse, set _visible(False) Alien, image = pygame. image, load ( ‘data/graphics/Spaceshipl6.png’).convert() A1 ien . image . set_colorkey (WHITE) self.ani_pos = 5 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_newl.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 = py game, mixer. Sou nd( ‘data/sound/timgormly__8-bit-explosion.ogg’) self. explosion_f x . set_volume (0 . 5) self. explodey_al ien = [] GameState.end_game = False GameState.start_screen = True GameState. vector = 0 GameState.shoot_bullet = False 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 Set flags We’ve added ‘animateJeft’ and ‘animate_right’ Boolean flags to the control method. When they’re true, the actual animation code is called via a separate method 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 _f x . play () fx.playO Having already loaded the sound effect we want when we shoot, we now just need to call it when we press the space bar : 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 player_explos ion (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 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, * (self. explodey_al ien [1]) - 60]) [ (self. explodey_alien[0]) - 50 else: self.alien_explode = False self.alien_explode_pos = 0 self. explodey_al ien = [] splash_screen(self ) : while GameState. start_screen: self.kill_all() self. screen. blit(self.intro_screen, [0, 0]) self, screen . blit(self. int ro_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. ref resh_rate / 2) make_player(self ) : self.player = PlayerQ The Python Book 121 Create with Python Downloading pivaders 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. To clone the version of the project we created for this tutorial, go to your home folder from the command line (cd ~) and type: git pull https://github.com/ russb78/pivaders . git This will create a folder called pivaders - go inside (cd pivaders) and take a look around. Navigating the project 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) and type: 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 press it again to exit the game completely. Animation & sound 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 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 the animations as they happen. Finding images to animate Before we can program anything, 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 at opengameart.org, as used here. Tweaking assets 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 the central ship sprite and centred it into a new window. We set the size and width of the frame and then copy-pasted the other 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. Loadingthe sprite sheet Since we’re inheriting from the Sprite class to create our Player class, 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.loadO. Since we made our sheet with a transparent background, we can append .convert_alpha() to the 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. anLpos, which has an initial value of 5. Changing this value will alter the ship image drawn to the screen: ‘0’ would draw it leaning fully left, ‘1 1 ’ fully to the right. Animation flags w Down the list in the initialising code for the Game class, 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 continue to look as if it’s flying left when it has stopped). OG The animation method w w 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 anLpos variable and set the player’s image accordingly. The Else statement then animates the ship sprite back to its resting state and the same logic is then applied in the opposite direction. Animating explosions I w The player_explosion() and alien_explosion() methods that come after the player animation block in the Game class are 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 explosionjmage are set to a different part of subsurface than before. Adding music to your project I Pygame makes it easy to add a musical score to a project. Just obtain a suitable piece of music in your preferred format (we found ours via freemusicarchive.org) and load it using the Mixer Pygame class. As it’s already been initialised via pygame.initO, we can go ahead and load the music with this code: pygame . mixer, music . load ( ' ‘data/sound/10_Arpanauts . ogg ’ ) pygame. mixer, music, play (-1) pygame. mixer, music. set_volume(0.7) The music.play(-l) requests that the music should start with the app and continue to loop until it quits. If we replaced -1 with 5, the music would loop five times before ending. Learn more about the Mixerclassviawww.pygame.org/docs/ref/mixer.html. Using sound effects I 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. For the laser beam, the initialisation looks like this: 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, straight after we raise the shoot_bullet flag. If you’re struggling to find free and open sound effects, we recommend www.freesound.org. 122 The Python Book Create with Python self. player_group. add(self. player) self. all_spr ite_list . add (self, player) ?f ref resh_screen (self): self. all_spr ite_list . d raw(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. ref resh_rate) ref resh_sco res (self ) : self, screen . blit (self . game_font . render ( “SCORE “ + (self. score), 1, WHITE), (10, 8)) self, screen . blit (self . game_font . render ( “LIVES “ + (self. lives + 1), 1, RED), (355, 575)) alien_wave(self, speed): for column in (BARRIER_COLUMN) : for row in (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 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 make_missile(self): if (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_spr ite_list . add (missile) make_barrier(self, columns, rows, spacer): for column in a nge (columns): for row in rang* (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_spr ite_list . add (barrier) 5f make_defenses(self): for spacing, spacing in ( (4)): self.make_barrier(3, 9, spacing) 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() 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 5f 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 . t ime . delay (3000) return True ef 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 . t ime . delay (3000) return True 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 Bf calc_collisions(self): pygame. sprite. groupcollide( self.missile_group, self.barrier_group, True, True) pygame. sprite. groupcollide( self, bullet .group, self, bar rier_group, True, True) for z in pygame. sprite. groupcollide( self, bullet .group, self.alien_group, True, True): self.alien.explode = True self, explodey.al ien .append (z . rec t . x) self, explodey.al ien .append (z . rec t .y) self, score += 10 self, explosion.f x . play () if pygame. sprite. groupcollide( self.player_group, self.missile_group, False, True): self. lives -= 1 self, explode = True self, explosion.f x . play () 5f 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_g roup, 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() The Python Book 123 Create with Python Make a visual novel game with Python Bridge the gap between books and videogames by creating an interactive novel or choose-your-own-adventure with Python and Pygame ■ Change scenes to add more depth to the story, and allow the game to have decisions and routes Resources Python 2: www.python.org/ Pygame: pygame.org/download.shtml IDLE Python IDE Game assets Code from FileSilo (optional) 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 clickthrough a story and make decisions as they go along in order to experience different plot points and endings. 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 coding process. We’ll be coding this in standard Python 2, so make sure to run it in IDLE 2 and not IDLE 3 while you are writing, testing and coding. 124 The Python Book Create with Python M* i ‘In *Mw lpw.*H **m rubj^jtiLinl^j. % iuili> 4pt~4*t "crtyrUl pjrlhqn -i*** python -fwjnpj LVfr*4L- l«P9fl + |-tff'w Utrfl-* it Hindi 1.? difw “)*(*%> Ut-m-rffllr dr-v llhiYfV^it dr* lllwtvswiSFt tfr* pj-.'-Hcrd far rebli fie*dlA0 t>fl£b*g* lUtl-L. DfliW iutldlne d*ptnd«nc^ t*« fif+iJl*ia ICIta inf er»*ttWi k , frgnt pflhnn dr* tv aI« rtrfip the iww^l w > 1 . 0 * 1 . python aunpv ti qtrradv (he n*wrVt MMlM. tVth-an-runpy i4L fa rivnuiU-f In it i ll*d. Ubtv^tMlt Hi ilrtidy 1 hr nMit vtnVHi. Ubtv-twlt set t4 ninviUy tnftiUfd^ Thr fsillowlnq p*ic. kftflrV werr mi tonal It ally itralvllrd vnd nr ns lomqFr rrttulTrd;: llmin h^fljers i.ts.fli 4* It mm headers l-ll.l WM rt t 1 tflvx - H-rvdtr & J . 1 2 . 5 r«i- Hiuix - find*' 1 f J , 1 J 1 « - #1 ■ ■gvr-r r U Unux Im^#* i, iJ,e-4o-fl*n*rtc J.iJ.a- 4i-f tfi+r 1c I tfiux r iMg#- • mutt 3k • 1 . 1 1 . tfl -ID- gvrvvr 1 c ttnus* lru 9 v-fxtra-l.il .1- 4 5 dvu I L b Jbk q ■ rSw Llbjpvi] dvv lib |pc*Q turbot ikm ILbJpeqA drv ISblirvo duv ILbrudt dvv llbnLhnud? 6rv Ithn firv UbpufflZ tthpytw Ubslin;^ dew Irnlffi hv U.Jbttffe"S Ubvprbti’dfiy Llbtavbpdev Ub**tb*demfM nertyrtll’CflnnqiS filtkigti . LlbJMiLwid 7 -tf£i£ I 1 Hide lib 1 7 7 • tlvv i kbgutl - dvu L Lbr,*Bl 114 -dv-u I I fa ■>[ hr oed L nqri dpv 1 LbspL'L'H drv ILblhcar.i dev Itbliiiu dm. qc 1 win mvcv WtffJ *(dtff3-qt ifonpB-Fe neld tttYi pyth^-B^sqldb Mfh^n-p/gwnii (he fgUnM-nig N4.H p|tk*g«* wtU t*e kn? t j t Led Ufaetbundz-dcv Utivr*M-tll*nt-ii*¥ UiWkvihl-tGrtrtofl-iJiv U&bvtedtoL-ttonr I Hij'.' l m n*1 -dvw 1 Ibdvul I L - rl*v L Ibc jr j- d w 1 k bdbuv • I -dw llbr L.*c -dru llbjblq dev Ikbjpcq dmi ILbjprq lurboi dr u IkbJpcqB drv llblnu dev UbMdO dev l‘bntt"dS2 >dev Ubo^g-dev Itbunflii-dev llbporentdt -dev UbwUt-d*v Ubsdl‘t*igfl,2-de* ttb5dt-m,Hf ri . 2 -de^ Ubsdl-tt?i-P-4«w libsdll , 2 -dcv lUrtUrafZ-dFv ILbSftpeg-dev UfaVMS-tdle-dev UbtVfr5-dew llbtLfluxl llbvqrfalft-dpv I I hwb|> ■ dw 1 1 bvvtif»d#MX 1 avt cifi LjI nc-rcurldl 1 on non t to upgrade. )4 to ne«ly tnitill, t to renove *rvd *•'? not to upgrade. 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-imagel.2-dev libsdl-mixerl.2- dev libsdl-ttf2.0-dev libsmpeg- dev libsdll.2-dev libportmidi-dev libswscale-dev libavformat-dev libavcodec-dev Get the Pygame code Wb Next we need to download the code for Pygame direct from the source. Still in the terminal, you can do this by typing in: $ hg clone https://bitbucket.org/pygame/ pygame Build the Pygame module To install it, we need to do it in two steps. First we need to prepare the code to install usingtheterminal with: $ python setup. py build Once that’s finished you can then actually install itwith: Install in other ways 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 work in any OS. Which will download it to the folder ‘pygame’. $ sudo python setup. py install Move to that using CD pygame in the terminal so we can continue building it. This won’t take too long. vU Ila rif t i #43V*i c ftMfti wtl * 44\*4 p*mr#ai fllV ClUifM tMrt Mil (Ivqruln iitk 1-MJ* iMPgn 1» iM lltn (-11 hfrfT 1 V KU E tap t* bfPfk if+fwU tn filvi tfAit*!. * MUi wH. • nits rwHri. I rUn H/fuIwi * VtjfrrIi . ' H ti 1 ti mt«i# n &vll4 m “iMmtr flit W-rr m nlnq Wi %*>: 1: Trmlyviv caftrif; Mt r«ari ■ I fnnijfpi ta*l Vfti | : '^Vtu .1 : r«M*d rMlMi : h 4 (hh! Get the visual novel files ww 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. Understand the script file 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. This includes the location of each line, the character, the actual line itself and information on how the game flows. These are matrices with the information in, and are completely customisable. ef Kilt ppQim d*i*fidvM ue-rv fn>t ranai ntvll. but na p p i Svnvv! *■ "Is *1*1 1 M»il 4 Ufcy !♦ The Python Book 125 ^Create with Python « How the script relates 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. \i r.f rt pygame , time, script pygame . init ( ) Starting the main game 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() Add variables and assets We add a mixture of information we need to run the novel. We define the size of the display screen to use (1 000 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. Start the game 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 blitto add images or in this case text. Once you’ve created a buffer of changes to the screen, you update it with the flipQ function. Seethe mouse 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.getO 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 afterthat. w! a (.a i L_iffaiT!£ O : turn * 0 IJUK,9tlte W 1 Start the story 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. First screen 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 i The code pulls in elements from the script file as it goes, allowing us to implement decisions later on el if turn > 0 and click_state [0] == 1: line_atart = 0 for i in range (4): line_Btart == 0 and line[0] ! = "O': print line [0] screen .blit (location [ 1 ine to] ] , [O , O] } time - sleep { 1 ) if line_atart == 1 and line(l] ! - 'O’: screen. blit (character [lined] I , [377, 113] ) time . sleep [ 1 } el if line_start -- 2: pygame . draw . rect ( screen, (grey), pygame . Rect [ 130 , 423, 740, 120)) el if line__start == 3: screen .blit (game_font * render ( line [2 ] r li white), (135, turn += 1 if line [3] l « '0": game_script = line [31 time . s 1 eep (0.5) gamers t ate - line [4] clicked ■ 0 430) ) line_start += 1 pygame , di splay * f 1 ip () 126 The Python Book 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 advancingthe progression value. i The code here is very expandable, allowing you to add decisions that take you to different scenes gates U * game_running ™ True: m#nu_0cr«en ( * gatne { ) Add variables and assets 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. The starting function 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 to add different ways of turning it off in the future. 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 function at the end which will start the code in the command line. Expand your code 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 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 collidefunction. Move the assets 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. 1 The Python Book 127 ^Create with Python Pygame Zero Pygame Zero cuts out the boilerplate to turn your ideas into games instantly, and we’ll show you how Resources Pygame Zero: pygame-zero.readthedocs.org Pygame: pygame.org/download.shtml Pip pip-installer.org Python 3.2 or later www.python.org/ Code from FileSilo (optional) Games are a great way of understanding a language: you have a goal to work towards, and each feature you add brings more fun. However, games need libraries and modules for graphics and other essential games features. While the Pygame library made it relatively easy to make games in Python, it still brings in boilerplate code that you need before you get started - barriers to you or your kids getting started in coding. Pygame Zero deals with all of this boilerplate code for you, aiming to get you coding games instantly. PgO (as we’ll abbreviate it) makes sensible assumptions about what you’ll need for a game - from the size of the window to importing the game library - so that you can get straight down to codingyour ideas. PgO’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-styie. The project will illustrate what can be done with very little effort. PgO 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. 128 The Python Book Create with Python Right Breakouts a classic arcade game that can be reimagined in Pygame Zero Young and old In situations where Pygame is used boilerplate and all with young people, great results can also be achieved (see Bryson Payne’s book), but Pygame and PgO, despite their use as powerful educational tools, are also good for creating games for coders no matter what stage of learningtheyareat. Great games are all about the gameplay, driven by powerful imaginations generating images, animations, sounds and journeys through game worlds. Good frameworks open up this creative activity to people who are not traditional learners of programming, which is an area where Python has long excelled. Code on FileSiloj |§. . Zero effort w I Although game writing is not easy, getting started certainly is. If you’ve got Raspbian Jessie installed on your Pi, you’re ready to go. Open a terminal and type: touch example. py pgzrun example. py n 4 NoPi? 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/IGYznUB. And you’ll see an empty game window open (Ctrl+Q will close the window). Yes, it’s that easy to get started! Python 3 Wfc If you haven’t got Raspbian Jessie, chances are you’ll have neither PgO nor Pygame installed. The Python’s pip package installer will take care of grabbing PgO 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 PgO your chance for a gentle upgrade to Python 3. Older Raspbian If you’re still running Raspbian Wheezy, you’ll need to run the following steps to install Pygame Zero: Intro.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 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 PgO for the window display, with methods available for all sorts of different sprites... sudo apt-get update sudo apt-get install python3-setuptools python3-pip sudo pip-3.2 install pgzero » The Python Book 129 Create with Python Right The bat and ball come first -they’re the cornerstones of Pong and Breakout Program objects David Ames, who uses PgO to teach younger children to code at events across the UK, told us: “One thing to avoid when it comes to teaching kids is Object Orientation.” OOP (object-oriented programming) is partly abstracted away by PgO, but it can’t be ignored. Perhaps the best approach is using PgO and some simple code to start, then dropping in a piece of 00 when it’s needed to solve a particular problem. With the Code Club age group -about eight to eleven - feeding information to solve practical problems works well. It can work with adults, too - but there’s always someone who’s read ahead and has a few tricky questions. Sprite wU The intro example from the PgO docs expands on that with the Actor class, which will automatically load the named sprite (PgO will hunt around for a .jpg or .png in a subdirectory called images). alien = Actor(‘alien’) alien. pos = 100, 56 WIDTH = 500 HEIGHT = alien. height + 20 draw(): screen. clear() alien. draw() You can download the alien from the PgO documentation (bit. ly/1Sm5lM7) and try out the animation shown there, but we’re taking a different approach in our game. j Breakout via Pong w/ 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 Breakoutdone. ric Batt y wO 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 the screen, using the screen function. W = 800 H = 600 RED = 200, 0, 0 bat = Rect((W/2, 0.96 * H), (150, 15)) draw(): screen. clear() screen. draw.filled_rect(bat, RED) OO Mouse move w We want to move the bat, and the mouse is closer to an arcade paddle than the arrow keys. Add the following: on_mouse_move(pos) : x, y = pos bat. center = (x, bat.center[l]) Use pgzrun to test that you haveascreen, bat and movement. 130 The Python Book Create with Python To get the ball to move we need to define move(ball) for each case where the ball meets a wall Full code listing ## Breakout type game to demonstrate Pygame Zero library ## Based originally upon Tim Viner’s London Python Do jo ## 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’ , ‘xy’) ball_dir = Direction(5, -5) bat = Rect((W/2, 0.96 * H), (120, 15)) s Block (Rect): if init (self, colour, rect): Rect. init (self, rect) self. colour = colour blocks = [] n_block in range(24): block = Block(G0LD, ((((n_block % 8)* 100) + 2, ((n_block // 8) * 25) + 2), (96, 23))) blocks . append (block) draw_blocks() : >r block in blocks: screen . draw. filled_rect (block, block colour) if draw(): screen. clear() screen draw. filled_rect (ball, WHITE) screen draw. filled_rect (bat, RED) draw_blocks() if on_mouse_move(pos) : x, y = pos bat center = (x, bat.center[l]) if on_mouse_down() : ball_dir ball_dir Di rect ion (ball_dir.x * 1.5, ball_dir.y * 1.5) Square ball In properly retro graphics-style, we define a square ball too - another rectangle, essentially, with the (30, 30) size making it that subset of rectangles that we call a square. We’re doing this because Rect is another built-in in PgO. 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 can call directly. Simply add: WHITE = 200,200,200 ball = Rect((W/2, H/2), (30, 30)) ...to the initial variable assignments, and: screen .draw. filled_rect(ball, WHITE) ...tothedef draw() block. Action! I 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 balLdir = Direction^, -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! PgO will call the updateO function you define once per frame, giving the illusion of smooth(ish) scrolling if you’re not running much else. def move(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 each of the boundaries. Refer to the full code listing on page 67. 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 screen would need a value of W - 30. Absolute values You might expect multiplying y by minus one to work for reversing the direction ofthe ball when it hits the bat: ball_dir Direction(ball_dir.x, -1 * ball_dir.y) ... but you actually need to use abs, which removes any minus signs, then minus: ball_dir Direction(ball_dir.x, - abs(ball_dir.y)) Try it without in the finished code and see if you get some strange behaviour. Your homework is to work out why. The Python Book 131 ^Create with Python Right Tom Viner’s array of blocks negates the need for bordered rectangles •rw EOgU Jkb w/Ttav«biLt M ^ ^ m •: rtuthcopn V # # ft £) U * tjii clfrnc qit|ni:tt hub, ea*tt Milner /hreflifrut ..qiL * ed br^h.wt * pgzrun bmhwtT.py PgO+1 There’s a new version of PgO in development - it may even be out as you read this. PgO creator Daniel Pope tells us “atone generation API is in the works,” and that atthePgO PyConUK sprint, “we finished Actor rotation.” Contributions are welcome - not only to the PgO code, but more examples are needed not just to show what can be done, but to give teachers tools to enthuse children about the creative act of programming. PgO has also inspired GPIO Zero, to make GPIO programming easier on the Raspberry Pi, with rapid development occurringonthis new library as we go to press. Sounds 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 ! Blockhead! 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. Building blocks 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 relation to number across the screen, 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 screen sizes - see github.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 from hard-coded values... Goingforgold Create a Block class: ;s Block(Rect): init (self, colour, rect): Rect. init (self, rect) self.colour = colour ... and pick a nice colour foryour blocks: GOLD = 205,145,0 Line up the blocks This builds an array of 24 blocks, three rows of eight: blocks = [] for n_block range(24): block = Block(GOLD, ((((n_block % 8)* 100) + 2, ((n_block // 8) * 25) + 2), (96, 23))) blocks . append (block) Drawing blocks Draw_blocks() is added to def draw() after defining: draw_blocks(): for block blocks: screen. draw. filled_rect(block, block. colour) Block bashing 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) to_kill >= 0: sounds, block. play() ball_dir = Direction(ball_dir.x, abs(ball_dir.y)) blocks . pop(to_kill) 132 The Python Book Create with Python LeftTest your game once it’s finished -then test other people’s Breakout games to see how the code differs Full code listing (cont.) move(ball) : ball_dir ball move_ip(ball_dir) ball.x > 781 or ball x <= 0: ball_dir = Direction(-l * ball_dir.x, ball_dir.y) ball.y <= 0: ball_dir Direction(ball_dir x, abs(ball_dir.y)) ball . colliderect(bat) : sounds blip play() ball_dir = Direction(ball_dir.x, - abs(ball_dir.y)) to_kill ball . collidelist(blocks) to_kill >= 0: sounds block play() ball_dir Direction(ball_dir,x, abs(ball_dir.y)) blocks pop(to_kill) not blocks: sounds win. play () sounds win. play () (“Winner!”) time sleep(l) sys.exit() ball y > H: sounds die. play () (“Loser!”) time sleep(l) sys.exit() update() : move(ball) Game over Lastly, we need to allow for the possibility of successfully destroying all blocks, not blocks: sounds. win. play() sounds. win. play() print(“Winner!”) time.sleep(l) sys.exit() Score draw 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 PgO to explore, but a look into Pygame unmediated by the PgO wrapper is your next step but one. 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 PgO might do more for easy score keeping. Class of nine lives 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 PgO examples, such as the excellent pLlander example by Tim Martin: github.com/timboe/ pLlander. Don’t stop here 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. ■ The Python Book 133 1 36 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 1 50 Deliver content to your blog Add content to your site 154 Enhance your blog Complete your blog with add-ons "Python is a versatile language, perfect for making websites" 134 The Python Book \t *r Don’t be fooled into thinking Python is a restrictive language or incompatible with the modern web. Explore options for building Python web apps and experience rapid application development 136 The Python Book Web developments Why? | First released in 1991, companies j like Google and NASA have been j 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 restrictingthem to 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 manager pip included. Once Python is installed the first package to download should be virtualenv using ‘pip install virtualenv’, which enables you to create project- specific shell environments. You can run projects on separate versions of Python with separate project-specific packages installed. Check out the detailed Hitchhiker’s Guide to Python for more information: docs.python- guide.org/en/latest. Frameworks Let’s take a look at some of the frameworks available when developing Python web applications Django dj an go project .com GOOD FOR: Large database-driven web apps with multiuser support and sites that need to have heavily customisable admin interfaces Tornado tornadoweb.org GOOD FOR: Web socket interaction and long polling due to its ability to scale to manage vast numbers of connections 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 approach to code using subapplications. 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 remains as active and useful as ever. Flask flask .pocoo.org GOOD FOR: Creating full-featured RESTful APIs. Its ability to manage multiple routes and methods is very impressive PyramiD pylonsproject.org GOOD FOR: Highly extensible and adaptable to any project requirement. Not a lightweight system either 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 powerful microframework. 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 Werkzeug Debuggertoo. 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 includes a powerful debugger. Flask web development, one drop at a time giiWmeM // does // oommuniiu // snippets// extensions // seondj F?osJc is a microfraineworkfor Python based on Werkzeug, Jmja sand yuod intentions. And before you ask : It's BSD licensed! Flask is Fun Latest Version: ojqi from Flask import Flask The Python Book 137 eb development Create an API Let us explore the Flask microframework and build a simple yet powerful RESTful API with minimal code Install Flask w I 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 Create Index 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 application is Flask itself. import sqlite3 from flask import Flask, request, g, redirect, url_for, render_template, abort, jsonify Declare Conf ig wO For a small application we can declare configuration options as upper-case name value pairs inside the main module, which we’ll do now. Flere 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 placed above these last two lines. # 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() Connect to Database 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’]) Database Schema w w 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 bootstrapped data. 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)’); Instantiate the Database w w 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.closingO’ 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 the open database. 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.commitO AH Populate the Database w # 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 AQ Request DB Connection wO 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(); “World-renowned image sharing service Instagram and social pin board Pinterest have also implemented Python as part of their web stack, opting for Django” 138 The Python Book @app.teardown_request def teardown_request(exception): db = getattr(g, ‘db’, None) if db is not None: db.close() Display Posts 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 diet method and saved as the posts variable. To render a template we then call render_ templateO and pass in the file name and the variable to display as the second argument. Multiple variables can be passed through as a comma-separated list. @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[l]) for row in cur.fetchallQ] return render_template(‘show_posts. htmr, posts=posts) Template Output 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. {% for post in posts %} {{ post. title }} Information {{ post. title }} {{ post. title }} {{ post_page. title }} {{ post. title }}

OVER 2 HOURS OF VIDEO TUTORIALS The ultimate guide to coding with Python Over 20 incredible projects Learn to use Python • Program games • Get creative with Pi Welcome to Python is an incredibly versatile, expansive language which, due to its similarity to everyday language, is surprisingly easy to learn even for inexperienced programmers. It has seen a huge increase in popularity since the release and rise of the Raspberry Pi, for which Python is the officially recognised programming language. In this new edition ofThe Python Book, you'll find plenty of creative projects to help you get to grips with the combination ofyour Raspberry Pi and Python's powerful functionality, plus lots of tutorials that focus on Python's effectiveness away from the Raspberry Pi. You'll learn all about how to code with Python from a standing start, with our comprehensive masterclass, then go on to complete tutorials that will consolidate your skills and help you to become fluent in the language. You'll learn how to make Python workfor you with tutorials on coding with Django, Flask, Pygame and even more useful third-party frameworks. Get ready to become a true Python expert with the wealth of information contained within these pages. Imagine Publishing Ltd Richmond House 33 Richmond Hill Bournemouth Dorset BH2 6EZ s +44 (0) 1202 586200 Website: www.imagine-publishing.co.uk Twitter: @Books_l imagine Facebook: www.facebook.com/lmagineBookazines 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 Part of the LnuxUser vmzsi bookazine series t TS IMAGINE PUSLISKINC 8 Get started with Python Master the basics the right way 16 50 essential commands The commands you need to know Python 1 . 7.6 (default, «ar 11 2014, i [CCt 4,9.2] on Umi*2 Tyjn? “Iwl|) , ‘ p ^DpjFilyhr, m *llu t^port sctpy nystr ■ "scipy ,slrr(45.6)’ i >» evil (fly sin) »> Python essentials 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 Create with Python 1 08 Build tic-tac-toe with Kivy Program noughts and crosses 112 Create two-step authentication UseTwilio 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 PygameZero Turn your ideas into games 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 6 The Python Book 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 "Get to grips with Python, and master highly versatile code" 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 1 68 Send SMS with Pi Send text messages for free 1 70 Build an LED Matrix Use Pi to control light sequences The Python Book 7 Get started with Python P! % Always wanted to have a go at programming? No more excuses, because Python is the perfect way to get started! 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. You can make all kinds of applications in Python: you could use the Pygame framework to write simple 2D games, you could use the GTK libraries to create a windowed application, or you could try something a little more ambitious like an app such as creating one using Python's Bluetooth and Input libraries to capture the input from a USB keyboard and relay the input events to an Android phone. For this guide we're going to be using Python 2.x since that is the version that is most likely to be installed on your Linux distribution. In the following tutorials, you'll learn howto create popular games using Python programming. Well also show you howto add sound and Al to these games. 8 The Python Book Get started with Python SIM sGet started with Python Hello World 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 cd Python. 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 if you 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 workout what the rest of the lines need to be interpreted with. If you’re running this in an IDE like IDLE, you don’t necessarily need to 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 gets passed to it from the console. You can save the change s yo u’ve just made to the file in nano using the key combination Ctrl+O, followed by Enter. Use Ctrl+X to exit 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 TIP If you were using a graphical editor such as gedit, then you would only have to do the last step of making the file executable. You should only have to mark the file as executable once. You can freely edit the file once it is executable. Variables and data types 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 Stores decimal numbers Boolean Can have a value of True or False String Stores a collection of characters. “Hello World” is a string 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 as a main data type): List Contains a collection of data in a specific order 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" Interpreted vs compiled languages An interpreted language such as Python is one where the source code is converted to machine code and then executed each time the program runs. This is different from a compiled language such as C, where the source code is only converted to machine code once - the resulting machine code is then executed each time the program runs. 10 The Python Book Get started with Python^ #!/usr/bin/env python2 The following line creates an integer variable called hellojnt with the # value of 21 . Notice how it doesn't need to go in quotation marks The same principal is true of Boolean values We create a tuple in the following way And a list in this way # 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" hellojnt = 21 hello_bool =True hello_tuple = (21,32) hellojist = ["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. You could also create the same list in the following way hellojist = list() helloJist.append("Hello,") helloJist.append("this") helloJist.append("is") hello Jist.append("a") helloJist.append("list") 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 Notice that there will now be two exclamation marks when we print the element At this point, it’s worth explaining that any text in a Fython file that follows a# character will be ignored by the interpreter. This is so you can write comments in your code. # The first 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 first list without any warning as we # are using the same variable name as the previous list. hello_dict = {"first_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 hellojist 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 # first element has an index of 0. print(helloJist[4]) helloJist[4] +="!" #The above line is the same as helloJist[4] = helloJist[4] +"!" print(helloJist[4]) "Any text in a Python file that follows character will be ignored" s Get started with Python 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 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[ // first_name"] + "" + hello_dictriast_name"] +" has" + hello_dict["eye_colour"] +" eyes") print("{0} {1} has {2} eyes/'formatChello.dictrfirst.name"], hello_dict["last_name"L hello_dict["eye_colour"])) More about a Python list A Python list is similar to an array in other languages. A list (or tuple) in Python can contain data of multiple types, which is not usually the case with arrays in other languages. For this reason, we recommend that you only store data of the same type in a list. This should almost always be the case anyway due to the nature of the way data in a list would be processed. Control structures 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 look something 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. Other control 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 is true. 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: Indentation in detail As previously mentioned, the level of indentation dictates which statement a block of code belongs to. Indentation is mandatory in Python, whereas in other languages, sets of braces are used to organise code blocks. For this reason, it is essential that you use a consistent indentation style. Four spaces are typically used to represent a single level of indentation in Python. You can use tabs, but tabs are not well defined, especially if you happen to open a file in more than one editor. 'The 'for' loop uses a local copy, so changes in the loop won't affect the list" [liam@liam-laptop Python]$ ./construct.py Flow many integers? acd You must enter an integer [liam@liam-laptop Python]$ ./construct.py Flow 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 12 The Python Book 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 By now, the user has given up or we have a list filled with integers. We can loop through these in a couple of ways. The first is with a for loop #!/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 targetjnt = raw_input("How many integers?"' # By now, the variable targetjnt 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: targetjnt = int(targetjnt) except ValueError: sys.exitf'You must enter an integer") ints = list() count = 0 # Keep asking for an integer until we have the required number while count < targetjnt: newjnt = rawJnput("Please enter integer {0}:".format(count + 1)) isint = False try: newjnt = int(newjnt) 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 different 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(newjnt) # Increment the count by 1 count += 1 print("Using a for loop") for value in ints: print(str(value)) The Python Book 13 s Get started with Python TIP You can define defaults for variables if you want to be able to call the function without passing any variables through at all. You do this by putting an equals sign after the variable name. For example, you can do: 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 Functions and variable scope 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. The output from the program opposite is as follows: "Functions are used in programming to break processes down in" 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 #! /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. 14 The Python Book 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 bytryingto 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 print var NameError: name ‘var’ is not defined Comparison operators The common comparison operators available in Python include: 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 codefile describing what the application does, ora part of the application if it’s made up of multiple files. 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 problems one manageable chunk at a time. 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. Coding style 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 Happy programming! ESSENTIAL PYTHON COMMANDS Python is known as a very dense Language, with Lots of moduLes capabLe of doing aLmost anything. Here, we wiLL Look at the core essentiaLs that everyone needs to know 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 different set, but this List contains the best of the best. 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 defined and how memory is being used. Because the Python environment involves using a Lot 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. Since this is going to be a List of commands, there is the 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 to a few that you may not have seen yet. 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 that we didn’t cover here, once you’ve mastered these. 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 min’. 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 method or object names. 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 code re-run, you need to use the reload command. The format is ‘reload(modulename)’. Something to keep in mind is that the dictionary from the previous import isn’t dumped, 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 always use with caution. © 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. Installing a module involves downloading the source code, and compiling any included external code. Luckily, there is a repository of hundreds of Python modules available 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 simply run the pip command with sudo. Otherwise, you can install it to your personal library directory by addingthe command line option ‘ — 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” 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 value of this command is None. © 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 initialisation steps that you may need to do every time you use iPython. Evaluating code Sometimes, you may have chunks of code that are put together programmatically. If these pieces of code are put together as a 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 object that is created with the compile command instead of the code string. The return value of this command is None. The Python Book 17 lU r V / inn romr I KJt I VVl 111 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 Assertion Error 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 to recover from the failed assertion. © 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 ‘mapO’ 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 parameters for the given function. “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)” 0 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 and putting them in a temporary variable. You continue until either you have processed every element or you have hit a break command. In a while loop, you continue going through 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 current iteration and move on to the next one. 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. Virtualenvs Because of the potential complexity of the Python environment, it is sometimes best to set up a clean environment within which to install 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 need for a given set of tasks. IM 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 that it should only take two input parameters. 18 The Python Book 50 Python commands^ I File Edit View Search Terminal Help I •- python ■Python 2.7,6 (default, Mar 22 2614. 22:S9:S6) l[C£C 4. a. 2] on Unux2 llype "hetp ,T a "‘copyright* a "'credits*' or ""license 1 * for rvore infornattcn. m»> ny_boois ■ [True, True* False, False) 1»> aiT(fly bools) I False !?■>> anyCniy bools) iTrue ■ »> my_iist = [6 *1,2,1] all(ny_llst) Ipalse 1>>> any{ny_llst) ■True fly_ltst2 s ['a', *b\ "c'] aU(fy_Ust2) I True |>» any(ny_llst2) I True 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 given any empty iterable. © 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 towards zero. Enumerating Sometimes, we need to label the elements 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 instead of 0. 4V!|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. ® ,ls it a subclass? The command ‘issubclass(class1, class2)’ checks to see if classl is a subclass of class2. If classl and class2 are the same, this is returned as True. © , Global objects You can get a dictionary of the global symbol table for the current module with the command ‘globalsO’. Local objects You can access an updated dictionary of the current local symbol table by using the command ‘localsO’. Variables w The command ‘vars(dict)’ returns writeable elements for an object. If you use ‘vars()’, it behaves like ‘localsO’. ® , Making a global A list of names can be interpreted as globals for the entire code block with the command ‘global names’. ® , Nonlocals In Python 3.X, you can access names from the nearest enclosing scope with the command ‘nonlocal names’ and bind ittothe local scope. m Kl 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. © 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. © .Static methods You can create a statis method, similar to that in Java or C++, with the command ‘staticmethod(function_name)’. The Python Book 19 26 Ranges You may need a list of numbers, maybe in a ‘for’ loop. The command ‘rangeO’ 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. ’Cum With modules JP 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 help out. © 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. Xranges One problem with ranges is that all of the elements need to be calculated up front and stored in memory. The command ‘xrangeO’ takes the same parameters and provides the same result, but only calculates the next element as it is needed. 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. t Sorted lists You can use the command ‘sorted(listl)’ 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. 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. “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” File Edit View Search Terminal Help J&- p - m , python Python 2.7.6 (default. Mar 22 2614, 22:5^:56) [GCC 4 . 8 . 2 ] an Unux 2 Type "foelp*% h copyright " t "credits" or "license" for iwre information . >» a ■ “Hello World* b = menoryviewCa) >» b *>> VUt(b) lr«\ ¥ V* *V , 'o', * % 'W\ ’oh V, ■!% b[5] * * >» b[6] 1 w ! m 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 access the individual elements as you would get elements from a list (mem_view[1j, for example). 20 The Python Book 50 Python commands^ 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 ‘openO’ 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’). After opening the file, a file object is returned so that you can further interact with it. You can then read it, write to it, and finally close it. Yielding In many cases, a function may need to yield the context of execution to some other function. This is the case with generators. The preferred method for a generator is that it will only calculate the next value when it is requested through the method ‘nextO’. 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. In this way, you only need to have enough memory available to store the bare minimum to calculate the next needed value, rather than having to store all of the possible values in memory all at once. Pickling data There are a few different ways of serialising memory when you need to checkpoint 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. One thing to be aware of is that pickle does 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 definition has changed in the interim. 38 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. Once you have opened a shelf, you can read and write to it using key value pairs. When you are done, 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 best to not open the relevant files outside of the shelve module in Python. You can also open the shelf with writeback set to True. If so, you can explicitly call the sync method to write out cached changes. 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 necessary step. * You can do multiple threads of execution within Python. The ‘threadO’ 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 everything is the responsibility of the programmer. You need to handle mutex locks (with the methods ‘acquire’ and ‘release’), as well as create the original mutexes with the method ‘allocate_lock’. When you are done, you need to ‘exit’ the thread to ensure that it is properly cleaned up and no resources get left behind. You also have fine-grained control over the threads, being able to set things like the stack size for new threads. The Python Book 21 0 Python commands 40 Inputting data Sometimes, you need to collect input from an end user. The command ‘inputO’ can take a prompt string to display to the user, and then wait for the user to type a response. Once the user is done typing and hits the enter key, 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 readline module to get enhanced line editing. File Edit View Search Terminal Help class ny. c lass : _lnternal_num = ^Interna lustring = de _lnternal_func(): print def m ; { ) : print ny_cbj w my classQ ny_obj , _lnter nal_nufi Internal variables For people coming from other programming languages, there is a concept of having certain variables 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. They are not hidden, however, and there is no explicit protection for these variables or methods. It is u p to the programmer to honour the intention from the authorthe class and not alter any of these internal names. You are free to make these types of changes if it becomes necessary, though. File Edit Vie* Search Terminal Help »• . -5 python Python 2 . 7.6 (default, Har 22 2014, 22:59:56) [GCC 4,8,2] on UriilKZ Type "help", "copyright H * "credits” nr "license" for nore Information* strl = 'Hello World* »> str2 = strl >» strl = "Hello world" strl == str2 True »> strl ==~ str3 True CFip(stri* str2) »> cnptstri, strJ) >» strl Is str2 True strl is strl false (2 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 aretesting 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, ‘objl 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 ‘objl == obj2’. Testing for equality can become complex for more complicated objects. 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 l 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 list and count backwards. 22 The Python Book 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” F * Edit View Search Ter mi ra l Help jpfc* * * t # * python Pythofi 2.7.* (default, War 22 2614, 22:59:56) [GtC 4.0.23 on llnux2 Type "help " r "copyright* 1 p "credits" or "license" for wore information. >» sqrl ■■ Uwbde x: x*x »> »* 106 »> sqrl(6) 36 >» def gen_func(x): return lambda y: cubic - gen^funcO) cuhtc(2) 6 »> Since objects, and the names that point to them, are truly different things, you can have objects that have no references to them. One example of this isthe lambda expression. With this, you can create 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, you could build your own function to square a number with ‘lambda x: x*x’. You can then have a 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 that they are limited to a single expression, so you can’t generate very complex functions. © Compiling code objects 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 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 ‘compileQ’ 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 ‘evalO’ or ‘execO’ 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’ is a whole code block. m •V 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. ® , 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. Exiting your program There are two pseudo-commands available to exit from the Python interpreter: ‘exitO’ 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)\ /Tv Return values Hir 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. © If 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. The Python Book 23 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 "Get to grips with python and start building on the basics with these expert guides" The Python Book 25 ^Python essentials lack * 1 W*r - 3 SCi»* r4 Mkt » * te #p*l** It* UfluU **“ U ‘ ih-i* **; ’* w'i' i Ijt'i p,v*v * gm of lock, ici-itwi. lock * 1 Pip*r m I Scliiori - I n»k* * **vt: i l.i. *.** | Consul*' you NiUw fcr*» f*^' 1 M . Wg — ‘ Allow the Python script to run in a terminal, and outside the IDE Human input in the form of integers is used for comparing moves and, ultimately, playing the game Use deduction to determine one of three outcomes Loop the code over again and start from the beginning Append to integer variables to keep track of scores and more Code a game of rock, paper, scissors Learn how to do some basic Python coding by following our breakdown of a simple rock, paper, scissors game Resources Python 2: www.python.org/download IDLE I www.python.org/idle This tutorial will guide you through making a rock, paper, scissors game in Python. The code applies the lessons from the masterclass - and expands on what was included there - and doesn’t require any extra Python modules to run, like Pygame. 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 rudimentary Al if you wish. 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 sure it’s working properly. 26 The Python Book Python essentials Jfl m This section imports the extra Python functions we’ll need forthe code - they’re still parts of the standard Python libraries, just not part of the default environment f | O The initial rules of the game are created Wb here. The three variables we’re using and their relationship is defined. We also provide a variable so we can keep score of the games We begin the game code by defining the wO start of each round. The end of each play session comes back through here, whether we want to play again or not The game is actually contained all in here, asking for the player input, getting the computer input and passing these on to get the results. Atthe end of that, it then asks if you’d like to play again Pla y er i n P u t is done here. We give the w w 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 invalid option There are a few things going on when we w w show the results. First, we’re putting in a 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, and how to update the scores A Y We now ask for text input on whether w M or not someone wants to play again. Depending on their response, we go back to the start, or end the game and display the results pythonl # LLiuiih Umi I prtHDtii: iHk. Np«C P ffclfcaftc lb# n« r«rum ■jj = t S.M rock - l * a - 3 ni&*i - I rc-cfc? *PG€& a # pepes : ait iise.rH , 1 Ciipi * ] mi** - t r&cki Miwft. p±p *ri roe*, Kiwwcai i plifnjKtn - 0 ccep^tei _*<;ere - D ilm ( itartOi print *Ul ' ■ pity • i|is« f Np«i. tdlttfi * ahUe ^Hlh tcoret {) : pl«¥*f - JfcOVeO - rah den . rendLlfii 1 1, 13 result [player, •>-1* moVaO: aim* frvt: print player ■* i*v_ input ( * • - 1 r.f i; «> ■ 2 \n 3 *:i m 4 r a ■ 3 rKah t *J Cf T ft piAj'et - UtMpi47*f) ■ pi*y*r it, 2, Jls i«i.ur? player pr | nt I 5 l 1* fin'* uni}* r ifi l 1 ¥1**:. ri»iia bhS«! 4* J fit 1 . * Mlult I player. t paint t in*. sleep U) pdnt *j/h - prlftt *lt w i ime b sleep i^St paint " +p u ■ « a ■. ha i? ■ " . (ftAis#* ! e^iflputer 1 1 pleyer_icc re. tunpi*ter_HHjte player — print # Ti* jW.* *1 at E rales [player] — - - rcsputir: pr int *ftwr rinot3f Mt been k ^Urnumlv +* i else 1 pr int * !"h« < saatpuT ■ a ievgfcs at. y&u. ym> hs v * ■ g . i* l »* ¥ *ii , * _ computers core »• 1 *af p 1 #iy_ • ija inf! t answer - f aa upwt | a *f- j : vein ilka U ptiy h :• ; * T f/r- B, 1 t*/’ f * Hf'p res ill lAiaii else I prim * t r. a fi t aoe very h^_ tor p ley i tip oiit gw, H*e yci* mutt. ;ie#j 4 ‘ ■ pley* c 3 Bputer_s 0 eEe piiat # MJ5H iCQ«A 4 print layer: * P playar^aeore print *cc«fnjtsF: coapuEtr^l»re Lt mm ** ' fiilr.. _ l i itart II The Python Book 27 ^Python essentials The breakdown We need to start with the path to the Python interpreter here. This aiiows 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 from the system. We’re importingtwo extra modules on top of the standard Python code so we can use some extra functions throughout the code. We’ll use the random module to determine what move the computer will throw, 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 to display them or otherwise. We’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. Ifyou so wish, you can add additional moves, and this will start here. Here 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, mainly to tell the player how the computer moved. These names are only equated to these variables when they are needed - this way, the number assigned to each of them is maintained while it’s needed. Similar to the way the text names of thevariablesaredefined and used only 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. Very 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, although we have no scoring for tied games in this particularversion. Python modules 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 standard with Python. string Perform common string operations datetime and calendar Other modules related to time math Advanced mathematical functions json JSON encoder and decoder pydoc Documentation generator and online help system 28 The Python Book Here 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 of other tasks if so wished. If we do stop playingthe game, the score function is then called u pon - we’ll go over what that does when we get to it. We’ve kept the game function fairly simple so we can break down each step a bit more easily in the code. This is called upon from the 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, onto the result function which we use to find the outcome. E j*ri start (> : print "Let 1 s play a game of Rock, Paper, Scissors-* while game { ) : scores { ) (sE game ( ) s player = move ( I computer = random * randint ( 1 , 3) result (player , computer) i ■ t ui ■. play_again ( ) Te: movefM lie True: print player * r aw_input { *Rock lFaper nScissors - 3\nMak.e a move; * ) B 3 player ■** int (player) if player in (1,2,3): return player ■t ValueError; print "Cop a s ! i aian 'Python ShtlE* rile kd«t Shell Qebiig pptions ftindwws yelp ! Python 2,. 7.3 (default, Sep 26 2012, 21x51:14} [GCC 4 . 7 . 2 ] on linux 2 type 'copyright*, "credits' or "license ( | * for tnore information.. RESTART >> > ‘Lot's pl«y game of Rock# Paper# Sci score. Rfir-k ™ I I Pap^r - 2 Scissors - 3 : Make a move : 5 Oops! l didn't understand that. Please enter l f 2 Or 3. Rock — 1 Paper * 2 scissors - 3 make a juufve; l| The code in action We 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 rawjnput. We’ve also printed instruction text to go along with it. The‘\n’ we’ve used in the text adds a line break; this way, the instructions appear as a list. The try statement is used to clean up code and handle errors or other exceptions. We parse what the 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 up a ValueError, we use except to do nothing. It prints an error message and the while loop starts again. This will happen until an acceptable move is made. The Python Book 29 Python essentials « The resultf unction only takesthe variables player and computerforthistask, which is 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 code by the number of seconds in the brackets. We’ve put a one-second pause between counts, then half a second after that to show the results. To 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. Using names[computer], we’re telling the code to look up what the text version of the move is called from the names we set earlier on, and then to insert that where {0} is. Here 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 appended a number to one of their scores. Q) B 0 result {playe r, computer) : print time « sleep ( 1 ) print m 2 , * . „ * time- sleep (1) print " 3 ( * time , sleep (0.5) (EJ print 'omputer threw {Qj format { names [computer ] ) \ global player_score , computer_score EE : player -= computer: print "Tie game** m rules [player] -- computer: print "Your victory has been assured, player_score 1 .lk print "The computer laughs as you realise you have been defeated. " computer_score += 1 ‘Python Shell* Eifc? Edit Shell Qrtug Options Endows fcfelp Python 2-7.3 {default, Sep 26 2012, 21:51:14) IGCC ^+ 7 * 2 ] on linux 2 Type "copyright ", "credits" 1 or "license (|* for more information. >>> —————————————— RESTART — — — — The way we’re checking the result is basically through a process of elimination. 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. It then prints our tie message and goes back to the game function for the next step. If it’s not a tie, we need to keep checking, 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_score variable from before. If we get to this point, the player has lost. We print the losing message, give the computer a point and it immediately ends the result function, returningtothegamefunction. >>>■ let's play a game of Hock, Paper, Stock - 1 Paper * 2 ■ 3 Make a fnovei 5 Oops I I didn't understand that. Rack — 1 Paper * 2 Scissors - 3 Make a move: 1 1 . . . 2*ii 31 Computer threw Rock! Tie game- Scissors, Please enter 1 , 2 or 3* The code in action 30 The Python Book Python essentials^ The 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 with rawjnput, with the simple ‘y/n’ suggestion in an attempt to elicit an expected response. Giving 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 that it accepts both y and Y. If this is the case, it returns a positive response to game, which will start it again. If 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 game function to move onto the next section and not restart. play_again U : PM .answer - raw_inpui ("Would you like to play again? y/i |Q : answer in ("y", *¥", /es " , "Ye s", "of course! ”) :] m return answer [7177: print "Thank you very much for playing our game. ~ : scores () : : I > . player_score, eorrtputer_score print " HIGH SCORES" print "Player: ", player_score print "Computer: ", comput er_score name == * main ' : start ( ) J ELIF IFalso hasthe ELIF (else if) operator, which can be used in place of the second IF statement we employed. It’s usually used to keep code clean, but performs the same function. nf\ Going back to the start function, after game finishes we move onto the results. This section calls the scores, which are integers, and then prints them individually afterthe names 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 have Python write it to a file to keep if you wish. O ^ The final part allows for the script to m m I 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 the code when being imported. The Python Book 31 ^Python essentials This section imports the extra Python functions we’ll need for the code - they’re still parts of the standard Python libraries, just not part of the default environment We’re again providing variables so we can keep score of the games played, and they’re updated each round Our very basic graphics involve ASCII art of the game’s stages, printed out after every turn Program a game of Hangman Learn howto do some more Python coding by following our breakdown of a simple Hangman game #!/usr/bin/env python2 from random import * player_score = 0 computer_score = 0 r j def hangedman (hangman): graphic = [ Code listing + 1 1 1 1 1 + + + 1 1 1 1 1 1 0 =• 1 1 1 1 0 _ 1 _ 1 1 1 1 / \ print graphic[hangman] return Resources Python 2 I www.python.org/download IDLE I www.python.org/idle 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 techniques we implemented last time as well, so we can build upon them. 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. 32 The Python Book Python essentials^! The actual game starts here, with a while loop to let you continually play the game until you decide otherwise, then endingthe program The game rules are decided here, as well as the setup for the word and keeping track of tries and incorrect answers Each round of the game is played here, asking for an input, then tellingyou if you were corrector not. It prints out the graphic and changes any variables that need to be updated, especially incorrect and correct guesses After each round, the code checks if you’ve won or lost yet -the win condition beingthatyou guessed the word, or losing if you’ve made six guesses 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 select whether or not you wish to play again Upon quitting the game, scores are given for the duration of the play session. We also end the script with the if name code like before .91 Code highlightin IDLE automatically highlights the codeto make readingyour work that bit easier. It also allows you to change these colours and highlighting in IDLE’s Preferences, in case you’re colour blind or are just used to a different colour scheme in general. def start(): Code listing continued 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)~l 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, st rip() 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() The Python Book 33 ^Python essentials I see ASCII 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 printed out. #!/usr/bin/env python2 from random import * player_score = 0 computer_score = 0 def hangedman(hangman): graphic = [ uyyyy The rules 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 the words, you could also create a separate file and importthem like the random module. uyyyy def start(): print “Let’s play a game of Linux Hangman.” while game(): pass scoresQ We 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 therefore ensure compatibility. We’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 function to select a word for the player to guess. Very 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. Our 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 first letter is guessed. Here 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 34 The Python Book Python essentials^! 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() jjj!) if len(letter)==l and letter.isalpha(): if letters_tried.find(letter) != -1: print “You’ve already picked”, letter of other tasks if so wished. If we do stop playing the game, the score function is then called upon -we’ll go over what that does when we get to it. We 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 bit more. The 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 word as you build it up from guesses. We 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 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 scores here. We’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 statements are true, it goes on to the next turn. 1 C \ ca ^ u P on f unc1: ' on w e’ r e using to I 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. Indentations While IDLE will keep track of the indents in the code, if you’re using atexteditorto write some Python, 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 does aid in readability as well. 9 — » The Python Book 35 ^Python essentials (9l 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’re not using an IDE. If you plan to split this code up, we’d suggest starting with the word selection and results. 1 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.” TO 4 for i in range(word_length): if letter == word[i]: clue[i] = letter else: > print “Choose another.” ha ngedma n (letters_wrong) print “ “,join(clue) print “Guesses: “, letters_tried EH if letters_wrong == tries: print “Game Over.” print “The word was”, word compute r_score +- 1 break if “”.join(clue) = word: print “You Win!” print “The word was”, word player_score += 1 break return play_again() |Q « If 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 firstJndex variable is -1. If so, it adds one to the number of letters_wrong and then prints a message to let the player know that it was an incorrect guess. If 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 the player know that they’ve been successful and then make a record of it. We’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 check whether or not the game is over. We 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 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 number of guesses that have been made. Here 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 ended and reveal the mystery of the hidden word. We increase the computer’s score and break the loop. The next loop checks to see if the full clue concatenated is the same as the original word - if that’s the case, we print the win message, the full word and add one point to the player score before breaking the loop again. This can also be done with ifsand elifs to avoid using breaks. 36 The Python Book Python essentials^! guess_letter(): print letter = raw_input(“Take a guess at our mystery word:”) letter. strip() letter. lower() print return letter ss[ 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!” scores(): global player_score, computer_score print “HIGH SCORES” print “Player: “, player_score print “Computer: “, computer_score if name start() ‘ main ’: We end the entire game function loop by calling upon return again, which we will then pass all the way upto the start function once it’s finished. The human input function first of all prints out a rawjnput 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. The 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. Giving 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. If 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. Going 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 E9 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 current code and last month’s tutorial. not permanently save the scores, but you can have Python write it to a file to keep if you wish. 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 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. L ! .11: l 1 The Python Book 37 ^Python essentials Play poker dice using Python Put on your poker face and get ready to gamble as you hone your programming skill with a bit of poker dice Resources Python 2 I www.python.org/download IDLE I www.python.org/idle 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 cards right. Or in this case, virtual dice, and more like Reno as we continue with our Python game tutorials and introduce you to some poker dice. 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’ll be creating and appending lists with random numbers, and using functions multiple times in one block of code to cut down on bloat. 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 (we are coding in Python, after all)! The Start Here we’re doing some minor setups so we can get our code to run with some extra modules not included with the basics #!/usr/bin/env python2 import random from itertools import groupby Code listing The Rules We’re setting names for each dice roll so they can be properly identified to the player - much more interestingthan numbers The Score Again we’ve got some basic variables set up so we can keep score of the games if we want to The Script The game is handled here, passingthe player onto the next function to actually play, and handlingthe end of the session as well The Game We access the full game loop via here, and the function that allows us to play again if we’re so inclined 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 j def start(): print “Let’s play a game of Linux Poker Dice.” while game(): pass scores() C def game(): print “The computer will help you throw your 5 dice” throws() return play_again() The Throw 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 dice rolls to another function def throws(): roll_number = 5 dice = roll(roll_number) dice.sort() for i in range(len(dice)): print “Dice”,i + l,”:”,names[dice[i]] The Hand We’ve also got a special function so we can inform the player exactly what style of hand they have The Decision 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’re also using a correct number 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.” 38 The Python Book Python essentials^! The Re-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-rolls means the end of the game The Dice 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, we print something after every turn if rerolls == 0: Code listing continued 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-l] = selection-1 print “You have changed dice”, selection Second Hand We change and display the new dice hand to end the game. Again, we make sure to tell the player what the actual hand they have is The Rolls The function we reuse to roll our virtual six dice using a simple while loop. This allows us to keep the codebase smaller The Analysis 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 check for two iterations = 0 while iterations < rerolls: iterations = iterations + 1 replacement = dice_rerolls[iterations-l] dice[dice_changes[iterations-l]] = replacement dice.sort() for i in range(len(dice)): print “Dice”,i + l,”:”,names[dice[i]] result = hand(dice) print “You finish with”, result r~ £ def roll(roll_number): numbers = range(l,7) dice = range(roll_number) iterations = 0 while iterations < roll_number: iterations = iterations + 1 dice[iterations-l] = random, choice (numbers) return dice ^def hand (dice): dice_hand = [len(list(group)) for key, group in groupby(dice)] dice_hand . sort(reverse=True) straightl = [1,2, 3, 4, 5] straight2 = [2, 3,4, 5, 6] The Question Our simple ‘play again’ function that parses player input so we can restart or end the script The End Scores are displayed at the end of the script, and the very final part allows us to import this into other Python scripts as a module if dice == straightl 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[l] == 2: return “a full house!” else: return “three of a kind.” elif dice_hand[0] == 2: if dice_hand[l] == 2: return “two pair.” else: return “one pair.” else: return “a high card.” IF] EXTRA FUNCTIONS Splitting up actions into functions makes it easier to not only perform them multiple times, but reduce H3 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 the amount of code. On larger projects, this can aid with speed. if name == ‘ main ’: start() The Python Book 39 ^Python essentials #!/usr/bin/env python2 import random from itertools import groupby nine = 1 ten = 2 jack = 3 queen = 4 king = 5 ace = 6 Efl 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 others to read. 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 scoresQ def game(): print “The computer will help you throw your 5 dice” throws() return play_again() ni Begin I 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 that we’re also using Python 2 for this script. Importing \ J As well as importing the random module 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 telling the player what hand they have. Cards wO 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 equate what these should be printed out as. Scores w^T 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 Al for both rolls. HR start w' w 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, the score function is then called upon. /\C Game wU 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. Throws \ J I 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 it later with a different number that the player chooses. We get five random numbers in a list returned from the function, and we order it using sort to make it a bit more readable for the player and also later on forthe hand function. OQ Dice display C? We printout 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 specific number of the dice list. Current hand w w 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 of dice we have on to this function, and print. ^ ^ Throw again I Before we can throw the dice for the second round, we need to know which dice the 40 The Python Book Python essentials^! throws(): roll_number = 5 dice = roll(roll_number) dice.sort() for i in range(len(dice)): print “Dice”,i + l,”:”,namesEdiceEi]] result = hand(dice) print “You currently have”, result E3 INDENTATIONS Watch the indentations again as we split the else function. The following page’s code is on the same level as roll rolLnumber, dice_rerolls and dice_changes in the code. 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, dice_changesEiterations-l] = selection-1 print “You have changed dice”, selection El WHITE SPACE The big if function at the end of throws doesn’t have many line breaks between sections - you can add these as much as you want to break up the code into smaller chunks visually, aidingdebugging. 2, 3, 4 or 5.” player wants to roll again. We start this by asking 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 correct number of times. 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 are wrong. Stick 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 of eliminations or following flow charts. If the user wants to re-roll zero times, then that means they’re happy with their hand, and it must be the end of the game. We print a message to indicate this and display their hand again. The re-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 rolLnumber 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 use thanks to range(rerolls). Parse 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 that can be used, and add the valid choices to the dice_changes list. We use iterations-1 here as Python lists begin at 0 rather than 1 . We also print out a short message so the player knows the selection was successful. » The Python Book 41 ^Python essentials iterations = 0 while iterations < rerolls: iterations = iterations + 1 replacement = dice_rerolls[iterations-l] dice[dice_changes[iterations-l]] = replacement dice.sort() for i in range(len(dice)): print “Dice”,i + 1, ,names[dice[i]] g] HIGHER OR LOWER 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. result = hand(dice) print “You finish with”, result roll(roll_number) : numbers = range(l,7) dice = range(roll_number) iterations = 0 while iterations < roll_number: iterations = iterations + 1 dice[iterations-l] = random. choice(numbers) return dice Five of a Kind 6/7776 Four of a Kind 150/7776 FullFlouse 300/7776 Straight 240/7776 Three of a Kind .... 1200/7776 Two Pairs 1800/7776 One Pair 3600/7776 High Card 480/7776 New dice We’re resetting and reusing the iterations variable to perform a similar while loop to update 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 we want to replace it with. Sorting 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 the function, sending the whole thing back to the game function to ask if you want to play again. Dice rolling 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 three rounds, or modify it for real poker. Number of rolls We begin the whole thing by bringing over the rolLnumber 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 while loop. Remember Much like the while loops in the rest of the code so far, we’re keeping it going until iterations is the same as rolLnumber. 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 makes up the majority of the game. Hand analysis 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, a four and a five, it will return [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, we use the sort function again to sort it; however, this time we use the reverse=TRUE argument to make the analysis easier again. Straights 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. Your hand 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 in poker dice, we don’t have to worry about those. 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 must be four of a kind. If the first number is three, then it can be either a full house or three ofakind, 42 The Python Book Python essentials^! def hand (dice): dice_hand = [len(list(group)) for key, group in groupby(dice)] dice_hand.sort(reverse=True) straightl = [1,2, 3,4, 5] straight2 = [2, 3, 4, 5, 6] if dice == straightl 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[l] == 2: return “a full house!” else: return “three of a kind.” elif dice_hand[0] == 2: if dice_hand[l] == 2: return “two pair.” else: return “one pair.” else: return “a high card.” def play_again(): answer = raw_input(“Would you if answer in (“y”, “Y”, “yes”, return answer like to play again? y/n: “Yes”, “Of course!”): “) E3 TEXT EDITORS 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 programming languages, so you can get used to them with Python, then make the switch. 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 startQ ‘ main ’: E3 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 make it two-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 string to send back to the throw function so that it can be printed. Play again 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 the message thanking them for playing the game. This ends the gamefunction. Final scores 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 Python write it to a file to keep if you wish. Modules 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 when being run directly. I il 11 I 1 The Python Book 43 ^Python essentials Create a graphical interface for Python games Bring everything together with a Python GUI and take the next step in programming your own software Resources Python 2 I www.python.org/download IDLE l www.python.org/idle 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 graphical interface. 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. The start Here we’re doingsome minorsetup, including getting a new module that helps us create a simple graphical interface The imports We’re importingthe three games we created in past issues so we can call upon or use them The window Create a graphical window and give it a name so we can add some functions to it The frame Define the dimensions of the window and give a rough guide to placement of the objects within The welcome Print a message in the window and place it in a specific orientation. This works a little differently to print The button 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 The interface Creating and formatting buttons to start the other two tutorial games in the command line or shell The exit 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 The loop The mainloop allows the main windowto continue to work and be updated without exitingthe program unless specified #!/usr/bin/env python2 Main Interface Code Listing #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 mainframe. pack_propagate(0) mainframe. pack(padx = 5, pady = 5) 500) 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() ’ exi L ex i 1 exit_button = Button(mainframe, text = “Quit”, command = root. destroy) exit_button.pack(side = BOTTOM) root.mainloop() 44 The Python Book Python essentials^! New imports Import new modules that allow us to create the GUI part of Rock, Paper, Scissors, as well as removing the modules we no longer need New interface Our new main function allows us to call the majority of the game script when the rps_button is pressed. This contains the game components and the graphical components Newstart We’ve changed the start function so that it no longer goes to the score function after it’s finished. We’ve also removed the score function, as we track that differently so it can be displayed properly New game 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, otherwise it works roughlythesameas before New results The result function remains largely unchanged, only now it sends the outcome message to a variable we use for the interface, and generally uses the new GUI’s variables New window We create the game window with a slightly different method due to already having a ‘mainloop’ root window. We’re also giving it a name so you can identify it properly New variables Our new variables are set up so they can interact with both the game code and the interface code properly. We’ve also made sure to have a default selection for the player so that the code runs properly New frame 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 New choice 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 the variable and used by the game code Modified RPS Code Listing #!/usr/bin/env python2 # Linux User & Developer presents: Rock, Paper, Scissors: The Video Game: The Module C from Tkinter import * from ttk import * import random -j^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(l, 3) computer_choice.set(names[computer]) result(player, computer) -j 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() 1 rps_window. title (“Rock, Paper, Scissors”) player_choice = IntVar() computer_choice = StringVar() result_set = StringVar() player_choice.set(l) 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=l) rps_frame.rowconfigure(0,weight=l) Label(rps_frame, text=’Player’).grid(column=l, row = 1, sticky = W) Radiobutton(rps_frame, text =’Rock’, variable = player_choice, value = l).grid(column=l, row=2, sticky=W) Radiobutton(rps_frame, text =’Paper’, variable = player_choice, value = 2).grid(column=l, row=3, sticky=W) Radiobutton(rps_frame, text ^’Scissors’, variable = player_choice, value = 3).grid(column=l, row=4, sticky=W) New move Here we allow for the computer’s move to be displayed underthe ‘Computer’ label 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) New button Pressing the Play button we’ve put here runs the game script, prints out the scores and finally a message based on the outcome 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) New ending We’ve changed this so that the main script begins with gui now rather than the start function Label(rps_frame, textvariable = result_set).grid(column = ■j if name == ‘ main ’: gui() 2 , row = 7) The Python Book 45 ^Python essentials #!/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.mainloopQ F] MAIN WINDOW 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. First line 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 also using Python 2 for this particular script. Import graphics Tkinter is the graphical interface we’re using and while it’s a standard Python function, you’ll need to import the module so you can use it. We’ve used the ‘from [module] import *’ method so that we can use the functions from it without having to add Tkinter at the beginning. 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. Root window 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. 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. Introductions 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 tell Tkinter to put it at the top of the interface. Import games We’re importing the modules for the three games. We added the line at the bottom Mainframe 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 Rock, 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 46 The Python Book 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 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 EB def game(): player = player_choice.get() computer = random. randint(l, 3) computer_choice.set(names[computer]) result(player, computer) } EE PYTHON SHELL Our other code will run in the shell or via a command line in the same way as before when the buttons are pressed. 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 Other games For the other two games, the code is mostly the same; however, we call upon the start function in both of them. In the final interface, this will cause the games to run in the shell or command line as they’ve been running before. Break the loop 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’. Game code Nothing much has changed in the start of this code, other than a few import changes. The code for running it in the command line is still 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 easierto use and understand. Game interface 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. Game variables 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. Start function 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. Game function 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. v> The Python Book 47 ^Python essentials result(player, computer): new_score = 0 if player == computer: result_set.set(“Tie game.”) else: i 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(l) player_score = IntVar() computer_score = IntVar() m GAME WINDOW 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 bringyou back to the main interface. Result function 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 before adding it back into it. Tie 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 and computer variable is the same. What changes this time is that, instead of printing the text, we send the “Tie game” message to our result variable using the set function from Tkinter. Win 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 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, as it is not a standard variable. Lose 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. New 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 TopleveK). This allows the window to run separately and on top of the main window. We’ve also given it a name, which will not change the main window’s name in the process. Interface variables Flere 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 the choice for rock. This means there will at least be a default choice when the game is started, and it won’t cause an error. Game frame 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 48 The Python Book 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, weights) rps_frame.rowconfigure(0,weight=l) Label(rps_frame, text-Tlayer ).grid(column=l, row = 1, sticky = W) Radiobutton(rps_frame, text =’Rock’, variable = player_choice, value = l).grid(column=l, row=2, sticky=W) Radiobutton(rps_frame, text =’ Paper', variable = player_choice, value = 2).grid(column=l, row=3, sticky=W) Radiobutton(rps_frame, text ^Scissors’, variable = player_choice, value = 3).grid(column=l, 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, Label(rps_frame, Label(rps_frame, Label(rps_frame, text = “Score”). grid(column = 1, row = 5, sticky = W) textvariable = player_score).grid(column = 1, row = 6, sticky = W) text = “Score”). grid(column = 3, row = 5, sticky = W) textvariable = computer_score).grid(column = 3, row = 6, sticky = W) Label(rps_frame, textvariable = result_set).grid(column = 2, name == ‘ main gui() row 7) 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 justifies this to the left. Player’s choice 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’ve determined in the first set of rules. Press Play 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 function usingthe 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 not need a specific quit button. Computer’s move We display the computer move here. First of all, we label what this is and then create Running score We have two sets of scores to display - one for the player and the other for the 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 display the message for the game’s outcome End game b w 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 the main interface. The Python Book 49 ^Python essentials Bring graphics to simple Python games Complete your trio of games with a graphical interface for the hangman and poker dice code Resources Python 2 I www.python.org/download IDLE l www.python.org/idle 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. 1 1mported Here we’re doingthe same minorsetup, includinggetting the Tkinter module that helps us create a simple graphical interface 2 Words We’re keeping our variables that determine the word to guess here so it can be easily accessed anywhere in the code 3 Function Like last time, we’re puttingthe majority of our original code into a new function, gui 4 Analysis We select the word and analyse it before continuingon with the rest of the code 5 Graphics The hangedman function is largely unchanged, albeit with new code to display our ASCII graphics on the interface 6 Guesses We checkthe number of mistakes made, and call the guessjetter function to checkthe letter entered qTI Hangman Code Listing EB: EE EE i Tkinter import * if letter == word[i]: i ttk import * clue[i] = letter i random import * hangedman (letters_wrong) i = 0 clue_set = “ “ . join(clue) Llength = 0 word_output . set(clue_set) i = 0 if letters_wrong == tries: result_text = “Game Over. The word gui 0 : was “ + word global word, word_length, clue result_set . set(result_text) dictionary = [“gnu” , ’’kernel” , ”linux” , ’’magei new_score = computer_score.get() ’penguin” , ’’ubuntu”] word = choice(dictionary) new_score += 1 computer_score . set (new_score) word_length = len(word) if . join(clue) == word: clue = word_length * result_text = “You Win! The word tries = 6 was “ + word result_set . set(result_text) def hangedman(hangman) : new_score = player_score.get() graphic = [ new_score += 1 U99 99 player_score. set(new_score) 1 1 def guess_letter() : 1 o letter = letter_guess.get() -1- letter ,strip() / \ letter . lower() 1 return letter “””1 def reset_game() : graphic_set = graphic[hangman] global word, word_length, clue hm_graphic.set(graphic_set) incorrect_guesses. set(0) hangedman(0) def game() : result_set . set(“”) letters_wrong = incorrect_guesses . get() letter_guess. set(“”) letter=guess_letter() word = choice(dictionary) first_index=word. find(letter) word_length = len(word) if first_index == -1: clue = word_length * [“_”] letters_wrong +=1 new_clue = “ “ . join(clue) incorrect_guesses. set(letters_ wrong) word_output . set (new_clue) else: if name == ‘ main ’: for i in range(word_length) : gui() 50 The Python Book Python essentials^! 1 More imports We’ve added the new imported modules we need to make Tkinter work and keep the rest the same 2 Dice list The list that holds the dice is kept outside the main function so that it can be accessed everywhere 3 Rolls Same goes for the roll function. It doesn’t specifically need to be inside the gui function anyway 4 Decisions The checkboxes in the graphical code we’re goingto create later will give us numbers we can analyse for the code. We retrieve these numbers and checkthem to find out which dice the user wishes to re-roll 5 Hands Finally, our hand analysis function is the last part of the original code that is kept outside the gui function. Both this and the above function pass the necessary details back up the chain to then be added into the new graphical elements of the new interface 6 No dice If no dice have been selected to re-roll, the hand output is changed to show a final message 7 Re-roll This part is almostthe same as before - a new set of dice are rolled and then inserted into the list of dice like before, then re-sorted to make the hand analysis easier 8 More functions The new gui function is the main change to the Poker Dice code, and as before includes the Tkinter elements and other parts of the original code 9 Game start A simple function that we can use to activate the re-rolls of the dice 10 New hand The new dice are named, analysed, and everything is then set for the gui to display the final outcome 11 Reset Like with the hangman code, we have a function to reset all the variables, allowingyouto start the game again |“from Tkinter import * fJPJ from ttk import * “1 import random L from itertools import groupby dice = 0 "def roll(roll_number) : numbers = range(l,7) dice = range(roll_number) iterations = 0 while iterations < roll_number: iterations = iterations + 1 dice[iterations-l] = random. choice(numbers) return dice E E E E "def hand(dice) : dice_hand = [len(list(group)) for key group in groupby(dice)] dice_hand. sort(reverse=True) straightl = [1,2, 3, 4, 5] straight2 = [2, 3, 4, 5, 6] if dice == straightl 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[l] == 2: return “a full house!” else: return “three of a kind.” elif dice_hand[0] == 2: if dice_hand[l] == 2: return “two pair.” else: return “one pair.” else: . return “a high card.” 'def gui () : global dice dice = roll(5) dice.sortO 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) Poker Dice Code Listing - global dice dicel_check = dicel.get() dice2_check = dice2.get() dice3_check = dice3.get() dice4_check = dice4.get() dice5_check = dice5.get() dice_rerolls = [dicel_check, dice2_check, dice3_check, dice4_check, dice5_check] i 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) rerolls)) dice_changes = range(len(dice_ Gif def game() : throws() def throwsQ: iterations = 0 while iterations < roll_number: iterations = iterations + 1 dice_changes[iterations-l] = number_rerolls[iterations-l] iterations = 0 while iterations < roll_number: iterations = iterations + 1 replacement = number. rerolls [iterations-1] dice[dice_ changes[iterations-l]] = 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(f inal.dice) final.result = “You finish with hand(dice) hand.output . set(f inal.result) def reset_game() : global dice dice = roll(5) dice.sortO 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() The Python Book 51 ^Python essentials [j^ esh #! /usr/bin/env python2 from Tkinter import * from ttk import * from random import * word = 0 word_length - 0 clue = 0 m def gui () : global word, word_length, clue dictionary = [“gnu” , ’’kernel” , ”linux” word = choice(dictionary) word_length = len(word) clue = word_length * [“_”] tries = 6 def hangedman(hangman) : graphic = [ ’’mageia” , ’’penguin” , ’’ubuntu”] When you’ve run out of guesses, the game stops. From here, you can also reset the game to play again if you wish. + 1 1 1 1 - 1 0 — 1 — 1 1 1 1 / \ graphic_set = graphicEhangman] hm_graphic . set (graph ic_set) Ef def Elf m game() : letters_wrong = incorrect_guesses . get() letter=guess_letter() f irst_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 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 “ result_set . set (result. text) new.score = player.score . get() new.score += 1 player.score. set (new.score) word word First lines 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 ttk, for the grid code we’ll be using to align the different elements. Global variables We have kept these three variables outside of the gui function so they can be accessed at all points in the code. Python 2 does not allow you to call upon global variables when you’re in a nested function, whereas in Python 3 this could have gone into the gui function. Graphical function 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 we activate the gui function from here. Random word 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 clue to display is set. The hanged man 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 display the result. Games begin 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 the code. Check the letter This section of the code is again largely unchanged - the letter is taken and compared to 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 variable to add the letter in the right spot. Update interface 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 to read. Update scores 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 wins and losses score is updated using set. 52 The Python Book Python essentials^! nf n m 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 = StringVarQ E3 ORIGINAL INTERFACE 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. hm_frame = Frame (hm_window, padding = ‘3 3 12 12’, width = 300) hm_f rame . grid(column=0 , row = 0, sticky=(N , W, E , S)) hm_f rame . columnconf igure(0, weight=l) hm_f rame . rowconf igure(0 , weight=l) Label(hm_f rame , textvariable = hm_graphic) . grid(column=2 , row = 1) Label(hm_f rame , text=’Word’ ) . grid(column=2 , row = 2) Label(hm_f rame , textvariable = word_output) . grid(column=2 , row = 3) E9 THE HANGMAN GUI Press the updated Hangman button to launch a new window. Here we have the initial graphic, word clue and entry forthe player to interact with. The scores are set to zero, and no result message is displayed as no games have been played yet. Label(hm_f rame , text=’Enter a letter ’). grid(column=2 , row = 4) hm_entry = Entry (hm_f rame , exportselection = 0, textvariable = letter_guess) . grid(column = 2, row = 5) hm_entry_button = Button (hm_f rame , text = “Guess”, command = game) . grid(column = 2, row = 6) Label(hm_f rame , text = “Wins”) . grid(column = 1, row = 7, sticky = W) Label(hm_f rame , textvariable = player_score) . grid(column = 1, row = 8, sticky = W) Label(hm_f rame , text = “Losses”) . grid(column = 3, row = 7, sticky = W) Label(hm_f rame , textvariable = computer_score) . grid(column = 3, row = 8, sticky = W) Label(hm_f rame , textvariable = result_set) . grid(column = 2, row = 9) replay_button = Button (hm_f rame, text = “Reset”, command = reset_game) . grid(column = 2, row = 10) if name == ‘ main gui() Sanitise input I w The guessjetter 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 the letter can be used properly. A A New window I We use the Toplevel command from Tkinter like last month to separate the loops of the main interface and game window. We then use title to call it Hangman. Interface variables I Cm 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 rest of the code with these to get and set values. i The frame is set up as before Bl Framed window I O 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 with specific row and column points. I Clue to Hangman I These labels are fairly straightforward - we’re either giving them fixed text, or telling them to use a specific textvariable so they can be updated as we play the game. Text entry 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 the letter the player entered. Results and reset 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 us to import the module into the interface code. The Python Book 53 H^Python essentials ^ ^ Start over I # The usual array of command-line compatibility and module importing here. The groupby function is specifically imported here for dice analysis. Outside dice I O 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 Python 2, we need to call it with global from here to make sure the game can be reset properly. IQ Dicerolls I w 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 changed at all from the original code. OO Han d°f dice fcw 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 function into another script if you wish. GUI start b I As we’ve mentioned last month and in the Hangman code, we put all the GUI code into a function so that we can call on it when we want to. In this case, pressing the Poker Dice button on the main interface activates pokerdice.gui, which is this function. OO First roll 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 for the interface towards the end of the script QQ Start game 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 can add other functions to this part if you wish. Q I Dice selection 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 dice rolls are. 4 EEC nil m #! /usr/bin/env python2 from Tkinter import * from ttk import * import random from itertools import groupby dice = 0 EXTRA GAME FUNCTIONS 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. Experiment with what you want to do! THE POKER DICE GUI SC m def roll(roll_number) : numbers = range(l,7) dice = range(roll_number) iterations = 0 while iterations < roll_number: iterations = iterations + 1 dice[iterations-l] = random. choice(numbers) return dice def hand(dice) : dice_hand = [len(list(group)) for key, group in groupby(dice)] dice_hand . sort (reverse=T rue) straightl = [1,2, 3,4, 5] straight2 = [2, 3, 4, 5, 6] if dice == straightl 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[l] == 2: return “a full house!” else : return “three of a kind, elif dice_hand[0] == 2: if dice_hand[l] == 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” ace : “A” } result = “You have “ + hand(dice) m 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 Reroll button. queen: “Q” , king: “K” def game() : throwsQ def throws(): global dice dicel_check = dicel.get() dice2_check = dice2.get() dice3_check = dice3.get() dice4_check = dice4.get() dice5_check = dice5.get() dice_rerolls = [dicel_check, _ check, dice5_check] dice2_check, dice3_check, dice4_ 54 The Python Book m pi pi H M M Python essentials 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-l] = number_rerolls[iterations-l] iterations = 0 while iterations < roll_number: iterations = iterations + 1 replacement = number_rerolls[iterations-l] dice[dice_changes[iterations-l]] = 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(f inal_dice) final_result = “You finish with “ + hand(dice) hand_output . set(f inal_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) IP M ONE WINDOW The way we’ve madetheseTkinter interfaces is to have the games launch in a separate window. You can have them all running in one window, though, by replacingthe 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. 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) dicel = IntVar() dice2 = IntVar() dice3 = IntVar() dice4 = IntVar() dice5 = IntVar() result_set = StringVar() player_score = IntVar() computer_score = IntVarQ 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_f rame . columnconf igure(0 , weight=l) pd_f rame . rowconf igure(0 , weight=l) 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) rerolll = Checkbutton (pd_f rame, = 0) . grid(column=l , row = 5) text = ‘ ‘1”, , variable = dicel, onvalue = 1, offvalue reroll2 = Checkbutton(pd_frame, = 0) ,grid(column=2, row = 5) text = ‘ ■2”, , variable = dice2, onvalue = 2, offvalue reroll3 = Checkbutton(pd_frame, = 0) ,grid(column=3, row = 5) text = “ ‘3”, , variable = dice3, onvalue = 3, offvalue reroll4 = Checkbutton(pd_frame, = 0) . grid(column=4, row = 5) text = ‘ ‘4”, , variable = dice4, onvalue = 4, offvalue reroll5 = Checkbutton(pd_frame, = 0) ,grid(column=5, row = 5) text = * ‘5”, , variable = dice5, onvalue = 5, offvalue pd_reroll_button = Button (pd_f rame, text “Reroll” , command = game) ,grid(column = 3, row = 6) replay_button = Button(pd_f rame , text = “Reset”, command = reset_game) . grid(column = 3, row = 7) The check buttons are new Of Dice tore-roll bO 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 function when the element does equal zero. QO Earlyfinish U If no dice have been selected to re-roll, the list will contain all Os, which will then be removed. The length of this list will then also be zero, meaning we can usethattoendthegameif the player hits Reroll without selecting any dice. New dice b # This else function works roughly the same as before. We start by getting the necessary information for how many dice to roll, and a listto put the re-rolls. We then roll as many new dice as we need with the first while loop QQ Game over 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 also create and set. OQ Graphical variables fav 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 addingto the variable. *30 C heck buttons 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 outcome is to the specific Tkinter variable. if name_ gui() The Python Book 55 ^Python essentials Build an app for Android with Python Master Kivy, the excellent cross-platform application framework to make your first Android app... ■ Here we've drawn all the simple graphics for our game.. . now we just have to make the shapes actually do something! 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- style game. We'll then be able to compile this straight to an Android APK that you can use just like any other. 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 libraries to make any sort of program you like. 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 have any problems following along. 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 to be drawn anywhere on your screen and on any widget type. Before we can do any of this we'll need a class 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 56 The Python Book Python essentials^! 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: : canvas: Color: rgba: 1, 1, 1, 1 Rectangle: pos: self.pos size: self.size : canvas: Color: rgb: 1, 0.55, 0 Rectangle: pos: self.pos size: self.size : 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! Once you have the basic techniques, you aren’t limited to one app. . . your imagination is the only limit kl 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 BreakoutApp( pp): pass BreakoutApp().run() from kivy. properties import (ListProperty, NumericProperty, ObjectProperty, StringProperty) ;s Game(FloatLayout): # Will contain everything blocks = ListProperty([]) player = ObjectProperty() # The game's Player instance ball = ObjectPropertyO # The game's Ball instance >s Player(Widget): # A moving paddle position = NumericProperty(0.5) direction = StringProperty('none') >s 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([ L, 0. ]) >s Block(Widget): # Each coloured block to destroy colour = ListProperty([l, 0, 0]) from kivy. graphics. context_instruct ions import Color from kivy. graphics .vertex_instruct ions import Rectangle >s Player(Widget): (self, **kwargs): (Player, self). init (**kwargs) with self. canvas: Color(l, 0, 0, 1) # r, g, b, a -> red Rectangle (pos=self. pos, size=self . size) # or without the with syntax, self, canvas, add (...) The Python Book 57 ^Python essentials ■ Running the app shows our coloured blocks on the screen. . . but they all overlap! We can fix that easily 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 a random colour (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 actually see something there. Game(FloatLayout) : F setup_blocks(self): for y_jump in (5): for x_jump in (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) Breakout App (App): if build (self): g = Game() g.setup_blocks() return g Here we create the widgets we want then use add_widget to add them to the graphics tree. Our root widget on the screen is an instance of Game and every block is added to that to be displayed. 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 position proportionate to the layout. 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 parent width and height. 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 other widgets as well (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 their positions. The Ball also uses an extra property to remain square rather than rectangular, just because the alternative would likely look a little bit odd. We've now almost finished the basic graphics of our app! All that remains is to add a Ball and a Player widget to the Game. : ball: the_ball player: the_player Ball: : 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 time since its last call (dt here), which we've passed 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 the regular 1 /60s you want. 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 off the Player class by adding this key handler along with touch/mouse input. ss Player (Widget): on_touch_down (self, touch) : self, direction = ( 'right' if touch, x > self, pa rent. W center_x else 'left') ’ on_touch_up(self, touch): self, direction = 'none' ;f on_key_down(self, keypress, scancode, *args): if scancode = 275: self, direction = 'right' el if scancode == 276: self, direction = 'left' else: self, direction = 'none' ;f on_key_up(self, *args): self, direction = 'none' sf update (self, dt): dir_dict = {'right': 1, 'left': -1, 58 The Python Book 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 update method is called. We can also add the right behaviour forthe ball (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.loseO if the ball hits the bottom of the player's screen, so let's now add in some game end code to handle all of this (Fig. 08). And then add the code in Fig. 09 to your ' 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 ball hits them (Fig. 10). This fully covers these last conditions, 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 first collided with the Block. 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 settings screen as well. We’re just going to finish showing one cool thing that you can already do - compile your game for Android! Generally speaking you can take any Kivy 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, sensors or native notifications. 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-Android tools that create the APK. import random Block(Widget): ;f init (self, **kwargs): (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)]) : size_hint: 0.09, 0.05 # ... canvas part : size_hint: 0.1, 0.025 pos_hint: {'x': self. position, 'y': 0.1} # ... canvas part : pos_hint: {'x': self.pos_hint_x, 'y': self.pos_hint_y} size_hint: None, None proper_size: (0.03*self. parent. height, 0.03*self. parent. width) size: self.proper_size, self.proper_size # ... canvas part from kivy. clock import Clock from kivy. core. window import Window from kivy. utils import platform >s Game(FloatLayout): if update (self, dt): self. ball, update (dt) # Not defined yet self, player. update(dt) # Not defined yet start(self, *args): Clock . schedule_interval(self . update , 1./60 .) stop(self): Clock. unschedule(self. update) 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 BreakoutApp( pp): 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. 04 Fig. 05 Fig. 06 The Python Book 59 ■^Python essentials >s Ball(Widget) if 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 * (self.velocity[0]) if self.x < self. parent. x: # Bounce from left self.velocity[0] = (self.velocity[0]) if self. top > self. parent. top: # Bounce from top self. velocity [1] = -1 * (self. velocity [ ]) if self.y < self, parent. y: # Lose at bottom self. parent. lose() # Not implemented yet self. bounce_from_player(self. parent, player) sf bounce_from_player(self, player): if self.collide_widget(player): self.velocity[l] = (self.velocity[l]) self.velocity[0] += ( 0.1 * ((self.center_x - player. center_x) / player, width)) GameEndPopup(ModalView) : message = StringProperty() game = ObjectProperty() Game(Widget): lose (self): self.stop() GameEndPopup(message=' [color=#ff0000]You lose! [/color] game=self).open() ?f win (self): # Not called yet, but we'll need it later self.stop() GameEndPopup(message=' [color=#00ff00]You win! [/color] ', game=self).open() : 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.dismissQ Fig. 07 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 0penJDK7, 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++, as well as libgcc. You can then go on and download and install Buildozer: 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 Kivy app, and you’ll have to name the main code file ‘main.py’, this is the access point that the Android APK will expect. 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 the specific Android build options. We suggest that you check through the whole file just to see what's available but most of the default settings will be fine, the only thing we suggest changing is (Fig. 1 1). 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 build your APK and get going! buildozer android debug This will take some time, so be patient and it will work out fine. When you first run it, it will download 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 Fig 0Q compile the Python components of your APK. A lot of this only needs to be done once, as future builds will take a couple of minutes if you change the buildozer.spec, or just a few seconds if you've only changed your code. The APK produced is a debug APK, and you can install and use it but there are extra steps if you want to fully digitally sign it so that it can be posted on the Play store. This isn't hard, and Buildozer can do some of the work, but you can check the documentation onlinefor full details. 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 yourSettings before you can install it. 60 The Python Book Python essentials Putting your APK on the Play Store Find out howto digitally sign a release APK and upload it to an app store of your choice 1 Build and sign a release APK 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 ## 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 SHAlwithRSA -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 self, parent . do_layout() self, pa rent . dest roy_blocks(self ) Game( .oatLayout): destroy_blocks(self, ball): for i, block in (self. blocks): if ball.collide_widget(block): y_overlap = ( ball. top - block. y if ball. velocity D] > 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 [(] *= -1 else: ball. velocity [1] *= -1 self. remove_widget(block) self, blocks, pop(i) if (self, blocks) == 0: self, win () return # Only remove at most 1 block per frame 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 2 SignupasaGoogle Play Developer 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 upload asmanyapps as you like. 3 Upload your app to the store 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 hours for your app to go live! ■ Your game should run on any modern Android device... you can even build a release version and publish to an app store! ■^Python essentials Making web apps with Python Python provides quick and easy way to build applications, including web apps. Read on to find out how to use it to build a feature-complete web app 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 development you get access to a huge catalogue of modules and community support - make the most of them. 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. Resources Python 2.7: https://www.python.org/download/releases/2.7/ Django version 1.4: https://www.djangoproject.com/ Creating the Django Project magazine issue tracker The django-admin.py file is used to create new Django projects. Let’s create one for our issue tracker project... 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 Django sites with very little effort. $ django-admin.py startproject ludlssueTracker 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 directoryyou will find the following items... manage.py: Python script to work with your project. ludlssueTracker: A python package (a directory with init .py file) for your project. This package contains your project’s settings and configuration data. ludlssueTracker/settings.py: This file contains all the configuration options for the project. ludlssueTracker/urls.py: This file contains various URL mappings. wsgi.py: An entry-point for WSGI-compatible web servers to serve your project. Only useful when you are deploying your project. For this tutorial we won’t be needing it. Configuringthe Django project settings Before we start working on the application, let’s configure the Django project as per our req uirements. Edit ludlssueTracker/settings.py as follows (only parts requiring modification are shown): Database Settings: We will be using SQLite3 as our database system. NOTE: Red text indicates new code or updated code. ‘default’ : { ‘ENGINE’: ‘django. db. backends. sqlite3’ , ‘ NAME ’ : ‘ ludsite . db3 , Path settings 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 following code at the top of the settings.py file: import os def getabspath(*x) : return os . path . join(os . path . abspath(os. path .dirname( file )) , *x) Now you can update the path options: @code TEMPLATE.DIRS = ( getabspath(‘ templates’ ) ) MEDIA_ROOT = getabspath( ‘ media ’ ) 62 The Python Book Python essentials^! MEDIAJJRL = ‘/media/’ Nowwewillneedtoenabletheadmin interface for our Django site. This is a neatfeature of Django which allows the automatic creation of an admin interface of the site based on the data model. The admin interface can be used to add and manage content for a Django site. Uncomment the following line: 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’ , ) Creating lud issues app In this step we will create the primary app for our site, called ludissues. To do that, we will use the manage.py script: $ python manage.py startapp ludissues We will need to enable this app in the config file as well: INSTALLED_APPS = ( 'django. contrib. admin ' , ‘ludissues’ , ) Creating the data model This is the part where we define the data model for our app. Please see the inline comments to understand what is happening. From django.db import models: # 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 = ( ('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=T rue , blank=T rue) # multichoice with defaulting to "new” status = models. CharField(max_ length=25 , choices=ISSUE_STATUS_ CHOICES, defaults 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 ' , ' modif ied_on ' ) search_f ields = ['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, run the following command: $ python manage.py syncdb You’ll be also asked to create a superuser for it: 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 Enablingthe admin site 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 model to view. Update the urls.py file as follows: from django. conf . urls import patterns, include, url from django. contrib import admin admin . autodiscover() urlpatterns = patterns(‘’, url(r ’ A admin/’ , include(admin . site. urls)) , ) Starting the Django web server 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 interface works... To start the web server: $ 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 browser to http://localhost: 8000 /admin. You will be asked to log in here. Enter the username and password that you created while syncingthe database. ■ Admin login screen After logging in, you will notice that all the apps installed in your project are available here. We are only interested in the Auth and Ludissues app. 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, you can now add a few issues to the system. The Python Book 63 ^Python essentials « *d Tim i>lrjlign SfltldminlitntiiHi ■ Admin homepage 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 date time on which an issue is modified. Another cool thing is that the owner field is automatically populated with all the users inside the site. 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/. ■ The ‘Add issue’ menu ltl«1 liftikw la chi-rift ■ The list view for issues Creatingthe public user interface 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 public interface. Let’s create it now. We will have to begin by editing the main urls.py(ludlssueTracker/urls.py). urlpatterns = patterns(‘’, (r ’ A ’ , include ( ‘ ludissues. urls’ )) , (r ,A admin/’, include(admin . site . urls)), ) This ensures that all the requests will be processed by ludissues.urls first. Creating ludissues.url Create a urls.py file in the app directory (ludissues/urls.py) with the following content: 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. gener ic. list_detail’ , #issue-list and issue-detail are the — 111, I II u template names #which will be looked in the default template #directories url(r’ A $’ , ’object, list’ , info, name=’ issue-list ’ ) , url(r ’ A (?P\d+)/$’ , ’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. Setting up template and media directories In this step we will create the template and media directories. We have already mentioned the template directory as TEMPLATE.DIRS = ( getabspath( ‘ templates ’ ) ) Which translates to ludlssueTracker/ ludlssueTracker/tem plates/. Since we will be accessing the templates from the ludissues app, the complete directory path would be ludlssueTracker/ludlssueTracker/templates/ ludissues. Create these folders in your project folder. Also, create the directory ludlssueTracker/ ludlssueTracker/media/ for holding the CSS file. Copy the style.css file from the resources directory of the code folder. To serve files from this folder we need to make it available publicly. To do that, open settings.py and add the following lines in ludlssueTracker/ ludlssueTracker/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’ A ’ , include( ‘ ludissues. urls’ )) , (r’ A admin/’, include(admin . site . 64 The Python Book Python essentials^! I ud Lraup tracker D# sc rip l ion 1 T1^4A£flrt§QfV11ttlWmOAfe t Marat is 3 BHtastfc MbmH&n hM uim !uu m trn SWWn «suj»c* & Slant m. Hktfc itiiifu 5 mmllm i m-wifw from nairriw Et lafeft Status Owner joaftai am The magazine Issue Tracker in action - list of issues urls)), (r ’ A media/ (?P. *)$’ , ’django. views. static, serve’ , { ‘document_root ’ : settings. MEDIA_R00T}) ) Creating the template files Templates will be loaded from the ludlssueTracker/ludlssueTracker/tem plates directory. In Django, we start with the ludlssueTracker/ludlssueTracker/templates/ base.html template. Think of it as the master template which can be in herited by sla ve ones . ludlssueTracker/ludlssueTracker/templates/ base.html <! DOCTYPE html PUBLIC “-//W3C//DTD XHTML Strict//EN” “ HYPERLINK “http://www.w3.org/ TR/xhtmll/DTD/xhtmll -strict . dtd” http://www.w3 . org/TR/xhtmll/DTD/ xhtmll-strict . dtd”> {% block title %}{% endblock %}LUD Issues

LUD Issue Tracker

  • View Issues Admin Site

{% block content %} {% endblock %}

{{variablename}} represents a Django variable. (% block title %} represents blocks. Contents of a block are evaluated by Django and are displayed. These blocks can be replaced by the child templates. Now we need to create the issue_list.html template. This template is responsible for displaying all th e iss ues available in the system. ludlssueTracker/ludlssueTracker/templates/ ludissues/issue_list.html {% extends ‘base.html’ %} {% block title %}View Issues - {% endblock %} {% block content %} {% for issue in object_list %} {{ issue. id }} ax/td> {{ issue. name }} {% endfor %}
Issue Description Status Owner
{{ issue. status }} {{ issue. owner}}
{% endblock %} Here we are inheriting the base.html file that we created earlier. {% for issue in objeetjist %} runs on the object sent by the urls.py. Then we are iterating on the objeetjist for issue.id and issue.name. Now we will create issue_detail.html. This template is responsible for displaying the detail view of a case. ludlssueTracker/ludlssueTracker/templates/ ludissues/issue_detail.html {% extends ‘base.html’ %} {% block title %}Issue #{{ object. id }} - {% endblock %} {% block content %}

Issue #{{ object. id }} {{ object . status }} {% 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. The Python Book 65 V ^Python essentials Python is a programming language that lets you work more quickly and integrate your systems more effectively. Today, Python is one of the most popular programming languages in the open source space. Look around and you will find it running everywhere, from various configuration tools to XML parsing. Here is the collection of 50 gems to make your Python experience worthwhile. . . Basics T Running Python scripts On most of the UNIX systems, you can run Python scripts from the command line. $ python mypyprog.py 2. Running Python programs from Python interpreter 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, and the answer is immediate. Python interpreter can be started by issuing the command: $ 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. »> In this article, all the code starting at the »> symbol is meant to be given at the Python prompt. It is also important to remember that Python takes tabs very seriously - so if you are receiving any error that mentions tabs, correct the tab spacing. 3^ Dynamic typing 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 type internally. 66 The Python Book Python essentials ^Python statements 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 brackets to separate code blocks. 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 compare it. 6. Concatenating strings You can use “+’ to concatenate strings. »> print ‘kun’+’al’ kunal TJhe 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# or Java. 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 module that can be imported into other scripts and programs. These files must have a. py extension. 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 x,y = my_function .minmax(25, 6.3) 9. Module defined names Example: The built-in function ‘dir()’ can be used to find out which names a module defines. It returns a sorted list of strings. »> 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. Module internal 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 system records. 1 1 . Passing arguments to a Python script Python lets you access whatever you have passed to a script while calling it. The ‘command line’ content is stored in the sys.argv list, import sys print sys.argv 12. Loading modules or commands at startup 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 modules or commands . 13. Converting a string to date object You can use the function ‘DateTime’ to convert a string to a date object. Example: from DateTime import DateTime dateobj = DateTime(string) 14. Converting a list to a string for display You can convert a list to string in either of the following ways. 1st method: »> mylist = [‘spam’, ‘ham’, ‘eggs’] »> print ‘, ‘ . join(mylist) spam, ham, eggs 2nd method: »> print ‘\n’ . join(mylist) spam ham eggs 15. Tab completion in Python interpreter You can achieve auto completion inside Python interpreter by adding these lines to your .pythonrc file (or your file for Python to read on startup): import rlcompleter, readline readline . parse_and_bind( ‘ tab : complete ’ ) This will make Python complete partially typed function, method and variable names when you press the Tab key. 16. Python documentation tool You can pop up a graphical interface for searching the Python documentation usingthe command: $ pydoc -g You will need python-tk package for this to work. 17 Python documentation server You can start an HTTP server on the given port on the local machine. This will give you a nice-looking access to all Python documentation, including third-party module documentation. $ pydoc -p 18, Python development software There are plenty of tools to help with Python development. Here are a few important ones: IDLE: The Python built-in IDE, with autocompletion, function signature popup help, and file editing. IPythonj Another enhanced Python shell with tab-completion and other features. Eric3: A GUI Python IDE with autocompletion, class browser, built-in shell and debugger. WingIDE: Commercial Python IDE with free licence available to open-source developers everywhere. » ► The Python Book 67 ^Python essentials Built-in modules 19. Executing functions at the time of Python interpreter termination You can use ‘atexit’ module to execute functions at the time of Python interpreter termination. Example: def sum() : print(4+5) def message() : print(“Executing Now”) import atexit atexit. register(sum) atexit. register(message) Output: Executing Now 9 20. Converting from integer to binary, hexadecimal and octal Python provides easy-to-use functions - bin(), hex() and oct() - to convert from integer to binary, decimal and octal format respectively. Example: »> bin(24) ‘0bll000’ »> hex(24) ‘0x18’ »> oct (24) ‘030’ 21. Converting any charset to UTF-8 You can use the following function to convert any charset to UTF-8. data . decode(“input_charset_here”) . encode( ‘utf-8’ ) 22. Removing duplicates from lists If you want to remove duplicates from a list, just put every element into a diet 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), 1, []) return d.keysQ 23. Do-while loops Since Python has no do-while or do-until loop constructs (yet), you can use the following method to achieve similar results: while True: do_something() if condition() : break 24. Detecting system platform To execute platform-specific functions, it is very useful to detect the platform on which the Python interpreter is running. You can use ‘sys. platform’ to find out the current platform. Example: OnUbuntu Linux »> import sys »> sys. platform ‘1100x2’ On Mac OS X Snow Leopard »> import sys »> sys. platform ‘darwin’ 25. Disabling and enabling garbage collection Sometimes you may want to enable or disable the garbage collector at runtime. You can use the ‘go’ module to enable or disable the garbage c ollection. Example: »> import gc »> gc. enable »> gc. disable 26. Using C-based modules for better performance 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, cStringlO instead of StringlO . 27. Calculating maximum, minimum and sum out of any list or iterable You can use the following built-in functions, max: Returns the largest element in the list, min: Returns the smallest element in the list. 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 (defaults to 0). 28. Representing fractional numbers Fraction instance can be created using the following constructor: Fraction ([numerator [, denominator]]) 29. Performing math operations 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’. For example: math.acos(x) : Return arc cosine of x. math.cos(x): Returns cosine of x. math. factorial(x) : Returns x factorial . 30 . Working with arrays The ‘array’ module provides an efficient way to use arrays in your programs. The ‘array’ module defines the following type: array(typecode [, initializer]) Once you have created an array object, say myarray, you can apply a bunch of methods to it. Flere are a few important ones: 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 . Sorting items 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 the right of any existing entries. 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 the left of any existing entries. 68 The Python Book Python essentials^! 32. Using regular expression-based search The ‘re’ module makes it very easy to use regxp- based searches. You can use the function ‘re.searchO’ with a regexp-based expression. Check out the example below. 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. Working with bzip2 (.bz2) compression format You can use the module ‘bz2’ to read and write data usingthe bzip2 compression algorithm. 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: ‘BZh91AY&SY\xc4\ x0fG\x98\x00\x00\x02\xl5\x80@\x00\ x00\x084%\x8a \x00”\x00\x0c\x84\r\ x03C\xa2\xb0\xd6s\xa5\xb3\xl9\x00\ xf8\xbb\x92)\xc2\x84\x86 z<\xc0’ decompressed message: ‘Kunal is a bad boy’ 34. Using SQLite database with Python SQLite is fast becoming a very popular embedded database because of its zero configuration needed, and superior levels of performance. You can use the module ‘sqlite3’ in order to work with SQLite databases. 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) ’ ’ ’ ) 35. Working with zip files 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 to a file (a string) or a file-like object. zipfile. close()H Close the archive file. You must call ‘closeO’ before exiting your program or essential records will not be written. 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’ is the password used for encrypted files. 36. Using UNIX-style wildcards to search for filenames 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 ranges expressed with [] will be matched. Example: »> import glob »> glob.glob( ‘ . / [0-9] .*’ ) [‘./l.gif’, ‘ . /2 . txt ’ ] »> glob. glob(‘*. gif ’) [‘l.gif’, ‘card.gif’] »> glob. glob(‘?. gif ’) ['l.gif'] 37. Performing basic file operations (copy, delete and rename) 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) Copies the file src to the file or directory dst. shutil . copymode (src, dst) Copies the file permissions from src to dst. shutil. move (src, dst) Moves a file or directory to dst. shutil . copytree(src, dst, symlinks [, ignore]]) Recursively copy an entire directory at src. shutil . rmtree(path [, ignore_errors [, onerror]]) Deletes an entire directory. 38. Executing UNIX commands from Python You can use module commands to execute UNIX commands. This is not available in Python 3 - instead you need to use the module ‘subprocess’. Example: »> import commands »> commands. getoutput(‘ Is’) ‘ bz2-example . py\ntest . py ’ 39. Reading environment variables You can use the module ‘os’ to gather operating- system-specific information: Example: »> import os »> os. path os. name » »> os.linesep HHr The Python Book 69 ^Python essentials 40. Sending email You can use the module ‘smtplib’ to send email using an SMTP (Simple Mail Transfer Protocol) client interface. smtplib. SMTP([host [, port]]) Example (send an email using Google Mail SMTP server): 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 FTP server ‘ftplib’ is a fully fledged client FTP module for Python. To establish an FTP connection, you can usethefollowingfunction: 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 Older versions of Red Flat Linux have been moved to the following location: ftp://archive.download. redhat.com/pub/redhat/linux/ 42. Launching a webpage with the default web browser The ‘webbrowser’ module provides a convenient way to launch webpages using the default web browser. Example (launch google.co.uk with system’s default web browser): »> import webbrowser »> webbrowser . open ( ‘ http : //google . co.uk’) True 43. Creating secure hashes The ‘hashlib’ module supports a plethora of secure hash algorithms including SFHA1 , SFIA224, SFI A256, SF IA384, SFHA51 2 and MD5. Example (create hex digest of the given text): »> import hashlib # shal Digest »> hashlib. shal(“MI6 Classified Information 007”) . hexdigest() ‘ e224bl543f 229cc0cb935aleb9593 18balb20c85’ # sha224 Digest »> hashlib. sha224(“MI6 Classified Information 007”) . hexdigest() ‘3d01e2f741000b0224084482f905e9b7b97 7a59b480990ea8355e2c0 ’ # sha256 Digest »> hashlib. sha256(“MI6 Classified Information 007”) . hexdigest() ‘2fdde5733f5d47b672fcb39725991c89 b2550707cbf4c6403e fdb33blcl9825e ’ # sha384 Digest »> hashlib. sha384(“MI6 Classified Information 007”) . hexdigest() ‘5c4914160f03dfbdl9el4d3ecle74bd8b99 dcl92edcl38aaf7682800982488daaf540be 9e0e50fc3d3a65c8b6353572d ’ # sha512 Digest »> hashlib. sha512(“MI6 Classified Information 007”) . hexdigest() ‘a704ac3dbef6e8234578482a31d5ad29d25 2c822dlf4973f49b850222edcc0a29bb89077 8aea807a0a48ee4ff8bbl8566140667fbaf7 3aldclff 192febc713d2 ’ # MD5 Digest »> hashlib. md5(“MI6 Classified Information 007”) . hexdigest() ‘ 8e2f Ic52acl46f Ia999a670c826f 7126 ’ 44. Seeding random 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 generator when the module is first imported. 45. Working with CSV (comma-separated values) files CSV files are very popular for data exchange over the web. Using the module ‘csv’, you can read and write CSV files. 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, pet in stocks: status = status. labels[cmp(float(change) , 0.0)] print ‘%s is %s (%s%%) ’ % (name, status, pet) 46. Installing third- party modules using 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 ‘easyjnstall’ to perform Python package management tasks. 70 The Python Book Python essentials^! Example (installing simplejson using setuptools): kunal@ubuntu:~$ sudo easy_install simplejson Searching for simplejson Reading http : // pypi . python . org/simple/ simplejson/ Reading http://undefined.org/ python/#simple j son Best match: simplejson 2.0.9 Downloading http: //pypi .python, org/packages/source/s/simplejson/ simple j son-2 . 0 . 9 . tar . gz#md5=af 5e67a39c a3408563411d357e6d5e47 Processing simple json-2 .0.9. tar . gz Running simple json-2 . 0 . 9/setup . py -q bdist_egg — dist-dir /tmp/easy_ install-FiyfNL/simple json-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/simple json-2 . 0 . 9-py2 . 6- linux-i686.egg Processing dependencies for simplejson Finished processing dependencies for simplejson 47. Logging to system log You can use the module ‘syslog’ to write to system log. ‘syslog’ acts as an interface to UNIX syslog library routines. 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! Third-party modules 48. Generating PDF documents ‘ReportLab’ is a very popular module for PDF generation from Python. Perform the following steps to install ReportLab $ 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 For a successful installation, you should see a similar message: It It It It It It It It It It It It I II l/vl \ I _L I 1 1 \JII II It II It It 1 1 II II It II iTTT' T TT i' I I i f TTT T fl //'7T7T If 1 1 I TlT 1 1 l lj lTT I I ItlTT r I I II Ti l l II I tnlt II II II II II II 11 II II II II II II 11 II 11 II II II II II II 11 II II II II II II II II 11 II II II #Attempting install of _rl_accel, sgmlop & pyHnj #extensions from ‘/home/kunal/python/ ReportLab_2_3/src/rl_addons/rl_accel’ h mm m i/T/Tr^ it it T in itit. i f f t it if ii ii ii ii ii ii ii ii ii ii ii ii ii ii ii ii ii ii it ii ii ii ii ii ii ii ii ii ii ii ii ii ii ii ii #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(l, 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. Using Twitter API You can connect to Twitter using the ‘Python- Twitter’ module. Perform the following steps to install Python-Twitter: $ wget http: //python-twitter, googlecode . com/f iles/python-twitter- 0.6. tar.gz $ tar xvfz python-twitter* $ cd python-twitter* $ sudo python setup. py install Example (fetching followers list): »> 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’iFI8sn0w’ , u’ IndianVideoGamer.com’ , u’FakeAaron Hillegass’, u ’ ChaosCode ’ , u’nileshp’, u’Frank Jennings’,..’] 50. Doing Yahoo! news search You can use the Yahoo! search SDK to access Yahoo! search APIs from Python. Perform the following steps to install it: $wget http : //developer . yahoo . com/ download/f iles/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 Flandover to Be Planned at London Conference, Brown Says’, from Bloomberg M The Python Book 71 2013-04-21 1427:55.333252 client 2 has joined: 2013-04*21 1427:59.363522 ciient2 says: Hi 2013-04-21 14:28:09.799543 client 1 says Hi 2013*04-21 14:28:19 703654 dientl has quit, 013-04-21 14:28:26727603 Server has quit. 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 "With Python, you can tweak and realise your ideal system set-up" 72 The Python Book JA kspnOuttcruLI Afdumo i:1.D.5idrijZ 4 I Sm«c>i N«to ud QDD 82 88 Jtvil&i anpwt. »iilH totNi Mtul toi.Ni fc*S r- mini iwl pin 1 1 ; s ft# wslt t* • rmm t'f< 1*4 c*i* f!« 'twll te Mt iKf }«lH«£4ftk Hluhn* (Wf} it to 0*1*11 toluilli t* ih* wml *iarilu r 1h* ciPtiLil * IftrhfUrt tl to'l»j fl4 & pip ct it* jwwiutow* iw it in to»‘*q r>A Ii4t pin 9f ihi pcitoliwiKr ps 19 iW tod fftv*d ® !#•*** t id frg* iifitql Pin ft *■ i*totod riUH 3 r« 2®r Mf tto 0 SOU a* fto tHlt M*** 1 .! Cto J4 %» to N& Lit Selur // ItaU fto*llnfv «"1 «Wf! I tot » II flto «i // tt &• P4.*l urt*d ■ - Mt»i’iu|iiitoik 4 m- ■ « J ml tn! BfittflUHrmin * H . |“[II + ■ - ■ ■ ■ •’ Conivguf* Ikttoltotw Unmittll nuHiito* CMgt^ Addon trifrrmatian LUDfledd it Viewer Media sources Author LUO Vwtiti 0,0 J Rating LllDEni £x*m( Dei r. Dpt iuii LuD Ei ample Addon Id deKWrfiStJ ate ) MdartSenpliuDt 41i^# W-9*i la* t til p*i»*i-Mi|>lBiUfe r‘Q PIC**0f! llltl... ftOlto Wildwm w*rid#n- ¥ %tm n«vdln| iT-iit infvtm*. io«i.. . sotoe 1H* toUttolng ntrB pjckagtt mill l» inulUn; bit fanli-lyx |i4i>»|||b-M l 1 rp pg e t taiy-l , ft pylMlMlievtil p^tftOfl-^L PVtUon-f U0«I Pftlwi-^ pyLMn-HtptQftlth-Qltl python- .pypar E PfttoHh luggeiied jUChrytt? bU-ftttoi pytbfm-fli-eiUo fflrth&ft-gfftJ-dK pylTnsn-g; pyttiVJl^nf t^i n tmwf-*iCltiritdr pylhgir -triple thit roiiswing mu pacing** *iti tm mtiUwii bit ffthtt-Lfr Ubflirtpdv Hdry-1,# pyiiwa-oiiBvtiii pythati-fli pyt n-wi-fllwi*; python-p PftMjimtplntlll pjftimp-utplBtUHlU pytnofi-p i uE^fid*d f V nr.lv IfliiiUp* t ic rvnvi and * f* Htst4 to fit lf«4 wft of iroHlw^ An*n mi op*r»tlo* # 31, 3 Kb idflitloril dll* ip Did yuu Vim to £dn1 i flirt [T/nll T Celt I nit ps/jPf ip. ji .flriitriH arg/dftil*n/ rf-ot-S /■/■** Ift Get. ‘I Ml ps//f f p j 9 .Or&iin , grg/dfiftiiH/ ^my/iuLn tttM ft? hfti 6*1 ! 4. ii i ( p : / / f t f . ui« .{JfSir.g tg/det 1 iln f #9f^/tolh tuts http://f rp.MinfiioHii+Di^iUtftUnf oM-try/uin Gftsft htlpi/yflp,ul^E^Un.C!ri/4eDli!i^ Nbtfj/W 1* G4t.? Ml ps //ftp .«■ ,OirD lan „ pr^/dkOJ. rf^try/iiiln m bittf http://fTp.aE.oroiin.dri/dtoiinA «titrry/aitn G«ti4 http ;//ftp.oi,d«ftiifi.Qri/dttft l*n/ >#*aty/o«in 61 6* l :1ft htlfti //ftp. ue . dvblflii.pi'py'drblin/ *fM«iy/«ai/i The Python Book 73 ^Work with Python PYTHON FOR PROFESSIONALS Python is relied upon by web developers, engineers and academic researchers across the world. Here’s how to put your Python skills to professional use 74 The Python Book Work with Python System administration Get the most out of Python in handling all of the day-to-day upkeep that keeps your system healthy 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. They started with basic shell scripts, and then moved on to various scripting languages. For a long time, Perl had been the language of choice for developing these types of maintenance tools. Plowever, 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 have any excuse for not writingyour own scripts. 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 environment variables active right now: SYSTEM ADMINISTRATION BASH, PERL, PYTHON Left Python scripts enable you to instruct and interact with your operating system OPERATING SYSTEM v / \ CPU FILES/10 way to do this is to edit the values directly within the environs mapping. Another category of tasks you may want to automate is when working with files. For example, you can get the current working directory with code like import os os. environ You can get a list of the available environment variables with the function “os.environs.keysO”, and then access individual variables with “os.environs[keyj”. These environment variables are used when you spawn a subprocess, as well. So you will want to change values, like the PATFI 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 cwd = os.getcwd() You can then get a list of the files in this directory with 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.openO” and open it for reading, writing and/or appending. You can then read or write to it with the functions “os.readO” and “os.writeO”. Once done, you can close the file with “os.closeO”. Running subprocesses from Python 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 “popenO” and “spawnlO” 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 Is, by using: import subprocess subprocess. runflYls’, ‘-1’]) 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 “popenO” function that we mentioned earlier instead. If you want to get the output, you can use the following: cmd_output = subprocess. run([‘ls’, ‘-1’], stdout=subprocess.PIPE) The variable “cmd_output” is a Completed Process object that contains the return code and a string holding the stdout output. I J Scheduling with cron Once you have your script all written up, you may want to schedule them to run automatically without your intervention. On Unix systems, you can have cron run your script on whatever schedule is necessary. The utility “crontab -1” lists the current contents of your cron file, and “crontab -e” lets you edit the scheduled jobs that you want cron to run. The Python Book 75 ^Work with Python Web development Python has several frameworks available for all of your various web development tasks. We will look at some of the more popular ones 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 postgresql to store your application data. There are Django utilities available to automatically generate a starting point for your new project’s code: 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-compatible web servers. 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 following command: 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 need to restart the server to see your code changes. All of these steps get you to a working project. You are now ready to start developing your applications. Within the “newsite” subdirectory, you can type: 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. The simplest possible view consists of the code: from django.http import HttpResponse f 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’t exist yet, create it and then add the code: from django.conf.urls import url from . Import views urlpatterns = [ url(r ,A $’, views. index, name=‘index’), ] The last step is to get the URL registered within your project. You can do this with the code from django.conf.urls import include, url from django.contrib import admin urlpatterns = [ url(r ,A newapp/’, include(‘newapp. urls’)), Left Python interpreters work with your databases to power a Webserver Bottom The Model-View- Controller architecture is often used for Uls Virtual environments When you start developing your own applications, you may begin a descent into dependency hell. Several Python packages depend on other Python packages. This is its strength, but also its weakness. Luckily, you have virtualenv available to help tame thisjungle. You can create new virtual environments for each of your projects. Thankfully with this, you can be sure to capture all of the dependencies for your own package. 76 The Python Book Work with Python^ Using the PyCharm IDE * -■•■•.p- P* ■ KVCh#rr%teirrtihc*NlT Ldi?ifr" T t 0 ■ £»♦*■ E-* 1 tt**t*" C/rt* v v?i a**- r yNlUH a|k T+Kl tv 1 0 i * r - 4 indn ■ THE EDITOR PANE * <***! P< liffft -■■■ j.pj The main editor pane can be configured to match your own style, orthe style of one of the other main editors, ► ii iMpnn y&roi like emacs. It handles syntax highlighting, and even displays error locations THE PROJECT PANE This pane is the central location foryour project. All of your files and libraries are located here. Right-clicking in the pane brings up a drop-down menu where you can add new files or libraries, run unit tests, or even start up a debugger THE STATUS BARE Tool Window? Quick Access k«p b+iewle- acctntodl wtmVwt cfccl fTOTi tt] Ifi&fcH tort MtrtkHrt iHJfctHW Hit”* PyCharm does a lot of work behind the scenes. The status bar helps you keep track of all of these background processes ^1 11 n TPMl#4 f-BU WHi* iw* ^ h«mi *wj id** f fewhPh* M itnpVf^'V f fh# rj-mv,*** *4*1 ■ >.» UTJ urlCr^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 application with the URL http://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 this command: python manage. py migrate Terminal development environments When you are in the middle of developingyour application, you may need to have several different terminal windows open in order to have a code editor open, a monitor on the server, and potentially testing output. If you are doingthis on your own machine, this isn’t an issue. But, if you are working remotely, you should look into usingtmux. It can provide a much more robust terminal environment for you. 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 I NSTALLED_APPS section of the “settings. py” so that django knows to include it in any database activity. You initialize it with: python manage. py makemigrations newapp Once these migrations have been created, you need to apply them to the database by using the command: python manage. py migrate Any time you make changes to your model, you will need to run the makemigrations and migrate steps again. 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 puttingtoo much work into developingyour own code. Other Python frameworks While Django is one of the most popular frameworks around for doing web development, it is by no means the only one arou nd. There are several others available that may prove to be a better fit for particular problem domains. For example, if you are looking for a really self-contained framework, you could lookatweb2py. Everything you need to be able to have a complete system, from databases to web servers to a ticketi ng system, are included as part of the framework. It is so self-contained that it can even run from a USB drive If you need even less of a framework, there are several mini-frameworks that are available. For example, CherryPy is a purely Pythonic multi-threaded web serverthatyou can embed within your own application. This is actually the server included with TurboGears and web2py. A really popular microframework is a project called flask. It includes integrated unit testing support, jinja2templating and RESTful request dispatching. One of the oldest frameworks around is zope, now up to version 3. This latest version was renamed BlueBream. Zope is fairly low-level, however. You may be more interested in looking at some of the other frameworks that are built on top of what is provided by zope. For example, pyramid is a very fast, easy to use framework that focuses on the most essential functions required by most web applications. To this end, it provides templating, the serving of static content, mappingof URLs to code, amongotherfunctions. It handles this while providing tools for application security. If you are looking for some ideas, there are several open source projects that have been built using these frameworks, from blogs, to forums to ticketing systems. These projects can provide some best-practices when you go to construct your own application. The Python Book 77 ^Work with Python Computational science Python is fast becoming the go-to language for computational science 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 heavy computations to more efficient code. 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 you wanted to scale a vector by some value, something like a*b. In regular Python, this would look like for elem in b: c.append(a * elem) In numpy, this would look like: a*b 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, to take advantage of this new datatype. 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 Left The numpy package makes it simple to visualise your data Parallel Python One of the really powerful parts of Ipython (or jupyter) is that it is built with a client/ server model. This means that it is relatively easy to setup multiple machines to act as a server pool. You can then farm out multiple tasks to these other machines to get even more work done. While this doesn’t run any particular function in parallel, it does let you run longer functions in the background while you work on somethingelse. Spyder, the IDE for scientists □ ' U U ► * t * ^ ^ N , * Lr*Y> ■ Bifl At* #■ #« V+i«* -# f ■ ■ CSABEiv P^ tfcap i wammtr (Ad tjt l# flti&dfq irf* ■ IjAf 11 CafeppiL 1 78 The Python Book Work with Python ^ 00 + Above The ability to generate complex plots is essential differential equations, you can use the “integrate” section to solve them with code that looks like import scipy import scipy. integrate result = scipy. integrate. quad( x: sin(x), 0, 4.5) The need for speed Sometimes you need as much speed as your are capable of pushing on your hardware. In these cases, you always have the option of using Cython. This lets you take C code from some other project, which has probably already been optimised, and use it within your own Python program. In scientific programming, you are likely to have access to code that has been worked on for decades and is highly specialised. There is no need to redo the development effort that has gone into it. 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 package is definitely the first stop for anyone wanting to do any scientific processing. 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 on a cluster. 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 “symbolsQ” function to tell sympy Interactive science withjupyter Fora lot of scientific problems, you need to play with your data in an interactive way. The original way you would do this was to use the Ipython web notebook. This project has since been renamed Jupyter. Forthosewhohaveuseda program like Mathematica or Maple, the interface should seem very familiar. Jupyter starts a server process, by default on port 8888, and then will open a web browser where you can open a worksheet. Like most other programs of this type, the entries run in chronological order, notin the order thatthey happen on the worksheet. This can be a bit confusing at first, but it means that if you go to edit an earlier entry, all of the following entries need to be re-executed manually in order to propagate that change through the rest of the computations. Jupyter has support for pretty printing math within the produced web page. You can also mix documentation blocks and code blocks within the same page. This means that you can use it to produce very powerful educational material, where students can read about some technique, and then actually run it and see it in action. By default, Jupyter will also embed matplotlib plots within the same worksheet as a results section, so you can see a graph of some data along with the code that generated it. This is huge in the growing need for reproducible science. You can always go back and see how any analysis was done and be able to reproduce any result at all. Above Jupyter Notebook is a web application that is used for creating and sharing documents that contain live code and equations what variables are valid to be considered in your equations. You can then start doing manipulations using these registered variables. 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 heaviest lifting is done by Cython code that incorporates functions written in C. Quite a few ideas on how to manipulate your data was borrowed from how things are done in R. You now have no reason not to start using Python for your scientific work. You should be able to use it for almost any problem that comes up! The Python Book 79 Work with Python Robotics and electronics 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 work done. 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 Robotics is the most direct interface between your code and the real world around you for machine learning, where you can do statistical classification or data clustering, and use it to feed decision trees or even neural networks. 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 commands to execute. 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 Arduino In contrast to the Raspberry Pi, which runs a full OS from its SD card, the Arduino boards are microcontrollers rather than complete computers. Instead of runningan OS, the Arduino platform executes code that is interpreted by its firmware. It is mainly used to interface with hardware such as motors and servos, sensors, and devices such as LEDs, and is incredibly capable in this regard. Arduinos are widely used in robotics projects and can be a powerful complementtothe Pi. Raspberry Pi While we haven’t discussed what kind of computerto use for your robotics project, you should considerthe famous Raspberry Pi. This tiny computer should be small enough to fit into almost any robot structure that you might be building. Since it is already running Linuxand Python, you should be able to simply copy your code development work to the Pi. It also includes its own 10 bus so that you can have it read it’s own sensors. ROS - Robot Operating System 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. 80 The Python Book Work with Python^ For low-level work, check out Arduinos * AnaloglnOutSerial | Arduino 2:1 .0.S+dP&g £-4 File Edit Sketch Tools Help T AfialoQlnOut serial Analog input, analog output, serial output * Reacts an analog input pin ? naps the result to a range frou 0 to 255 and uses the result to set the pulsevidth lodulation [fwO of an output pin, Also prints the results to the serial aemitor. The circuit; * potentiometer connected: to analog pin G. Center pin of the poientioaeier goes to the analog pm side pins of the potentiometer go to +5V and ground * LEX' connected fro* digital pin 9 to ground created 29 c^c. 2we ■odifisd 9 Apr 1312 bf Toi lgo« This example code is in the public donain /,/ These constants von't change // to the pins used; const int analog inPin - AG: • Analog input pin that 1 const int analoqOutPin - 9; •/ Analog output pin that t THE MAIN EDITOR You have access to a large number of libraries, and support fora large number of versions of the Arduino boards. The code is essentially C, so Python programmers shouldn’t be too far out of their depths OUTPUT WIN DOW This pane contains output from various tasks. This might be compilingthe source code, or uploading it to the Arduino board being used in your project 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 connect to the serial port and read data off the connection. You can use: 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 some debugging time. 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 the most popular candidates for this task is the Arduino. 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 to provide the sensing capabilities that you require straight out of the box. There are also several models of Arduino, so you may be able to find a specialised model that best fits your needs. 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, like SimpleAl or PyBrain. Bypassing the GIL For robotics work, you may need to run some code truly in parallel, on multiple CPUs. Python currently has the GIL, which meansthatthereisa fundamental bottleneck built i nto the i nterpreter. One way around this is to actually run multiple Python interpreters, one for each thread of execution. The other option is to move from Cpython to either Jython or IronPython, as neither has a GIL. The Python Book 81 i^Work with Python f Vnfcw W-0P4 4 13 AM Current media selection k LUDMdrtV^tf IUO * 1 « 001 r, lUDEAlEaMfle Configure launcher Rating (only available for hosted plug-ins) Localised description string Make extensions for Opens changelog forthe plug-in XBMC 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 Resources XBMC: www.xbmc.org/download Python 2.7x Python IDE (optional) Code on FileSilo 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, Mac OS X, Android, iOS and Raspberry Pi. 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 like C/C++ (from which the core of XBMC is made). 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 the look and feel of just about every facet of the package. Depending upon which category your extension fits, you will have to create the extension directory accordingly. For example. . . Plug-ins: plugin. audio.ludaudi: An audio plug-in plugin.video.ludvidi: Avideo plug-in script.xxx.xxx: A program 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. Our plug-in will show various content such 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 user experience. Due to space limitations, we aren’t able to print the full code here. We recommend downloading the complete code from FileSilo. 82 The Python Book Work with Python^ Preparing the directory structure 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 folders and files as well. The following describes the directory structure of LUD Linux Entertainer: plugin.video.ludent - Root Plugin directory |— addon.xml l-changelog.txt |— default.py |— icon. png |- LICENSE.txt |- README resources I- lib settings.xml Creating addon.xml An addon.xml file needs to be created in the root of the extension directory. The addon.xml file contains the primary metadata from a XBMC extension. It contains overview, credits, version information and dependencies information about the extension. The root element of addon.xml is the element. It is defined as: cimport addon=”plugin .video, youtube” version=”3 . 0. 0”/> I n the above code we have added a dependency 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 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 to 2.0 then it would only work in XBMC Eden 1 1 .0 and not in the latest version. For the current version of XBMC 12.1, the following versions of core XBMC components are shipped: xbmc.python 2.1.0 xbmc. gui 4.0.0 xbmc.json 6.0.0 xbmc. metadata 2.1 .0 xbmc. addon 12.0.0 In addition to xbmc.python we are also adding 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. Setting up the provider and entry point Our extension is supposed to provide the video content for XBMC. In order to convey that, we have to set up the following element: : 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 (Windows OpenGL) and android. : This gives a brief description of the plug-in. Our example sets the language attribute as English, but you can use other languages too. : A detailed description of the plug-in. : Webpage where the plug-in is hosted. : Source code repository URL. If you are hosting your plug-in on GitHub, you can mention the repository URL here. : Discussion forum URLforyour plug-in. : Author email. You can directly type email or use a bot-friendly email address like max at domain dotcom. Setting changelog, icon, fanart and licence 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 is visible fromtheXBMCUI. An example changelog: 0 . 0.1 - Initial Release 0 . 0.2 - Fixed Video Buffering Issue icon. png: This will represent the plug-in in the XBMC Ul. It needs to be a non-transparent PNG file of size 256x256. 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) up to the maximum 1 920x1 080 (1 080p). » The Python Book 83 ^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 copyleft licences can be used. Note: For the purpose of packaging, extensions/ add-ons/themes/plug-ins are the same. Providing settings for the plug-in Settings can be provided by the file resources/settings.xml. These are great for user- configurable options. Partial: resources/settings.xml ccategory label=”30109”> *!■ froi* nu«py luport * nyArray - imyl »* nyftrray.Hi ni| 3 -IBS >» nyflrrflVi.MKU 2M lyAffllyi.MaPij |i 24.333333313333312 »> riyArrfty rriBfl IftriH Traceha^k Cnost r-ecerlT call liS-th File "■cstdirii *’ 1 ' r line in smcdule* AttribkCeError : 1 njmpy . ndsrray 1 object has no iti rlbute ’median 1 >»> nyArrBy.ncdlBpn Trace back fnost recent call lastjs File t line 1* in ^nodule* AttrlbiiteError: 1 n jepy. ndarray 1 object has no a*tt rlbute ‘iiedian* »> ayArray P Bedian(3 Trace back C nest recent call Uitb File “<5tdin>", line 1* in ™dule> AttnbiuteError; , minpy.fid«rrsy 4 object has no attribute ‘median 1 >» iiyArray .med lanieyAr rayj Traceback (most recent call Intli File , line l, in ^t«ddule> AttribiateError: 4 njnpy,ndarray 1 ebject has no attribute ’median* 03 Making simple calculations Traceback (most recent call last): File ””, line 1, in 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. About NumPy 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 usingthe arrayO function: »> oneD = array([l,2,3,4]) The aforementioned command creates a one-dimensional array. If you want to create a two-dimensional array, you can use the arrayO function as follows: »> twoD = array([ [1,2,3], [3,3,3], [- 1 ,- 0 . 5 , 4 ], [ 0 , 1 , 0 ]] ) You can also create arrays with more dimensions. Making 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.maxO 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. Using arrays with NumPy 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 following notation: »> twoD[l,2] You can also select a part of an array (a slice) using the following notation: »> twoD[:l,l:3] Finally, you can convert an array into a Python list using the tolistO function. Readingfiles Imagine that you have just extracted information from an Apache log file using AWK and you want to process the text file using NumPy. The following AWK code finds out the total number of requests per hour: $ cat access.log | cut -d[ -f2 | cut -d] -fl | 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'') Writing to files Writing variables to a file is largely similar to reading a file. If you have an array variable named aal, you can easily save its contents into a file called aal.txt by using the following command: In [17]: np.savetxt(''aal.txt'', aal) As you can easily imagine, you can read the contents of aal.txt later by using the loadtxtO function. Common functions NumPy supports many numerical and statistical functions. When you apply a function to an array, the function is automatically applied to all array elements. 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)”. Working with matrices 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 usingthe matrix (or mat) function as follows: 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. » The Python Book 89 Work with Python f$h - 90*45 ■ QQPlotting with Matplotlib ml'uxilc — lUO/var - Wjhj nnYfMil:^* g.pt-gc-t imtiU pythan-Bstplotltb Reading package Lists.,, Done Building dependency tree Redding stale info rut torn < . Pone the following e*tri packages will be instolledt bit fcnti’lyn gi rln i-gUb-2, & libgireppsitorY-l*^! Libgt4de2-0 python-ealro python -da leu til python gl python -glade 2 python-gobject python-gob )eet-a python-gtkz python-malplatUfr-dftta python-pyparsing python-tk python-ti Suggested packages; ftlt-deso python-gi-cairo pytbo#i-gH2-dac python -gob ject-z-dbg dvipng ipythen python -conf igobj python -exeele rat or p?th&n-*atplotUb-tfoc python -”4 (4 python- traits pyt han-wsgtkZ. fl tenllwe-est r»-ut its (ex live- latex-extra tlx The fallowing NEW packages will be installed: bit Tqnts-lyx girl, 2-ylib*’2„ G libgireposltory-l .0-1 libqlade2-t python-cairn python -da teu til python -gi python-glade I python -gob jeet python-gobjecT-2 pythor-glkZ python ir.at plot Lib python matplotlib- data python pyparsing pythan th python -tz 9 upgraded, 17 newly installed, O to remove and G not upgraded. Need to get It. 4 HD of archives. After this operation, 31. j MB of additional disk space will be used, Uoi ypu want tn rnot inue [r/n | ? Y Get: i n 1 1 p ; // f t p . us , debt a n . g f g/dep i an / wfoeezy/B®in bit 3beJG 4 2,4z-4-2 (1 + 654 K0l Get: 2 http://f Ip, us .deblan- org/deblan/ vheejy/aalri fonts-ly* all 2 ,0,3-3 (16? kh] Get: 3 http;//ftp,u5pdebidn,Drg/dcbian/ wfieezy/aain libqi repos it sjry-l, 4-1 aad64 1.32,1-1 Si 07 kB) Get: 4 bt tp://f tp. os .debtan . o rq/deb ian/ wheeiy/noin girl. 2-g lib-2, 0 aw36* 1.32.1-1 C171 kB] Gets 5 httpf//ftp,u9,debi*n, org/debian/ wheery/Bain UbgladeS-B aind64 ii2,6.4-l (89- & kftt Get: 6 http://f|p,oi.debijn,orq/debian/ wheery/Bain python-ctlro aadfifl 1.8,3-1+02 (84,2 kB] Get: 7 http://fip.of .debijn.gfg/de&ian/ wtievzy/naln pythan-dateutil all l,5+#fig<4,l [Sl+3 kB? Get: a rmpi//ftp>ui .dehlan.Qrg/tieblan/ whecry/Bain pythnn-gl avd64 3. 2,2-2 I lie kB] Get: 9 http:/yf(p.us.drbldn,Qrg/debian/ wheezy/Min python-gdb]ect-2 flBdfrf 2,21,6-18 (555 k fll Get: 10 hi ip ; //f cp. us. deb Ian. dr g/debian/ wbeely/Baln pytbOn-|tk2 amM* 2,24.l-3*bl (1,815 k 8J Get: 11 http47/ftp^ uifrom 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 . Interfacing operating system & 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, users and file descriptors. 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. os module environ: environment represents the OS environment variables in a string object. 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 . f ramework/Versions/2 . 7/bin : /Users/ kunaldeo/narwhal/bin : /opt/local/sbin : / usr/local/bin : /usr/bin : /bin : /usr/sbin : / sbin : /usr/local/bin : /usr/Xll/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[l ; 34m\\]\\w\\[\\e[m\\] \\ [\\e[l ; 32m\\]\\$\\[\\e[m\\] \\ [\\e[l;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 . f ramework/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/pgknm8bll8 737mb8psz8x4z80000gn/T/ ’ , ‘ LSCOLORS ’ : ‘ ExFxCxDxBxegedabagacad ’ , ‘ CLICOLOR ’ : ‘ 1 ’ , ‘ CF.USER.TEXT.ENCODING ’ : ‘0xlF5: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. The Python Book 99 ^Work with Python « listdir(path) : Similar to Is, returns a list with the content of directories and file available on the given path. command with arguments. On process completion it returns the returncode attribute. 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 Qt based. Example: »> os.listdir(‘7home/homer”) [ ‘ . gnome2 ’ , ‘ . pulse ’ , ‘ . gconf ’ , ‘ . gconfd ’ , ‘ . beagle ’ , ‘ . gnome2_ private’, ‘ .gksu. lock’ , ‘Public’, ‘ . ICEauthority ’ , ‘ . bash_history’ , ‘.compiz’, ‘.gvfs’, ‘.update- notif ier ’ , ‘ . cache ’ , ‘ Desktop ’ , ‘Videos’, ‘.profile’, ‘.config’, ‘.esd_auth’, ‘.viminfo’, ‘.sudo_ as_admin_successf ul ’ , ‘ 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 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, this is the console input buffer. STD_OUTPUT_HANDLE: The standard output device. Initially, this is the active console screen buffer. STD_ERROR_HANDLE: The standard error device. Initially, this is the active console screen buffer. 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 IPython without any problems. 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 can also install it using easyjnstall or PyPI. IPython provides a lot of interesting features which makes it a great shell replacement. . . 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 os.pardir os . popen os. path os . popen2 os.pathconf os.popen3 {hit tab} os.pathconf_names os.popen4 os.pathsep os.putenv os. pipe 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. SECTION 2: IPython: a ready-made Python system shell replacement 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 Example: In [28]: os. path? Type: module Base Class: String Form: Namespace: Interactive File: /System/Library/Frameworks/ 100 The Python Book Work with Python U I Python also comes with its own Qt-based console You can start the Qt console with: $ ipython qtconsole If you get errors related to missing modules, make sure that you have installed the dependent packages, such as PyQt, pygments, pyexpect and ZeroMQ. Python . f ramework/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. path provides the same operations in a manner specific to that platform, and is an alias to another module (eg macpath, ntpath). %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 Some of this can actually be useful on non- POSIX systems too, eg for manipulation of the pathname component of URLs. You can also use double question marks (??) to view the source code for the relevant object. Magic functions: I Python 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 magic functions. 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 To view help on any Magic Function, call ‘%somemagic?’ to read its docstring. 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 defined bythe$EDITOR environment variable. Shell command support: If you are in the mood to just run a shell command, you can do it very easily by prefixingthe command with ! . 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() A 1 warning generated. Qt console : I Python 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, and graphical calltips . 1 i v h & ■ IPython Qt console with GUI capabilities 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 gold when platinum is a step away? The Python Book 101 fcwork with Python Python for system administrators Learn how Python can help in system administration as it dares to replace the usual shell scripting. . . Resources Python-devel Python development libraries, required for compiling third-party Python module setuptools setuptools allows you to download, build, install, upgrade, and uninstall Python packages with ease 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 (not just one but many, including Bash, 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 snake connection?). Also, tools like GNU Mailman, 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 - you can build command-line, GUI and web apps using Python. This way, it has got covered almost all the possible interfaces. Here we will look into executing sysadmin- related tasks using Python. Parsing configuration files Configuration files provide a way for applications 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 source software, such as PHP and MySQL. lExcerptforphp.ini configuration file:| [PHP] engine = On zend. zel_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). You can use this module to parse and create configuration files. @code: writeconf ig.py ■ ©description: The f< allowing demonstrates! adding MySQL section to the php.ini file, ©warning: Do not use this script wil ■ th i actual php.ini file, as it’s not designed handle all aspects of a complete php.ini file. 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^B^ ^S^^^^™ ©description: Parsing and updating the confij m import ConfigParser config = ConfigParser . ConfigParser() config. read ( ‘php. ini’) # Print config values print config. get(‘MySQL’ , ’mysql. 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, BSDsand Mac OS X). 102 The Python Book Work with Python^ default_host ’ ) print config.get(‘MySQL’ , ’mysql. default_port ’ ) conf ig . remove_option ( ‘ MySQL ’ , ’ mysql . trace_mode’ ) with open( ‘php. ini ’ , ‘wb’) as configfile : conf ig. write (conf igfile) Parsing JSON data 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 JSON data. ■ fo use this section, you should have the 1. Python module: simplejson. Note: You can install Python modules using the command ‘easyjnstall Cmodule name>’. This command assumes that you have a working internet connection. | 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 the screenshot below for details. ■ Generating the Yahoo App ID 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 data without developing any XML parsing code. JSON PYTHON DATA MAPPING JSON Python object diet 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 JSON object into a Python object. import simplejson, APP_ID = ‘xxxxxxxx your APP ID urllib ’ # Change this to SEARCH_BASE = ‘ http : //search . yahooapis . com/WebSearchService/Vl/ webSearch ’ class YahooSearchError(Exception) : pass def search(query , results=20, start=l, **kwargs) : kwargs. update({ ‘appid’: APP_ID, ‘query’ : query, ‘ results’ : results, ‘ start’ : start, ‘output’: ‘json’ }) url = SEARCH_BASE + ‘?’ + url lib. 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 open a Python shell. @code: Python Shell Output. Lines startinj with *>»’ indicate input^ »> 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; Developer | Linux User http: //www. linuxuser . co.uk/ tag/ linux- user-developer / Gathering system 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, g IGAR is a very complete API and it can provide lot of [information, including the followini 1.| System memory, swap, CPU, load average, uptime, logins. | Per-process memory, CPU, credential info, state, arguments, environment, open files. | File system detection and metrics. ( Network interface detection, configuration info and metrics. |TCP and UDP connection tables. | Network route table. Installing SIGAR 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 Python bindings: $ git clone git : //github. com/ hyperic/sigar.git sigar.git $ cd sigar. git/bindings/python $ sudo python setup. py install i Python doesn’t limit your choice of interface The Python Book 103 ^Work with Python At the end yo u should see a output similar to| the following:®- ’ ' Writing /usr/local/lib/python2 . 6/ dist-packages/pysigar-0. 1 . egg-info SIGAR is a very easy-to-use library and can be used to get information on almost every aspect of a system. The next example shows you how. The following code sho ws the memory and the] file system informati on @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 “\tTotal\tllsed\tFree” 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 ‘ Filesystem\tSize\tUsed\ tAvail\tUse%\tMounted on\tType\n ’ 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() pet = usage. use_percent() * 100 if pet == 0.0: pet = ‘ - ’ print fs.dev_name() , format. size(total), format_size(used) , format.size(avail) ,\ pet, dir.name, fs.sys_type_ name(), ‘/’, fs. type_name() temwrani ==========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 Accessing Secure Shell (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 protocol for Python. param iko can be installed using the followinj 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}, [-{Channel}, and L{SFTPClient} to handle most of the aspects of SSH . You can use split(‘@’) else: hostname = raw_input( ‘Flostname: ‘) if len(hostname) == 0: print ‘*** Flostname required.’ sys.exit(l) 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. SSFIClient() client. load_system_host_keys() client . set_missing_host_key_ pol icy (paramiko . WarningPolicy) print ‘*** Connecting...’ client. connect (hostname, port, username, password) chan = client . invoke_shell() print repr (client. get_transport()) print ‘*** SSH Server Connected! kkk 1 SSHClient as: client = SSFIClient() client. load_system_host_keys() client . connect ( ‘some. host . com’ ) stdin, stdout, stderr = client. exec_ command( ‘dir ’ ) The following example demonstrates a full SSH client written using the paramiko module. import base64, getpass, os, socket, sys, socket, traceback import paramiko import interactive # setup logging paramiko . util . log_to_f ile( ‘demo_simple . log’) # get hostname username = ‘ ’ if len(sys.argv) > 1: hostname = sys.argv[l] if hostname. find( ‘@’ ) >= 0: username, hostname = hostname. 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(l) To run this code you will also need a custom Python class interactive. py which implements Note If you are confused with the tab spacing of L the code, lookforthe codefiles on FileSilo. A 104 The Python Book Work with Python the interactive shell for the SSH session. Look for this file on FileSilo and copy it into the same folder where you have created PySSFIClient.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. . . *** 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 youjy $HOME/.ssh/known_host s file, the client will throw the following errorj *** Caught exception: : 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 the ssh client. 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 : dl . 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’s versatile language. As is the case with all Python coding, the code that is presented here can easily be adopted into your GUI application (with software such as PyGTK or PyQt) or a web application (using a framework such as Django or Grok). Writing a user interface using Python Learn howto create a user-friendly interface using 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 users to select default boot entry and the timeout. 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 the Python audio library, tksnack). — This app consists of two files. . . grub.py: GRUB Config File (grub.conf) Parser (available on FileSilo). It implements two main functions, readBootDBO and writeBootFileO, which are responsible for reading and writing the GRUB configuration file. Text user interface file for manipulating the GRUB configuration file using the functions available in grub.py. (((“Ok”), “ok”), ((“Cancel”), ‘cancel”)^^ e=Entry (3, st r(entry_ yalue)) l=Label((“Timeout (in seconds) : ”)) The Python Book 105 108 Build tic-tac-toe with Kivy Program noughts and crosses 112 Create two-step authentication UseTwilio 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 PygameZero Turn your ideas into games Here’s scene two! 108 |° X 0 0 X "You'll be surprised by the diversity of what you can make with Python" 106 The Python Book The Python Book 107 ^Create with Python Build tic-tac-toe with Kivy Ease into the workings of Kivy by creating the pen-and-paper classic in just over 100 lines of Python... 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 only on the traditional desktop platforms of Linux, OS X and Windows, but also Android and iOS, plus devices like the Raspberry Pi. 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 graphics pipeline, allowing advanced GPU effects even though the basic Python API is very simple. Kivy is a set of Python/Cython modules that can easily be installed via pip, but you’ll need a few dependencies. It uses Pygame as a rendering 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, or via pip where applicable. 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, running in python2.7 and python3.3. pip install kivy If you have any problems with pip, you can use easyjnstalvia easy_install kivy. There are also packages or repositories available for several popular distros. You can find more information on Kivy’s website. A kivy application is started by instantiating and running an 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 simplest Kivy app possible: from kivy. app import App TicTacToeApp(App) : pass if name == “ main T icTacToeAppO . r un () You can already run this, your app will start up and you’ll get a plain black window. 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 Textlnput), 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 for every need imaginable, widgets are kept simple but are easy to join to invent new interfaces. We’ll see some of that in this tutorial. Since ‘Hello World!’ is basically compulsory in any programming tutorial, let’s get it over with by using a simple ‘Label’ widgetto display the text: from kivy. uix. label import Label You can develop cross-platform apps using various Python libraries We’ll display the ‘Label’ by returning it as our app’s root widget. Every app has a single root widget, the top level of its widget tree, and it will automatically be sized to fill the window. We’ll see later how to construct a full GUI by adding more widgets for this one, but for now it’s enough to set the root widget by adding a new method to the ‘App’: if build(self): return Label(text=’ Hello World!’, font_size=100, Hello World! ■ The classic ‘Hello World!’ in Kivy GUI form, using the built-in Label widget color=0, 1, 0, 1)) 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 them just once upon instantiation. 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, and you should do the same if you want to use your variables with Kivy’s event or bi ndi ng f u nctionality. That’s all you need to show some simple text, so run the program again to check that this does work. You can experiment with the parameters if it’s unclear what any of them are doing. Our own widget: 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 widget class to contain this behaviour: from kivy. uix. gridlayout import GridLayout 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 108 The Python Book Create with Python 0 0 X 0 X ■ Atic-tac-toe grid now accepting input, adding a 0 or X alternately automatically arrange any child widgets to fit nicely with as many rows as necessary. Tic-tac-toe requires three columns and nine children. 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 manipulatingthe GUI with Python code - as a loose analogy you might think of it as the HTML/CSS to Python’s JavaScript. Python gives us the dynamic power to do anything, but all that power gets in the way if we just want to declare the basic structure of our GUI. Note that you never need kv language, you can always do the same thing in Python alone, but the rest of the example may show why Kivy programmers usually like to use kv. Kivy comes with all the tools needed to use kv language; the simplest way is to write it in a file with a name based on our App class. That is, we should place the following in a file named ‘tictactoe.kv’: : cols: 3 1 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 the former, creating a rule for the ‘TicTacToeGrid’ widget by declaring that every ‘TicTacToeGrid’ instantiated should have its ‘cols’ property set to 3. We’ll use some more kv language features later, but for now let’s go back to Python to create the buttons that will be the entries in ourtic-tac-toe grid. from kivy. uix. button import Button from kivy. properties import ListProperty 5S 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 created a new Kivy property for our widget, ‘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 a more interesting feature. : font_size: self. height J Kivy comes with all the tools needed to use kv language 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 that we’ve referenced the Button’s own height and 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 referencing the property we want to bind to. 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. This is a minor feature that is automatically handled by Kivy properties. 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 wheneverthe property is modified. 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 number of columns we set earlier. Now all we have to do is replace our root widget (returned from ‘App. build’) with a ‘TicTacToeGrid’ and we can see what our app looks like. » ►- The Python Book 109 ^Create with Python « A The game with final additions, makingthe grid square and extendingthe interface if 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 now we’ll leave them as they are. Has anyone won yet? We’ll want to keep track of the state of the board to check if anyone has won, which we can do with a couple more Kivy properties: from kivy. properties import *-i (ListProperty, NumericProperty) TicTacToeGrid(GridLayout) : status = ListProperty([0, 0, 0, 0, 0, G, 0 , 0 , 01 ) current_player = NumericProperty(l) 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 wins because the sum of a row, column or diagonal will be +-3. Now we can update our graphical grid when a move is played (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 ‘0’ 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 array keeping track of existing moves. 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 to add some code to check if the game is over. 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 has filled a column, row or diagonal. 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 = [ (status[0:3]), (status [3: 6]), (status[6:9]), (status[0::3]), (status[l::3]), (status[2::3]), (status[:: ]), (status [2: -2: 2])] # Sums can only be +-3 if one player # filled the whole line if 3 in sums: print(‘0s win!’) elif -3 in sums: print(‘Xs win!’) elif 0 not in self, status: print(‘Draw!’) This covers the basic detection of a won or drawn board, but it only prints the result to stdout. At this stage we probably want to reset the board so that the players can try again, along with displaying a graphical indicator of the result (Fig. 03). Finally, we can modify the on_status" method to both reset the board and display the winner in a ‘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 user clicks or taps outside it. 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, 110 The Python Book Create with Python 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 maintainingthese proportions. This is another trick made possible by a binding with the ‘size_hint’ Kivy property, this time managed internally by Kivy. 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 run your program and enjoy hours of fun! Time to experiment 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 bindings between properties. 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 InterfaceO’ line in ‘TicTacToeG rid. 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 things like swapping out the different widget types to see how other widgets behave. i Try swapping out the different widget types to see how other widgets behave TicTacToeGrid(GridLayout): (self, *args, **kwargs): (TicTacToeGrid, self). init (*args, **kwargs) for row in rang* (3): for column in (3): grid_entry = GridEntry( coords=(row, column)) grid_entry. bind(on_release=self. button_pressed) self. add_widget(grid_entry) if button_pressed(self, instance): # Print output just to see what’s going on print(‘{} button clicked! ’.format(instance. coords)) Fig 01 if 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)} row, column = button. coords # passed as an argument Fig 02 # Convert 2D grid coordinates to ID 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, cur rent_player button. text = {1: ‘O’, -1: ‘X’}[self.current_player] button. background_color = colours[self.current_player] self. cur rent_player *= -1 # Note the *args parameter! It’s important later when we make a binding Fig 03 # to reset, which automatically passes an argument that we don’t care about reset(self, *args): self, status = [0 for _ in (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, cur rent_player = 1 The Python Book 111 ^Create with Python Create a two-step authentication with Twilio Increase security in access to your web services by building a simple two-step authentication with Twilio’s SMS APIs to help you Resources Python 2.7+ Flask 0.10.0: flask.pocoo.org/ Flask Github: github.com/mitsuhiko/flask ATwilio account: twilio.com Twilio’s Python REST API Helper Library: github.com/twilio/twilio-python/zipbaU/master MySQLDB: mysql-python.sourceforge.net Stoics -'••Vizirs. TeM^iC m j " iH 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 - and yet phones are more important now to our daily lives than ever. So, what can we do to leverage some of that versatile technology? 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 little of Flask’s detail will be covered here. SM5 AfUltytHTK Todftyl &MS MilMjM l*i m 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 worki ng 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. Add credit 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’ll need those a little later. The Twilio interface is kept nice and simple - no unnecessary complications here 112 The Python Book Create with Python Install the Twilio Helper Library and MySQLDB The Twilio helper library is a fantastic piece of code that lets you jump straight into sendi ng and handling text messages in no time at all. There are a couple of ways to install the library: you can use either PIP or EasyJnstall, like so import MySQLdb from flask import Flask, redirect, request, session, render_template from twilio. rest import TwilioRestClient 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 = client = twilio(account_sid, auth_token) @app.route( / ) f index() : return "index page" @app.route( /login 1 , methods=[ 'GET ' ]) f login() : return "login page" @app.route( /check-user', methods=[ POST']) f checkUser() : return "check user page" @app.route( /logout') f logout() : return "logout page" @app.route( /verify', methods=[ ' GET' ]) f twoStep() : return "verify page" @app.route( /check-code', methods=[ POST']) f checkCode(): return "check code page" if name == ' main ': app. secret_key = ' R4nD0MCRypt( app. run(host='0. 0.0.0 , debug=True) $ 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, the package for which you can grab like so. . . 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 add a new file ‘server.py’. Server setup Open the ‘server.py’ file for editing. The first thing we're going to do is import the libraries we need for our authentication flow, create the endpoints for our server and assign some of the variables needed to run our Flask server. (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 the index page of your server at 1 27.0.0.1 :5000/. Server logic 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 7’ 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( / ) f index(): checkSessionState() return render_template( 1 ’) f checkSessionStateQ : » > The Python Book 113 « try: session[ /erified’] == True except KeyError: session[ /erified’] = try: session[ Loggedin’] == True except KeyError: session[ Loggedin , ] = try: session[ jser ] == True except KeyError: session[ jser ] = Logging in The first step in two-step authentication is logging in with a traditional username/email and password. Access your database and create a new user with the following query: INSERT INTO users (username, password, phonenumber) VALUES (‘A USERNAME', ‘A *-i PASSWORD', 1 +44Y0URUSERSPH0NENUMBER ) For the purposes of this tutorial, the password is plain text - but we implore you, when you’re implementing passwords in a live environment, make sure that you hash them. Still, for now we’re goingto use a plain text password. Our login.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 module to get details from our database. In order to query our database we need to create a cursor from which to execute our MySQL statements. We do this with cur = db.cursor(): @app.route( /check-user’, methods=[ POST']) f checkUser() : #session.clear() if request .method == POST': #print request . form[ 'username ' ] cur = db.cursor() cur.execute( '"SELECT * FROM users WHERE username = %s"" , (request . form[ ],)) result = cur . fetchone() returnedPassword = results ] returnedPhoneNumber = result[ ] We can then build an SQL statement using cur. execute(). Notice the %s; this will be replaced with the value passed through in the next variable. We execute the statement with cur.fetchoneO, 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’ll next checkthatthe password assigned to that user is the same as the one submitted. If so, we’ll generate the validation code to send to the user, which we’ll store in the verification table of ourdatabase until it’s used or expires. We’ll need to create a new cursor to insert the verification code into our table. After we’ve executed the statement we need to commit the changes to the database, we do this with db. commit() - we’ll then add the results of the query to our session so we can check against them later. (Fig 03) Send the verification code 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: f sendVerificationCode(code, number) : text = client. messages. create( body=‘Your verification code is: " — + code, to=number , f rom_=“+Y0URTWILI0NUMBER" ) 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 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(V verify’) at the end of /check-user. Check verification code 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 goingto 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.timeO > expirationTime : expirySQL = db cursor () expirySQL . execute( w verification WHERE code=%s"” , (codeToCheck, )) 114 The Python Book Create with Python expirySQL. close() session[ ] == 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( M verification WHERE code=%s'"’ , — i (codeToCheck, )) delSql . close() db commit() session[ 'verified ’ ] = True return redirect('/ ) else : return redirect('/ e ' ) 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 redirected to their user page, and so on. Conclusion Vv 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( ' / ) if index(): return render_template( ' index. html ) @app. route ( login', methods=[ ]) if login() : return render_template( ' login . html ) @app. route( ' /check-user ' , methods=[ ' POST ]) if checkllser() : return "check user page" @app . route( ' /logout ' ) if logout() : return "logout page" @app. route( '/verify ' , methods=[ 'GET ' ]) f twoStep() : return render_template( ) @app. route( ' /check-code ' , methods=[ ' POST ]) if checkCode(): return "check code page" verficationCode = generateVerificationCode(size=6) ins = db.cursor() expiration = (time.time() + expirationLength) sql = "INSERT INTO ver % (verficationCode, expiration, request. form[ jsername ]) ins.execute(sql) ins.close() db. commit() sessionE ' user ] = request. form[ 'username ] sessionE ' loggedin ] = True @app. route( '/check-code' , methods=E ' POST ]) if checkCode(): if request .method == 'POST': codeToCheck = request. formE 'code ] if not 'user' in session: return redirect( /login') else: cur = db.cursor() cur.execute( "SELECT * FROM verification WHERE code = %s’ M , (codeToCheck,)) result = cur . fetchone() cur.close() if result != None: returnedllser = resultE3] expirationTime = (resultE2]) if returnedllser != sessionE ' user ]: return redirect( 1 /verify?error=true ’ ) The Python Book 115 Create with Python Part one: Program a Space Invaders clone Write your own RasPi shooter in 300 lines of Python Resources Raspbian: www.raspberrypi.org/ downloads Python I www.python.org/doc Pygame I www.pygame.org/docs 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 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... Setting up dependencies w I 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 own program. It’s great practice and gives you a chance to tweak elements of the game to suit you, be it a different ship image, changing the difficulty or the ways the alien waves behave. If you 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’t already) can be installed from the terminal by typing: sudo apt-get install python-pygame Downloading the project For Pivaders we’ve used Git, a brilliant form of version control used to safely store the game files and retain historical versions of your code. Git should already be installed on your Pi; if not, you can acquire it by typing: 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), enter the directory (cd pivaders) and type: git pull https://github.com/russb78/pivaders.git 116 The Python Book Create with Python 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) Clean clode Havingall the most regularly used global variables clearly labelled here makes our code later on easier to read. Also, if we want to change the size of something, we only need to do it here and it will Player(pygame. sprite. Sprite): work everywhere. (self): ■■■■■■■■ pygame. sprite. Sprite. (self) self.size = (60, 55) self.rect = self.image.get_rect() self.rect.x = (RESC0] / 2) (self.size[0] / 2) self.rect.y = 520 self.travel = 7 self, speed = 350 self.time = pygame. time. get_ticks() 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] Alien (pygame. sprite. Sprite): (self): pygame. sprite. Sprite. (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() 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[l]: self.rect.y += self.vector[l] * self.travel[l] self.vector[0] *= -1 self.has_moved = [0, 0] self.speed -= 20 if self.speed <= 100: self.speed = 100 self.time = GameState. alien_time >s Ammo(pygame. sprite. Sprite): (self, color, (width, height)): pygame. sprite. Sprite. (self) self.image = pygame. Surface([width, height]) self, image. fill(color) self.rect = self.image. get_rect() self.speed = 0 self.vector = 0 update(self): self.rect.y += self.vector * self.speed if self.rect.y < 0 or self.rect.y > RES[1]: self.kill() Block(pygame. sprite. Sprite): (self, color, (width, height)): pygame. sprite. Sprite. (self) self.image = pygame. Surface([width, height]) self, image. fill(color) self.rect = self.image. get_rect() Rain bullets The Ammo class is short and sweet. We only need a few initialising attributes and the update method checks if it’s still on the screen. If not, it’s destroyed. GameState: pass Game( ): (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[(], RES[1]]) self.time = pygame. time. get_ticks() self, ref resh_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() collisions and drawn self.intro_screen = pygame. image. load( with ease, ‘data/sta rt_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/Spaceshipl6.png’).convert() Alien. image. set_colorkey (WHITE) GameState. end_game = False GameState. start_screen = True GameState. vector = 0 GameState. shoot_bullet = False Groups This long list of groups we’re creating are essentially sets. Each time we create one of these items, it’s added to the set so it can be tested for if 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 Control Taking care of keyboard input is the control method. It checks for key events and acts accordingly dependingwhether we’re on the start screen or playing the game. if splash_screen(self): while GameState. start_screen: self.kill_all() self.screen. blit(self.intro_screen, [0, 0]) self, screen, blit (self, int ro_font. render ( “PIVADERS”, 1, WHITE), (265, 120)) self, screen. blit(self. game_font. render ( “PRESS SPACE TO PLAY”, 1, WHITE), (274, 191)) The Python Book 117 Testing Pi vaders 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 and right. Creating your own done 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.pyfile of your own. Global variables & tuples 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 that never change - they represent things 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 you can’t reassign individual items within them. Perfect for constants, which aren’t designed to change anyway. Classes - part 1 wU 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. Classes - part 2 w # 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 updateO method apart from the Block class (a method is a function within a class). The updateO 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). OQ Ammo wO What’s most interesting about classes, though, is that you can use one class to create lots of different things. - m PlUflDERS PRESS SPACE TO PL BY We used widely available open source Jf, 1 art and fonts to make the game IH r‘ X 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 objects we create. The game \ J w 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 need to return to the start screen, among other things. The main loop I \ J There are a lot of methods (class functions) in the Game class, and each is designed to control a particular aspect of either setting up the game or the gameplay itself. The actual logic that dictates what happens within any one round of the game is actually contained inthemain_loop() method right at the bottom 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 second when you’re playingthe game. Main loop key logic - part 1 I 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.quitO, 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 controlO method, which checks to see if the location of the player needs to change. Next we attempt to make an enemy missile and we use the random module to limit the number of missiles that can be created. Next we call the updateO method for each and every actor on the screen using a simple for loop. This makes sure everyone’s up to date and moved before we check collisions incalc_collisions(). Main loop key logic - part 2 I ^ 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 of the loop is of no consequence. 118 The Python Book Create with Python l A class is essentially a blueprint Dead or alive Probably two of the most important questions are answered here - is the player dead or did you win the round? pygame. display. flip() self.control() ? make_player(self): self, player = Player() self. player_group. add (self, player) self. all_sprite_list.add(self. player) r 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) refresh_scores(self): self, screen, blit (self. game_font. render ( “SCORE “ + (self. score), 1, WHITE), (10, 8)) self, screen. blit(self.game_font.render( “LIVES “ + ;tr (self.lives + 1), 1, RED), (355, 575)) if alien_wave(self, speed): for column in (BARRIER.COLUMN): for row in (BARRIER.ROW): alien = Alien() alien. rect.y = 65 + (column * ( ALIEN_SIZE[1] + ALIEN_SPACER)) alien. rect.x = ALIEN.SPACER + ( row * (ALIEN_SIZE[ ] + ALIEN_SPACER)) self.alien_group.add(alien) self.all_sprite_list.add(alien) alien. speed -= speed Refreshing the screen you need to carefully considerthe way in which you update the screen. Blitting the background between actor movements is vital for clean animation. if make_bullet(self): if GameState.game_time - self. player. time > 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 if make_missile(self): if (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) self, player, speed: Guns ’n’ ammo Bullets and missiles use the same parent class. We change a few key attributes originally initialised to create the behaviour we need; eg the vector for each is opposite. ‘ f make_barrier(self, columns, rows, spacer): for column in range(columns): for row in (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) i make_defenses(self): for spacing, spacing in ( (4)): self.make_barrier(3, 9, spacing) if 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() is_dead(self): if self.lives < 0: self, screen, bl it (self. game_font. render ( “The war is lost! You scored: “ + str ( self. score), 1, RED), (250, 15)) self.rounds_won = 0 self, ref resh_screen() pygame. time. delay (3000) return True f win_round(self): if len(self.alien_group) < 1: self.rounds_won += 1 self, screen, blit (self. game_font. render ( “You won round “ + (self.rounds_won) + “ but the battle rages on”, 1, RED), (200, 15)) self. refresh_screen() pygame. time. delay (3000) return True if 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, ref resh_screen() pygame. time. delay(3000) return True if 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 next_round(self): for actor in [self.missile_group, self. bar rier_group, self.bullet_group]: for i in actor: i.kill() self.alien_wave(self.level_up) self.make_defenses() self.level_up += 50 Main loop This is the business end of our application. This loop executes 20 times a second. It needs to be logical and easy for another coderto understand. 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. ref resh_screen() self.splash_screen() pygame. quit() if name == pv = Game() pv.main_loop() Start the game The very last thing we do is create a Game object and call the main loop. Besides our constants, this is the only code that sits outside a class. The Python Book 119 Create with Python Part two: Add animation and sound to Pivaders After writing a Space Invaders clone in just 300 lines of Python, now we expand it to include animation and sound Resources Raspbian: www.raspberrypi.org /downloads Python: www.python.org/doc Pygame: www.pygame.org/docs Art assets: opengameart.org 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, 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 vO.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 forthe Raspberry Pi. Setting up dependencies w I 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 120 The Python Book Create with Python Pivaders.py listing from line 86 (continued on next page) RES[1]]) ship_sheet We set the player image to be equal to one small segment of the sprite sheet by usingthe‘ani_pos’ variable. Change the variable to change the picture Game( ): (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[] self, time = pygame.time.get_ticks() self, ref resh_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 . j pg ’ ) . convert () pygame. display.set_caption(‘Pivaders - ESC to exit’) pygame. mouse, set _visible(False) Alien, image = pygame. image, load ( ‘data/graphics/Spaceshipl6.png’).convert() A1 ien . image . set_colorkey (WHITE) self.ani_pos = 5 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_newl.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 = py game, mixer. Sou nd( ‘data/sound/timgormly__8-bit-explosion.ogg’) self. explosion_f x . set_volume (0 . 5) self. explodey_al ien = [] GameState.end_game = False GameState.start_screen = True GameState. vector = 0 GameState.shoot_bullet = False 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 Set flags We’ve added ‘animateJeft’ and ‘animate_right’ Boolean flags to the control method. When they’re true, the actual animation code is called via a separate method 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 _f x . play () fx.playO Having already loaded the sound effect we want when we shoot, we now just need to call it when we press the space bar : 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 player_explos ion (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 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, * (self. explodey_al ien [1]) - 60]) [ (self. explodey_alien[0]) - 50 else: self.alien_explode = False self.alien_explode_pos = 0 self. explodey_al ien = [] splash_screen(self ) : while GameState. start_screen: self.kill_all() self. screen. blit(self.intro_screen, [0, 0]) self, screen . blit(self. int ro_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. ref resh_rate / 2) make_player(self ) : self.player = PlayerQ The Python Book 121 Create with Python Downloading pivaders 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. To clone the version of the project we created for this tutorial, go to your home folder from the command line (cd ~) and type: git pull https://github.com/ russb78/pivaders . git This will create a folder called pivaders - go inside (cd pivaders) and take a look around. Navigating the project 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) and type: 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 press it again to exit the game completely. Animation & sound 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 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 the animations as they happen. Finding images to animate Before we can program anything, 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 at opengameart.org, as used here. Tweaking assets 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 the central ship sprite and centred it into a new window. We set the size and width of the frame and then copy-pasted the other 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. Loadingthe sprite sheet Since we’re inheriting from the Sprite class to create our Player class, 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.loadO. Since we made our sheet with a transparent background, we can append .convert_alpha() to the 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. anLpos, which has an initial value of 5. Changing this value will alter the ship image drawn to the screen: ‘0’ would draw it leaning fully left, ‘1 1 ’ fully to the right. Animation flags w Down the list in the initialising code for the Game class, 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 continue to look as if it’s flying left when it has stopped). OG The animation method w w 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 anLpos variable and set the player’s image accordingly. The Else statement then animates the ship sprite back to its resting state and the same logic is then applied in the opposite direction. Animating explosions I w The player_explosion() and alien_explosion() methods that come after the player animation block in the Game class are 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 explosionjmage are set to a different part of subsurface than before. Adding music to your project I Pygame makes it easy to add a musical score to a project. Just obtain a suitable piece of music in your preferred format (we found ours via freemusicarchive.org) and load it using the Mixer Pygame class. As it’s already been initialised via pygame.initO, we can go ahead and load the music with this code: pygame . mixer, music . load ( ' ‘data/sound/10_Arpanauts . ogg ’ ) pygame. mixer, music, play (-1) pygame. mixer, music. set_volume(0.7) The music.play(-l) requests that the music should start with the app and continue to loop until it quits. If we replaced -1 with 5, the music would loop five times before ending. Learn more about the Mixerclassviawww.pygame.org/docs/ref/mixer.html. Using sound effects I 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. For the laser beam, the initialisation looks like this: 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, straight after we raise the shoot_bullet flag. If you’re struggling to find free and open sound effects, we recommend www.freesound.org. 122 The Python Book Create with Python self. player_group. add(self. player) self. all_spr ite_list . add (self, player) ?f ref resh_screen (self): self. all_spr ite_list . d raw(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. ref resh_rate) ref resh_sco res (self ) : self, screen . blit (self . game_font . render ( “SCORE “ + (self. score), 1, WHITE), (10, 8)) self, screen . blit (self . game_font . render ( “LIVES “ + (self. lives + 1), 1, RED), (355, 575)) alien_wave(self, speed): for column in (BARRIER_COLUMN) : for row in (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 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 make_missile(self): if (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_spr ite_list . add (missile) make_barrier(self, columns, rows, spacer): for column in a nge (columns): for row in rang* (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_spr ite_list . add (barrier) 5f make_defenses(self): for spacing, spacing in ( (4)): self.make_barrier(3, 9, spacing) 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() 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 5f 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 . t ime . delay (3000) return True ef 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 . t ime . delay (3000) return True 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 Bf calc_collisions(self): pygame. sprite. groupcollide( self.missile_group, self.barrier_group, True, True) pygame. sprite. groupcollide( self, bullet .group, self, bar rier_group, True, True) for z in pygame. sprite. groupcollide( self, bullet .group, self.alien_group, True, True): self.alien.explode = True self, explodey.al ien .append (z . rec t . x) self, explodey.al ien .append (z . rec t .y) self, score += 10 self, explosion.f x . play () if pygame. sprite. groupcollide( self.player_group, self.missile_group, False, True): self. lives -= 1 self, explode = True self, explosion.f x . play () 5f 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_g roup, 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() The Python Book 123 Create with Python Make a visual novel game with Python Bridge the gap between books and videogames by creating an interactive novel or choose-your-own-adventure with Python and Pygame ■ Change scenes to add more depth to the story, and allow the game to have decisions and routes Resources Python 2: www.python.org/ Pygame: pygame.org/download.shtml IDLE Python IDE Game assets Code from FileSilo (optional) 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 clickthrough a story and make decisions as they go along in order to experience different plot points and endings. 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 coding process. We’ll be coding this in standard Python 2, so make sure to run it in IDLE 2 and not IDLE 3 while you are writing, testing and coding. 124 The Python Book Create with Python M* i ‘In *Mw lpw.*H **m rubj^jtiLinl^j. % iuili> 4pt~4*t "crtyrUl pjrlhqn -i*** python -fwjnpj LVfr*4L- l«P9fl + |-tff'w Utrfl-* it Hindi 1.? difw “)*(*%> Ut-m-rffllr dr-v llhiYfV^it dr* lllwtvswiSFt tfr* pj-.'-Hcrd far rebli fie*dlA0 t>fl£b*g* lUtl-L. DfliW iutldlne d*ptnd«nc^ t*« fif+iJl*ia ICIta inf er»*ttWi k , frgnt pflhnn dr* tv aI« rtrfip the iww^l w > 1 . 0 * 1 . python aunpv ti qtrradv (he n*wrVt MMlM. tVth-an-runpy i4L fa rivnuiU-f In it i ll*d. Ubtv^tMlt Hi ilrtidy 1 hr nMit vtnVHi. Ubtv-twlt set t4 ninviUy tnftiUfd^ Thr fsillowlnq p*ic. kftflrV werr mi tonal It ally itralvllrd vnd nr ns lomqFr rrttulTrd;: llmin h^fljers i.ts.fli 4* It mm headers l-ll.l WM rt t 1 tflvx - H-rvdtr & J . 1 2 . 5 r«i- Hiuix - find*' 1 f J , 1 J 1 « - #1 ■ ■gvr-r r U Unux Im^#* i, iJ,e-4o-fl*n*rtc J.iJ.a- 4i-f tfi+r 1c I tfiux r iMg#- • mutt 3k • 1 . 1 1 . tfl -ID- gvrvvr 1 c ttnus* lru 9 v-fxtra-l.il .1- 4 5 dvu I L b Jbk q ■ rSw Llbjpvi] dvv lib |pc*Q turbot ikm ILbJpeqA drv ISblirvo duv ILbrudt dvv llbnLhnud? 6rv Ithn firv UbpufflZ tthpytw Ubslin;^ dew Irnlffi hv U.Jbttffe"S Ubvprbti’dfiy Llbtavbpdev Ub**tb*demfM nertyrtll’CflnnqiS filtkigti . LlbJMiLwid 7 -tf£i£ I 1 Hide lib 1 7 7 • tlvv i kbgutl - dvu L Lbr,*Bl 114 -dv-u I I fa ■>[ hr oed L nqri dpv 1 LbspL'L'H drv ILblhcar.i dev Itbliiiu dm. qc 1 win mvcv WtffJ *(dtff3-qt ifonpB-Fe neld tttYi pyth^-B^sqldb Mfh^n-p/gwnii (he fgUnM-nig N4.H p|tk*g«* wtU t*e kn? t j t Led Ufaetbundz-dcv Utivr*M-tll*nt-ii*¥ UiWkvihl-tGrtrtofl-iJiv U&bvtedtoL-ttonr I Hij'.' l m n*1 -dvw 1 Ibdvul I L - rl*v L Ibc jr j- d w 1 k bdbuv • I -dw llbr L.*c -dru llbjblq dev Ikbjpcq dmi ILbjprq lurboi dr u IkbJpcqB drv llblnu dev UbMdO dev l‘bntt"dS2 >dev Ubo^g-dev Itbunflii-dev llbporentdt -dev UbwUt-d*v Ubsdl‘t*igfl,2-de* ttb5dt-m,Hf ri . 2 -de^ Ubsdl-tt?i-P-4«w libsdll , 2 -dcv lUrtUrafZ-dFv ILbSftpeg-dev UfaVMS-tdle-dev UbtVfr5-dew llbtLfluxl llbvqrfalft-dpv I I hwb|> ■ dw 1 1 bvvtif»d#MX 1 avt cifi LjI nc-rcurldl 1 on non t to upgrade. )4 to ne«ly tnitill, t to renove *rvd *•'? not to upgrade. 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-imagel.2-dev libsdl-mixerl.2- dev libsdl-ttf2.0-dev libsmpeg- dev libsdll.2-dev libportmidi-dev libswscale-dev libavformat-dev libavcodec-dev Get the Pygame code Wb Next we need to download the code for Pygame direct from the source. Still in the terminal, you can do this by typing in: $ hg clone https://bitbucket.org/pygame/ pygame Build the Pygame module To install it, we need to do it in two steps. First we need to prepare the code to install usingtheterminal with: $ python setup. py build Once that’s finished you can then actually install itwith: Install in other ways 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 work in any OS. Which will download it to the folder ‘pygame’. $ sudo python setup. py install Move to that using CD pygame in the terminal so we can continue building it. This won’t take too long. vU Ila rif t i #43V*i c ftMfti wtl * 44\*4 p*mr#ai fllV ClUifM tMrt Mil (Ivqruln iitk 1-MJ* iMPgn 1» iM lltn (-11 hfrfT 1 V KU E tap t* bfPfk if+fwU tn filvi tfAit*!. * MUi wH. • nits rwHri. I rUn H/fuIwi * VtjfrrIi . ' H ti 1 ti mt«i# n &vll4 m “iMmtr flit W-rr m nlnq Wi %*>: 1: Trmlyviv caftrif; Mt r«ari ■ I fnnijfpi ta*l Vfti | : '^Vtu .1 : r«M*d rMlMi : h 4 (hh! Get the visual novel files ww 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. Understand the script file 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. This includes the location of each line, the character, the actual line itself and information on how the game flows. These are matrices with the information in, and are completely customisable. ef Kilt ppQim d*i*fidvM ue-rv fn>t ranai ntvll. but na p p i Svnvv! *■ "Is *1*1 1 M»il 4 Ufcy !♦ The Python Book 125 ^Create with Python « How the script relates 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. \i r.f rt pygame , time, script pygame . init ( ) Starting the main game 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() Add variables and assets We add a mixture of information we need to run the novel. We define the size of the display screen to use (1 000 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. Start the game 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 blitto add images or in this case text. Once you’ve created a buffer of changes to the screen, you update it with the flipQ function. Seethe mouse 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.getO 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 afterthat. w! a (.a i L_iffaiT!£ O : turn * 0 IJUK,9tlte W 1 Start the story 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. First screen 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 i The code pulls in elements from the script file as it goes, allowing us to implement decisions later on el if turn > 0 and click_state [0] == 1: line_atart = 0 for i in range (4): line_Btart == 0 and line[0] ! = "O': print line [0] screen .blit (location [ 1 ine to] ] , [O , O] } time - sleep { 1 ) if line_atart == 1 and line(l] ! - 'O’: screen. blit (character [lined] I , [377, 113] ) time . sleep [ 1 } el if line_start -- 2: pygame . draw . rect ( screen, (grey), pygame . Rect [ 130 , 423, 740, 120)) el if line__start == 3: screen .blit (game_font * render ( line [2 ] r li white), (135, turn += 1 if line [3] l « '0": game_script = line [31 time . s 1 eep (0.5) gamers t ate - line [4] clicked ■ 0 430) ) line_start += 1 pygame , di splay * f 1 ip () 126 The Python Book 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 advancingthe progression value. i The code here is very expandable, allowing you to add decisions that take you to different scenes gates U * game_running ™ True: m#nu_0cr«en ( * gatne { ) Add variables and assets 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. The starting function 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 to add different ways of turning it off in the future. 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 function at the end which will start the code in the command line. Expand your code 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 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 collidefunction. Move the assets 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. 1 The Python Book 127 ^Create with Python Pygame Zero Pygame Zero cuts out the boilerplate to turn your ideas into games instantly, and we’ll show you how Resources Pygame Zero: pygame-zero.readthedocs.org Pygame: pygame.org/download.shtml Pip pip-installer.org Python 3.2 or later www.python.org/ Code from FileSilo (optional) Games are a great way of understanding a language: you have a goal to work towards, and each feature you add brings more fun. However, games need libraries and modules for graphics and other essential games features. While the Pygame library made it relatively easy to make games in Python, it still brings in boilerplate code that you need before you get started - barriers to you or your kids getting started in coding. Pygame Zero deals with all of this boilerplate code for you, aiming to get you coding games instantly. PgO (as we’ll abbreviate it) makes sensible assumptions about what you’ll need for a game - from the size of the window to importing the game library - so that you can get straight down to codingyour ideas. PgO’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-styie. The project will illustrate what can be done with very little effort. PgO 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. 128 The Python Book Create with Python Right Breakouts a classic arcade game that can be reimagined in Pygame Zero Young and old In situations where Pygame is used boilerplate and all with young people, great results can also be achieved (see Bryson Payne’s book), but Pygame and PgO, despite their use as powerful educational tools, are also good for creating games for coders no matter what stage of learningtheyareat. Great games are all about the gameplay, driven by powerful imaginations generating images, animations, sounds and journeys through game worlds. Good frameworks open up this creative activity to people who are not traditional learners of programming, which is an area where Python has long excelled. Code on FileSiloj |§. . Zero effort w I Although game writing is not easy, getting started certainly is. If you’ve got Raspbian Jessie installed on your Pi, you’re ready to go. Open a terminal and type: touch example. py pgzrun example. py n 4 NoPi? 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/IGYznUB. And you’ll see an empty game window open (Ctrl+Q will close the window). Yes, it’s that easy to get started! Python 3 Wfc If you haven’t got Raspbian Jessie, chances are you’ll have neither PgO nor Pygame installed. The Python’s pip package installer will take care of grabbing PgO 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 PgO your chance for a gentle upgrade to Python 3. Older Raspbian If you’re still running Raspbian Wheezy, you’ll need to run the following steps to install Pygame Zero: Intro.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 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 PgO for the window display, with methods available for all sorts of different sprites... sudo apt-get update sudo apt-get install python3-setuptools python3-pip sudo pip-3.2 install pgzero » The Python Book 129 Create with Python Right The bat and ball come first -they’re the cornerstones of Pong and Breakout Program objects David Ames, who uses PgO to teach younger children to code at events across the UK, told us: “One thing to avoid when it comes to teaching kids is Object Orientation.” OOP (object-oriented programming) is partly abstracted away by PgO, but it can’t be ignored. Perhaps the best approach is using PgO and some simple code to start, then dropping in a piece of 00 when it’s needed to solve a particular problem. With the Code Club age group -about eight to eleven - feeding information to solve practical problems works well. It can work with adults, too - but there’s always someone who’s read ahead and has a few tricky questions. Sprite wU The intro example from the PgO docs expands on that with the Actor class, which will automatically load the named sprite (PgO will hunt around for a .jpg or .png in a subdirectory called images). alien = Actor(‘alien’) alien. pos = 100, 56 WIDTH = 500 HEIGHT = alien. height + 20 draw(): screen. clear() alien. draw() You can download the alien from the PgO documentation (bit. ly/1Sm5lM7) and try out the animation shown there, but we’re taking a different approach in our game. j Breakout via Pong w/ 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 Breakoutdone. ric Batt y wO 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 the screen, using the screen function. W = 800 H = 600 RED = 200, 0, 0 bat = Rect((W/2, 0.96 * H), (150, 15)) draw(): screen. clear() screen. draw.filled_rect(bat, RED) OO Mouse move w We want to move the bat, and the mouse is closer to an arcade paddle than the arrow keys. Add the following: on_mouse_move(pos) : x, y = pos bat. center = (x, bat.center[l]) Use pgzrun to test that you haveascreen, bat and movement. 130 The Python Book Create with Python To get the ball to move we need to define move(ball) for each case where the ball meets a wall Full code listing ## Breakout type game to demonstrate Pygame Zero library ## Based originally upon Tim Viner’s London Python Do jo ## 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’ , ‘xy’) ball_dir = Direction(5, -5) bat = Rect((W/2, 0.96 * H), (120, 15)) s Block (Rect): if init (self, colour, rect): Rect. init (self, rect) self. colour = colour blocks = [] n_block in range(24): block = Block(G0LD, ((((n_block % 8)* 100) + 2, ((n_block // 8) * 25) + 2), (96, 23))) blocks . append (block) draw_blocks() : >r block in blocks: screen . draw. filled_rect (block, block colour) if draw(): screen. clear() screen draw. filled_rect (ball, WHITE) screen draw. filled_rect (bat, RED) draw_blocks() if on_mouse_move(pos) : x, y = pos bat center = (x, bat.center[l]) if on_mouse_down() : ball_dir ball_dir Di rect ion (ball_dir.x * 1.5, ball_dir.y * 1.5) Square ball In properly retro graphics-style, we define a square ball too - another rectangle, essentially, with the (30, 30) size making it that subset of rectangles that we call a square. We’re doing this because Rect is another built-in in PgO. 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 can call directly. Simply add: WHITE = 200,200,200 ball = Rect((W/2, H/2), (30, 30)) ...to the initial variable assignments, and: screen .draw. filled_rect(ball, WHITE) ...tothedef draw() block. Action! I 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 balLdir = Direction^, -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! PgO will call the updateO function you define once per frame, giving the illusion of smooth(ish) scrolling if you’re not running much else. def move(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 each of the boundaries. Refer to the full code listing on page 67. 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 screen would need a value of W - 30. Absolute values You might expect multiplying y by minus one to work for reversing the direction ofthe ball when it hits the bat: ball_dir Direction(ball_dir.x, -1 * ball_dir.y) ... but you actually need to use abs, which removes any minus signs, then minus: ball_dir Direction(ball_dir.x, - abs(ball_dir.y)) Try it without in the finished code and see if you get some strange behaviour. Your homework is to work out why. The Python Book 131 ^Create with Python Right Tom Viner’s array of blocks negates the need for bordered rectangles •rw EOgU Jkb w/Ttav«biLt M ^ ^ m •: rtuthcopn V # # ft £) U * tjii clfrnc qit|ni:tt hub, ea*tt Milner /hreflifrut ..qiL * ed br^h.wt * pgzrun bmhwtT.py PgO+1 There’s a new version of PgO in development - it may even be out as you read this. PgO creator Daniel Pope tells us “atone generation API is in the works,” and that atthePgO PyConUK sprint, “we finished Actor rotation.” Contributions are welcome - not only to the PgO code, but more examples are needed not just to show what can be done, but to give teachers tools to enthuse children about the creative act of programming. PgO has also inspired GPIO Zero, to make GPIO programming easier on the Raspberry Pi, with rapid development occurringonthis new library as we go to press. Sounds 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 ! Blockhead! 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. Building blocks 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 relation to number across the screen, 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 screen sizes - see github.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 from hard-coded values... Goingforgold Create a Block class: ;s Block(Rect): init (self, colour, rect): Rect. init (self, rect) self.colour = colour ... and pick a nice colour foryour blocks: GOLD = 205,145,0 Line up the blocks This builds an array of 24 blocks, three rows of eight: blocks = [] for n_block range(24): block = Block(GOLD, ((((n_block % 8)* 100) + 2, ((n_block // 8) * 25) + 2), (96, 23))) blocks . append (block) Drawing blocks Draw_blocks() is added to def draw() after defining: draw_blocks(): for block blocks: screen. draw. filled_rect(block, block. colour) Block bashing 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) to_kill >= 0: sounds, block. play() ball_dir = Direction(ball_dir.x, abs(ball_dir.y)) blocks . pop(to_kill) 132 The Python Book Create with Python LeftTest your game once it’s finished -then test other people’s Breakout games to see how the code differs Full code listing (cont.) move(ball) : ball_dir ball move_ip(ball_dir) ball.x > 781 or ball x <= 0: ball_dir = Direction(-l * ball_dir.x, ball_dir.y) ball.y <= 0: ball_dir Direction(ball_dir x, abs(ball_dir.y)) ball . colliderect(bat) : sounds blip play() ball_dir = Direction(ball_dir.x, - abs(ball_dir.y)) to_kill ball . collidelist(blocks) to_kill >= 0: sounds block play() ball_dir Direction(ball_dir,x, abs(ball_dir.y)) blocks pop(to_kill) not blocks: sounds win. play () sounds win. play () (“Winner!”) time sleep(l) sys.exit() ball y > H: sounds die. play () (“Loser!”) time sleep(l) sys.exit() update() : move(ball) Game over Lastly, we need to allow for the possibility of successfully destroying all blocks, not blocks: sounds. win. play() sounds. win. play() print(“Winner!”) time.sleep(l) sys.exit() Score draw 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 PgO to explore, but a look into Pygame unmediated by the PgO wrapper is your next step but one. 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 PgO might do more for easy score keeping. Class of nine lives 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 PgO examples, such as the excellent pLlander example by Tim Martin: github.com/timboe/ pLlander. Don’t stop here 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. ■ The Python Book 133 1 36 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 1 50 Deliver content to your blog Add content to your site 154 Enhance your blog Complete your blog with add-ons "Python is a versatile language, perfect for making websites" 134 The Python Book \t *r Don’t be fooled into thinking Python is a restrictive language or incompatible with the modern web. Explore options for building Python web apps and experience rapid application development 136 The Python Book Web developments Why? | First released in 1991, companies j like Google and NASA have been j 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 restrictingthem to 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 manager pip included. Once Python is installed the first package to download should be virtualenv using ‘pip install virtualenv’, which enables you to create project- specific shell environments. You can run projects on separate versions of Python with separate project-specific packages installed. Check out the detailed Hitchhiker’s Guide to Python for more information: docs.python- guide.org/en/latest. Frameworks Let’s take a look at some of the frameworks available when developing Python web applications Django dj an go project .com GOOD FOR: Large database-driven web apps with multiuser support and sites that need to have heavily customisable admin interfaces Tornado tornadoweb.org GOOD FOR: Web socket interaction and long polling due to its ability to scale to manage vast numbers of connections 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 approach to code using subapplications. 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 remains as active and useful as ever. Flask flask .pocoo.org GOOD FOR: Creating full-featured RESTful APIs. Its ability to manage multiple routes and methods is very impressive PyramiD pylonsproject.org GOOD FOR: Highly extensible and adaptable to any project requirement. Not a lightweight system either 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 powerful microframework. 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 Werkzeug Debuggertoo. 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 includes a powerful debugger. Flask web development, one drop at a time giiWmeM // does // oommuniiu // snippets// extensions // seondj F?osJc is a microfraineworkfor Python based on Werkzeug, Jmja sand yuod intentions. And before you ask : It's BSD licensed! Flask is Fun Latest Version: ojqi from Flask import Flask The Python Book 137 eb development Create an API Let us explore the Flask microframework and build a simple yet powerful RESTful API with minimal code Install Flask w I 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 Create Index 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 application is Flask itself. import sqlite3 from flask import Flask, request, g, redirect, url_for, render_template, abort, jsonify Declare Conf ig wO For a small application we can declare configuration options as upper-case name value pairs inside the main module, which we’ll do now. Flere 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 placed above these last two lines. # 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() Connect to Database 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’]) Database Schema w w 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 bootstrapped data. 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)’); Instantiate the Database w w 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.closingO’ 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 the open database. 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.commitO AH Populate the Database w # 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 AQ Request DB Connection wO 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(); “World-renowned image sharing service Instagram and social pin board Pinterest have also implemented Python as part of their web stack, opting for Django” 138 The Python Book @app.teardown_request def teardown_request(exception): db = getattr(g, ‘db’, None) if db is not None: db.close() Display Posts 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 diet method and saved as the posts variable. To render a template we then call render_ templateO and pass in the file name and the variable to display as the second argument. Multiple variables can be passed through as a comma-separated list. @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[l]) for row in cur.fetchallQ] return render_template(‘show_posts. htmr, posts=posts) Template Output 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.
    {% for post in posts %} {{ post. title }}

{{ post, text | safe }} {% else %}
  • Sorry, no post matches your request. {% endfor %} Make an API Response 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/vl/posts/’, methods=[‘GET’]) def show_entries(): cur = g.db.execute(‘select title, text from posts order by id desc’) posts = [dict(title=row[0], text=row[l]) for row in ur.fetchall()] return jsonify({‘count , i len(posts), ‘posts’: posts}) Get a specific Post 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 URL 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/vl/posts/’, 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[l]) 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’}) Run the application 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://1 27.0.0.1:5000. python index. py API JSON Output 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 is incredibly powerful. Python in the wild Interested in Python development? You’d be in good company with big names currently using it 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 and requests made to their services. Mozilla, Atlassian’s Bitbucket repository service, and popular satire site The Onion have all been noted as using Django fortheir products. The Python Book 139 liweb development Django application development Django is a full Python web-app framework with impressive command-line tools Installing Django The installation of Django is relatively easy once you have python installed. See for yourself as we build a simple app here Create Virtual Environment 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 Activate and Install 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 prl* Lmbrtl U)o^i i ftp ii 1 ! gifl .ru, *. r i pji H» i imMUM i iwiBmMM i mM ii " l m Hnrt-llM t T 7 M i *P mu, [Itt *i.i fowi-.»U KriJ U* 4.» it l«v-4M * r. ■fm Tiilfl'. 'vMW' * 'IIWH 9 tm mr* Hrfmm. 1-7. ? Create Core Project 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.py script with the name of the project that you want created. django-admin.py startproject myblog # * J -/pgi 'iW-> ’.join([p. title for p in post_list]) return HttpResponse(output) Manage the URLs Create ‘ b log/ur Is. py’ and add the code to import the views that were just made in the module and the accompanying URL patterns. Open my b log/ur Is. py and add the URL function call to implement a new URL for the app to display the view. Visit http://1 27.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 ,A $’, views. index, name=’ index’), ) # my blog/urls.py urlpatterns = patterns(‘’, url(r ,A blog/\ include(‘blog.urls’)), url(r ’ A admin/’, include(admin . site. urls)) , ) Admin sections can be problematic in their own right. Django provides an extensible admin interface for you * * — , - .* — — . ” - ■ —■ -- — - J Getting Started with Python on Haroku Heroku heroku.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 without any cost. Hwl, fun, and code Python In the cloud!! Python Anywhere 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 your plans performance. 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 you need them. The Python Book 141 r eb development Creating dynamic templates with Flask, Jinja2 and Twitter Create a dynamic webpage with Twitter and Flask’s rendering engine, Jinja2 # alt Tweets e — JBL JL M. Nl to wri 1 tan* ■ k *«!«• ' o A O A O -J*V Ca - ■ni^itaqM £ The template uses a loop to generate a list of Twitter tweets Resources Python 2.7+ Flask 0.10.0: flask.pocoo.org Flask GitHub: github.com/mitsuhiko/flask ATwitter account Your favourite text editor Code downloaded from FileSilo 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 usingforthis. Rearranging our code 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 shal 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 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. Adjust the function declaration like so: def sign_request(parameters, method, • baseURL, consumer_secret, oauth_secret): server.py modules 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 ourfirst import statementto be... import urllib2, time, random, json ...our project will continue to function as it did before. Note the addition of the json module: 142 The Python Book Web developmental 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. Our first template 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 declarations for this file. {% if session[‘oauth_token’] != %} Already Authorised

    Hello, You’ve authenticated !
    Let’s get some tweets

    {% else %} Authorisation required

    We need to authenticate

    {% endif %} The BSD-licensed Flask is easy to set up and use - check out the website for more info Flask web development, one drop at a time Code on FileSilo, Rendering our template In server.py, let’s create a route for 7’ 1 handle the authorisation process. @app.route(7’) def home(): Qvemtwff ds&ff c om munity ff snip pet s// txtm m mff midi Flask is a micro fra mework for Python based on Werkzeug r Jinja sand good intentions. And before you ask: It r s BSD licensed ! Flask is Fun utest Versioo: aa? if not ‘oauth_token’ in session: session. clear() session[‘oauth_secret’] = ° session[‘oauth_token’] = ** return render_template(‘index.htmr) 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.htmr). from flask import Flask app = FlasM nane } $app. route ("/" ) def helloO: return "Hello World* 1 ' if name = " main 11 . app- ryn( J And Easy to Setup pip install Flask python hello k py * Running on httpt //localhost :5BS*0/ Template variables We can choose to send variables to our template with rende^templateC'index.htm’, variableOne=value, variableTwo=Value) but in this instance we don’t need to as each template has access to the request and session variables. Interested? ®st«r a^ss Download latest release Co. jo) l or downloa d as PDF and zi pped HTML - Jyiu the mailin&lisi * Fork it on frit hub ■ Add issues and feature requests The Python Book 143 Web development 3 Now we know how to build templates, let’s grab some tweets to display Open index.html. All code executed in a Flask template is contained within {% %}. As this is our homepage, we want to direct users accordingly, So let’s check if we’ve got an access token (Fig 01). Between the ifs and else of the template is standard FITML. If we want to include some data - for example, the access token - we can just add {{ session [ £ oauth_token’] }} in the FITML 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 that we have a template, we can redirect our users back our root URL and have a page rendered for us that explains the progress we’ve made. Getting lost (and then found again) With every server, some things get misplaced or people get lost. So how do we handle this? Rather than defining a route, we can define a handler that deals with getting lost. @app.errorhandler(404) def fourOh Four (error): return render_template(‘fourohfour.htmr) 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, but we could also redirect to another page or dump the error message. Static files 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/sty les.css or /static/js/core.js. The tutorial resources include a CSS file for styling this project. Let’s get some tweets So now we know how to build templates, let’s grab some tweets to display. In server.py define a new route, get-tweets, like so: @app. route (‘/get- tweets’) @app.route(yget-tweets/’) 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 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 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 was always an integer instead of a float or string, we’d define our route like so: @app.route(yget-tweets/’) Checking our session and building our 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 ‘Ljoin^andom. choice (chars) for x in range(size)) Signing and sending our request 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 userjd 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’] Handling Twitter’s response 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.htmr, data=tweetResponse) Whereas before, we accessed the session to get data into our template, this time we’re explicitly passing a value through to ourtemplate. Displaying our tweets 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 to generate a list of tweets we’ve received. 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’t get anything. Once again, any template logic that we want to use to generate our page is included between 144 The Python Book Web development {% %}. This time we’re creating a loop; inside the loop we’ll be able to access any property we have of that object and print it out. In this template we’re going to create an
  • element for each tweet we received and display the user’s profile picture and text of the tweet (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’re trying to access. 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 %}. tweetRequestParams = { “oauth_consumer_key” : consumer_key, “oauth_nonce” : helpers. nonce(32), “oauth_signature_method” : “HMAC-SHA1”, “oauth_timestamp” : (time.time()), “oauth_version” : M 1.0”, “oauth_token” : session[,Aooauth_token’], “user_id” : session[‘user_id’], “count” : st (count) tweetRequest = helpers. sign_request (tweetRequestParams, “GET”, “https : //api . twitter, com/1 . l/statuses/user_t imeline . 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’]) Flask filters I 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 Iforceescape, 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. Wrapping up 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 circ*mstances. 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() {% if data %} {% else %}

    We didn’t get any tweets :(

    {% endif %} Fig 02 Fig 03 Fig 04 The Python Book 145 eb development Django comes with a lightweight development server so you can test all your work locally Django is of course able to read and write to SQL databases, but it needs very little prior knowledge to succeed in doing so Using HTML and CSS in conjunction with Django is clear and straightforward; it’s much easierto bug-fix than PHP Django comes with a generic back-end site that is set up in seconds, and easily customisable after that Resources Python Source Code www. py t h o n .o rg/do w n load/re leases/2 .7. 2 Django Source Code www.djangoproject.com/download Build your own blog with Django Learn how to use this extremely powerful Python-based web framework to create a complete blog from scratch in record time Creating your own blog always feels like a great accomplishment. Sure, you could use the fantastic WordPress if you need a complete blog with every feature you’d ever need right now. And Tumblr exists for people who just want to write something, or post pictures of corgis in space. 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 array of applications. 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 interactions with HTML. 146 The Python Book Web developmental Install Python 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. Verify your Django To make sure Django installed properly, and that you have the right version, enter the Pyth on shell by typing ‘python’ and enter the following: import django print django. get_version() Install Django 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 version 1.3. It will return a version number if it has installed correctly, which should be 1 .3. Start a new project In the terminal, cd to the folder you want to develop the blog in, and then run the next command: django-admin startproject myblog Here, ‘myblog’ can be replaced by whatever you wish to name the project, but we’ll use it for the upcoming examples. Start the development server 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 myblog folder and then use: python manage. py runserver If all goes well, it should return zero errors. Use Ctrl+C to exit the server. Configure the database 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’ : Vhome/user/projects/myblog/ sqlite.db’ , Save and exit. 07 Create the database The database file will be generated by usingthe command: python manage. py syncdb During the creation, it will ask you to set up a superuser, which you can do now. The SQLite database file will be created in your myblog folder. The Python Book 147 ^Web development 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, but we’ll use it in our examples. Start your blog model We can now take the first steps in creating our blog model. Open models.py and change it so it says the following: from django.db import models class Post (models. Model) : post = models. TextFieldQ This creates the Post class, which has a subclass that contains your blog text. Customise your blog Let’s now expand the blog model a bit so it resembles a more classic blog: class Post (models. Model) : post = models. TextField() title = models. TextField() author = models. CharField(max_ length=50) pub_date = models. DateTimeFieldQ A CharField needs to have a character limit defined, and DateTimeField holds the time values. B You don’t have full control from start to finish with a prefabricated blog - but you will with Django Install yourapp 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 and add: ‘blog’ , Then run the following to create the database tables: python manage. py sql blog And finally: python manage. py syncdb Set up to post Now we can create a post and test out our code. First though, enterthe Python shell: python manage. py shell Then execute these commands to add all the necessary fields and data: from blog. models import Post import datetime Let’s blog Create the post. For this example, we will call it test_post: test_post = Post() Now let’s add the blog content: test_post . post = ‘Hello World!’ test_post. title = ‘First Post’ test_post. author = ‘Me’ test_post . pub_date = datetime, datetime. now() And then save it with: test_post.save() Start the site back-end To create the admin site, edit urls.py from the myblog direct ory, and uncomment or add the following lines: from django.contrib import admin admin . autodiscover () url(r’ A admin/’ , include(admin.site. urls)) , Save and exit, then edit settings. py and uncomment this line from INSTALLED_APPS: ‘django.contrib. admin’ , The admin site is now at 127.0.0.1 :8000/ad min/. 148 The Python Book Web development^ Setup the admin page 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 file admin. py in the blog directory and enter: 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, and run: 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 easier to add more. Activate the front-end I Open up urls.py from the myblog directory in your editor and add the following to the urlpatterns section: url(r’ A myblog/’ , ‘blog. urls. index’ )) , One of the examples in the file can be uncommented and edited to this as well. It points to a model we will now create. Create another urls file I I You need to create another urls file in the app directory, in our case blog/urls.py. Create it and add the following: 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)) Django is an incredibly powerful and simple-to-use language Start the template 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 looked for in settings.py: TEMPLATE_DIRS = ( ‘ /home/user/pro jects/templates ’ , ) You can put the template directory wherever you want, as long as it’s referenced here. Write a template Now to write the site template. In our example, we’re using index.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 template directory. % • • * O U7 (1 1 I n ha kai t MpI 14 joij. * ^IJI Hrfe toatf View your handiwork Let’s make sure this worked. Start the developer server with: python manage. py runserver And navigate to 1 27.0.0.1 :8000/myblog/. It’s not pretty, but you should have successfully called upon your stored data. We’ll spend the next steps tidying it up a bit. Format the front page Go back into the template file, index.html, and add the following htmltags: {% for post in post_list %}

    {{ post. title }}

    {{ post. author }} on {{ post.pub_ date }}

    {{ post. post }}

    {% endfor %} This is just an example - the post can be in any order with any tags. Spruce up the admin list We’ll do this in the admin. py file in our blog directory; open it in your editor and make the following changes: 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) In this case ‘list_display’ is a fixed variable name. A logical post page 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 following additions: class Admin (admin. ModelAdmin) : list_display = [‘title’, ‘author’, ‘ pub_date ’ ] fields = [‘title’, ‘pub_date’, ‘author’, ‘post’] admin. site. register (Post, Admin) Remember to save! Afunctional blog So there you have it! Navigating to 1 27.0.0.1 :8000/admin/ or 1 27.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 able to make after this tutorial. H The Python Book 149 »Web development With Django we can make simple sidebars that list archives by month Django has built-in code to deal with pagination very cleanly and effectively Allow your readers to give you feedback, and moderate them in the admin panel With minimal extra code, ourtemplate can display the month archive from the sidebar Deliver content to your blog We continue building an awesome blog using the powerful Django framework, and this tutorial is all about the front-end content delivery Resources Python base: http://www.python.org/download/ Django source: https://www. djangoproject.com/download/ In the last tutorial we began to build the most basic of blogs, and learned how to use a bit of Django in the process. We can now set up a new project, create a database and write basic code to read and write to the database. All simple stuff, but of course it’s core to building websites where Django might be called upon. 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 develop websites. You should keep using Django 1.3 for this tutorial, as we did before. 150 The Python Book Web developmental | 1 I « MnlF 1 Mi* • pin 14 tv f ini hM m New blog order 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 following line: post_list = Post. objects. all () .order_ by(“-pub-date”) This makes sure that posts are displayed in reverse order (newest first). Aviewtoapage 1 fa 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}) 1 HTT ■' I Clean upyourcode O 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. To get it working, add: from django. shortcuts import render_to_ response We recommend that you edit the index code to match post_page. i 06 Link to the page Let’s get these links working from the main page. Open upthe index.html file and make the following change:

    {{ post. title }}

    This is a very simple addition using an absolute link, and requires no fiddling with the views or model. Edit URLs 1 T In urls.py in myblog we need to make some additions and modifications for the website to direct to the post correctly: url(r’ A myblog/$’ , ‘ blog. urls. index’) , url(r ’ A myblog/(?P\d+)/$ , , ‘ blog . urls . post_page ’ ) , The postJd is the number of the post, which is auto-generated. The ‘$’ is important to make the redirection work. Pagination To get blog posts split up over pages, we need to make some additions to urls.py in the blog folder: 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( 4 blog/index, html’, {‘post_list’ : post_list}) A post template 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 following formatting to resemble the front page:

    {{ post_page. title }}

    {{ post_page . author }} on {{ post_page. pub_date }}

    {{ post_page . post }}

    AQ Please turn over Now we need to add the navigation links to the blog, so open the index template for editing: {% if post_list.has_previous %} Newer {% endif %} {% if post_list.has_next %} 01der {% endif %} The Python Book 151 l&Web development Q Wrong page Let’s add a quick bit of code to return somebody to the previous page if they get the URL wrong: We need to be able to process the data and metadata in the forms 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)’. Have your say Everyone has their opinion on the internet. You can give your readers the ability to comment, and we’ll start by editing models.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 a comment. Back to the comment We now need to add a small line to the urls.py file in myblog so the comment can be posted then sent back to the original page: url(r’ A myblog/add_comment/(\d+)/$ , , ‘ blog . urls . add_comment ’ ) , This URL pattern calls the ID of the page that you’re on. Form a comment i 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 following additions: from django. forms import ModelForm from blog. models import Post, Comment class CommentForm(ModelForm) : class Meta: model = Comment exclude = [‘post’] In the post I u \/Ve need to attribute the comments to the post they’re being made on, so update the post_page definition: from d j ango . 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 request forgery. [ — Comment template r Let’s get the post page ready for comments by adding this to post.html:

    Comments:

    {% for comment in comments %} {{ comment. author }} < p > {{ comment. text }}
  • {% endfor %} Add comment Define your comments w The final step is defining the comments in blog/urls.py, and it’s a big one: 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 FlttpResponseRedirect (reverse (‘blog. urls. post_page’ , args= [comment, id])) This ensures text has been entered, and if not specified author is ‘Anonymous’. Before testing, run syncdb so comment tables can be created. 152 The Python Book Web developments Administrate I V Like the posts, we can get the Admin page to see comments. Start editing blogs/ad m i n . py to get t h i s f eatu re added : 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) — Comment-specific admin features Now we can add the comment-specific admin features without causing any clashes: 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 the post it’s connected to. Start to define month_timeline First we need to get all the information from the posts: 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 information we need. Finish your def inition Now we will order the posts by month and year starting from our first month. Sidebar beginnings Django makes it pretty easy to order posts by years and months, but first we need to import some new models into blog/urls.py: import time from calendar import month_name We’re going to define two new functions, month_timelineand month, to make the sidebar. for y in range(year, year_begin-l, -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 Return to reader I With the list organised, we can now define month so we can display it on the blog: 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())) Now we need to link it up to the index template. Finalise your sidebar definition Edit the return command on the index function to include the sidebar information: return render_to_response( ‘ blog/index . html’, dict(post_list=post_list, sidebar_list=post_list . objectJList , month_list=month_timeline())) Then add this line to urls.py in myblog so a month page can be rendered: url(r’ A myblog/month/(\d+)/(\d+)/$’ , ‘blog. urls. month’ ) , All we need to do now is display the information on the site. Sidebar on the web Go to the index template. First of all, change the first line of the post forloopto: {% for post in sidebar_list %} Simple enough. Now we need to add the sidebar information: {% for month in month_list %}

    {{ month. 2 }} P> {% endfor %} Sidebar finale h Obviously it’s not at the side right now - that’s a job for the FITML and CSS. The info is there, though, and you can manipulate it any way you want. Flowever, your blog is now a lot more friendly to your readers. The Python Book 1 53 ^Web development Resources Python base: http://www.python.org/download/ Django source: https ://www. djangoproject.com/download/ Enhance your blog with extra features To add to the previous tutorials, we’ll cover some of the more advanced features you can utilise with the power of Django 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 classic blog, 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 when creating an SQL query. 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 finally finished! Summarise 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 template like so: < p > {{ post . post | truncatewords : 3 }}

    This automatically takes the first three words of the post -of course, you can use any number. 'A We’re going to add summaries, excerpts and an RSS feed we can add an excerpt field to ou r post model so you can craft one manually: excerpt = models. TextField() To limit the characters in your excerpt, use a CharField like for our author section. 33 Write an excerpt To write the excerpt, or append it to the jrevious posts, we’ll have to add it to the admin >age. Open up admin. py and edit the fields ;ection ofthe AdminPost class to add excerpt: ields = [‘title’, ‘pub_date’, author ’ , ‘ post ’ , ‘ excerpt ’ ] 154 The Python Book Web developmen Have automatic summaries or manually crafted excerpts foryour blog posts Create and manage parent and child categories as a separate function of the blog Learn howto alter the database to create posts with categories, and add them to other posts Create custom RSS feeds using built-in Django functions Excerpt or summary You can replace the post content in the index template with the excerpt, but we can keep it as a backup for if the excerpt is empty: {% if post. excerpt %}

    {{ post, excerpt }}

    {% else %}

    {{ post, post | truncatewords:3 }}

    {% endif %} you’ll have noticed our web server has stopped working. This is because there is no excerpt column in our database. Therefore we need to add the excerpt column. To find out how, run: $ python manage. py sqlall blog Database query w I The output will showyou whatthe SQL code is to add the models to the database. We want to add the excerpt field specifically, which should look something like this: “excerpt” text NOT NULL Make a note of it. Alter table To get into the database shell and add the field, run: $ python manage, py dbshell Then we need to use an ALTER TABLE query: ALTER TABLE “blog_post” . And then enter the code we noted down like so: ADD “excerpt” text; AQ Save the changes O We’ve removed NOT NULL as we already have entries that won’t have an excerpt, and want to make it so an auto summary can be made. Save the changes with: COMMIT ; and then exit the shell with: .quit Test it out Now we can test out the excerpt code - create a new post or edit an existing one to have an excerpt. If you’ve followed our steps correctly it should work; if not, you may need to do a bit of bugfixing. The Python Book 1 55 l&Web development comments, we wantto add a ForeignKey to the Post model so we can attribute a post to a category. Add this line: category = models. ForeignKey (Categories) And move Categories to the top of models.py. class Categor ies (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) This allows for parent and child categories. Database category Like before, we’ll find out the SQL needed to alter the table: $ python manage. py sqlall blog Which for our example returns a somewhat different code than before: “category.id” integer NOT NULL REFERENCES “blog_ categories” (“id”) It’s an ID we’re getting, not text, from the categories table. Administrate categories We can add it to the admin site by creating a Categories section in admin. py: class CategoriesAdmin (admin. ModelAdmin): list.display = [‘name’, ‘slug’, ‘parent’] fields = [‘name’, ‘slug’, ‘parent’] admin. site. register (Categories, CategoriesAdmin) Before we can make categories, though, we need to create the database table: $ python manage. py syncdb much like before, but with the new code: ALTER TABLE “blog.post” ADD “category.id” integer REFERENCES “blog_categories” (“id”) ; And finally, to save: COMMIT ; We can now create categories separately all the post fields, to the index template we just add:

    Category: {{ post . category }} p> And to the post template:

    Category : {{ post.list. category }}

    blog/urls.py. Import Categories and then add: def blog_categories(request, category, id): categories = Categories. objects. get(pk=category_id) We need the category.id to call the corresponding posts. SSR3E — Categorise the posts I i Similarlyto what we did with the Administrate categories - part 2 Now we can go backto admin. py and add the new category fields to the PostAdmin model: list.display = [‘title’, ‘author’, ‘pub.date’, ‘category’] fields = [‘title’, ‘pub.date’, ‘author’, ‘post’, ‘excerpt’, ‘category’] Our previous blog posts with no category have disappeared! To fix this, go backto models.py and make this change to the Post model: category = models. ForeignKey (Categories, blank=True, null=True) So we can now create categories separately, assign them to posts, and view posts without a category. 156 The Python Book Web development Category definition Finish the definition by using the parent_ id to filter the correct Posts, then renderthe response: category _posts = Post. objects, filter (category=categories) return render_to_response ( ‘ blog/categories . html’ , dict(category_posts=category_ posts, categories=categories)) Again we’re calling a new template that we’ll Categorytemplate _ i We’ll usesomething similarto the Index and Post template to create a category page template: {% for post in category_posts %}

    {{ post. title }}

    {{ post. author }} on {{ post . pub_date }} % if post, excerpt %}

    {{ post. excerpt }}

    {% else %}

    {{ post. post | truncatewords:3 }} < /p > {% endif %}

    Category: {{ post. category }}

    {% endfor %} — RSS links We need to define item_linkforthe feed so that the feed items can linkto the right place. We have to give the complete URL and the post ID for it work: def item_link(self , post): link = “http://127. 0.0. 1:8000/ myblog/”+str (post.pk) return link IQ Category URLs We’ll create the URL in urls.pyasforthe post page, only it’ll give the slug of the category instead of an ID in the link: url(r’ A myblog/ category/ (?P\d+/$ ’ , ‘ blog . urls . blog_categories ’ ) , 21 Category clickthrough Finally, let’s make the categories click through to the relevant page by changing the Finally, let’s make the categories click through to the relevant page RSS URLs The final step is adding the feed URL to urls.py: url(r ’ A myblog/feed/$ , , BlogFeed()) , And nowyour blog is now fully functional. With a bit more tweaking and theming, you can get it online and blog away! The Python Book 1 57 0 ib| 168 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 film 168 Send an SMS from Raspberry Pi Combine simple Python code and Twilio Raspberry Pi 16 J6 Sent from theTwillio Sandbox Number * Hey] Did you know you can send text messages from your Pi? 170 Build a complex LED matrix Build and program this useful light display "The Raspberry Pi takes the 'Pi' of its name from Python, as the official Pi language" 158 The Python Book The Python Book 1 59 Use Python with Pi Programming in Python on the Raspberry Pi Learn the basics of programming in Python with the Raspberry Pi, laying the foundations for all your future projects It’s good practice to describe what the program’s purpose is at the top of the file. This will help you out when working on larger projects with multiple files It’s important to think about data types. We convert the numberto decimal to make sure that we don’t lose any decimal numbers during arithmetic ISf+f-T (l 1 * IrfiB. d-rraaal *1 riskin' - m trip | | ' WfU«*i > ^u»btr - DvcIhU * iMfefr prLfttF'fflr rpm trlair rhv ritn prl.it I r-ttu print i ‘ ‘1 r# ID r fc* - FiHi Will* ptiCrkn p* Ifltl* bpuT i ’ PI Plir 4-H I IP |Aur ■ llrilki** * *1**1 a* kilii*! t*il niMkrr P * flu ufrl L n| C h Pi i uiili i it HUlFlilt I Pdf PVPtpi r + 1 1 . ~|T — ■ Itp 1 ■■■ ■ ■ |f tUkuLi — *j*4“ *t — ***" l^i.csikri b In* pluti tff* r* i if If »* #1 if pf in i '.nil iBf *T* prlit Die r«i«4 ■ill* 4 drill 4* V i hr Ivin r 1 S Si pH I ** I pr jp| I h 1 It -r 4 PU»|« r Mg l I h * *tf I nuBfcp rll#u*l*fl f . * p ! Folder, then type a name and click OK. We created a folder called Python, and inside that created a folder called Hello World v2. 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 and more complicated. Starting Geany 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 the Python interpreter. 1 # 1 /us r / bi n/e n v py thon 2 3 # An advanced Hello world pr 4 # programming in Python via 5 # for Linu* User and Dev el of 6 7 # Import the sys libary for 8 import sys 9 # Import everything from th* 10 from decimal import * When using the addition operator with strings, they are joined together Setting it up Having detailed comments in your code is important because it allows you to note down things you find confusing and document complex procedures. If another programmer has 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 make use of the decimal type. la i -1 V • * ** : * - - - . ts IiiUDb ■ r*_ I p^jl |. ■ PI mliF f*jr 1i*%! prlntf* i fve TH-ault ol halving that nufibe 26 p r i n t. i “ 7 be result of doubling that rtumb 17 primCTb* result af squiring thil rtu fib 28 arihlX"") Printing our numbers I 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 empty string just prints a new line. The print function only accepts string values, so convert each decimal value to a string 162 The Python Book Use Python with Pi 30 31 32 33 34 35 36 37 38 39 40 4 The stopping condition for a while loop yesOrNo = False # A while loop that will run untill a user enters either □while yesOrNo -- False: result = raw_input(’' Do you want to continue? (yes/no) a if result = "yes" or result == "no": r yesOrNo = True B else: L printO'Error, please type yes or no" + "\n") yes" or "no" Input validation with While loops and If statements 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 of False, or the while loop will not run. 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 equal to’. 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 program at anytime. 42 # Deal with the result 43 EJif result == "yes*: 44 L print (" \n Continuing* } 45 Seise: 46 print (" \nE*i ting" ) Continue or exit? I w 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 quotation marks like above. # Create the count count - 1 Loops with numbers I 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 the while loop. 52 53 54 55 56 57 50 59 60 # Use a while loop to add 5 to the print (■ incrementing the number by E^while count 55 nurjiber += 1 print ( !l number + + atr Ccount) * increin count += the csi J, The count variable lets us know exactly how many times we have been through the while loop SI tLi * Fifuifu §ff ey pritttinq tMi w* ar* H priip|(»\riEim^T Finishing off I w 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 to interpret. Incrementing numbers with a loop 1 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 is incremented. Hfal’CCHH Ll your fir-vt njm#: Liam Pl*«i a number: 12. 12 Th* mult nl halving th At rttinhwf: ft.flfi ftas r-aeult a\ doubling that numbor: 24.24 kfm * u- 1 1 1 q ihdi 'lunitei . i46.S$44 Do you want to eontinytT (ywtoo> yes Cootiruji ng I rlt repenting the number by a m ird-.Ar + 1 ■ lit *7 nufffccr + 2 » 14+13 liuiiivl W 3 = La*iz number + 4 ■ 16.12 nudbor + 5 * 17.12 Cai Admire your work I # Now that we’ve finished coding, save any changes you have made and run your program with the F5 key. The Python Book 163 Use Python with Pi Turn your Raspberry Pi into a stop-motion studio Build your own animation studio by using your Raspberry Pi as a stop-motion camera Resources Hard drive OSMC: osmc.tv/ Home network Another Linux computer, less than eight years old 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 chances are that you’ve overlooked the Pi camera module for a while. 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 stop motion Pi camera with a smartphone mount for stability. Mount your stop-motion Pi 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 rostrum to mountthe Raspberry Pi. Alternatively, you may be animating something on a desk, table or perhaps the 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 mountingyour Raspberry Pi. Find somewhere to shoot 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 creation of your stop-motion film. As time progresses and your skill develops, other surfaces can prove useful alternatives, but keep it simple for now and stick with flat surfaces while you get to grips with the art form using the Raspberry Pi stop-motion camera. Connect the Pi camera module 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 version of your Raspberry Pi. Use Python with Pi 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-out and HDMI ports. 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. Take precautions before removing the device from its antistatic bag, as the camera module is very sensitive to static electricity. 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 into place. Test your Pi camera module 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 the command line: 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 the terminal and enter: raspistill -o imagel.jpg You can review the resulting image in your Home directory. Straighten out the image 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 open the terminal and enter: sudo apt-get install python-picamera python3- picamera ; sudo idle3 In the Python editor, open File>New 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 correctly outputted image. To save time, however, you might try rotating the position of your camera or Pi camera module! import picamera from time import sleep with picamera. PiCamera() as camera: camera.vflip = True camera. hflip = True camera. start_preview() sleep(3) camera . captu re ( Vhome/pi/image2 . j pg ’ ) camera . stop_preview() Set up the breadboard and button We have two ways to add a button to the Raspberry Pi, but before proceeding, ensure you have switched the computer off and disconnected it from the mains. You should also disconnect any cables and hardware. 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. With a script designed to detect action from the button on the GPIO, each frame of your animation can be captured with a single button push. II Till 'l III II II Uni' Left Consider the angle you’ll be shootingfrom as you are settingup Right With the camera module, ensure the shiny side faces away from the Ethernet port The Python Book 165 Use Python with Pi Don’t want to build your own rostrum? Why bother when a camera tripod can be positioned as needed? Tripods and suction holders Don’t want to build your own rostrum? Why bother when a camera tripod can be positioned as needed and other items, like smartphone suction holders and grips, can be employed to hold your Raspberry Pi case and camera module in place? Fortop-down animation, suction-pad smartphone holders (available for under £10) that use a sticky gel for a stronger grip are perfect for holdingyour stop-motion Pi camera and attaching to a flat surface above the animation subject. « ■4 AQ Stitch together your stop-motion animation wO The collected images can be cycled through relatively quickly using a special picture viewing app, but for a true animation you will need to compile them into one single file. In the terminal, install ffmpeg: sudo apt-get install ffmpeg After installing, you can then convert your images into a video clip, as follows: 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 With this file created, open with the command: omxplayer animation. mp4 The video will then be played in full-screen mode. AQ Use an app instead w w Don’t fancy using the script? Try this stop-motion application. Begin by installing the raspicam-extras package that includes the UB4L drives for the Pi: Code for stop motion Once satisfied with the results of your Pi camera, it’s time to turn it into a stop-motion camera. 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 code as animation. py: import picamera from RPi import GPIO button = 17 GPIO.setmode(GPIO.BCM) GPIO. setup(button , GPIO. IN, GPIO.PUDJJP) 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, in a new terminal window, enter the following: sudo python3 animation.py Press the button to capture each frame, moving the subject as needed. When you’re all done, hit Ctrl+C to terminate the script. wget http://www.linux-projects.org/listing/uv41_ repo/lrkey.asc && sudo apt-key add ./lrkey.asc sudo sh -c ‘echo “deb http://www.linux-projects. org/listing/uv41_repo/raspbian/ wheezy main” » / etc/apt/sources. list* I sudo apt-get update sudo apt-get install uv41 uv41-raspicam uv41- raspicam-extras With that done, 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 solution and captured images can be stitched together using the ‘Number of images’ slider and the camera button above it. 166 The Python Book Use Python with Pi Here’s the stopmotion program in action - it’s a simple enough GUI to get your head around and gives you a nice preview window Put it all together 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. Storyboard your shoot It’s easy to get tied up with the idea of creating a stop-motion camera and forget all about a subject and how it will act. You can avoid any problems here by taking the time to carefully plan what will happen in your film: your story. Remember, each second of the video will require 26 frames! The best way to plan at this level is to simply write up an outline, but beyond this you may prefer to storyboard instead by making pencil sketches to help you progress the story. Cast your stop-motion shoot 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, toys and child’s play clay. 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 subjects) in the positions you wish them to take throughout. Stop-motion with toys If you cast toys as your stop-motion stars, you will get a much better result from something that is built to stand up on its own than toys that tend to sit or fall over. LEGO sets and Minifigs appear in many stop-motion productions on YouTube. This is with good reason, as they’re really easy to place in a desired 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, but you should aim to develop your own approach overtime. People in stop-motion films 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) and the technique can be used on humans to create surreal effects. If you want your subject to be moving around a room too, they can appearto be floating or gliding. The results can be strange, but useful if you know what you want. Make your own Wallace and Gromit 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 remarkably clever dogs, have a wire skeleton that is used to keep movement in the desired position. 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! From stop motion to time lapse 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 into a film. 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 script remotely: import time import picamera VIDEO_DAYS = 1 FRAMES_PER_HOUR = 60 FRAMES FRAMES_PER_HOUR * 24 * VIDEO_DAYS capture_f rame(frame) : with picamera. PiCamera() as cam: time.sleep(2) cam.capture(yhome/pi/Desktop/frame%03d.jpg’ % frame) for frame in (FRAMES): start = time.timeO capture_frame(frame) time.sleep( (60 * 60 / FRAMES_PER_HOUR) - (time.timeO - start) ) Take your stop-motion studio to the next level 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 small ones. After all, this device photographs in high resolution so there is no reason not to adopt this setup and incorporate it into a working stop-motion studio with a miniature set. Sharing your work through YouTube is a great idea too, especially as it will make it simple to add a soundtrack using YouTube’s browser-based editor. ■ l I I l I I « The Python Book 167 i&Jse Python with Pi Send an SMS from your Raspberry Pi Create a program that combines Twilio and simple Python code to enable you to send an SMS (text message) from your Pi to a mobile phone Resources Raspberry Pi Twilio account 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. Set up your Twilio account w I 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-twilioand create your account. Raspberry Pi Sent from the Twilho Sandbox Number - Hey! Did you know you can send text messages from your Pi? Left With this method, you could get your Pi to drop you a text when it finishes running a script f 7 '•"«* m ^7 ** -f Hi t* VI i jp-f r 1 1 Mil Register and verify mobile numbers 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 form and press submit. © Q 0 © 9-jI £> 16:46 168 The Python Book Use Python with Pi 0 V ¥OCE.5m*l«*5 NWWEHS HP DEV TQCH.5 LOGS USAGE BOC* HELP ▼ inti AtmA n*. wnru nro fi.rTT»*fi vtawtfp rfiHFIHIPPf remi raFHTF i Voice, SMS & MMS API Credentials Th? iht AP* jni *il dm rourA^ceuni^fjnd iMh tdkm ACCOUNT SID Ready to remove tn*l rejinclwi? euxl enjoy Ml benefits? LcAm mere atari Indi mcDurtri I? AU!H TOKEN I Sandbox App Tv>| Yom App © Cillhn ?>jndbot t g™t«i HM*| * Cdl ATlh twilio clienl The dashboard wO Once registered and logged in, visit the dashboard page, which will display your AccountSid and your Auth Token. 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 will need them foryour Python program later. Install the software w^T 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 easyjnstall 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 sudo pip install -U pip.) Twilio authentication w w 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 the X with yours, as you will find on your dashboard: from twilio. rest import TwilioRestClient account.sid = “XXXXXXXXXXXXXXXXXXXXX” # Enter Yours auth_token = “XXXXXXXXXXXXXXXXXXXXX” # Enter Yours client = TwilioRestClient(account_sid, auth_ token) Create your message w w 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 to enter the phrase that you want to send to the mobile phone. When the program runs, this is the message that will be sent: message = raw_input(“Please enter your message”) Developer Tods Upgrade you r account b‘figrtr GfifitH&lGi Vtiilf COG# for Til 6 TttiM API Above You will be able to find your AccountSid and your Auth Token on the Twilio dashboard Twilio enables you to send SMS messages for free Bl Add your numbers w # 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 ‘Call the Sandbox number’. Change the Sandbox number to your country location and remember to add the international country code. message = client. messages. create(to=“+44Y0URM0BNUMBER”, from_=“+44Y0URTWILI0NUMBER”, body=message) OQ Send the message 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 Raspberry Pi! REST print message. sid print “Your message is being sent” print “Check your phone!” Other API and codes 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, Java and Node.js (twilio.com/api). ■ REST stands for Representational State Transfer. (It is sometimes spelt “ReST”.) It relies on a stateless, client- server, cacheable communications protocol -and in virtually all cases, the HTTP protocol is used. REST is an architecture style for designing networked applications. The Python Book 169 Build a complex LED matrix LED Matrix display systems find use everywhere from gaudy kebab shops to impressive steam punk-styled systems 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 ratherthan Python. Follow along using the code listing annos. Think about LEDs 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 - driving 1 6 diodes ties up 1 6 pins. Arrange your diodes 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) A 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 corresponding pins high and low. 170 The Python Book Use Python with Pi J Our LED model has a total of 32 inputs, which overwhelms older versions of the RPi Figure A BL-M15BFF1 (BL-M15AFF1 CC-) IE i COL 1 » * > < f s < r > j p 4 4 4 4 » ( f > $ 4 ) 1 Li. 1 < f O' r > < f t 1 i « r I 1 M 1 1 > < 1 + 1 4 5 n • 9 4 e f 4 r 4 4 f f f f f r f f r ft X a f x [f x t Lc ;t X 4 f 4 X T f t X X X X r X x X 4‘ 4 r r r :f 41 f i f f r r .F f ,r * f 4 r 4 4 f i X * 4 f 4' f f f r f f tf f f r X li it f 4 fl 4 i f i * i 4 r r I f f f t 4 4 4 4 f f * 4 ( 4 ft f 4 r i 4 * 4 * * j .1 * + i 4' Jr r 4T f f f f i if f r r r 47 jr t* T 4 r t 4 f 4 4 4 * 4 4 f 4 4 f f f ► fl S a C 4 f r :r f f r r t* 4 c f 4 fl 4 4 If 4 f f 4 f’ * f f i X X * r X r X X 4 f L X X if X X X X X X X 1 X 4 r 4 X X Full code listing Above The extended version of this schematic is inside FileSilo.co.uk - just sign in and download Harness the MUX wO 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 described in the Figure B image on the next page. Separate concerns 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. Round them up ww 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 1C is active. The 74HC244 ensures that but one of the two groups is active. Configure the pins 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 1C. This could lead to internal shorting if the output is not turned off. Step 12 Step 07 #include "CoemmapGpio . h" #include #include #define PINAO 2 // 3 #define PINA1 3 // 5 #define PINA2 4 // 7 #define PINA3 14// 8 #define PINCSO 17// 11 #define PINCS1 18// 12 #define PINDO 23// 16 #define PIND1 24// 18 #define PIND2 10// 19 #define PIND3 9 // 21 #define PIND4 25// 22 #define PIND5 11// 23 #define PIND6 8 // 24 #define PIND7 7 // 26 d setAddress(uE { (_which&l ) { _where->writePinHigh ( PINAO ) ; } else { _where->writePinLow( PINAO ) ; } (_which&2 ) { _where->writePinHigh ( PINA1 ) ; } else { _where->writePinLow(PINAl ) ; } which, mmapGpio' _where) (_which&4 ) { _where->writePinHigh ( PINA2 ) ; } else { _where->writePinLow(PINA2 ) ; } (_which&8) { _where->writePinHigh ( PINA3 ) ; } else { _where->writePinLow(PINA3 ) ; } } I saf elySetRow ( un _which, mmapGpio* _where) { _where->writePinHigh(PINCSO) ; _where->writePinHigh(PINCSl) ; if (_which==0 ) { _where->writePinLow(PINCSO) ; } else { The Python Book 171 Use Python with Pi Power the MUX w/ Create a convenience function taking an address ranging from zero to 1 5. 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. Full code listing _where->writePinLow(PINCSl) ; Step 11 d setData(un (_which&l) _which, mmapGpio* _where) _where->writePinHigh ( PINDO ) ; _where->writePinLow(PINDO ) ; (_which&2 ) _where->writePinHigh ( PIND1 ) ; _where->writePinLow(PINDl) ; (_which&4 ) _where->writePinHigh ( PIND2 ) ; AQ Select a row In the74HC244, we first disable both units and proceed to turning on the one which is needed. This sequence prevents ghosting during the switching process. OQ Dothemainl °°p ww 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 the building blocks described in the next step. Complete the loop I w 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’ the viewer’s eyes. Digital LED matrices like this one give you far more control over each individual ‘pixel’ in the display { } { } { } { } _where->writePinLow(PIND2 ) ; (_which&8) _where->writePinHigh ( PIND3 ) ; _where->writePinLow(PIND3 ) ; (_which&16) _where->writePinHigh ( PIND4 ) ; { } { } { } { } { } { } _where->writePinLow(PIND4) ; (_which&32 ) _where->writePinHigh ( PIND5 ) ; _where->writePinLow(PIND5 ) ; (_which&64) _where->writePinHigh ( PIND6 ) ; _where->writePinLow(PIND6) ; (_which&128) _where->writePinHigh ( PIND7 ) ; LED stripes Two versions of LED strips are offered. ‘Primitive’ ones are based on analogue technology. In it, an entire strip of diodes has the colour set bythethree input pins. Systems such as the mega-display shown in the left- hand image require the use of the digital version. They are based on the concept of the shift register. Your system inputs individual colour values which are then pushed on along the strip. 172 The Python Book Use Python with Pi Above This is the full schematic of the LED matrix that we’re working with here (you can also view it at its full size on FileSIlo) main( ) { mmapGpio rpiGpio; //Set outputs rpiGpio . setPinDir ( PINAO , mmapGpio : : OUTPUT ) ; rpiGpio . setPinDir ( PINA1 , mmapGpio : : OUTPUT ) ; rpiGpio . setPinDir ( PINA2 , mmapGpio : : OUTPUT ) ; rpiGpio . setPinDir ( PINA3 , mmapGpio : : OUTPUT ) ; / /TURN OFF ASAP! rpiGpio . setPinDir ( PINCSO , mmapGpio : : OUTPUT ) ; rpiGpio . writePinHigh ( PINCSO ) ; //TURN OFF ASAP! rpiGpio . setPinDir ( PINCS 1 , mmapGpio OUTPUT ) ; rpiGpio .writePinHigh ( PINCS 1 ) ; rpiGpio . setPinDir ( PINDO , 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 s : OUTPUT ) ; rpiGpio . setPinDir ( PIND6 , mmapGpio : : OUTPUT ) ; rpiGpio . setPinDir ( PIND7 , mmapGpio OUTPUT ) ; Energy control I LEDs light up if current flows through them. SetData pulls the pins of the 74HC244 low to ensure that the energy supplied from the 74HC238 can flowthrough the diode. Avoid GPIO trouble I ^ 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 correspond to the logical GPIO numbers. Add example data 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. Kick it off l“T 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. Notice a flicker I w 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. Step 13 Step 09 Step 10 dataStore [ 2 ] [ 1 6 ] ; Dr(int j=0;j<2;j++) { Dr(int k=0;k<16;k++) { datastore [ j ] [ k ] ^6 4 ; } } blockCounter=0 ; it rowCounter=0 ; le(l) { blockCounter++ ; ( blockCounter== L 6 ) { ( rowCounter == 0 ) { blockCounter=0 ; rowCounter= L ; } else { blockCounter=0 ; rowCounter=0 ; } } saf elySetRow ( rowCounter , &rpiGpio ) ; setAddress ( blockCounter , &rpiGpio ) ; setData ( datastore [ rowCounter ] [ blockCounter ] , &rpiGpio ) ; usleep(50) ; } 0 ; } The Python Book 1 73 Enjoyed this book? Exclusive offer for new *This offer entitles new UK direct debit subscribers to receive their first three issues for £5. After these issues, subscribers will then pay £25.1 5 every six issues. Subscribers can cancel this subscription at any time. New subscriptions will start from the next available issue. Offer code ZGGZINE must be quoted to receive this special subscriptions price. Direct debit guarantee available on request. This offer will expire 31 January 201 7. **This is an US subscription offer. 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 offer expires 31 January 201 7. Dedicated to all things Linux Written for you Linux User & Developer is the only magazine dedicated to advanced users, developers & IT professionals In-depth guides & features Written by grass-roots developers and industry experts Free assets every issue Four of the hottest distros feature every month - log in to FileSilo, download and test them all! subscribers to. . . Try 3 issues for £5 in the UK* or just $7.85 per issue in the USA** (saving 54% off the newsstand price) For amazing offers please visit www.imaginesubs.co.uk/lud Quote code ZGGZINE Or telephone UK 0844 249 0282 + overseas +44 (0) 1 795 41 8 661 + Calls will cost 7p per minute plus your telephone company's access charge YOUR FREE RESOURCES Log in to filesilo.co.uk/bks-864 and download your great resources NOW! All the tutorial files you'll need PACKED WITH BRILLIANT DIGITAL CONTENT AVAILABLE ANY TIME, ON DEMAND Inspirational projects Hours of free video tutorials filesilo.co.uk/bks-864 YOUR BONUS RESOURCES ON FILESILO WITH THIS BOOKAZINE, FREE AND EXCLUSIVE FOR THE PYTHON BOOK READERS, YOU’LL FIND A WEALTH OF RESOURCES, INCLUDING THE FOLLOWING... • A walkthrough video on writing good- quality code with Python from the very beginning • A series of tutorials on making a PiSnake game with Raspberry Pi and Python • A guide to using GUI with GTK • Everything you need to complete the tutorials in this book and become a Python expert 176 The Python Book FILESILO - THE HOME OF PRO RESOURCES Discover your free online assets A rapidly growing library . Updated continually with cool resources i Lets you keepyourdownloadsorganised Browse and access your content from anywhere 3 No more torn disc pages to ruin your magazines No more broken discs Print subscribers get all the content Digital magazine owners get all the content too! Each issue's content is free with your magazine Secure online access to your free resources This is the new FileSilo site that replaces your disc. You’ll find it by visiting the link on the following page The first time you use FileSilo, you’ll need to register. After that, you can use your email address and password to log in The most popular downloads are shown in the carousel here, so check out what your fellow readers are enjoying If you’re looking for a particular type of content, like software or video tutorials, use the filters here to refine your search Whether it’s programming tutorials or video workshops, categories make it easy to identify the content you’re looking for See key details for each resource including number of views and downloads, and the community rating Find out more about our online stores, and useful FAQs, such as our cookie and privacy policies and contact details Discover our fantastic sister magazines and the wealth of content and information that they provide The Python Book 1 77 HOW TO USE EVERYTHING YOU NEED TO KNOW ABOUT ACCESSING YOUR NEW DIGITAL REPOSITORY To access FileSilo, please visit filesilo.co.uk/bks-864 /'"'Vi Followthe \J1 on-screen i nstructions to create an account with our secure FileSilo system, log in and unlock the bookazine by answering a simple question about it. You can now access the content for free at any time. /'"'VO Once you have logged in, you are free to explore the wealth of content available on FileSilo, from great video tutorials and online guides to superb downloadable resources. And the more bookazinesyou purchase, the more your instantly accessible collection of digital content will grow. pvQ You can access FileSilo on any desktop, tablet or smartphone device using any popular browser (such as Safari, Firefox or Google Chrome). However, we recommend that you use a desktopto download content, as you may not be able to download f i les to your phone ortablet. (~\/ 1 If you have any \J H- problems with accessing content on FileSilo, orwith the registration process, take a look at the FAQs online or email filesilohelp@ imagine-publishing.co.uk. i . - Making a PyGame - An introduction to Game Development dm how 10 make games with Raspberry Pi RASPBERRY PI Pygame Novel Bridge the gap between booki and by creating an mwactwe novel vitfeogafni r « n n C GUI With GTK am shows us how Ed NEED HELP WITH THE TUTORIALS? Having trouble with any of the techniques in this bookazine’s tutorials? Don’t know how to make the best use of your free resources? Want to have your work critiqued by those in the know? Then why not visit the Linux User & Developer and Imagine Bookazines Facebook pages for all your questions, concerns and qualms. There is a friendly community of fellow Linux and Open Source enthusiasts waiting to help you out, as well as regular posts and updates from the team behind Linux User & Developer magazine. Like us today and start chatting! facebook.com/lmagineBookazines facebook.com/LinuxUserUK 178 The Python Book The ultimate guide to coding with Python Get to grips with the basics Learn Python the right way and complete basic projects with our simple guides jl jH Put Python to work Supercharge your system and make life easier with handy coding tutorials Use Python with Raspberry Pi Work on any Raspberry Pi model using its officially recognised language
    {% block title %}{% 
endblock %}LUD Issues (2024)
    Top Articles
    Latest Posts
    Article information

    Author: Dong Thiel

    Last Updated:

    Views: 5503

    Rating: 4.9 / 5 (59 voted)

    Reviews: 90% of readers found this page helpful

    Author information

    Name: Dong Thiel

    Birthday: 2001-07-14

    Address: 2865 Kasha Unions, West Corrinne, AK 05708-1071

    Phone: +3512198379449

    Job: Design Planner

    Hobby: Graffiti, Foreign language learning, Gambling, Metalworking, Rowing, Sculling, Sewing

    Introduction: My name is Dong Thiel, I am a brainy, happy, tasty, lively, splendid, talented, cooperative person who loves writing and wants to share my knowledge and understanding with you.