Extra Modules for Pygame Projects

NOTE - These modules were updated slightly around Feb 1, 2017.

Simple Argument Extraction

The module sarg is used quite a bit in the Pygame programs. It lets us put key=value pairs on the command line and builds a dictionary for later lookups. It's handy when lots of values may be specified for different modules and requires hardly any overhead in coding for them. If a pair is left out defaults are provided. Here is the code:

01 # sarg.py - simple argument parser
03 def _parse() :
04     import sys
05     global _cmd_dict
06     _cmd_dict = {}
08     cmds = sys.argv[1:]
09     cmdi = 0
11     for cmd in cmds :
12         cmdi += 1
13         parts = cmd.split("=")
14         if len(parts) == 1 : _cmd_dict[cmdi] = parts[0]
15         else :
16             tag,value = parts
17             _cmd_dict[tag] = value  # store all vals as strings
19 def Int(tag,default=0) :
20     return int(_cmd_dict.get(tag,default))
22 def Float(tag,default=0.0) :
23     return float(_cmd_dict.get(tag,default))
25 def Str(tag,default="") :
26     return str(_cmd_dict.get(tag,default))
28 _parse()

A typical command line with arguments might look like

$ python kmeans.py pixels=300 prefix=kmeans maxpics=40

and later calls to extract the values might look like

pixels = sarg.Int("pixels", 600)
prefix = sarg.Str("prefix")

When sarg is imported for the first time the dictionary _cmd_dict (private by convention) is assembled from the command line sys.argv[1:]. Key/value pairs can be used anywhere in the program and converted from string to integer or float. A default value may be provided (as with pixels above) in case the key value pair is missing on the command line. Or if missing on the command line and no default is provided in the extraction then an empty string or zero is returned. In these Pygame samples some keys are common, operating the graphics and generation of static images and gifs. To add a new key just use it somehwere in the program to extract a value and when needed add the pair to the command line.

Pycon and the camera

The pycon module provides a somewhat simpler interface to pygame for the most of the graphic projects using it. It also provides a simple mechanism to generate little GIF movies. Pycon also uses the sarg module to extract control values. Here is the class definition:

16 class Pgcon :
17     def __init__ (self) :
18         import pygame as pg
19         pg.init()
20         self.pg     = pg
21         self.size   = sarg.Int("pixels",600)
22         self.delay  = sarg.Float("delay",.5)
23         self.frame  = 0
24         self.camera = camera.Camera (pg)
25         self.screen = pg.display.set_mode((self.size,self.size))
27     def newScreen (self, background=BGCOLOR) :
28         self.screen.fill(background)
30     def lite(self, color) :
31         # for example white (255,255,255) becomes gray (127,127,127)
32         return [x/2 for x in color]
34     def textDraw(self, color, pos, text) :
35         font = self.pg.font.Font(None,24)
36         rend = font.render(text,True,color)
37         tw,th = font.size(text)
38         org = (pos[0]-tw/2, pos[1]-th/2)
39         self.screen.blit(rend, org)
41     def lineDraw(self,color,apos,bpos,width=1) :
42         self.pg.draw.line(self.screen, color, apos, bpos, width)
44     def writeScreen (self, delay=None) :
45         self.pg.display.flip()
46         if delay == None : delay = self.delay
47         time.sleep(delay)
48         self.camera.takePicture(self.screen)
50     def close (self, pause=True) :
51         if pause : raw_input ("Hit Return to Continue")
52         self.camera.makeGif(delay=int(self.delay*100))
53         self.pg.quit()

Only a single instance of the Pycon class is useful in a program.

The code should be very straight-forward. Asking for a newscreen (line 27) just fills the display with the background color. The geometry is square and defaults to 600 pixels. But in the example graphics in the project write-ups smaller windows like pixels=300 is generally used.

The attribute self.pg is simply a reference to pygame as imported. It is passed around as needed.

The method textDraw (line 34) draws text in a standard 24pt font and centered on the position passed. This make it easy to plop labels over a point on the plot or to center the banner on the top of the screen.

The method writeScreen does a pygame display flip and a wait. The command line argument delay=.2 would wait .2 seconds between frames and if generating a GIF image that delay would be used there as well.

Finally, pgcon.close() normally waits for the user to release the final frame from the screen. It then instructs the camera to possibly create a gif animation from any frames that it has saved to disk as .png files.

The Camera

A Camera instance is always attached to the Pycon instance. Pygame has a nifty function image.save to convert an image to a standard format (like jpeg or png) and write it to the disk. This file can then by opened by a program like the linux eog to display it on the screen. The file can also be loaded and viewed in a browser like firefox or chrome.

The camera typically captures a set of images that can be played back either as a slide show or little movie. The latter is done using the ImageMagick program convert. You must install ImageMagick on your computer to do this.

When the display is about to be updated, the camera may be armed to save the image to disk. At the end of the run the images saved may be converted into a gif animation. Both of these steps are optional. If using the camera you need to set maxpics=n where n is a maximum of images to capture. This prevents runaway programs from eating your disk. prefix=xx where xx is arbitrary also is needed to name the images. In addition, to generate a GIF animation, gif=1 must be included. The camera has a number of attributes to control what it does. These are set through sarg explained above in the current implementation. So to recap:

  • maxpics: the maximum number of images to capture.
  • prefix: a name prefix for both the png files and the gif animation.
  • gif: set to 1 if you want the camera to create a gif from the images saved
  • keep_pngs: if a gif is made, setting this to 1 keeps the pngs from being removed.
  • skip: set to number of frames to ignore at the beginning. This is rarely used.

The following is a simple example. It captures 12 frames, converts the png files to a single gif and then deletes the png files. Adding keep_pngs=1 to the command lines would leave both pngs and the gif. On the other hand, leaving out gif=1 would generate the pngs but not the gif.

$ python kmeans.py seed=180 pixels=300 prefix=km maxpics=40 gif=1
Just captured frame: 1, km_001.png
Just captured frame: 2, km_002.png
Just captured frame: 3, km_003.png
Just captured frame: 4, km_004.png
Just captured frame: 5, km_005.png
Just captured frame: 6, km_006.png
Just captured frame: 7, km_007.png
Just captured frame: 8, km_008.png
Just captured frame: 9, km_009.png
Just captured frame: 10, km_010.png
Just captured frame: 11, km_011.png
Just captured frame: 12, km_012.png
Hit Return to Continue
Making uniform GIF
convert -delay 50 -loop 1 km_*.png km.gif
km.gif has 12 frames

The following is the class description for the camera. Four values (lines 7-10) are extracted from the command line (or defaulted) and 3 attributes set (line 11-13). pg is a reference to the pygame module.

The method takePicture is passed a frame, checks to see if all is proper (line 16) and builds a PNG file onto the disk.

05 class Camera :
06     def __init__ (self, pg) :
07         self.maxpics   = sarg.Int("maxpics",0)
08         self.prefix    = sarg.Str("prefix")
09         self.gif       = sarg.Int("gif",0)
10         self.skip      = sarg.Int("skip",0)
11         self.picsTaken= 0
12         self.armed = False
13         self.pg    = pg
15     def takePicture(self, screen) :
16         if self.picsTaken>=self.maxpics or not self.armed or not self.prefix :
17             return False
18         self.picsTaken += 1
19         file = "%s_%03d.png" % (self.prefix, self.picsTaken)
20         if self.skip <= self.picsTaken :
21             self.pg.image.save(screen, file)
22             print "Just captured frame: %d, %s"%(self.picsTaken, file)
23         self.armed = False
24         return True
26     def makeGif(self, loop=1, delay=50) :
27         import os
28         if not self.picsTaken or not self.gif : return
29         prefix = self.prefix
30         print "Making GIF"
31         cmd = "convert -delay %d -loop %d %s_*.png %s.gif"
32         cmd = cmd % (delay, loop, prefix,prefix)
33         print cmd  # print the imageMagick convert command
34         # create the .gif file with ImageMagick
35         print "%s.gif has %d frames" % (prefix,self.picsTaken)
36         os.system(cmd)
37         if not sarg.Int("keep_pngs", 0) :
38             os.system("rm %s_*.png" % prefix)

Finally, the method makeGif will generate a GIF animation by building a command line to invoke the ImageMagick convert utility. The variable loop defaults to 1 for a single run of the animation. The delay variable is the time between frames and is in units of 1/100 of a second. makeGif is called from Pycon.close and the delay is converted from the floating point seconds used in the rest of the programs.

Copyright © 2017 Chris Meyers