| 1 |
from AppKit import NSApplication |
|---|
| 2 |
|
|---|
| 3 |
from nodebox import graphics |
|---|
| 4 |
from nodebox import util |
|---|
| 5 |
|
|---|
| 6 |
class NodeBoxRunner(object): |
|---|
| 7 |
|
|---|
| 8 |
def __init__(self): |
|---|
| 9 |
# Force NSApp initialisation. |
|---|
| 10 |
NSApplication.sharedApplication().activateIgnoringOtherApps_(0) |
|---|
| 11 |
self.namespace = {} |
|---|
| 12 |
self.canvas = graphics.Canvas() |
|---|
| 13 |
self.context = graphics.Context(self.canvas, self.namespace) |
|---|
| 14 |
self.__doc__ = {} |
|---|
| 15 |
self._pageNumber = 1 |
|---|
| 16 |
self.frame = 1 |
|---|
| 17 |
|
|---|
| 18 |
def _check_animation(self): |
|---|
| 19 |
"""Returns False if this is not an animation, True otherwise. |
|---|
| 20 |
Throws an expection if the animation is not correct (missing a draw method).""" |
|---|
| 21 |
if self.canvas.speed is not None: |
|---|
| 22 |
if not self.namespace.has_key('draw'): |
|---|
| 23 |
raise graphics.NodeBoxError('Not a correct animation: No draw() method.') |
|---|
| 24 |
return True |
|---|
| 25 |
return False |
|---|
| 26 |
|
|---|
| 27 |
def run(self, source_or_code): |
|---|
| 28 |
self._initNamespace() |
|---|
| 29 |
if isinstance(source_or_code, basestring): |
|---|
| 30 |
source_or_code = compile(source_or_code + "\n\n", "<Untitled>", "exec") |
|---|
| 31 |
exec source_or_code in self.namespace |
|---|
| 32 |
if self._check_animation(): |
|---|
| 33 |
if self.namespace.has_key('setup'): |
|---|
| 34 |
self.namespace['setup']() |
|---|
| 35 |
self.namespace['draw']() |
|---|
| 36 |
|
|---|
| 37 |
def run_multiple(self, source_or_code, frames): |
|---|
| 38 |
if isinstance(source_or_code, basestring): |
|---|
| 39 |
source_or_code = compile(source_or_code + "\n\n", "<Untitled>", "exec") |
|---|
| 40 |
|
|---|
| 41 |
# First frame is special: |
|---|
| 42 |
self.run(source_or_code) |
|---|
| 43 |
yield 1 |
|---|
| 44 |
animation = self._check_animation() |
|---|
| 45 |
|
|---|
| 46 |
for i in range(frames-1): |
|---|
| 47 |
self.canvas.clear() |
|---|
| 48 |
self.frame = i + 2 |
|---|
| 49 |
self.namespace["PAGENUM"] = self.namespace["FRAME"] = self.frame |
|---|
| 50 |
if animation: |
|---|
| 51 |
self.namespace['draw']() |
|---|
| 52 |
else: |
|---|
| 53 |
exec source_or_code in self.namespace |
|---|
| 54 |
yield self.frame |
|---|
| 55 |
|
|---|
| 56 |
def _initNamespace(self, frame=1): |
|---|
| 57 |
self.canvas.clear() |
|---|
| 58 |
self.namespace.clear() |
|---|
| 59 |
# Add everything from the namespace |
|---|
| 60 |
for name in graphics.__all__: |
|---|
| 61 |
self.namespace[name] = getattr(graphics, name) |
|---|
| 62 |
for name in util.__all__: |
|---|
| 63 |
self.namespace[name] = getattr(util, name) |
|---|
| 64 |
# Add everything from the context object |
|---|
| 65 |
self.namespace["_ctx"] = self.context |
|---|
| 66 |
for attrName in dir(self.context): |
|---|
| 67 |
self.namespace[attrName] = getattr(self.context, attrName) |
|---|
| 68 |
# Add the document global |
|---|
| 69 |
self.namespace["__doc__"] = self.__doc__ |
|---|
| 70 |
# Add the frame |
|---|
| 71 |
self.frame = frame |
|---|
| 72 |
self.namespace["PAGENUM"] = self.namespace["FRAME"] = self.frame |
|---|
| 73 |
|
|---|
| 74 |
def make_image(source_or_code, outputfile): |
|---|
| 75 |
|
|---|
| 76 |
"""Given a source string or code object, executes the scripts and saves the result as an image. |
|---|
| 77 |
Supported image extensions: pdf, tiff, png, jpg, gif""" |
|---|
| 78 |
|
|---|
| 79 |
runner = NodeBoxRunner() |
|---|
| 80 |
runner.run(source_or_code) |
|---|
| 81 |
runner.canvas.save(outputfile) |
|---|
| 82 |
|
|---|
| 83 |
def make_movie(source_or_code, outputfile, frames, fps=30): |
|---|
| 84 |
|
|---|
| 85 |
"""Given a source string or code object, executes the scripts and saves the result as a movie. |
|---|
| 86 |
You also have to specify the number of frames to render. |
|---|
| 87 |
Supported movie extension: mov""" |
|---|
| 88 |
|
|---|
| 89 |
from nodebox.util import QTSupport |
|---|
| 90 |
runner = NodeBoxRunner() |
|---|
| 91 |
movie = QTSupport.Movie(outputfile, fps) |
|---|
| 92 |
for frame in runner.run_multiple(source_or_code, frames): |
|---|
| 93 |
movie.add(runner.canvas) |
|---|
| 94 |
movie.save() |
|---|
| 95 |
|
|---|
| 96 |
def usage(err=""): |
|---|
| 97 |
if len(err) > 0: |
|---|
| 98 |
err = '\n\nError: ' + str(err) |
|---|
| 99 |
print """NodeBox console runner |
|---|
| 100 |
Usage: console.py sourcefile imagefile |
|---|
| 101 |
or: console.py sourcefile moviefile number_of_frames [fps] |
|---|
| 102 |
Supported image extensions: pdf, tiff, png, jpg, gif |
|---|
| 103 |
Supported movie extension: mov""" + err |
|---|
| 104 |
|
|---|
| 105 |
def main(): |
|---|
| 106 |
import sys, os |
|---|
| 107 |
if len(sys.argv) < 2: |
|---|
| 108 |
usage() |
|---|
| 109 |
elif len(sys.argv) == 3: # Should be an image |
|---|
| 110 |
basename, ext = os.path.splitext(sys.argv[2]) |
|---|
| 111 |
if ext not in ('.pdf', '.gif', '.jpg', '.jpeg', '.png', '.tiff'): |
|---|
| 112 |
return usage('This is not a supported image format.') |
|---|
| 113 |
make_image(open(sys.argv[1]).read(), sys.argv[2]) |
|---|
| 114 |
elif len(sys.argv) == 4 or len(sys.argv) == 5: # Should be a movie |
|---|
| 115 |
basename, ext = os.path.splitext(sys.argv[2]) |
|---|
| 116 |
if ext != '.mov': |
|---|
| 117 |
return usage('This is not a supported movie format.') |
|---|
| 118 |
if len(sys.argv) == 5: |
|---|
| 119 |
try: |
|---|
| 120 |
fps = int(sys.argv[4]) |
|---|
| 121 |
except ValueError: |
|---|
| 122 |
return usage() |
|---|
| 123 |
else: |
|---|
| 124 |
fps = 30 |
|---|
| 125 |
make_movie(open(sys.argv[1]).read(), sys.argv[2], int(sys.argv[3]), fps) |
|---|
| 126 |
|
|---|
| 127 |
def test(): |
|---|
| 128 |
# Creating the NodeBoxRunner class directly: |
|---|
| 129 |
runner = NodeBoxRunner() |
|---|
| 130 |
runner.run('size(500,500)\nfor i in range(400):\n oval(random(WIDTH),random(HEIGHT),50,50, fill=(random(), 0,0,random()))') |
|---|
| 131 |
runner.canvas.save('console-test.pdf') |
|---|
| 132 |
runner.canvas.save('console-test.png') |
|---|
| 133 |
|
|---|
| 134 |
# Using the runner for animations: |
|---|
| 135 |
runner = NodeBoxRunner() |
|---|
| 136 |
for frame in runner.run_multiple('size(300, 300)\ntext(FRAME, 100, 100)', 10): |
|---|
| 137 |
runner.canvas.save('console-test-frame%02i.png' % frame) |
|---|
| 138 |
|
|---|
| 139 |
# Using the shortcut functions: |
|---|
| 140 |
make_image('size(200,200)\ntext(FRAME, 100, 100)', 'console-test.gif') |
|---|
| 141 |
make_movie('size(200,200)\ntext(FRAME, 100, 100)', 'console-test.mov', 10) |
|---|
| 142 |
|
|---|
| 143 |
if __name__=='__main__': |
|---|
| 144 |
main() |
|---|