Skip to content

Instantly share code, notes, and snippets.

@enakai00
Created August 30, 2016 02:32
Show Gist options
  • Save enakai00/883684e8f0dccada5f84940b2ca8a2ea to your computer and use it in GitHub Desktop.
Save enakai00/883684e8f0dccada5f84940b2ca8a2ea to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"%matplotlib nbagg"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"import matplotlib.animation as animation\n",
"from scipy.integrate import quad"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def fourier(f, n_max):\n",
" a, b = [], []\n",
" for n in range(0,n_max+1):\n",
" res, err = quad(lambda x:f(x)*np.cos(n*x), -np.pi, np.pi)\n",
" a.append(res/np.pi)\n",
" res, err = quad(lambda x:f(x)*np.sin(n*x), -np.pi, np.pi)\n",
" b.append(res/np.pi)\n",
"\n",
" def fn(x):\n",
" sum = a[0] / 2\n",
" for n in xrange(1, 1+n_max):\n",
" sum += a[n]*np.cos(n*x) + b[n]*np.sin(n*x)\n",
" return sum\n",
"\n",
" def lastfn(x):\n",
" return a[n_max]*np.cos(n_max*x)+b[n_max]*np.sin(n_max*x)\n",
" \n",
" return fn, lastfn"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"def make_fourier(f, n_max, filename):\n",
" fig = plt.figure(figsize=(6,4))\n",
" subplot = fig.add_subplot(1,1,1)\n",
"\n",
" y1s, y2s, y3s = [], [], []\n",
" y1, y2, y3 = 0, 0, 0\n",
"\n",
" images = []\n",
" xs = np.linspace(-np.pi,np.pi,500)\n",
" y1s = f(xs)\n",
" for n in range(1, n_max+1):\n",
" image1, = subplot.plot(xs, y1s, color='blue')\n",
" g, h = fourier(f, n)\n",
" y2s = g(xs)\n",
" y3s = h(xs)\n",
" image2, = subplot.plot(xs, y2s, color='green')\n",
" image3, = subplot.plot(xs, y3s, color='red')\n",
" image4 = subplot.text(-np.pi, 1.0, ('n = %d' % n))\n",
" images.append([image1, image2, image3, image4])\n",
"\n",
" ani = animation.ArtistAnimation(fig, images, interval=1000)\n",
" ani.save(filename, writer='imagemagick', fps=1)\n",
" return ani"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false,
"scrolled": true
},
"outputs": [
{
"data": {
"application/javascript": [
"/* Put everything inside the global mpl namespace */\n",
"window.mpl = {};\n",
"\n",
"mpl.get_websocket_type = function() {\n",
" if (typeof(WebSocket) !== 'undefined') {\n",
" return WebSocket;\n",
" } else if (typeof(MozWebSocket) !== 'undefined') {\n",
" return MozWebSocket;\n",
" } else {\n",
" alert('Your browser does not have WebSocket support.' +\n",
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
" 'Firefox 4 and 5 are also supported but you ' +\n",
" 'have to enable WebSockets in about:config.');\n",
" };\n",
"}\n",
"\n",
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
" this.id = figure_id;\n",
"\n",
" this.ws = websocket;\n",
"\n",
" this.supports_binary = (this.ws.binaryType != undefined);\n",
"\n",
" if (!this.supports_binary) {\n",
" var warnings = document.getElementById(\"mpl-warnings\");\n",
" if (warnings) {\n",
" warnings.style.display = 'block';\n",
" warnings.textContent = (\n",
" \"This browser does not support binary websocket messages. \" +\n",
" \"Performance may be slow.\");\n",
" }\n",
" }\n",
"\n",
" this.imageObj = new Image();\n",
"\n",
" this.context = undefined;\n",
" this.message = undefined;\n",
" this.canvas = undefined;\n",
" this.rubberband_canvas = undefined;\n",
" this.rubberband_context = undefined;\n",
" this.format_dropdown = undefined;\n",
"\n",
" this.image_mode = 'full';\n",
"\n",
" this.root = $('<div/>');\n",
" this._root_extra_style(this.root)\n",
" this.root.attr('style', 'display: inline-block');\n",
"\n",
" $(parent_element).append(this.root);\n",
"\n",
" this._init_header(this);\n",
" this._init_canvas(this);\n",
" this._init_toolbar(this);\n",
"\n",
" var fig = this;\n",
"\n",
" this.waiting = false;\n",
"\n",
" this.ws.onopen = function () {\n",
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
" fig.send_message(\"send_image_mode\", {});\n",
" fig.send_message(\"refresh\", {});\n",
" }\n",
"\n",
" this.imageObj.onload = function() {\n",
" if (fig.image_mode == 'full') {\n",
" // Full images could contain transparency (where diff images\n",
" // almost always do), so we need to clear the canvas so that\n",
" // there is no ghosting.\n",
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
" }\n",
" fig.context.drawImage(fig.imageObj, 0, 0);\n",
" };\n",
"\n",
" this.imageObj.onunload = function() {\n",
" this.ws.close();\n",
" }\n",
"\n",
" this.ws.onmessage = this._make_on_message_function(this);\n",
"\n",
" this.ondownload = ondownload;\n",
"}\n",
"\n",
"mpl.figure.prototype._init_header = function() {\n",
" var titlebar = $(\n",
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n",
" 'ui-helper-clearfix\"/>');\n",
" var titletext = $(\n",
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n",
" 'text-align: center; padding: 3px;\"/>');\n",
" titlebar.append(titletext)\n",
" this.root.append(titlebar);\n",
" this.header = titletext[0];\n",
"}\n",
"\n",
"\n",
"\n",
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
"\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
"\n",
"}\n",
"\n",
"mpl.figure.prototype._init_canvas = function() {\n",
" var fig = this;\n",
"\n",
" var canvas_div = $('<div/>');\n",
"\n",
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
"\n",
" function canvas_keyboard_event(event) {\n",
" return fig.key_event(event, event['data']);\n",
" }\n",
"\n",
" canvas_div.keydown('key_press', canvas_keyboard_event);\n",
" canvas_div.keyup('key_release', canvas_keyboard_event);\n",
" this.canvas_div = canvas_div\n",
" this._canvas_extra_style(canvas_div)\n",
" this.root.append(canvas_div);\n",
"\n",
" var canvas = $('<canvas/>');\n",
" canvas.addClass('mpl-canvas');\n",
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
"\n",
" this.canvas = canvas[0];\n",
" this.context = canvas[0].getContext(\"2d\");\n",
"\n",
" var rubberband = $('<canvas/>');\n",
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
"\n",
" var pass_mouse_events = true;\n",
"\n",
" canvas_div.resizable({\n",
" start: function(event, ui) {\n",
" pass_mouse_events = false;\n",
" },\n",
" resize: function(event, ui) {\n",
" fig.request_resize(ui.size.width, ui.size.height);\n",
" },\n",
" stop: function(event, ui) {\n",
" pass_mouse_events = true;\n",
" fig.request_resize(ui.size.width, ui.size.height);\n",
" },\n",
" });\n",
"\n",
" function mouse_event_fn(event) {\n",
" if (pass_mouse_events)\n",
" return fig.mouse_event(event, event['data']);\n",
" }\n",
"\n",
" rubberband.mousedown('button_press', mouse_event_fn);\n",
" rubberband.mouseup('button_release', mouse_event_fn);\n",
" // Throttle sequential mouse events to 1 every 20ms.\n",
" rubberband.mousemove('motion_notify', mouse_event_fn);\n",
"\n",
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
"\n",
" canvas_div.on(\"wheel\", function (event) {\n",
" event = event.originalEvent;\n",
" event['data'] = 'scroll'\n",
" if (event.deltaY < 0) {\n",
" event.step = 1;\n",
" } else {\n",
" event.step = -1;\n",
" }\n",
" mouse_event_fn(event);\n",
" });\n",
"\n",
" canvas_div.append(canvas);\n",
" canvas_div.append(rubberband);\n",
"\n",
" this.rubberband = rubberband;\n",
" this.rubberband_canvas = rubberband[0];\n",
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
" this.rubberband_context.strokeStyle = \"#000000\";\n",
"\n",
" this._resize_canvas = function(width, height) {\n",
" // Keep the size of the canvas, canvas container, and rubber band\n",
" // canvas in synch.\n",
" canvas_div.css('width', width)\n",
" canvas_div.css('height', height)\n",
"\n",
" canvas.attr('width', width);\n",
" canvas.attr('height', height);\n",
"\n",
" rubberband.attr('width', width);\n",
" rubberband.attr('height', height);\n",
" }\n",
"\n",
" // Set the figure to an initial 600x600px, this will subsequently be updated\n",
" // upon first draw.\n",
" this._resize_canvas(600, 600);\n",
"\n",
" // Disable right mouse context menu.\n",
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
" return false;\n",
" });\n",
"\n",
" function set_focus () {\n",
" canvas.focus();\n",
" canvas_div.focus();\n",
" }\n",
"\n",
" window.setTimeout(set_focus, 100);\n",
"}\n",
"\n",
"mpl.figure.prototype._init_toolbar = function() {\n",
" var fig = this;\n",
"\n",
" var nav_element = $('<div/>')\n",
" nav_element.attr('style', 'width: 100%');\n",
" this.root.append(nav_element);\n",
"\n",
" // Define a callback function for later on.\n",
" function toolbar_event(event) {\n",
" return fig.toolbar_button_onclick(event['data']);\n",
" }\n",
" function toolbar_mouse_event(event) {\n",
" return fig.toolbar_button_onmouseover(event['data']);\n",
" }\n",
"\n",
" for(var toolbar_ind in mpl.toolbar_items) {\n",
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
"\n",
" if (!name) {\n",
" // put a spacer in here.\n",
" continue;\n",
" }\n",
" var button = $('<button/>');\n",
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
" 'ui-button-icon-only');\n",
" button.attr('role', 'button');\n",
" button.attr('aria-disabled', 'false');\n",
" button.click(method_name, toolbar_event);\n",
" button.mouseover(tooltip, toolbar_mouse_event);\n",
"\n",
" var icon_img = $('<span/>');\n",
" icon_img.addClass('ui-button-icon-primary ui-icon');\n",
" icon_img.addClass(image);\n",
" icon_img.addClass('ui-corner-all');\n",
"\n",
" var tooltip_span = $('<span/>');\n",
" tooltip_span.addClass('ui-button-text');\n",
" tooltip_span.html(tooltip);\n",
"\n",
" button.append(icon_img);\n",
" button.append(tooltip_span);\n",
"\n",
" nav_element.append(button);\n",
" }\n",
"\n",
" var fmt_picker_span = $('<span/>');\n",
"\n",
" var fmt_picker = $('<select/>');\n",
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
" fmt_picker_span.append(fmt_picker);\n",
" nav_element.append(fmt_picker_span);\n",
" this.format_dropdown = fmt_picker[0];\n",
"\n",
" for (var ind in mpl.extensions) {\n",
" var fmt = mpl.extensions[ind];\n",
" var option = $(\n",
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n",
" fmt_picker.append(option)\n",
" }\n",
"\n",
" // Add hover states to the ui-buttons\n",
" $( \".ui-button\" ).hover(\n",
" function() { $(this).addClass(\"ui-state-hover\");},\n",
" function() { $(this).removeClass(\"ui-state-hover\");}\n",
" );\n",
"\n",
" var status_bar = $('<span class=\"mpl-message\"/>');\n",
" nav_element.append(status_bar);\n",
" this.message = status_bar[0];\n",
"}\n",
"\n",
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
" // which will in turn request a refresh of the image.\n",
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
"}\n",
"\n",
"mpl.figure.prototype.send_message = function(type, properties) {\n",
" properties['type'] = type;\n",
" properties['figure_id'] = this.id;\n",
" this.ws.send(JSON.stringify(properties));\n",
"}\n",
"\n",
"mpl.figure.prototype.send_draw_message = function() {\n",
" if (!this.waiting) {\n",
" this.waiting = true;\n",
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
" }\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
" var format_dropdown = fig.format_dropdown;\n",
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
" fig.ondownload(fig, format);\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
" var size = msg['size'];\n",
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
" fig._resize_canvas(size[0], size[1]);\n",
" fig.send_message(\"refresh\", {});\n",
" };\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
" var x0 = msg['x0'];\n",
" var y0 = fig.canvas.height - msg['y0'];\n",
" var x1 = msg['x1'];\n",
" var y1 = fig.canvas.height - msg['y1'];\n",
" x0 = Math.floor(x0) + 0.5;\n",
" y0 = Math.floor(y0) + 0.5;\n",
" x1 = Math.floor(x1) + 0.5;\n",
" y1 = Math.floor(y1) + 0.5;\n",
" var min_x = Math.min(x0, x1);\n",
" var min_y = Math.min(y0, y1);\n",
" var width = Math.abs(x1 - x0);\n",
" var height = Math.abs(y1 - y0);\n",
"\n",
" fig.rubberband_context.clearRect(\n",
" 0, 0, fig.canvas.width, fig.canvas.height);\n",
"\n",
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
" // Updates the figure title.\n",
" fig.header.textContent = msg['label'];\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
" var cursor = msg['cursor'];\n",
" switch(cursor)\n",
" {\n",
" case 0:\n",
" cursor = 'pointer';\n",
" break;\n",
" case 1:\n",
" cursor = 'default';\n",
" break;\n",
" case 2:\n",
" cursor = 'crosshair';\n",
" break;\n",
" case 3:\n",
" cursor = 'move';\n",
" break;\n",
" }\n",
" fig.rubberband_canvas.style.cursor = cursor;\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_message = function(fig, msg) {\n",
" fig.message.textContent = msg['message'];\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
" // Request the server to send over a new figure.\n",
" fig.send_draw_message();\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
" fig.image_mode = msg['mode'];\n",
"}\n",
"\n",
"mpl.figure.prototype.updated_canvas_event = function() {\n",
" // Called whenever the canvas gets updated.\n",
" this.send_message(\"ack\", {});\n",
"}\n",
"\n",
"// A function to construct a web socket function for onmessage handling.\n",
"// Called in the figure constructor.\n",
"mpl.figure.prototype._make_on_message_function = function(fig) {\n",
" return function socket_on_message(evt) {\n",
" if (evt.data instanceof Blob) {\n",
" /* FIXME: We get \"Resource interpreted as Image but\n",
" * transferred with MIME type text/plain:\" errors on\n",
" * Chrome. But how to set the MIME type? It doesn't seem\n",
" * to be part of the websocket stream */\n",
" evt.data.type = \"image/png\";\n",
"\n",
" /* Free the memory for the previous frames */\n",
" if (fig.imageObj.src) {\n",
" (window.URL || window.webkitURL).revokeObjectURL(\n",
" fig.imageObj.src);\n",
" }\n",
"\n",
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
" evt.data);\n",
" fig.updated_canvas_event();\n",
" fig.waiting = false;\n",
" return;\n",
" }\n",
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
" fig.imageObj.src = evt.data;\n",
" fig.updated_canvas_event();\n",
" fig.waiting = false;\n",
" return;\n",
" }\n",
"\n",
" var msg = JSON.parse(evt.data);\n",
" var msg_type = msg['type'];\n",
"\n",
" // Call the \"handle_{type}\" callback, which takes\n",
" // the figure and JSON message as its only arguments.\n",
" try {\n",
" var callback = fig[\"handle_\" + msg_type];\n",
" } catch (e) {\n",
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
" return;\n",
" }\n",
"\n",
" if (callback) {\n",
" try {\n",
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
" callback(fig, msg);\n",
" } catch (e) {\n",
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
" }\n",
" }\n",
" };\n",
"}\n",
"\n",
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
"mpl.findpos = function(e) {\n",
" //this section is from http://www.quirksmode.org/js/events_properties.html\n",
" var targ;\n",
" if (!e)\n",
" e = window.event;\n",
" if (e.target)\n",
" targ = e.target;\n",
" else if (e.srcElement)\n",
" targ = e.srcElement;\n",
" if (targ.nodeType == 3) // defeat Safari bug\n",
" targ = targ.parentNode;\n",
"\n",
" // jQuery normalizes the pageX and pageY\n",
" // pageX,Y are the mouse positions relative to the document\n",
" // offset() returns the position of the element relative to the document\n",
" var x = e.pageX - $(targ).offset().left;\n",
" var y = e.pageY - $(targ).offset().top;\n",
"\n",
" return {\"x\": x, \"y\": y};\n",
"};\n",
"\n",
"/*\n",
" * return a copy of an object with only non-object keys\n",
" * we need this to avoid circular references\n",
" * http://stackoverflow.com/a/24161582/3208463\n",
" */\n",
"function simpleKeys (original) {\n",
" return Object.keys(original).reduce(function (obj, key) {\n",
" if (typeof original[key] !== 'object')\n",
" obj[key] = original[key]\n",
" return obj;\n",
" }, {});\n",
"}\n",
"\n",
"mpl.figure.prototype.mouse_event = function(event, name) {\n",
" var canvas_pos = mpl.findpos(event)\n",
"\n",
" if (name === 'button_press')\n",
" {\n",
" this.canvas.focus();\n",
" this.canvas_div.focus();\n",
" }\n",
"\n",
" var x = canvas_pos.x;\n",
" var y = canvas_pos.y;\n",
"\n",
" this.send_message(name, {x: x, y: y, button: event.button,\n",
" step: event.step,\n",
" guiEvent: simpleKeys(event)});\n",
"\n",
" /* This prevents the web browser from automatically changing to\n",
" * the text insertion cursor when the button is pressed. We want\n",
" * to control all of the cursor setting manually through the\n",
" * 'cursor' event from matplotlib */\n",
" event.preventDefault();\n",
" return false;\n",
"}\n",
"\n",
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
" // Handle any extra behaviour associated with a key event\n",
"}\n",
"\n",
"mpl.figure.prototype.key_event = function(event, name) {\n",
"\n",
" // Prevent repeat events\n",
" if (name == 'key_press')\n",
" {\n",
" if (event.which === this._key)\n",
" return;\n",
" else\n",
" this._key = event.which;\n",
" }\n",
" if (name == 'key_release')\n",
" this._key = null;\n",
"\n",
" var value = '';\n",
" if (event.ctrlKey && event.which != 17)\n",
" value += \"ctrl+\";\n",
" if (event.altKey && event.which != 18)\n",
" value += \"alt+\";\n",
" if (event.shiftKey && event.which != 16)\n",
" value += \"shift+\";\n",
"\n",
" value += 'k';\n",
" value += event.which.toString();\n",
"\n",
" this._key_event_extra(event, name);\n",
"\n",
" this.send_message(name, {key: value,\n",
" guiEvent: simpleKeys(event)});\n",
" return false;\n",
"}\n",
"\n",
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
" if (name == 'download') {\n",
" this.handle_save(this, null);\n",
" } else {\n",
" this.send_message(\"toolbar_button\", {name: name});\n",
" }\n",
"};\n",
"\n",
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
" this.message.textContent = tooltip;\n",
"};\n",
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
"\n",
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
"\n",
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
" // Create a \"websocket\"-like object which calls the given IPython comm\n",
" // object with the appropriate methods. Currently this is a non binary\n",
" // socket, so there is still some room for performance tuning.\n",
" var ws = {};\n",
"\n",
" ws.close = function() {\n",
" comm.close()\n",
" };\n",
" ws.send = function(m) {\n",
" //console.log('sending', m);\n",
" comm.send(m);\n",
" };\n",
" // Register the callback with on_msg.\n",
" comm.on_msg(function(msg) {\n",
" //console.log('receiving', msg['content']['data'], msg);\n",
" // Pass the mpl event to the overriden (by mpl) onmessage function.\n",
" ws.onmessage(msg['content']['data'])\n",
" });\n",
" return ws;\n",
"}\n",
"\n",
"mpl.mpl_figure_comm = function(comm, msg) {\n",
" // This is the function which gets called when the mpl process\n",
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
"\n",
" var id = msg.content.data.id;\n",
" // Get hold of the div created by the display call when the Comm\n",
" // socket was opened in Python.\n",
" var element = $(\"#\" + id);\n",
" var ws_proxy = comm_websocket_adapter(comm)\n",
"\n",
" function ondownload(figure, format) {\n",
" window.open(figure.imageObj.src);\n",
" }\n",
"\n",
" var fig = new mpl.figure(id, ws_proxy,\n",
" ondownload,\n",
" element.get(0));\n",
"\n",
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
" // web socket which is closed, not our websocket->open comm proxy.\n",
" ws_proxy.onopen();\n",
"\n",
" fig.parent_element = element.get(0);\n",
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
" if (!fig.cell_info) {\n",
" console.error(\"Failed to find cell for figure\", id, fig);\n",
" return;\n",
" }\n",
"\n",
" var output_index = fig.cell_info[2]\n",
" var cell = fig.cell_info[0];\n",
"\n",
"};\n",
"\n",
"mpl.figure.prototype.handle_close = function(fig, msg) {\n",
" fig.root.unbind('remove')\n",
"\n",
" // Update the output cell to use the data from the current canvas.\n",
" fig.push_to_output();\n",
" var dataURL = fig.canvas.toDataURL();\n",
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
" // the notebook keyboard shortcuts fail.\n",
" IPython.keyboard_manager.enable()\n",
" $(fig.parent_element).html('<img src=\"' + dataURL + '\">');\n",
" fig.close_ws(fig, msg);\n",
"}\n",
"\n",
"mpl.figure.prototype.close_ws = function(fig, msg){\n",
" fig.send_message('closing', msg);\n",
" // fig.ws.close()\n",
"}\n",
"\n",
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
" // Turn the data on the canvas into data in the output cell.\n",
" var dataURL = this.canvas.toDataURL();\n",
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\">';\n",
"}\n",
"\n",
"mpl.figure.prototype.updated_canvas_event = function() {\n",
" // Tell IPython that the notebook contents must change.\n",
" IPython.notebook.set_dirty(true);\n",
" this.send_message(\"ack\", {});\n",
" var fig = this;\n",
" // Wait a second, then push the new image to the DOM so\n",
" // that it is saved nicely (might be nice to debounce this).\n",
" setTimeout(function () { fig.push_to_output() }, 1000);\n",
"}\n",
"\n",
"mpl.figure.prototype._init_toolbar = function() {\n",
" var fig = this;\n",
"\n",
" var nav_element = $('<div/>')\n",
" nav_element.attr('style', 'width: 100%');\n",
" this.root.append(nav_element);\n",
"\n",
" // Define a callback function for later on.\n",
" function toolbar_event(event) {\n",
" return fig.toolbar_button_onclick(event['data']);\n",
" }\n",
" function toolbar_mouse_event(event) {\n",
" return fig.toolbar_button_onmouseover(event['data']);\n",
" }\n",
"\n",
" for(var toolbar_ind in mpl.toolbar_items){\n",
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
"\n",
" if (!name) { continue; };\n",
"\n",
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n",
" button.click(method_name, toolbar_event);\n",
" button.mouseover(tooltip, toolbar_mouse_event);\n",
" nav_element.append(button);\n",
" }\n",
"\n",
" // Add the status bar.\n",
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n",
" nav_element.append(status_bar);\n",
" this.message = status_bar[0];\n",
"\n",
" // Add the close button to the window.\n",
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n",
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n",
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
" buttongrp.append(button);\n",
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
" titlebar.prepend(buttongrp);\n",
"}\n",
"\n",
"mpl.figure.prototype._root_extra_style = function(el){\n",
" var fig = this\n",
" el.on(\"remove\", function(){\n",
"\tfig.close_ws(fig, {});\n",
" });\n",
"}\n",
"\n",
"mpl.figure.prototype._canvas_extra_style = function(el){\n",
" // this is important to make the div 'focusable\n",
" el.attr('tabindex', 0)\n",
" // reach out to IPython and tell the keyboard manager to turn it's self\n",
" // off when our div gets focus\n",
"\n",
" // location in version 3\n",
" if (IPython.notebook.keyboard_manager) {\n",
" IPython.notebook.keyboard_manager.register_events(el);\n",
" }\n",
" else {\n",
" // location in version 2\n",
" IPython.keyboard_manager.register_events(el);\n",
" }\n",
"\n",
"}\n",
"\n",
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
" var manager = IPython.notebook.keyboard_manager;\n",
" if (!manager)\n",
" manager = IPython.keyboard_manager;\n",
"\n",
" // Check for shift+enter\n",
" if (event.shiftKey && event.which == 13) {\n",
" this.canvas_div.blur();\n",
" event.shiftKey = false;\n",
" // Send a \"J\" for go to next cell\n",
" event.which = 74;\n",
" event.keyCode = 74;\n",
" manager.command_mode();\n",
" manager.handle_keydown(event);\n",
" }\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
" fig.ondownload(fig, null);\n",
"}\n",
"\n",
"\n",
"mpl.find_output_cell = function(html_output) {\n",
" // Return the cell and output element which can be found *uniquely* in the notebook.\n",
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
" // IPython event is triggered only after the cells have been serialised, which for\n",
" // our purposes (turning an active figure into a static one), is too late.\n",
" var cells = IPython.notebook.get_cells();\n",
" var ncells = cells.length;\n",
" for (var i=0; i<ncells; i++) {\n",
" var cell = cells[i];\n",
" if (cell.cell_type === 'code'){\n",
" for (var j=0; j<cell.output_area.outputs.length; j++) {\n",
" var data = cell.output_area.outputs[j];\n",
" if (data.data) {\n",
" // IPython >= 3 moved mimebundle to data attribute of output\n",
" data = data.data;\n",
" }\n",
" if (data['text/html'] == html_output) {\n",
" return [cell, data, j];\n",
" }\n",
" }\n",
" }\n",
" }\n",
"}\n",
"\n",
"// Register the function which deals with the matplotlib target/channel.\n",
"// The kernel may be null if the page has been refreshed.\n",
"if (IPython.notebook.kernel != null) {\n",
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
"}\n"
],
"text/plain": [
"<IPython.core.display.Javascript object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"<img src=\"data:image/png;base64,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\">"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"<matplotlib.animation.ArtistAnimation at 0x3bc9c10>"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def f(x):\n",
" return x\n",
"\n",
"make_fourier(f, 20, 'fourier01.gif')"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false,
"scrolled": true
},
"outputs": [
{
"data": {
"application/javascript": [
"/* Put everything inside the global mpl namespace */\n",
"window.mpl = {};\n",
"\n",
"mpl.get_websocket_type = function() {\n",
" if (typeof(WebSocket) !== 'undefined') {\n",
" return WebSocket;\n",
" } else if (typeof(MozWebSocket) !== 'undefined') {\n",
" return MozWebSocket;\n",
" } else {\n",
" alert('Your browser does not have WebSocket support.' +\n",
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
" 'Firefox 4 and 5 are also supported but you ' +\n",
" 'have to enable WebSockets in about:config.');\n",
" };\n",
"}\n",
"\n",
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
" this.id = figure_id;\n",
"\n",
" this.ws = websocket;\n",
"\n",
" this.supports_binary = (this.ws.binaryType != undefined);\n",
"\n",
" if (!this.supports_binary) {\n",
" var warnings = document.getElementById(\"mpl-warnings\");\n",
" if (warnings) {\n",
" warnings.style.display = 'block';\n",
" warnings.textContent = (\n",
" \"This browser does not support binary websocket messages. \" +\n",
" \"Performance may be slow.\");\n",
" }\n",
" }\n",
"\n",
" this.imageObj = new Image();\n",
"\n",
" this.context = undefined;\n",
" this.message = undefined;\n",
" this.canvas = undefined;\n",
" this.rubberband_canvas = undefined;\n",
" this.rubberband_context = undefined;\n",
" this.format_dropdown = undefined;\n",
"\n",
" this.image_mode = 'full';\n",
"\n",
" this.root = $('<div/>');\n",
" this._root_extra_style(this.root)\n",
" this.root.attr('style', 'display: inline-block');\n",
"\n",
" $(parent_element).append(this.root);\n",
"\n",
" this._init_header(this);\n",
" this._init_canvas(this);\n",
" this._init_toolbar(this);\n",
"\n",
" var fig = this;\n",
"\n",
" this.waiting = false;\n",
"\n",
" this.ws.onopen = function () {\n",
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
" fig.send_message(\"send_image_mode\", {});\n",
" fig.send_message(\"refresh\", {});\n",
" }\n",
"\n",
" this.imageObj.onload = function() {\n",
" if (fig.image_mode == 'full') {\n",
" // Full images could contain transparency (where diff images\n",
" // almost always do), so we need to clear the canvas so that\n",
" // there is no ghosting.\n",
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
" }\n",
" fig.context.drawImage(fig.imageObj, 0, 0);\n",
" };\n",
"\n",
" this.imageObj.onunload = function() {\n",
" this.ws.close();\n",
" }\n",
"\n",
" this.ws.onmessage = this._make_on_message_function(this);\n",
"\n",
" this.ondownload = ondownload;\n",
"}\n",
"\n",
"mpl.figure.prototype._init_header = function() {\n",
" var titlebar = $(\n",
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n",
" 'ui-helper-clearfix\"/>');\n",
" var titletext = $(\n",
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n",
" 'text-align: center; padding: 3px;\"/>');\n",
" titlebar.append(titletext)\n",
" this.root.append(titlebar);\n",
" this.header = titletext[0];\n",
"}\n",
"\n",
"\n",
"\n",
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
"\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
"\n",
"}\n",
"\n",
"mpl.figure.prototype._init_canvas = function() {\n",
" var fig = this;\n",
"\n",
" var canvas_div = $('<div/>');\n",
"\n",
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
"\n",
" function canvas_keyboard_event(event) {\n",
" return fig.key_event(event, event['data']);\n",
" }\n",
"\n",
" canvas_div.keydown('key_press', canvas_keyboard_event);\n",
" canvas_div.keyup('key_release', canvas_keyboard_event);\n",
" this.canvas_div = canvas_div\n",
" this._canvas_extra_style(canvas_div)\n",
" this.root.append(canvas_div);\n",
"\n",
" var canvas = $('<canvas/>');\n",
" canvas.addClass('mpl-canvas');\n",
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
"\n",
" this.canvas = canvas[0];\n",
" this.context = canvas[0].getContext(\"2d\");\n",
"\n",
" var rubberband = $('<canvas/>');\n",
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
"\n",
" var pass_mouse_events = true;\n",
"\n",
" canvas_div.resizable({\n",
" start: function(event, ui) {\n",
" pass_mouse_events = false;\n",
" },\n",
" resize: function(event, ui) {\n",
" fig.request_resize(ui.size.width, ui.size.height);\n",
" },\n",
" stop: function(event, ui) {\n",
" pass_mouse_events = true;\n",
" fig.request_resize(ui.size.width, ui.size.height);\n",
" },\n",
" });\n",
"\n",
" function mouse_event_fn(event) {\n",
" if (pass_mouse_events)\n",
" return fig.mouse_event(event, event['data']);\n",
" }\n",
"\n",
" rubberband.mousedown('button_press', mouse_event_fn);\n",
" rubberband.mouseup('button_release', mouse_event_fn);\n",
" // Throttle sequential mouse events to 1 every 20ms.\n",
" rubberband.mousemove('motion_notify', mouse_event_fn);\n",
"\n",
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
"\n",
" canvas_div.on(\"wheel\", function (event) {\n",
" event = event.originalEvent;\n",
" event['data'] = 'scroll'\n",
" if (event.deltaY < 0) {\n",
" event.step = 1;\n",
" } else {\n",
" event.step = -1;\n",
" }\n",
" mouse_event_fn(event);\n",
" });\n",
"\n",
" canvas_div.append(canvas);\n",
" canvas_div.append(rubberband);\n",
"\n",
" this.rubberband = rubberband;\n",
" this.rubberband_canvas = rubberband[0];\n",
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
" this.rubberband_context.strokeStyle = \"#000000\";\n",
"\n",
" this._resize_canvas = function(width, height) {\n",
" // Keep the size of the canvas, canvas container, and rubber band\n",
" // canvas in synch.\n",
" canvas_div.css('width', width)\n",
" canvas_div.css('height', height)\n",
"\n",
" canvas.attr('width', width);\n",
" canvas.attr('height', height);\n",
"\n",
" rubberband.attr('width', width);\n",
" rubberband.attr('height', height);\n",
" }\n",
"\n",
" // Set the figure to an initial 600x600px, this will subsequently be updated\n",
" // upon first draw.\n",
" this._resize_canvas(600, 600);\n",
"\n",
" // Disable right mouse context menu.\n",
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
" return false;\n",
" });\n",
"\n",
" function set_focus () {\n",
" canvas.focus();\n",
" canvas_div.focus();\n",
" }\n",
"\n",
" window.setTimeout(set_focus, 100);\n",
"}\n",
"\n",
"mpl.figure.prototype._init_toolbar = function() {\n",
" var fig = this;\n",
"\n",
" var nav_element = $('<div/>')\n",
" nav_element.attr('style', 'width: 100%');\n",
" this.root.append(nav_element);\n",
"\n",
" // Define a callback function for later on.\n",
" function toolbar_event(event) {\n",
" return fig.toolbar_button_onclick(event['data']);\n",
" }\n",
" function toolbar_mouse_event(event) {\n",
" return fig.toolbar_button_onmouseover(event['data']);\n",
" }\n",
"\n",
" for(var toolbar_ind in mpl.toolbar_items) {\n",
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
"\n",
" if (!name) {\n",
" // put a spacer in here.\n",
" continue;\n",
" }\n",
" var button = $('<button/>');\n",
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
" 'ui-button-icon-only');\n",
" button.attr('role', 'button');\n",
" button.attr('aria-disabled', 'false');\n",
" button.click(method_name, toolbar_event);\n",
" button.mouseover(tooltip, toolbar_mouse_event);\n",
"\n",
" var icon_img = $('<span/>');\n",
" icon_img.addClass('ui-button-icon-primary ui-icon');\n",
" icon_img.addClass(image);\n",
" icon_img.addClass('ui-corner-all');\n",
"\n",
" var tooltip_span = $('<span/>');\n",
" tooltip_span.addClass('ui-button-text');\n",
" tooltip_span.html(tooltip);\n",
"\n",
" button.append(icon_img);\n",
" button.append(tooltip_span);\n",
"\n",
" nav_element.append(button);\n",
" }\n",
"\n",
" var fmt_picker_span = $('<span/>');\n",
"\n",
" var fmt_picker = $('<select/>');\n",
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
" fmt_picker_span.append(fmt_picker);\n",
" nav_element.append(fmt_picker_span);\n",
" this.format_dropdown = fmt_picker[0];\n",
"\n",
" for (var ind in mpl.extensions) {\n",
" var fmt = mpl.extensions[ind];\n",
" var option = $(\n",
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n",
" fmt_picker.append(option)\n",
" }\n",
"\n",
" // Add hover states to the ui-buttons\n",
" $( \".ui-button\" ).hover(\n",
" function() { $(this).addClass(\"ui-state-hover\");},\n",
" function() { $(this).removeClass(\"ui-state-hover\");}\n",
" );\n",
"\n",
" var status_bar = $('<span class=\"mpl-message\"/>');\n",
" nav_element.append(status_bar);\n",
" this.message = status_bar[0];\n",
"}\n",
"\n",
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
" // which will in turn request a refresh of the image.\n",
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
"}\n",
"\n",
"mpl.figure.prototype.send_message = function(type, properties) {\n",
" properties['type'] = type;\n",
" properties['figure_id'] = this.id;\n",
" this.ws.send(JSON.stringify(properties));\n",
"}\n",
"\n",
"mpl.figure.prototype.send_draw_message = function() {\n",
" if (!this.waiting) {\n",
" this.waiting = true;\n",
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
" }\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
" var format_dropdown = fig.format_dropdown;\n",
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
" fig.ondownload(fig, format);\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
" var size = msg['size'];\n",
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
" fig._resize_canvas(size[0], size[1]);\n",
" fig.send_message(\"refresh\", {});\n",
" };\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
" var x0 = msg['x0'];\n",
" var y0 = fig.canvas.height - msg['y0'];\n",
" var x1 = msg['x1'];\n",
" var y1 = fig.canvas.height - msg['y1'];\n",
" x0 = Math.floor(x0) + 0.5;\n",
" y0 = Math.floor(y0) + 0.5;\n",
" x1 = Math.floor(x1) + 0.5;\n",
" y1 = Math.floor(y1) + 0.5;\n",
" var min_x = Math.min(x0, x1);\n",
" var min_y = Math.min(y0, y1);\n",
" var width = Math.abs(x1 - x0);\n",
" var height = Math.abs(y1 - y0);\n",
"\n",
" fig.rubberband_context.clearRect(\n",
" 0, 0, fig.canvas.width, fig.canvas.height);\n",
"\n",
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
" // Updates the figure title.\n",
" fig.header.textContent = msg['label'];\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
" var cursor = msg['cursor'];\n",
" switch(cursor)\n",
" {\n",
" case 0:\n",
" cursor = 'pointer';\n",
" break;\n",
" case 1:\n",
" cursor = 'default';\n",
" break;\n",
" case 2:\n",
" cursor = 'crosshair';\n",
" break;\n",
" case 3:\n",
" cursor = 'move';\n",
" break;\n",
" }\n",
" fig.rubberband_canvas.style.cursor = cursor;\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_message = function(fig, msg) {\n",
" fig.message.textContent = msg['message'];\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
" // Request the server to send over a new figure.\n",
" fig.send_draw_message();\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
" fig.image_mode = msg['mode'];\n",
"}\n",
"\n",
"mpl.figure.prototype.updated_canvas_event = function() {\n",
" // Called whenever the canvas gets updated.\n",
" this.send_message(\"ack\", {});\n",
"}\n",
"\n",
"// A function to construct a web socket function for onmessage handling.\n",
"// Called in the figure constructor.\n",
"mpl.figure.prototype._make_on_message_function = function(fig) {\n",
" return function socket_on_message(evt) {\n",
" if (evt.data instanceof Blob) {\n",
" /* FIXME: We get \"Resource interpreted as Image but\n",
" * transferred with MIME type text/plain:\" errors on\n",
" * Chrome. But how to set the MIME type? It doesn't seem\n",
" * to be part of the websocket stream */\n",
" evt.data.type = \"image/png\";\n",
"\n",
" /* Free the memory for the previous frames */\n",
" if (fig.imageObj.src) {\n",
" (window.URL || window.webkitURL).revokeObjectURL(\n",
" fig.imageObj.src);\n",
" }\n",
"\n",
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
" evt.data);\n",
" fig.updated_canvas_event();\n",
" fig.waiting = false;\n",
" return;\n",
" }\n",
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
" fig.imageObj.src = evt.data;\n",
" fig.updated_canvas_event();\n",
" fig.waiting = false;\n",
" return;\n",
" }\n",
"\n",
" var msg = JSON.parse(evt.data);\n",
" var msg_type = msg['type'];\n",
"\n",
" // Call the \"handle_{type}\" callback, which takes\n",
" // the figure and JSON message as its only arguments.\n",
" try {\n",
" var callback = fig[\"handle_\" + msg_type];\n",
" } catch (e) {\n",
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
" return;\n",
" }\n",
"\n",
" if (callback) {\n",
" try {\n",
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
" callback(fig, msg);\n",
" } catch (e) {\n",
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
" }\n",
" }\n",
" };\n",
"}\n",
"\n",
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
"mpl.findpos = function(e) {\n",
" //this section is from http://www.quirksmode.org/js/events_properties.html\n",
" var targ;\n",
" if (!e)\n",
" e = window.event;\n",
" if (e.target)\n",
" targ = e.target;\n",
" else if (e.srcElement)\n",
" targ = e.srcElement;\n",
" if (targ.nodeType == 3) // defeat Safari bug\n",
" targ = targ.parentNode;\n",
"\n",
" // jQuery normalizes the pageX and pageY\n",
" // pageX,Y are the mouse positions relative to the document\n",
" // offset() returns the position of the element relative to the document\n",
" var x = e.pageX - $(targ).offset().left;\n",
" var y = e.pageY - $(targ).offset().top;\n",
"\n",
" return {\"x\": x, \"y\": y};\n",
"};\n",
"\n",
"/*\n",
" * return a copy of an object with only non-object keys\n",
" * we need this to avoid circular references\n",
" * http://stackoverflow.com/a/24161582/3208463\n",
" */\n",
"function simpleKeys (original) {\n",
" return Object.keys(original).reduce(function (obj, key) {\n",
" if (typeof original[key] !== 'object')\n",
" obj[key] = original[key]\n",
" return obj;\n",
" }, {});\n",
"}\n",
"\n",
"mpl.figure.prototype.mouse_event = function(event, name) {\n",
" var canvas_pos = mpl.findpos(event)\n",
"\n",
" if (name === 'button_press')\n",
" {\n",
" this.canvas.focus();\n",
" this.canvas_div.focus();\n",
" }\n",
"\n",
" var x = canvas_pos.x;\n",
" var y = canvas_pos.y;\n",
"\n",
" this.send_message(name, {x: x, y: y, button: event.button,\n",
" step: event.step,\n",
" guiEvent: simpleKeys(event)});\n",
"\n",
" /* This prevents the web browser from automatically changing to\n",
" * the text insertion cursor when the button is pressed. We want\n",
" * to control all of the cursor setting manually through the\n",
" * 'cursor' event from matplotlib */\n",
" event.preventDefault();\n",
" return false;\n",
"}\n",
"\n",
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
" // Handle any extra behaviour associated with a key event\n",
"}\n",
"\n",
"mpl.figure.prototype.key_event = function(event, name) {\n",
"\n",
" // Prevent repeat events\n",
" if (name == 'key_press')\n",
" {\n",
" if (event.which === this._key)\n",
" return;\n",
" else\n",
" this._key = event.which;\n",
" }\n",
" if (name == 'key_release')\n",
" this._key = null;\n",
"\n",
" var value = '';\n",
" if (event.ctrlKey && event.which != 17)\n",
" value += \"ctrl+\";\n",
" if (event.altKey && event.which != 18)\n",
" value += \"alt+\";\n",
" if (event.shiftKey && event.which != 16)\n",
" value += \"shift+\";\n",
"\n",
" value += 'k';\n",
" value += event.which.toString();\n",
"\n",
" this._key_event_extra(event, name);\n",
"\n",
" this.send_message(name, {key: value,\n",
" guiEvent: simpleKeys(event)});\n",
" return false;\n",
"}\n",
"\n",
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
" if (name == 'download') {\n",
" this.handle_save(this, null);\n",
" } else {\n",
" this.send_message(\"toolbar_button\", {name: name});\n",
" }\n",
"};\n",
"\n",
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
" this.message.textContent = tooltip;\n",
"};\n",
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
"\n",
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
"\n",
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
" // Create a \"websocket\"-like object which calls the given IPython comm\n",
" // object with the appropriate methods. Currently this is a non binary\n",
" // socket, so there is still some room for performance tuning.\n",
" var ws = {};\n",
"\n",
" ws.close = function() {\n",
" comm.close()\n",
" };\n",
" ws.send = function(m) {\n",
" //console.log('sending', m);\n",
" comm.send(m);\n",
" };\n",
" // Register the callback with on_msg.\n",
" comm.on_msg(function(msg) {\n",
" //console.log('receiving', msg['content']['data'], msg);\n",
" // Pass the mpl event to the overriden (by mpl) onmessage function.\n",
" ws.onmessage(msg['content']['data'])\n",
" });\n",
" return ws;\n",
"}\n",
"\n",
"mpl.mpl_figure_comm = function(comm, msg) {\n",
" // This is the function which gets called when the mpl process\n",
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
"\n",
" var id = msg.content.data.id;\n",
" // Get hold of the div created by the display call when the Comm\n",
" // socket was opened in Python.\n",
" var element = $(\"#\" + id);\n",
" var ws_proxy = comm_websocket_adapter(comm)\n",
"\n",
" function ondownload(figure, format) {\n",
" window.open(figure.imageObj.src);\n",
" }\n",
"\n",
" var fig = new mpl.figure(id, ws_proxy,\n",
" ondownload,\n",
" element.get(0));\n",
"\n",
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
" // web socket which is closed, not our websocket->open comm proxy.\n",
" ws_proxy.onopen();\n",
"\n",
" fig.parent_element = element.get(0);\n",
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
" if (!fig.cell_info) {\n",
" console.error(\"Failed to find cell for figure\", id, fig);\n",
" return;\n",
" }\n",
"\n",
" var output_index = fig.cell_info[2]\n",
" var cell = fig.cell_info[0];\n",
"\n",
"};\n",
"\n",
"mpl.figure.prototype.handle_close = function(fig, msg) {\n",
" fig.root.unbind('remove')\n",
"\n",
" // Update the output cell to use the data from the current canvas.\n",
" fig.push_to_output();\n",
" var dataURL = fig.canvas.toDataURL();\n",
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
" // the notebook keyboard shortcuts fail.\n",
" IPython.keyboard_manager.enable()\n",
" $(fig.parent_element).html('<img src=\"' + dataURL + '\">');\n",
" fig.close_ws(fig, msg);\n",
"}\n",
"\n",
"mpl.figure.prototype.close_ws = function(fig, msg){\n",
" fig.send_message('closing', msg);\n",
" // fig.ws.close()\n",
"}\n",
"\n",
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
" // Turn the data on the canvas into data in the output cell.\n",
" var dataURL = this.canvas.toDataURL();\n",
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\">';\n",
"}\n",
"\n",
"mpl.figure.prototype.updated_canvas_event = function() {\n",
" // Tell IPython that the notebook contents must change.\n",
" IPython.notebook.set_dirty(true);\n",
" this.send_message(\"ack\", {});\n",
" var fig = this;\n",
" // Wait a second, then push the new image to the DOM so\n",
" // that it is saved nicely (might be nice to debounce this).\n",
" setTimeout(function () { fig.push_to_output() }, 1000);\n",
"}\n",
"\n",
"mpl.figure.prototype._init_toolbar = function() {\n",
" var fig = this;\n",
"\n",
" var nav_element = $('<div/>')\n",
" nav_element.attr('style', 'width: 100%');\n",
" this.root.append(nav_element);\n",
"\n",
" // Define a callback function for later on.\n",
" function toolbar_event(event) {\n",
" return fig.toolbar_button_onclick(event['data']);\n",
" }\n",
" function toolbar_mouse_event(event) {\n",
" return fig.toolbar_button_onmouseover(event['data']);\n",
" }\n",
"\n",
" for(var toolbar_ind in mpl.toolbar_items){\n",
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
"\n",
" if (!name) { continue; };\n",
"\n",
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n",
" button.click(method_name, toolbar_event);\n",
" button.mouseover(tooltip, toolbar_mouse_event);\n",
" nav_element.append(button);\n",
" }\n",
"\n",
" // Add the status bar.\n",
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n",
" nav_element.append(status_bar);\n",
" this.message = status_bar[0];\n",
"\n",
" // Add the close button to the window.\n",
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n",
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n",
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
" buttongrp.append(button);\n",
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
" titlebar.prepend(buttongrp);\n",
"}\n",
"\n",
"mpl.figure.prototype._root_extra_style = function(el){\n",
" var fig = this\n",
" el.on(\"remove\", function(){\n",
"\tfig.close_ws(fig, {});\n",
" });\n",
"}\n",
"\n",
"mpl.figure.prototype._canvas_extra_style = function(el){\n",
" // this is important to make the div 'focusable\n",
" el.attr('tabindex', 0)\n",
" // reach out to IPython and tell the keyboard manager to turn it's self\n",
" // off when our div gets focus\n",
"\n",
" // location in version 3\n",
" if (IPython.notebook.keyboard_manager) {\n",
" IPython.notebook.keyboard_manager.register_events(el);\n",
" }\n",
" else {\n",
" // location in version 2\n",
" IPython.keyboard_manager.register_events(el);\n",
" }\n",
"\n",
"}\n",
"\n",
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
" var manager = IPython.notebook.keyboard_manager;\n",
" if (!manager)\n",
" manager = IPython.keyboard_manager;\n",
"\n",
" // Check for shift+enter\n",
" if (event.shiftKey && event.which == 13) {\n",
" this.canvas_div.blur();\n",
" event.shiftKey = false;\n",
" // Send a \"J\" for go to next cell\n",
" event.which = 74;\n",
" event.keyCode = 74;\n",
" manager.command_mode();\n",
" manager.handle_keydown(event);\n",
" }\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
" fig.ondownload(fig, null);\n",
"}\n",
"\n",
"\n",
"mpl.find_output_cell = function(html_output) {\n",
" // Return the cell and output element which can be found *uniquely* in the notebook.\n",
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
" // IPython event is triggered only after the cells have been serialised, which for\n",
" // our purposes (turning an active figure into a static one), is too late.\n",
" var cells = IPython.notebook.get_cells();\n",
" var ncells = cells.length;\n",
" for (var i=0; i<ncells; i++) {\n",
" var cell = cells[i];\n",
" if (cell.cell_type === 'code'){\n",
" for (var j=0; j<cell.output_area.outputs.length; j++) {\n",
" var data = cell.output_area.outputs[j];\n",
" if (data.data) {\n",
" // IPython >= 3 moved mimebundle to data attribute of output\n",
" data = data.data;\n",
" }\n",
" if (data['text/html'] == html_output) {\n",
" return [cell, data, j];\n",
" }\n",
" }\n",
" }\n",
" }\n",
"}\n",
"\n",
"// Register the function which deals with the matplotlib target/channel.\n",
"// The kernel may be null if the page has been refreshed.\n",
"if (IPython.notebook.kernel != null) {\n",
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
"}\n"
],
"text/plain": [
"<IPython.core.display.Javascript object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"<img src=\"data:image/png;base64,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\">"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"<matplotlib.animation.ArtistAnimation at 0x42d5b50>"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def f(x):\n",
" return 0.1 * x**3\n",
"\n",
"make_fourier(f, 20, 'fourier02.gif')"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false,
"scrolled": true
},
"outputs": [
{
"data": {
"application/javascript": [
"/* Put everything inside the global mpl namespace */\n",
"window.mpl = {};\n",
"\n",
"mpl.get_websocket_type = function() {\n",
" if (typeof(WebSocket) !== 'undefined') {\n",
" return WebSocket;\n",
" } else if (typeof(MozWebSocket) !== 'undefined') {\n",
" return MozWebSocket;\n",
" } else {\n",
" alert('Your browser does not have WebSocket support.' +\n",
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
" 'Firefox 4 and 5 are also supported but you ' +\n",
" 'have to enable WebSockets in about:config.');\n",
" };\n",
"}\n",
"\n",
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
" this.id = figure_id;\n",
"\n",
" this.ws = websocket;\n",
"\n",
" this.supports_binary = (this.ws.binaryType != undefined);\n",
"\n",
" if (!this.supports_binary) {\n",
" var warnings = document.getElementById(\"mpl-warnings\");\n",
" if (warnings) {\n",
" warnings.style.display = 'block';\n",
" warnings.textContent = (\n",
" \"This browser does not support binary websocket messages. \" +\n",
" \"Performance may be slow.\");\n",
" }\n",
" }\n",
"\n",
" this.imageObj = new Image();\n",
"\n",
" this.context = undefined;\n",
" this.message = undefined;\n",
" this.canvas = undefined;\n",
" this.rubberband_canvas = undefined;\n",
" this.rubberband_context = undefined;\n",
" this.format_dropdown = undefined;\n",
"\n",
" this.image_mode = 'full';\n",
"\n",
" this.root = $('<div/>');\n",
" this._root_extra_style(this.root)\n",
" this.root.attr('style', 'display: inline-block');\n",
"\n",
" $(parent_element).append(this.root);\n",
"\n",
" this._init_header(this);\n",
" this._init_canvas(this);\n",
" this._init_toolbar(this);\n",
"\n",
" var fig = this;\n",
"\n",
" this.waiting = false;\n",
"\n",
" this.ws.onopen = function () {\n",
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
" fig.send_message(\"send_image_mode\", {});\n",
" fig.send_message(\"refresh\", {});\n",
" }\n",
"\n",
" this.imageObj.onload = function() {\n",
" if (fig.image_mode == 'full') {\n",
" // Full images could contain transparency (where diff images\n",
" // almost always do), so we need to clear the canvas so that\n",
" // there is no ghosting.\n",
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
" }\n",
" fig.context.drawImage(fig.imageObj, 0, 0);\n",
" };\n",
"\n",
" this.imageObj.onunload = function() {\n",
" this.ws.close();\n",
" }\n",
"\n",
" this.ws.onmessage = this._make_on_message_function(this);\n",
"\n",
" this.ondownload = ondownload;\n",
"}\n",
"\n",
"mpl.figure.prototype._init_header = function() {\n",
" var titlebar = $(\n",
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n",
" 'ui-helper-clearfix\"/>');\n",
" var titletext = $(\n",
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n",
" 'text-align: center; padding: 3px;\"/>');\n",
" titlebar.append(titletext)\n",
" this.root.append(titlebar);\n",
" this.header = titletext[0];\n",
"}\n",
"\n",
"\n",
"\n",
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
"\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
"\n",
"}\n",
"\n",
"mpl.figure.prototype._init_canvas = function() {\n",
" var fig = this;\n",
"\n",
" var canvas_div = $('<div/>');\n",
"\n",
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
"\n",
" function canvas_keyboard_event(event) {\n",
" return fig.key_event(event, event['data']);\n",
" }\n",
"\n",
" canvas_div.keydown('key_press', canvas_keyboard_event);\n",
" canvas_div.keyup('key_release', canvas_keyboard_event);\n",
" this.canvas_div = canvas_div\n",
" this._canvas_extra_style(canvas_div)\n",
" this.root.append(canvas_div);\n",
"\n",
" var canvas = $('<canvas/>');\n",
" canvas.addClass('mpl-canvas');\n",
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
"\n",
" this.canvas = canvas[0];\n",
" this.context = canvas[0].getContext(\"2d\");\n",
"\n",
" var rubberband = $('<canvas/>');\n",
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
"\n",
" var pass_mouse_events = true;\n",
"\n",
" canvas_div.resizable({\n",
" start: function(event, ui) {\n",
" pass_mouse_events = false;\n",
" },\n",
" resize: function(event, ui) {\n",
" fig.request_resize(ui.size.width, ui.size.height);\n",
" },\n",
" stop: function(event, ui) {\n",
" pass_mouse_events = true;\n",
" fig.request_resize(ui.size.width, ui.size.height);\n",
" },\n",
" });\n",
"\n",
" function mouse_event_fn(event) {\n",
" if (pass_mouse_events)\n",
" return fig.mouse_event(event, event['data']);\n",
" }\n",
"\n",
" rubberband.mousedown('button_press', mouse_event_fn);\n",
" rubberband.mouseup('button_release', mouse_event_fn);\n",
" // Throttle sequential mouse events to 1 every 20ms.\n",
" rubberband.mousemove('motion_notify', mouse_event_fn);\n",
"\n",
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
"\n",
" canvas_div.on(\"wheel\", function (event) {\n",
" event = event.originalEvent;\n",
" event['data'] = 'scroll'\n",
" if (event.deltaY < 0) {\n",
" event.step = 1;\n",
" } else {\n",
" event.step = -1;\n",
" }\n",
" mouse_event_fn(event);\n",
" });\n",
"\n",
" canvas_div.append(canvas);\n",
" canvas_div.append(rubberband);\n",
"\n",
" this.rubberband = rubberband;\n",
" this.rubberband_canvas = rubberband[0];\n",
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
" this.rubberband_context.strokeStyle = \"#000000\";\n",
"\n",
" this._resize_canvas = function(width, height) {\n",
" // Keep the size of the canvas, canvas container, and rubber band\n",
" // canvas in synch.\n",
" canvas_div.css('width', width)\n",
" canvas_div.css('height', height)\n",
"\n",
" canvas.attr('width', width);\n",
" canvas.attr('height', height);\n",
"\n",
" rubberband.attr('width', width);\n",
" rubberband.attr('height', height);\n",
" }\n",
"\n",
" // Set the figure to an initial 600x600px, this will subsequently be updated\n",
" // upon first draw.\n",
" this._resize_canvas(600, 600);\n",
"\n",
" // Disable right mouse context menu.\n",
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
" return false;\n",
" });\n",
"\n",
" function set_focus () {\n",
" canvas.focus();\n",
" canvas_div.focus();\n",
" }\n",
"\n",
" window.setTimeout(set_focus, 100);\n",
"}\n",
"\n",
"mpl.figure.prototype._init_toolbar = function() {\n",
" var fig = this;\n",
"\n",
" var nav_element = $('<div/>')\n",
" nav_element.attr('style', 'width: 100%');\n",
" this.root.append(nav_element);\n",
"\n",
" // Define a callback function for later on.\n",
" function toolbar_event(event) {\n",
" return fig.toolbar_button_onclick(event['data']);\n",
" }\n",
" function toolbar_mouse_event(event) {\n",
" return fig.toolbar_button_onmouseover(event['data']);\n",
" }\n",
"\n",
" for(var toolbar_ind in mpl.toolbar_items) {\n",
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
"\n",
" if (!name) {\n",
" // put a spacer in here.\n",
" continue;\n",
" }\n",
" var button = $('<button/>');\n",
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
" 'ui-button-icon-only');\n",
" button.attr('role', 'button');\n",
" button.attr('aria-disabled', 'false');\n",
" button.click(method_name, toolbar_event);\n",
" button.mouseover(tooltip, toolbar_mouse_event);\n",
"\n",
" var icon_img = $('<span/>');\n",
" icon_img.addClass('ui-button-icon-primary ui-icon');\n",
" icon_img.addClass(image);\n",
" icon_img.addClass('ui-corner-all');\n",
"\n",
" var tooltip_span = $('<span/>');\n",
" tooltip_span.addClass('ui-button-text');\n",
" tooltip_span.html(tooltip);\n",
"\n",
" button.append(icon_img);\n",
" button.append(tooltip_span);\n",
"\n",
" nav_element.append(button);\n",
" }\n",
"\n",
" var fmt_picker_span = $('<span/>');\n",
"\n",
" var fmt_picker = $('<select/>');\n",
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
" fmt_picker_span.append(fmt_picker);\n",
" nav_element.append(fmt_picker_span);\n",
" this.format_dropdown = fmt_picker[0];\n",
"\n",
" for (var ind in mpl.extensions) {\n",
" var fmt = mpl.extensions[ind];\n",
" var option = $(\n",
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n",
" fmt_picker.append(option)\n",
" }\n",
"\n",
" // Add hover states to the ui-buttons\n",
" $( \".ui-button\" ).hover(\n",
" function() { $(this).addClass(\"ui-state-hover\");},\n",
" function() { $(this).removeClass(\"ui-state-hover\");}\n",
" );\n",
"\n",
" var status_bar = $('<span class=\"mpl-message\"/>');\n",
" nav_element.append(status_bar);\n",
" this.message = status_bar[0];\n",
"}\n",
"\n",
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
" // which will in turn request a refresh of the image.\n",
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
"}\n",
"\n",
"mpl.figure.prototype.send_message = function(type, properties) {\n",
" properties['type'] = type;\n",
" properties['figure_id'] = this.id;\n",
" this.ws.send(JSON.stringify(properties));\n",
"}\n",
"\n",
"mpl.figure.prototype.send_draw_message = function() {\n",
" if (!this.waiting) {\n",
" this.waiting = true;\n",
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
" }\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
" var format_dropdown = fig.format_dropdown;\n",
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
" fig.ondownload(fig, format);\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
" var size = msg['size'];\n",
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
" fig._resize_canvas(size[0], size[1]);\n",
" fig.send_message(\"refresh\", {});\n",
" };\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
" var x0 = msg['x0'];\n",
" var y0 = fig.canvas.height - msg['y0'];\n",
" var x1 = msg['x1'];\n",
" var y1 = fig.canvas.height - msg['y1'];\n",
" x0 = Math.floor(x0) + 0.5;\n",
" y0 = Math.floor(y0) + 0.5;\n",
" x1 = Math.floor(x1) + 0.5;\n",
" y1 = Math.floor(y1) + 0.5;\n",
" var min_x = Math.min(x0, x1);\n",
" var min_y = Math.min(y0, y1);\n",
" var width = Math.abs(x1 - x0);\n",
" var height = Math.abs(y1 - y0);\n",
"\n",
" fig.rubberband_context.clearRect(\n",
" 0, 0, fig.canvas.width, fig.canvas.height);\n",
"\n",
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
" // Updates the figure title.\n",
" fig.header.textContent = msg['label'];\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
" var cursor = msg['cursor'];\n",
" switch(cursor)\n",
" {\n",
" case 0:\n",
" cursor = 'pointer';\n",
" break;\n",
" case 1:\n",
" cursor = 'default';\n",
" break;\n",
" case 2:\n",
" cursor = 'crosshair';\n",
" break;\n",
" case 3:\n",
" cursor = 'move';\n",
" break;\n",
" }\n",
" fig.rubberband_canvas.style.cursor = cursor;\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_message = function(fig, msg) {\n",
" fig.message.textContent = msg['message'];\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
" // Request the server to send over a new figure.\n",
" fig.send_draw_message();\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
" fig.image_mode = msg['mode'];\n",
"}\n",
"\n",
"mpl.figure.prototype.updated_canvas_event = function() {\n",
" // Called whenever the canvas gets updated.\n",
" this.send_message(\"ack\", {});\n",
"}\n",
"\n",
"// A function to construct a web socket function for onmessage handling.\n",
"// Called in the figure constructor.\n",
"mpl.figure.prototype._make_on_message_function = function(fig) {\n",
" return function socket_on_message(evt) {\n",
" if (evt.data instanceof Blob) {\n",
" /* FIXME: We get \"Resource interpreted as Image but\n",
" * transferred with MIME type text/plain:\" errors on\n",
" * Chrome. But how to set the MIME type? It doesn't seem\n",
" * to be part of the websocket stream */\n",
" evt.data.type = \"image/png\";\n",
"\n",
" /* Free the memory for the previous frames */\n",
" if (fig.imageObj.src) {\n",
" (window.URL || window.webkitURL).revokeObjectURL(\n",
" fig.imageObj.src);\n",
" }\n",
"\n",
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
" evt.data);\n",
" fig.updated_canvas_event();\n",
" fig.waiting = false;\n",
" return;\n",
" }\n",
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
" fig.imageObj.src = evt.data;\n",
" fig.updated_canvas_event();\n",
" fig.waiting = false;\n",
" return;\n",
" }\n",
"\n",
" var msg = JSON.parse(evt.data);\n",
" var msg_type = msg['type'];\n",
"\n",
" // Call the \"handle_{type}\" callback, which takes\n",
" // the figure and JSON message as its only arguments.\n",
" try {\n",
" var callback = fig[\"handle_\" + msg_type];\n",
" } catch (e) {\n",
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
" return;\n",
" }\n",
"\n",
" if (callback) {\n",
" try {\n",
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
" callback(fig, msg);\n",
" } catch (e) {\n",
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
" }\n",
" }\n",
" };\n",
"}\n",
"\n",
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
"mpl.findpos = function(e) {\n",
" //this section is from http://www.quirksmode.org/js/events_properties.html\n",
" var targ;\n",
" if (!e)\n",
" e = window.event;\n",
" if (e.target)\n",
" targ = e.target;\n",
" else if (e.srcElement)\n",
" targ = e.srcElement;\n",
" if (targ.nodeType == 3) // defeat Safari bug\n",
" targ = targ.parentNode;\n",
"\n",
" // jQuery normalizes the pageX and pageY\n",
" // pageX,Y are the mouse positions relative to the document\n",
" // offset() returns the position of the element relative to the document\n",
" var x = e.pageX - $(targ).offset().left;\n",
" var y = e.pageY - $(targ).offset().top;\n",
"\n",
" return {\"x\": x, \"y\": y};\n",
"};\n",
"\n",
"/*\n",
" * return a copy of an object with only non-object keys\n",
" * we need this to avoid circular references\n",
" * http://stackoverflow.com/a/24161582/3208463\n",
" */\n",
"function simpleKeys (original) {\n",
" return Object.keys(original).reduce(function (obj, key) {\n",
" if (typeof original[key] !== 'object')\n",
" obj[key] = original[key]\n",
" return obj;\n",
" }, {});\n",
"}\n",
"\n",
"mpl.figure.prototype.mouse_event = function(event, name) {\n",
" var canvas_pos = mpl.findpos(event)\n",
"\n",
" if (name === 'button_press')\n",
" {\n",
" this.canvas.focus();\n",
" this.canvas_div.focus();\n",
" }\n",
"\n",
" var x = canvas_pos.x;\n",
" var y = canvas_pos.y;\n",
"\n",
" this.send_message(name, {x: x, y: y, button: event.button,\n",
" step: event.step,\n",
" guiEvent: simpleKeys(event)});\n",
"\n",
" /* This prevents the web browser from automatically changing to\n",
" * the text insertion cursor when the button is pressed. We want\n",
" * to control all of the cursor setting manually through the\n",
" * 'cursor' event from matplotlib */\n",
" event.preventDefault();\n",
" return false;\n",
"}\n",
"\n",
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
" // Handle any extra behaviour associated with a key event\n",
"}\n",
"\n",
"mpl.figure.prototype.key_event = function(event, name) {\n",
"\n",
" // Prevent repeat events\n",
" if (name == 'key_press')\n",
" {\n",
" if (event.which === this._key)\n",
" return;\n",
" else\n",
" this._key = event.which;\n",
" }\n",
" if (name == 'key_release')\n",
" this._key = null;\n",
"\n",
" var value = '';\n",
" if (event.ctrlKey && event.which != 17)\n",
" value += \"ctrl+\";\n",
" if (event.altKey && event.which != 18)\n",
" value += \"alt+\";\n",
" if (event.shiftKey && event.which != 16)\n",
" value += \"shift+\";\n",
"\n",
" value += 'k';\n",
" value += event.which.toString();\n",
"\n",
" this._key_event_extra(event, name);\n",
"\n",
" this.send_message(name, {key: value,\n",
" guiEvent: simpleKeys(event)});\n",
" return false;\n",
"}\n",
"\n",
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
" if (name == 'download') {\n",
" this.handle_save(this, null);\n",
" } else {\n",
" this.send_message(\"toolbar_button\", {name: name});\n",
" }\n",
"};\n",
"\n",
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
" this.message.textContent = tooltip;\n",
"};\n",
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
"\n",
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
"\n",
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
" // Create a \"websocket\"-like object which calls the given IPython comm\n",
" // object with the appropriate methods. Currently this is a non binary\n",
" // socket, so there is still some room for performance tuning.\n",
" var ws = {};\n",
"\n",
" ws.close = function() {\n",
" comm.close()\n",
" };\n",
" ws.send = function(m) {\n",
" //console.log('sending', m);\n",
" comm.send(m);\n",
" };\n",
" // Register the callback with on_msg.\n",
" comm.on_msg(function(msg) {\n",
" //console.log('receiving', msg['content']['data'], msg);\n",
" // Pass the mpl event to the overriden (by mpl) onmessage function.\n",
" ws.onmessage(msg['content']['data'])\n",
" });\n",
" return ws;\n",
"}\n",
"\n",
"mpl.mpl_figure_comm = function(comm, msg) {\n",
" // This is the function which gets called when the mpl process\n",
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
"\n",
" var id = msg.content.data.id;\n",
" // Get hold of the div created by the display call when the Comm\n",
" // socket was opened in Python.\n",
" var element = $(\"#\" + id);\n",
" var ws_proxy = comm_websocket_adapter(comm)\n",
"\n",
" function ondownload(figure, format) {\n",
" window.open(figure.imageObj.src);\n",
" }\n",
"\n",
" var fig = new mpl.figure(id, ws_proxy,\n",
" ondownload,\n",
" element.get(0));\n",
"\n",
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
" // web socket which is closed, not our websocket->open comm proxy.\n",
" ws_proxy.onopen();\n",
"\n",
" fig.parent_element = element.get(0);\n",
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
" if (!fig.cell_info) {\n",
" console.error(\"Failed to find cell for figure\", id, fig);\n",
" return;\n",
" }\n",
"\n",
" var output_index = fig.cell_info[2]\n",
" var cell = fig.cell_info[0];\n",
"\n",
"};\n",
"\n",
"mpl.figure.prototype.handle_close = function(fig, msg) {\n",
" fig.root.unbind('remove')\n",
"\n",
" // Update the output cell to use the data from the current canvas.\n",
" fig.push_to_output();\n",
" var dataURL = fig.canvas.toDataURL();\n",
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
" // the notebook keyboard shortcuts fail.\n",
" IPython.keyboard_manager.enable()\n",
" $(fig.parent_element).html('<img src=\"' + dataURL + '\">');\n",
" fig.close_ws(fig, msg);\n",
"}\n",
"\n",
"mpl.figure.prototype.close_ws = function(fig, msg){\n",
" fig.send_message('closing', msg);\n",
" // fig.ws.close()\n",
"}\n",
"\n",
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
" // Turn the data on the canvas into data in the output cell.\n",
" var dataURL = this.canvas.toDataURL();\n",
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\">';\n",
"}\n",
"\n",
"mpl.figure.prototype.updated_canvas_event = function() {\n",
" // Tell IPython that the notebook contents must change.\n",
" IPython.notebook.set_dirty(true);\n",
" this.send_message(\"ack\", {});\n",
" var fig = this;\n",
" // Wait a second, then push the new image to the DOM so\n",
" // that it is saved nicely (might be nice to debounce this).\n",
" setTimeout(function () { fig.push_to_output() }, 1000);\n",
"}\n",
"\n",
"mpl.figure.prototype._init_toolbar = function() {\n",
" var fig = this;\n",
"\n",
" var nav_element = $('<div/>')\n",
" nav_element.attr('style', 'width: 100%');\n",
" this.root.append(nav_element);\n",
"\n",
" // Define a callback function for later on.\n",
" function toolbar_event(event) {\n",
" return fig.toolbar_button_onclick(event['data']);\n",
" }\n",
" function toolbar_mouse_event(event) {\n",
" return fig.toolbar_button_onmouseover(event['data']);\n",
" }\n",
"\n",
" for(var toolbar_ind in mpl.toolbar_items){\n",
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
"\n",
" if (!name) { continue; };\n",
"\n",
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n",
" button.click(method_name, toolbar_event);\n",
" button.mouseover(tooltip, toolbar_mouse_event);\n",
" nav_element.append(button);\n",
" }\n",
"\n",
" // Add the status bar.\n",
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n",
" nav_element.append(status_bar);\n",
" this.message = status_bar[0];\n",
"\n",
" // Add the close button to the window.\n",
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n",
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n",
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
" buttongrp.append(button);\n",
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
" titlebar.prepend(buttongrp);\n",
"}\n",
"\n",
"mpl.figure.prototype._root_extra_style = function(el){\n",
" var fig = this\n",
" el.on(\"remove\", function(){\n",
"\tfig.close_ws(fig, {});\n",
" });\n",
"}\n",
"\n",
"mpl.figure.prototype._canvas_extra_style = function(el){\n",
" // this is important to make the div 'focusable\n",
" el.attr('tabindex', 0)\n",
" // reach out to IPython and tell the keyboard manager to turn it's self\n",
" // off when our div gets focus\n",
"\n",
" // location in version 3\n",
" if (IPython.notebook.keyboard_manager) {\n",
" IPython.notebook.keyboard_manager.register_events(el);\n",
" }\n",
" else {\n",
" // location in version 2\n",
" IPython.keyboard_manager.register_events(el);\n",
" }\n",
"\n",
"}\n",
"\n",
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
" var manager = IPython.notebook.keyboard_manager;\n",
" if (!manager)\n",
" manager = IPython.keyboard_manager;\n",
"\n",
" // Check for shift+enter\n",
" if (event.shiftKey && event.which == 13) {\n",
" this.canvas_div.blur();\n",
" event.shiftKey = false;\n",
" // Send a \"J\" for go to next cell\n",
" event.which = 74;\n",
" event.keyCode = 74;\n",
" manager.command_mode();\n",
" manager.handle_keydown(event);\n",
" }\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
" fig.ondownload(fig, null);\n",
"}\n",
"\n",
"\n",
"mpl.find_output_cell = function(html_output) {\n",
" // Return the cell and output element which can be found *uniquely* in the notebook.\n",
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
" // IPython event is triggered only after the cells have been serialised, which for\n",
" // our purposes (turning an active figure into a static one), is too late.\n",
" var cells = IPython.notebook.get_cells();\n",
" var ncells = cells.length;\n",
" for (var i=0; i<ncells; i++) {\n",
" var cell = cells[i];\n",
" if (cell.cell_type === 'code'){\n",
" for (var j=0; j<cell.output_area.outputs.length; j++) {\n",
" var data = cell.output_area.outputs[j];\n",
" if (data.data) {\n",
" // IPython >= 3 moved mimebundle to data attribute of output\n",
" data = data.data;\n",
" }\n",
" if (data['text/html'] == html_output) {\n",
" return [cell, data, j];\n",
" }\n",
" }\n",
" }\n",
" }\n",
"}\n",
"\n",
"// Register the function which deals with the matplotlib target/channel.\n",
"// The kernel may be null if the page has been refreshed.\n",
"if (IPython.notebook.kernel != null) {\n",
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
"}\n"
],
"text/plain": [
"<IPython.core.display.Javascript object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"<img src=\"data:image/png;base64,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\">"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"<matplotlib.animation.ArtistAnimation at 0x472d3d0>"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def f(x):\n",
" return np.sign(x) \n",
"\n",
"make_fourier(f, 20, 'fourier03.gif')"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false,
"scrolled": true
},
"outputs": [
{
"data": {
"application/javascript": [
"/* Put everything inside the global mpl namespace */\n",
"window.mpl = {};\n",
"\n",
"mpl.get_websocket_type = function() {\n",
" if (typeof(WebSocket) !== 'undefined') {\n",
" return WebSocket;\n",
" } else if (typeof(MozWebSocket) !== 'undefined') {\n",
" return MozWebSocket;\n",
" } else {\n",
" alert('Your browser does not have WebSocket support.' +\n",
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
" 'Firefox 4 and 5 are also supported but you ' +\n",
" 'have to enable WebSockets in about:config.');\n",
" };\n",
"}\n",
"\n",
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
" this.id = figure_id;\n",
"\n",
" this.ws = websocket;\n",
"\n",
" this.supports_binary = (this.ws.binaryType != undefined);\n",
"\n",
" if (!this.supports_binary) {\n",
" var warnings = document.getElementById(\"mpl-warnings\");\n",
" if (warnings) {\n",
" warnings.style.display = 'block';\n",
" warnings.textContent = (\n",
" \"This browser does not support binary websocket messages. \" +\n",
" \"Performance may be slow.\");\n",
" }\n",
" }\n",
"\n",
" this.imageObj = new Image();\n",
"\n",
" this.context = undefined;\n",
" this.message = undefined;\n",
" this.canvas = undefined;\n",
" this.rubberband_canvas = undefined;\n",
" this.rubberband_context = undefined;\n",
" this.format_dropdown = undefined;\n",
"\n",
" this.image_mode = 'full';\n",
"\n",
" this.root = $('<div/>');\n",
" this._root_extra_style(this.root)\n",
" this.root.attr('style', 'display: inline-block');\n",
"\n",
" $(parent_element).append(this.root);\n",
"\n",
" this._init_header(this);\n",
" this._init_canvas(this);\n",
" this._init_toolbar(this);\n",
"\n",
" var fig = this;\n",
"\n",
" this.waiting = false;\n",
"\n",
" this.ws.onopen = function () {\n",
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
" fig.send_message(\"send_image_mode\", {});\n",
" fig.send_message(\"refresh\", {});\n",
" }\n",
"\n",
" this.imageObj.onload = function() {\n",
" if (fig.image_mode == 'full') {\n",
" // Full images could contain transparency (where diff images\n",
" // almost always do), so we need to clear the canvas so that\n",
" // there is no ghosting.\n",
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
" }\n",
" fig.context.drawImage(fig.imageObj, 0, 0);\n",
" };\n",
"\n",
" this.imageObj.onunload = function() {\n",
" this.ws.close();\n",
" }\n",
"\n",
" this.ws.onmessage = this._make_on_message_function(this);\n",
"\n",
" this.ondownload = ondownload;\n",
"}\n",
"\n",
"mpl.figure.prototype._init_header = function() {\n",
" var titlebar = $(\n",
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n",
" 'ui-helper-clearfix\"/>');\n",
" var titletext = $(\n",
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n",
" 'text-align: center; padding: 3px;\"/>');\n",
" titlebar.append(titletext)\n",
" this.root.append(titlebar);\n",
" this.header = titletext[0];\n",
"}\n",
"\n",
"\n",
"\n",
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
"\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
"\n",
"}\n",
"\n",
"mpl.figure.prototype._init_canvas = function() {\n",
" var fig = this;\n",
"\n",
" var canvas_div = $('<div/>');\n",
"\n",
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
"\n",
" function canvas_keyboard_event(event) {\n",
" return fig.key_event(event, event['data']);\n",
" }\n",
"\n",
" canvas_div.keydown('key_press', canvas_keyboard_event);\n",
" canvas_div.keyup('key_release', canvas_keyboard_event);\n",
" this.canvas_div = canvas_div\n",
" this._canvas_extra_style(canvas_div)\n",
" this.root.append(canvas_div);\n",
"\n",
" var canvas = $('<canvas/>');\n",
" canvas.addClass('mpl-canvas');\n",
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
"\n",
" this.canvas = canvas[0];\n",
" this.context = canvas[0].getContext(\"2d\");\n",
"\n",
" var rubberband = $('<canvas/>');\n",
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
"\n",
" var pass_mouse_events = true;\n",
"\n",
" canvas_div.resizable({\n",
" start: function(event, ui) {\n",
" pass_mouse_events = false;\n",
" },\n",
" resize: function(event, ui) {\n",
" fig.request_resize(ui.size.width, ui.size.height);\n",
" },\n",
" stop: function(event, ui) {\n",
" pass_mouse_events = true;\n",
" fig.request_resize(ui.size.width, ui.size.height);\n",
" },\n",
" });\n",
"\n",
" function mouse_event_fn(event) {\n",
" if (pass_mouse_events)\n",
" return fig.mouse_event(event, event['data']);\n",
" }\n",
"\n",
" rubberband.mousedown('button_press', mouse_event_fn);\n",
" rubberband.mouseup('button_release', mouse_event_fn);\n",
" // Throttle sequential mouse events to 1 every 20ms.\n",
" rubberband.mousemove('motion_notify', mouse_event_fn);\n",
"\n",
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
"\n",
" canvas_div.on(\"wheel\", function (event) {\n",
" event = event.originalEvent;\n",
" event['data'] = 'scroll'\n",
" if (event.deltaY < 0) {\n",
" event.step = 1;\n",
" } else {\n",
" event.step = -1;\n",
" }\n",
" mouse_event_fn(event);\n",
" });\n",
"\n",
" canvas_div.append(canvas);\n",
" canvas_div.append(rubberband);\n",
"\n",
" this.rubberband = rubberband;\n",
" this.rubberband_canvas = rubberband[0];\n",
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
" this.rubberband_context.strokeStyle = \"#000000\";\n",
"\n",
" this._resize_canvas = function(width, height) {\n",
" // Keep the size of the canvas, canvas container, and rubber band\n",
" // canvas in synch.\n",
" canvas_div.css('width', width)\n",
" canvas_div.css('height', height)\n",
"\n",
" canvas.attr('width', width);\n",
" canvas.attr('height', height);\n",
"\n",
" rubberband.attr('width', width);\n",
" rubberband.attr('height', height);\n",
" }\n",
"\n",
" // Set the figure to an initial 600x600px, this will subsequently be updated\n",
" // upon first draw.\n",
" this._resize_canvas(600, 600);\n",
"\n",
" // Disable right mouse context menu.\n",
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
" return false;\n",
" });\n",
"\n",
" function set_focus () {\n",
" canvas.focus();\n",
" canvas_div.focus();\n",
" }\n",
"\n",
" window.setTimeout(set_focus, 100);\n",
"}\n",
"\n",
"mpl.figure.prototype._init_toolbar = function() {\n",
" var fig = this;\n",
"\n",
" var nav_element = $('<div/>')\n",
" nav_element.attr('style', 'width: 100%');\n",
" this.root.append(nav_element);\n",
"\n",
" // Define a callback function for later on.\n",
" function toolbar_event(event) {\n",
" return fig.toolbar_button_onclick(event['data']);\n",
" }\n",
" function toolbar_mouse_event(event) {\n",
" return fig.toolbar_button_onmouseover(event['data']);\n",
" }\n",
"\n",
" for(var toolbar_ind in mpl.toolbar_items) {\n",
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
"\n",
" if (!name) {\n",
" // put a spacer in here.\n",
" continue;\n",
" }\n",
" var button = $('<button/>');\n",
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
" 'ui-button-icon-only');\n",
" button.attr('role', 'button');\n",
" button.attr('aria-disabled', 'false');\n",
" button.click(method_name, toolbar_event);\n",
" button.mouseover(tooltip, toolbar_mouse_event);\n",
"\n",
" var icon_img = $('<span/>');\n",
" icon_img.addClass('ui-button-icon-primary ui-icon');\n",
" icon_img.addClass(image);\n",
" icon_img.addClass('ui-corner-all');\n",
"\n",
" var tooltip_span = $('<span/>');\n",
" tooltip_span.addClass('ui-button-text');\n",
" tooltip_span.html(tooltip);\n",
"\n",
" button.append(icon_img);\n",
" button.append(tooltip_span);\n",
"\n",
" nav_element.append(button);\n",
" }\n",
"\n",
" var fmt_picker_span = $('<span/>');\n",
"\n",
" var fmt_picker = $('<select/>');\n",
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
" fmt_picker_span.append(fmt_picker);\n",
" nav_element.append(fmt_picker_span);\n",
" this.format_dropdown = fmt_picker[0];\n",
"\n",
" for (var ind in mpl.extensions) {\n",
" var fmt = mpl.extensions[ind];\n",
" var option = $(\n",
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n",
" fmt_picker.append(option)\n",
" }\n",
"\n",
" // Add hover states to the ui-buttons\n",
" $( \".ui-button\" ).hover(\n",
" function() { $(this).addClass(\"ui-state-hover\");},\n",
" function() { $(this).removeClass(\"ui-state-hover\");}\n",
" );\n",
"\n",
" var status_bar = $('<span class=\"mpl-message\"/>');\n",
" nav_element.append(status_bar);\n",
" this.message = status_bar[0];\n",
"}\n",
"\n",
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
" // which will in turn request a refresh of the image.\n",
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
"}\n",
"\n",
"mpl.figure.prototype.send_message = function(type, properties) {\n",
" properties['type'] = type;\n",
" properties['figure_id'] = this.id;\n",
" this.ws.send(JSON.stringify(properties));\n",
"}\n",
"\n",
"mpl.figure.prototype.send_draw_message = function() {\n",
" if (!this.waiting) {\n",
" this.waiting = true;\n",
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
" }\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
" var format_dropdown = fig.format_dropdown;\n",
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
" fig.ondownload(fig, format);\n",
"}\n",
"\n",
"\n",
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
" var size = msg['size'];\n",
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
" fig._resize_canvas(size[0], size[1]);\n",
" fig.send_message(\"refresh\", {});\n",
" };\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
" var x0 = msg['x0'];\n",
" var y0 = fig.canvas.height - msg['y0'];\n",
" var x1 = msg['x1'];\n",
" var y1 = fig.canvas.height - msg['y1'];\n",
" x0 = Math.floor(x0) + 0.5;\n",
" y0 = Math.floor(y0) + 0.5;\n",
" x1 = Math.floor(x1) + 0.5;\n",
" y1 = Math.floor(y1) + 0.5;\n",
" var min_x = Math.min(x0, x1);\n",
" var min_y = Math.min(y0, y1);\n",
" var width = Math.abs(x1 - x0);\n",
" var height = Math.abs(y1 - y0);\n",
"\n",
" fig.rubberband_context.clearRect(\n",
" 0, 0, fig.canvas.width, fig.canvas.height);\n",
"\n",
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
" // Updates the figure title.\n",
" fig.header.textContent = msg['label'];\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
" var cursor = msg['cursor'];\n",
" switch(cursor)\n",
" {\n",
" case 0:\n",
" cursor = 'pointer';\n",
" break;\n",
" case 1:\n",
" cursor = 'default';\n",
" break;\n",
" case 2:\n",
" cursor = 'crosshair';\n",
" break;\n",
" case 3:\n",
" cursor = 'move';\n",
" break;\n",
" }\n",
" fig.rubberband_canvas.style.cursor = cursor;\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_message = function(fig, msg) {\n",
" fig.message.textContent = msg['message'];\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
" // Request the server to send over a new figure.\n",
" fig.send_draw_message();\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
" fig.image_mode = msg['mode'];\n",
"}\n",
"\n",
"mpl.figure.prototype.updated_canvas_event = function() {\n",
" // Called whenever the canvas gets updated.\n",
" this.send_message(\"ack\", {});\n",
"}\n",
"\n",
"// A function to construct a web socket function for onmessage handling.\n",
"// Called in the figure constructor.\n",
"mpl.figure.prototype._make_on_message_function = function(fig) {\n",
" return function socket_on_message(evt) {\n",
" if (evt.data instanceof Blob) {\n",
" /* FIXME: We get \"Resource interpreted as Image but\n",
" * transferred with MIME type text/plain:\" errors on\n",
" * Chrome. But how to set the MIME type? It doesn't seem\n",
" * to be part of the websocket stream */\n",
" evt.data.type = \"image/png\";\n",
"\n",
" /* Free the memory for the previous frames */\n",
" if (fig.imageObj.src) {\n",
" (window.URL || window.webkitURL).revokeObjectURL(\n",
" fig.imageObj.src);\n",
" }\n",
"\n",
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
" evt.data);\n",
" fig.updated_canvas_event();\n",
" fig.waiting = false;\n",
" return;\n",
" }\n",
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
" fig.imageObj.src = evt.data;\n",
" fig.updated_canvas_event();\n",
" fig.waiting = false;\n",
" return;\n",
" }\n",
"\n",
" var msg = JSON.parse(evt.data);\n",
" var msg_type = msg['type'];\n",
"\n",
" // Call the \"handle_{type}\" callback, which takes\n",
" // the figure and JSON message as its only arguments.\n",
" try {\n",
" var callback = fig[\"handle_\" + msg_type];\n",
" } catch (e) {\n",
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
" return;\n",
" }\n",
"\n",
" if (callback) {\n",
" try {\n",
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
" callback(fig, msg);\n",
" } catch (e) {\n",
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
" }\n",
" }\n",
" };\n",
"}\n",
"\n",
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
"mpl.findpos = function(e) {\n",
" //this section is from http://www.quirksmode.org/js/events_properties.html\n",
" var targ;\n",
" if (!e)\n",
" e = window.event;\n",
" if (e.target)\n",
" targ = e.target;\n",
" else if (e.srcElement)\n",
" targ = e.srcElement;\n",
" if (targ.nodeType == 3) // defeat Safari bug\n",
" targ = targ.parentNode;\n",
"\n",
" // jQuery normalizes the pageX and pageY\n",
" // pageX,Y are the mouse positions relative to the document\n",
" // offset() returns the position of the element relative to the document\n",
" var x = e.pageX - $(targ).offset().left;\n",
" var y = e.pageY - $(targ).offset().top;\n",
"\n",
" return {\"x\": x, \"y\": y};\n",
"};\n",
"\n",
"/*\n",
" * return a copy of an object with only non-object keys\n",
" * we need this to avoid circular references\n",
" * http://stackoverflow.com/a/24161582/3208463\n",
" */\n",
"function simpleKeys (original) {\n",
" return Object.keys(original).reduce(function (obj, key) {\n",
" if (typeof original[key] !== 'object')\n",
" obj[key] = original[key]\n",
" return obj;\n",
" }, {});\n",
"}\n",
"\n",
"mpl.figure.prototype.mouse_event = function(event, name) {\n",
" var canvas_pos = mpl.findpos(event)\n",
"\n",
" if (name === 'button_press')\n",
" {\n",
" this.canvas.focus();\n",
" this.canvas_div.focus();\n",
" }\n",
"\n",
" var x = canvas_pos.x;\n",
" var y = canvas_pos.y;\n",
"\n",
" this.send_message(name, {x: x, y: y, button: event.button,\n",
" step: event.step,\n",
" guiEvent: simpleKeys(event)});\n",
"\n",
" /* This prevents the web browser from automatically changing to\n",
" * the text insertion cursor when the button is pressed. We want\n",
" * to control all of the cursor setting manually through the\n",
" * 'cursor' event from matplotlib */\n",
" event.preventDefault();\n",
" return false;\n",
"}\n",
"\n",
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
" // Handle any extra behaviour associated with a key event\n",
"}\n",
"\n",
"mpl.figure.prototype.key_event = function(event, name) {\n",
"\n",
" // Prevent repeat events\n",
" if (name == 'key_press')\n",
" {\n",
" if (event.which === this._key)\n",
" return;\n",
" else\n",
" this._key = event.which;\n",
" }\n",
" if (name == 'key_release')\n",
" this._key = null;\n",
"\n",
" var value = '';\n",
" if (event.ctrlKey && event.which != 17)\n",
" value += \"ctrl+\";\n",
" if (event.altKey && event.which != 18)\n",
" value += \"alt+\";\n",
" if (event.shiftKey && event.which != 16)\n",
" value += \"shift+\";\n",
"\n",
" value += 'k';\n",
" value += event.which.toString();\n",
"\n",
" this._key_event_extra(event, name);\n",
"\n",
" this.send_message(name, {key: value,\n",
" guiEvent: simpleKeys(event)});\n",
" return false;\n",
"}\n",
"\n",
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
" if (name == 'download') {\n",
" this.handle_save(this, null);\n",
" } else {\n",
" this.send_message(\"toolbar_button\", {name: name});\n",
" }\n",
"};\n",
"\n",
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
" this.message.textContent = tooltip;\n",
"};\n",
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
"\n",
"mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
"\n",
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
" // Create a \"websocket\"-like object which calls the given IPython comm\n",
" // object with the appropriate methods. Currently this is a non binary\n",
" // socket, so there is still some room for performance tuning.\n",
" var ws = {};\n",
"\n",
" ws.close = function() {\n",
" comm.close()\n",
" };\n",
" ws.send = function(m) {\n",
" //console.log('sending', m);\n",
" comm.send(m);\n",
" };\n",
" // Register the callback with on_msg.\n",
" comm.on_msg(function(msg) {\n",
" //console.log('receiving', msg['content']['data'], msg);\n",
" // Pass the mpl event to the overriden (by mpl) onmessage function.\n",
" ws.onmessage(msg['content']['data'])\n",
" });\n",
" return ws;\n",
"}\n",
"\n",
"mpl.mpl_figure_comm = function(comm, msg) {\n",
" // This is the function which gets called when the mpl process\n",
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
"\n",
" var id = msg.content.data.id;\n",
" // Get hold of the div created by the display call when the Comm\n",
" // socket was opened in Python.\n",
" var element = $(\"#\" + id);\n",
" var ws_proxy = comm_websocket_adapter(comm)\n",
"\n",
" function ondownload(figure, format) {\n",
" window.open(figure.imageObj.src);\n",
" }\n",
"\n",
" var fig = new mpl.figure(id, ws_proxy,\n",
" ondownload,\n",
" element.get(0));\n",
"\n",
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
" // web socket which is closed, not our websocket->open comm proxy.\n",
" ws_proxy.onopen();\n",
"\n",
" fig.parent_element = element.get(0);\n",
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
" if (!fig.cell_info) {\n",
" console.error(\"Failed to find cell for figure\", id, fig);\n",
" return;\n",
" }\n",
"\n",
" var output_index = fig.cell_info[2]\n",
" var cell = fig.cell_info[0];\n",
"\n",
"};\n",
"\n",
"mpl.figure.prototype.handle_close = function(fig, msg) {\n",
" fig.root.unbind('remove')\n",
"\n",
" // Update the output cell to use the data from the current canvas.\n",
" fig.push_to_output();\n",
" var dataURL = fig.canvas.toDataURL();\n",
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
" // the notebook keyboard shortcuts fail.\n",
" IPython.keyboard_manager.enable()\n",
" $(fig.parent_element).html('<img src=\"' + dataURL + '\">');\n",
" fig.close_ws(fig, msg);\n",
"}\n",
"\n",
"mpl.figure.prototype.close_ws = function(fig, msg){\n",
" fig.send_message('closing', msg);\n",
" // fig.ws.close()\n",
"}\n",
"\n",
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
" // Turn the data on the canvas into data in the output cell.\n",
" var dataURL = this.canvas.toDataURL();\n",
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\">';\n",
"}\n",
"\n",
"mpl.figure.prototype.updated_canvas_event = function() {\n",
" // Tell IPython that the notebook contents must change.\n",
" IPython.notebook.set_dirty(true);\n",
" this.send_message(\"ack\", {});\n",
" var fig = this;\n",
" // Wait a second, then push the new image to the DOM so\n",
" // that it is saved nicely (might be nice to debounce this).\n",
" setTimeout(function () { fig.push_to_output() }, 1000);\n",
"}\n",
"\n",
"mpl.figure.prototype._init_toolbar = function() {\n",
" var fig = this;\n",
"\n",
" var nav_element = $('<div/>')\n",
" nav_element.attr('style', 'width: 100%');\n",
" this.root.append(nav_element);\n",
"\n",
" // Define a callback function for later on.\n",
" function toolbar_event(event) {\n",
" return fig.toolbar_button_onclick(event['data']);\n",
" }\n",
" function toolbar_mouse_event(event) {\n",
" return fig.toolbar_button_onmouseover(event['data']);\n",
" }\n",
"\n",
" for(var toolbar_ind in mpl.toolbar_items){\n",
" var name = mpl.toolbar_items[toolbar_ind][0];\n",
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
" var image = mpl.toolbar_items[toolbar_ind][2];\n",
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
"\n",
" if (!name) { continue; };\n",
"\n",
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n",
" button.click(method_name, toolbar_event);\n",
" button.mouseover(tooltip, toolbar_mouse_event);\n",
" nav_element.append(button);\n",
" }\n",
"\n",
" // Add the status bar.\n",
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n",
" nav_element.append(status_bar);\n",
" this.message = status_bar[0];\n",
"\n",
" // Add the close button to the window.\n",
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n",
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n",
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
" buttongrp.append(button);\n",
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
" titlebar.prepend(buttongrp);\n",
"}\n",
"\n",
"mpl.figure.prototype._root_extra_style = function(el){\n",
" var fig = this\n",
" el.on(\"remove\", function(){\n",
"\tfig.close_ws(fig, {});\n",
" });\n",
"}\n",
"\n",
"mpl.figure.prototype._canvas_extra_style = function(el){\n",
" // this is important to make the div 'focusable\n",
" el.attr('tabindex', 0)\n",
" // reach out to IPython and tell the keyboard manager to turn it's self\n",
" // off when our div gets focus\n",
"\n",
" // location in version 3\n",
" if (IPython.notebook.keyboard_manager) {\n",
" IPython.notebook.keyboard_manager.register_events(el);\n",
" }\n",
" else {\n",
" // location in version 2\n",
" IPython.keyboard_manager.register_events(el);\n",
" }\n",
"\n",
"}\n",
"\n",
"mpl.figure.prototype._key_event_extra = function(event, name) {\n",
" var manager = IPython.notebook.keyboard_manager;\n",
" if (!manager)\n",
" manager = IPython.keyboard_manager;\n",
"\n",
" // Check for shift+enter\n",
" if (event.shiftKey && event.which == 13) {\n",
" this.canvas_div.blur();\n",
" event.shiftKey = false;\n",
" // Send a \"J\" for go to next cell\n",
" event.which = 74;\n",
" event.keyCode = 74;\n",
" manager.command_mode();\n",
" manager.handle_keydown(event);\n",
" }\n",
"}\n",
"\n",
"mpl.figure.prototype.handle_save = function(fig, msg) {\n",
" fig.ondownload(fig, null);\n",
"}\n",
"\n",
"\n",
"mpl.find_output_cell = function(html_output) {\n",
" // Return the cell and output element which can be found *uniquely* in the notebook.\n",
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
" // IPython event is triggered only after the cells have been serialised, which for\n",
" // our purposes (turning an active figure into a static one), is too late.\n",
" var cells = IPython.notebook.get_cells();\n",
" var ncells = cells.length;\n",
" for (var i=0; i<ncells; i++) {\n",
" var cell = cells[i];\n",
" if (cell.cell_type === 'code'){\n",
" for (var j=0; j<cell.output_area.outputs.length; j++) {\n",
" var data = cell.output_area.outputs[j];\n",
" if (data.data) {\n",
" // IPython >= 3 moved mimebundle to data attribute of output\n",
" data = data.data;\n",
" }\n",
" if (data['text/html'] == html_output) {\n",
" return [cell, data, j];\n",
" }\n",
" }\n",
" }\n",
" }\n",
"}\n",
"\n",
"// Register the function which deals with the matplotlib target/channel.\n",
"// The kernel may be null if the page has been refreshed.\n",
"if (IPython.notebook.kernel != null) {\n",
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
"}\n"
],
"text/plain": [
"<IPython.core.display.Javascript object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"<img src=\"data:image/png;base64,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\">"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"<matplotlib.animation.ArtistAnimation at 0x4f71390>"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def f(x):\n",
" return (x-3)*(x+3)*(x-1)*(x+1)\n",
"\n",
"make_fourier(f, 20, 'fourier04.gif')"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.5"
}
},
"nbformat": 4,
"nbformat_minor": 0
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment