Created
November 26, 2015 23:13
-
-
Save pedritomelenas/996b999a5c249249643e to your computer and use it in GitHub Desktop.
otra prueba
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| "cells": [ | |
| { | |
| "cell_type": "code", | |
| "execution_count": 1, | |
| "metadata": { | |
| "collapsed": false | |
| }, | |
| "outputs": [], | |
| "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, | |
| "scrolled": 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", | |
| " };\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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9vN4azcpCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAAC3hD4f1k2GxVYBCRJAAAAAElFTkSuQmCC\">" | |
| ], | |
| "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 3", | |
| "language": "python", | |
| "name": "python3" | |
| }, | |
| "language_info": { | |
| "codemirror_mode": { | |
| "name": "ipython", | |
| "version": 3 | |
| }, | |
| "file_extension": ".py", | |
| "mimetype": "text/x-python", | |
| "name": "python", | |
| "nbconvert_exporter": "python", | |
| "pygments_lexer": "ipython3", | |
| "version": "3.4.3" | |
| } | |
| }, | |
| "nbformat": 4, | |
| "nbformat_minor": 0 | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment