Friday, November 30, 2012

Utopie Python (ou realite?)

Utopia

Imaginez un monde ou on peut programmer en Python, ou que l'on veut. Oui, ca existe pour iPad, Android, iPhone, PC, Unix, Mac, Linux etc. Mais il reste un domaine....

Navigateurs Web


Ca serait genial si on n'avait pas a utiliser javascript. On mettrait dans notre page web, quelque chose du genre:


<script type="text/python">
for i in range(10):
    log(i)


</script>

Plutôt que quelque chose du genre:
<script type="text/javascript"> try{$ns}catch($err){$ns={0:{}}}
document.line_num=1
$Iterable1 =$assign(iter(range(int(10))))
while(true){
try{
    var $Next=next($Iterable1)
    i =$assign($Next)
    document.line_num=2
    log(i)
}catch(err0){
        if(err0.name=="StopIteration"){break}
    else{throw err0}
}
} </script>

Oui, ca serait genial, pas de doute. Mais bon il faut arreter de rever a cette utopie, quoi!

Vraiment?

Visitons Brython. C'est quoi?

Le code javascript ci-dessus a ete généré a partir du code Python, par Brython, un Python pour Browser (Navigateur).

On telecharge brython.js ici: brython/downloads et comment l'utiliser ici.

Allez voir cette page (http://www.brython.info/test/test_clock.html), ou vous y verrez une horloge analogique. Faites un view source sur cette page. Oui, c'est bien un programme en Python.

Oui, je sais... ça laisse sans parole...

Thursday, November 29, 2012

Matplotlib without an X server

Short form, by running xvfb-run python script.py

In a more detailed answer, you need to install xvfb first:

$ sudo apt-get install xvfb

And then, supposing a script like this that we save as mpl.py:

from matplotlib.pyplot import *

plot([2,4,2,5,6,3,1])
savefig("graph.png")

We then run it:

$ xvfb-run python mpl.py
 There should be a file named graph.png that will look like:

 All from the console.

Wednesday, November 28, 2012

Python sur Raspberrypi 01

Comme promis, apprenez Python. Et bien sur, on commence au commencement...

Pour les experts, il n'y a pas grand chose a voir. Mais si vous voyez continuellement une référence a Python dans vos recherches sur le Raspberry Pi, et vous vous demandez c'est quoi l'histoire, alors bienvenue a notre série de tutoriels sur le langage de programmation Python.

Sites

Alors tout d'abord, le site officiel pour le langage, c'est: python.org

Mais pour les francophones, il y a deux liens plus facile a utiliser, car tout en français:

wiki.python.org en français

wikipython.flibuste.net

Livres


Si vous débutez, il y a beaucoup d'options de livres sur Python en français.

Si vous préférez le papier, sur Amazon.fr, j'ai vu par exemple:
"Apprendre à programmer avec Python 3" de Gérard Swinnen et "Apprenez à programmer en Python" de Vincent Le Goff, ainsi que "Programmation Python" de Tarek Ziadé et Stefan Richter. Ils sont tous disponibles également en format Kindle.

Je doit dire que je n'en ai lu aucun des trois. J'ai lu "Think Python: How to think like a computer scientist" qui est la base du livre de Swinnen et il était bien. Aussi si quelqu'un veut recommander un autre livre sur Python en français, laissez le nous savoir en commentaire. Il y a une liste avec d'autres suggestions ici.

Livres gratuits


'Domptage de serpents pour les enfants', que j'ai deja mentionne sur mon blog. Apprendre à programmer avec Python (Python 2 ou 3) par Gérard Swinnen, oui, le même que le livre en papier chez Amazon. 
. Dive Into Python traduit en francais. Et il y a une traduction en ligne de A byte of python.

Python, le programme


Donc, Python est un langage de programmation, mais c'est aussi un programme exécutable. Il est inclus avec le Raspberry Pi et avec la majorité des systèmes de type Unix (Solaris, OpenIndiana, Mac OS/X et les varietes de Linux ). Il peut aussi être installé sous Windows, iPhone, iPad, Android etc. Pour cet article, je vais me référer au programme python en l’écrivant en caractères gras.

On peut utiliser python de plusieures manieres.

Le premier mode

Premièrement, de façon interactive sur la ligne de commande:


pi@raspberrypi ~ $ python
Python 2.7.3rc2 (default, May  6 2012, 20:02:25) 
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 


L’interprète de commande python est maintenant en attente d'une commande, d'avoir quelque chose a faire. On va donc lui demander d'imprimer (print) les mots "Bonjour, monde!". De la même façon que j'ai besoin de borner les mots avec des guillemets pour être clair sur ce qui va être imprimé, en Python on doit délimiter une chaine de caractères (une phrase) par des guillemets droits simple (') ou double (") de par et d'autre de cette chaine:

pi@raspberrypi ~ $ python
Python 2.7.3rc2 (default, May  6 2012, 20:02:25) 
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> print "Bonjour, monde!"
Bonjour, monde!
>>>


python nous a répondu:
Bonjour, monde!

Vous pouvez l'essayer ici dans votre browser web: print "Bonjour,+monde!" (cliquer sur [ Forward >] pour faire executer le code)

Si notre chaine de caractères dépasse une ligne, alors il faudra utiliser ces guillemets droits simple ou double, trois fois de chaque cote ("""beaucoup de mots, donc ca risque d'etre multiligne""")


pi@raspberrypi ~ $ python
Python 2.7.3rc2 (default, May  6 2012, 20:02:25) 
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> print """
... Bonjour, monde!
... Je continue.
... J'ai fini!
... """

Bonjour, monde!
Je continue.
J'ai fini!



python nous a répondu:
Bonjour, monde!
Je continue.
J'ai fini!

Par contre, nous, on commence a peine! Il est aussi bon de mentionner qu'une chaine de caractères entre triple guillemets ("""chaine de caractères""") d'elle même, sans instruction, est ce que l'on appelle une docstring, une chaine de caractères (ou phrase) de documentation. Un commentaire, en d'autres termes.

On peut aussi utiliser python comme une calculatrice:


pi@raspberrypi ~ $ python
Python 2.7.3rc2 (default, May  6 2012, 20:02:25) 
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 2 * 32
64


Pour quitter le programme python, on doit taper la commande: exit()

Le deuxième mode


Si notre code est bien court, il n'y a pas de problème a utiliser ce mode interactif. Mais, ça devient un peu lassant de réécrire le même code a chaque fois. On peut donc sauver notre code dans un fichier, auquel on donnera une extension .py et que l'on peut lancer encore et encore.

On va donc sauver notre programme qui imprime "bonjour, monde!" dans un fichier. Pour cela toutefois, il faut un éditeur de fichier. Pour les experts, je recommande scribes en mode graphique et vim en mode console (texte).

Comme on débute, je recommande plutôt d'installer geany:

 pi@raspberrypi ~ $ sudo apt-get install geany
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following extra packages will be installed:
  geany-common
Suggested packages:
  doc-base
The following NEW packages will be installed:
  geany geany-common
0 upgraded, 2 newly installed, 0 to remove and 134 not upgraded.
Need to get 3,401 kB of archives.
After this operation, 8,682 kB of additional disk space will be used.
Do you want to continue [Y/n]? y
Get:1 http://mirrordirector.raspbian.org/raspbian/ wheezy/main
 geany-common all 1.22+dfsg-2 [2,336 kB]
Get:2 http://mirrordirector.raspbian.org/raspbian/ wheezy/main
 geany armhf 1.22+dfsg-2 [1,065 kB]
Fetched 3,401 kB in 6s (518 kB/s)                                              
Selecting previously unselected package geany-common.
(Reading database ... 91199 files and directories currently installed.)
Unpacking geany-common (from .../geany-common_1.22+dfsg-2_all.deb) ...
Selecting previously unselected package geany.
Unpacking geany (from .../geany_1.22+dfsg-2_armhf.deb) ...
Processing triggers for hicolor-icon-theme ...
Processing triggers for man-db ...
Processing triggers for menu ...
Processing triggers for desktop-file-utils ...
Setting up geany-common (1.22+dfsg-2) ...
Setting up geany (1.22+dfsg-2) ...
Processing triggers for menu ...
pi@raspberrypi ~ $ geany bonjour.py


On vient donc de lancer notre editeur. On tape notre code dans la fenetre bonjour.py:


Editeur de code Geany
On sauve, et on quitte (pour le moment, question de rester le plus simple possible).

Comment exécuter notre programme bonjour.py? Tres simplement:


pi@raspberrypi ~ $ python bonjour.py
Bonjour, monde!
pi@raspberrypi ~ $ 

C'est le deuxième mode d’opération, python exécutant un script.

Le troisième mode


Le troisième mode est disponible sur les ordinateurs de type Unix, et donc le Raspberry Pi. On relance notre éditeur geany:

pi@raspberrypi ~ $ geany bonjour.py 

On insere une nouvelle ligne 1
On sauve, et on quitte. La ligne que l'on vient d'ajouter indique quel programme va exécuter notre code. Ici, on indique python.

Mais, il faut aussi changer notre fichier du mode document, au mode executable, par l'entremise de la commande chmod:

pi@raspberrypi ~ $ chmod +x bonjour.py

On est maintenant prêt a lancer notre script:

pi@raspberrypi ~ $ ./bonjour.py
Bonjour, monde!
pi@raspberrypi ~ $

Par l'entremise de geany


Cela serait bien mieux si on pouvait rester dans notre éditeur, changer le code, l’exécuter, voir le résultat, continuer l’édition, l’exécuter, voir le résultat etc, sans avoir a quitter geany a chaque fois. On peut faire cela grace a la touche F5, ou du menu Build->Execute, ou encore le bouton avec les engrenages (View or Execute):

Bonjour, monde!
Ceci conclut notre premier tutoriel sur le langage Python. J’espère que cela vous aide.

Tuesday, November 27, 2012

Minecraft + Python + Raspberry Pi =



>>> Minecraft Raspberry Pi Edition (raspberrypi.org)

Python


#!/usr/bin/env python
import minecraft as mc

mc.connect()

for x in range(-10,10):
    for y in range(-10,1):
        for z in range(-10,10):
            mc.setblock(x, y, z, mc.GLASS)

Minecraft + Python + Raspberry Pi =

Updating a local mercurial project

In the previous article, we cloned the pyptug repository from bitbucket. But what if the repository changes (and it did, this morning)?

Follow me


First, make sure you have signed up to bitbucket and follow the repository ( https://bitbucket.org/fdion/pyptug ). That way you will get an email notification when there is a change.

Solid Hg


Next, you need to go into the pyptug directory and issue:

 fdion@srv:~/bitbucket$ cd pyptug/  
 fdion@srv:~/bitbucket/pyptug$ hg incoming http://bitbucket.org/fdion/pyptug  
 warning: bitbucket.org certificate with fingerprint 24:9c:45:8b:9c:aa:ba:55:4e:01:6d:58:ff:e4:28:7d:2a:14:ae:3b not verified (check hostfingerprints or web.cacerts config setting)  
 real URL is https://bitbucket.org/fdion/pyptug  
 comparing with http://bitbucket.org/fdion/pyptug  
 searching for changes  
 changeset:  1:8e0c4e63bbb1  
 user:    fdion@bitbucket.org  
 date:    Mon Nov 26 09:52:46 2012 -0500  
 summary:   pep8 compliance  
 changeset:  2:638251f3f511  
 user:    fdion@bitbucket.org  
 date:    Mon Nov 26 10:39:57 2012 -0500  
 summary:   pep8 and fo3 fix  
 changeset:  3:b09297ef88b2  
 user:    fdion@bitbucket.org  
 date:    Mon Nov 26 10:43:09 2012 -0500  
 summary:   pep8 compliance  
 changeset:  4:9e0101cf03f8  
 user:    fdion@bitbucket.org  
 date:    Mon Nov 26 10:45:04 2012 -0500  
 summary:   pep8 compliance  
 changeset:  5:89aa2b01e1be  
 tag:     tip  
 user:    fdion@bitbucket.org  
 date:    Tue Nov 27 06:24:25 2012 -0500  
 summary:   Missing if __name__ ==  
 fdion@srv:~/bitbucket/pyptug$ hg pull http://bitbucket.org/fdion/pyptug  
 warning: bitbucket.org certificate with fingerprint 24:9c:45:8b:9c:aa:ba:55:4e:01:6d:58:ff:e4:28:7d:2a:14:ae:3b not verified (check hostfingerprints or web.cacerts config setting)  
 real URL is https://bitbucket.org/fdion/pyptug  
 pulling from http://bitbucket.org/fdion/pyptug  
 searching for changes  
 adding changesets  
 adding manifests  
 adding file changes  
 added 5 changesets with 9 changes to 9 files  
 (run 'hg update' to get a working copy)  
 fdion@srv:~/bitbucket/pyptug$ hg update  
 9 files updated, 0 files merged, 0 files removed, 0 files unresolved  

The hg incoming will basically tell us what the changes are, but will not download them. This is purely informational, you can skip that step if you want.

The hg pull command is what gets the changes to the pyptug repository pulled to your local machine.

Finally, hg update will apply those changes to a working copy. In the above example, 9 files have been changed over 5 changesets since I first published the repository.

Chchch...changes


If you are curious as to what was the change:

When running a web.py application through wsgi, and that means not only mod_wsgi or an external wsgi server, but also when running as python script.py [port] (since it will launch CherryPy), it is required to use:

if __name__ == "__main__":
to invoke app.run(). Else, it will lock itself by trying to open a socket on the same port twice.

hw2.py and hw3.py didn't have this. Although it did work in my automatic deployment test setup (uses cgi instead of wsgi so I can test every applications all at once), it didn't work in a standalone install.


Sunday, November 25, 2012

PYPTUG mercurial repository

A little experiment


web.py, the antiframework framework
I'm putting the code up before the actual talk (Monday, the 26th of November), for those who would like to follow along with their laptop. The code again is made available under an MIT license.

Assuming you already have mercurial:

$ hg clone https://bitbucket.org/fdion/pyptug

If you need to install mercurial first:

debian based
$ sudo apt-get install mercurial

or fedora
$ sudo yum install mercurial

Under solaris, it is available under the packagemanager. On windows or mac you can get it here: http://mercurial.selenic.com/

All those .py scripts and directories will make more sense during the talk, as we dig through them.

I will also post screencam captures later, for those who are not local.

Saturday, November 24, 2012

It's more fun to compute

last track  - "It's more fun to compute"


Ah, Computer World, by Kraftwerk. The last track is titled "It's more fun to compute". I'm thinking Hutter, Schneider and Bartos must have written this song in November, after raking and blowing leaves for a few days... All joking aside, I hope you have this album on your ipod.

So what is it exactly that makes computing more fun? More fun than what, I hear you ask... What do you think, is it more fun to compute?

Also, either tomorrow or Monday, I'll post the bitbucket Mercurial repository for the web.py presentation at PYPTUG that I'll be doing on Monday. In the interim, go ahead and install web.py. I have the instructions on the sidekick page. Basically:

pi@raspberrypi ~/Desktop $ sudo easy_install web.py
Searching for web.py
Best match: web.py 0.37
Processing web.py-0.37-py2.7.egg
web.py 0.37 is already the active version in easy-install.pth

Using /usr/local/lib/python2.7/dist-packages/web.py-0.37-py2.7.egg
Processing dependencies for web.py
Finished processing dependencies for web.py


If you've gone through the PyHack workshop #01, you already installed mercurial (hg). If not, you will need to install it on your Raspberry Pi before you can get the code:


pi@raspberrypi ~/Desktop $ sudo apt-get install mercurial

Friday, November 23, 2012

Python: de débutant a expert

Série de tutoriels 

 Je vais commencer une série de tutoriels pour Python, en français. On va couvrir l'ABC du langage. Python est parfait comme premier langage de programmation, car il est relativement facile a apprendre pour faire ce que l'on veut. De plus, c'est un langage sérieux qui est partout en industrie.

Donc, non seulement on peut s'amuser, mais on peut être payés pour cela!

Débutants

On va commencer bien sur par la sélection d'un éditeur. Comme la majorité des lecteurs vont faire les tutoriels sur leur Raspberry Pi, on va surtout parler de geany. Mais je vais aussi mentionner d'autres en mode graphique, et aussi quelques configurations pour vim, pour ceux qui veulent rester en mode texte, ou qui préfèrent la console.

Intermédiaireset experts

Pour le moment, je vous conseille les articles du blog sur Python qui sont en anglais, et de suivre en simultané avec google translate.

Je vais introduire de temps a autres, un thème pour intermédiaires et experts. Par exemple, aujourd'hui, je vous laisse sur un sujet intéressant, mais pas pour débutants. René Bastien, a écrit un module pour faire de la composition de musique électronique. Un lien, une présentation de PyConFR et un module:

site officiel de Pythoneon

Présentation PyConFR 2012: PDF

L'installation de pythoneon (il faut avoir easy_install au minimum):
$ sudo easy_install pythoneon
 ou
$ sudo pip install pythoneon
Je vous conseille de voir les diapos de PyConFR (le PDF).

Je vous laisse vous amuser avec cela, et on y revient plus tard. La musique, c'est un autre de mes domaines d'expertise.


Tuesday, November 20, 2012

Fibovisual

Fibonacci


We started this series (no pun intended) by providing a simple Python script that gave us the Fibonacci numbers less than 50.

As previously mentionned, the code was (save as fibonacci.py):

a, b = 0, 1
print a,
while b < 50:
    print b,
    a, b = b, a + b

We would then run it as:
$ python fibo.py

Fibospeak


We then added a way to speak those same numbers (save as fibospeak.py):


import os

def say(something):
    os.system('espeak -ven+f2 "{0}"'.format(something))

a, b = 0, 1
say(a)
while b < 50:
    say(b)
    a, b = b, a+b

Everything below the line in the middle of the code is pretty much the same, except we use say() instead of print. We defined say() in the first part of our code.

As part of our original challenge, we offered an alternate challenge, for those who are more visual:

Fibocairo


We will be using pycairo. We will visualize the Fibonacci numbers, but without using actual numbers. We will draw big dots instead.

There is some basic setup to do before we can use it to, say, draw a line, or save an image. We wont go into all the details, there are some good tutorials on the pycairo site. But just to get an idea here is an excerpt:

import cairo

WIDTH = 350
HEIGHT = 450

surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, WIDTH, HEIGHT)  
ctx = cairo.Context(surface)  
ctx.set_line_width(DOTSIZE)  
ctx.set_source_rgb(1, 0, 0) # red  

We create a surface (which we will later save as a PNG) that is 350 by 450 (we use constants to define those, ALLCAPS is the standard for constants in Python) then create a context from that surface. We could scale it, as is demoed in the tutorial on the pycairo site, but we will stay with pixels for right now so we dont make things more complicated than they need to be.

We will replace our say() function from our previous example, by a new one, which we will call drawdots().

DOTSIZE = 3


def drawdots(ctx, qty):  
    """ generate a vertical sequence of qty number of dots """  
    ctx.save() # save our position before doing anything  
    ctx.set_line_width(DOTSIZE)  
    ctx.set_source_rgb(1, 0, 0) # RED color  
    for _ in xrange(qty):  
        ctx.rectangle(-DOTSIZE, -DOTSIZE, DOTSIZE, DOTSIZE)  
        ctx.stroke()  
        ctx.translate(0, DOTSIZE * 4) # move down 4 times dotsize  
    ctx.restore() # restore our position  

We are passing the context to this function, so we can do stuff to it. First, we save it (so we can restore it at the end). We set the line width to 3 and the color to red (1,0,0 in RGB). We are ready to do the work, we will loop for the Fibonacci number that is passed (qty).

Note: we didn't do a for i in xrange() because we don't need an actual value. The "standard" way to discard a variable in a loop like this is to use the underscore (_). It keeps pylint happy instead of complaining about a variable that is defined, but not used. It is as if I was saying for whatever in xrange(). Just like in the spoken language when somebody replies "whatever!", it implies whatever, i don't care. The same applies here.

Inside the loop, I am preparing a small rectangle (a square, really), painting it and moving down on my surface with ctx.translate().

Let us now move on to the last part, the main loop we have covered several times by now:

   a, b = 0, 1  
   ctx.translate(30, 30) # for item 0, skip over some space  
   while b < 50:  
     ctx.translate(30, 0)  
     drawdots(ctx, b)  
     a, b = b, a + b  

All that we have changed is that instead of printing, we translate (move) and drawdots.

Putting it all together (save as fibocairo.py):

#!/usr/bin/env python
""" Representing Fibonacci numbers visually, as big dots """

import cairo

# hardwired limit
LIMIT = 50
DOTSIZE = 3
# These work for the current limits.
# You will need to adjust these if the above are changed.
# It is not linear and depend in both x and y on Fibonacci numbers.
WIDTH = 350
HEIGHT = 450


def drawdots(ctx, qty):
    """ generate a vertical sequence of qty number of dots """
    ctx.save() # save our position before doing anything
    ctx.set_line_width(DOTSIZE)
    ctx.set_source_rgb(1, 0, 0) # RED color
    for _ in xrange(qty):
        ctx.rectangle(-DOTSIZE, -DOTSIZE, DOTSIZE, DOTSIZE)
        ctx.stroke()
        ctx.translate(0, DOTSIZE * 4) # move down 4 times dotsize
    ctx.restore() # restore our position


def main():
    """ our main loop to generate a PNG file with a """
    # here, we are just setting up cairo
    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, WIDTH, HEIGHT)
    ctx = cairo.Context(surface)

    # where the actual work is done
    a, b = 0, 1
    ctx.translate(30, 30) # for item 0, skip over some space
    while b < 50:
        ctx.translate(30, 0)
        drawdots(ctx, b)
        a, b = b, a + b

    surface.write_to_png('fibocairo.png') # done, write to png
 
if __name__ == "__main__":
    main()

The line:
   surface.write_to_png('fibocairo.png') # done, write to png

is where we save the surface to a png file. There are other formats supported by pycairo.

Visual?


I did say it would be visual, didn't I. The last line of the main() function saves the surface to fibocairo.png, and this is how it looks:




Perhaps it is not as impressive as the code looks...

Fibotangle


So let us try drawing rectangles that we will rotate around a point, as a visual representation. Perhaps this will look a little bit more interesting...

Instead of a drawdots() function we will write a draw(). We are not drawing dots, nor any other typical shape here, so we'll just leave it a generic draw().

 def draw(ctx, fibo):  
   """ a rotational shape based on fibo number of rectangles """  
   for i in xrange(fibo):  
     ctx.save()  
     ctx.rotate(i * 2 * math.pi / fibo)  
     ctx.rectangle(-20, -50, 20, 50)  
     ctx.stroke()  
     ctx.restore()  

The main loop that we've covered in the past few examples does have to change now. The shapes are much bigger and we need to start a new line everytime we've drawn 5 shapes.

 
   # where the actual work is done  
   a, b = 0, 1  
   ctx.translate(0, 120)  
   ctx.save()  
   ctx.translate(120, 0) # for item 0, skip over some space  
   count = 0  
   while b < 50:  
     count += 1  
     if count == 5:  
       ctx.restore()  
       ctx.translate(0, 120)  
       ctx.save()  
       count = 0  
     ctx.translate(120, 0)  
     draw(ctx, b)  
     a, b = b, a + b  

We've added a counter, we increment it in the loop. We could have used a modulo instead of a counter and comparison to 5, but the logic would have been more obtuse. We will cover an example of that in the future, however.

Putting it all together, we get (save to fibotangle.py):

 #!/usr/bin/env python  
 """ Representing Fibonacci numbers visually, as rotated rectangles """  
   
 import cairo  
 import math  
   
 # hardwired limit  
 LIMIT = 50  
   
 WIDTH = 720  
 HEIGHT = 320  
   
   
 def draw(ctx, fibo):  
   """ a rotational shape based on fibo number of rectangles """  
   for i in xrange(fibo):  
     ctx.save()  
     ctx.rotate(i * 2 * math.pi / fibo)  
     ctx.rectangle(-20, -50, 20, 50)  
     ctx.stroke()  
     ctx.restore()  
   
   
 def main():  
   """ our main loop to generate a PNG file with a """  
   # here, we are just setting up cairo  
   surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, WIDTH, HEIGHT)  
   ctx = cairo.Context(surface)  
   ctx.set_line_width(3)  
   
   # where the actual work is done  
   a, b = 0, 1  
   ctx.translate(0, 120)  
   ctx.save()  
   ctx.translate(120, 0) # for item 0, skip over some space  
   count = 0  
   while b < 50:  
     count += 1  
     if count == 5:  
       ctx.restore()  
       ctx.translate(0, 120)  
       ctx.save()  
       count = 0  
     ctx.translate(120, 0)  
     draw(ctx, b)  
     a, b = b, a + b  
   
   surface.write_to_png('fibotangles.png') # done, write to png  
   
 if __name__ == "__main__":  
   main()  

And what does it look like?


This is way more interesting. The complexity level just adds something that is much more satisfying to the eye, than just a series of dots, or numbers.

Showing this around, I usually get a comment that the last three look like flowers, perhaps a rose, a daisy and a sunflower. Interestingly, many daisies and sunflowers have 34 petals (our last shape) and 55, 89 and 144 are all possible counts of florets on a sunflower, all Fibonacci numbers. It is a fact that many flowers have a number of petals from the Fibonacci sequence. It is not a coincidence, it is tied to the golden angle, spirals and phyllotaxis.

Anyway, just mentioning this as an interesting anecdote, as the answer to where you can find the Fibonacci numbers around us (question #2 of the original challenge), and to introduce our next math concept. There is math all around us in nature.

It is in the title


When I concluded the first article on Fibonacci numbers, I had a section with this title:
Bucolic Mix

And we were talking (literally) about Fibonacci numbers. Bucolic Mix was one remix that Moby did of a Brian Eno song. One reader thought I was perhaps making a reference to Moby's 257 zero song (using recordings from numbers stations - that's a story for another post).

Good guess, but I was simply making a reference to the title of that Brian Eno song:

Fractal Zoom (Bucholic Mix)

Yes, we are about to talk about Fractals. Order in chaos, you know: On fractals (next article)

Pregunta para los lectores

Popular 

Reflejando en los artículos lo mas populares (en cuanto a los países hispanohablantes), no estoy seguro de lo que mis lectores esperan o buscan.

Los artículos mas populares en España, México y América Latina:
  1. XBMC (en ingles)
  2. Systema embebido (mi oficina móvil)
  3. PiQuizMachine (en ingles)
  4. RISC OS
  5. raspberry-python.blogspot.com

Es posible ver todos los artículos en español con el botón ES, arriba.

El numero 3 (y 4 indirectamente) fue en la pagina principal del sitio raspberrypi.org, y así no es sorprendente. 1 y 2 hablan ambos de vídeo. 5 cambia a cada día...

Aunque me parece que hay mucho interés en una utilización del Raspberry Pi para ver vídeos, y quizás con artículos que hablan de cosas como VLC, MPlayer y otros similares, es difícil a saber mas.

¿Python? ¿Electrónica? ¿Robótica? ¿Automatización? ¿Gráficos 3d? ¿Ordenadores Retro? ¿Otras cosas? ¿Mas detalles sobre un articulo existente? ¿Version en espanol de un articulo existente en otro lenguaje?

Digame

Dejar un comentario, por favor.

Monday, November 19, 2012

Fibospeak

espeak

Speaking (of) the Fibonacci numbers, did you figure it out?

It is actually quite simple to use espeak. On the Raspberry Pi, we cant use directly the espeak module in Python. We thus have to call the espeak application through the os module.

 import os  

 def say(something):  
   os.system('espeak -ven+f2 "{0}"'.format(something))

 a, b = 0, 1  
 say(a)  
 while b < 50:  
     say(b)  
     a, b = b, a+b  

And that is basically it!

Variations

If we wanted the numbers read in spanish:
   os.system('espeak -ves+f2 "{0}"'.format(something))  

Or in French, male, female and Portuguese, male female:
   os.system('espeak -vfr+m2 "{0}"'.format(something)) 
   os.system('espeak -vfr+f2 "{0}"'.format(something)) 
   os.system('espeak -vpt+m2 "{0}"'.format(something)) 
   os.system('espeak -vpt+f2 "{0}"'.format(something)) 

Under Linux, on a PC with a properly functioning espeak-python module, it would bea little different. After importing espeak, instead of using my function say(), we would use:

   espeak.synth(str(s))

Other options

On the raspberry pi forum, someone pointed to a different approach, which is to use the google translate web service.

Under Linux, but not on the Raspberry Pi (it just clicks instead of speaking), you could also use the Speech Dispatcher server (speechd, python-dispatcher).

Still unresolved

I'll follow up with the visual version tomorrow. There is still time to leave a solution in a comment.

See the next part: Fibovisual

"Fab"oard Deux (2)

On continue avec le thème de la planche a découper. Je vous présente le "Fab"oard Deux.

Comme je l'indiquais pour le "Fab"oard Un, c'est parfait pour les démo en public, a l’école, pour les conférences, les groupes d'usagers, les hackerspaces etc


boitier 3d et vis en nylon
Je vous présente plein d'images, dans le désordre.

On a plein d'espace

boitier avec couvercle et cable fait maison
Module d'expansion fait main, sur support
Le gertboard sur support


Sunday, November 18, 2012

Python user group meeting



PYPTUG (PYthon Piedmont Triad User Group, covering the Greensboro, High Point and Winston Salem NC area is meeting on Monday November 26th in Kernersville:


PYPTUG meeting

All level of (in)experience welcome!

Saturday, November 17, 2012

Python Speak Hint

A hint for our current python challenge:

$ sudo apt-get install espeak

or

import pycairo

Depending which challenge you picked... The answer will probably vary if you try it under Raspbian versus Debian, it is all in the library.

Friday, November 16, 2012

LuzBox

Bluetooth?


Desde el verano, fue a buscar una manera de conectar sin hilos a mi LuzBox lo mas barato posible. Finalmente, he recibido un módulo Bluetooth que opera con el Arduino Uno. O, al menos, eso es lo que pensé.


Todo bien de este lado...
Sólo he recibido una placa sin chips...

!Ay!

Por eso, estoy de vuelta a tratar de encontrar un módulo bluetooth muy barato que conecta a un Arduino. ¿Porque? Para hacer la programación y ajustar los horarios de mi LuzBox.

LuzBox


El nombre es muy obvio cuando uno ve la LuzBox en operación. Es un prototipo para experimentar con control de aparatos eléctricos. La LuzBox puede hacer igual que un ser humano, pero miles de veces a cada día, sin olvidar, sin descansar, y mucho mas rápido.

Por ejemplo, mi LuzBox esta siguiendo un programa en secuencia, demostrado aquí con lámparas de noche:


La LuzBox tiene cierta independencia por que el horario se ajusta con el tiempo y con sensores (temperatura, sonido y voy a añadir otros), pero, a cada vez que hay de cambiar el horario, la secuencia o el modo de operación, es necesario conectar el Raspberry Pi a la LuzBox (Arduino) por medio de USB:

Funciona, pero no tan conveniente...

Y es la razón por que estoy buscando un modulo Bluetooth que opera con el Arduino. Pero debe ser muy barato. Sino, un Raspberry Pi modelo A o B con dongle wifi es mucho mas barato que un Arduino + Bluetooth (o wifi).

Mientras tanto, pensé que les gustaría ver las entrañas de la LuzBox.

Componentes


Empecé con un Arduino Uno (eventualmente voy a utilizar mi propio diseño para reducir el costo.

El Arduino Uno, opera independientemente del Pi
Una modificación: alimentación re-ubicada atrás

Fue necesario a re-ubicar la alimentación atrás, porque solo la conexión USB es accesible afuera de la LuzBox:

montaje empotrado
Mucho espacio en una caja eléctrica
con un "shield" de control de 4 canales
Al punto de poner todo en la caja


En la caja, hay el Arduino, el "shield", los sensores, una alimentación de 12V, receptáculos eléctricos 110V, un disyuntor y una cantidad de cable eléctrico.


Casi finalizado

Falta 6 tornillos

En cuanto a hacer la programmacion de un Arduino con un Raspberry Pi, es necessario hacer ciertas operaciones. Al minimo:


pi@raspberrypi ~ $ sudo usermod -a -G dialout fdion
pi@raspberrypi ~ $ sudo apt-get install arduino
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following extra packages will be installed:
  arduino-core avr-libc avrdude binutils-avr ca-certificates-java default-jre
  default-jre-headless extra-xdg-menus gcc-avr icedtea-6-jre-cacao
  icedtea-netx icedtea-netx-common java-common libatk-wrapper-java
  libatk-wrapper-java-jni libftdi1 libjna-java libnspr4 libnss3 libnss3-1d
  librxtx-java openjdk-6-jre openjdk-6-jre-headless openjdk-6-jre-lib
  ttf-dejavu-extra tzdata-java
Suggested packages:
  arduino-mk avrdude-doc task-c-devel gcc-doc gcc-4.2 equivs libjna-java-doc
  icedtea-plugin libnss-mdns sun-java6-fonts fonts-ipafont-gothic
  fonts-ipafont-mincho ttf-wqy-microhei ttf-wqy-zenhei ttf-indic-fonts
Recommended packages:
  icedtea-6-jre-jamvm
The following NEW packages will be installed:
  arduino arduino-core avr-libc avrdude binutils-avr ca-certificates-java
  default-jre default-jre-headless extra-xdg-menus gcc-avr icedtea-6-jre-cacao
  icedtea-netx icedtea-netx-common java-common libatk-wrapper-java
  libatk-wrapper-java-jni libftdi1 libjna-java libnspr4 libnss3 libnss3-1d
  librxtx-java openjdk-6-jre openjdk-6-jre-headless openjdk-6-jre-lib
  ttf-dejavu-extra tzdata-java
0 upgraded, 27 newly installed, 0 to remove and 0 not upgraded.
Need to get 58.3 MB of archives.
After this operation, 173 MB of additional disk space will be used.
Do you want to continue [Y/n]? Y 


Después de eso, es lo mismo que hacer la programación inicial de un Arduino con Windows o una machina Linux.

Tengo un poco mas en este articulo en frances: framboise a la mode arduino.

Thursday, November 15, 2012

Fibonacci

When we say Fibonacci, some might think of the man, the mathematician, the Fibonacci numbers or even specifically of the following relation:



Or, if you watched (or read) David Mitchell's presentation on iPython at PyHack Workshop #01, you'll recall that it was part of it.

The code was:

 a, b = 0, 1  
 print a,  
 while b < 50:  
   print b,  
   a, b = b, a + b  

Incredibly simple, isn't it? The result of which is:

0 1 1 2 3 5 8 13 21 34


I added the print a, since mathematically speaking, the Fibonacci sequence starts at 0, but most of the time it is displayed starting at 1 (1, 1, 2, 3 etc). I also increased the upper boundary to 50 (it was 10 in the demo).

If you can't read the math, you can at least clearly see that any given member of the Fibonacci series is equal to the sum of the two preceding elements.

So, why am I talking about this?


I have two challenges for my readers. The first, is a Python programming challenge, while the other just requires some power of observation:

  1. modify the above code to have the numbers read (audio) instead of printed, or visualize them graphically, but not as numbers.
  2. look around. Where can you find occurrences of Fibonacci numbers

Bucolic Mix


You'll see where we are going with this on our next mathematical intrigue, perhaps this weekend... In the meantime, do post your answers to the challenge in our comments section below.


See the next step: Fibospeak

Wednesday, November 14, 2012

Fazer um cabo GPIO

Como fazer um cabo GPIO


Vamos fazer um cabo de interface com conectador GPIO. Este é o conector P1 do Raspberry Pi. Isto irá permitir-nos a ligar os motores, LEDs, botões e outros componentes físicos:


É um cabo de fita com 26 fios. Isto é semelhante a cabo IDE (ou ATA) de 40 vias para disco rígido (não ATA66/133 de 80 vias):

Cabo IDE/ATA original de 40 vias

Queremos 2 conectores, não 3. Com um cabo que tem três conectores, cortamos uma seção.

Corte com uma tesoura.

Com uma faca X-acto, vamos dividir o cabo em duas partes: uma de 26 vias e uma de 14 vias.


Marcar com uma caneta

dividir em duas partes

Fizemos um corte com uma serra (ou roda de corte de tipo Dremel).

conector serrada
A remoção da porção da direita

Nós terminamos o corte. Agora você pode conectar o cabo ao computador:



Conexão


A conexão é simples:

LED vermelho e LED verde, perna curta -> terceiro buraco no lado esquerdo.
LED vermelho, perna comprida -> segundo buraco no lado direito.
LED verde, perna comprida -> terceiro buraco no lado direito.



Codigo Python


#!/usr/bin/env python  
""" Setting up two pins for output """  
import RPi.GPIO as gpio  
import time  
PINR = 0 # this should be 2 on a V2 RPi  
PING = 1 # this should be 3 on a V2 RPi  
gpio.setmode(gpio.BCM) # broadcom mode  
gpio.setup(PINR, gpio.OUT) # set up red LED pin to OUTput  
gpio.setup(PING, gpio.OUT) # set up green LED pin to OUTput  
#Make the two LED flash on and off forever  
try:
    while True:  
        gpio.output(PINR, gpio.HIGH)  
        gpio.output(PING, gpio.LOW)  
        time.sleep(1)  
        gpio.output(PINR, gpio.LOW)  
        gpio.output(PING, gpio.HIGH)  
        time.sleep(1)
except KeyboardInterrupt:
    gpio.cleanup()

Coloque este código em um arquivo chamado flashled.py.

 

Em operação

Normalmente, é necessário proteger um LED com um resistor para limitar a amperagem, mas como se fez este de forma intermitente, para testar a cabo, não há nenhum risco.

Para uso prolongado, deve haver um resistor em série de 220 Ohm a 360 Ohm.
$ chmod +x flashled.py
$ sudo ./flashled.py
Control-C para interromper.

LED vermelho

LED verde


Monday, November 12, 2012

PiQuizMachine

This article continues documenting one of the PYPTUG Workshops: PyHack Workshop #01, and goes into writing the PiQuizMachine code.

The machine

The PiQuizMachine

The Circuit


Each button controller is made from 1/2" PVC parts and a momentary mini push button, connected by a wire to a board.

On the board, one 10K Ohm resistor pulls up the GPIO to high, while the push button is connected to the GPIO on one end and to ground through a 1K Ohm resistor on the other end.

 This last resistor is optional if you are certain you can avoid pressing the buttons whenever the GPIOs are configured as outputs instead of inputs...


This circuit is repeated for all 4 buttons

Source Code


Make sure you've installed Mercurial and pulled the code from bitbucket (as instructed in the previous article), and go into:

    $ cd fablocker/PyHack/workshop01
    $ cd trivia
    $ ls
    load.py  piquiz.py  piquizmachine.py  questions.txt  README.md short.txt

The load.py is the first piece of code we will review.

We have a text file (question.txt) with all the questions and answers for the quiz game. We generated this file using a python script to web-scrape the data from a few pages of triviachamp.com (you can also do this by hand, selecting all the questions on a screen, and copy-pasting into a text file).

There is also a shorter version, with 2 questions / answers. We will use that first to figure out how we will load them into memory in our program. Here is the content of short.txt:

 Louis Leterrier - This film was released in 2010.Who directed Clash of the Titans?  
   
     a. Rodrigo Garcia  
     b. Louis Leterrier  
     c. Joe Carnahan  
     d. Iain Softley  
   
   
 Chicago - This team is part of the NFL.If you wanted to see the Bears play football, which city would you need to visit?  
   
     a. Chicago  
     b. Houston  
     c. San Diego  
     d. Denver  
   

Wow... So what do we have here? We do not have the data nicely separated by a special delimiter or by a new line. We will have to figure out a way to ignore the blank lines, handle the slight variations and extract the data into various fields:

answer - trivia blah blah.question blah blah?
multiple choices of answers

The thing is, the trivia and question can have all kinds of punctuation marks such as comma, period, dash and varying amount of white spaces. Doing this by hand coding a function to do it would be a lot of work and quite boring.

There is something that can deal with this fairly easily. I'm referring again to Python's "batteries included", a library called re, for Regular Expressions.

Regular Expressions


Regular Expressions ( abbreviated as re, regex or regexp), is a language designed to create simple to complex matching against data ( a string or a file). It is about as much the opposite to Python as can be, as it is dense, hard to read and hard to debug.

But sometimes, it is the perfect solution to a problem, and it is fairly easy to use in the Python implementation (a full implementation on Python, just like in Perl - in some other languages, it is a partial implementation, and can be hard to use or at least way more involved).

At any rate, if you ever have a career in IT, chances are you will have to become familiar with them, from system administrators wanting to parse log files, to programmers doing EDI, to web developers doing URL matching (Django, Web.py, even config files for certain web servers), to loading and extracting data from a file that is not 100% structured to be read by a computer (as we will do here).

The official documentation of the re module can be found at docs.python.org

A quick cheat sheet on regular expressions can be found here at tartley.com

And for those who like to follow video tutorials, check out this google video tutorial by Nick Parlante, on youtube.

Let's now look at the code in load.py:

1:  #!/usr/bin/env python  
2:  # -*- coding: utf-8 -*-  
3:  """  
4:  load.py - just loading the question file  
5:  Loads questions and answers from quiz data file. It follows the format  
6:  from triviachamp.com  
7:  """  
8:  # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4  
9:    
10:  import re  
11:    
12:  with open('short.txt', 'r') as f:  
13:    for line in f:  
14:      if len(line) > 1:  
15:        match = re.search(r'^([\w ]+)-([\w ,-]+)\.(.+)', line)  
16:        if match:  
17:          answer = match.group(1).strip()  
18:          trivia = match.group(2).strip()  
19:          question = match.group(3).strip()  
20:          choices = []  
21:        else:  
22:          match = re.search(r'\s+(\w)\.([\w ,-]+)', line)  
23:          if match:  
24:            choices.append(match.group().strip())  
25:            choice = match.group(1)  
26:            description = match.group(2).strip()  
27:            if description == answer:  
28:              correct_response = choice  
29:            if choice == 'd':  
30:              print(question, choices[0], choices[1],  
31:                   choices[2], choices[3],  
32:                   correct_response, trivia)  
33:    


Line 1 through 7 are typical of what we've done for the past several scripts, with the exception of line 2. On that line we specify that this document or script is following not ASCII, not ISO8859-1 but UTF-8 for the encoding of the characters. In this particular case we do not need it, but if we had to use accented letters, or special glyphs (for a card game, the heart, spade, diamond and clover glyphs, for example) then we would need this. Python 3 defaults to UTF-8, so it is a good idea to start learning about unicode and UTF-8, even though we are writing Python 2.x code right now. Line 8 is simply some instructions for an editor named vim.

Line 10 is where we import the regular expression module we just discussed. This is part of the Python library, so no need to download anything. This follows the "batteries included" pattern of Python. For general programming, you typically dont need to download anything else. For domain specific applications however, you will need to download and install other modules (like web.py, matplotlib, pygame, scipy etc)

Line 12, we are opening a file named short.txt, in the 'r' (or read) mode.

12:  with open('short.txt', 'r') as f:  

This is quite alien looking for some having a background in another language, such as C. In fact, it is also possible to open a file this way:

f = open('short.txt', 'r')

However, by using the statement with, we get exception handling and graceful housekeeping for free. We dont have to use try, except and finally, it is done implicitly. So just use this form.

Line 13, we have a for loop, and just like we did with the PINS in the previous article, we are getting items from an object. For PINS it was either a tuple or a list, both of which can be iterated by a for loop. In this case, we use a file object named f, which we obtained by calling open(). This is different than, say, a file descriptor in C/C++ (which is what fopen() in these languages would return) where it is only a reference to be used by other functions to do stuff. In Python, it is an object that, when an iteration is requested, will give us one item. For files, it will be one line. We could have also used a different variable name: for blah in file would get me a whole line in variable blah.

Line 14, we use a built-in, len() to tell us if we are dealing with an actual line with some data, or just a blank line, based on the length of the line.

Let me isolate the next 5 lines so we can focus on them:

15:        match = re.search(r'^([\w ]+)-([\w ,-]+)\.(.+)', line)  
16:        if match:  
17:          answer = match.group(1).strip()  
18:          trivia = match.group(2).strip()  
19:          question = match.group(3).strip()

Now, the scary part, line 15. The regular expression. You'll just have to trust me that it works as intended. In the workshop I couldn't go into all the details. Similarly, this article would be way too long if I did, but I'll try to do it anyway... The actual regular expression is this:

^([\w ]+)-([\w ,-]+)\.(.+)

The caret (^) is to start the match at the beginning of the line. The first group in parenthesis will match an alphanumeric character (\w) or a space (anything listed between the two square braces), while the + says to repeat it for as long as you can:

([\w ]+)

But we also follow this with a dash (-) and so it will stop the first group just before the dash. This separates the answer, from the trivia about the answer. The square brackets delimit a set of what characters should match (think of it as a Python list with no commas). This is convenient, because in the second group, we want to get the trivia, which includes not only alphanumeric or 'word characters', but also spaces commas and dashes:

([\w ,-]+)

The + repeats the match until the next rule, the period. It has to be escaped, because a period means match any character. We want it to match an actual period, so we escape it with a backslash (\). The last group uses the period to match any character and wont stop until the end of the line.

To actually use this whole regular expression in our Python code, we have to put it in a string. In python we can use the single or double quote mark for strings ( 'a_string' , "also_a_string" ).

In this case however, not just any string, a raw string. We do this by adding a r at the beginning: r'a_string'. That way we do not have to escape the backslash. And we pass that, along with our line to the search() function of the re module.

This returns a match object only if there is an actual match, so we have to test for existence of such on line 16, before we can use it on lines 17-19. There, we get the data from the groups we defined (defined in the regular expression by the parenthesis pairs) and assign them to variables: answer, trivia and question.

Wow, lots of explanation for only 5 lines of code! But that is the nature of the regular expression beast. As I said, often, it is not the answer, but when it is, we just have to live with its denseness...

Line 20 is just setting up (or clearing) a list to store the multiple choice answers that will follow. Yes, that is right, we haven't dealt with those yet...

Line 21 is the else tied to the existence of a match. Basically, if we couldn't match the first regular expression we wrote, that means we are probably dealing with one of the multiple choice answers. This next part could have been done without regular expressions, but since we spent all this time explaining them, let's use them again on line 22, this time with only 2 groups defined. The first will have the a-d letter and the second the description.

Line 23, we test again for a match. Line 24, we get the match.group() without specifying which of the group we want. By doing this, we get the whole match. We further use the strip() function to remove any leading or trailing white spaces, and we then append this to our choices list. Initially it is empty, and we add to it the multiple choices until the last choice (d).

Let me put the last piece of code repeated here:
25:            choice = match.group(1)  
26:            description = match.group(2).strip()  
27:            if description == answer:  
28:              correct_response = choice  
29:            if choice == 'd':  
30:              print(question, choices[0], choices[1],  
31:                   choices[2], choices[3],  
32:                   correct_response, trivia)  

Here, on line 25 we get the letter (a-d) assigned to choice. We then get the description on line 26 (using strip() again to remove leading and trailing white spaces). We can then use this description to compare it  (line 27) to the answer we picked up earlier. If the answer is on that line, we then assign to correct_response that letter.

Furthermore, if we are on the last line of the multiple choice answers, we are now ready to either store the whole group of question, choices, correct response and trivia, or in this case (lines 30-32) to print it.

We could also have passed the file to the regular expression and write a single regular expression getting all the data we wanted on multiple lines at once, but the sheer complexity of it would have rendered this tutorial useless.

Simple Quiz


In piquiz.py, we keep things simple (relatively... !) again. It is a basis that can be evolved into something a bit more interesting. Just a straight script, a trivia game in 50 lines of code (a little bare bone obviously with no scoring):


1:  #!/usr/bin/env python  
2:  # -*- coding: utf-8 -*-  
3:  """  
4:  PiQuizMachine - A quiz game for the Raspberry Pi.  
5:  Loads questions and answers from quiz data file. It follows the format  
6:  from triviachamp.com  
7:  """  
8:  # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4  
9:    
10:  import re  
11:  import random  
12:    
13:  data = []  
14:  with open('questions.txt', 'r') as f:  
15:    for line in f:  
16:      if len(line) > 1:  
17:        match = re.search(r'^([\w ]+)-([\w ,-]+)\.(.+)', line)  
18:        if match:  
19:          answer = match.group(1).strip()  
20:          trivia = match.group(2).strip()  
21:          question = match.group(3).strip()  
22:          choices = []  
23:        else:  
24:          match = re.search(r'\s+(\w)\.([\w ,-]+)', line)  
25:          if match:  
26:            choices.append(match.group().strip())  
27:            choice = match.group(1)  
28:            description = match.group(2).strip()  
29:            if description == answer:  
30:              correct_response = choice  
31:            if choice == 'd':  
32:              entry = (question, choices[0], choices[1],  
33:                   choices[2], choices[3],  
34:                   correct_response, trivia)  
35:              data.append(entry)  
36:    
37:  random.shuffle(data)  
38:  for question,choice_a,choice_b,choice_c,choice_d,\  
39:      correct_response,trivia in data:  
40:    print question  
41:    print choice_a  
42:    print choice_b  
43:    print choice_c  
44:    print choice_d  
45:    team_answer = raw_input("Your answer:")  
46:    if team_answer == correct_response:  
47:      print "That is correct, the answer is",correct_response  
48:      print trivia  
49:    else:  
50:      print "Not correct."

Up to line 31, it is almost the same as we already discussed (but using the full size questions.txt file). 32-34, instead of printing, we now store this entry into a list named data, which we initialized empty on line 13.

We are now going to randomize or shuffle the list of questions on line 37. On line 11 we imported the random module which includes a shuffle function. This is very convenient for games, not just for a quiz, but really interesting for a card game. We could define a card deck as a list, then shuffle it.

On line 38 and 39 (the backslash makes it as if it was a single line), we now loop through all the questions in the data list using a for loop. We then print the question and multiple choices on lines 40 through 44. I'm using a print syntax of a statement, but make note that starting with Python 3, print is a function. The next code example uses the print() function syntax (works in python 2.7 and 3). Lines 38-44: This can all be coded in a prettier way, but in order to keep this as simple as I can, I just did it by specifying all the fields so it is very obvious what we are doing.

Line 45 allows us to get an answer from a keyboard, with a prompt of "Your answer:". We check if the answer is correct on line 46 and if so print a message and the trivia tied to the answer on lines 47 and 48. If the answer was wrong (else) we print a different message on line 50.

So that is the basic core of a quiz program.

The Real Deal

Combining the code we've done in the button/quiz*.py scripts in the previous article, with the code above, we have all the ingredients to make an interactive quiz machine, one where each of the four teams or players gets a game controller, and will be able to "buzz" in first to answer, much as in TV games, such as Jeopardy or Family Feud.

This code was designed to teach about Python and GPIOs for a workshop and it is what I would call "squeaky clean". It is kept on purpose simple, yet demoes several key features of Python and the GPIOs.

The code was run through pep8 and pylint and is properly documented and formatted (even having 2 blank lines between functions, no use of ; etc), is quite verbose (several things were done in multiple lines but in normal use I would probably do as one) and yet, is less than 100 physical lines of code.

1:  #!/usr/bin/env python  
2:  # -*- coding: utf-8 -*-  
3:  """  
4:  PiQuizMachine - A quiz game for the Raspberry Pi.  
5:  Loads questions and answers from quiz data file. It follows the format  
6:  from triviachamp.com. Lock out through pushbutton controllers  
7:  """  
8:    
9:  __author__ = "Francois Dion"  
10:  __email__ = "francois.dion@gmail.com"  
11:    
12:  import re  
13:  import random  
14:  import RPi.GPIO as gpio  
15:    
16:  PINS = (22, 23, 24, 25) # list of pins as tuple  
17:  OFFSET = 21 # team number to GPIO pin offset  
18:    
19:    
20:  def loadtrivia(filename):  
21:    """ Load the trivia into a list, after extracting the fields """  
22:    data = []  
23:    with open(filename, 'r') as f:  
24:      for line in f:  
25:        if len(line) > 1:  
26:          match = re.search(r'^([\w ]+)-([\w ,-]+)\.(.+)', line)  
27:          if match:  
28:            answer = match.group(1).strip()  
29:            trivia = match.group(2).strip()  
30:            question = match.group(3).strip()  
31:            choices = []  
32:          else:  
33:            match = re.search(r'\s+(\w)\.([\w ,-]+)', line)  
34:            if match:  
35:              choices.append(match.group().strip())  
36:              choice = match.group(1)  
37:              description = match.group(2).strip()  
38:              if description == answer:  
39:                correct_response = choice  
40:              if choice == 'd':  
41:                entry = (question, choices[0], choices[1],  
42:                     choices[2], choices[3],  
43:                     correct_response, trivia)  
44:                data.append(entry)  
45:    return data  
46:    
47:    
48:  def getteam(lockedout):  
49:    """ figure out which team presses their button first """  
50:    poll = [pin for pin in PINS if pin - OFFSET not in lockedout]  
51:    while True:  
52:      buttons = [gpio.input(pin) for pin in poll] # list comprehension  
53:      if False in buttons: # at least one button was pressed  
54:        if buttons.count(False) == 1:  
55:          return buttons.index(False) + 1  
56:        else: # trouble, multiple buttons  
57:          teams = [i for i, b in enumerate(buttons) if b is False]  
58:          return random.choice(teams)  
59:    
60:    
61:  def main():  
62:    """ our main program """  
63:    data = loadtrivia('questions.txt')  
64:    
65:    gpio.setmode(gpio.BCM) # broadcom mode, by GPIO  
66:    for pin in PINS:  
67:      gpio.setup(pin, gpio.IN) # set pins as INput  
68:    random.shuffle(data)  
69:    for question, choice_a, choice_b, choice_c, choice_d, \  
70:        correct_response, trivia in data:  
71:      # if we wanted to make a graphical game using pygame  
72:      # we would replace the print statements below  
73:      print(question)  
74:      print(choice_a)  
75:      print(choice_b)  
76:      print(choice_c)  
77:      print(choice_d)  
78:      lockedout = [] # we start with no team locked out  
79:      while len(lockedout) < 4:  
80:        team = getteam(lockedout)  
81:        prompt = "Your answer, team {0}? ".format(team)  
82:        team_answer = raw_input(prompt) # get an answer  
83:        if team_answer == correct_response:  
84:          print("That is correct, the answer is:")  
85:          print(correct_response)  
86:          print(trivia)  
87:          print("")  
88:          lockedout = [1, 2, 3, 4]  
89:        else:  
90:          print("That is not the answer.")  
91:          lockedout.append(team)  
92:    
93:    
94:  if __name__ == "__main__":  
95:    try:  
96:      main()  
97:    except KeyboardInterrupt:  
98:      print("Goodbye")  
99:      gpio.cleanup()

Lines 1 to 16 should be familiar. I did add two variables for author and email. This is just a convention some people do in their code. Line 17 I'm defining an offset between the team number (1,2,3,4) and the GPIOs (22,23,24,25).

Lines 20 through 45 is the code from the previous example, but put into a function that accepts a file name for the quiz data and that upon execution, will return a list containing all our questions.

lines 48 through 58 is our quiz4.py code from the previous article, put into a function, but with a twist:

On line 52, instead of using directly the PINS tuple, we filter it first on line 50 to see which pins we should really poll. When getteam() is called, a list of teams that have been locked out is provided. We are not even going to check these teams button controllers, because they answered this question already, with a wrong answer.

50:    poll = [pin for pin in PINS if pin - OFFSET not in lockedout]  

So, what is happening here merits an explanation. You've probably recognized this as a list comprehension (which we've introduced in the previous article). But it looks strange... Let's read it. We will add to this list a pin, from an iteration of the PINS tuple (containing 22,23,24,25), but we will do this only if the pin minus the offset (21) is not in the list of locked out teams. So, if we take 22 - 21, that is 1. If team 1 is locked out, the list stays empty and continues on to the next value and test it. So on and so forth.

I mentioned earlier we had just touched the tip of the list comprehensions. here we went a little deeper, but it goes on.

Lines 51-58, we loop until a button is pressed. If only one button was pressed at the exact same time, we are good to go, and return which of the teams (1 - 4) pressed the button first. But trouble is looming on the horizon. It is possible for 2 or more buttons to be pressed at the exact same moment.

We thus have to introduce some random process to select one of those that have been captured as pressed. We do that on lines 57 and 58. 58 uses the choice() function of the random module (imported on line 13), but 57 requires some explanation, for those who are just starting with Python:

57:          teams = [i + 1 for i, b in enumerate(buttons) if b is False]  

We want to generate a list of all the teams that had pressed their buttons. buttons is a list containing something like [False, True, True, False], indicating here that team 1 and 4 pressed their button at the same time.

What I now want is a list containing [1, 4] to select randomly from it. So what we do is to use a built in function called enumerate() on that buttons list. This returns the index (starting at zero) and the value, so we capture this with a for i, b. We will assign i (the zero based index) + 1 (to get a team number) to the list, but only if b (the value) is False. There, that wasn't so bad, after all!

We are now ready for our main() function, lines 61 to 91 (only 30 lines).

On line 63 we load the data from the file questions.txt

Lines 65-67, we set up the GPIOs, as we've done in quiz4.py.

Lines 68-77 is the logic we had in our previous example: piquiz.py

At line 78, we initialize the list of locked out teams to be empty, and start a loop on line 79 that will go on until we have 4 teams locked out by 4 wrong answers, or a right answer (which forces all 4 teams to be locked out).

Line 80 is where we poll the button controllers until we have somebody pressing a button.

81:        prompt = "Your answer, team {0}? ".format(team)  
82:        team_answer = raw_input(prompt) # get an answer  

Lines 81 and 82 are asking for an answer from the team that pushed their button first. This answer is given using the keyboard.  Line 81 could also have been written as:

prompt = "Your answer, team " + str(team) + "?"

This is closer to what is done in other languages, but each concatenation with + creates a new resulting object. As such, it is a less efficient way of doing it, and if used in a loop with lots of data, will consume a lot of memory and be slow.

lines 83-88 handle a correct answer (concluding by locking out all teams to force a new question), while lines 89-91handle a wrong one, adding that one team to the list of locked out teams (passed to the function on line 80)

94:  if __name__ == "__main__":  
95:    try:  
96:      main()  
97:    except KeyboardInterrupt:  
98:      print("Goodbye")  
99:      gpio.cleanup() 

Lines 94-99 repeat the safeguard pattern we established in our previous article, in quiz4.py on lines 21-25

There you go, a complete game with Raspberry Pi GPIO hardware integration, that can be played with friends, in less than 100 lines of python code.

Team 2 is smoking! Two correct answers in a row

Conclusion


I've avoided classes and methods on purpose. These would have complicated things too much for the workshop audience (which ranged from teenagers to adults and from some who had never programmed in python, to some who had written a good bit).

Several also had a background in basic and shell scripts, so I did a good bit as straight scripts, without functions, waiting to the end to introduce these concepts. I've also added a bit more code than in the workshop in order to provide a better reference after the fact.

I hope it is useful to others, and if you are in the area, make sure to keep an eye for our next PyHack Workshop. Let me know if I've provided enough details, what things I could have explained more etc.