IntegratingPyGame

来源:互联网 发布:c专家编程pdf高清下载 编辑:程序博客网 时间:2024/06/05 01:52

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:

  • David Woods' Transana might hold another option for playing video & audio files in a wxPython application. I think he is using Windows Media Player as a COM control or something, and something else to play video on the Mac.

Additional Resources

  • Search the wx-python user list for PyGame

  • PyGame documentation

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.

切换行号显示
   1 import os   2    3 from wxPython.wx import *   4 import pygame   5    6 class wxSDLWindow(wxFrame):   7     def __init__(self, parent, id, title = 'SDL window', **options):   8         options['style'] = wxDEFAULT_FRAME_STYLE | wxTRANSPARENT_WINDOW   9         wxFrame.__init__(*(self, parent, id, title), **options)  10   11         self._initialized = 0  12         self._resized = 0  13         self._surface = None  14         self.__needsDrawing = 1  15   16         EVT_IDLE(self, self.OnIdle)  17   18     def OnIdle(self, ev):  19         if not self._initialized or self._resized:  20             if not self._initialized:  21                 # get the handle  22                 hwnd = self.GetHandle()  23   24                 os.environ['SDL_WINDOWID'] = str(hwnd)  25                 if sys.platform == 'win32':  26                     os.environ['SDL_VIDEODRIVER'] = 'windib'  27   28                 pygame.init()  29   30                 EVT_SIZE(self, self.OnSize)  31                 self._initialized = 1  32         else:  33             self._resized = 0  34   35         x,y = self.GetSizeTuple()  36         self._surface = pygame.display.set_mode((x,y))  37   38         if self.__needsDrawing:  39             self.draw()  40   41     def OnPaint(self, ev):  42         self.__needsDrawing = 1  43   44     def OnSize(self, ev):  45         self._resized = 1  46         ev.Skip()  47   48     def draw(self):  49         raise NotImplementedError('please define a .draw() method!')  50   51     def getSurface(self):  52         return self._surface  53   54   55 if __name__ == "__main__":  56   57     class CircleWindow(wxSDLWindow):  58         "draw a circle in a wxPython / PyGame window"  59         def draw(self):  60             surface = self.getSurface()  61             if surface is not None:  62                 topcolor = 5  63                 bottomcolor = 100  64   65                 pygame.draw.circle(surface, (250,0,0), (100,100), 50)  66   67                 pygame.display.flip()  68   69     def pygametest():  70         app = wxPySimpleApp()  71         sizeT = (640,480)  72         w = CircleWindow(None, -1, size = sizeT)  73         w.Show(1)  74         app.MainLoop()  75   76     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

  • (SDL only allows one active window anyway (at least, according to the docs), so this isn't actually a problem. --TomPlunket)

    (the below code only allows one window to be created per process even if the first window is destroyed. however there is a way to create more than one window perprocess but they can not exist at the same time. see the section I added farther down. -BenjaminPowers)

IMPORTANT NOTE: Pygame has issues running in anything besides the main thread. The below code will likely be unstable. I'm leaving this note here until I can fix the example myself, but a solution more like the above should be used where any pygame event handling and drawing should happen in an OnIdle handler or using a wx.Timer. I had issues with code based on the below while also using twisted, and they went away when I ran pygame code from a twisted LoopingCall (which runs the code in the main thread). --ClaudiuSaftoiu

切换行号显示
   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!

Creating new pygame windows after an old pygame window has been destroyed

Only one pygame surface can exists at one time in the same python process. However, a new surface can be created provided the previous surface has been properly destroyed. The above sample do not show this as such I crested my own re-useing most of the above code and pulling from one of the wx examples. reed the comments to find the relevant changes.

切换行号显示
   1 import wx   2 import wx.aui   3 import os   4 import threading   5 global pygame # when we import it, let's keep its proper name!   6 global pygame_init_flag   7 pygame_init_flag = False   8 from pygame.locals import *   9   10 import sys; sys.path.insert(0, "..")  11   12 class ParentFrame(wx.aui.AuiMDIParentFrame):  13     def __init__(self, parent):  14         wx.aui.AuiMDIParentFrame.__init__(self, parent, -1,  15                                           title="AuiMDIParentFrame",  16                                           size=(640,480),  17                                           style=wx.DEFAULT_FRAME_STYLE)  18         self.count = 0  19         mb = self.MakeMenuBar()  20         self.SetMenuBar(mb)  21         self.CreateStatusBar()  22   23     def MakeMenuBar(self):  24         mb = wx.MenuBar()  25         menu = wx.Menu()  26         item = menu.Append(-1, "New SDL child window\tCtrl-N")  27         self.Bind(wx.EVT_MENU, self.OnNewChild, item)  28         item = menu.Append(-1, "Close parent")  29         self.Bind(wx.EVT_MENU, self.OnDoClose, item)  30         mb.Append(menu, "&File")  31         return mb  32   33     def OnNewChild(self, evt):  34         self.count += 1  35         child = ChildFrameSDL(self, self.count)  36         child.Show()  37   38     def OnDoClose(self, evt):  39         self.Close()  40   41 class ChildFrameSDL(wx.aui.AuiMDIChildFrame):  42     def __init__(self, parent, count):  43         wx.aui.AuiMDIChildFrame.__init__(self, parent, -1,  44                                          title="Child: %d" % count)  45         mb = parent.MakeMenuBar()  46         menu = wx.Menu()  47         item = menu.Append(-1, "This is child %d's menu" % count)  48         mb.Append(menu, "&Child")  49         self.SetMenuBar(mb)  50   51         p = SDLPanel(self, -1, (640,480))  52   53         sizer = wx.BoxSizer()  54         sizer.Add(p, 1, wx.EXPAND)  55         self.SetSizer(sizer)  56   57         wx.CallAfter(self.Layout)  58   59 class SDLThread:  60     def __init__(self,screen):  61         self.m_bKeepGoing = self.m_bRunning = False  62         self.screen = screen  63         self.color = (255,0,0)  64         self.rect = (10,10,100,100)  65         self.thread = None  66         self.init = True  67   68     def Start(self):  69         #I rewrote this to use the higherlevel threading module  70         self.m_bKeepGoing = self.m_bRunning = True  71         self.thread = threading.Thread(group=None, target=self.Run, name=None,  72                                        args=(), kwargs={})  73         self.thread.start()  74   75     def Stop(self):  76         self.m_bKeepGoing = False  77         #this important line make sure that the draw thread exits before  78         #pygame.quit() is called so there is no errors  79         self.thread.join()  80   81     def IsRunning(self):  82         return self.m_bRunning  83   84     def Run(self):  85         while self.m_bKeepGoing:  86             #I rewrote this to only draw when the position changes  87             e = pygame.event.poll()  88             if e.type == pygame.MOUSEBUTTONDOWN:  89                 self.color = (255,0,128)  90                 self.rect = (e.pos[0], e.pos[1], 100, 100)  91                 print e.pos  92                 self.screen.fill((0,0,0))  93                 self.screen.fill(self.color,self.rect)  94             if self.init:  95                 self.screen.fill((0,0,0))  96                 self.screen.fill(self.color,self.rect)  97             pygame.display.flip()  98         self.m_bRunning = False;  99         print "pygame draw loop exited" 100  101 class SDLPanel(wx.Panel): 102     def __init__(self,parent,ID,tplSize): 103         global pygame 104         global pygame_init_flag 105         wx.Panel.__init__(self, parent, ID, size=tplSize) 106         self.Fit() 107         os.environ['SDL_WINDOWID'] = str(self.GetHandle()) 108         os.environ['SDL_VIDEODRIVER'] = 'windib' 109         #here is where things change if pygame has already been initialized  110         #we need to do so again 111         if pygame_init_flag: 112             #call pygame.init() on subsaquent windows 113             pygame.init() 114         else: 115             #import if this is the first time 116             import pygame 117         pygame_init_flag = True #make sure we know that pygame has been imported 118         pygame.display.init() 119         window = pygame.display.set_mode(tplSize) 120         self.thread = SDLThread(window) 121         self.thread.Start() 122  123     def __del__(self): 124         self.thread.Stop() 125         print "thread stoped" 126         #very important line, this makes sure that pygame exits before we  127         #reinitialize it other wise we get errors 128         pygame.quit() 129  130 app = wx.PySimpleApp() 131 frame = ParentFrame(None) 132 frame.Show() 133 app.MainLoop()

you will notice that two SDL panels created at the same time causes python to crash, but if you close the first one before creating a second you can. -BenjaminPowers