├── .gitmodules ├── .nojekyll ├── README.creole ├── mandelbrot-nojit.html ├── mandelbrot.html ├── mandelbrot.py ├── mandelbrot_enhanced-nojit.html ├── mandelbrot_enhanced.html ├── mandelbrot_enhanced.py ├── minimal_console.html └── simple_http_server.py /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "pypyjs-release"] 2 | path = pypyjs-release 3 | url = https://github.com/pypyjs/pypyjs-release.git 4 | [submodule "pypyjs-release-nojit"] 5 | path = pypyjs-release-nojit 6 | url = https://github.com/pypyjs/pypyjs-release-nojit.git 7 | -------------------------------------------------------------------------------- /.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pypyjs/pypyjs-examples/9fac2d37b0339e50ba30322fa77ee89a307c855c/.nojekyll -------------------------------------------------------------------------------- /README.creole: -------------------------------------------------------------------------------- 1 | 2 | === PyPy.js examples 3 | 4 | **Note:** Development is sleeping at the moment. Discuss this here: https://github.com/pypyjs/pypyjs/issues/213 5 | 6 | This git repository contains some example usage of PyPy.js 7 | 8 | It used the git repro https://github.com/pypyjs/pypyjs-release (and the no-JIT) as a git submodule. 9 | 10 | 11 | Please fork and contribute own examples/snippets! 12 | 13 | 14 | ==== Small snippets: 15 | 16 | Here some small snippets for copy&paste into [[http://pypyjs.org/editor.html|PyPyJS-Editor]]. 17 | 18 | Display some browser information from JavaScript object **navigator**: 19 | {{{ 20 | import js 21 | navigator = js.globals["navigator"] 22 | for js_string in dir(navigator): 23 | attr_name = str(js_string) # FIXME 24 | attr = getattr(navigator, attr_name, "-") 25 | if not isinstance(attr, (js.String, js.Array, js.Boolean)): 26 | continue 27 | print "%20s: %s" % (attr_name, attr) 28 | }}} 29 | 30 | 31 | Create html buttons and bind the click event with jQuery: 32 | {{{ 33 | import js # https://github.com/pypyjs/pypy/tree/pypyjs/pypy/module/js 34 | 35 | jquery = js.globals["$"] 36 | for i in range(10): 37 | jquery("#editor").before('' % (i,i)) 38 | 39 | @js.Function 40 | def click_handler(event): 41 | print "You clicked on button with ID:", 42 | print event.target.id 43 | 44 | jquery("button").click(click_handler) 45 | 46 | print "Please click on new button above!" 47 | }}} 48 | 49 | 50 | Display information about a //js// object: 51 | {{{ 52 | from __future__ import print_function 53 | import js # https://github.com/pypyjs/pypy/tree/pypyjs/pypy/module/js 54 | 55 | def pprint_js_object(obj): 56 | print("Object:", obj, type(obj), obj.__doc__) 57 | print("dir:", dir(obj)) 58 | print("Existing object attributes:") 59 | for attr_name in obj: 60 | attr_name = str(attr_name) # convert js.String https://github.com/pypyjs/pypy/issues/2 61 | print("%30s:" % attr_name, end="") 62 | try: 63 | value = getattr(obj, attr_name) 64 | except Exception as err: 65 | print("[Error: %s]" % err) 66 | else: 67 | value = str(value) # evaluate js.Handle 68 | value = repr(value) # e.g. escape newlines 69 | if len(value)>70: 70 | value = "%s..." % value[:70] 71 | print(value) 72 | 73 | window = js.globals.window 74 | pprint_js_object(window) 75 | print(" --- END --- ") 76 | }}} 77 | 78 | Some system information: 79 | {{{ 80 | import os 81 | 82 | methods=( 83 | "getlogin", "getuid", "getgid", 84 | "getpid", "getpgrp", "getppid", 85 | "geteuid", "getegid", 86 | ) 87 | for method in methods: 88 | func = getattr(os, method) 89 | doc = func.__doc__.split("\n\n")[-1].strip() 90 | result = func() 91 | print "%12s: %-10s %s" % (method, result, doc) 92 | }}} 93 | 94 | 95 | Manipulating the virtual file system: 96 | {{{ 97 | import os 98 | 99 | LIB_PATH = "/lib/pypyjs/lib_pypy/" 100 | 101 | with open(os.path.join(LIB_PATH, "my_module.py"), "w") as f: 102 | f.write("x='hi'") 103 | 104 | import my_module 105 | 106 | print my_module.x # hi 107 | print "my_module.__file__:", my_module.__file__ # /lib/pypyjs/lib_pypy/my_module.py 108 | 109 | import os 110 | for filename in os.listdir(LIB_PATH): # Will list my_module.py and my_module.pyc 111 | if filename.startswith("my"): 112 | filepath = os.path.join(LIB_PATH, filename) 113 | print "*", filename, os.stat(filepath) 114 | }}} 115 | (Currently there is no public API for manipulating the virtual filesystem, see also: https://github.com/pypyjs/pypyjs/issues/132 ) 116 | 117 | ==== Existing examples: 118 | 119 | ===== Mandelbrot 120 | 121 | Render Mandelbrot fractal in browser via PyPy.js 122 | 123 | * [[http://pypyjs.org/pypyjs-examples/mandelbrot.html|/mandelbrot.html]] 124 | * [[http://pypyjs.org/pypyjs-examples/mandelbrot-nojit.html|/mandelbrot-nojit.html]] (without PyPy JIT) 125 | 126 | WIP: 127 | 128 | * [[http://pypyjs.org/pypyjs-examples/mandelbrot_enhanced.html|/mandelbrot_enhanced.html]] 129 | * [[http://pypyjs.org/pypyjs-examples/mandelbrot_enhanced-nojit.html|/mandelbrot_enhanced-nojit.html]] 130 | 131 | 132 | ===== minimal console 133 | 134 | A minimal console with the aim to use as few external dependencies. 135 | A full featured console, that used **jq-console** is here: [[http://pypyjs.org|pypyjs.org]] 136 | 137 | * [[http://pypyjs.org/pypyjs-examples/minimal_console.html|/minimal_console.html]] 138 | 139 | 140 | ==== try out at home 141 | 142 | There is a [[http://pypyjs.org/pypyjs-examples/simple_http_server.py|/simple_http_server.py]] for test this repro at home. 143 | 144 | This is needed for 'mandelbrot' example and you can better see file requests. 145 | 146 | Just do it e.g.: 147 | {{{ 148 | ~$ git clone https://github.com/pypyjs/pypyjs-examples.git 149 | ~$ cd pypyjs-examples 150 | ~/pypyjs-examples$ git submodule init 151 | ~/pypyjs-examples$ git submodule update 152 | ~/pypyjs-examples$ python simple_http_server.py 153 | }}} 154 | The server worked with Python 2 and 3 and starts at [[http://127.0.0.1:8000]]. 155 | 156 | 157 | === What is PyPy.js ? 158 | 159 | In short: PyPy compiled to JavaScript 160 | 161 | Little bit longer: PyPy.js is an experiment in building a fast and compliant in-browser python interpreter, by compiling PyPy into javascript and retargeting its JIT to emit javascript code at runtime. 162 | 163 | More info: http://pypyjs.org/ 164 | 165 | 166 | === Links 167 | 168 | * [[http://salvatore.diodev.fr/pypybox|PyPyBox]] - create 2D graphics in pure Python 169 | 170 | 171 | === Repository Overview 172 | 173 | | [[https://github.com/pypyjs/pypyjs|pypyjs]] | Main repository to built a PyPyJS release 174 | | [[https://github.com/pypyjs/pypy|pypy]] | Fork of PyPy with support for compiling to javascript 175 | | [[https://github.com/pypyjs/pypyjs-release|pypyjs-release]] | Latest release build of PyPyJS, as a handy git submodule 176 | | [[https://github.com/pypyjs/pypyjs-release-nojit|pypyjs-release-nojit]] | Latest release build of PyPyJS, without a JIT 177 | | [[https://github.com/pypyjs/pypyjs-examples|pypyjs-examples]] | Examples/snippets usage of **pypyjs-release** and **pypyjs-release-nojit** 178 | | [[https://github.com/pypyjs/pypyjs.github.io|pypyjs.github.io]] | source for **pypyjs.org** website use **pypyjs-release** and **pypyjs-release-nojit** 179 | 180 | 181 | -------------------------------------------------------------------------------- /mandelbrot-nojit.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PyPy.js no-JIT - Mandelbrot 6 | 19 | 20 | 21 |

