Skip to content

Instantly share code, notes, and snippets.

@pedritomelenas
Created November 26, 2015 23:06
Show Gist options
  • Save pedritomelenas/193cf7ebe274058a312d to your computer and use it in GitHub Desktop.
Save pedritomelenas/193cf7ebe274058a312d to your computer and use it in GitHub Desktop.
Prueba de animación
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/Users/pedro/lib/anaconda/lib/python2.7/site-packages/IPython/kernel/__init__.py:13: ShimWarning: The `IPython.kernel` package has been deprecated. You should import from ipykernel or jupyter_client instead.\n",
" \"You should import from ipykernel or jupyter_client instead.\", ShimWarning)\n"
]
}
],
"source": [
"%matplotlib notebook\n",
"#%matplotlib inline"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"from numpy import sin, cos\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"import matplotlib.animation as animation"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"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",
" fig.waiting = false;\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",
" 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",
" 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",
"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",
"\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",
" 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",
" // 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.send_message('closing', {});\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-danger\" href=\"#\" title=\"Close figure\"><i class=\"fa fa-times icon-remove icon-large\"></i></button>');\n",
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
" button.mouseover('Close figure', toolbar_mouse_event);\n",
" buttongrp.append(button);\n",
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
" titlebar.prepend(buttongrp);\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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naVr166yZs0a2bp1q4wYMUIqVqyYa7X9+vWT8uXLy4oVK2T9+vXSunVrOf/883Odny8QQAABBBBAIG8BEsC8fVzx7YABA+Srr76SJ554QipVqiQpKSlOu4YPHy7169c/1MZrr71WLFkaNGiQ1KhRwzli9tRTT8nq1aslNTXVWa5Vq1byzTffHFrG3rzxxhvStm1b52icJVfvvvvuYd/H8sPQoUPl7LPPljvuuONQ0mfrrFChQq6rsaTxyiuvdOZJTk6W66+/XtauXSu///67s8wPP/wgZ555plStWlWqVasm7du3lyVLluRaH18ggAACCCAQdAESQA9sAS+++KKcfvrp8r//+7+ybds2ycjIyLXVY8eOlXPOOcdJjl599VUn0brmmmvkySefdI62devWTSxRDBVLIu3U8muvveZ8/9JLL0n//v1l+vTpoVmOeD3hhBOcZMsSrpzCEtXcyqeffuokp5YE2unc448/Xqx/eZUhQ4bIm2++Kenp6bJz506xNp522mlOgmvLWcJr/dq0aZNs3LjR6YudXqYggAACCCCAQM4CpXKezFSvCliiePHFFzvNv/DCC52jenb0z478Wbnqqqvk6aefdhJJO5poieG9994rJ554ovO9nZ7t06ePWGJo73Mq8+bNy2lygabZUTs75Tt+/HjnNLAlmj169HCO3Nl6cyp2zd+YMWOceexUeOPGjWXChAmHZi1TpoysXLnSOUV8zDHHiCWoFAQQQAABBBDIXYAjgLnbePKb2rVrH9ZuO7UaPi10qtWOJFqxGyzuvPPOw47kjRo1yrnW7rCKYvShcuXKYompHQEsUaKEc2Tz8ssvl9GjR+e4BrtG0K4XPOuss2T79u2ya9cuufvuu53k1K4HtGLJqiWGNo+dErfTyzt27MixPiYigAACCCCAgAgJoEe2AkuW8iuWBB04cCC/2Q773pLDF154QbZs2XIoLDkMP8J22AL6wY4m2tHD3OLxxx+PXOTQ59CRxkMT9I212dqeU7Hr/ywJtKSvXLlyUrp0aecUtS0zbdo0Z5EGDRrIyy+/7BwBnDp1qnzyySfy2GOP5VQd0xBAAAEEEEBABfLPKmByhcDRRx8tP/30U55tKWzyZ5Xdfvvt8uCDD8rs2bNl//79smfPHpk1a5Z89913ua5r4cKFzilkSxRzinvuuSfXZW+99VYZN26cWKJm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i3wJ5HiTAB7cDIbri/3rILeYcnC2w/4/br8sctha4vthuFafW59tek79LmiL3tcZW2gcpWGP47EE4VMNN/zhGK7tiEe/U7ReK1sPvhz6/y36zp43VdxluDagIP22fuTUh1D/cuqHm8c7p/aG+pJTP3MaK5s/pz8etnyorpzW47ZpobYWtN/W/sEaTTXs1LedQk3VGKbh5+KX8S7KGPltvM9RhPs1BuSBEcjxtvPjFJFbFOHOPCD2Rnxn/zG0639GadiGlVvJyP6iSsQM9h/SNRHTiuNjYftdmDYuDJt5nb6/XsP+gNqvrFgiWJwlXv32y3hbP6pGDJB9DvUv4ivno5vHO6f2hvpS0H3T5rejZ5HFlg/VFfmdGz+H2lrQflsfvg7ryDx9f7vGZxplNOyoqB+LX8a7KGPjp/G+QAHe1LhCY3IeGEEe7zxY+CpS4ASdYAnNkMgvcvn8i06/New7S7rttKhtkF4sdgTprCI0PFmX2aVhN9F4sRS0334Y79d0gD6IGKQx+vmViGl5ffTCeBdmrOzUvm0Dx4d1uk32NK9dA1iYfod199DbM/TdXo2yh6Z4600Xba6NZV5nwPw03qHRKUi/Q/OGv3p1vO1v7FaNc8I7k8t7P453Ll1lclEF7FTmZo3bClGBX35ZxP5jX07D/sN5roZ9LqmRW+mjX9idklZqabyh8YuGG04BazMKXArbbz+M98mqY8n6RRqlNS7RyO8uYC+Od2HHaoI6pGnYdl1Dw45kj9XwWilMv+0SDtvfbb9N0milYdeD2il/rxVL+Gx/7q5h/x0rn/3Z+pVT8ct4F6bffhnvP+uA2t/qzjkNbC7T/DLeuXSPydEKTNEK9mlsi4jwXxJZoN+Ff9aPnv9lkUbaB/sPpoU96y30/n59HyqR/f5Qv7AjnTs07HS33QRyjIaXSiNtbKivBe239e8BjfUa1vepGvZH02vlUm3wIg1LBH/U6K0RXvwy3rmNVQPtrO3n4X9A7DS4bcd2VMHCTi2laHixFLTf5jBTw/prHks1Htfw2j/ktMlyrUZO+/MZOt3P412YfvtlvG2c7fKEyL/Vof3Zz+Ot3aYggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAgh4Q+D/AWlaHsuE9GTsAAAAAElFTkSuQmCC\">"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"\n",
"\n",
"L1 = 1.0 # length of pendulum 1 in m\n",
"L2 = 1.0 # length of pendulum 2 in m\n",
"\n",
"\n",
"# create a time array from 0..100 sampled at 0.05 second steps\n",
"dt = 0.05\n",
"t = np.arange(0.0, 20, dt)\n",
"\n",
"\n",
"\n",
"x1 = L1*sin(t)\n",
"y1 = -L1*cos(t)\n",
"\n",
"x2 = x1 #L2*cos(y[:, 2]) + x1\n",
"y2 = y1 #-L2*cos(y[:, 2]) + y1\n",
"\n",
"\n",
"\n",
"fig = plt.figure()\n",
"ax = fig.add_subplot(111, autoscale_on=False, xlim=(-2, 2), ylim=(-2, 2))\n",
"\n",
"circ=plt.Circle((0,0), radius=L1, color='g', fill=False)\n",
"ax.add_patch(circ)\n",
"#ax.grid()\n",
"\n",
"line, = ax.plot([], [], '> ' , lw=2)\n",
"time_template = 'time = %.1fs'\n",
"time_text = ax.text(0.05, 0.9, '', transform=ax.transAxes)\n",
"\n",
"\n",
"def init():\n",
" line.set_data([], [])\n",
" time_text.set_text('')\n",
" return line, time_text\n",
"\n",
"\n",
"def animate(i):\n",
" thisx = [0, x1[i], x2[i]]\n",
" thisy = [0, y1[i], y2[i]]\n",
"\n",
" line.set_data(thisx, thisy)\n",
" time_text.set_text(time_template % (i*dt))\n",
" return line, time_text\n",
"\n",
"ani = animation.FuncAnimation(fig, animate, np.arange(1, 200),\n",
" interval=25, blit=True, init_func=init)\n",
"plt.show()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": []
}
],
"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.10"
}
},
"nbformat": 4,
"nbformat_minor": 0
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment