#!/usr/bin/env python # coding: utf-8 # The brythonmagic extension has been tested on: # In[10]: import IPython IPython.version_info # # brythonmagic installation # Just type the following: # In[11]: get_ipython().run_line_magic('install_ext', 'https://raw.github.com/kikocorreoso/brythonmagic/master/brythonmagic.py') # In[12]: get_ipython().run_line_magic('load_ext', 'brythonmagic') # And load the brython js lib in the notebook: # In[13]: from brythonmagic import load_brython_dev load_brython_dev() # # Warning # In order to load javascript libraries in a safety way you should try to use https instead of http when possible (read more [here](http://mail.scipy.org/pipermail/ipython-dev/2014-July/014572.html)). If you don't trust the source and/or the source cannot be loaded using https then you could download the javascript library and load it from a local location. # # Usage: # The brythonmagic provides you a cell magic, `%%brython`, to run brython code and show the results in a html `div` tag below the code cell. # # You can use several options: # # * -p, --print: will show you the generated html code below the results obtained from the brython code. # # # * -c, --container: you can define de name of the `div` container in case you want to 'play' with it in other cell. If you don't define an output the `div` will have and `id` with the following format 'brython-container-[random number between 0 and 999999]' # # # * -i, --input: you can pass variables defined in the Python namespace separated by commas. If you pass a python list it will be converted to a brython list, a python tuple will be converted to a brython tuple, a python dict will be converted to a brython dict, a python string will be converted to a brython string. # # # * -h, --html: you can pass a string with html markup code. This html code will be inserted inside the div container. In this way you can avoid the generation of HTML markup code via a Brython script so you can separate the layout from the 'action'. # # # * -s, --script: Use this option to provide and id to the script defined in the Brython code cell. Also, this value could be used to run the code of this cell in other brython cells. # # # * -S, --scripts: Use this option to run code previously defined in other Brython code cells. The values should be the provided values in the -s/--script option in other Brython code cells. # # * -f, --fiddle: With this option, the code in the cell will be automatically uploaded to [gist.github.com/](https://gist.github.com/) as an anonymous gist with several files in it. This files will be used to create an anonymous 'fiddle' on [jsfiddle.net](http://jsfiddle.net). Finally, some links will be printed in the output linking to the gist and the fiddle. See an example here ([https://gist.github.com/anonymous/b664e8b4617afc09db6c](https://gist.github.com/anonymous/b664e8b4617afc09db6c) and [http://jsfiddle.net/gh/gist/library/pure/b664e8b4617afc09db6c/](http://jsfiddle.net/gh/gist/library/pure/b664e8b4617afc09db6c/)) # # * -e, --embedfiddle: With this option, the code in the cell will be automatically uploaded to [gist.github.com/](https://gist.github.com/) as an anonymous gist with several files in it. This files will be used to create an anonymous 'fiddle' on [jsfiddle.net](http://jsfiddle.net). Finally, some links will be printed in the output linking to the gist and the fiddle and an iframe will be created showing the fiddle on [jsfiddle.net](http://jsfiddle.net). # # [WARNING] This options may change as the brythonmagic is in active development. # ### `-p, --print` option # The following example shows the use of the `-p`, `--print` option. # # [HINT] The result of the print is shown in the javascript console of your browser. # In[4]: get_ipython().run_cell_magic('brython', '-p', "print('hello world!')\n") # ### `-c, --container` option # In the following example can be seen the use of the `-c`, `--container`. The `-p` is also used to show you the result. See the `id` attribute of the `div` tag created: # In[18]: get_ipython().run_cell_magic('brython', '-c my_container -p', 'from browser import document, html\n\n# This will be printed in the js console of your browser\nprint(\'Hello world!\')\n\n# This will be printed in the container div on the output below\ndocument["my_container"] <= html.P("This text is inside the div", \n style = {"backgroundColor": "cyan"})\n') # ### `-i, --input` option # In this example you can see how the data are passed to brython from python using the `-i` or `--input` option. First, we create some data in a regular Python cell. # In[6]: data_list = [1,2,3,4] data_tuple = (1,2,3,4) data_dict = {'one': 1, 'two': 2} data_str = """ Hello GoodBye """ # A numpy array can be converted to a list and you will obtain a brython list import numpy as np data_arr = np.empty((3,2)) data_arr = data_arr.tolist() # And now, the created data are passed to Brython and used in the Brython code cell. **Remember that only Python lists, tuples, dicts and strings are allowed as inputs**. # In[19]: get_ipython().run_cell_magic('brython', '-c p2b_data_example -i data_list data_tuple data_dict data_str data_arr', 'from browser import document, html\n\ndocument["p2b_data_example"] <= html.P(str(data_list))\ndocument["p2b_data_example"] <= html.P(str(type(data_list)))\n\ndocument["p2b_data_example"] <= html.P(str(data_tuple))\ndocument["p2b_data_example"] <= html.P(str(type(data_tuple)))\n\ndocument["p2b_data_example"] <= html.P(str(data_dict))\ndocument["p2b_data_example"] <= html.P(str(type(data_dict)))\n\ndocument["p2b_data_example"] <= html.P(data_str.replace(\'Hello\', \'Hi\'))\ndocument["p2b_data_example"] <= html.P(str(type(data_str)))\n\ndocument["p2b_data_example"] <= html.P(str(data_arr))\ndocument["p2b_data_example"] <= html.P(str(type(data_arr)))\n') # ### `-h, --html` option # In this example you can see how to create some HTML code in a cell and then use that HTML code in the brython cell. In this way you do not need to create the HTML code via scripting with Brython. # In[8]: html = """
Hi
""" # In[20]: get_ipython().run_cell_magic('brython', '-c html_ex -h html', 'from browser import document\n\ndocument["paragraph"].style = {\n "color": "yellow",\n "fontSize": "100px",\n "lineHeight": "150px",\n "textAlign": "center",\n "backgroundColor": "black"\n}\n') # ### `-s, --script` option # With this option you are creating a reference of the code in the Brython cell (e.g., an `id` of the HTML `script` tag created to run the Brython code). So, if you need to use the code of the Brython cell in a future Brython cell you could reference it by its `id`. Let's see this on an example (the `-p` option is used to show you the generated code and how the `id` of the `script` tag is created): # In[10]: get_ipython().run_cell_magic('brython', '-s my_dummy_function', 'def dummy_function(some_text):\n print(some_text)\n') # ### `-S, --scripts` option # This option could be used to call code created in a previous Brython code cell using its `id` (see the `-s` option above). In the following code cell we will use the `dummy_function` created in another Brython code cell. The `dummy_function` was created in a `script` tag with an `id="my_dummy_function"`. # # [HINT] The result of the Brython code cell below is shown in the javascript console of your browser. # In[11]: get_ipython().run_cell_magic('brython', '-S my_dummy_function', "dummy_function('Hi')\n") # ### `-f, --fiddle` option # With this option, the code in the cell will be automatically uploaded to [gist.github.com/](https://gist.github.com/) as an anonymous gist with several files in it. This files will be used to create an anonymous 'fiddle' on [jsfiddle.net](http://jsfiddle.net). Finally, some links will be printed in the output linking to the gist and the fiddle. # In[12]: get_ipython().run_cell_magic('brython', '-f', "from browser import alert\nalert('hello world from jsfiddle!')\n") # ### `-e, --embedfiddle` option # With this option, the code in the cell will be automatically uploaded to [gist.github.com/](https://gist.github.com/) as an anonymous gist with several files in it. These files will be used to create an anonymous 'fiddle' on [jsfiddle.net](http://jsfiddle.net). Finally, some links will be printed in the output linking to the gist and the fiddle and an iframe will be created showing the fiddle on [jsfiddle.net](http://jsfiddle.net). # In[13]: get_ipython().run_cell_magic('brython', '-e', "from browser import alert\nalert('hello world from jsfiddle!')\n") # # How to use Brython in the IPython notebook # First step should be to read the brython documentation. You can find the docs here: # # http://brython.info/doc/en/index.html?lang=en # # In the following section I will show you some dummy examples. # ## Hello world example # In this example let's see how to pop up an alert window. This could be an standard 'Hello world!' example in the Brython world. # In[14]: get_ipython().run_cell_magic('brython', '', "from browser import alert\n\nalert('Hello world!, Welcome to the brythonmagic!')\n") # ## Simple example, writing some numbers in the `div` container # In this example we just write inside a `
` ten numbers using a `

` tag for each number. # # [HINT] To see the line numbers in the code cell just go to the cell and press **`-m`** and then **`l`**. # # * Line 2: We import the libraries to use # * Line 4: A for loop :-P # * Line 10: We create a `P` tag and write the value of `i` inside. Finally, add the `P` element to the selected `div`, in this case the `div` with "simple_example" `id` attribute. # In[21]: get_ipython().run_cell_magic('brython', '-c simple_example', 'from browser import document, html\n\nfor i in range(10):\n document["simple_example"] <= html.P(i)\n') # ## A more useful example: A multiplication table # In the following cell we create a multiplication table. First, we create a `table` tag. We append the table rows and cells (`TR` and `TD` tags) and, finally, we append the final table to the `div` with "table" `id` attribute. # In[22]: get_ipython().run_cell_magic('brython', '-c table', "from browser import document, html\n\ntable = html.TABLE()\n\nfor i in range(10):\n color = ['cyan','#dddddd'] * 5\n table <= html.TR(\n html.TD(str(i+1) + ' x 2 =', style = {'backgroundColor':color[i]}) + \n html.TD((i+1)*2, style = {'backgroundColor':color[i]}))\ndocument['table'] <= table\n") # ## Let's add some animation using HTML5 canvas technology... # In the following example we draw a shape using the HTML5 `canvas`. Also, we add some controls to stop and animate the shape. The example has been adapted from the javascript example available [here](http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/). # In[23]: get_ipython().run_cell_magic('brython', '-c canvas_example', 'from browser.timer import request_animation_frame as raf\nfrom browser.timer import cancel_animation_frame as caf\nfrom browser import document, html\nfrom time import time\nimport math\n\n# First we create a table to insert the elements\ntable = html.TABLE(cellpadding = 10)\nbtn_anim = html.BUTTON(\'Animate\', Id="btn-anim", type="button")\nbtn_stop = html.BUTTON(\'Stop\', Id="btn-stop", type="button")\ncnvs = html.CANVAS(Id="raf-canvas", width=256, height=256)\n\ntable <= html.TR(html.TD(btn_anim + btn_stop) +\n html.TD(cnvs))\n\ndocument[\'canvas_example\'] <= table\n# Now we access the canvas context\nctx = document[\'raf-canvas\'].getContext( \'2d\' ) \n\n# And we create several functions in charge to animate and stop the draw animation\ntoggle = True\n\ndef draw():\n t = time() * 3\n x = math.sin(t) * 96 + 128\n y = math.cos(t * 0.9) * 96 + 128\n global toggle\n if toggle:\n toggle = False\n else:\n toggle = True\n ctx.fillStyle = \'rgb(200,200,20)\' if toggle else \'rgb(20,20,200)\'\n ctx.beginPath()\n ctx.arc( x, y, 6, 0, math.pi * 2, True)\n ctx.closePath()\n ctx.fill()\n\ndef animate(i):\n global id\n id = raf(animate)\n draw()\n\ndef stop(i):\n global id\n print(id)\n caf(id)\n\ndocument["btn-anim"].bind("click", animate)\ndocument["btn-stop"].bind("click", stop)\n') # ## Interaction with other javascript libraries: D3.js # In Brython there is a javascript library that allows to access objects available in the javascript namespace. In this example we are using a javascript object (D3.js library) from Brython. # # So, in order to allow Brython to access to D3 first you should load the D3 library. # In[24]: from brythonmagic import load_js_lib load_js_lib("http://d3js.org/d3.v3.js") # Now, we can access D3 objects(see example below). In the result you can see how the circle change its color when the mouse is over the circle. # In[26]: get_ipython().run_cell_magic('brython', '-c simple_d3', 'from browser import window, document, html\n\nd3 = window.d3\n\ncontainer = d3.select("#simple_d3")\nsvg = container.append("svg").attr("width", 100).attr("height", 100)\ncircle1 = svg.append("circle").style("stroke", "gray").style("fill", "gray").attr("r", 40)\ncircle1.attr("cx", 50).attr("cy", 50).attr("id", "mycircle")\n\ncircle2 = svg.append("circle").style("stroke", "gray").style("fill", "white").attr("r", 20)\ncircle2.attr("cx", 50).attr("cy", 50)\n\ndef over(ev):\n document["mycircle"].style.fill = "blue"\n\ndef out(ev):\n document["mycircle"].style.fill = "gray"\n\ndocument["mycircle"].bind("mouseover", over)\ndocument["mycircle"].bind("mouseout", out)\n') # ## Manipulating the IPython notebook # An example to hide or show the code cells using a button. # In[27]: get_ipython().run_cell_magic('brython', '-c manipulating', 'from browser import document, html\n\ndef hide(ev):\n divs = document.get(selector = \'div.input\')\n for div in divs:\n div.style.display = "none"\n\ndef show(ev):\n divs = document.get(selector = \'div.input\')\n for div in divs:\n div.style.display = "inherit"\n\ndocument["manipulating"] <= html.BUTTON(\'Hide code cells\', Id="btn-hide")\ndocument["btn-hide"].bind("click", hide)\n\ndocument["manipulating"] <= html.BUTTON(\'Show code cells\', Id="btn-show")\ndocument["btn-show"].bind("click", show)\n') # ## A more complete d3 example calculating things in Python and drawing results in Brython using D3.js # A more complete D3 example. In this case, first we create some data in Python. # In[28]: from random import randint n = 100 x = [randint(0,800) for i in range(n)] y = [randint(0,600) for i in range(n)] r = [randint(25,50) for i in range(n)] red = [randint(0,255) for i in range(n)] green = [randint(0,255) for i in range(n)] blue = [randint(0,255) for i in range(n)] # And now, the data is passed to Brython to be used in a D3 plot. In this case, the D3.js library is already loaded so it is not necessary to load it. # In[29]: get_ipython().run_cell_magic('brython', '-c other_d3 -i x y r red green blue', 'from browser import window, document, html\n\nd3 = window.d3\n\nWIDTH = 800\nHEIGHT = 600\n\ncontainer = d3.select("#other_d3")\nsvg = container.append("svg").attr("width", WIDTH).attr("height", HEIGHT)\n\nclass AddShapes:\n def __init__(self, x, y, r, red, green, blue, shape = "circle", interactive = True):\n self.shape = shape\n self.interactive = interactive\n self._color = "gray"\n self.add(x, y, r, red, green, blue)\n\n def over(self, ev):\n self._color = ev.target.style.fill\n document[ev.target.id].style.fill = "white"\n \n def out(self, ev):\n document[ev.target.id].style.fill = self._color\n \n def add(self, x, y, r, red, green, blue):\n for i in range(len(x)):\n self.idx = self.shape + \'_\' + str(i) \n self._color = "rgb(%s,%s,%s)" % (red[i], green[i], blue[i])\n shaped = svg.append(self.shape).style("stroke", "gray").style("fill", self._color).attr("r", r[i])\n shaped.attr("cx", x[i]).attr("cy", y[i]).attr("id", self.idx)\n if self.interactive:\n document[self.idx].bind("mouseover", self.over)\n document[self.idx].bind("mouseout", self.out)\n\nplot = AddShapes(x, y, r, red, green, blue, interactive = True)\n') # ## Mapping with Python in the IPython notebook using OpenLayers? # In the following example we will use OpenLayers to center a map in a specific location, with a zoom and a projection and then we will draw some vector points around the location. # # As before, first we should load the OpenLayers.js library. # In[4]: from brythonmagic import load_js_lib load_js_lib("http://cdnjs.cloudflare.com/ajax/libs/openlayers/2.11/OpenLayers.js") # And now we can create a map. # In[ ]: get_ipython().run_cell_magic('brython', '-c ol_map', 'from browser import document, window\n\n## Div layout\ndocument[\'ol_map\'].style.width = "800px"\ndocument[\'ol_map\'].style.height = "400px"\ndocument[\'ol_map\'].style.border = "1px solid black"\n\nOpenLayers = window.OpenLayers\n\n## Map\n_map = OpenLayers.Map.new(\'ol_map\')\n\n## Addition of an OpenStreetMap layer\n_layer = OpenLayers.Layer.OSM.new(\'Simple OSM map\')\n_map.addLayer(_layer)\n\n## Map centered on Lon, Lat = (-3.671416, 40.435897) and a zoom = 14\n## with a projection = "EPSG:4326" (Lat-Lon WGS84)\n_proj = OpenLayers.Projection.new("EPSG:4326")\n_center = OpenLayers.LonLat.new(-3.671416, 40.435897)\n_center.transform(_proj, _map.getProjectionObject())\n_map.setCenter(_center, 10)\n\n## Addition of some points around the defined location\nlons = [-3.670, -3.671, -3.672, -3.672, -3.672,\n -3.671, -3.670, -3.670]\nlats = [40.435, 40.435, 40.435, 40.436, 40.437,\n 40.437, 40.437, 40.436]\n\npoints_layer = OpenLayers.Layer.Vector.new("Point Layer")\n\nfor lon, lat in zip(lons, lats):\n point = OpenLayers.Geometry.Point.new(lon, lat)\n point.transform(_proj, _map.getProjectionObject())\n _feat = OpenLayers.Feature.Vector.new(point)\n points_layer.addFeatures([_feat])\n\n_map.addLayer(points_layer)\n\n# Add a control for the layers\nlayer_switcher= OpenLayers.Control.LayerSwitcher.new({})\n_map.addControl(layer_switcher)\n') # ## Using Raphaël.js # A dummy example using raphaël.js library. # # As usual, first we should include the library: # In[6]: load_js_lib("http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.2/raphael-min.js") # And now let's make a dumb example using `JSObject`. # In[7]: get_ipython().run_cell_magic('brython', '-c raphael_ex', 'from browser import window\nfrom javascript import JSObject\n\nRaphael = window.Raphael\n\npaper = JSObject(Raphael("raphael_ex", 400, 400))\n\n#Draw rectagle\nrect = paper.rect(1,1,398,398)\nrect.attr("stroke", "black")\n\n#Draw orbits\nfor rot in range(90,280,60):\n ellipse = paper.ellipse(200, 200, 180, 50)\n ellipse.attr("stroke", "gray")\n ellipse.rotate(rot)\n\n#Draw nucleus\nnucleus = paper.circle(200,200,40)\nnucleus.attr("fill", "black")\n\n# Draw electrons\nelectron = paper.circle(200, 20, 10)\nelectron.attr("fill", "red")\nelectron = paper.circle(44, 290, 10)\nelectron.attr("fill", "yellow")\nelectron = paper.circle(356, 290, 10)\nelectron.attr("fill", "blue")\n') # ## Include the cell number for each cell # The cells starts by 0 and all the cells (markdown, headings, code,...) has a number. If we want to re-run some cells in a programmatically way it is useful to know the number of the cells to identify them. You can delete the cell numbers using `show_cell_number(on = False)`: # In[8]: get_ipython().run_cell_magic('brython', '', 'from browser import doc, html\n\ndef show_cell_number(on = True):\n cells = doc.get(selector = \'.input_prompt\')\n for i, cell in enumerate(cells):\n if on:\n if \'In\' in cell.html and \'
\' not in cell.html:\n cell.html += "
cell #" + str(i)\n else:\n if \'In\' in cell.text:\n cell.html = cell.html.split(\'
\')[0]\n\nshow_cell_number(on = True)\n') # ## Running Python cells as a loop # Imagine you have several cells of code and you want just to modify some data and run again these cells as a loop not having to create a big cell with the code of the cells together. # In[9]: get_ipython().run_cell_magic('brython', '', 'from javascript import JSObject\nfrom browser import window\n\nIPython = window.IPython\nnb = IPython.notebook\n# This is used to prevent an infinite loop\nthis_cell = nb.get_selected_index()\n\nfor i in range(1,10): # Ths will run cells 1 to 9 (the beginning of the nb)\n cell = nb.get_cell(i)\n if cell.cell_type == "code" and i != this_cell:\n cell.execute()\n') # ## Get the code of all the cells and create a new cell with the code # If you want to compile all the code used in a notebook you can use this recipe (use `crtl + Enter` to run the cell if you don't want a bad behaviour): # In[14]: get_ipython().run_cell_magic('brython', '', 'from javascript import JSObject\nfrom browser import window\n\nIPython = window.IPython\nnb = IPython.notebook\nthis_cell = nb.get_selected_index()\ntotal_cells = nb.ncells()\n\ncode = ""\nfirst_cell = True\nfor i in range(total_cells):\n cell = nb.get_cell(i)\n if cell.cell_type == "code" and i != this_cell:\n if first_cell:\n code += "# This cell has been generated automatically using a brython script\\n\\n"\n code += "# code from cell " + str(i) + \'\\n\'\n first_cell = False\n else:\n code += "\\n\\n\\n# code from cell " + str(i) + \'\\n\'\n code += cell.get_text() + \'\\n\'\n\nnb.insert_cell_below(\'code\')\nnew_cell = nb.get_cell(this_cell + 1)\nnew_cell.set_text(code)\n') # ## Styling the nb # Lets modify a little bit the look of the notebook. Warning: The result will be very ugly... # In[16]: get_ipython().run_cell_magic('brython', '-s styling', 'from browser import doc, html\n\n# Changing the background color\nbody = doc[html.BODY][0]\nbody.style = {"backgroundColor": "#99EEFF"}\n \n# Changing the color of the imput prompt\ninps = body.get(selector = ".input_prompt")\nfor inp in inps:\n inp.style = {"color": "blue"}\n \n# Changin the color of the output cells\nouts = body.get(selector = ".output_wrapper")\nfor out in outs:\n out.style = {"backgroundColor": "#E0E0E0"}\n \n# Changing the font of the text cells\ntext_cells = body.get(selector = ".text_cell")\nfor cell in text_cells:\n cell.style = {"fontFamily": """"Courier New", Courier, monospace""",\n "fontSize": "20px"}\n \n# Changing the color of the code cells.\ncode_cells = body.get(selector = ".CodeMirror")\nfor cell in code_cells:\n cell.style = {"backgroundColor": "#D0D0D0"}\n') # In[ ]: