Author: EvanCofsky Date: 2008-04-20 16:15:10 -0400 (Sun, 20 Apr 2008) New Revision: 1246
Added: trunk/pysoy/examples/Widget/ trunk/pysoy/examples/Widget/Examples.py trunk/pysoy/examples/Widget/HeadsUp.py trunk/pysoy/examples/Widget/Interfaces.py trunk/pysoy/examples/Widget/__init__.py trunk/pysoy/examples/Widget/main.py Log: Ticket #894: Widget Examples This first example demonstrates how to create a basic set of HUD widgets. It also shows stacking, margins, and Canvases, and how to subclass soy Widgets to make new widgets. I also made an Example-loading and running framework which is probably a bit excessive. And it attempts to set the CPU affinity just to be safe. That bit should not be necessary after the new mill compiler is working. After building and installing soy, run examples/Widgets/main.py for instructions. Currently, to run the HUD example: examples/Widgets/main.py HeadsUp Added: trunk/pysoy/examples/Widget/Examples.py =================================================================== --- trunk/pysoy/examples/Widget/Examples.py (rev 0) +++ trunk/pysoy/examples/Widget/Examples.py 2008-04-20 20:15:10 UTC (rev 1246) @@ -0,0 +1,46 @@ +"""Loads all subclasses of Example within this package. + +Why? Because Gonzology hates main methods and loops and repeated code +for updating things that can be updated and basically is obsessively +autistic about these things. This can either be reworked or discarded +as anyone else sees fit.""" + +__all__ = ['getExamples'] + +import os +import os.path +import imp +import inspect + +from Interfaces import Example + +def isexample(o): + """True if o is a subclass of Example.""" + + try: + return o is not Example and issubclass(o, Example) + except Exception, e: + pass + +MODPATH = os.path.dirname(__file__) or '.' +def getExamples(): + """Finds all of the modules in this package and yield the + subclasses of L{Example}.""" + + f = None + for root, dirs, files in os.walk(MODPATH): + for fn in files: + try: + name = inspect.getmodulename(fn) + if name is None: continue + f, path, description = imp.find_module(name, [root]) + mod = imp.load_module(name, f, path, description) + for c in inspect.getmembers(mod, isexample): + yield c + + except ImportError: + continue + + finally: + if f is not None: + f.close() Added: trunk/pysoy/examples/Widget/HeadsUp.py =================================================================== --- trunk/pysoy/examples/Widget/HeadsUp.py (rev 0) +++ trunk/pysoy/examples/Widget/HeadsUp.py 2008-04-20 20:15:10 UTC (rev 1246) @@ -0,0 +1,184 @@ +__all__ = ['Examples'] + +from random import random + +import soy +from Interfaces import Example + +class LabeledText(soy.widgets.StackX): + """A widget with two text fields arranged horizontally.""" + + def __init__(self, parent = None, margin = None, aspect = -1.0, + label = "", text = "", + labelFont = "DejaVu Sans", textFont = "DejaVu Sans"): + + """Creates the Canvases for the label text and the updatable + text.""" + + self._labelTexture = soy.textures.Print(font = labelFont) + self._textTexture = soy.textures.Print(font = textFont) + self._labelCanvas = soy.widgets.Canvas(self, + texture = self._labelTexture) + self._textCanvas = soy.widgets.Canvas(self, + texture = self._textTexture) + + self.label = label + self.text = text + + def getText(self): + """The text displayed in the text field.""" + + return self._textTexture.text + + def setText(self, text): + self._textTexture.text = str(text) + + text = property(getText, setText) + + def getTextColor(self): + """The RGB floats of the current text color.""" + + return self._textTexture.foreground.floats + + def setTextColor(self, color): + self._textTexture.foreground = soy.colors.Color(color) + + textColor = property(getTextColor, setTextColor) + + def getLabel(self): + """The label displayed in the label field.""" + + return self._labelTexture.text + + def setLabel(self, label): + self._labelTexture.text = str(label) + + label = property(getLabel, setLabel) + +class HudWidget(soy.widgets.StackY): + """Displays the velocity and position of body.""" + + def __init__(self, parent = None, margin = None, aspect = -1.0, + body = None): + """Initializes the L{LabeledText} widgets displaying position + and velocity and stores C{body}.""" + + self.positionHud = LabeledText(self, + label = "Position:") + self.velocityHud = LabeledText(self, + label = "Velocity:") + + self.body = body + + self.update() + + def formatVector(self, vector): + """Formats a 3-tuple of floats.""" + return "(%6.2f x, %6.2f y, %6.2f z)" % tuple(vector) + + def update(self): + """Updates the position and velocity text fields with the + position of self.body.""" + + if self.body is None: + return + + self.positionHud.text = self.formatVector(self.body.position) + self.velocityHud.text = self.formatVector(self.body.velocity) + +class BouncingBody(soy.bodies.Body): + """This body bounces off the walls.""" + + def __init__(self, scene=None, + position=None, rotation=None, velocity=None, + model=None, shape=None, bounds = 3.0): + """Initializes this Body to be constrained to a cube centered + on the origin with edges M{bounds * 2} long.""" + + self.shape = soy.shapes.Box(1,1,1) + self.model = soy.models.Shape(soy.materials.StainlessSteel()) + + self.rotation = [random() - 0.5 * 1.1 + for v in range(3)] + self.position = [3.0 - (random() - 0.5) + for v in range(3) ] + self.velocity = [2.0 - (1.5 * (random() - 0.5)) + for v in range(3)] + + self.bounds = bounds + + def update(self): + """Updates the motion and rotation vectors with some + randomness and keeps us constrained to the bounds with some + random jitter.""" + + vel = [] + for p, v in zip(self.position, self.velocity): + v += (random() - 0.5) * 0.1 + if abs(p) > self.bounds + random() - 0.5 and ((v > 0 and p > 0) or + v < 0 and p < 0): + v = -v * (1 - random() * 0.15) + vel.append(v) + self.velocity = vel + + rot = [] + for r in self.rotation: + r += (random() - 0.5) * 0.06 + + rot.append(r) + + self.rotation = rot + +class InstructionsWidget(soy.widgets.StackY): + def __init__(self, parent = None, margin = None, aspect = -1.0, + label = "", text = "", + labelFont = "DejaVu Sans", textFont = "DejaVu Sans"): + + width, height = self.size + margin = [140, 0, 0, 125] + self.directions = LabeledText(self, + margin = margin, + label = "To Quit", + text = "Hit ESC") + + self.directions.textColor = [random() for c in range(3)] + + def update(self): + color = [] + + for c in self.directions.textColor: + c *= (random() + 0.8) + if c > 1.0: + c = random() + color.append(c) + self.directions.textColor = color + +class HeadsUp(Example): + """A simple Heads-Up Display example. + + This example creates a scene containing a basic shape moving + around the screen with a Canvas widget displaying the x, y, and z + coordinates of the body. + + The Widgets used are the Projector, the Canvas, and the StackZ. + The StackZ stacks the Canvas in front of the Projector so that the + text showing the block position and velocity is rendered last. + """ + + def createWidgets(self): + """Builds the widget stack on self.window.""" + + self.stackz = soy.widgets.StackZ(self.window) + self.projector = soy.widgets.Projector(self.stackz, + camera = self.camera) + + self.hud = HudWidget(self.stackz, body = self.body) + self.instructions = InstructionsWidget(self.stackz) + self.addUpdatable(self.hud) + self.addUpdatable(self.instructions) + + def buildScene(self): + """Builds the scene graph on L{scene}.""" + + self.body = BouncingBody(self.scene) + self.addUpdatable(self.body) Added: trunk/pysoy/examples/Widget/Interfaces.py =================================================================== --- trunk/pysoy/examples/Widget/Interfaces.py (rev 0) +++ trunk/pysoy/examples/Widget/Interfaces.py 2008-04-20 20:15:10 UTC (rev 1246) @@ -0,0 +1,110 @@ +"""Declares the interfaces used in the Widget examples.""" + +__all__ = ['Example', 'Animation'] + +from time import sleep + +class Updatable(object): + """Interface for any scene item that should be updated by + L{Example.run}.""" + + def update(self): + """Update the state of this object in a main loop iteration.""" + + raise NotImplementedError("Updatable.update must be implemented in subclasses.") + +class Example(Updatable): + """Interface implemented by all examples. Implementations of this + are searched for by the L{main} module for listing and running. + The class docstring is displayed to the user by L{main} when it + presents the available examples.""" + + def __init__(self, frameRate = 10.0): + """Initializes the attributes and the frame length for this + example.""" + + self._updatables = () + self._scene = None + self._light = None + self._camera = None + self._window = None + self.frameLength = 1.0 / frameRate + + def addUpdatable(self, widget): + """Adds an updatable item to the list of items updated during + the main loop.""" + + self._updatables += (widget, ) + + def update(self): + """Called during the main loop to update all updatable widgets.""" + + for u in self._updatables: + u.update() + + def buildScene(self): + """Hook for subclasses to build the scene when L{scene} is + changed.""" + + raise NotImplementedError("buildScene must be implemented in subclasses of Example.") + + def getScene(self): + """The L{Scene} for this example. The scene is initialized by + the L{Example} instance, but can be manipulated at any time by + L{main} or the L{Example}.""" + + return self._scene + + def setScene(self, scene): + self._scene = scene + self.buildScene() + + scene = property(getScene, setScene) + + def getCamera(self): + """The active L{soy.bodies.Camera} for this example.""" + + return self._camera + + def setCamera(self, camera): + self._camera = camera + + camera = property(getCamera, setCamera) + + def getLight(self): + """The active L{soy.bodies.Light} for this example.""" + + return self._light + + def setLight(self, light): + self._light = light + + light = property(getLight, setLight) + + def getWindow(self): + """The Window to build the Widget stack in. + + Setting this will rebuild the widget stack for the scene in + the new window.""" + + return self._window + + def setWindow(self, window): + self._window = window + self.createWidgets() + + window = property(getWindow, setWindow) + + def createWidgets(self): + """Creates the widget stack for this L{Example}. This is + called by L{setWindow} whenever the L{Window} changes.""" + + pass + + def run(self): + """Sleeps between each iteration and updates the textures.""" + + while True: + self.update() + sleep(self.frameLength) + Added: trunk/pysoy/examples/Widget/__init__.py =================================================================== --- trunk/pysoy/examples/Widget/__init__.py (rev 0) +++ trunk/pysoy/examples/Widget/__init__.py 2008-04-20 20:15:10 UTC (rev 1246) @@ -0,0 +1,8 @@ +""" +Examples of L{Widget} usage. + +This package contains sample modules demonstrating how the L{Widget} +classes can be used. The simple examples show basic L{Widget} +stacking, while the more complex examples show changing L{Camera}s and +heads-up displays and backgrounds. +""" Added: trunk/pysoy/examples/Widget/main.py =================================================================== --- trunk/pysoy/examples/Widget/main.py (rev 0) +++ trunk/pysoy/examples/Widget/main.py 2008-04-20 20:15:10 UTC (rev 1246) @@ -0,0 +1,100 @@ +#!/usr/bin/env python + +import sys + +import os +import os.path +import subprocess + +from time import sleep +from textwrap import TextWrapper + +import soy +from Examples import getExamples + +class ExampleError(Exception): + """Errors caused by problems with loading and running Examples.""" + + pass + +class NoExampleProvided(ExampleError): + """No example was specified on the command line.""" + + @property + def message(self): + return "Please specify one of the above examples as an argument." + +def help_message(exception, examples): + """Displays the help message if something has gone wrong.""" + + sys.stderr.write(""" +PySoy Widget Examples + +This will run one of the Widget examples from examples/Widget. The +following examples are available: + +""") + + for name, scene in examples.items(): + wrapper = TextWrapper(initial_indent = "%-13s : " % name, + subsequent_indent = "%13s : " % " ", + fix_sentence_endings = True, + replace_whitespace = True) + + sys.stderr.write(wrapper.fill(scene.__doc__.replace(" ", " "))) + sys.stderr.write('\n') + + sys.stderr.write('\n') + sys.stderr.write('The error encountered was:\n') + sys.stderr.write('%s\n' % TextWrapper(initial_indent = " ", + subsequent_indent = " ", + fix_sentence_endings = True).fill(exception.message)) + sys.stderr.write('\n') + sys.exit(1) + +def taskset(): + """Tries to set our CPU affinity using taskset. This should not + be necessary once mill is working.""" + + pid = os.getpid() + try: + c = subprocess.check_call(['taskset', '-p', '1', '%d' % os.getpid()]) + except subprocess.CalledProcessError, e: + sys.stderr.write("Could not set CPU affinity (taskset returned %d). Example may crash." % e.returncode) + +def main(examples, *names): + taskset() + + try: + exampleName = names[0] + except IndexError: + raise NoExampleProvided() + + example = examples[exampleName]() + + example.scene = soy.scenes.Scene() + + example.camera = soy.bodies.Camera(example.scene) + example.camera.position = (0.0, 0.0, 10.0) + + example.light = soy.bodies.Light(example.scene) + example.light.position = (-10.0, 10.0, 2.0) + + screen = soy.Screen() + window = soy.Window(screen, exampleName, size = (800, 480)) + + example.window = window + + quitController = soy.controllers.Keyboard(window) + quitController[ 1 ] = soy.actions.Quit() # 9 = esc key + + example.run() + +if __name__ == '__main__': + examples = dict(getExamples()) + + try: + main(examples, *sys.argv[1:]) + + except ExampleError, e: + help_message(e, examples) Property changes on: trunk/pysoy/examples/Widget/main.py ___________________________________________________________________ Name: svn:executable + * _______________________________________________ PySoy-SVN mailing list PySoy-SVN@pysoy.org http://www.pysoy.org/mailman/listinfo/pysoy-svn