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:

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:

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 running pip 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 the PYSDL2_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 use sdl2.SDL_SaveBMP() to save them as bitmap files. Other formats are currently unsupported, but might be added to the sdl2.ext package in the future.

Font handling…

… is too hard. Why can’t it work the same way as pygame does?

The sdl2.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 the minx and maxx test

  • you could enhance the CollisionSystem to process PlayerData components and add the functionality to add points there (or write a small processor that keeps track of the ball only and processes only the PlayerData and video.SoftSprite objects of each player for adding points). Alternatively, you could use the sdl2.ext.EventHandler class to raise a score count function within the CollisionSystem, 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 sets

    StaticRepeatingSprite(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.

pygame.version
pygame.version sdl2
ver sdl2.__version__
vernum sdl2.version_info

Footnotes

[1](1, 2, 3, 4, 5) Check https://bitbucket.org/marcusva/python-utils for an easy to use system font detection module

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 and SDL_REVISION_NUMBER from SDL_revision.h
  • SDL_NAME() from SDL_name.h
  • SDL_MostSignificantBitIndex32() and SDL_HasExactlyOneBitSet32 from SDL_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 via sdl2.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.

Behavioural design

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.

Information design

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.

All in all

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.

_images/ebs.png
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.

_images/copprocessing.png

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.

world

The World the entity resides in.

delete() → None

Deletes the Entity from its World. This basically calls World.delete() with the Entity.

class sdl2.ext.Applicator

A processing system for combined data sets. The Applicator is an enhanced System that receives combined data sets based on its set System.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 the Applicator. If, for example, the Applicator is defined as

class MyApplicator(Applicator):
    def __init__(self):
        self.componenttypes = (Foo, Bar)

its process method will receive (Foo, Bar) tuples

def 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 same Entity. That said, an Entity must contain a Foo as well as a Bar component in order to have them both processed by the Applicator (while a System with the same componenttypes 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.

componenttypes

A tuple of class identifiers that shall be processed by the System

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 a componenttypes attribute and a process() method, which match the signatures of the System class

class 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 a is_applicator property, that evaluates to True

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 to World.process().

delete(entity : Entity)

Removes an Entity from the World, including all its component data.

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.

process()

Processes all component items within their corresponding System instances.

remove_system(system : System)

Removes a processing System from the world.

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 the EventHandler 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. The MPEventHandler 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 the multiprocessing module. If the module is not available in the target environment, a sdl2.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 the sender 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.

x

The top-left horizontal offset at which the Sprite resides.

y

The top-left vertical offset at which the Sprite resides.

position

The top-left position (x and y) as tuple.

size

The width and height of the Sprite as tuple.

Note

This is an abstract property and needs to be implemented by inheriting classes.

area

The rectangular area occupied by the Sprite.

depth

The layer depth on which to draw the Sprite. Sprite objects with higher depth values will be drawn on top of other Sprite values by the SpriteRenderSystem.

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 the SoftwareSprite.

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 the SoftwareSprite. 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 the TextureSprite.

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 the TextureSprite.

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 displaying Sprite 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 for sorted().

process(world : World, components : iterable) → None

Renders the passed Sprite objects via the render() method. The Sprite objects are sorted via sortfunc before they are passed to render().

render(sprite : iterable) → None

Renders the Sprite objects.

Note

This is a no-op function and needs to be implemented by inheriting classes.

class sdl2.ext.SoftwareSpriteRenderSystem(window : object)

A rendering system for SoftwareSprite components. The SoftwareSpriteRenderSystem class uses a sdl2.SDL_Window as drawing device to display SoftwareSprite 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 or sdl2.SDL_Window instance.

window

The sdl2.SDL_Window that is used as drawing device.

surface

The sdl2.SDL_Surface that acts as drawing context for window.

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 to None, the location information of the sprites are used. If set and sprites is an iterable, such as a list of SoftwareSprite objects, x and y are relative location values that will be added to each individual sprite’s position. If sprites is a single SoftwareSprite, x and y denote the absolute position of the SoftwareSprite, if set.

class sdl2.ext.TextureSpriteRenderSystem(target : object)

A rendering system for TextureSprite components. The TextureSpriteRenderSystem class uses a sdl2.SDL_Renderer as drawing device to display Sprite surfaces.

target can be a sdl2.ext.Window, sdl2.SDL_Window, a:class:sdl2.ext.Renderer or a sdl2.SDL_Renderer. If it is a sdl2.ext.Window or sdl2.SDL_Window instance, it will try to create a sdl2.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 to None, the location information of the sprites are used. If set and sprites is an iterable, such as a list of TextureSprite objects, x and y are relative location values that will be added to each individual sprite’s position. If sprites is a single TextureSprite, x and y denote the absolute position of the TextureSprite, if set.

class sdl2.ext.SpriteFactory(sprite_type=TEXTURE, **kwargs)

A factory class for creating Sprite objects. The SpriteFactory can create TextureSprite or SoftwareSprite instances, depending on the sprite_type being passed to it, which can be SOFTWARE or TEXTURE. 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 for SoftwareSprite or TEXTURE for TextureSprite 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 the sprite_type, this will return a SoftwareSprite or TextureSprite.

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() and create_texture_sprite() method signatures. kwargs however will be mixed with the set default_args so that one does not necessarily have to provide all arguments, if they are set within the default_args. If kwargs and default_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 set sprite_type. If sprite_type is TEXTURE, a TextureSpriteRenderSystem is created with the the renderer from the default_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 via sdl2.ext.load_image().

from_object(obj: object) → Sprite

Creates a Sprite from an object. The object will be passed through sdl2.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 passed sdl2.SDL_Surface. If free is set to True, the passed surface will be freed automatically.

from_text(text : str[, **kwargs]) → Sprite

Creates a Sprite from a string of text. This method requires a sdl2.ext.FontManager to be in kwargs or default_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.

_images/uiprocessing.png
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 input
  • CHECKBUTTON - as BUTTON, but it retains its state on clicks
  • TEXTENTRY - 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 bound EventHandler 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 elements

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)

An EventHandler that is invoked, if the mouse moves around while being over the BUTTON.

button.pressed(event : sdl2.events.SDL_Event)

An EventHandler that is invoked, if a mouse button is pressed on the BUTTON.

button.released(event : sdl2.events.SDL_Event)

An EventHandler that is invoked, if a mouse button is released on the BUTTON.

button.click(event : sdl2.events.SDL_Event)

An EventHandler that is invoked, if a mouse button is pressed and released on the BUTTON.

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.

Text input elements

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)

An EventHandler that is invoked, if the mouse moves around while being over the TEXTENTRY.

textentry.pressed(event : sdl2.events.SDL_Event)

An EventHandler that is invoked, if a mouse button is pressed on the TEXTENTRY.

textentry.released(event : sdl2.events.SDL_Event)

An EventHandler that is invoked, if a mouse button is released on the TEXTENTRY.

textentry.keydown(event : sdl2.events.SDL_Event)

An EventHandler that is invoked on pressing a key.

textentry.keyup(event : sdl2.events.SDL_Event)

An EventHandler that is invoked on releasing a key.

textentry.input(event : sdl2.events.SDL_Event)

An EventHandler that is invoked on text input events. Text input events are automatically created, once the UIProcessor activates a TEXTENTRY 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 the UIProcessor activates a TEXTENTRY 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 the Sprite class. To do this, it requires a SpriteFactory, which will create the sprites, to which the UIFactory 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 new Sprite objects.

create_button(**kwargs) → Sprite

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.

create_check_button(**kwargs) → Sprite

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 to True, 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 the UIProcessor.

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, its state 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, its state 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, its state 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 a BUTTON.

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 and EventHandler instances bound to the object. If obj is a iterable, such as a list or set, every item within obj must feature an events attribute as described above.

process(world : World, components : iterable) → None

The UIProcessor class does not implement the process() method by default. Instead it uses dispatch() 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 to process() 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 to process() and a set 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 to process() 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.

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

sdl2.sdlttf.TTF_GetError()

Returns the most recently encountered SDL2 error message, if any.

This function is a simple wrapper around SDL_GetError().

Retuns:A UTF-8 encoded string describing the most recent SDL2 error.
Return type:bytes
sdl2.sdlttf.TTF_SetError(fmt)

Sets the most recent SDL2 error message to a given string.

This function is a simple wrapper around SDL_SetError().

Parameters:fmt (bytes) – A UTF-8 encoded string containing the error message to set.
Retuns:Always returns -1.
Return type:int

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.

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

sdl2.sdlimage.IMG_GetError()

Returns the most recently encountered SDL2 error message, if any.

This function is a simple wrapper around SDL_GetError().

Retuns:A UTF-8 encoded string describing the most recent SDL2 error.
Return type:bytes
sdl2.sdlimage.IMG_SetError(fmt)

Sets the most recent SDL2 error message to a given string.

This function is a simple wrapper around SDL_SetError().

Parameters:fmt (bytes) – A UTF-8 encoded string containing the error message to set.
Retuns:Always returns -1.
Return type:int

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.

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

sdl2.sdlmixer.Mix_GetError()

Returns the most recently encountered SDL2 error message, if any.

This function is a simple wrapper around SDL_GetError().

Retuns:A UTF-8 encoded string describing the most recent SDL2 error.
Return type:bytes
sdl2.sdlmixer.Mix_SetError(fmt)

Sets the most recent SDL2 error message to a given string.

This function is a simple wrapper around SDL_SetError().

Parameters:fmt (bytes) – A UTF-8 encoded string containing the error message to set.
Retuns:Always returns -1.
Return type:int

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.

Drawing functions

Pixel drawing functions
Line drawing functions
Rectangle drawing functions
Circle drawing functions
Ellipse drawing functions
Pie slice drawing functions
Triangle drawing functions
Polygon drawing functions
Bezier curve drawing functions

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, and sdlgfx 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, and sdlgfx modules, as well as partial inline documentation for the sdlmixer 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() and utf8() Python 2/3 compatibility functions to the compat 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() and sdl2.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 converting PIL.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 the sdl2.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 the sdl2.ext.Renderer.copy() method (PR #207)
  • Added a new method remap() to the BitmapFont 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 to sdl2.ext.BitmapFont.render_on() to allow specifying custom line heights (PR #208)
  • Added the FontTTF class, providing a new and flexible Pythonic wrapper around the sdlttf 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 the TTF_RenderUTF8_Shaded() function was incorrectly bound to TTF_RenderUTF instead of TTF_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() and byteify() 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 using SDL_Rect objects and surfaces to be passed either directly or as a pointer (PR #204)
  • The sdl2.ext.pixels2d() and sdl2.ext.pixels3d() functions no longer raise an ExperimentalWarning (PR #204)
  • Updated the draw_line() and draw_point() methods of the Renderer class to accept coordinates as lists of (x, y) tuples or SDL_Point`s in addition to flat ``[x, y, x, y, x, y]` lists (PR #207)
  • Updated the draw_rect() and fill() methods of the Renderer class to accept coordinates as lists of SDL_Rects`s in addition to lists of ``(x, y, w, h)` tuples (PR #207)
  • Updated the copy() method of the Renderer 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 the Renderer 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 new sdl2.ext.load_img(), sdl2.ext.load_bmp(), and/or sdl2.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 of sdl2.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 and UIProcessor 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 flexible FontTTF 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 to sdl2.ext.draw.fill() (issue #169)
  • Added support for passing SDL_Surface pointers directly to many sdl2.ext functions, removing the need to explicitly use the .contents attribute.
  • Added sdl2.ext.MessageBox, sdl2.ext.show_messagebox(), and sdl2.ext.show_alert() as Pythonic wrappers around the SDL2 MessageBox API (PR #188)

Fixed bugs:

  • Fixed NameError when calling SDL_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() and sdl2.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() and sdl2.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 and sdl2.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 to sdl2.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 found
  • sdl2.ext.load_image() tries to use sdl2.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() and sdl2.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() and sdl2.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 from sdl2.SDL_Surface objects
  • new sdl2.ext.SoftwareSprite.subsprite() method to create sdl2.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
  • 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 the sdl2.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 a SDL_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() and sdl2.ext.SoftwareSpriteRenderer.render() methods do not misinterpret x and y arguments anymore, if set to 0
  • issue #21: sdl2.ext.load_image() raises a proper UnsupportedError, 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 a RuntimeError, 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 for sdl2.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 and sdl2.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 default size 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() and sdl2.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

0.1.0

Released on 2013-04-23.

  • Initial Release

Todo list for PySDL2

General

  • more unit tests

Windows

  • Add support for SDL_SetWindowsMessageHook()

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 and sdl2/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.

Indices and tables