""" Using pygame as a web interface builder. python pygame2web.py # runs the demo program in a pygame window python pygame2web.py 8000 # runs as a server for http://127.0.0.1:8000 """ import os, sys, re, time import pygame from pygame.locals import * import SimpleHTTPServer, htmlentitydefs from cStringIO import StringIO from py.magic import greenlet import process, pixmap, httpserver class BaseInterface: def pixmap(self, w, h, data, colorkey=-1, alpha=255): img = pygame.image.fromstring(data, (w, h), "RGB") if colorkey >= 0: r = colorkey & 0xFF g = (colorkey >> 8) & 0xFF b = (colorkey >> 16) & 0xFF img.set_colorkey([r, g, b], RLEACCEL) elif alpha >= 255: return img.convert(self.offscreen) if alpha < 255: img.set_alpha(alpha, RLEACCEL) return img def loadpixmap(self, filename, colorkey=-1, alpha=255): w, h, data = pixmap.decodepixmap(open(filename, 'rb')) return self.pixmap(w, h, data, colorkey, alpha) def putppm(self, x, y, bitmap): self.offscreen.blit(bitmap, (x, y)) # ____________________________________________________________ class PygameInterface(BaseInterface): def __init__(self, game): # Initialize pygame pygame.init() # Set the display mode winstyle = HWSURFACE # |FULLSCREEN bestdepth = pygame.display.mode_ok((game.width, game.height), winstyle, 32) self.screen = pygame.display.set_mode((game.width, game.height), winstyle, bestdepth) self.offscreen = pygame.Surface((game.width, game.height)) #decorate the game window pygame.display.set_caption(game.title) def flip(self): self.screen.blit(self.offscreen, (0, 0)) pygame.display.flip() def next_mouse_clic(self): self.flip() return greenlet.getcurrent().parent.switch() def mainloop(cls, game): intf = cls(game) g = greenlet(game.run) g.switch(intf) while 1: e = pygame.event.wait() if e.type == MOUSEBUTTONDOWN: g.switch(e.pos) elif e.type == QUIT: break mainloop = classmethod(mainloop) # ____________________________________________________________ class HttpInterface(BaseInterface): INDEXPAGE = '''
'''
re_coords = re.compile(r"(\d+)[,](\d+)")
TIMEOUT = 5 * 60 # never timeout before 5 minutes
MAX_CLIENTS = 5 # timeout older clients if this is reached
Clients = {}
def __init__(self, game):
# Initialize pygame
pygame.init()
self.offscreen = pygame.Surface((game.width, game.height))
self.game = game
self.runner = greenlet(game.run)
self.runner.switch(self) # let the game initialize itself
def age(self):
return time.time() - self.last_activity
def indexpage(self, query='', **options):
match = self.re_coords.match(query)
if match:
# invalidate the current state
self.unregister()
# send clics to the game
x = int(match.group(1))
y = int(match.group(2))
self.runner.switch((x, y))
# validate the new state
self.register()
# build the html page
f = httpserver.TranslatorIO(self.INDEXPAGE, locals())
return f, 'text/html'
def currentimage(self, **options):
width, height = self.offscreen.get_size()
data = pygame.image.tostring(self.offscreen, "RGB")
data = pixmap.encodepixmap(width, height, data)
ppm = process.StringWriter(data)
png = process.Process('pnmtopng', ppm)
return png.as_file(), "image/png"
def next_mouse_clic(self):
return greenlet.getcurrent().parent.switch()
# the answer is (x,y) sent by indexpage above
def register(self):
def randomid():
import random
return str(random.randrange(0, sys.maxint))
stateid = randomid()
while stateid in HttpInterface.Clients:
stateid = randomid()
HttpInterface.Clients[stateid] = self
self.stateid = stateid
self.last_activity = time.time()
httpserver.register('%s/index.html' % self.stateid, self.indexpage)
httpserver.register('%s/screen.png' % self.stateid, self.currentimage)
def unregister(self):
httpserver.unregister('%s/index.html' % self.stateid)
httpserver.unregister('%s/screen.png' % self.stateid)
del HttpInterface.Clients[self.stateid]
def mainloop(cls, game):
# class method.
def mainindexpage(**options):
clients = HttpInterface.Clients
if len(clients) >= HttpInterface.MAX_CLIENTS:
# throw off a client
ages = [(c.age(), c) for c in clients.values()]
age, c = max(ages)
if age < HttpInterface.TIMEOUT:
raise httpserver.HTTPRequestError(
"too many clients, please try again later")
c.unregister()
# create a new client
intf = cls(game)
intf.register()
return intf.indexpage()
httpserver.register('', mainindexpage)
httpserver.register('index.html', mainindexpage)
httpserver.server_mainloop()
mainloop = classmethod(mainloop)
# ____________________________________________________________
if __name__ == '__main__':
if len(sys.argv) == 1:
Intf = PygameInterface
else:
Intf = HttpInterface
class MyGame:
width = 320
height = 200
title = "Hello!"
def run(self, intf):
"The complete demo program."
FILE = 'demo.ppm'
bmp = intf.loadpixmap(FILE, colorkey=0x010101)
w, h = bmp.get_size()
while 1:
x, y = intf.next_mouse_clic()
intf.putppm(x - w//2, y - h//2, bmp)
Intf.mainloop(MyGame())