Skip to content

Commit

Permalink
awesome pygame wrapper for XO activities
Browse files Browse the repository at this point in the history
  • Loading branch information
DAWacker committed Sep 9, 2013
1 parent da7656a commit 0ceb737
Show file tree
Hide file tree
Showing 10 changed files with 668 additions and 0 deletions.
137 changes: 137 additions & 0 deletions example/README.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
== Sugargame ==

Sugargame is a Python package which allows [http://www.pygame.org/ Pygame]
programs to run well under Sugar.
It is fork of the olcpgames framework, which is no longer maintained.

http://git.sugarlabs.org/projects/sugargame

What it does:

* Wraps a Sugar activity around an existing Pygame program with few changes
* Allows Sugar toolbars and other widgets to be added to the activity UI
* Provides hooks for saving to and restoring from the Journal

==== Differences between Sugargame and olpcgames ====

The olpcgames framework provides a wrapper around Pygame which attempts to
allow a Pygame program to run mostly unmodified under Sugar. To this end,
the Pygame program is run in a separate thread with its own Pygame message
loop while the main thread runs the GTK message loop. Also, olpcgames wraps
Sugar APIs such as the journal and mesh into a Pygame-like API.

Sugargame takes a simpler approach; it provides a way to embed Pygame into a
GTK widget. The Sugar APIs are used to interact with Sugar, the Pygame APIs
are used for the game.

Sugargame advantages:

* Simpler code
* More elegant interface between Pygame and GTK
* Runs as a single thread: no thread related segfaults
* Possible to use Sugar widgets with Pygame

Sugargame limitations:

* No support for Pango or SVG sprites (yet)

== Using Sugargame ==

See also [[Development Team/Sugargame/Examples]].

==== Wrapping a Pygame program ====

To use Sugargame to Sugarize a Pygame program, set up an activity directory and
copy the Sugargame package to it.

The activity directory should look something like this:

activity/ - Activity directory: activity.info, SVG icon, etc.
sugargame/ - Sugargame package
MyActivity.py - Activity class
mygame.py - Pygame code
setup.py - Install script

To make the Activity class, start with test/TestActivity.py from the Sugargame
distribution.

The activity should create a single PygameCanvas widget and call run_pygame on it.
Pass the main loop function of the Pygame program.

self._canvas = sugargame.canvas.PygameCanvas(self)
self.set_canvas(self._canvas)

# Start the game running.
self._canvas.run_pygame(self.game.run)

In your Pygame main loop, pump the GTK message loop:

while gtk.events_pending():
gtk.main_iteration()

==== Adding Pygame to a PyGTK activity ====

To add Pygame to an existing Sugar activity, create a PygameCanvas widget and call
run_pygame on it.

widget = sugargame.canvas.PygameCanvas(self)
vbox.pack_start(widget)

widget.run_pygame(self.game.run)

Due to limitations of Pygame and SDL, there can only be one PygameCanvas in the
entire activity.

The argument to run_pygame is a function structured like a Pygame program. In the
main loop, remember to dispatch GTK messages using gtk.main_iteration().

def main_loop():
clock = pygame.time.Clock()
screen = pygame.display.get_surface()

while self.running:
# Pump GTK messages.
while gtk.events_pending():
gtk.main_iteration()

# Pump PyGame messages.
for event in pygame.event.get():
if event.type == pygame.QUIT:
return
elif event.type == pygame.VIDEORESIZE:
pygame.display.set_mode(event.size, pygame.RESIZABLE)

# Check the mouse position
x, y = pygame.mouse.get_pos()

# Clear Display
screen.fill((255,255,255)) #255 for white

# Draw stuff here
.................

# Flip Display
pygame.display.flip()

# Try to stay at 30 FPS
self.clock.tick(30)

== Support ==

For help with Sugargame, please email the Sugar Labs development list:

: [email protected]

Sugargame is developed by Wade Brainerd <[email protected]>.

It is loosely based on the source code to the olpcgames framework, developed by
the One Laptop Per Child project.

=== Changelog ===

====v1.1====
* Fix bugs in event handling. (Pablo Moleri)
* Remove reference to gtk.Socket.get_window() method, which is missing in older versions of PyGTK.

====v1.0====
* Initial version of Sugargame
92 changes: 92 additions & 0 deletions example/test/TestActivity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
from gettext import gettext as _

import sys
from gi.repository import Gtk
import pygame

import sugar3.activity.activity
from sugar3.graphics.toolbarbox import ToolbarBox
from sugar3.activity.widgets import ActivityToolbarButton
from sugar3.graphics.toolbutton import ToolButton
from sugar3.activity.widgets import StopButton


sys.path.append('..') # Import sugargame package from top directory.
import sugargame.canvas

import TestGame


class TestActivity(sugar3.activity.activity.Activity):
def __init__(self, handle):
super(TestActivity, self).__init__(handle)

self.paused = False

# Create the game instance.
self.game = TestGame.TestGame()

# Build the activity toolbar.
self.build_toolbar()

# Build the Pygame canvas.
self._pygamecanvas = sugargame.canvas.PygameCanvas(self)

# Note that set_canvas implicitly calls read_file when
# resuming from the Journal.
self.set_canvas(self._pygamecanvas)
self._pygamecanvas.grab_focus()

# Start the game running (self.game.run is called when the
# activity constructor returns).
self._pygamecanvas.run_pygame(self.game.run)

def build_toolbar(self):
toolbar_box = ToolbarBox()
self.set_toolbar_box(toolbar_box)
toolbar_box.show()

activity_button = ActivityToolbarButton(self)
toolbar_box.toolbar.insert(activity_button, -1)
activity_button.show()

# Pause/Play button:

stop_play = ToolButton('media-playback-stop')
stop_play.set_tooltip(_("Stop"))
stop_play.set_accelerator(_('<ctrl>space'))
stop_play.connect('clicked', self._stop_play_cb)
stop_play.show()

toolbar_box.toolbar.insert(stop_play, -1)

# Blank space (separator) and Stop button at the end:

separator = Gtk.SeparatorToolItem()
separator.props.draw = False
separator.set_expand(True)
toolbar_box.toolbar.insert(separator, -1)
separator.show()

stop_button = StopButton(self)
toolbar_box.toolbar.insert(stop_button, -1)
stop_button.show()

def _stop_play_cb(self, button):
# Pause or unpause the game.
self.paused = not self.paused
self.game.set_paused(self.paused)

# Update the button to show the next action.
if self.paused:
button.set_icon('media-playback-start')
button.set_tooltip(_("Start"))
else:
button.set_icon('media-playback-stop')
button.set_tooltip(_("Stop"))

def read_file(self, file_path):
self.game.read_file(file_path)

def write_file(self, file_path):
self.game.write_file(file_path)
91 changes: 91 additions & 0 deletions example/test/TestGame.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#!/usr/bin/python
import pygame
from gi.repository import Gtk


class TestGame:
def __init__(self):
# Set up a clock for managing the frame rate.
self.clock = pygame.time.Clock()

self.x = -100
self.y = 100

self.vx = 10
self.vy = 0

self.paused = False
self.direction = 1

def set_paused(self, paused):
self.paused = paused

# Called to save the state of the game to the Journal.
def write_file(self, file_path):
pass

# Called to load the state of the game from the Journal.
def read_file(self, file_path):
pass

# The main game loop.
def run(self):
self.running = True

screen = pygame.display.get_surface()

while self.running:
# Pump GTK messages.
while Gtk.events_pending():
Gtk.main_iteration()

# Pump PyGame messages.
for event in pygame.event.get():
if event.type == pygame.QUIT:
return
elif event.type == pygame.VIDEORESIZE:
pygame.display.set_mode(event.size, pygame.RESIZABLE)
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
self.direction = -1
elif event.key == pygame.K_RIGHT:
self.direction = 1

# Move the ball
if not self.paused:
self.x += self.vx * self.direction
if self.direction == 1 and self.x > screen.get_width() + 100:
self.x = -100
elif self.direction == -1 and self.x < -100:
self.x = screen.get_width() + 100

self.y += self.vy
if self.y > screen.get_height() - 100:
self.y = screen.get_height() - 100
self.vy = -self.vy

self.vy += 5

# Clear Display
screen.fill((255, 255, 255)) # 255 for white

# Draw the ball
pygame.draw.circle(screen, (255, 0, 0), (self.x, self.y), 100)

# Flip Display
pygame.display.flip()

# Try to stay at 30 FPS
self.clock.tick(30)


# This function is called when the game is run directly from the command line:
# ./TestGame.py
def main():
pygame.init()
pygame.display.set_mode((0, 0), pygame.RESIZABLE)
game = TestGame()
game.run()

if __name__ == '__main__':
main()
23 changes: 23 additions & 0 deletions example/test/activity/activity-generic.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions example/test/activity/activity.info
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[Activity]
name = SugargameTest
bundle_id = org.sugarlabs.SugargameTest
exec = sugar-activity TestActivity.TestActivity
icon = activity-generic
activity_version = 1
8 changes: 8 additions & 0 deletions example/test/activity/mimetypes.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
<mime-type type="application/x-physics-activity">
<comment xml:lang="en">Physics Activity</comment>
<glob pattern="*.physics"/>
</mime-type>
</mime-info>

4 changes: 4 additions & 0 deletions example/test/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env python
from sugar3.activity import bundlebuilder
bundlebuilder.start()

1 change: 1 addition & 0 deletions sugargame/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__version__ = '1.1'
Loading

0 comments on commit 0ceb737

Please sign in to comment.