Sunday, December 30, 2007

Tablet Python #2 - Resource Modules

Introduction

In this second episode of Tablet Python, I'm going to tell you about an easy way to make resource modules. A resource module is a Python module which you can import for loading resources such as images, fonts, or whatever you want.

For example:

import graphics

img = gtk.Image()
img.set_from_pixbuf(graphics.icon)


Implementation

In episode #1 we talked about writing relocatable software. We are going to apply the same technique for resource modules, since they will be relocatable as well.

Our resource modules are Python packages, i.e. a subdirectory with a __init__.py file in it. The name of the directory will be the module's name. Be careful to only use valid characters (characters valid for Python variables) in the directory name and the filenames of the resource files (e.g., my_icon.png is OK, while my-icon.png would be not!).

Let's assume the following filesystem structure for our example:

graphics/
+ __init__.py
+ icon.png
+ logo.png
+ background.png

test.py

The package directory will also contain the resource files. In order to access them, we'll have to put them into the module namespace. Write the following into graphics/__init__.py:

import os
import gtk

_RESOURCE_PATH = os.path.dirname(__file__)

def _load_resources():

# Find all .png files in the resource directory. This construct is called
# "list comprehension" and will be covered in detail in episode #3.
# This returns a list of the names of all files in the resource directory
# ending with ".png".
resources = [ f for f in os.listdir(_RESOURCE_PATH)
if f.endswith(".png") ]

# load resources into module namespace
for r in resources:

# the filename without extension will be the name to access the
# resource, so we strip off the extension
name = os.path.splitext(r)[0]

# this is the full path to the resource file
path = os.path.join(_RESOURCE_PATH, r)

# Now we can load the resource into the module namespace.
# globals() gives us the dictionary of the module-global variables,
# and we can easily extend it by assigning new keys.
globals()[name] = gtk.gdk.pixbuf_new_from_file(path)

#end for

# load the resources when importing the module
_load_resources()

That's all. Let's test it with a tiny test program test.py:

import gtk
import graphics

win = gtk.Window(gtk.WINDOW_TOPLEVEL)
img = gtk.Image()
img.set_from_pixbuf(graphics.logo)
win.add(img)
win.show_all()

gtk.main()

You may put as many resource files as you want into the resource directory and simply access them by their name. The files are loaded only once when the module gets imported the first time. Subsequent import statements reuse the module without reloading the resources. I'm going to talk about the "singleton" nature of Python modules in a later episode.

A more sophisticated example of resource modules can be found in the theming engine of MediaBox (the subdirectory called theme).

No comments: