Graphical displays with Python and Pygame

Direct Execution

Once a script is finished, it is often ideal to call it directly by name without also having to call Python. To do this, I add the location of the file's interpreter to the top of my script. To illustrate, I'll work with a file called (This process works for any text-based script, so I'm just using as an example file name, without providing a listing.)

Step 1. Find where Python lives. Enter whereis python in a terminal. whereis says to Linux, "show me where you go when I enter the command … ." In my case, it's /usr/python.

Step 2. Add the full path to the top of the script. Edit and add the path from step 1 as the first line, starting with #! (pronounced "shebang," "hashbang," or "pound-bang"):

#! /usr/python

Usually, I add a blank line afterward, but it's not strictly required. Linux will find this line and call the interpreter automatically.

Step 3. Make the script executable. Text files are usually not marked executable when they are created, so I have to set the executable bit with chmod,

chmod +x

for Linux to accept my script as executable.

Step 4. Remove the file extension. Now that my file is executable, I rename to viewer,

mv viewer

so I don't have to type the full file name with extension every time.

Image Management Class

Listing 2 is an image management class that uses Pygame to provide some shortcuts when you need to show multiple images. It includes example usage of each method at the end of the listing as a working script.

Listing 2

01 import pygame
03 class imageHandler:
04   def __init__ ( self ):
05 = dict()
07   def loadFromFile ( self, filename, id=None ):
08     if id == None: id = filename
09 [ id ] = pygame.image.load ( filename ).convert()
11   def loadFromSurface ( self, surface, id ):
12 [ id ] = surface.convert()
14   def render ( self, surface, id, position = None, clear = False, size = None ):
15     if clear == True:
16       surface.fill ( ( 0, 0, 0 ) )
18     if position == None:
19       picX = int ( surface.get_width() / 2 - [ id ].get_width() / 2 )
20       picY = int ( surface.get_height() / 2 - [ id ].get_height() / 2 )
21     else:
22       picX = position [ 0 ]
23       picY = position [ 1 ]
25     if size == None:
26       surface.blit ( [ id ], ( picX, picY ) )
27     else:
28       surface.blit ( pygame.transform.smoothscale ( [ id ], size ), ( picX, picY ) )
30 pygame.display.init()
31 screen = pygame.display.set_mode ( ( 640, 480 ) )
33 handler = imageHandler()
34 handler.loadFromFile ( "images/network.png", "net" )
35 handler.loadFromFile ( "images/laptop.png", "laptop" )
36 handler.loadFromFile ( "images/presentation.png", "ppt" )
38 handler.render ( screen, "net", ( 0, 0 ), True, ( 100, 100 ) )
39 handler.render ( screen, "laptop", None, False )
40 handler.render ( screen, "ppt", ( 200, 200 ), None )
42 handler.loadFromSurface ( screen, "screenshot" )
44 pygame.display.flip()
46 raw_input()
47 pygame.quit()

This class is written to handle the loading of multiple image files and reference them by a unique ID. The render function draws an image onto the provided surface. The imageHandler class uses three functions:

  • loadFromFile needs a file name and an optional ID as arguments. If an ID is not provided, it defaults to the full file name, including extension. This method is called once for each image to be loaded.
  • loadFromSurface accepts an existing Pygame surface as the first argument. The second argument, the ID to be assigned, is required because it has no file name to act as a default.
  • render is where the majority of the work takes place. The two required arguments are a Pygame surface and an ID. The surface is what to draw on, and the ID identifies the image you want to draw. In the absence of any other arguments, the class draws the picture specified by id in the center of the provided surface. Optionally, I can specify position, clear, and size. The position argument accepts a tuple and places the image origin (upper left corner) at the provided coordinates. The clear argument accepts True or False: If True, the destination surface is filled with black before the image is rendered. A size provided as a (width, height) tuple defines the image size.

Usage examples start on line 30, where I initialize the display and create a 640x480-pixel window. Line 33 creates an instance of the class, then I call loadFromFile as many times as I need to get the image files I want. Figure 2 shows the results of the provided example code.

Figure 2: Output of the example. Images are laptop.png [3], presentation.png [4], and network.png [2].

Starting at line 38, I call render to retrieve my stored images. In this example, I'm providing the screen surface directly so I can display images in my window. I could just as easily create a new surface and render images to it, perhaps for a grid of pictures or a photo collage.

Line 42 provides a glimpse of the flexibility of Pygame. Because screen is a surface, I can provide it to my handler class to load a new image and essentially capture a screenshot of what I've created. The remainder of the program is identical to the simple image viewer, flipping the display, waiting for user input, and exiting.

Lines 9 and 12 add one new function call, convert(). When pygame.image.load completes, it returns a Pygame surface – the image I just asked for. The convert() function changes the surface to match the format of the display window at a pixel level. Without this call, the surface would be converted "on the fly" during the blitting process. This slows down blitting (and thus your frame rate) significantly.

If you're just showing a few images, then this slowdown likely won't be noticed. For animation or when presenting a sequence of images, though, convert() is essential.

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy Raspberry Pi Geek

Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content