Introduction

PyGame is a platform-independent wrapper for the SDL libraries. A common question on the wxPython-users mailing list is how to display PyGame graphics within a wxPython window. This page attempts to gather some of the common responses and code samples in a central location.

What Objects are Involved

In addition to wxPython, you'll need to install PyGame. You can download PyGame at http://www.pygame.org/download.shtml .

Play Video/Audio Using PyGame

You can also use PyGame to play video and audio files in a wxPython window. wxGameCanvas is not really necessary because pygame.movie.play() runs in its own thread. We're still not sure though how stable this really is, and if it works on Mac or Linux. Only tested on Windows. Also the sound quality of SDL (the library used by PyGame) might not be as good as other audio/video playing libraries, so this might not be a good option for those of you wanting to create your own MP3 players.

   1 class myFrame(wx.wxFrame):
   2 
   3 
   4     def play(self, filename):
   5         import sys
   6         ##Note we call the GetHandle() method of a control in the window/frame, not the wxFrame itself
   7         self.hwnd = self.GetChildren()[0].GetHandle()
   8         if sys.platform == "win32":
   9             os.environ['SDL_VIDEODRIVER'] = 'windib'
  10         os.environ['SDL_WINDOWID'] = str(self.hwnd) #must be before init
  11 
  12         ## NOTE WE DON'T IMPORT PYGAME UNTIL NOW.  Don't put "import pygame" at the top of the file.
  13         import pygame
  14         pygame.display.init()
  15 
  16         self.movie = pygame.movie.Movie(filename)
  17 
  18         if self.movie.has_video():
  19             w,h = self.movie.get_size()
  20             if w<=0 or h<=0: w,h = 1,1
  21         else:
  22             #? need something to display if audio only.
  23             #We can't have a 0,0 canvas, pygame/SDL doesn't like that.
  24             w,h = 1,1
  25         self.display = pygame.display.set_mode((w,h)) #size no matter
  26 
  27         self.movie.set_display(self.display)
  28         self.movie.play()

--Doug Holton

Based on the code samples above and code by Kevin Altis.

Other Options:

Additional Resources

Old Code Samples

This code was shamelessly cribbed from the wxPython-users list archive. It demonstrates a couple of key concepts. First, it defines a wxSDLWindow class which wraps PyGame in a top-level wxPython frame. This class is suitable for use in mixed wxPython/PyGame apps. To actually do the drawing, you subclass wxSDLWindow and implement the draw method (the CircleWindow class in this example).

Note this code has been tested on Linux only using PyGame 1.5.5 and wxPython 2.4.0.2.

import os

from wxPython.wx import *
import pygame

class wxSDLWindow(wxFrame):
    def __init__(self, parent, id, title = 'SDL window', **options):
        options['style'] = wxDEFAULT_FRAME_STYLE | wxTRANSPARENT_WINDOW
        wxFrame.__init__(*(self, parent, id, title), **options)

        self._initialized = 0
        self._resized = 0
        self._surface = None
        self.__needsDrawing = 1

        EVT_IDLE(self, self.OnIdle)
        
    def OnIdle(self, ev):
        if not self._initialized or self._resized:
            if not self._initialized:
                # get the handle
                hwnd = self.GetHandle()
                
                os.environ['SDL_WINDOWID'] = str(hwnd)
                if sys.platform == 'win32':
                    os.environ['SDL_VIDEODRIVER'] = 'windib'
                
                pygame.init()
                
                EVT_SIZE(self, self.OnSize)
                self._initialized = 1
        else:
            self._resized = 0

        x,y = self.GetSizeTuple()
        self._surface = pygame.display.set_mode((x,y))

        if self.__needsDrawing:
            self.draw()

    def OnPaint(self, ev):
        self.__needsDrawing = 1

    def OnSize(self, ev):
        self._resized = 1
        ev.Skip()

    def draw(self):
        raise NotImplementedError('please define a .draw() method!')

    def getSurface(self):
        return self._surface


