Welcome to PySDL2’s documentation!¶
PySDL2 is a wrapper around the SDL2 library and as such similar to the
discontinued PySDL project. In contrast to PySDL, it has no licensing
restrictions, nor does it rely on C code, but uses ctypes
instead.
Getting Started¶
Installing PySDL2¶
This section provides an overview and guidance for installing PySDL2 on various target platforms.
Getting the sources¶
You can download the official releases of PySDL2 from https://github.com/marcusva/py-sdl2/releases. Download the most recent release, unpack it and make sure that you installed the relevant prerequisites before continuing with the installation.
Prerequisites¶
PySDL2 relies on some 3rd party packages to be fully usable and to provide you full access to all of its features.
You must have at least one of the following Python versions installed:
- Python 2.7, 3.5+ (http://www.python.org)
- PyPy 1.8.0+ (http://www.pypy.org)
Other Python versions or Python implementations might work, but are (currently) not officially tested or supported by the PySDL2 distribution.
You need to have a working SDL2 library on your target system. You can obtain the source code (to build it yourself) or a prebuilt version at http://www.libsdl.org. Alternatively, on macOS and Windows, you can install the SDL2 binaries for PySDL2 using pip via the pysdl2-dll package.
PySDL2 also offers support for the following SDL-related libraries:
- SDL2_image (http://www.libsdl.org/projects/SDL_image/)
- SDL2_mixer (http://www.libsdl.org/projects/SDL_mixer/)
- SDL2_ttf (http://www.libsdl.org/projects/SDL_ttf/)
- SDL2_gfx (http://www.ferzkopp.net/Software/SDL_gfx-2.0/)
Those are optional though and only necessary if you want to use
sdl2.sdlimage
, sdl2.sdlmixer
, sdl2.sdlttf
or
sdl2.sdlgfx
.
Installation¶
You can either use the python way of installing the package or the make command using the Makefile on POSIX-compatible platforms, such as Linux or BSD, or the make.bat batch file on Windows platforms.
Simply type
python setup.py install
for the traditional python way or
make install
for using the Makefile or make.bat. Both will try to perform a default installation with as many features as possible.
Trying out¶
You also can test out PySDL2 without actually installing it. You just
need to set up your PYTHONPATH
to point to the location of the
source distribution package. On Windows-based platforms, you might use
something like
set PYTHONPATH=C:\path\to\pysdl2\:%PYTHONPATH%
to define the PYTHONPATH
on a command shell. On Linux/Unix, use
export PYTHONPATH=/path/to/pysdl2:$PYTHONPATH
for bourne shell compatibles or
setenv PYTHONPATH /path/to/pysdl2:$PYTHONPATH
for C shell compatibles. You can omit the :$PYTHONPATH, if you did not use it so far and if your environment settings do not define it.
Note
If you did not install SDL2 using the preferred way for your operation system, please read the information about Bundling SDL2 with pysdl2-dll in the section Integrating PySDL2.
Integrating PySDL2¶
PySDL2 consists of two packages, sdl2
, which is a plain 1:1 API
wrapper around the SDL2 API, and sdl2.ext
, which offers enhanced
functionality for sdl2
.
The sdl2
package is implemented in a way that shall make it easy for
you to integrate and deploy it with your own software projects. You can rely
on PySDL2 as third-party package, so that the user needs to install it
before he can use your software. Alternatively, you can just copy the
whole package into your project to ship it within your own project
bundle.
Bundling SDL2 with pysdl2-dll¶
The sdl2
package relies on an external SDL2 library for creating the
wrapper functions. This means that the user needs to have SDL2 installed or
that you ship a SDL2 library with your project.
For macOS, Windows, and most distributions of x86 Linux, the easiest and most
flexible way to bundle and install the SDL2 binaries with your project is via
the pysdl2-dll
package on PyPI,
which pysdl2 will load automatically if available. This approach allows you to
add the SDL2 binaries as a dependency for your project in a requirements.txt
file, a setup.py file, a Pipfile, or any other form of Python dependency
tracking. You can also specify a minimum version of the SDL2 binaries for your
project using this mechanism if your project depends on a function not
available in earlier versions of SDL2.
For platforms without any available pysdl2-dll binaries (e.g. Alpine Linux, ARM64 Linux, BSD), PySDL2 will still work as long as a recent version of SDL2 is installed using the system’s package manager. Additionally, pysdl2-dll will still install successfully on unsupported platforms as an empty pacakge with no binaries, making it safe to add as a dependency for cross-platform projects.
Bundling SDL2 without pysdl2-dll¶
If the user has a SDL2 library installed on the target system, the
ctypes
hooks of sdl2
try to find it in the OS-specific standard
locations via ctypes.util.find_library()
. If you are going to ship your
own SDL2 library with the project or cannot rely on the standard mechanism of
ctypes
, it is also possible to set the environment variable
PYSDL2_DLL_PATH
, which shall point to the directory of the SDL2
library or consist of a list of directories, in which the SDL2 libraries can
be found.
Note
PYSDL2_DLL_PATH
is preferred over the standard
mechanism. If the module finds a SDL2 library in PYSDL2_DLL_PATH
,
it will try to use that one in the first place, before using any SDL2
library installed on the target system.
Let’s assume, you ship your own library SDL2.dll within your project
location fancy_project/third_party. You can set the environment
variable PYSDL2_DLL_PATH
before starting Python.
# Win32 platforms
set PYSDL2_DLL_PATH=C:\path\to\fancy_project\third_party
# Unix/Posix-alike environments - bourne shells
export PYSDL2_DLL_PATH=/path/to/fancy_project/third_party
# Unix/Posix-alike environments - C shells
setenv PYSDL2_DLL_PATH /path/to/fancy_project/third_party
# Define multiple paths to search for the libraries - Win32
set PYSDL2_DLL_PATH=C:\first\path;C:\second\path
You also can set the environment variable within Python using
os.environ
.
dllpath = os.path.join('path', 'to', 'fancy_project', 'third_party')
os.environ["PYSDL2_DLL_PATH"] = dllpath
Note
If you aim to integrate sdl
directly into your software and do
not want or are not allowed to change the environment variables, you
can also change the os.getenv("PYSDL2_DLL_PATH")
query within the
sdl2/dll.py (or sdl2/sdlimage.py, sdl2/sdlttf.py, sdl2/sdlgfx.py)
file to point to the directory, in which you keep the DLL.
Using different SDL2 versions¶
PySDL2 tries to provide interfaces to the most recent versions of the SDL2 libraries. Sometimes this means that PySDL2 tries to test for functions that might not be available for your very own project or that are not available on the target system due to a version of the specific library.
If a PySDL2 function is called that requires a newer version of a binary than the one currently being used, it will raise a RuntimeWarning indicating the minimum version of SDL2 (or SDL2_mixer, or SDL2_ttf, etc.) required to use the called function. Additionally, if you already know what minimum versions your project needs, you can check the linked binary versions at runtime:
if not (sdl2.dll.version >= 2008 and sdl2.sdlttf.dll.version >= 2015):
err = ("This project requires SDL2 >= 2.0.8 and SDL2_ttf >= 2.0.15. "
"Please update your SDL2 binaries and relaunch.")
raise RuntimeError(err)
Binary version numbers are stored as 4-digit integers, with the first digit being the major release, the second digit being the minor release, and the last two digits indicating the patch level. Thus, SDL2 2.0.10 would be version 2010 and SDL2 2.0.6 would be 2006.
Known problems with SDL2 binaries¶
The SDL2 libraries are designed to be highly consistent across platforms, but there are some occasional bugs/issues with certain common binaries that can result in unexpected problems. Here are the ones we are currently aware of:
- Support for XCF images broken in SDL2_image
- Affects: official 32-bit Windows SDL2_image 2.0.5 binaries, all official macOS SDL2_image binaries
- Support for FLAC and MP3 missing by default in SDL2_mixer
- Affects: SDL2_mixer installed with Homebrew on macOS
- Support for FLAC/MP3/OGG/MID/MOD broken in SDL2_mixer
- Affects: official SDL2_mixer 2.0.2 binaries for macOS and Windows (fixed in 2.0.4)
PySDL2 FAQ¶
This is a list of Frequently Asked Questions about PySDL2. If you think, something is missing, please suggest it!
On importing…¶
… my script fails and complains that a SDL2 library could not be found!
Do you have the libraries properly installed? If on macOS or Windows, try runningpip install pysdl2-dll
and opening a fresh terminal to fix the problem. If on Linux or similar, did you follow the operating system’s way of installing or registering libraries? If you placed the libraries in some folder, make sure that thePYSDL2_DLL_PATH
environment variable points to the correct location.
… my script fails complaining that the found SDL2 library can’t be used!
Do you use a 64-bit operating system? Please make sure, that the Python interpreter and that the SDL2 libraries are either 64-bit ones or 32-bit ones. A 32-bit Python interpreter can’t deal with a 64-bit library and vice versa.
Using…¶
… the sdl2 API is weird. Why do you use the SDL_ prefix all the time?
The low-level APIs for SDL2, SDL2_mixer, SDL2_ttf, … shall represent a clean wrapping around the original C API calls. Thus, if you have to search for documentation or want to make a Python to C conversion (or C to Python), most of the code cleanly maps to the original API naming and layout and you do not have to think about whether you had to use SDL_ or TTF_ or whatever as prefix or suffix.
… the sdl2 API is does not comply to PEP-8. Please make it PEP-8 compatible.
Most of the API is PEP-8 compatible. The low-level bindings to SDL2 and related libraries however use the exact naming (including capital letters) as the functions or structures, they map to. See the previous entry for the reason of that.
How do I…¶
… save my surfaces as image files?
You can usesdl2.SDL_SaveBMP()
to save them as bitmap files. Other formats are currently unsupported, but might be added to thesdl2.ext
package in the future.
Font handling…¶
… is too hard. Why can’t it work the same way as pygame does?
Thesdl2.sdlttf
API does not know about platform-specific font locations and is unable to resolve font paths based on e.g. the font name or typeface. It’s not its job and PySDL2 likewise does not provide such functionality. If you need improved font detection support, you might want to take a look at the sysfont module of the python-utils project, which can be found at https://bitbucket.org/marcusva/python-utils/. That said, it’s usually a bad idea for a projects to rely on system fonts that may not be available on every computer: finding a free-use font you like and bundling it with your code is much safer.
Learn to fly - the tutorials¶
PySDL2 is easy to learn and a powerful multimedia programming framework. It features efficient high- and low-level structures and an excellent object-oriented programming layout.
The following tutorials will guide you through your first applications written with PySDL2 and introduces certain parts of the PySDL2 packages to you. They will most likely not cover each single part of PySDL2, but instead show you the most noteworthy features.
Hello World¶
Ahhh, the great tradition of saying “Hello World” in a programming language. To whet your appetite, we will do this with a most simple application, which will display an image. It is not important to understand everything at once, which will be used by the example. Nearly all parts used now are explained in later chapters, so do not hesitate, if the one or other explanation is missing.
Importing¶
Let’s start with importing some basic modules, which are necessary to display a small nice window and to do some basic drawing within that window.
import sys
import sdl2.ext
RESOURCES = sdl2.ext.Resources(__file__, "resources")
We need some resources from the resources
folder, so that we have a test
image around to display on the window later on. In your own applications, it is
unlikely that you will ever need to import them, but we need them here, so we
use the sdl2.ext.Resources
class to have them available.
Window creation and image loading¶
Any graphical application requires access to the screen, mostly in form of a window, which basically represents a portion of the screen, the application has access to and the application can manipulate. In most cases that portion has a border and title bar around it, allowing the user to move it around on the screen and reorganise everything in a way to fit his needs.
Once we have imported all necessary parts, let’s create a window to have access to the screen, so we can display the logo and thus represent it to the user.
sdl2.ext.init()
window = sdl2.ext.Window("Hello World!", size=(640, 480))
window.show()
factory = sdl2.ext.SpriteFactory(sdl2.ext.SOFTWARE)
sprite = factory.from_image(RESOURCES.get_path("hello.bmp"))
spriterenderer = factory.create_sprite_render_system(window)
spriterenderer.render(sprite)
First, we initialise the sdl2.ext
internals to gain access to the
screen and to be able to create windows on top of it. Once done with that,
sdl2.ext.Window
will create the window for us and we
supply a title to be shown on the window’s border along with its initial size.
Since sdl2.ext.Window
instances are not shown by default,
we have to tell the operating system and window manager that there is a new
window to display by calling sdl2.ext.Window.show()
.
Afterwards, we get an image from the resources folder and create a
sdl2.ext.Sprite
from it, which can be easily shown later
on. This is done via a sdl2.ext.SpriteFactory
, since the
factory allows us to switch between texture-based, hardware-accelerated, and
software-based sprites easily.
To display the image, we will use a sdl2.ext.SpriteRenderSystem
,
which supports the sprite type (texture- or software-based) and can copy the
image to the window for display. The sdl2.ext.SpriteRenderSystem
needs to know, where to copy to, thus we have to supply the window as target
for copy and display operations.
All left to do is to initiate the copy process by calling
sdl2.ext.SpriteRenderSystem.render()
with the image we
created earlier.
Tip
You will notice that the sprite used above will always be drawn at the
top-left corner of the sdl2.ext.Window
. You can change
the position of where to draw it by changing its
sdl2.ext.Sprite.position
value.
# will cause the renderer to draw the sprite 10px to the right and
# 20 px to the bottom
sprite.position = 10, 20
# will cause the renderer to draw the sprite 55px to the right and
# 10 px to the bottom
sprite.position = 55, 10
Experiment with different values to see their effect. Do not forget to do
this before spriterenderer.render(sprite)
is called.
Making the application responsive¶
We are nearly done now. We have an image to display, we have a window, where the image should be displayed on, so we can execute the written code, not?
Well, yes, but the only thing that will happen is that we will notice a short flickering before the application exits. Maybe we can even see the window with the image for a short moment, but that’s not what we want, do we?
To keep the window on the screen and to make it responsive to user input, such as closing the window, react upon the mouse cursor or key presses, we have to add a so-called event loop. The event loop will deal with certain types of actions happening on the window or while the window is focused by the user and - as long as the event loop is running - will keep the window shown on the screen.
processor = sdl2.ext.TestEventProcessor()
processor.run(window)
Since this is a very first tutorial, we keep things simple here and use a
dummy class for testing without actually dealing with the event loop magic.
By calling sdl2.ext.TestEventProcessor.run()
, we implicitly start an
event loop, which takes care of the most important parts for us.
And here it ends…¶
The window is shown, the image is shown, great! All left to do is to clean up
everything, once the application finishes. Luckily the
sdl2.ext.TestEventProcessor
knows when the window is closed, so
it will exit from the event loop. Once it exits, we should clean up the
video internals, we initialised at the beginning. Thus, a final call to
sdl2.ext.quit()
should be made.
The Pong Game¶
The following tutorial will show you some capabilities of the component-based approach, PySDL2 features. We will create the basics of a simple Pong game implementation here. The basics of creating a event loop, dealing with user input, moving images around and creating a rendering function are covered in this tutorial.
Getting started¶
We start with creating the window and add a small event loop, so we are able to close the window and exit the game.
import sys
import sdl2
import sdl2.ext
def run():
sdl2.ext.init()
window = sdl2.ext.Window("The Pong Game", size=(800, 600))
window.show()
running = True
while running:
events = sdl2.ext.get_events()
for event in events:
if event.type == sdl2.SDL_QUIT:
running = False
break
window.refresh()
return 0
if __name__ == "__main__":
sys.exit(run())
The import statements, video initialisation and window creation were
discussed previously in the Hello World tutorial. We import everything
from the sdl2
package here, too, to have all SDL2 functions available.
Instead of some integrated event processor, a new code fragment is introduced, though.
running = True
while running:
events = sdl2.ext.get_events()
for event in events:
if event.type == sdl2.SDL_QUIT:
running = False
break
window.refresh()
The while loop above is the main event loop of our application. It deals with
all kinds of input events that can occur when working with the window, such as
mouse movements, key strokes, resizing operations and so on. SDL handles a lot
for us when it comes to events, so all we need to do is to check, if there are
any events, retrieve each event one by one, and handle it, if necessary. For
now, we will just handle the sdl2.SDL_QUIT
event, which is raised when the
window is about to be closed.
In any other case we will just refresh the window’s graphics buffer, so it is updated and visible on-screen.
Adding the game world¶
The window is available and working. Now let’s take care of creating the game world, which will manage the player paddles, ball, visible elements and everything else. We are going to use an implementation layout loosely based on a COP [1] pattern, which separates data structures and functionality from each other. This allows us to change or enhance functional parts easily without having to refactor all classes we are implementing.
We start with creating the two player paddles and the rendering engine that will display them.
[...]
WHITE = sdl2.ext.Color(255, 255, 255)
class SoftwareRenderer(sdl2.ext.SoftwareSpriteRenderSystem):
def __init__(self, window):
super(SoftwareRenderer, self).__init__(window)
def render(self, components):
sdl2.ext.fill(self.surface, sdl2.ext.Color(0, 0, 0))
super(SoftwareRenderer, self).render(components)
class Player(sdl2.ext.Entity):
def __init__(self, world, sprite, posx=0, posy=0):
self.sprite = sprite
self.sprite.position = posx, posy
def run():
...
world = sdl2.ext.World()
spriterenderer = SoftwareRenderer(window)
world.add_system(spriterenderer)
factory = sdl2.ext.SpriteFactory(sdl2.ext.SOFTWARE)
sp_paddle1 = factory.from_color(WHITE, size=(20, 100))
sp_paddle2 = factory.from_color(WHITE, size=(20, 100))
player1 = Player(world, sp_paddle1, 0, 250)
player2 = Player(world, sp_paddle2, 780, 250)
running = True
while running:
events = sdl2.ext.get_events()
for event in events:
if event.type == sdl2.SDL_QUIT:
running = False
break
world.process()
if __name__ == "__main__":
sys.exit(run())
The first thing to do is to enhance the
sdl2.ext.SoftwareSpriteRenderSystem
so that it will paint
the whole window screen black on every drawing cycle, before drawing all
sprites on the window.
Afterwards, the player paddles will be implemented, based on an
sdl2.ext.Entity
data container. The player paddles are
simple rectangular sprites that can be positioned anywhere on the
window.
In the main program function, we put those things together by creating a
sdl2.ext.World
, in which the player paddles and the renderer
can live and operate.
Within the main event loop, we allow the world to process all attached
systems, which causes it to invoke the process()
methods for all
sdl2.ext.System
instances added to it.
Moving the ball¶
We have two static paddles centred vertically on the left and right of our window. The next thing to do is to add a ball that can move around within the window boundaries.
[...]
class MovementSystem(sdl2.ext.Applicator):
def __init__(self, minx, miny, maxx, maxy):
super(MovementSystem, self).__init__()
self.componenttypes = Velocity, sdl2.ext.Sprite
self.minx = minx
self.miny = miny
self.maxx = maxx
self.maxy = maxy
def process(self, world, componentsets):
for velocity, sprite in componentsets:
swidth, sheight = sprite.size
sprite.x += velocity.vx
sprite.y += velocity.vy
sprite.x = max(self.minx, sprite.x)
sprite.y = max(self.miny, sprite.y)
pmaxx = sprite.x + swidth
pmaxy = sprite.y + sheight
if pmaxx > self.maxx:
sprite.x = self.maxx - swidth
if pmaxy > self.maxy:
sprite.y = self.maxy - sheight
class Velocity(object):
def __init__(self):
super(Velocity, self).__init__()
self.vx = 0
self.vy = 0
class Player(sdl2.ext.Entity):
def __init__(self, world, posx=0, posy=0):
[...]
self.velocity = Velocity()
class Ball(sdl2.ext.Entity):
def __init__(self, world, sprite, posx=0, posy=0):
self.sprite = sprite
self.sprite.position = posx, posy
self.velocity = Velocity()
def run():
[...]
sp_ball = factory.from_color(WHITE, size=(20, 20))
[...]
movement = MovementSystem(0, 0, 800, 600)
spriterenderer = SoftwareRenderer(window)
world.add_system(movement)
world.add_system(spriterenderer)
[...]
ball = Ball(world, sp_ball, 390, 290)
ball.velocity.vx = -3
[...]
Two new classes are introduced here, Velocity
and
MovementSystem
. The Velocity
class is a simple data bag. It
does not contain any application logic, but consists of the relevant
information to represent the movement in a certain direction. This
allows us to mark in-game items as being able to move around.
The MovementSystem
in turn takes care of moving the in-game items around
by applying the velocity to their current position. Thus, we can simply enable
any Player
instance to be movable or not by adding or removing a
velocity attribute to them, which is a Velocity
component instance.
Note
The naming is important here. The EBS implementation as described in
Working with component-based entities requires every in-application or in-game item attribute
bound to a sdl2.ext.Entity
to be the lowercase class name of its
related component.
Player.vel = Velocity(10, 10)
for example would raise an exception, since the system expects
Player.vel
to be an instance of a Vel
component.
The MovementSystem
is a specialised sdl2.ext.System
, a
sdl2.ext.Applicator
, which can operate on combined sets of
data. When the sdl2.ext.Applicator.process()
method is
called, the passed componentsets
iterable will contain tuples of
objects that belong to an instance and feature a certain type. The
MovementSystem
’s process()
implementation hence will loop over
sets of Velocity
and Sprite
instances that belong to the same
sdl2.ext.Entity
. Since we have a ball and two players
currently available, it typically would loop over three tuples, two for
the individual players and one for the ball.
The sdl2.ext.Applicator
thus enables us to process combined
data of our in-game items, without creating complex data structures.
Note
Only entities that contain all attributes (components) are taken
into account. If e.g. the Ball
class would not contain a
Velocity
component, it would not be processed by the
MovementSystem
.
Why do we use this approach? The sdl2.ext.Sprite
objects carry a
position, which defines the location at which they should be rendered, when
processed by the SoftwareRenderer
. If they should move around (which is
a change in the position), we need to apply the velocity to them.
We also define some more things within the MovementSystem
, such as a
simple boundary check, so that the players and ball cannot leave the
visible window area on moving around.
Bouncing¶
We have a ball that can move around as well as the general game logic
for moving things around. In contrast to a classic OO approach we do not
need to implement the movement logic within the Ball
and Player
class individually, since the basic movement is the same for all (yes,
you could have solved that with inheriting Ball
and Player
from
a MovableObject
class in OO).
The ball now moves and stays within the bounds, but once it hits the left side, it will stay there. To make it bouncy, we need to add a simple collision system, which causes the ball to change its direction on colliding with the walls or the player paddles.
[...]
class CollisionSystem(sdl2.ext.Applicator):
def __init__(self, minx, miny, maxx, maxy):
super(CollisionSystem, self).__init__()
self.componenttypes = Velocity, sdl2.ext.Sprite
self.ball = None
self.minx = minx
self.miny = miny
self.maxx = maxx
self.maxy = maxy
def _overlap(self, item):
pos, sprite = item
if sprite == self.ball.sprite:
return False
left, top, right, bottom = sprite.area
bleft, btop, bright, bbottom = self.ball.sprite.area
return (bleft < right and bright > left and
btop < bottom and bbottom > top)
def process(self, world, componentsets):
collitems = [comp for comp in componentsets if self._overlap(comp)]
if collitems:
self.ball.velocity.vx = -self.ball.velocity.vx
def run():
[...]
world = World()
movement = MovementSystem(0, 0, 800, 600)
collision = CollisionSystem(0, 0, 800, 600)
spriterenderer = SoftwareRenderer(window)
world.add_system(movement)
world.add_system(collision)
world.add_system(spriterenderer)
[...]
collision.ball = ball
running = True
while running:
events = sdl2.ext.get_events()
for event in events:
if event.type == sdl2.SDL_QUIT:
running = False
break
sdl2.SDL_Delay(10)
world.process()
if __name__ == "__main__":
sys.exit(run())
The CollisionSystem
only needs to take care of the ball and objects
it collides with, since the ball is the only unpredictable object within our
game world. The player paddles will only be able to move up and down
within the visible window area and we already dealt with that within the
MovementSystem
code.
Whenever the ball collides with one of the paddles, its movement direction (velocity) should be inverted, so that it bounces back.
Additionally, we won’t run at the full processor speed anymore in the
main loop, but instead add a short delay, using the
sdl2.SDL_Delay()
function. This reduces the overall load on the
CPU and makes the game a bit slower.
Reacting on player input¶
We have a moving ball that bounces from side to side. The next step would be to allow moving one of the paddles around, if the player presses a key. The SDL event routines allow us to deal with a huge variety of user and system events that could occur for our application, but right now we are only interested in key strokes for the Up and Down keys to move one of the player paddles up or down.
[...]
def run():
[...]
running = True
while running:
events = sdl2.ext.get_events()
for event in events:
if event.type == sdl2.SDL_QUIT:
running = False
break
if event.type == sdl2.SDL_KEYDOWN:
if event.key.keysym.sym == sdl2.SDLK_UP:
player1.velocity.vy = -3
elif event.key.keysym.sym == sdl2.SDLK_DOWN:
player1.velocity.vy = 3
elif event.type == sdl2.SDL_KEYUP:
if event.key.keysym.sym in (sdl2.SDLK_UP, sdl2.SDLK_DOWN):
player1.velocity.vy = 0
sdl2.SDL_Delay(10)
world.process()
if __name__ == "__main__":
sys.exit(run())
Every event that can occur and that is supported by SDL2 can be identified by a
static event type code. This allows us to check for a key stroke, mouse button
press, and so on. First, we have to check for sdl2.SDL_KEYDOWN
and
sdl2.SDL_KEYUP
events, so we can start and stop the paddle movement on
demand. Once we identified such events, we need to check, whether the pressed
or released key is actually the Up or Down key, so that we do not start or stop
moving the paddle, if the user presses R or G or whatever.
Whenever the Up or Down key are pressed down, we allow the left player paddle to move by changing its velocity information for the vertical direction. Likewise, if either of those keys is released, we stop moving the paddle.
Improved bouncing¶
We have a moving paddle and we have a ball that bounces from one side to another, which makes the game … quite boring. If you played Pong before, you know that most variations of it will cause the ball to bounce in a certain angle, if it collides with a paddle. Most of those implementations achieve this by implementing the paddle collision as if the ball collides with a rounded surface. If it collides with the center of the paddle, it will bounce back straight, if it hits the paddle near the center, it will bounce back with a pointed angle and on the corners of the paddle it will bounce back with some angle close to 90 degrees to its initial movement direction.
class CollisionSystem(sdl2.ext.Applicator):
[...]
def process(self, world, componentsets):
collitems = [comp for comp in componentsets if self._overlap(comp)]
if collitems:
self.ball.velocity.vx = -self.ball.velocity.vx
sprite = collitems[0][1]
ballcentery = self.ball.sprite.y + self.ball.sprite.size[1] // 2
halfheight = sprite.size[1] // 2
stepsize = halfheight // 10
degrees = 0.7
paddlecentery = sprite.y + halfheight
if ballcentery < paddlecentery:
factor = (paddlecentery - ballcentery) // stepsize
self.ball.velocity.vy = -int(round(factor * degrees))
elif ballcentery > paddlecentery:
factor = (ballcentery - paddlecentery) // stepsize
self.ball.velocity.vy = int(round(factor * degrees))
else:
self.ball.velocity.vy = - self.ball.velocity.vy
The reworked processing code above simulates a curved paddle by creating segmented areas, which cause the ball to be reflected in different angles. Instead of doing some complex trigonometry to calculate an accurate angle and transform it on a x/y plane, we simply check, where the ball collided with the paddle and adjust the vertical velocity.
If the ball now hits a paddle, it can be reflected at different angles, hitting the top and bottom window boundaries… and will stay there. If it hits the window boundaries, it should be reflected, too, but not with a varying angle, but with the exact angle, it hit the boundary with. This means that we just need to invert the vertical velocity, once the ball hits the top or bottom.
class CollisionSystem(sdl2.ext.Applicator):
[...]
def process(self, world, componentsets):
[...]
if (self.ball.sprite.y <= self.miny or
self.ball.sprite.y + self.ball.sprite.size[1] >= self.maxy):
self.ball.velocity.vy = - self.ball.velocity.vy
if (self.ball.sprite.x <= self.minx or
self.ball.sprite.x + self.ball.sprite.size[0] >= self.maxx):
self.ball.velocity.vx = - self.ball.velocity.vx
Creating an enemy¶
Now that we can shoot back the ball in different ways, it would be nice to have an opponent to play against. We could enhance the main event loop to recognise two different keys and manipulate the second paddle’s velocity for two people playing against each other. We also could create a simple computer-controlled player that tries to hit the ball back to us, which sounds more interesting.
class TrackingAIController(sdl2.ext.Applicator):
def __init__(self, miny, maxy):
super(TrackingAIController, self).__init__()
self.componenttypes = PlayerData, Velocity, sdl2.ext.Sprite
self.miny = miny
self.maxy = maxy
self.ball = None
def process(self, world, componentsets):
for pdata, vel, sprite in componentsets:
if not pdata.ai:
continue
centery = sprite.y + sprite.size[1] // 2
if self.ball.velocity.vx < 0:
# ball is moving away from the AI
if centery < self.maxy // 2:
vel.vy = 3
elif centery > self.maxy // 2:
vel.vy = -3
else:
vel.vy = 0
else:
bcentery = self.ball.sprite.y + self.ball.sprite.size[1] // 2
if bcentery < centery:
vel.vy = -3
elif bcentery > centery:
vel.vy = 3
else:
vel.vy = 0
class PlayerData(object):
def __init__(self):
super(PlayerData, self).__init__()
self.ai = False
class Player(sdl2.ext.Entity):
def __init__(self, world, sprite, posx=0, posy=0, ai=False):
self.sprite = sprite
self.sprite.position = posx, posy
self.velocity = Velocity()
self.playerdata = PlayerData()
self.playerdata.ai = ai
def run():
[...]
aicontroller = TrackingAIController(0, 600)
world.add_system(aicontroller)
world.add_system(movement)
world.add_system(collision)
world.add_system(spriterenderer)
player1 = Player(world, sp_paddle1, 0, 250)
player2 = Player(world, sp_paddle2, 780, 250, True)
[...]
aicontroller.ball = ball
[...]
We start by creating a component PlayerData
that flags a player as
being AI controlled or not. Afterwards, a TrackingAIController
is
implemented, which, depending on the information of the PlayerData
component, will move the specific player paddle around by manipulating
its velocity information.
The AI is pretty simple, just following the ball’s vertical movement, trying to hit it at its center, if the ball moves into the direction of the AI-controlled paddle. As soon as the ball moves away from the paddle, the paddle will move back to the vertical center.
Tip
Add True
as last parameter to the first Player()
constructor to
see two AIs playing against each other.
Next steps¶
We created the basics of a Pong game, which can be found in the examples folder. However, there are some more things to do, such as
- resetting the ball to the center with a random vertical velocity, if it hits either the left or right window bounds
- adding the ability to track the points made by either player, if the ball hit the left or right side
- drawing a dashed line in the middle to make the game field look nicer
- displaying the points made by each player
It is your turn now to implement these features. Go ahead, it is not as complex as it sounds.
you can reset the ball’s position in the
CollisionSystem
code, by changing the code for theminx
andmaxx
testyou could enhance the
CollisionSystem
to processPlayerData
components and add the functionality to add points there (or write a small processor that keeps track of the ball only and processes only thePlayerData
andvideo.SoftSprite
objects of each player for adding points). Alternatively, you could use thesdl2.ext.EventHandler
class to raise a score count function within theCollisionSystem
, if the ball collides with one of the paddles.write an own render sytem, based on
sdl2.ext.Applicator
, which takes care of position and sprite setsStaticRepeatingSprite(Entity): ... self.positions = Positions((400, 0), (400, 60), (400, 120), ...) ...draw some simple images for 0-9 and render them as sprites, depending on the points a player made.
Footnotes
[1] | Component-Oriented Programming |
PySDL2 for Pygamers¶
Care to move to a newer SDL with your Pygame knowledge? Then you should know one thing or two about PySDL2 before hacking code, since it is completely different from Pygame. Do not let that fact scare you away, the basics with graphics and sound are still the same (as they are fundamental), but you will not find many similarities to the Pygame API within PySDL2.
Todo
More details, examples, etc.
Technical differences¶
Pygame is implemented as a mixture of Python, C and Assembler code,
wrapping 3rd party libraries with CPython API interfaces. PySDL2 in
contrast is written in pure Python, using ctypes
to interface
with the C interfaces of 3rd party libraries.
API differences¶
pygame¶
pygame | sdl2 |
---|---|
init() |
sdl2.SDL_Init() where appropriate |
quit() |
sdl2.SDL_Quit() where appropriate |
error |
No equivalent |
get_error() |
sdl2.SDL_GetError() |
set_error() |
sdl2.SDL_SetError() |
get_sdl_version() |
sdl2.SDL_GetVersion() |
get_sdl_byteorder() |
sdl2.SDL_BYTEORDER |
register_quit() |
No equivalent planned |
encode_string() |
No equivalent planned |
encode_file_path() |
No equivalent planned |
sdl2.ext.init()
initializes only the video subsystem. By comparison,
pygame.init()
initializes all Pygame submodules (which includes
initializing other SDL subsystems).
pygame.cdrom¶
PySDL2 does not feature any CD-ROM related interfaces. They were removed in SDL2 and PySDL2 does not provide its own facilities.
pygame.Color¶
You can find a similar class in sdl2.ext.Color
. It does
not feature a set_length()
or correct_gamma()
method, though.
pygame.cursors¶
PySDL2 does not feature any pre-defined cursor settings at the moment.
pygame.display¶
pygame.display | sdl2 |
---|---|
init() |
sdl2.ext.init() |
quit() |
sdl2.ext.quit() |
get_init() |
sdl2.SDL_WasInit() |
set_mode() |
sdl2.ext.Window |
get_surface() |
sdl2.ext.Window.get_surface() |
flip() |
sdl2.ext.Window.refresh() |
update() |
sdl2.ext.Window.refresh() |
get_driver() |
sdl2.SDL_GetCurrentVideoDriver() |
Info |
No equivalent |
get_wm_info() |
sdl2.SDL_GetWindowWMInfo() |
list_modes() |
sdl2.SDL_GetNumDisplayModes() |
mode_ok() |
sdl2.SDL_GetClosestDisplayMode() |
gl_get_attribute() |
sdl2.SDL_GL_GetAttribute() |
gl_set_attribute() |
sdl2.SDL_GL_SetAttribute() |
get_active() |
No equivalent |
iconify() |
sdl2.ext.Window.minimize() |
toggle_fullscreen() |
sdl2.SDL_SetWindowFullscreen() |
set_gamma() |
sdl2.SDL_SetWindowBrightness() |
set_gamma_ramp() |
sdl2.SDL_SetWindowGammaRamp.() |
set_icon() |
sdl2.SDL_SetWindowIcon() |
set_caption() |
sdl2.ext.Window.title |
get_caption() |
sdl2.ext.Window.title |
set_palette() |
sdl2.SDL_SetSurfacePalette() |
pygame.draw¶
Drawing primitives can be accessed through either the
sdl2.SDL_RenderDraw*()
and sdl2.SDL_RenderFill*()
functions or
the more powerful sdl2.sdlgfx
module,
pygame.event¶
pygame.event | sdl2 |
---|---|
pump() |
sdl2.SDL_PumpEvents() |
get() |
sdl2.SDL_PollEvent() or sdl2.ext.get_events() |
poll() |
sdl2.SDL_PollEvent() |
wait() |
sdl2.SDL_WaitEvent() |
peek() |
sdl2.SDL_PeepEvents() |
clear() |
sdl2.SDL_FlushEvents() |
event_name() |
No equivalent |
set_blocked() |
sdl2.SDL_EventState() |
get_blocked() |
sdl2.SDL_EventState() |
set_allowed() |
sdl2.SDL_EventState() |
set_grab() |
sdl2.SDL_SetWindowGrab() |
get_grab() |
sdl2.SDL_GetWindowGrab() |
post() |
sdl2.SDL_PeepEvents() |
Event |
sdl2.SDL_Event |
pygame.font¶
pygame.font | sdl2 |
---|---|
init() |
sdl2.sdlttf.TTF_Init() |
quit() |
sdl2.sdlttf.TTF_Quit() |
get_init() |
sdl2.sdlttf.TTF_WasInit() |
get_default_font() |
No equivalent planned [1] |
get_fonts() |
No equivalent planned [1] |
match_font() |
No equivalent planned [1] |
SysFont |
No equivalent planned [1] |
Font |
No equivalent planned [1] |
pygame.freetype¶
PySDL2 does not feature direct FreeType support.
pygame.gfxdraw¶
PySDL2 offers SDL_gfx support through the sdl2.sdlgfx
module.
pygame.image¶
pygame.image | sdl2 |
---|---|
load() |
sdl2.sdlimage.IMG_Load() ,
sdl2.ext.load_image() |
save() |
sdl2.surface.SDL_SaveBMP() ,
sdl2.sdlimage.IMG_SavePNG() |
get_extended() |
sdl2.sdlimage.IMG_isBMP() et al. |
tostring() |
No equivalent yet |
fromstring() |
No equivalent yet |
frombuffer() |
No equivalent yet |
pygame.joystick¶
pygame.joystick | sdl2 |
---|---|
init() |
sdl2.SDL_Init() |
quit() |
sdl2.SDL_Quit() |
get_init() |
sdl2.SDL_WasInit() |
get_count() |
sdl2.joystick.SDL_NumJoysticks() |
Joystick() |
sdl2.joystick.SDL_Joystick and related
functions |
pygame.key¶
pygame.key | sdl2 |
---|---|
get_focused() |
sdl2.keyboard.SDL_GetKeyboardFocus() |
get_pressed() |
sdl2.keyboard.SDL_GetKeyboardState() |
get_mods() |
sdl2.keyboard.SDL_GetModState() |
set_mods() |
sdl2.keyboard.SDL_SetModState() |
set_repeat() |
Based on the OS/WM settings, no equivalent |
get_repeat() |
Based on the OS/WM settings, no equivalent |
name() |
sdl2.keyboard.SDL_GetKeyName() |
pygame.locals¶
Constants in PySDL2 are spread across the different packages and modules, depending on where they originate from.
pygame.mixer¶
pygame.mixer | sdl2 |
---|---|
init() |
sdl2.sdlmixer.Mix_Init() |
quit() |
sdl2.sdlmixer.Mix_Quit() |
get_init() |
No equivalent planned |
stop() |
sdl2.sdlmixer.Mix_HaltChannel() ,
sdl2.sdlmixer.Mix_HaltGroup() ,
sdl2.sdlmixer.Mix_HaltMusic() |
pause() |
sdl2.sdlmixer.Mix_Pause() ,
sdl2.sdlmixer.Mix_PauseMusic() |
unpause() |
sdl2.sdlmixer.Mix_Resume() ,
sdl2.sdlmixer.Mix_ResumeMusic() |
fadeout() |
sdl2.sdlmixer.Mix_FadeOutChannel() ,
sdl2.sdlmixer.Mix_FadeOutGroup() ,
sdl2.sdlmixer.Mix_FadeOutMusic() |
set_num_channels() |
sdl2.sdlmixer.Mix_AllocateChannels() |
get_num_channels() |
sdl2.sdlmixer.Mix_AllocateChannels() |
set_reserved() |
sdl2.sdlmixer.Mix_ReserveChannels() |
find_channel() |
No equivalent planned |
get_busy() |
sdl2.sdlmixer.Mix_ChannelFinished() |
Sound |
sdl2.sdlmixer.Mix_Chunk |
Channel |
No equivalent, use the channel functions instead |
pygame.mixer.music¶
See pygame.mixer.
pygame.mouse¶
pygame.mouse | sdl2 |
---|---|
get_pressed() |
sdl2.mouse.SDL_GetMouseState() |
get_pos() |
sdl2.mouse.SDL_GetMouseState() |
get_rel() |
sdl2.mouse.SDL_GetRelativeMouseState() |
set_pos() |
sdl2.mouse.SDL_WarpMouseInWindow() |
set_visible() |
sdl2.mouse.SDL_ShowCursor() |
get_focused() |
sdl2.mouse.SDL_GetMouseFocus() |
set_cursor() |
sdl2.mouse.SDL_GetCursor() |
get_cursor() |
sdl2.mouse.SDL_SetCursor() |
pygame.movie¶
No such module is planned for PySDL2.
pygame.Overlay¶
You can work with YUV overlays by using the sdl2.render
module
with sdl2.render.SDL_Texture
objects.
pygame.PixelArray¶
You can access pixel data of sprites and surfaces directly via the
sdl2.ext.PixelView
class. It does not feature comparison or
extractions methods.
pygame.Rect¶
No such functionality is available for PySDL2. Rectangles are represented
via sdl2.rect.SDL_Rect
for low-level SDL2 wrappers or 4-value
tuples.
pygame.scrap¶
PySDL2 offers basic text-based clipboard access via the
sdl2.clipboard
module. A feature-rich clipboard API as for Pygame
does not exist yet.
pygame.sndarray¶
No such module is available for PySDL2 yet.
pygame.sprite¶
PySDL2 uses a different approach of rendering and managing sprite
objects via a component-based system and the sdl2.ext.Sprite
class. A sprite module as for Pygame is not planned.
pygame.Surface¶
pygame.Surface | sdl2 |
---|---|
blit() |
sdl2.surface.SDL_BlitSurface() ,
sdl2.ext.SpriteRenderSystem |
convert() |
sdl2.surface.SDL_ConvertSurface() |
convert_alpha() |
sdl2.surface.SDL_ConvertSurface() |
copy() |
sdl2.surface.SDL_ConvertSurface() |
fill() |
sdl2.surface.SDL_FillRect() ,
sdl2.surface.SDL_FillRects() ,
sdl2.ext.fill() |
scroll() |
No equivalent planned |
set_colorkey() |
sdl2.surface.SDL_SetColorKey() |
get_colorkey() |
sdl2.surface.SDL_GetColorKey() |
set_alpha() |
sdl2.surface.SDL_SetSurfaceAlphaMod() |
get_alpha() |
sdl2.surface.SDL_GetSurfaceAlphaMod() |
lock() |
sdl2.surface.SDL_LockSurface() |
unlock() |
sdl2.surface.SDL_UnlockSurface() |
mustlock() |
sdl2.surface.SDL_MUSTLOCK() |
get_locked() |
sdl2.surface.SDL_Surface.locked |
get_locks() |
No equivalent planned |
get_at() |
Direct access to the pixels for surfaces can be
achieved via the sdl2.ext.PixelView class |
set_at() |
Direct access to the pixels for surfaces can be
achieved via the sdl2.ext.PixelView class |
get_at_mapped() |
No equivalent planned |
get_palette() |
via sdl2.surface.SDL_Surface.format and the
sdl2.pixels.SDL_PixelFormat.palette
attribute |
get_palette_at() |
sdl2.pixels.SDL_Palette.colors[offset] |
set_palette() |
sdl2.surface.SDL_SetSurfacePalette() |
set_palette_at() |
sdl2.pixels.SDL_Palette.colors[offset] |
map_rgb() |
sdl2.pixels.SDL_MapRGB() |
unmap_rgb() |
sdl2.pixels.SDL_GetRGB() |
set_clip() |
sdl2.surface.SDL_SetClipRect() |
get_clip() |
sdl2.surface.SDL_GetClipRect() |
subsurface() |
sdl2.ext.subsurface() |
get_parent() |
No equivalent yet |
get_abs_parent() |
As for get_parent |
get_offset() |
As for get_parent |
get_abs_offset() |
As for get_parent |
get_size() |
sdl2.ext.Sprite.size ,
sdl2.surface.SDL_Surface.w ,
sdl2.surface.SDL_Surface.h |
get_width() |
sdl2.ext.Sprite.size[0] ,
sdl2.surface.SDL_Surface.w , |
get_height() |
sdl2.ext.Sprite.size[1] ,
sdl2.surface.SDL_Surface.h |
get_rect() |
No equivalent planned |
get_bitsize() |
sdl2.pixels.SDL_PixelFormat.BitsPerPixel |
get_bytesize() |
sdl2.pixels.SDL_PixelFormat.BytesPerPixel |
get_flags() |
sdl2.surface.SDL_Surface.flags |
get_pitch() |
sdl2.surface.SDL_Surface.pitch |
get_masks() |
sdl2.pixels.SDL_PixelFormat.Rmask , … |
get_shifts() |
sdl2.pixels.SDL_PixelFormat.Rshift , … |
get_losses() |
sdl2.pixels.SDL_PixelFormat.Rloss , … |
get_bounding_rect() |
No equivalent planned |
get_view() |
sdl2.ext.PixelView |
get_buffer() |
sdl2.ext.PixelView or
sdl2.surface.SDL_Surface.pixels |
pygame.surfarray¶
2D and 3D pixel access can be achieved via the
sdl2.ext.PixelView
class in environments without
numpy. Simplified numpy-array creation with direct pixel access (similar
to pygame.surfarray.pixels2d()
and pygame.surfarray.pixels3d()
)
is available via sdl2.ext.pixels2d()
and
sdl2.ext.pixels3d()
.
pygame.time¶
pygame.time | sdl2 |
---|---|
get_ticks() |
sdl2.timer.SDL_GetTicks() |
wait() |
sdl2.timer.SDL_Delay() |
delay() |
sdl2.timer.SDL_Delay() |
Clock |
No equivalent planned |
pygame.transform¶
The are no transformation helpers in PySDL2 at moment. Those might be implemented later on via numpy helpers, the Python Imaging Library or other 3rd party packages.
API Reference¶
API reference¶
This is the core documentation of the various modules, classes and functions PySDL2 offers. If you want to have a quick overview about the modules, use the Module Index. If you just want to look up a specific class, method or function, use the Index or Search Page.
sdl2 - SDL2 library wrapper¶
The sdl2
package is a ctypes
-based wrapper around
the SDL2 library. It wraps nearly all publicly accessible structures and
functions of the SDL2 library to be accessible from Python code.
A detailed documentation about the behaviour of the different functions can found within the SDL2 documentation.
Usage¶
You can use sdl2
in nearly exactly the same way as you would do with
the SDL library and C code.
A brief example in C code:
#include <SDL.h>
int main(int argc, char *argv[]) {
int running;
SDL_Window *window;
SDL_Surface *windowsurface;
SDL_Surface *image;
SDL_Event event;
SDL_Init(SDL_INIT_VIDEO);
window = SDL_CreateWindow("Hello World",
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
592, 460, SDL_WINDOW_SHOWN);
windowsurface = SDL_GetWindowSurface(window);
image = SDL_LoadBMP("exampleimage.bmp");
SDL_BlitSurface(image, NULL, windowsurface, NULL);
SDL_UpdateWindowSurface(window);
SDL_FreeSurface(image);
running = 1;
while (running) {
while (SDL_PollEvent(&event) != 0) {
if (event.type == SDL_QUIT) {
running = 0;
break;
}
}
}
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
Doing the same in Python:
import sys
import ctypes
from sdl2 import *
def main():
SDL_Init(SDL_INIT_VIDEO)
window = SDL_CreateWindow(b"Hello World",
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
592, 460, SDL_WINDOW_SHOWN)
windowsurface = SDL_GetWindowSurface(window)
image = SDL_LoadBMP(b"exampleimage.bmp")
SDL_BlitSurface(image, None, windowsurface, None)
SDL_UpdateWindowSurface(window)
SDL_FreeSurface(image)
running = True
event = SDL_Event()
while running:
while SDL_PollEvent(ctypes.byref(event)) != 0:
if event.type == SDL_QUIT:
running = False
break
SDL_DestroyWindow(window)
SDL_Quit()
return 0
if __name__ == "__main__":
sys.exit(main())
You can port code in a straightforward manner from one language to the other,
though it is important to know about the limitations and slight differences
mentioned below. Also, PySDL2 offers advanced functionality, which also feels
more ‘pythonic’, via the sdl2.ext
package.
Missing interfaces¶
The following functions, classes, constants and macros of SDL2 are not
available within sdl2
.
SDL_REVISION
andSDL_REVISION_NUMBER
fromSDL_revision.h
SDL_NAME()
fromSDL_name.h
SDL_MostSignificantBitIndex32()
and SDL_HasExactlyOneBitSet32 fromSDL_bits.h
- Most functions from
SDL2_stdinc.h
(most are for math and string operations Python already has excellent built-in support for) - Everything from
SDL_main.h
- Everything from
SDL_system.h
- Everything from
SDL_assert.h
- Everything from
SDL_thread.h
- Everything from
SDL_atomic.h
- Everything from
SDL_opengl.h
(see PyOpenGL for a compatible OpenGL API) - Everything from
SDL_mutex.h
Additional interfaces¶
The following functions, classes, constants and macros are not part of
SDL2, but were introduced by sdl2
.
-
sdl2.
ALL_PIXELFORMATS
¶ Tuple containing all SDL2 pixel format constants (SDL_PIXELFORMAT_INDEX1LSB, …, SDL_PIXELFORMAT_RGB565, …).
-
sdl2.
AUDIO_FORMATS
¶ Set containing all SDL2 audio format constants (AUDIO_U8, AUDIO_S8, … AUDIO_F32LSB, … ).
-
sdl2.
rw_from_object
(obj : object) → SDL_RWops¶ Creates a SDL_RWops from any Python object. The Python object must at least support the following methods:
read(length) -> data
length is the size in bytes to be read. A call to len(data) must return the correct amount of bytes for the data, so that len(data) / [size in bytes for a single element from data] returns the amount of elements. Must raise an error on failure.seek(offset, whence) -> int
offset denotes the offset to move the read/write pointer of the object to. whence indicates the movement behaviour and can be one of the following values:
- RW_SEEK_SET - move to offset from the start of the file
- RW_SEEK_CUR - move by offset from the relative location
- RW_SEEK_END - move to offset from the end of the file
If it could not move read/write pointer to the desired location, an error must be raised.
tell() -> int
Must return the current offset. This method must only be provided, if seek() does not return any value.close() -> None
Closes the object(or its internal data access methods). Must raise an error on failure.write(data) -> None
Writes the passed data(which is a string of bytes) to the object. Must raise an error on failure.
Note
The write() method is optional and only necessary, if the passed object should be able to write data.
The returned
sdl2.SDL_RWops
is a pure Python object and must not be freed viasdl2.SDL_FreeRW()
.
sdl2.ext - Helpful wrappers for the SDL2 API¶
The sdl2.ext
module provides a rich set of modules, classes, and
functions for creating games and other applications using PySDL2.
The aim of the sdl2.ext module is to wrap commonly-used parts of the SDL2 API
in a more friendly and Pythonic manner, reducing the need for developers to
understand the intricacies of working with ctypes
and making it simpler and
more fun to get PySDL2 programs up and running.
In addition, this module provides a number of template classes and utility functions for working with colors, input events, file assets, and more.
Due to its broad scope, the sdl2.ext
module is divided into a number of
submodules. However, everything in these submodules can be imported directly
from sdl2.ext
. For example, from sdl2.ext import Window
is exactly
the same as from sdl2.ext.window import Window
.
SDL2 module wrappers¶
Some parts of the sdl2.ext
module contain Pythonic wrappers for common
and/or unpleasant parts of the SDL2 API. At present, these modules include:
sdl2.ext.common - Frequently Used SDL2 Functions¶
This module wraps various common SDL2 functions, including methods for initializing and quitting SDL2 and its various subsystems and for retrieving events from the SDL2 event queue.
sdl2.ext.window - Creating and Working with SDL2 Windows¶
This module contains the Window
class, which provides a friendly Pythonic
API for creating, moving, resizing, closing, or otherwise working with SDL2
windows.
sdl2.ext.renderer - Accelerated 2D Rendering¶
The sdl2.ext.renderer
module implements a Pythonic interface for working
with the SDL Renderer API, which allows for easy hardware-accelerated 2D
rendering with backends for a number of platforms (e.g. OpenGL, Direct3D, Metal)
as well as a software-accelerated fallback.
sdl2.ext.msgbox - Displaying Alerts and Dialog Boxes¶
The sdl2.ext.msgbox
module provides a Pythonic interface for working
with the SDL MesssageBox API. These classes and functions make it easy to
create user prompts and simple alerts using the system’s window manager.
Utilities for working with SDL2 in Python¶
Other parts of this module implement functions and classes that make it easier
to work with SDL2 functions and data structures in Python (and vice versa).
These include functions for converting text to and from UTF8-encoded bytes,
reading and writing ctypes
arrays, and casting SDL_Surface
objects to Numpy arrays:
sdl2.ext.compat - Python Version Compatibility Helpers¶
The sdl2.ext.compat
module provides various helper functions for writing
code that works seamlessly on both Python 2.7 and Python 3.x.
-
ISPYTHON2
¶ True
, if executed in a Python 2.x compatible interpreter,False
otherwise.
-
ISPYTHON3
¶ True
, if executed in a Python 3.x compatible interpreter,False
otherwise.
sdl2.ext.array - Tools for Working with ctypes Arrays¶
This module provides a number of utilites for accessing data in ctypes
arrays and converting ctypes
arrays into common Python formats.
Warning
These functions are primarily meant for internal use in PySDL2, and should be treated as experimental.
Providing read-write access for sequential data¶
Two classes allow you to access sequential data in different ways. The
CTypesView
provides byte-wise access to iterable objects and allows
you to convert the object representation to matching byte-widths for
ctypes
or other modules.
Depending on the the underlying object and the chosen size of each particular
item of the object, the CTypesView
allows you to operate directly
on different representations of the object’s contents.
>>> text = bytearray("Hello, I am a simple ASCII string!")
>>> ctview = CTypesView(text, itemsize=1)
>>> ctview.view[0] = 0x61
>>> print(text)
aello, I am a simple ASCII string!"
>>> ctview.to_uint16()[3] = 0x6554
>>> print(text)
aello,Te am a simple ASCII string!"
The snippet above provides a single-byte sized view on a bytearray()
object. Afterwards, the first item of the view is changed, which causes a
change on the bytearray()
, on the first item as well, since both, the
CTypesView
and the bytearray()
provide a byte-wise access to
the contents.
By using CTypesView.to_uint16()
, we change the access representation to
a 2-byte unsigned integer ctypes
pointer and change the fourth 2-byte
value, I to something else.
>>> text = bytearray("Hello, I am a simple ASCII string!")
>>> ctview = CTypesView(text, itemsize=2)
>>> ctview.view[0] = 0x61
>>> print(text)
aello, I am a simple ASCII string!"
>>> ctview.to_uint16()[3] = 0x6554
>>> print(text) aello,Te am a simple ASCII string!"
If the encapsuled object does not provide a (writable) buffer()
interface, but is iterable, the CTypesView
will create an
internal copy of the object data using Python’s array
module and
perform all operations on that copy.
>>> mylist = [18, 52, 86, 120, 154, 188, 222, 240]
>>> ctview = CTypesView(mylist, itemsize=1, docopy=True)
>>> print(ctview.object)
array('B', [18, 52, 86, 120, 154, 188, 222, 240])
>>> ctview.view[3] = 0xFF
>>> print(mylist)
[18, 52, 86, 120, 154, 188, 222, 240]
>>> print(ctview.object)
array('B', [18, 52, 86, 255, 154, 188, 222, 240])
As for directly accessible objects, you can define your own itemsize to be used. If the iterable does not provide a direct byte access to their contents, this won’t have any effect except for resizing the item widths.
>>> mylist = [18, 52, 86, 120, 154, 188, 222, 240]
>>> ctview = CTypesView(mylist, itemsize=4, docopy=True)
>>> print(ctview.object)
array('I', [18L, 52L, 86L, 120L, 154L, 188L, 222L, 240L])
Accessing data over multiple dimensions¶
The second class, MemoryView
provides an interface to access
data over multiple dimensions. You can layout and access a simple
byte stream over e.g. two or more axes, providing a greater flexibility
for functional operations and complex data.
Let’s assume, we are reading image data from a file stream into some buffer object and want to access and manipulate the image data. Images feature two axes, one being the width, the other being the height, defining a rectangular graphics area.
When we read all data from the file, we have an one-dimensional view of the
image graphics. The MemoryView
allows us to define a
two-dimensional view over the image graphics, so that we can operate on
both, rows and columns of the image.
>>> imagedata = bytearray("some 1-byte graphics data")
>>> view = MemoryView(imagedata, 1, (5, 5))
>>> print(view)
[[s, o, m, e, ], [1, -, b, y, t], [e, , g, r, a], [p, h, i, c, s], [ , d, a, t, a]]
>>> for row in view:
... print(row)
...
[s, o, m, e, ]
[1, -, b, y, t]
[e, , g, r, a]
[p, h, i, c, s]
[ , d, a, t, a]
>>> for row in view:
... row[1] = "X"
... print row
...
[s, X, m, e, ]
[1, X, b, y, t]
[e, X, g, r, a]
[p, X, i, c, s]
[ , X, a, t, a]
>>> print(imagedata)
sXme 1XbyteXgrapXics Xata
On accessing a particular dimension of a MemoryView
, a new
MemoryView
is created, if it does not access a single
element.
>>> firstrow = view[0]
>>> type(firstrow)
<class 'sdl2.ext.array.MemoryView'>
>>> type(firstrow[0])
<type 'bytearray'>
A MemoryView
features, similar to Python’s builtin
memoryview
, dimensions and strides, accessible via the
MemoryView.ndim
and MemoryView.strides
attributes.
>>> view.ndim
2
>>> view.strides
(5, 5)
The MemoryView.strides
, which have to be passed on creating a
new MemoryView
, define the layout of the data over different
dimensions. In the example above, we created a 5x5 two-dimensional view
to the image graphics.
>>> twobytes = MemoryView(imagedata, 2, (5, 1))
>>> print(twobytes)
[[sX, me, 1, Xb, yt], [eX, gr, ap, Xi, cs]]
Array API¶
sdl2.ext.pixelaccess - Array-like Access to SDL Surface Contents¶
The sdl2.ext.pixelaccess
module offers a number of methods for reading,
modifying, and copying the contents SDL surface objects.
In most cases, the Numpy-based pixels2d()
,
pixels3d()
, and surface_to_ndarray()
functions
are the fastest and most flexible way of directly accessing the pixels of an
SDL surface. However, the pure-Python PixelView
class can
be used instead to avoid adding Numpy as a dependency for your project.
SDL2-based extensions¶
In addition to simple Pythonic wrappers for SDL2 functions and structures, the
sdl2.ext
module also offers a number of high-level classes and functions
that use SDL2 internally to provide APIs for font rendering, building GUIs,
importing images, and more:
sdl2.ext.surface - Creating and Manipulating Software Surfaces¶
This module provides methods for working with SDL_Surface
objects.
Currently, the only function provided by this module is subsurface()
,
which allows the creation of a new surface from a subsection of a larger one.
sdl2.ext.draw - Drawing Routines for Software Surfaces¶
The sdl2.ext.draw module provides some basic methods for drawing to unaccelerated 2D SDL surfaces. At present, the functions in this module can fill the surface with a given color and draw rectangles and lines.
sdl2.ext.image - Importing and Exporting Image Files¶
The sdl2.ext.image
module provides some simple functions for importing
images as SDL surfaces and exporting SDL surfaces to image files.
The basic functions load_bmp()
and save_bmp()
load and save BMP
files, and are guaranteed to be available on all systems supported by PySDL2.
The load_img()
function can be used to import additional image formats
(e.g. JPEG, PNG), but requires that the SDL_image library is installed on
the target system (can be installed as a Python dependency with pysdl2-dll
on platforms that support it).
In addition to importing images from files, this module also provides the
pillow_to_surface()
function for converting Image
objects from the
Pillow Python library to SDL
surfaces.
sdl2.ext.ttf - Rendering Text With TrueType Fonts¶
The sdl2.ext.ttf
module provides the FontTTF
class,
which provides a friendly and Pythonic API for font rendering based around
the SDL_ttf library. SDL_ttf can be installed as a Python dependency
with pysdl2-dll
on platforms that support it).
Additionally, this module provides the deprecated FontManager
class, which provides a different (and less featureful) API for rendering text
using SDL_ttf.
sdl2.ext.bitmapfont - Basic Bitmap Font Rendering¶
This module provides the BitmapFont
class, which allows for
basic font rendering in PySDL2 without depending on the SDL_ttf library.
Useful helpers for building apps¶
Beyond wrappers and utilities for working with SDL2’s API, the
sdl2.ext
module also includes a number of submodules with various
functions and classes to help facilitate general game and app development with
PySDL2. These include classes for managing program resource files, working
with colors, and more:
sdl2.ext.resources - Resource Management¶
Nearly every application or game includes resources, such as image and data
files, configuration files and so on. Accessing those files from an asset folder
hierarchy or a compressed bundle across platforms can become a complex
task. The Resources
class aims to simplify this by providing
dictionary-style access for your application’s resources.
Let’s assume your application has the following installation layout:
Application Directory
Application.py
Application.conf
data/
background.jpg
button1.jpg
button2.jpg
info.dat
Within the Application.py
code, you can - completely system-agnostic -
define a new resource that keeps track of all data
items.
apppath = os.path.dirname(os.path.abspath(__file__))
appresources = Resources(os.path.join(apppath, "data"))
# Access some images
bgimage = appresources.get("background.jpg")
btn1image = appresources.get("button1.jpg")
...
To access individual files, you do not need to concat paths the whole
time and regardless of the current directory, your application operates
on, you can access your resource files at any time through the
Resources
instance, you created initially.
The Resources
class is also able to scan an index archived files,
compressed via ZIP or TAR (gzip or bzip2 compression), and subdiectories
automatically.
Application Directory
Application.py
Application.conf
data/
audio/
example.wav
background.jpg
button1.jpg
button2.jpg
graphics.zip
[tileset1.bmp
tileset2.bmp
tileset3.bmp
]
info.dat
tilesimage = appresources.get("tileset1.bmp")
audiofile = appresources.get("example.wav")
If you request an indexed file via Resources.get()
, you will receive
a io.BytesIO
stream, containing the file data, for further processing.
Note
The scanned files act as keys within the Resources
class. This
means that two files, that have the same name, but are located in different
directories, will not be indexed. Only one of them will be accessible
through the Resources
class.
API¶
sdl2.ext.color - Color Representation and Conversion¶
The sdl2.ext.color module provides a number of classes and functions for working with colors.
The primary part of this module is the Color class, which allows flexible representation of colours across sdl2.ext functions and offers easy conversion between colorspaces (e.g. RGB to HSV). Additionally, this module provides functions for easily converting RGBA/ARGB integers and hexidecimal strings to Color objects.
sdl2.ext.colorpalettes - Predefined Color Palettes¶
sdl2.ext.algorithms - Useful Algorithms¶
This module contains some useful algorithms for working with shapes and surfaces. At present it contains functions for clipping lines to fit within a set of 2D boundaries and for determining whether a point falls along a given line.
Component-Oriented Systems¶
PySDL2 contains several modules designed around the concept of component-oriented game programming. These modules define various types of “systems” for processing events and “factories” for creating different types of objects, among other things.
Note
These modules are not regularly maintained, and their documentation may be inaccurate or out-of-date.
Working with component-based entities¶
sdl2.ext
supports a component oriented programming pattern to separate
object instances, carried data and processing logic within applications
or games. It uses an entity based approach, in which object instances are
unique identifiers, while their data is managed within components, which
are stored separately. For each individual component type a processing
system will take care of all necessary updates on running the application.
Component-based patterns¶
Component-based means that - instead of a traditional OOP approach - object information are split up into separate data bags for reusability and that those data bags are separated from any application logic.
Imagine a car class in traditional OOP, which might look like
class Car:
def __init__(self):
self.color = "red"
self.position = 0, 0
self.velocity = 0, 0
self.sprite = get_some_car_image()
...
def drive(self, timedelta):
self.position[0] = self.velocity[0] * timedelta
self.position[1] = self.velocity[1] * timedelta
...
def stop(self):
self.velocity = 0, 0
...
def render(self, screen):
screen.display(self.sprite)
mycar = new Car()
mycar.color = "green"
mycar.velocity = 10, 0
The car features information stored in attributes (color
, position
,
…) and behaviour (application logic, drive()
, stop()
…).
A component-based approach aims to split and reduce the car to a set of information and external systems providing the application logic.
class Car:
def __init__(self):
self.color = "red"
self.position = 0, 0
self.velocity = 0, 0
self.sprite = get_some_car_image()
class CarMovement:
def drive(self, car, timedelta):
car.position[0] = car.velocity[0] * timedelta
car.position[1] = car.velocity[1] * timedelta
...
def stop(self):
car.velocity = 0, 0
class CarRenderer:
def render(self, car, screen):
screen.display(car.sprite)
At this point of time, there is no notable difference between both approaches, except that the latter one adds additional overhead.
The benefit comes in, when you
- use subclassing in your OOP design
- want to change behavioural patterns on a global scale or based on states
- want to refactor code logic in central locations
- want to cascade application behaviours
The initial Car
class from above defines, how it should be displayed
on the screen. If you now want to add a feature for rescaling the screen
size after the user activates the magnifier mode, you need to refactor
the Car
and all other classes that render things on the screen, have
to consider all subclasses that override the method and so on.
Refactoring the CarRenderer
code by adding a check for the magnifier
mode sounds quite simple in contrast to that, not?
The same applies to the movement logic - inverting the movement logic requires you to refactor all your classes instead of a single piece of application code.
Subclassing with traditional OOP for behavioural changes also might
bloat your classes with unnecessary information, causing the memory
footprint for your application to rise without any need. Let’s assume
you have a Truck
class that inherits from Car
. Let’s further
assume that all trucks in your application look the same. Why should any
of those carry a sprite
or color
attribute? You would need to
refactor your Car
class to get rid of those superfluous information,
adding another level of subclassing. If at a later point of time you
decide to give your trucks different colors, you need to refactor
everything again.
Wouldn’t it be easier to deal with colors, if they are available on the truck and leave them out, if they are not? We initially stated that the component-based approach aims to separate data (information) from code logic. That said, if the truck has a color, we can handle it easily, if it has not, we will do as usual.
Also, checking for the color of an object (regardless, if it is a truck, car, aeroplane or death star) allows us to apply the same or similar behaviour for every object. If the information is available, we will process it, if it is not, we will not do anything.
Once we split up the previously OOP-style classes into pure data containers and some separate processing code for the behaviour, we are talking about components and (processing) systems. A component is a data container, ideally grouping related information on a granular level, so that it is easy to (re)use. When you combine different components to build your in-application objects and instantiate those, we are talking about entities.

- Component
- provides information (data bag)
- Entity
- In-application instance that consists of component items
- System
- Application logic for working with Entity items and their component data
- World
- The environment that contains the different System instances and all Entity items with their component data
Within a strict COP design, the application logic (ideally) only knows about data to process. It does not know anything about entities or complex classes and only operates on the data.

To keep things simple, modular and easy to maintain and change, you usually
create small processing systems, which perform the necessary operations on the
data they shall handle. That said, a MovementSystem
for our car entity would
only operate on the position and velocity component of the car entity. It does
not know anything about the the car’s sprite or sounds that the car makes,
since this is nothing it has to deal with.
To display the car on the screen, a RenderingSystem
might pick up the
sprite component of the car, maybe along with the position information (so it
knows, where to place the sprite) and render it on the screen.
If you want the car to play sounds, you would add an audio playback system, that can perform the task. Afterwards you can add the necessary audio information via a sound component to the car and it will make noise.
Component-based design with sdl2.ext¶
Note
This section will deal with the specialities of COP patterns and provide the bare minimum of information. If you are just starting with such a design, it is recommended to read through the The Pong Game tutorial.
sdl2.ext
provides a World
class in which all other objects
will reside. The World
will maintain both, Entity
and
component items, and allows you to set up the processing logic via
the System
and Applicator
classes.
>>> appworld = World()
Components can be created from any class that inherits from the
object
type and represent the data bag of information for the
entity and application world. Ideally, they should avoid any
application logic (except from getter and setter properties).
class Position2D(object):
def __init__(self, x=0, y=0):
self.x = x
self.y = y
Entity
objects define the in-application objects and only consist of
component-based attributes. They also require a World
at
object instantiation time.
class CarEntity(Entity):
def __init__(self, world, x=0, y=0):
self.position2d = Position2D(x, y)
Note
The world argument in __init__()
is necessary. It will be
passed to the internal __new__()
constructor of the
Entity
and stores a reference to the World
and also
allows the Entity
to store its information in the
World
.
The Entity
also requries its attributes to be named exactly as
their component class name, but in lowercase letters. If you name a
component MyAbsolutelyAwesomeDataContainer
, an Entity
will
force you to write the following:
class SomeEntity(Entity):
def __init__(self, world):
self.myabsolutelyawesomedatacontainer = MyAbsolutelyAwesomeDataContainer()
Note
This is not entirely true. A reference of the object will be stored on a
per-class-in-mro basis. This means that if MyAbsolutelyAwesomeDataContainer
inherits from ShortName
, you can also do:
class SomeEntity(Entity):
def __init__(self, world):
self.shortname = MyAbsolutelyAwesomeDataContainer()
Components should be as atomic as possible and avoid complex
inheritance. Since each value of an Entity
is stored per class
in its mro list, components inheriting from the same class(es) will
overwrite each other on conflicting classes:
class Vector(Position2D):
def __init__(self, x=0, y=0, z=0):
super(Vector, self).__init__(x, y)
class SomeEntity(Entity):
def __init__(self, world):
# This will associate self.position2d with the new Position2D
# value, while the previous Vector association is overwritten
self.position2d = Position2D(4, 4)
# self.vector will also associate a self.position2d attribute
# with the Entity, since Vector inherits from Position2D. The
# original association will vanish, and each call to
# entity.position2d will effectively manipulate the vector!
self.vector = Vector(1,2,3)
API¶
-
class
sdl2.ext.
Entity
(world : World)¶ An entity is a specific object living in the application world. It does not carry any data or application logic, but merely acts as identifier label for data that is maintained in the application world itself.
As such, it is a composition of components, which would not exist without the entity identifier. The entity itself is non-existent to the application world as long as it does not carry any data that can be processed by a system within the application world.
-
id
¶ The id of the Entity. Every Entity has a unique id, that is represented by a
uuid.UUID
instance.
-
delete
() → None¶ Deletes the
Entity
from itsWorld
. This basically callsWorld.delete()
with theEntity
.
-
-
class
sdl2.ext.
Applicator
¶ A processing system for combined data sets. The
Applicator
is an enhancedSystem
that receives combined data sets based on its setSystem.componenttypes
-
is_applicator
¶ A boolean flag indicating that this class operates on combined data sets.
-
componenttypes
¶ A tuple of class identifiers that shall be processed by the
Applicator
.
-
process
(world : World, componentsets : iterable)¶ Processes tuples of component items. componentsets will contain object tuples, that match the
componenttypes
of theApplicator
. If, for example, theApplicator
is defined asclass MyApplicator(Applicator): def __init__(self): self.componenttypes = (Foo, Bar)
its process method will receive
(Foo, Bar)
tuplesdef process(self, world, componentsets): for foo_item, bar_item in componentsets: ...
Additionally, the
Applicator
will not process all possible combinations of valid components, but only those, which are associated with the sameEntity
. That said, anEntity
must contain aFoo
as well as aBar
component in order to have them both processed by theApplicator
(while aSystem
with the samecomponenttypes
would pick either of them, depending on their availability).
-
-
class
sdl2.ext.
System
¶ A processing system within an application world consumes the components of all entities, for which it was set up. At time of processing, the system does not know about any other component type that might be bound to any entity.
Also, the processing system does not know about any specific entity, but only is aware of the data carried by all entities.
-
process
(world : World, components : iterable)¶ Processes component items.
This method has to be implemented by inheriting classes.
-
-
class
sdl2.ext.
World
¶ An application world defines the combination of application data and processing logic and how the data will be processed. As such, it is a container object in which the application is defined.
The application world maintains a set of entities and their related components as well as a set of systems that process the data of the entities. Each processing system within the application world only operates on a certain set of components, but not all components of an entity at once.
The order in which data is processed depends on the order of the added systems.
-
systems
¶ The processing system objects bound to the world.
-
add_system
(system : object)¶ Adds a processing system to the world. The system will be added as last item in the processing order.
The passed system does not have to inherit from
System
, but must feature acomponenttypes
attribute and aprocess()
method, which match the signatures of theSystem
classclass MySystem(object): def __init__(self): # componenttypes can be any iterable as long as it # contains the classes the system should take care of self.componenttypes = [AClass, AnotherClass, ...] def process(self, world, components): ...
If the system shall operate on combined component sets as specified by the
Applicator
, the class instance must contain ais_applicator
property, that evaluates toTrue
class MyApplicator(object): def __init__(self): self.is_applicator = True self.componenttypes = [...] def process(self, world, components): pass
The behaviour can be changed at run-time. The
is_applicator
attribute is evaluated for every call toWorld.process()
.
-
delete_entities
(entities : iterable)¶ Removes a set of
Entity
instances from the World, including all their component data.
-
insert_system
(index : int, system : System)¶ Adds a processing
System
to the world. The system will be added at the specified position in the processing order.
-
get_entities
(component : object) → [Entity, ...]¶ Gets the entities using the passed component.
Note
This will not perform an identity check on the component but rely on its
__eq__
implementation instead.
-
General purpose event handling routines¶
-
class
sdl2.ext.
EventHandler
(sender)¶ A simple event handling class, which manages callbacks to be executed.
The EventHandler does not need to be kept as separate instance, but is mainly intended to be used as attribute in event-aware class objects.
>>> def myfunc(sender): ... print("event triggered by %s" % sender) ... >>> class MyClass(object): ... def __init__(self): ... self.anevent = EventHandler(self) ... >>> myobj = MyClass() >>> myobj.anevent += myfunc >>> myobj.anevent() event triggered by <__main__.MyClass object at 0x801864e50>
-
callbacks
¶ A list of callbacks currently bound to the
EventHandler
.
-
sender
¶ The responsible object that executes the
EventHandler
.
-
add
(callback : Callable)¶ Adds a callback to the
EventHandler
.
-
remove
(callback : Callable)¶ Removes a callback from the
EventHandler
.
-
__call__
(*args) → [ ... ]¶ Executes all connected callbacks in the order of addition, passing the
sender
of theEventHandler
as first argument and the optional args as second, third, … argument to them.This will return a list containing the return values of the callbacks in the order of their execution.
-
-
class
sdl2.ext.
MPEventHandler
(sender)¶ An asynchronous event handling class based on
EventHandler
, in which callbacks are executed in parallel. It is the responsibility of the caller code to ensure that every object used maintains a consistent state. TheMPEventHandler
class will not apply any locks, synchronous state changes or anything else to the arguments or callbacks being used. Consider it a “fire-and-forget” event handling strategy.Note
The
MPEventHandler
relies on themultiprocessing
module. If the module is not available in the target environment, asdl2.ext.compat.UnsupportedError
is raised.Also, please be aware of the restrictions that apply to the
multiprocessing
module; arguments and callback functions for example have to be pickable, etc.-
__call__
(*args) → AsyncResult¶ Executes all connected callbacks within a
multiprocessing.pool.Pool
, passing thesender
as first argument and the optional args as second, third, … argument to them.This will return a
multiprocessing.pool.AsyncResult
containing the return values of the callbacks in the order of their execution.
-
Sprite, texture and pixel surface routines¶
-
sdl2.ext.
TEXTURE
¶ Indicates that texture-based rendering or sprite creation is wanted.
-
sdl2.ext.
SOFTWARE
¶ Indicates that software-based rendering or sprite creation is wanted.
-
class
sdl2.ext.
Sprite
¶ A simple 2D object, implemented as abstract base class.
-
class
sdl2.ext.
SoftwareSprite
(imgsurface : sdl2.SDL_Surface, free : boolean)¶ A simple, visible, pixel-based 2D object, implemented on top of SDL2 software surfaces. free indicates, whether imgsurface shall be automatically freed on deleting the SoftwareSprite.
-
free
¶ Indicates, if the bound
surface
shall be freed on deleting theSoftwareSprite
.
-
surface
¶ The
sdl2.SDL_Surface
containing the pixel data.
-
size
¶ The size of the
SoftwareSprite
as tuple.
-
subsprite
(area : (int, int, int, int)) → SoftwareSprite¶ Creates another
SoftwareSprite
from a part of theSoftwareSprite
. The two sprites share pixel data, so if the parent sprite’s surface is not managed by the sprite (free
is False), you will need to keep it alive while the subsprite exists.
-
-
class
sdl2.ext.
TextureSprite
(texture : sdl2.SDL_Texture[, free=True])¶ A simple, visible, pixel-based 2D object, implemented on top of SDL2 textures. free indicates, whether texture shall be automatically freed on deleting the TextureSprite.
-
free
¶ Indicates, if the bound
texture
shall be freed on deleting theTextureSprite
.
-
angle
¶ The rotation angle for the
TextureSprite
.
-
center
¶ The center to use for rotating the
TextureSprite
. None will reset the center to the default center of theTextureSprite
.
-
flip
¶ Allows the
TextureSprite
to be flipped over its horizontal or vertical axis via the appropriate SDL_FLIP_* value.
-
size
¶ The size of the
TextureSprite
as tuple.
-
texture
¶ The
sdl2.SDL_Texture
containing the texture data.
-
-
class
sdl2.ext.
SpriteRenderSystem
¶ A rendering system for
Sprite
components. This is a base class for rendering systems capable of drawing and displayingSprite
based objects. Inheriting classes need to implement the rendering capability by overriding the render() method.-
sortfunc
¶ Sort function for the component processing order. The default sort order is based on the depth attribute of every sprite. Lower depth values will cause sprites to be drawn below sprites with higher depth values. If
sortfunc
shall be overridden, it must match the callback requirements forsorted()
.
-
-
class
sdl2.ext.
SoftwareSpriteRenderSystem
(window : object)¶ A rendering system for
SoftwareSprite
components. TheSoftwareSpriteRenderSystem
class uses asdl2.SDL_Window
as drawing device to displaySoftwareSprite
surfaces. It uses the internal SDL surface of the window as drawing context, so that GL operations, such as texture handling or the usage of SDL renderers is not possible.window can be either a
sdl2.ext.Window
orsdl2.SDL_Window
instance.-
window
¶ The
sdl2.SDL_Window
that is used as drawing device.
-
render
(sprites : object[, x=None[, y=None]]) → None¶ Draws the passed sprites on the
sdl2.ext.Window
surface. x and y are optional arguments that can be used as relative drawing location for sprites. If set toNone
, the location information of the sprites are used. If set and sprites is an iterable, such as a list ofSoftwareSprite
objects, x and y are relative location values that will be added to each individual sprite’s position. If sprites is a singleSoftwareSprite
, x and y denote the absolute position of theSoftwareSprite
, if set.
-
-
class
sdl2.ext.
TextureSpriteRenderSystem
(target : object)¶ A rendering system for
TextureSprite
components. TheTextureSpriteRenderSystem
class uses asdl2.SDL_Renderer
as drawing device to displaySprite
surfaces.target can be a
sdl2.ext.Window
,sdl2.SDL_Window
, a:class:sdl2.ext.Renderer or asdl2.SDL_Renderer
. If it is asdl2.ext.Window
orsdl2.SDL_Window
instance, it will try to create asdl2.SDL_Renderer
with hardware acceleration for it.-
sdlrenderer
¶ The
sdl2.SDL_Renderer
that is used as drawing context.
-
rendertarget
¶ The target for which the
sdlrenderer
was created, if any.
-
render
(sprites : object[, x=None[, y=None]]) → None¶ Renders the passed sprites via the
renderer
. x and y are optional arguments that can be used as relative drawing location for sprites. If set toNone
, the location information of the sprites are used. If set and sprites is an iterable, such as a list ofTextureSprite
objects, x and y are relative location values that will be added to each individual sprite’s position. If sprites is a singleTextureSprite
, x and y denote the absolute position of theTextureSprite
, if set.
-
-
class
sdl2.ext.
SpriteFactory
(sprite_type=TEXTURE, **kwargs)¶ A factory class for creating
Sprite
objects. TheSpriteFactory
can createTextureSprite
orSoftwareSprite
instances, depending on the sprite_type being passed to it, which can beSOFTWARE
orTEXTURE
. The additional kwargs are used as default arguments for creating sprites within the factory methods.-
sprite_type
¶ The sprite type created by the factory. This will be either
SOFTWARE
forSoftwareSprite
orTEXTURE
forTextureSprite
objects.
-
default_args
¶ The default arguments to use for creating new sprites.
-
create_software_sprite
(size, bpp=32, masks=None) → SoftwareSprite¶ Creates a software sprite. A size tuple containing the width and height of the sprite and a bpp value, indicating the bits per pixel to be used, need to be provided.
-
create_sprite
(**kwargs) → Sprite¶ Creates a
Sprite
. Depending on thesprite_type
, this will return aSoftwareSprite
orTextureSprite
.kwargs are the arguments to be passed for the sprite construction and can vary depending on the sprite type. Usually they have to follow the
create_software_sprite()
andcreate_texture_sprite()
method signatures. kwargs however will be mixed with the setdefault_args
so that one does not necessarily have to provide all arguments, if they are set within thedefault_args
. If kwargs anddefault_args
contain the same keys, the key-value pair of kwargs is chosen.
-
create_sprite_render_system
(*args, **kwargs) → SpriteRenderSystem¶ Creates a new
SpriteRenderSystem
, based on the setsprite_type
. Ifsprite_type
isTEXTURE
, aTextureSpriteRenderSystem
is created with the therenderer
from thedefault_args
. Other keyword arguments are ignored in that case.Otherwise a
SoftwareSpriteRenderSystem
is created and args and kwargs are passed to it.
-
create_texture_sprite
(renderer : object, size, pformat=sdl2.SDL_PIXELFORMAT_RGBA8888, access=sdl2.SDL_TEXTUREACCESS_STATIC) → TextureSprite¶ Creates a texture sprite. A size tuple containing the width and height of the sprite needs to be provided.
TextureSprite
objects are assumed to be static by default, making it impossible to access their pixel buffer in favour for faster copy operations. If you need to update the pixel data frequently or want to use the texture as target for rendering operations, access can be set to the relevant SDL_TEXTUREACCESS_* flag.
-
from_color
(color : object, size, bpp=32, masks=None) → Sprite¶ Creates a
Sprite
with a certain color.
-
from_image
(fname : str) → Sprite¶ Creates a
Sprite
from an image file. The image must be loadable viasdl2.ext.load_image()
.
-
from_object
(obj: object) → Sprite¶ Creates a
Sprite
from an object. The object will be passed throughsdl2.rwops_from_object()
in order to try to load image data from it.
-
from_surface
(surface : SDL_Surface[, free=False]) → Sprite¶ Creates a
Sprite
from the passedsdl2.SDL_Surface
. If free is set toTrue
, the passed surface will be freed automatically.
-
from_text
(text : str[, **kwargs]) → Sprite¶ Creates a
Sprite
from a string of text. This method requires asdl2.ext.FontManager
to be in kwargs ordefault_args
.
-
Designing component-oriented user interfaces¶
Warning
This module is deprecated, and may be removed in a future release of PySDL2.
User interface elements within sdl2.ext
are simple
Sprite
objects, which are enhanced by certain input hooks; as such,
they are not classes on their own, but implemented as mixins. The user input
itself is handled by an UIProcessor
object, which takes care of
delegating input events, such as mouse movements, clicks and keyboard input,
to the correct UI element.
Depending on the event type (e.g. pressing a mouse button), the UIProcessor
will execute its matching method (e.g. mousedown()
) with only those UI
elements, which support the event type.

UI element types¶
Every sdl2.ext
UI element is a simple Sprite
object, to
which additional attributes and methods are bound.
Every UI element features the following attributes
element.uitype
The
uitype
attribute can have one of the following values, identifying the UI element:
BUTTON
- a UI element, which can react on mouse inputCHECKBUTTON
- asBUTTON
, but it retains its state on clicksTEXTENTRY
- a UI element that reacts on keyboard input
element.events
A dictionary containing the SDL2 event mappings. Each supported SDL2 event (e.g.SDL_MOUSEMOTION
) is associated with a boundEventHandler
acting as callback for user code (e.g.mousemotion()
).
Depending on the exact type of the element, it will feature additional methods and attributes explained below.
BUTTON
UI elements feature a state
attribute, which can be one of the
following values.
state | Description |
---|---|
RELEASED | Indicates that the UI element is not pressed. |
HOVERED | Indicates that the mouse cursor is currently hovering the UI element. |
PRESSED | Indicates that a mouse button is pressed on the UI element. |
BUTTON
UI elements react with the following event handlers on events:
button.motion(event : sdl2.events.SDL_Event)
AnEventHandler
that is invoked, if the mouse moves around while being over theBUTTON
.
button.pressed(event : sdl2.events.SDL_Event)
AnEventHandler
that is invoked, if a mouse button is pressed on theBUTTON
.
button.released(event : sdl2.events.SDL_Event)
AnEventHandler
that is invoked, if a mouse button is released on theBUTTON
.
button.click(event : sdl2.events.SDL_Event)
AnEventHandler
that is invoked, if a mouse button is pressed and released on theBUTTON
.
Besides the BUTTON
a special CHECKBUTTON
UI element type exists,
which enhances the BUTTON
bindings by an additional checked
attribute.
The checked
attribute switches its status (False
to True
and
True
to False
) every time the UI element is clicked.
TEXTENTRY
elements react on text input, once they are activated. Text being
input, once a TEXTENTRY
has been activated, is stored in its text
attribute.
The TEXTENTRY
reacts with the following event handlers on events:
textentry.motion(event : sdl2.events.SDL_Event)
AnEventHandler
that is invoked, if the mouse moves around while being over theTEXTENTRY
.
textentry.pressed(event : sdl2.events.SDL_Event)
AnEventHandler
that is invoked, if a mouse button is pressed on theTEXTENTRY
.
textentry.released(event : sdl2.events.SDL_Event)
AnEventHandler
that is invoked, if a mouse button is released on theTEXTENTRY
.
textentry.keydown(event : sdl2.events.SDL_Event)
AnEventHandler
that is invoked on pressing a key.
textentry.keyup(event : sdl2.events.SDL_Event)
AnEventHandler
that is invoked on releasing a key.
textentry.input(event : sdl2.events.SDL_Event)
AnEventHandler
that is invoked on text input events. Text input events are automatically created, once theUIProcessor
activates aTEXTENTRY
UI element.
textentry.editing(event : sdl2.events.SDL_Event)
An
EventHandler
that is invoked on text editing events. Text editing events are automatically created, once theUIProcessor
activates aTEXTENTRY
UI element.Text editing events are however only raised, if an IME system is involved, which combines glyphs and symbols to characters or word fragments.
API¶
-
class
sdl2.ext.
UIFactory
(spritefactory : SpriteFactory[, **kwargs])¶ A factory class for creating UI elements. The
UIFactory
allows you to create UI elements based on theSprite
class. To do this, it requires aSpriteFactory
, which will create the sprites, to which theUIFactory
then binds the additional methods and attributes.The additional kwargs are used as default arguments for creating sprites within the factory methods.
-
default_args
¶ A dictionary containing the default arguments to be passed to the sprite creation methods of the bound
SpriteFactory
.
-
spritefactory
¶ The
SpriteFactory
being used for creating newSprite
objects.
Creates a new button UI element.
kwargs are the arguments to be passed for the sprite construction and can vary depending on the sprite type. See
SpriteFactory.create_sprite()
for further details.
Creates a new checkbutton UI element.
kwargs are the arguments to be passed for the sprite construction and can vary depending on the sprite type. See
SpriteFactory.create_sprite()
for further details.
-
create_text_entry
(**kwargs) → Sprite¶ Creates a new textentry UI element.
kwargs are the arguments to be passed for the sprite construction and can vary depending on the sprite type. See
SpriteFactory.create_sprite()
for further details.
-
from_color
(color : object, size) → Sprite¶ Creates a UI element with a specific color.
uitype must be one of the supported UI element types classifying the type of UI element to be created.
-
from_image
(uitype : int, fname : str) → Sprite¶ Creates a UI element from an image file. The image must be loadable via
load_image()
.uitype must be one of the supported UI element types classifying the type of UI element to be created.
-
from_object
(uitype : int, obj: object) → Sprite¶ Creates a UI element from an object. The object will be passed through
sdl2.rwops_from_object()
in order to try to load image data from it.uitype must be one of the supported UI element types classifying the type of UI element to be created.
-
from_surface
(uitype : int, surface : SDL_Surface[, free=False]) → Sprite¶ Creates a UI element from the passed
sdl2.surface.SDL_Surface
. If free is set toTrue
, the passed surface will be freed automatically.uitype must be one of the supported UI element types classifying the type of UI element to be created.
-
-
class
sdl2.ext.
UIProcessor
¶ A processing system for user interface elements and events.
-
handlers
¶ A dict containing the mapping of SDL2 events to the available
EventHandler
bindings of theUIProcessor
.
-
activate
(component : object) → None¶ Activates a UI control to receive text input.
-
deactivate
(component : object) → None¶ Deactivate the currently active UI control.
-
passevent
(component : object, event : SDL_Event) → None¶ Passes the event to a component without any additional checks or restrictions.
-
mousemotion
(component : object, event : SDL_Event) → None¶ Checks, if the event’s motion position is on the component and executes the component’s event handlers on demand. If the motion event position is not within the area of the component, nothing will be done. In case the component is a
BUTTON
, itsstate
will be adjusted to reflect, if it is currently hovered or not.
-
mousedown
(component : object, event : SDL_Event) → None¶ Checks, if the event’s button press position is on the component and executes the component’s event handlers on demand. If the button press position is not within the area of the component, nothing will be done.
In case the component is a
BUTTON
, itsstate
will be adjusted to reflect, if it is currently pressed or not.In case the component is a
TEXTENTRY
and the pressed button is the primary mouse button, the component will be marked as the next control to activate for text input.
-
mouseup
(self, component, event) → None¶ Checks, if the event’s button release position is on the component and executes the component’s event handlers on demand. If the button release position is not within the area of the component, nothing will be done.
In case the component is a
BUTTON
, itsstate
will be adjusted to reflect, whether it is hovered or not.If the button release followed a button press on the same component and if the button is the primary button, the
click()
event handler is invoked, if the component is aBUTTON
.
-
dispatch
(obj : object, event : SDL_Event) → None¶ Passes an event to the given object. If obj is a
World
object, UI relevant components will receive the event, if they support the event type. If obj is a single object,obj.events
must be a dict consisting of SDL event type identifiers andEventHandler
instances bound to the object. If obj is a iterable, such as a list or set, every item within obj must feature anevents
attribute as described above.
-
process
(world : World, components : iterable) → None¶ The
UIProcessor
class does not implement the process() method by default. Instead it usesdispatch()
to send events around to components.process()
does nothing.
-
A simple particle system¶
-
class
sdl2.ext.particles.
ParticleEngine
¶ A simple particle processing system. The
ParticleEngine
takes care of creating, updating and deleting particles via callback functions. It only decreases the life of the particles by itself and marks them as dead, once the particle’s life attribute has reached 0 or below.-
createfunc
¶ Function for creating new particles. The function needs to take two arguments, the
world
argument passed toprocess()
and a list of the particles considered dead (Particle.life
<= 0).def creation_func(world, deadparticles): ...
-
updatefunc
¶ Function for updating existing, living particles. The function needs to take two arguments, the
world
argument passed toprocess()
and aset
of the still living particles.def update_func(world, livingparticles): ...
-
deletefunc
¶ Function for deleting dead particles. The function needs to take two arguments, the
world
argument passed toprocess()
and a list of the particles considered dead (Particle.life
<= 0).def deletion_func(world, deadparticles): ...
-
process
(world : World, components : iterable) → None¶ Processes all particle components, decreasing their life by 1.
Once the life of all particle components has been decreased properly and the particles considered dead (life <= 0) are identified, the creation, update and deletion callbacks are invoked.
The creation callback takes the passed world as first and the list of dead particles as second argument.
def particle_createfunc(world, list_of_dead_ones): ...
Afterwards the still living particles are passed to the update callback, which also take the passed world as first and the living particles as set as second argument.
def particle_updatefunc(world, set_of_living_ones): ...
Finally, the dead particles need to be deleted in some way or another, which is done by the deletion callback, taking the passed world as first and the list of dead particles as second argument.
def particle_deletefunc(world, list_of_dead_ones): ...
-
-
class
sdl2.ext.particles.
Particle
(x, y, life : int)¶ A simple particle component type. It only contains information about a x- and y-coordinate and its current life time. The life time will be decreased by 1, every time the particle is processed by the
ParticleEngine
.-
x
¶ The x coordinate of the particle.
-
y
¶ The y coordinate of the particle.
-
life
¶ The remaining life time of the particle.
-
position
¶ The x- and y-coordinate of the particle as tuple.
-
sdl2.sdlttf - Python bindings for SDL_ttf¶
py-sdl2 provides bindings for SDL2_ttf, a library designed for use with SDL2 that provides high quality TrueType font rendering.
SDL2_ttf supports a wide range of font formats, including TrueType (.ttf) and OpenType (.otf) fonts. It also supports different font styles, font hinting modes, and font outlines.
The SDL2_ttf library provides functions for rendering three main formats of
text, denoted by the suffix of the function. Functions ending in Text
can only
render plain ASCII text, whereas functions ending in UTF8
or UNICODE
can
render most unicode characters provided that a font supports them. In general,
you should always use the UTF8
rendering functions unless you have a strong
reason to do otherwise.
Table Of Contents
Note
This module handles font sizes in units of points (pt) instead of pixels. To
obtain a font with a given pixel height, you can use the
TTF_GlyphMetrics()
function to get the pixel heights of different
glyphs in the font at a given pt size and use the px/pt ratio to figure out
the pt size needed to render text at a given height in px.
Note
The surface sizes and contents of rendered text may vary slightly between systems depending on the version of FreeType used by SDL2_ttf.
Initialization functions¶
Module constants¶
-
sdl2.sdlttf.
TTF_MAJOR_VERSION
¶ Latest SDL2_ttf library major number supported by PySDL2.
-
sdl2.sdlttf.
TTF_MINOR_VERSION
¶ Latest SDL2_ttf library minor number supported by PySDL2.
-
sdl2.sdlttf.
TTF_PATCHLEVEL
¶ Latest SDL2_ttf library patch level number supported by PySDL2.
-
sdl2.sdlttf.
UNICODE_BOM_NATIVE
¶ This allows you to switch byte-order of UNICODE (UCS-2) text data to native order, meaning the mode of your CPU. This is meant to be used in UNICODE strings that you are using with the SDL2_ttf API. Not needed for UTF8 strings.
-
sdl2.sdlttf.
UNICODE_BOM_SWAPPED
¶ This allows you to switch byte-order of UNICODE (UCS-2) text data to swapped order, meaning the reversed mode of your CPU. Thus, if your CPU is LSB, then the data will be interpretted as MSB. This is meant to be used in UNICODE strings that you are using with the SDL2_ttf API. Not needed for UTF8 strings.
-
sdl2.sdlttf.
TTF_STYLE_NORMAL
¶ Used to indicate regular, normal, plain rendering style.
-
sdl2.sdlttf.
TTF_STYLE_BOLD
¶ Used to indicate bold rendering style. This is used in a bitmask along with other styles.
-
sdl2.sdlttf.
TTF_STYLE_ITALIC
¶ Used to indicate italicized rendering style. This is used in a bitmask along with other styles.
-
sdl2.sdlttf.
TTF_STYLE_UNDERLINE
¶ Used to indicate underlined rendering style. This is used in a bitmask along with other styles.
-
sdl2.sdlttf.
TTF_STYLE_STRIKETHROUGH
¶ Used to indicate strikethrough rendering style. This is used in a bitmask along with other styles.
-
sdl2.sdlttf.
TTF_HINTING_NORMAL
¶ Used to indicate set hinting type to normal. This corresponds to the default hinting algorithm, optimized for standard gray-level rendering.
-
sdl2.sdlttf.
TTF_HINTING_LIGHT
¶ Used to indicate set hinting type to light. A lighter hinting algorithm for non-monochrome modes. Many generated glyphs are more fuzzy but better resemble its original shape. A bit like rendering on macOS.
-
sdl2.sdlttf.
TTF_HINTING_MONO
¶ Used to indicate set hinting type to monochrome. Strong hinting algorithm that should only be used for monochrome output. The result is probably unpleasant if the glyph is rendered in non-monochrome modes.
-
sdl2.sdlttf.
TTF_HINTING_NONE
¶ Used to indicate set hinting type to none. No hinting is used, so the font may become very blurry or messy at smaller sizes.
sdl2.sdlimage - Python bindings for SDL_image¶
py-sdl2 provides bindings for SDL2_image, a library designed for use with SDL2
that adds support for loading a wide range of different common (and uncommon)
image formats for easy use with SDL2. In addition, SDL2_image includes functions
for saving SDL_Surface
objects to PNG and/or JPEG images.
Table Of Contents
Note
If using an alternative rendering system that doesn’t use SDL surfaces as input (e.g. PyOpenGL), the Pillow imaging library may be a better fit for your project.
Initialization and library information functions¶
Image format-checking functions¶
These functions are used to check whether an SDL file object
(SDL_RWops
) is a valid image file of a given format. Note that
all of these functions will return 0 if SDL2_image was not built with
support for that format, even if it is a valid image of that type, so be
cautious when using these for formats like WEBP, JPEG, PNG, and TIFF, which
are optional when building SDL2_image.
Module constants¶
-
sdl2.sdlimage.
IMG_MAJOR_VERSION
¶ Latest SDL2_image library major number supported by PySDL2.
-
sdl2.sdlimage.
IMG_MINOR_VERSION
¶ Latest SDL2_image library minor number supported by PySDL2.
-
sdl2.sdlimage.
IMG_PATCHLEVEL
¶ Latest SDL2_image library patch level number supported by PySDL2.
-
sdl2.sdlimage.
IMG_INIT_JPG
¶ IMG_Init()
flag to enable support for the JPEG image format.
-
sdl2.sdlimage.
IMG_INIT_PNG
¶ IMG_Init()
flag to enable support for the PNG image format.
-
sdl2.sdlimage.
IMG_INIT_TIF
¶ IMG_Init()
flag to enable support for the TIFF image format.
-
sdl2.sdlimage.
IMG_INIT_WEBP
¶ IMG_Init()
flag to enable support for the WEBP image format.
sdl2.sdlmixer - Python bindings for SDL_mixer¶
The sdl2.sdlmixer
module provides bindings for wrapper the SDL_mixer
audio playback library. It supports loading, playing, and mixing audio from a
wide range of popular formats (e.g. WAV, FLAC, MP3, OGG).
The SDL_mixer library is designed around the concept of virtual channels, each of which can be set to different volumes, have different left/right panning, and play different effects and sounds simultaneously (like a mixing board in a recording studio). In addition to the main mixing channels, SDL_mixer also provides a single channel for music playback, which is less flexible than the other channels but supports a wider range of playback formats (including MIDI) and requires less memory for playback.
Music playback is performed using Mix_Music
objects and their
corresponding functions. All other audio playback is performed using
Mix_Chunk
objects.
Table Of Contents
Note
The PySDL2 documentation for this module is currently a work-in-progress, and not all functions are documented. The official C documentation (linked below) can be used in the meantime to fill in any gaps.
The official documentation for the SDL_mixer library can be found here. Note that not all functions may be documented, as the official doucmentation is currently several releases out-of-date.
Initialization and library information functions¶
Channel functions¶
These functions affect regular mixer channels. Music is not affected by these functions.
sdl2.sdlgfx - Python bindings for SDL2_gfx¶
py-sdl2 provides bindings for SDL2_gfx, an unofficial addon library for SDL2 that contains a range of useful functions for rendering shapes, frame pacing, and SDL surface manipulation.
Note that unlike the other SDL2 modules supported by this package, SDL2_gfx is not an official part of the SDL2 project.
Table Of Contents
Project Information¶
Release News¶
This describes the latest changes between the PySDL2 releases.
0.9.10¶
Released on 2022-01-11.
New Features:
- Updated the
sdlttf
,sdlimage
,sdlmixer
, andsdlgfx
modules to use a new method of ctypes wrapping that allows functions to support kwargs (i.e. using function arguments by name), inline documentation, and more flexible handling of argument types and SDL errors (PR #199) - Updated to wrap new functions and constants in in SDL2 2.0.18 (PR #197)
- Added full inline documentation for the
sdlttf
,sdlimage
, andsdlgfx
modules, as well as partial inline documentation for thesdlmixer
modlue. In addition to Python-specific argument and return types, the new docstrings also include including code examples and documentation of Python-specific quirks (PR #200) - Update bindings to allow for experimental SDL 2.0.3 support. Versions of SDL2 this old are not officially supported, but this should allow for basic PySDL2 support on old PowerPC macs (PR #202)
- Added new
isiterable()
andutf8()
Python 2/3 compatibility functions to thecompat
module for checking if an object is a non-string iterable and for converting input to a UTF-8 unicode string, respectively (PR #204) - The
sdl2.ext.subsurface()
function has been re-documented and rewritten to have improved input handling and type checking (PR #204) - Improved inference of compression format for .tar archives in the
sdl2.ext.resources
submodule (PR #204) - Added a new function
sdl2.ext.surface_to_ndarray()
that returns a non-transposed copy of a given SDL surface as a 2D or 3D Numpy array (PR #204) - Added new functions
sdl2.ext.load_bmp()
andsdl2.ext.load_img()
for importing image files using SDL2 and SDL_image, respectively. Both new functions automatically convert the obtained surfaces to the ARGB8888 pixel format by default (PR #205) - Added a new function
sdl2.ext.save_bmp()
for saving SDL surfaces to BMP files (PR #205) - Added a new function
sdl2.ext.pillow_to_surface()
for convertingPIL.Image.Image
objects from the Pillow library to SDL surfaces (PR #205) - Added a new class
sdl2.ext.Texture
for creating renderer textures from SDL surfaces, as a basic wrapper for thesdl2.SDL_Texture
structure (PR #207) - Added a new function
sdl2.ext.set_texture_scale_quality()
that globally sets the scaling method (nearest-neighbour, linear filtering, or anisotropic filtering) to use for new SDL textures (PR #207) - Added a new method
sdl2.ext.Renderer.reset_logical_size()
to reset a Renderer’s logical size to its original value (PR #207) - Added a new method
sdl2.ext.Renderer.destroy()
to safely destroy and free memory associated with a Renderer after it is no longer needed (PR #207) - Added support for subpixel precision (i.e. using float coordinates)
with the drawing and copying methods of the
Renderer
class when using SDL2 2.0.10 or newer (PR #207) - Added
sdl2.ext.Renderer.blit()
as an alias for thesdl2.ext.Renderer.copy()
method (PR #207) - Added a new method
remap()
to theBitmapFont
class to allow specifying custom character widths and heights for each mapped character in a bitmap font (PR #208) - Added a new argument
line_h
tosdl2.ext.BitmapFont.render_on()
to allow specifying custom line heights (PR #208) - Added the
FontTTF
class, providing a new and flexible Pythonic wrapper around thesdlttf
module for opening and rendering text with TrueType and OpenType fonts. New features include custom line heights for multi-line text, left/right/center justification operations for multiline text, and specifying font sizes in units of pixels in addition to pt (PR #210) - Updated PySDL2 to automatically find and use Homebrew SDL2 binaries on Apple Silicon macs if no other usable binaries can be found.
Fixed Bugs:
- Fixed a typo in the
sdlttf
bindings where an alias for theTTF_RenderUTF8_Shaded()
function was incorrectly bound toTTF_RenderUTF
instead ofTTF_RenderUTF8
. - Fixed a bug introduced in 0.9.9 where the
SDL_WINDOW_INPUT_GRABBED
constant was no longer exported. MemoryView
and``arr[-1][-1]`
for accessing the last element in a 2D array). In previous versions, negative indices would retrieve values from undefined sections of memory outside the surface (PR #204)- Changed the functions in the
sdl2.ext.pixelaccess
module to no longer try to unlock RLE surfaces once their corresponding view objects are deleted. This prevents a segmentation fault when a view is garbage-collected but the surface has already been freed (PR #204) - Fixed a bug where the rectangle returned by
sdl2.ext.BitmapFont.render_on()
would overestimate the size of the rendered text by one character in both width and height (PR #208) sdl2.ext.BitmapFont.contains()
no longer assumes that the font map contains a space (PR #208)- Rendering multiline text with the
sdl2.ext.BitmapFont
class now always splits lines using the newline (n) character. Previously on Windows, it would only split on Windows-style line endings (rn) (PR #208)
API Changes:
- Updated and redocumented the
stringify()
andbyteify()
Python 2/3 compatibility functions to better handle bytes encoding/decoding and no longer require specifying an encoding type (defaults to UTF-8 if not manually specified) (PR #204) - The
subsurface()
function now allows subsurface areas to be specified usingSDL_Rect
objects and surfaces to be passed either directly or as a pointer (PR #204) - The
sdl2.ext.pixels2d()
andsdl2.ext.pixels3d()
functions no longer raise anExperimentalWarning
(PR #204) - Updated the
draw_line()
anddraw_point()
methods of theRenderer
class to accept coordinates as lists of(x, y)
tuples orSDL_Point`s in addition to flat ``[x, y, x, y, x, y]`
lists (PR #207) - Updated the
draw_rect()
andfill()
methods of theRenderer
class to accept coordinates as lists ofSDL_Rects`s in addition to lists of ``(x, y, w, h)`
tuples (PR #207) - Updated the
copy()
method of theRenderer
class to accept an(x, y)
tuple as a destination, inferring the destination width and height from the dimensions of the copied texture (PR #207) - Changed the
index
argument for theRenderer
class to take the name of the reqested rendering back end as a string instead of an index for better clarity and cross-platform consistency (PR #207)
Deprecation Notices:
- The
sdl2.ext.open_url()
function has been deprecated (PR #204) - The
sdl2.ext.load_image()
function has been deprecated, as it unexpectedly produces different surface formats depending on the backend used. New projects should use the newsdl2.ext.load_img()
,sdl2.ext.load_bmp()
, and/orsdl2.ext.pillow_to_surface()
functions instead (PR #205) - The
sdl2.ext.get_image_formats()
function has been deprecated, as it gives inaccurate results in most cases (PR #205) - The
sdl2.ext.BitmapFont.can_render()
method has been deprecated (PR #208) - The
sdl2.ext.BitmapFont.render()
method has been deprecated in favor ofsdl2.ext.BitmapFont.render_text()
, which returns an SDL surface instead of a SoftwareSprite and ensures the output surface is in ARGB8888 format by default (PR #208) - The
UIFactory
andUIProcessor
classes have been deprecated due to their complexity and maintenance burden. New functions and classes for creating GUIs with PySDL2 may be introduced in a future release (PR #209) - The
FontManager
class has been deprecated in favor of the new and more flexibleFontTTF
class (PR #210)
0.9.9¶
Released on 2021-09-02.
New Features:
- Updated to wrap new functions and constants in in SDL2 2.0.16 (PR #190)
Fixed bugs:
- Reverted the fix for (issue #139), which inadvertantly caused a serious bug
that prevented usage of any non-software renderer with windows created using
Window
objects.
0.9.8¶
Released on 2021-08-06.
New Features:
- Updated to wrap new functions and constants in introduced in SDL2 2.0.12 and 2.0.14 (PR #163 & PR #181)
- Fixed DLL loading issues with Python installed from the Microsoft Store on Windows (PR #185)
- Added informative errors for when the newest SDL2 binaries found on the system are too old to be used by PySDL2 (issue #165)
- Added support for passing
SDL_Rect
objects tosdl2.ext.draw.fill()
(issue #169) - Added support for passing
SDL_Surface
pointers directly to manysdl2.ext
functions, removing the need to explicitly use the.contents
attribute. - Added
sdl2.ext.MessageBox
,sdl2.ext.show_messagebox()
, andsdl2.ext.show_alert()
as Pythonic wrappers around the SDL2 MessageBox API (PR #188)
Fixed bugs:
- Fixed
NameError
when callingSDL_SetColorKey
, by @mgorny (PR #166) - Improved detection of SDL2 binaries on macOS, by @pvallet (PR #177 & PR #178)
- Fixed a bug preventing
sdl2.ext.font.BitmapFont
from being able to render, by @namelivia (PR #181) - Fixed
sdl2.ext.Window.show
behaviour under Wayland (issue #139) - Fixed a minor bug with the
helloworld.py
example (issue #174) - Fixed a bug that prevented the line clipping functions in
sdl2.ext.algorithms
from working if top and bottom arguments were specified backwards (issue #101)
0.9.7¶
Released on 2020-02-15.
- The minimum required SDL version is 2.0.5
- The minimum required SDL_ttf version is 2.0.14
- The minimum required SDL_mixer version is 2.0.1
- The minimum required SDL_image version is 2.0.1
- Improved compatibility with older SDL2 releases by raising informative exceptions whenever a function requiring a newer SDL2 binary is called
- added support for loading SDL2 .framework binaries on macOS
- added built-in support for pip installation of SDL2 binaries on macOS and Windows using pysdl2-dll
- fixed issue #75:
sdl2.SDL_JoystickGetGUIDString()
andsdl2.SDL_GameControllerMappingForGUID()
no longer cause a segfault on Python < 3.8 - fixed bug preventing use of background color with wrapped text using
sdl2.ext.FontManager.render()
(PR #134) - fixed issue #112: allow easy moving and resizing of
sdl2.ext.Window()
objects through ‘position’ and ‘size’ attributes, added ‘open’ and ‘close’ methods to Window objects - fixed issue #126: the write method for RW objects created with
sdl2.rw_from_object()
now returns the correct value - fixed issue #130: SDL_RW* functions now accept pointers to RW objects
- fixed issue #135:
sdl2.SDL_GetPrefPath()
andsdl2.SDL_GetPrefPath()
now return bytestrings instead of pointers to strings. - fixed issue #136:
sdl2.SysWMmsg
now properly defined and accessable for syswm events. - fixed issue #148: added support for loading SDL2 .so binaries with numeric suffixes on Unix-like OSes (e.g. ‘libSDL2.so.2’)
- fixed issue #152: restored compatibility with recent versions of PyPy
- fixed transparency issues with pallete indexed PNGs (PR #159)
- updated
sdl2
to include the latest changes of SDL2 (release 2.0.10) - updated
sdl2.sdlttf
to include the latest changes of SDL2_ttf (release 2.0.15) - updated
sdl2.sdlmixer
to include the latest changes of SDL2_mixer (release 2.0.4) - updated
sdl2.sdlimage
to include the latest changes of SDL2_image (release 2.0.5)
Big thanks to all the GitHub users who filed bug reports and submitted pull requests for this release.
0.9.6¶
Released on 2017-09-30.
- updated
sdl2
to include the latest changes of SDL2 (release 2.0.6) - fixed issue #98: wrap
sdl2.SDL_GetHintBoolean()
correctly - fixed issue #99: wrap
sdl2.sdlmixer.Mix_PausedMusic()
correctly - fixed issue #104:
sdl2.ext.Resources.scan()
shows the correct path on errors now - fixed issue #106: fix
TextureSprite.__repr__()
for an unset center property - fixed some minor GC issues in
sdl2.ext.sprite
andsdl2.ext.font
- fixed the __getattr__ implementation for
sdl2.ext.ebs.Entity
- fixed background colour handling for multiline text surfaces
0.9.5¶
Released on 2016-10-20.
- updated
sdl2
to include the latest changes of SDL2 (release 2.0.5) - fixed issue #94: added support for TrueType font collection (TTC) files
- fixed issue #80: added flip and rotation support for TextureSprite objects
- renamed
sdl2.ext.Renderer.renderer
attribute tosdl2.ext.Renderer.sdlrenderer
. The renderer attribute is deprecated and will be removed in a later version.
0.9.4¶
Released on 2016-07-07.
- updated
sdl2
to include the latest changes of SDL2 (release 2.0.4) - updated
sdl2.sdlttf
to include the latest changes of SDL_ttf (release 2.0.14) - new
sdl2.ext.Renderer.logical_size
attribute to set or retrieve the logical pixel size of a renderer - fixed issue #48: be more noisy about DLL loading issues
- fixed issue #65: misleading documentation for
sdl2.ext.Renderer.draw_line()
- fixed issue #67: Return a proper error code, when unittests running as subprocesses fail
- fixed issue #72:
sdl2.video.SDL_GL_DrawableSize()
not available on import - fixed issue #76: define missing SDL_PRESSED and SDL_RELEASED constants
- fixed issue #82: examples/gui.py fails due to an attribute error
- fixed issue #83: fix compatibility with newer PIL versions in
sdl2.ext.image.load_image()
- fixed issue #84: The setter of
sdl2.ext.Renderer.scale
works properly now - fixed issue #85: fix environment-dependent unit tests
- fixed issue #87: fix incorrect MIX_INIT_* constants in
sdl2.sdlmixer
- fixed issue #88: use PILs Image.tobytes() instead of the deprecated Image.tostring()
- fixed horizontical and vertical line drawing in
sdl2.ext.line()
- fixed a bug in
sdl2.ext.Renderer.draw_line()
for odd numbers of points - dropped IronPython support
0.9.3¶
Released on 2014-07-08.
- updated
sdl2
to include the latest changes of SDL2 (HG) - new
sdl2.ext.Renderer.scale
attribute, which denotes the horizontal and vertical drawing scale - new
sdl2.ext.point_on_line()
function to test, if a point lies on a line segment - PYSDL2_DLL_PATH can contain multiple paths separated by
os.pathsep
to search for the libraries now sdl2.ext.get_image_formats()
only returns BMP image support now, if SDL2_image and PIL are not foundsdl2.ext.load_image()
tries to usesdl2.SDL_LoadBMP()
now, if SDL2_image and PIL are not found- fixed issue #55:
sdl2.SDL_GameControllerAddMappingsFromFile()
does not raise a TypeError for Python 3.x anymore - fixed issue #56:
sdl2.ext.Renderer.draw_line()
andsdl2.ext.Renderer.draw_point()
handle multiple lines (or points) as arguments properly now - fixed issue #57: if SDL2_image is not installed and PIL is used, the loaded
pixel buffer of the image file is not referenced anymore after returning
from
sdl2.ext.load_image()
, causing random segmentation faults - fixed issue #58: raise a proper error,
if
sdl2.ext.FontManager.render()
could not render a text surface - fixed issue #59: The
sdl2.ext.TextureSpriteRenderSystem.sdlrenderer
attribute is correctly documented now - fixed a local variable and module name collision in
sdl2.ext.FontManager.render()
Thanks to Filip M. Nowak for the PYSDL2_DLL_PATH improvement.
0.9.2¶
Released on 2014-04-13.
- fixed issue #32: the line clipping algorithms do not run into precision errors anymore
- fixed issue #53 (again):
sdl2.video.SDL_GL_ResetAttributes()
is properly wrapped now to retain backwards compatibility with previous SDL2 releases - fixed issue #54: text input is correctly converted for the text entry component
- updated the example BMP files, which could not be loaded properly on some systems with SDL2_image and PIL
0.9.1¶
Released on 2014-04-05.
- fixed issue #50: corrected the
sdl2.ext.load_image()
documentation - fixed issue #52:
sdl2.ext.Renderer.fill()
,sdl2.ext.Renderer.draw_rect()
andsdl2.ext.Renderer.draw_point()
convert sequences correctly now - fixed issue #53: provide backwards compatibility for previous
SDL2 releases by adding a wrapper func for
sdl2.cpuinfo.SDL_HasAVX()
0.9.0¶
Released on 2014-03-23.
IMPORTANT: This release breaks backwards-compatibility. See the notes for the issues #36 and #39.
- updated
sdl2
to include the latest changes of SDL2 (release 2.0.3) - new
sdl2.ext.subsurface()
function to create subsurfaces fromsdl2.SDL_Surface
objects - new
sdl2.ext.SoftwareSprite.subsprite()
method to createsdl2.ext.SoftwarSprite
objects sharing pixel data - the unit test runner features a –logfile argument now to safe the unit test output to a file
- issues #36, #39: the different render classes of sdl2.ext.sprite were renamed
- the
sdl2.ext.RenderContext
class was renamed tosdl2.ext.Renderer
to be consistent with with SDL2’s naming scheme sdl2.ext.SpriteRenderer
was renamed tosdl2.ext.SpriteRenderSystem
sdl2.ext.SoftwareSpriteRenderer
was renamed tosdl2.ext.SoftwareSpriteRenderSystem
sdl2.ext.TextureSpriteRenderer
was renamed tosdl2.ext.TextureSpriteRenderSystem
sdl2.ext.SpriteFactory.create_sprite_renderer()
was renamed tosdl2.ext.SpriteFactory.create_sprite_render_system()
- the
- fixed
sdl2.audio.SDL_LoadWAV()
macro to provide the correct arguments - fixed issue #44: use a slightly less confusing
ValueError
, if a renderer argument for thesdl2.ext.SpriteFactory
is not provided - fixed issue #43: improved the code reference for the improved bouncing section in the docs
- fixed issue #40: typo in a
RuntimeWarning
message on loading the SDL2 libraries - fixed issue #38: the points arguments of
sdl2.ext.Renderer.draw_points()
are properly documented now - fixed issue #37:
sdl2.SDL_GetRendererOutputSize()
is now acccessible via a wildcard import - fixed issue #35: download location is now mentioned in the docs
- fixed issue #12: remove confusing try/except on import in the examples
0.8.0¶
Released on 2013-12-30.
- updated PD information to include the CC0 dedication, since giving software away is not enough anymore
- updated
sdl2
to include the latest changes of SDL2 (HG) - fixed a wrong C mapping of
sdl2.rwops.SDL_FreeRW()
- fixed various issues within the
sdl2.ext.BitmapFont
class - issue #26:
sdl2.SDL_AudioSpec.callback
is aSDL_AudioCallBack()
now - issue #30: the SDL_Add/DelHintCallback() unittest works with PyPy now
- issue #31:
sdl2.sdlmixer.SDL_MIXER_VERSION()
returns the proper version now
Thanks to Sven Eckelmann, Marcel Rodrigues, Michael McCandless, Andreas Schiefer and Franz Schrober for providing fixes and improvements.
0.7.0¶
Released on 2013-10-27.
- updated
sdl2
to include the latest changes of SDL2 (release 2.0.1) - fixed a bug in
sdl2.ext.FontManager.render()
, which did not apply the text color correctly - issue #14: improved the error messages on failing DLL imports
- issue #19: the
sdl2.ext.TextureSpriteRenderer.render()
andsdl2.ext.SoftwareSpriteRenderer.render()
methods do not misinterpret x and y arguments anymore, if set to 0 - issue #21:
sdl2.ext.load_image()
raises a properUnsupportedError
, if neither SDL_image nor PIL are usable
Thanks to Marcel Rodrigues, Roger Flores and otus for providing fixes and improvement ideas.
0.6.0¶
Released on 2013-09-01.
- new
sdl2.ext.FontManager.size
attribute, which gives a default size to be used for adding fonts or rendering text - updated
sdl2
to include the latest changes of SDL2 sdl2.ext.RenderContext.copy()
accepts any 4-value sequence as source or destination rectangle now- issue #11: throw an
ImportError
instead of aRuntimeError
, if a third-party DLL could not be imported properly - fixed a bug in the installation code, which caused
sdl2.examples
not to install the required resources
Thanks to Steven Johnson for his enhancements to the FontManager class. Thanks to Marcel Rodrigues for the improvements to RenderContext.copy().
0.5.0¶
Released on 2013-08-14.
- new
sdl2.ext.FontManager
class, which provides simple TTF font rendering. - new
sdl2.ext.SpriteFactory.from_text()
method, which creates text sprites - put the SDL2 dll path at the beginning of PATH, if a PYSDL2_DLL_PATH is provided to avoid loading issues for third party DLLs on Win32 platforms
- minor documentation fixes
Thanks to Dan Gillett for providing the FontManager and from_text() enhancements and his patience regarding all the small change requests. Thanks to Mihail Latyshov for providing fixes to the documentation.
0.4.1¶
Released on 2013-07-26.
- updated
sdl2
to include the latest changes of SDL2 - improved DLL detection for DLLs not being in a library path
- fixed a bug in
sdl2.ext.RenderContext.draw_rect()
for drawing a single rect - fixed a bug in the
repr()
call forsdl2.ext.SoftwareSprite
- issue #4: fixed a bug in
sdl2.ext.RenderContext.fill()
for filling a single rect - issue #5: fixed pip installation support
- issue #6: fixed a bug in
sdl2.ext.get_events()
, which did not handle more than 10 events in the queue correctly - issue #8:
sdl2.ext.SpriteFactory.create_texture_sprite()
can create sprites to be used as rendering targets now - issue #9: improved error messages on trying to bind non-existent library functions via ctypes
- minor documentation fixes
Thanks to Steven Johnson, Todd Rovito, Bil Bas and Dan McCombs for providing fixes and improvements.
0.4.0¶
Released on 2013-06-08.
- new
sdl2.sdlmixer
module, which provides access to the SDL2_mixer library - issue #1: fixed libc loading for cases where libc.so is a ld script
- updated
sdl2
andsdl2.sdlimage
to include the latest changes of the libraries, they wrap
0.3.0¶
Released on 2013-05-07.
- new
sdl2.sdlgfx
module, which provides access to the SDL2_gfx library - new
sdl2.ext.UIFactory.from_color
method; it creates UI-supportive sprites from a color - fixed color argument bugs in
sdl2.ext.RenderContext
methods - fixed a module namespace issues in
sdl2.ext.pixelaccess
sdl2.ext.SpriteFactory
methods do not use a defaultsize
argument anymore; it has to provided by the caller
0.2.0¶
Released on 2013-05-03.
- removed sdl2.ext.scene; it now lives in python-utils
- fixed
sdl2.haptic
module usage for Python 3 - fixed
sdl2.SDL_WindowGetData()
andsdl2.SDL_WindowSetData()
wrappers - fixed
sdl2.ext.RenderContext.copy()
- fixed
sdl2.ext.font
module usage for Python 3 - fixed
sdl2.ext.line()
sdl2
imports all submodules now- improved documentation
License¶
This software is distributed under the Public Domain. Since it is
not enough anymore to tell people: 'hey, just do with it whatever
you like to do', you can consider this software being distributed
under the CC0 Public Domain Dedication
(http://creativecommons.org/publicdomain/zero/1.0/legalcode.txt).
In cases, where the law prohibits the recognition of Public Domain
software, this software can be licensed under the zlib license as
stated below:
Copyright (C) 2012-2020 Marcus von Appen <marcus@sysfault.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgement in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
Some files are not covered by the license above.
For
examples/resources/tuffy.ttf
,examples/resources/tuffy.copy.ttf
andsdl2/test/resources/tuffy.ttf
the following terms apply:We, the copyright holders of this work, hereby release it into the public domain. This applies worldwide. In case this is not legally possible, We grant any entity the right to use this work for any purpose, without any conditions, unless such conditions are required by law. Thatcher Ulrich <tu@tulrich.com> http://tulrich.com Karoly Barta bartakarcsi@gmail.com Michael Evans http://www.evertype.com
doc/python.inv
An inventory index for linking to the proper places in the Python documentation, taken from http://docs.python.org/2/. Its copyright and license information can be found at http://docs.python.org/2/copyright.html and http://docs.python.org/2/license.html.