PyPy.js no-JIT - Mandelbrot

22 |

23 | Render Mandelbrot fractal with PyPy.js 24 |

25 | Fork me on GitHub 26 | 27 |

28 |     
29 |

30 | PyPy.js is an experiment in building a fast and compliant python environment for the web. 31 |
32 | It uses the PyPy python interpreter, compiled for the web via 33 | emscripten, with a custom JIT backend that emits asm.js 34 | code at runtime. 35 |

36 | 37 | 38 | 39 | 40 | 41 | 86 | 87 | -------------------------------------------------------------------------------- /mandelbrot.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PyPy.js - Mandelbrot 6 | 19 | 20 | 21 |

PyPy.js - Mandelbrot

22 |

23 | Render Mandelbrot fractal with PyPy.js 24 |

25 | Fork me on GitHub 26 | 27 |

28 |     
29 |

30 | PyPy.js is an experiment in building a fast and compliant python environment for the web. 31 |
32 | It uses the PyPy python interpreter, compiled for the web via 33 | emscripten, with a custom JIT backend that emits asm.js 34 | code at runtime. 35 |

36 | 37 | 38 | 39 | 40 | 41 | 86 | 87 | -------------------------------------------------------------------------------- /mandelbrot.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import, print_function 2 | 3 | import time 4 | 5 | import js 6 | 7 | class Canvas(object): 8 | def __init__(self, width, height): 9 | self.width = width 10 | self.height = height 11 | 12 | self.canvas = self.create_canvas() 13 | self.context = self.canvas.getContext('2d') 14 | 15 | self.pixel = self.context.createImageData(1, 1) 16 | self.pixel_data = self.pixel.data 17 | 18 | def create_canvas(self): 19 | jquery = js.globals["$"] 20 | 21 | canvas = jquery("#canvasMandelbrot")[0] # Maybe started in the past? 22 | if canvas == js.undefined: 23 | console = jquery("#output") 24 | console.before(""" 25 | 26 | """ % (self.width, self.height)) 27 | canvas = jquery("#canvasMandelbrot")[0] 28 | return canvas 29 | 30 | def draw_pixel(self, x, y, r, g, b, alpha=255): 31 | self.context.fillStyle = 'rgba(%i,%i,%i,%i)' % (r, g, b, alpha) 32 | self.context.fillRect(x, y, 1, 1) 33 | 34 | 35 | class Mandelbrot(object): 36 | def __init__(self, canvas, left, right, top, bottom, iterations): 37 | self.canvas = canvas 38 | self.left = left 39 | self.right = right 40 | self.top = top 41 | self.bottom = bottom 42 | self.iterations = iterations 43 | 44 | self.width = self.canvas.width 45 | self.height = self.canvas.height 46 | 47 | self.y = 0 48 | self.last_update = self.start_time = time.time() 49 | self.last_pos = 0 50 | 51 | self.done = False 52 | 53 | def render_mandelbrot_line(self): 54 | self.y += 1 55 | if self.y >= self.height: 56 | if not self.done: 57 | self.done = True 58 | duration = time.time() - self.start_time 59 | msg = "%ix%ipx Rendered in %iSec." % (self.width, self.height, duration) 60 | print(msg) 61 | print(" --- END --- ") 62 | return 63 | 64 | for x in range(self.width): 65 | z = complex(0, 0) 66 | c = complex( 67 | self.left + x * (self.right - self.left) / self.width, 68 | self.top + self.y * (self.bottom - self.top) / self.height 69 | ) 70 | norm = abs(z) ** 2 71 | for count in xrange(self.iterations): 72 | if norm <= 4.0: 73 | z = z * z + c 74 | norm = abs(z * z) 75 | else: 76 | break 77 | 78 | if count <= 4: 79 | (r, g, b) = (128, 128, 128) # grey 80 | elif count <= 8: 81 | (r, g, b) = (0, 255, 0) # green 82 | elif count <= 10: 83 | (r, g, b) = (0, 0, 255) # blue 84 | elif count <= 12: 85 | (r, g, b) = (255, 0, 0) # red 86 | elif count <= 15: 87 | (r, g, b) = (255, 255, 0) # yellow 88 | else: 89 | (r, g, b) = (0, 0, 0) # black 90 | 91 | (r, g, b) = (count * 6, 0, 0) 92 | 93 | canvas.draw_pixel(x, self.y, r, g, b) 94 | 95 | def display_stats(self): 96 | pos = (self.y * self.width) 97 | pos_diff = pos - self.last_pos 98 | 99 | duration = time.time() - self.last_update 100 | rate = pos_diff / duration 101 | 102 | print("%i Pixel/sec." % rate) 103 | self.last_pos = pos 104 | self.last_update = time.time() 105 | 106 | 107 | if __name__ == "__main__": 108 | width = 640 109 | height = 480 110 | 111 | canvas = Canvas(width, height) 112 | mandelbrot = Mandelbrot( 113 | canvas, 114 | left=-2, 115 | right=0.5, 116 | top=1.25, 117 | bottom=-1.25, 118 | iterations=40, 119 | ) 120 | 121 | @js.Function 122 | def render_line(): 123 | next_update = time.time() + 0.5 124 | while time.time() 2 | 3 | 4 | 5 | PyPy.js no-JIT - enhanced Mandelbrot 6 | 7 | 8 | 36 | 37 | 38 |
39 |
40 |
41 |

PyPy.js no-JIT - enhanced Mandelbrot

42 | Fork me on GitHub 43 | 44 |
45 |
46 |
47 |
48 |
49 | 50 |
51 | 52 | 53 | 54 |
55 |
56 | 57 |
58 |
59 |
60 |
61 |
62 |
63 | init... 64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | 73 | 74 | 75 | 76 |
77 |
78 | 79 |
80 |
81 |
82 |
83 |

 84 |         
85 |
86 |
87 |
88 |

89 | PyPy.js is an experiment in building a fast and compliant python environment for the web. 90 |
91 | It uses the PyPy python interpreter, compiled for the web via 92 | emscripten, with a custom JIT backend that emits asm.js 93 | code at runtime. 94 |

95 |
96 |
97 | 98 | 99 | 100 | 101 | 102 | 153 | 154 | 155 | -------------------------------------------------------------------------------- /mandelbrot_enhanced.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PyPy.js - enhanced Mandelbrot 6 | 7 | 8 | 36 | 37 | 38 |
39 |
40 |
41 |

PyPy.js - enhanced Mandelbrot

42 | Fork me on GitHub 43 | 44 |
45 |
46 |
47 |
48 |
49 | 50 |
51 | 52 | 53 | 54 |
55 |
56 | 57 |
58 |
59 |
60 |
61 |
62 |
63 | init... 64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | 73 | 74 | 75 | 76 |
77 |
78 | 79 |
80 |
81 |
82 |
83 |

 84 |         
85 |
86 |
87 |
88 |

89 | PyPy.js is an experiment in building a fast and compliant python environment for the web. 90 |
91 | It uses the PyPy python interpreter, compiled for the web via 92 | emscripten, with a custom JIT backend that emits asm.js 93 | code at runtime. 94 |

95 |
96 |
97 | 98 | 99 | 100 | 101 | 107 | 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /mandelbrot_enhanced.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import, print_function 2 | 3 | import time# after imports 4 | import random# add comments 5 | import sys# for: 6 | from colorsys import hsv_to_rgb# https://github.com/rfk/pypyjs/issues/109 7 | 8 | import js# from PyPy.js 9 | 10 | 11 | PY2 = sys.version_info[0] == 2 12 | if PY2: 13 | # Python 2 14 | range = xrange 15 | 16 | 17 | CANVAS_ID="#mandelbrot" 18 | PROGRESS_BAR_ID="#progress-bar" 19 | 20 | 21 | class jQuery(object): 22 | def __init__(self): 23 | self.jquery = js.globals["$"] 24 | 25 | def get_by_id(self, id): 26 | assert id.startswith("#") 27 | dom_object = self.jquery(id) 28 | if dom_object == js.undefined: 29 | print("ERROR: Object with ID %r not found!" % id) 30 | sys.exit() 31 | return dom_object 32 | 33 | def __call__(self, *args, **kwargs): 34 | return self.jquery(*args, **kwargs) 35 | 36 | jquery = jQuery() 37 | 38 | 39 | class ProgressBar(object): 40 | def __init__(self): 41 | self.progress_bar = jquery.get_by_id(PROGRESS_BAR_ID) 42 | 43 | def set_percent(self, percent, text): 44 | percent = int(percent) 45 | self.progress_bar.attr("aria-valuenow", percent) 46 | self.progress_bar.width("%i%%" % percent) 47 | self.progress_bar.text(text) 48 | 49 | 50 | 51 | class Canvas(object): 52 | def __init__(self, width, height): 53 | self.width = width 54 | self.height = height 55 | 56 | self.reset() 57 | # self.pixel = self.context.createImageData(1, 1) 58 | # self.pixel_data = self.pixel.data 59 | 60 | def draw_rect(self, x, y, r, g, b, alpha=255, width=1, height=1): 61 | self.context.fillStyle = 'rgba(%i,%i,%i,%i)' % (r, g, b, alpha) 62 | self.context.fillRect(x, y, width, height) 63 | 64 | def reset(self): 65 | self.canvas = jquery.get_by_id(CANVAS_ID)[0] 66 | self.canvas.width = self.width 67 | self.canvas.height = self.height 68 | self.context = self.canvas.getContext('2d') 69 | 70 | 71 | def interlace_generator(limit): 72 | def gen_pow(limit): 73 | interlace_steps = [] 74 | step=0 75 | while True: 76 | value = 2**step 77 | if value>=limit: 78 | return interlace_steps 79 | interlace_steps.append(value) 80 | step+=1 81 | interlace_steps = gen_pow(limit) 82 | interlace_steps.reverse() 83 | #~ print("interlace_steps:", interlace_steps) 84 | 85 | pos = 0 86 | step = 1 87 | iteration = 0 88 | size = interlace_steps[iteration] 89 | 90 | while True: 91 | yield (pos, size) 92 | pos += (size * step) 93 | 94 | if pos>limit: 95 | step = 2 96 | iteration += 1 97 | try: 98 | size = interlace_steps[iteration] 99 | except IndexError: 100 | return 101 | 102 | pos = size 103 | 104 | 105 | 106 | class Mandelbrot(object): 107 | LEFT = -2.0 108 | RIGHT = 2.0 109 | TOP = 2.0 110 | BOTTOM = -2.0 111 | 112 | def __init__(self, canvas): 113 | self.canvas = canvas 114 | self.progress_bar = ProgressBar() 115 | 116 | self.width = self.canvas.width 117 | self.height = self.canvas.height 118 | 119 | self.set_timeout_ids=[] 120 | self.running = True 121 | 122 | self.center() 123 | self.reset() 124 | self.calc_dimensions() 125 | 126 | def center(self): 127 | self.horizontal_offset = 0 128 | self.vertical_offset = 0 129 | self.zoom = 1.0 130 | self.running = True 131 | 132 | def reset(self): 133 | self.canvas.reset() 134 | self.progress_bar.set_percent(0, "start") 135 | self.change_scale = 0.5 136 | self.y = 0 137 | self.step = self.height // 4 138 | self.line_count = 0 139 | self.last_update = self.start_time = time.time() 140 | self.last_pos = 0 141 | 142 | def calc_dimensions(self): 143 | print("offset [h,v].......: %s, %s" % ( 144 | self.horizontal_offset, self.vertical_offset 145 | )) 146 | print("zoom...............: x%.1f (%s)" % (1/self.zoom, self.zoom)) 147 | 148 | self.left=(self.LEFT * self.zoom) + self.horizontal_offset 149 | self.right=(self.RIGHT * self.zoom) + self.horizontal_offset 150 | self.top=(self.TOP * self.zoom) + self.vertical_offset 151 | self.bottom=(self.BOTTOM * self.zoom) + self.vertical_offset 152 | print("coordinates........:", self.left, self.right, self.top, self.bottom) 153 | 154 | self.iterations = int(jquery.get_by_id("#iterations").val()) 155 | print("Iterations.........:", self.iterations) 156 | 157 | _interlace_generator = interlace_generator(self.height) 158 | try: 159 | self.interlace_generator_next = _interlace_generator.next # Python 2 160 | except AttributeError: 161 | self.interlace_generator_next = _interlace_generator.__next__ # Python 3 162 | self.done = False 163 | 164 | # self.color_func = self.color_func_random 165 | # self.color_func = self.color_func_monochrome_red 166 | # self.color_func = self.color_func_hsv2rgb # very colorfull 167 | # self.color_func = self.color_func_red_green_ramp # red <-> green color ramp 168 | # self.color_func = self.color_func_color_steps 169 | # self.color_func = self.color_func_psychedelic # Psychedelic colors 170 | self.color_func = self.color_func_color_map # use COLOR_MAP 171 | 172 | self.reset() 173 | 174 | def move_right(self): 175 | self.horizontal_offset += (self.change_scale * self.zoom) 176 | 177 | def move_left(self): 178 | self.horizontal_offset -= (self.change_scale * self.zoom) 179 | 180 | def move_top(self): 181 | self.vertical_offset += (self.change_scale * self.zoom) 182 | 183 | def move_bottom(self): 184 | self.vertical_offset -= (self.change_scale * self.zoom) 185 | 186 | def zoom_in(self): 187 | self.zoom -= self.zoom * self.change_scale 188 | self.horizontal_offset -= self.horizontal_offset * self.zoom 189 | self.vertical_offset -= self.vertical_offset * self.zoom 190 | 191 | def zoom_out(self): 192 | self.zoom += self.zoom * self.change_scale 193 | self.horizontal_offset -= self.horizontal_offset * self.zoom 194 | self.vertical_offset -= self.vertical_offset * self.zoom 195 | 196 | def stop(self): 197 | self.running = False 198 | print("stop rendering") 199 | for id in self.set_timeout_ids: 200 | js.globals.clearTimeout(id) 201 | if self.set_timeout_ids: 202 | print("cleared timeouts:", self.set_timeout_ids) 203 | self.set_timeout_ids=[] 204 | 205 | def color_func_monochrome_red(self, count, norm, iterations): 206 | return (int(256 / iterations * norm), 0, 0) 207 | 208 | RANDOM_MAP={} 209 | def color_func_random(self, count, norm, iterations): 210 | try: 211 | return self.RANDOM_MAP[count] 212 | except KeyError: 213 | self.RANDOM_MAP[count] = (r,g,b) = (random.randrange(255),random.randrange(255),random.randrange(255)) 214 | return (r,g,b) 215 | 216 | def color_func_hsv2rgb(self, count, norm, iterations): 217 | # very colorfull ;) 218 | (r,g,b) = hsv_to_rgb(h=norm/iterations,s=1,v=1) 219 | return int(r*255), int(g*255), int(b*255) 220 | 221 | def color_func_red_green_ramp(self, count, norm, iterations): 222 | # red <-> green color ramp 223 | return ( 224 | (255 * count) // iterations, # red 225 | (255 * (iterations - count)) // iterations, # green 226 | 0, # blue 227 | ) 228 | 229 | def color_func_color_steps(self, count, norm, iterations): 230 | if count <= 5: 231 | return (0, (255 // 5) * count, 0) # monochrome green 232 | elif count <= 8: 233 | return (0, 255, 0) # green 234 | elif count <= 10: 235 | return (0, 0, 255) # blue 236 | elif count <= 12: 237 | return (255, 0, 0) # red 238 | elif count <= 15: 239 | return (255, 255, 0) # yellow 240 | else: 241 | return (0, 0, 0) # black 242 | 243 | def color_func_psychedelic(self, count, norm, iterations): 244 | # Psychedelic colors: 245 | color = int((65536.0 / iterations) * count) 246 | return ( 247 | (color >> 16) & 0xFF, # red 248 | (color >> 8) & 0xFF, # green 249 | color & 0xFF, # blue 250 | ) 251 | 252 | COLOR_MAP = { 253 | 0: (66, 30, 15), 254 | 1: (25, 7, 26), 255 | 2: (9, 1, 47), 256 | 3: (4, 4, 73), 257 | 4: (0, 7, 100), 258 | 5: (12, 44, 138), 259 | 6: (24, 82, 177), 260 | 7: (57, 125, 209), 261 | 8: (134, 181, 229), 262 | 9: (211, 236, 248), 263 | 10: (241, 233, 191), 264 | 11: (248, 201, 95), 265 | 12: (255, 170, 0), 266 | 13: (204, 128, 0), 267 | 14: (153, 87, 0), 268 | 15: (106, 52, 3), 269 | 16: (90, 40, 0), 270 | 17: (40, 20, 0), 271 | 18: (20, 10, 0), 272 | 19: (10, 5, 0), 273 | 20: (5, 0, 0), 274 | } 275 | def color_func_color_map(self, count, norm, iterations): 276 | try: 277 | return self.COLOR_MAP[count] 278 | except KeyError: 279 | if count <= 33: 280 | return (count * 7, count * 5, 0) 281 | else: 282 | return (0, 0, 0) # black 283 | 284 | def _render_line(self, canvas, color_func, y, left, right, top, bottom, width, height, iterations, rect_height): 285 | count=0 286 | for x in range(width): 287 | z = complex(0.0, 0.0) 288 | c = complex( 289 | left + x * (right - left) / width, 290 | top + y * (bottom - top) / height 291 | ) 292 | norm = abs(z) ** 2.0 293 | for count in range(iterations): 294 | if norm <= 4.0: 295 | z = z * z + c 296 | norm = abs(z * z) 297 | else: 298 | break 299 | 300 | (r,g,b)=color_func(count, norm, iterations) 301 | canvas.draw_rect(x, y, r, g, b, height=rect_height) 302 | 303 | def render_mandelbrot(self): 304 | if not self.running or self.done: 305 | return self.stop() 306 | 307 | next_return = time.time() + 0.5 308 | while time.time() < next_return: 309 | try: 310 | y, size = self.interlace_generator_next() 311 | except StopIteration: 312 | self.done = True 313 | duration = time.time() - self.start_time 314 | self.display_stats() # Should display 100% ;) 315 | msg = "%ix%ipx Rendered in %iSec." % (self.width, self.height, duration) 316 | print(msg) 317 | print(" --- END --- ") 318 | return self.stop() 319 | 320 | self._render_line( 321 | self.canvas, 322 | self.color_func, 323 | y, 324 | self.left, self.right, self.top, self.bottom, 325 | self.width, self.height, self.iterations, 326 | rect_height=size 327 | ) 328 | self.line_count += 1 329 | 330 | self.display_stats() 331 | 332 | def display_stats(self): 333 | pos = (self.line_count * self.width) 334 | pos_diff = pos - self.last_pos 335 | self.last_pos = pos 336 | 337 | duration = time.time() - self.last_update 338 | self.last_update = time.time() 339 | 340 | rate = pos_diff / duration 341 | percent = 100.0 * self.line_count / self.height 342 | self.progress_bar.set_percent(percent, "%.1f%% (%i Pixel/sec.)" % (percent, rate)) 343 | 344 | 345 | 346 | 347 | 348 | if __name__ == "__main__": 349 | print("startup rendering...") 350 | width = height = 400 351 | 352 | canvas = Canvas(width, height) 353 | mandelbrot = Mandelbrot(canvas) 354 | 355 | @js.Function 356 | def pause_mandelbrot(event): 357 | if mandelbrot.running: 358 | print("pause") 359 | mandelbrot.stop() 360 | jquery.get_by_id("#pause").text("resume") 361 | else: 362 | print("resume") 363 | jquery.get_by_id("#pause").text("pause") 364 | mandelbrot.running = True 365 | render_mandelbrot() 366 | 367 | pause_button = jquery.get_by_id("#pause") 368 | pause_button.click(pause_mandelbrot) 369 | 370 | 371 | @js.Function 372 | def update_mandelbrot(event): 373 | print("update...") 374 | mandelbrot.calc_dimensions() 375 | mandelbrot.running = True 376 | render_mandelbrot() 377 | 378 | update_button = jquery.get_by_id("#update") 379 | update_button.click(update_mandelbrot) 380 | 381 | 382 | @js.Function 383 | def data_form_change(event): 384 | print("form, changed:") 385 | update_mandelbrot(event) 386 | 387 | data_form = jquery.get_by_id("#data_form") 388 | data_form.change(data_form_change) 389 | 390 | @js.Function 391 | def change_mandelbrot(event): 392 | mandelbrot.stop() 393 | obj_id = event.target.id 394 | obj_id = str(obj_id) # convert js.String to real string 395 | # print("clicked on ID %r" % obj_id) 396 | try: 397 | func = getattr(mandelbrot, obj_id) 398 | except Exception as err: 399 | print("Error: %s" % err) 400 | else: 401 | print("call 'mandelbrot.%s()'" % func.__name__) 402 | func() 403 | update_mandelbrot(event) 404 | 405 | jquery(".change_mandelbrot").click(change_mandelbrot) 406 | 407 | 408 | @js.Function 409 | def render_mandelbrot(): 410 | mandelbrot.render_mandelbrot() 411 | 412 | if not mandelbrot.done: # not complete, yet 413 | # see: https://github.com/rfk/pypyjs/issues/117 414 | mandelbrot.set_timeout_ids.append( 415 | int(js.globals.setTimeout(render_mandelbrot, 0)) 416 | ) 417 | else: 418 | print("done.") 419 | 420 | render_mandelbrot() 421 | -------------------------------------------------------------------------------- /minimal_console.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PyPy.js - minimal console 6 | 20 | 21 | 22 |

PyPy.js minimal console

23 |

24 | This is only a minimal console!
25 | Only single string input works and only a few special keys are supported.
26 | A full featured console, that used jq-console is here: pypyjs.org 27 |

28 | Fork me on GitHub 29 | 30 |

31 |     

32 | PyPy.js is an experiment in building a fast and compliant python environment for the web. 33 |
34 | It uses the PyPy python interpreter, compiled for the web via 35 | emscripten, with a custom JIT backend that emits asm.js 36 | code at runtime. 37 |

38 | 39 | 40 | 41 | 42 | 43 | 94 | 95 | -------------------------------------------------------------------------------- /simple_http_server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | try: 4 | # python 3 5 | from http.server import SimpleHTTPRequestHandler 6 | import socketserver 7 | except ImportError: 8 | # python 2 9 | from SimpleHTTPServer import SimpleHTTPRequestHandler 10 | import SocketServer as socketserver 11 | 12 | 13 | ADDR = "127.0.0.1" 14 | PORT = 8000 15 | 16 | 17 | class RequestHandler(SimpleHTTPRequestHandler): 18 | """ 19 | Add path info in error message 20 | e.g.: 404 - File not found 21 | """ 22 | def send_error(self, code, message=None): 23 | path_info = repr(self.path) 24 | if message is None: 25 | message = path_info 26 | else: 27 | message += " - %s" % path_info 28 | super(RequestHandler, self).send_error(code, message) 29 | 30 | 31 | httpd = socketserver.TCPServer( 32 | (ADDR, PORT), RequestHandler 33 | ) 34 | 35 | print("serving at http://%s:%s" % (ADDR, PORT)) 36 | httpd.serve_forever() 37 | 38 | --------------------------------------------------------------------------------