A Little Bit of Widgetry for PyGame

Version 3.1.0

This is a framework for creating a GUI using PyGame. It was developed for use in PyWeek competition entries, and has been used by the author in several games so far.



Typical usage of the widget system is as follows:
  1. Initialize the PyGame display surface.
  2. Create an instance of RootWidget or subclass thereof (such as Shell), passing it the display surface.
  3. Create additional widgets if needed and add them as subwidgets of the root widget. If using Shell, create the screens you will be using and display your main screen.
  4. Start the frame timer if you will be using it (see RootWidget.set_timer()).
  5. Call the run() method of the root widget.


There is a convention used by the constructors of Widget and its subclasses. As well as the arguments listed in the documentation for the class's constructor, you can also pass initial values for any other attributes of the class as keyword arguments.

You can do this for attributes of your own Widget subclasses as well, if you pass on any extra keyword arguments to the base class __init__ method. The only requirement is that the attribute already exist as an instance or class attribute, or as a property. This ensures that mistaken keyword arguments are diagnosed rather than silently creating a new attribute.


Themes provide a centralised way of customising the appearance of Albow widgets on a per-class basis. There are three parts to the theme system: theme properties, which are attributes that get looked up automatically in the currently active themes; the Theme class, instances of which hold values for the theme properties of a particular class; and the theme module, which holds a default hierarchy of Theme instances that your application can replace. See the documentation pages on each of these for more details.


In addition to the usual PyGame event attributes, some events have extra attributes:

Attribute Event type Description
time All events Time of occurrence of the event, in milliseconds since pygame.init() was called.
local Mouse events Position of the mouse in the local coordinate system of the widget to which it is delivered.
ray OpenGL mouse events A pair of points representing the projection of the mouse position onto the near and far clipping planes.

Mouse events are classified into mouse_down, mouse_drag, mouse_up and mouse_move, and are delivered by calling the corresponding method of the relevant widget. Mouse-down events are delivered to the widget in which the event occurs. Mouse-drag and mouse-up events are delivered to the widget that received the last mouse-down event. Mouse-move events (with no mouse button pressed) are delivered to the widget in which they occur.

Keyboard events are delivered by calling the key_down or key_up methods of the widget having the current keyboard focus. A widget is given the keyboard focus by calling its focus() method. If the focus widget does not handle a key event, it is passed up the widget hierarchy until a handler is found.

Both mouse and keyboard events have a set of boolean attributes indicating the state of the modifier keys at the time of the event:

shift True if one of the Shift keys is down.
ctrl True if the Control key is down.
alt True if the Alt key (Option key on Macintosh) is down.
meta True if the Meta key (Command key on Macintosh, Windows key on PC keyboards) is down.
cmd True if either the Control or Meta key is down. This can be used to implement command keys that work according to either Macintosh or Linux/Windows conventions.

Albow also provides timer events that are delivered to the root widget by calling its timer_event() method. If you use the Shell and Screen classes, timer events are dispatched to the currently active Screen. The timer_event() method returns a boolean indicating whether to update the display.


Albow can be used in an OpenGL environment. To do this, create an OpenGL display surface (using pygame.display.set_mode() with OPENGL in the flags) and pass it to the constructor of your root widget. You can then add widgets derived from GLViewport to provide 3D drawing areas, as well as ordinary 2D widgets.

There are a few things to be aware of when mixing 2D and 3D widgets. When using OpenGL, Albow distinguishes three kinds of widgets:
  1. 3D widgets -- GLViewport, GLOrtho, GLPerspective and classes derived from them.
  2. 2D widgets -- most other Albow widgets. These are rendered to a temporary surface which is then transferred to the screen using glDrawPixels.
  3. Container widgets -- used for laying out other widgets. By default these are Row, Column, Grid and classes derived from them. The root widget is also considered a container widget.
Container widgets and 3D widgets can have any kind of widget as a subwidget, but 2D widgets can only contain other 2D widgets.

You can turn any 2D widget into a container widget by setting its is_gl_container property to true. However, when you do this, no drawing is performed for the widget itself -- its bg_color and border properties are ignored, and its draw() method is never called.

You can also turn a container widget back into an ordinary 2D widget by setting its is_gl_container property to false. You might want to do this, for example, to give a Row of buttons a background or border.

Something to keep in mind is that drawing on 2D surfaces and transferring them to an OpenGL window is usually much slower than drawing directly with OpenGL. So if you want high performance, try to keep the window area covered by 2D widgets to a minimum.

Example Code

There are some programs in the demo directory that exercise many of the classes in the Albow library. You may find them useful as a source of usage examples.