if __name__ == "__main__":

    class CircleWindow(wxSDLWindow):
        "draw a circle in a wxPython / PyGame window"
        def draw(self):
            surface = self.getSurface()
            if surface is not None:
                topcolor = 5
                bottomcolor = 100

                pygame.draw.circle(surface, (250,0,0), (100,100), 50)
                
                pygame.display.flip()

    def pygametest():
        app = wxPySimpleApp()
        sizeT = (640,480)
        w = CircleWindow(None, -1, size = sizeT)
        w.Show(1)
        app.MainLoop()

    pygametest()

On Windows, however...

As has been indicated on the mailing list and so forth, a resource that I did not properly peruse during my quest for getting this thing working on Windows, the above does not work on Windows. This page confounded me because it absolutely in no way works. Well, ok, it works a bit, but not really acceptably. Riccardo Trocca however has worked out and posted the solution, so I'll copy it here from its home in the archives at http://aspn.activestate.com/ASPN/Mail/Message/1627085 . His statement is that merely importing pygame does some initialization which makes the setting of environment variables useless. Here's the code that does what we Windows users want.

Note: this code only tested to work in Python 2.4 with wx2.6 and Pygame 1.7.1. It is known not work with pygame 1.6.x. This used to be for older versions of wx, but it was updated in July 2006.

Note: stefano is right ; I found upgrading to SDL 1.2.8 fixed the crashes. And you won't receive mouse events (from SDL) with this technique. Also, the SDL_WINDOWID value used below will effectively only be read on the first import of pygame, so you can only make one window per Python process. -markh

   1 import wx
   2 import os
   3 import thread
   4 global pygame # when we import it, let's keep its proper name!
   5 
   6 class SDLThread:
   7     def __init__(self,screen):
   8         self.m_bKeepGoing = self.m_bRunning = False
   9         self.screen = screen
  10         self.color = (255,0,0)
  11         self.rect = (10,10,100,100)
  12 
  13     def Start(self):
  14         self.m_bKeepGoing = self.m_bRunning = True
  15         thread.start_new_thread(self.Run, ())
  16 
  17     def Stop(self):
  18         self.m_bKeepGoing = False
  19 
  20     def IsRunning(self):
  21         return self.m_bRunning
  22 
  23     def Run(self):
  24         while self.m_bKeepGoing:
  25             e = pygame.event.poll()
  26             if e.type == pygame.MOUSEBUTTONDOWN:
  27                 self.color = (255,0,128)
  28                 self.rect = (e.pos[0], e.pos[1], 100, 100)
  29                 print e.pos
  30             self.screen.fill((0,0,0))
  31             self.screen.fill(self.color,self.rect)
  32             pygame.display.flip()
  33         self.m_bRunning = False;
  34 
  35 class SDLPanel(wx.Panel):
  36     def __init__(self,parent,ID,tplSize):
  37         global pygame
  38         wx.Panel.__init__(self, parent, ID, size=tplSize)
  39         self.Fit()
  40         os.environ['SDL_WINDOWID'] = str(self.GetHandle())
  41         os.environ['SDL_VIDEODRIVER'] = 'windib'
  42         import pygame # this has to happen after setting the environment variables.
  43         pygame.display.init()
  44         window = pygame.display.set_mode(tplSize)
  45         self.thread = SDLThread(window)
  46         self.thread.Start()
  47 
  48     def __del__(self):
  49         self.thread.Stop()
  50 
  51 class MyFrame(wx.Frame):
  52     def __init__(self, parent, ID, strTitle, tplSize):
  53         wx.Frame.__init__(self, parent, ID, strTitle, size=tplSize)
  54         self.pnlSDL = SDLPanel(self, -1, tplSize)
  55         #self.Fit()
  56 
  57 app = wx.PySimpleApp()
  58 frame = MyFrame(None, wx.ID_ANY, "SDL Frame", (640,480))
  59 frame.Show()
  60 app.MainLoop()

Thanks, Riccardo!

-tom!

IntegratingPyGame (last edited 2009-11-29 07:43:05 by newacct)