{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Plotting with Matplotlib" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Overview\n", "Matplotlib is a package that was designed for plotting in a way that is very similar to Matlab. I would say it's biggest difference is the loss of interactive plotting/rescaling. This means you need to be a little smarter with how you plot your data. In particular, decoupling your data generation and plotting code is a good idea. Then you can re-run your plotting code frequently without regenerating data.\n", "\n", "### Resources\n", "- The [matplotlib gallery](https://matplotlib.org/gallery.html) has a lot of pictures of different plot types" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Preliminaries" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We need to specify for jupyter that we want our plots in-line, otherwise the plots won't show. You don't need this line in Spyder." ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": true }, "outputs": [], "source": [ "%matplotlib notebook" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, as always, we first need to import the module. Matplotlib comes with a bunch of different things under the hood, but 99% of the time you will use its submodule `pyplot`. Thus, I pretty much always import like this:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt # it's tradition to import it as plt\n", "import numpy as np # will use numpy to generate fake data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Creating Figures, Axes, and Subplots" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Matplotlib won't redraw a figure's size if it has already been created, so I recommend closing all figures at the beginning of scripts if you need one with a specific size." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.close('all') # close all figures" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, we create a figure using the `plt.figure()` command. A list of possible figure options can be found [here](https://matplotlib.org/api/pyplot_api.html?highlight=matplotlib%20pyplot%20figure#matplotlib.pyplot.figure)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig1 = plt.figure(num=1, figsize=(6,3)) # assign figure handle to variable `fig`\n", "plt.clf() # clear the figure in case it was already created (this prints the figure handle in jupyter)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can add a single axis to the current figure..." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "application/javascript": [ "/* Put everything inside the global mpl namespace */\n", "window.mpl = {};\n", "\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", " if (mpl.ratio != 1) {\n", " fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", " }\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 backingStore = this.context.backingStorePixelRatio ||\n", "\tthis.context.webkitBackingStorePixelRatio ||\n", "\tthis.context.mozBackingStorePixelRatio ||\n", "\tthis.context.msBackingStorePixelRatio ||\n", "\tthis.context.oBackingStorePixelRatio ||\n", "\tthis.context.backingStorePixelRatio || 1;\n", "\n", " mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\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 * mpl.ratio);\n", " canvas.attr('height', height * mpl.ratio);\n", " canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\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'] / mpl.ratio;\n", " var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n", " var x1 = msg['x1'] / mpl.ratio;\n", " var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\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 * mpl.ratio;\n", " var y = canvas_pos.y * mpl.ratio;\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\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\"];\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", " var width = fig.canvas.width/mpl.ratio\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 + '\" width=\"' + width + '\">');\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 width = this.canvas.width/mpl.ratio\n", " var dataURL = this.canvas.toDataURL();\n", " this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\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", " // select the cell after this one\n", " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", " IPython.notebook.select(index + 1);\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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\" width=\"432\">" ], "text/plain": [ "<IPython.core.display.HTML object>" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "ax1 = plt.axes() # create axes in the current figure" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Or add many subplots to the figure using `plt.subplot(n_x, n_y, plot_i)`. Note that, unlike pretty much everything else in python, the subplot index starts at 1. (This is to match Matlab's indexing.)" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "application/javascript": [ "/* Put everything inside the global mpl namespace */\n", "window.mpl = {};\n", "\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", " if (mpl.ratio != 1) {\n", " fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", " }\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 backingStore = this.context.backingStorePixelRatio ||\n", "\tthis.context.webkitBackingStorePixelRatio ||\n", "\tthis.context.mozBackingStorePixelRatio ||\n", "\tthis.context.msBackingStorePixelRatio ||\n", "\tthis.context.oBackingStorePixelRatio ||\n", "\tthis.context.backingStorePixelRatio || 1;\n", "\n", " mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\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 * mpl.ratio);\n", " canvas.attr('height', height * mpl.ratio);\n", " canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\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'] / mpl.ratio;\n", " var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n", " var x1 = msg['x1'] / mpl.ratio;\n", " var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\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 * mpl.ratio;\n", " var y = canvas_pos.y * mpl.ratio;\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\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\"];\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", " var width = fig.canvas.width/mpl.ratio\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 + '\" width=\"' + width + '\">');\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 width = this.canvas.width/mpl.ratio\n", " var dataURL = this.canvas.toDataURL();\n", " this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\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", " // select the cell after this one\n", " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", " IPython.notebook.select(index + 1);\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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\" width=\"144\">" ], "text/plain": [ "<IPython.core.display.HTML object>" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "fig2 = plt.figure(2, figsize=(2,2)) # create a second figure\n", "axs = [plt.subplot(2, 2, i) for i in range(1, 5)] # create a list of the handles to subplot axes handles" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the above line, I use Python's \"list comprehension\" to create my list of axes handles on one line. List comprehension is basically a one-line for loop with some optional logic if you want it. Here's a quick demo." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = ['cat', 'dog', 'bird', 'parrot', 'mouse', 'avocado']\n", "[print(x[i], '(l.c.)') for i in range(len(x)) if len(x[i]) < 6] # print using list comprehension\n", "for i in range(len(x)):\n", " if len(x[i]) < 6:\n", " print(x[i], '(f.l.)')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Plotting: 2D and 3D" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can do pretty much everything in matplotlib you would do in Matlab.\n", "\n", "Line plots... " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.plot(np.arange(4), np.arange(4))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Scatter plots..." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.scatter(np.random.rand(5), np.random.rand(5), label='data 1') # random data 1\n", "plt.scatter(np.random.rand(5), np.random.rand(5), label='data 2') # random data 2\n", "plt.legend() # add a legend (google `pyplot legend` for many more legend options)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Contour plots..." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "<matplotlib.contour.QuadContourSet at 0x873f278>" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAW4AAAD8CAYAAABXe05zAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAGPBJREFUeJzt3V+sXWWZx/HvQ8sp5VBbAvUf5Z84oTIkCjYMihIHxIAS\nTIwXkGiiGeeY6DigkxidC4kXM8kkxjgXE5MT659ExMECyQRnGDCixouptgWkpdVYLPUgWIwD1NJp\nOfjMxd6Hnp5/e+2911rv877r90kaT+nuOa9rv+vb56y999nm7oiISD5OSb0AEREZjsItIpIZhVtE\nJDMKt4hIZhRuEZHMKNwiIpmpFG4zu9XMdpvZHjO7relFiYjI8gaG28wuBf4WuAJ4M3Cjmb2x6YWJ\niMjSqkzcbwK2u/uL7j4L/Bj4QLPLEhGR5ayucJvdwD+Z2VnAUeC9wI6FNzKzKWAKYGLtqreefeEZ\nda5zSS/Mntb414jm+PEqd5mUYGJiNvUSWvWq1f+XegkrOnPVi41+/j2PvfQHd99Y5bZW5SXvZvY3\nwCeAI8Ae4Ji7L3ut+5y/3OCfuOsdFZc7vAef2dzY547mwEyl+1E64IJNz6ZeQiuue+2+1EtY1gdf\ntauxz735vKd3uvuWKretNL65+1ZgK4CZ/TMwM/ryRldysBVoGWSpPVJizOfO84gB3/bC5UCzAa+i\nUrjN7NXufsjMzqN3ffvKZpd1QqmxVqilDiXHfP65Hy3iqQNe9YLp3f1r3C8Bn3T35xpcE1BesBVq\nacv8vVZaxCMGPEW8q14qeWfTC5lTUrAVa0lt4R7MPeQRA55i+g7xFAXFWqQdpUzjES+jtBnwpOEu\nIdgKteSqtIh3KeCth1uxFomnhEsq0abwJq9/txbunIOtUEvX5D6NR5nCm5q+Gw93rsFWrEV6cp7G\no0zhdQe8kXC/MHtalsFWrEUGy3UajzCF13X5JMSzSlJRqNNZc3Cikc977LzjjXxeWVqOEU8d8Dqm\n786FW7EeX1PRrUMda1P8R5PbJZWcA96JcCvWg0WOcduGPRYK/dJymcajBBy+X/nvFB1uBXsxBbp+\nyx1TBf2EuXNRAa9HceFWrE9QpNNa6vh3PeY5TOE5BLyYcHc52Ap0PjSdnxB9Cn/wmc1h4511uLsW\nawW6XF2eziMHPOr0nWW4uxBsRVq6Np1HvowSLeDZhLvUWCvQMqwuBD3qFB4l4FXfAefTwMcABx4D\nPururbyzZ2nBVqilKfP3VikRjzqFpw74wHCb2TnA3wOXuPtRM7sLuBn4ZpMLKynYirW0beGeKyHk\nEafwVAGveqlkNbDWzF4CTgd+18RiFGuRZpQ0jUecwtsO+MBwu/tTZvYl4CBwFHjA3R+ocxElBFuh\nllyUNI1Hm8LbCvgpg25gZmcC7wcuBF4PTJrZh5a43ZSZ7TCzHS8992KlL35gZmPW0V5zcOKVXyK5\nKmEfR2tJ0z8dtcqlkncDv3H3ZwHM7B7g7cC359/I3aeBaYB1F7/WV/qEkQ7wsHLe3CKDzO3vXKfw\nSBN4k9N3lXAfBK40s9PpXSq5FtgxyhdTsEXykPs18WgBrzveVa5xbzezbcAuYBZ4mP5kXVWuwVas\nRfKOeJSA1z19V3pWibvfDtw+7CfPMdiKtcjyco14pIDXEe9GXjl5/PjqrKKtWMe17skTD5ccPt8S\nrkQWyvF6eISA1zF9Z/OS97op1u2aH+C2P4eC36wcp/ADMxtDTN8wWsA7F24Fezx1BLhto6xZsR9N\nThGPMH3DaJdPOhFuxbqaHKPclKrHQoFfXi6XUiIEvDd9663LAAV7JYp0PZY6jor5yXIKeOrpu6oi\nw61gn0yRbtfC462Q9+QQ8AjTdxVFhVvB7lGoY9FUfrJcAh453kWEu8vBVqTzpJjHD3jk6TvrcHct\n2Ip02bp6iSWHgEeLd5bh7kqwFepu69pUHjng0abvrMJdcrAVaamiCzGPHvAI8c4i3CUGW6GWupT6\nYwGiBjzC9B063Aq2yHBKjHjkgKeKd9hwlxRtxVpSKC3iEQOeavoOF+5Sgq1YSyQlRVwBDxTuEoKt\nWEsOSol41IC3Ee+B4Tazi4F/n/ef3gB8wd2/UscCcg+2Yi05m9u/Cnh92pi+q7x12S+BtwCY2Srg\nKeDecb+wgi0SRwlTeMSANxXvYS+VXAvsd/cnR/2COQdbsZYuyD3ikQLe1PQ9bLhvBu5c6g/MbAqY\nAlh11oZFf55rsBVr6bKcL6VEC3id8T6l6g3NbAK4CfjeUn/u7tPuvsXdt6xaN/nKf19zcCLLaK97\n0hVtkb658yHHcyJKg+p8H97K4QZuAHa5+++r/oUIB2tYuW5Okbbkeo5E6FFd8R7mUsktLHOZZCE7\nbiEO0jBy3IgiKeV4GSXC5ZM6rntXCreZTQLXAR8f+SsFpWCntX7/sdo+1/MXrantc0l1uQY89bXv\nca57Vwq3ux8BzhrpKwSlYDenzhg38XUV+GbkFvCc4x3mlZNtUKzHkyrIdRvm/4ciP7ycAp5rvDsT\nbkV7sFLCXKdBx0RhX14uAY8S72EUH24Fe3kK9fjmH0NFfGk5BDzCg5bDKDbcCvbSFOvmLDy2CvnJ\n1j3poeMNMabvKooMt6J9gkKdjkK+WC7Td/R4FxVuBbtHsY5Jl1VOiD59R493MeHucrQV6vxoGo8/\nfUeOd/bh7mKwFerydHkajzx9R33QMutwdynainV3dHEa1/Q9nCzD3YVgK9Qyp0vTeOSAR4p3duEu\nOdqKtQzSlWk86uWTKPHOJtwlBluhlnGVHPKo03eEeGcR7tKirWBLU+b2VmkBjxhvSPegZehwK9gi\noykt4Jq+TzbMO+C0qqRor99/TNGWJErbexG7kOJNYyqF28w2mNk2M9tnZnvN7G1NLSjXt0VaSmkn\njeSrpL0YsRFtx7vqpZJ/Be539w/23zT49LoXEu2OGFUpJ4eUqaRLKNGufbd52WRguM1sPXA18BEA\ndz8O1Lq6EqKtYEtOSgl4tGvfbT1oWWXivhB4FviGmb0Z2Anc2n87s1eY2RQwBbB6/ZmVvriCLZJW\nKS/u6dr0XeUa92rgcuCr7n4ZcAT43MIbufu0u29x9y2rJicHflJFWySW3PdztKY0ed27SrhngBl3\n397//TZ6IR9ZtAM8rJIe6BGZL/e9HfGByyYMDLe7PwP81swu7v+na4HHR/2CuR/UnDe1SFW57/Mo\nnWlq6q76rJJPAXf0n1HyBPDRUb5YlIM5itw3ssiwcn8AM8p17yaud1cKt7s/AmwZ5wsp2iJ5Wr//\nmOI9prrj3corJ3ONdu7X+0TqkvO5EKU/dV42aTzcUQ7aMHLepCJNyvW8iNKhuuLdaLijHKxh5Lox\nRdqS62ATpUd1xLuxcEc5SFXluhlFUsnxfMmtS8tp5Me6rkr/BhFDyXEDlm5i38yyf3Z886YWVyIr\nyfGZJxEesBz3wcrQP4+7aQp2OiuFedy/q7C3L7dnnuQe786GW9Fu1jhhbvJrK+rNyW36zjnenQu3\ngl2flHEelab15uUU8Fzj3alwK9rDyTHM41LY65PL5ZMo8R5GJ8KtYFfTxVAPa+ExUshXlku8c1N8\nuBXtlSnW45k7fgr48nKId4SpexhFh1vRXp6CXa/5x1MRXyyH6945xbvYcCvaiynW7VDElxd9+s4l\n3q38kKm2Kdonm9g3o2gnomO/WPTzM4dXVxY3cUffFG1RLGLRFH4yTd7jqRRuMzsAHAZeBmbdfayf\nzd2Urkdbsc6DHtDsUbxHN8zE/dfu/ofGVjKmLkdbwc6TpvD48Y6qiEslXYy2Yl2WLk/hkeMddequ\n+uCkAz8ws51mNrXUDcxsysx2mNmO2aNH6lvhAF2Lth7sKtvc/du1+zjyeRzxwcqqE/c73P0pM3s1\n8KCZ7XP3n8y/gbtPA9MAkxvPbeX/aeQ7u25dO5Gle1O4Ju/qKk3c7v5U/38PAfcCVzS5qCoUbekK\n3f+y0MBwm9mkma2b+xh4D7C76YWJLovICV3ZC5EHskiXTKpM3K8BfmpmjwI/A77v7vc3u6yVRb5z\n69KFk1SG14V90YXze1wDr3G7+xPAm1tYSyWl36ldODFlPF279h1JlGvdRb7kPVeKtgyj5P1S+oA2\nrqzCXeqd2ZXrl1K/kvdO1PM9wrXubMId9U4cR8knnbRL+6hbsgm3iKysxHhHHdhST91ZhDvqnTeO\nEk8ySa/EfVXi+T+u8OEu8U4r8eSSOLS/2pFy6g4f7tLopJI2lLbPShzgxhE63KXdWaWdTBJbafst\nYg9STd1hwx3xThpHaSeR5EH7rkxhw10SnTySUkn7L+JAl2LqDhnuiHfOqEo6aSRfJe3DkvowqnDh\nLulOKelkkfxpPzan7ak7XLhLoZNEIiplX5Y04I0iVLhLuTNKOTlEIovWizan7jDhjnYnjErRlui0\nR/NXOdxmtsrMHjaz+5pcUM50QkguStmr0Qa+tqbuYSbuW4G9TSwi2sEX6QLFO1+Vwm1mm4D3AV+r\newGlHPRSTgLpFu3b+rUxdVeduL8CfBb483I3MLMpM9thZjtmjx6pZXG50OaXnJWwf0sZAKuq8i7v\nNwKH3H3nSrdz92l33+LuW1avnaz0xUs42CVsehGpV9NTd5WJ+yrgJjM7AHwXuMbMvt3oqkSkVRpA\n8jIw3O7+eXff5O4XADcDP3T3DzW+MhGRIZTwHXxVYZ7HnSNNKSKSwlDhdvcfufuNTS1GRNLRIJKP\nZBN3l76tEZHuafIBSl0qGZGmE5F4ujIQKtwi8goNJHlQuEVEMpMk3F35dkZEpAmauEegbyelZNrf\n8SncIlKUSN/RN/XMEoVbRCQzCveQ9G2kiKTWergjfRsjIkvTgBKbJm4RKU7pA6LCPQRNISISgcIt\nIkvSoFKPJp5ZonCLiGSm1XDnfN1J04eIRFHlPSdPM7OfmdmjZrbHzL7YxsJEJL2cB5acB8VBVle4\nzTHgGnf/k5mdCvzUzP7L3f+n4bWJiMgSBobb3R34U/+3p/Z/NfsWxiIisqxK17jNbJWZPQIcAh50\n9+1L3GbKzHaY2Y7Zo0cWfY6cv23J+dtFEUmv7meWVAq3u7/s7m8BNgFXmNmlS9xm2t23uPuW1Wsn\nF32O5y9aM/ZiUzm+eVPqJYhIxg6fb7V+vmHfLPg54CHg+lpXISIilVV5VslGM9vQ/3gtcB2wr+mF\niYjI0qpM3K8DHjKzXwA/p3eN+75mlxWPLpdIF+W873O+PDtIlWeV/AK4rI4v9vxFa7J+kFJEJAK9\n5F1EJDMKt4gsKefLJJHU/YwSULiHoo0skoeSr2+Dwi0ikp3Ww136v4QiJdB3l7Fp4h6SNrSIpKZw\ni4g0pIkHJkHhFpEFcv+usguXY5OEO/cDm/vGFpG8aeIWEcmMwi0ikhmFe0S6XCIl0r7Og8ItIsWI\n9PhZU88ogYThjnSARURyoolbRABdJslJlXfAOdfMHjKzx81sj5nd2sbCcqCNLiIpDHwjBWAW+Ad3\n32Vm64CdZvaguz/e8NpERCrr0uXXgRO3uz/t7rv6Hx8G9gLn1PHFu3SgRSLTd495Geoat5ldQO9t\nzLYv8WdTZrbDzHbMHj1Sz+oyoA0vIgs1+YwSGCLcZnYGcDdwm7u/sPDP3X3a3be4+5bVaycrL6CE\nqVvxlpyVsH9L6MgwKoXbzE6lF+073P2eZpeUpxI2v3RPCfs2WrSbnrah2rNKDNgK7HX3LzexiGgH\nflQlnATSHdqv+aoycV8FfBi4xswe6f96b90LUbxF2lPKPo3WjTambajwdEB3/ynQzmoKcXzzJib2\nzaRehsiSFO38hXrlZEl3RCknh5RF+7I5bU3bECzcUFa8RSIpKdpd70S4cJekpBNF8lbSXowY7Tan\nbQga7oh3zKhKOmEkT9qD5QkZblC8RWSxiF1oe9qGwOEujeItKWjflSl0uCP+6zoOnUTSptL2W8Qe\npJi2IXi4IeadNY7STiaJqbR9VloHxhU+3CUq7aSSWLS/2pFq2oZMwl3iv7Y6uaQJJe6rEs//cWUR\nbijzzivxJJN0StxPUc/7lNM2VHvrMmnQ3Mmmn20ioyox2LKybCZuiPuvbx108skoSt43Uc/31NM2\nZDhxP3/RGtbvP5Z6GY3Q9C1VlRxsiBvtKLKauLui9JNSxqP9kU6EaRuqvQPO183skJntbmNBVXTh\nX+PjmzfpBJWTdGVPRD2/o0Qbqk3c3wSub3gdsowunKgyWFf2QdRoRzMw3O7+E+CPLaxlKF26g7sy\nacliuu9jiDRtQ4YPTs5X8gOVS9GDl93RxVh3aRgbV23hNrMpYApgYvLMuj7tQF2LNyjgJetisCF2\ntKNN21BjuN19GpgGmNx4rtf1eavoYrzh5JNcEc9XV2M9R9EeXtaXSubrarznaArPS9djPUfRHs3A\ncJvZncC7gLPNbAa43d23Nr2wUXQ93qApPDoFuydysCF2tKFCuN39ljYWUhfF+wRN4TEo1ieLHu0c\nFHOpZL65jaGA92gKT0PBXiyHaEeftqHQcM/R9L2YIt4sxXp5inZ9ig43KN4r0aWU+ijYK1O061V8\nuEHxHkRT+GgU62oU7fp1ItygeFe1XIy6HHQFenSKdjXHzjs+1O07E27Qg5bjWCleuUddYa5fDsGG\nGNEeRafCPUfTd70GhS9C2BXnduQS7EiGnbaho+EGTd9taiPsCnN6uUU7wrQ9SrShw+Geo+k7vSph\nV5jjyi3YkHe0oaG3Lnt5oonP2pznL1qT5ebrCkU7rhzPm9yjDQ2+52SEgzOsHDehSAq5DjsRujRu\ntKHhNwuOcJCGleuGFGlLrudHhB7VEW1o4Rr34fONdU+2+uO5a6EHL0VOlmuwIUa069ToxD0n54OW\n82YVqUuu58Hh8y1Mf+qatqHFZ5XkOnmDpm/prlyDDbEGxjqjDS1N3HMiHchR5LyJRYaV836P1Jq6\now0Vw21m15vZL83s12b2uXG+YKQDOgo9eCmly32PR2pME9GGCuE2s1XAvwE3AJcAt5jZJeN80UgH\ndlQ5b2yRpeQebIjVlqaiDdUm7iuAX7v7E+5+HPgu8P5xv3CkAzyq3De5yJwS9nIJTamqyoOT5wC/\nnff7GeCvFt7IzKaAqf5vj/3qC5/ZPf7yanU28IfUi1hAa6om4pog5rpGW9OPal/HfOUcp2ZdXPWG\ntT2rxN2ngWkAM9vh7lvq+tx10Jqq0Zqqi7guramaqGuqetsql0qeAs6d9/tN/f8mIiIJVAn3z4G/\nMLMLzWwCuBn4j2aXJSIiyxl4qcTdZ83s74D/BlYBX3f3PQP+2nQdi6uZ1lSN1lRdxHVpTdVkvSZz\nz/PVjCIiXdXqKydFRGR8CreISGZqDXedL42vi5l93cwOmVmY55Wb2blm9pCZPW5me8zs1gBrOs3M\nfmZmj/bX9MXUa5pjZqvM7GEzuy/1WgDM7ICZPWZmjwzzFK4mmdkGM9tmZvvMbK+ZvS3Ami7uH6O5\nXy+Y2W0B1vXp/h7fbWZ3mtlpAdZ0a389eyodI3ev5Re9By73A28AJoBHgUvq+vxjrOtq4HJgd+q1\nzFvT64DL+x+vA36V+lgBBpzR//hUYDtwZepj1V/PZ4DvAPelXkt/PQeAs1OvY8GavgV8rP/xBLAh\n9ZoWrG8V8AxwfuJ1nAP8Bljb//1dwEcSr+lSYDdwOr0njPwAeONKf6fOibuRl8aPy91/Avwx9Trm\nc/en3X1X/+PDwF56Gyrlmtzd/9T/7an9X8kfuTazTcD7gK+lXktUZrae3oCyFcDdj7v7c2lXtci1\nwH53fzL1QujFca2ZraYXy98lXs+bgO3u/qK7zwI/Bj6w0l+oM9xLvTQ+aYxyYGYXAJfRm3CT6l+S\neAQ4BDzo7snXBHwF+Czw59QLmceBH5jZzv6PekjtQuBZ4Bv9S0pfM7PJ1Ita4GbgztSLcPengC8B\nB4Gngefd/YG0q2I38E4zO8vMTgfey8kvelxED04mZGZnAHcDt7n7C6nX4+4vu/tb6L069gozuzTl\neszsRuCQu+9MuY4lvKN/nG4APmlmVydez2p6lwO/6u6XAUeAEI8xAfRfuHcT8L0AazmT3pWAC4HX\nA5Nm9qGUa3L3vcC/AA8A9wOPAC+v9HfqDLdeGj8EMzuVXrTvcPd7Uq9nvv632Q8B1ydeylXATWZ2\ngN6lt2vM7Ntpl/TK1Ia7HwLupXeZMKUZYGbed0jb6IU8ihuAXe7++9QLAd4N/Mbdn3X3l4B7gLcn\nXhPuvtXd3+ruVwP/S+9xr2XVGW69NL4iMzN61yP3uvuXU68HwMw2mtmG/sdrgeuAfSnX5O6fd/dN\n7n4Bvf30Q3dPOh2Z2aSZrZv7GHgPvW91k3H3Z4DfmtncT5e7Fng84ZIWuoUAl0n6DgJXmtnp/fPw\nWnqPMSVlZq/u/+959K5vf2el29f50wFHeWl848zsTuBdwNlmNgPc7u5b066Kq4APA4/1rykD/KO7\n/2fCNb0O+Fb/jTNOAe5y9xBPvwvmNcC9vXOe1cB33P3+tEsC4FPAHf2h6Qngo4nXA7zyj9t1wMdT\nrwXA3beb2TZgFzALPEyMl7/fbWZnAS8Bnxz04LJe8i4ikhk9OCkikhmFW0QkMwq3iEhmFG4Rkcwo\n3CIimVG4RUQyo3CLiGTm/wEWMXO9dWWYGAAAAABJRU5ErkJggg==\n", "text/plain": [ "<matplotlib.figure.Figure at 0x83862b0>" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "x_mat, y_mat = np.meshgrid(np.arange(10), np.arange(10))\n", "z_mat = x_mat + 2 * y_mat + 0.50 * (x_mat - 5) ** 2 + 0.50 * (y_mat - 5) ** 2\n", "plt.contourf(x_mat, y_mat, z_mat)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "3D surface plots... (Note that for this one, we need to change our magic matploblib function to `notebook` to get interactive plots.)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "application/javascript": [ "/* Put everything inside the global mpl namespace */\n", "window.mpl = {};\n", "\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", " if (mpl.ratio != 1) {\n", " fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", " }\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 backingStore = this.context.backingStorePixelRatio ||\n", "\tthis.context.webkitBackingStorePixelRatio ||\n", "\tthis.context.mozBackingStorePixelRatio ||\n", "\tthis.context.msBackingStorePixelRatio ||\n", "\tthis.context.oBackingStorePixelRatio ||\n", "\tthis.context.backingStorePixelRatio || 1;\n", "\n", " mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\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 * mpl.ratio);\n", " canvas.attr('height', height * mpl.ratio);\n", " canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\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'] / mpl.ratio;\n", " var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n", " var x1 = msg['x1'] / mpl.ratio;\n", " var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\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 * mpl.ratio;\n", " var y = canvas_pos.y * mpl.ratio;\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\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\"];\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", " var width = fig.canvas.width/mpl.ratio\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 + '\" width=\"' + width + '\">');\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 width = this.canvas.width/mpl.ratio\n", " var dataURL = this.canvas.toDataURL();\n", " this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\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", " // select the cell after this one\n", " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", " IPython.notebook.select(index + 1);\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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\" width=\"432\">" ], "text/plain": [ "<IPython.core.display.HTML object>" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "<mpl_toolkits.mplot3d.art3d.Poly3DCollection at 0x88f81d0>" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from mpl_toolkits.mplot3d import Axes3D # need a special import for 3D plots\n", "fig = plt.figure()\n", "ax = fig.add_subplot(111, projection='3d') # also need to specify a 3D projection\n", "ax.plot_surface(x_mat, y_mat, z_mat)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Adding Labels, Titles, and Rescaling" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Usually at the end of my visualization scripts, I \"prettify\" my plots by adding labels, titles, and making sure the axes look nice.\n", "\n", "A few things to note:\n", "- You can set x (and y) labels either with `plt.xlabel(str)` or `ax.set_xlabel(str)`\n", "- Matplotlib's subplot function is not so good with placing the axes, so the `plt.tight_layout()` function is very nice for rescaling functions to a specific rectangle in the figure window" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "application/javascript": [ "/* Put everything inside the global mpl namespace */\n", "window.mpl = {};\n", "\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", " if (mpl.ratio != 1) {\n", " fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", " }\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 backingStore = this.context.backingStorePixelRatio ||\n", "\tthis.context.webkitBackingStorePixelRatio ||\n", "\tthis.context.mozBackingStorePixelRatio ||\n", "\tthis.context.msBackingStorePixelRatio ||\n", "\tthis.context.oBackingStorePixelRatio ||\n", "\tthis.context.backingStorePixelRatio || 1;\n", "\n", " mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\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 * mpl.ratio);\n", " canvas.attr('height', height * mpl.ratio);\n", " canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\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'] / mpl.ratio;\n", " var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n", " var x1 = msg['x1'] / mpl.ratio;\n", " var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\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 * mpl.ratio;\n", " var y = canvas_pos.y * mpl.ratio;\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\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\"];\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", " var width = fig.canvas.width/mpl.ratio\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 + '\" width=\"' + width + '\">');\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 width = this.canvas.width/mpl.ratio\n", " var dataURL = this.canvas.toDataURL();\n", " this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\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", " // select the cell after this one\n", " var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", " IPython.notebook.select(index + 1);\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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\" width=\"432\">" ], "text/plain": [ "<IPython.core.display.HTML object>" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "<matplotlib.text.Text at 0xa1f11d0>" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "fig = plt.figure() # make a new figure\n", "\n", "# AXES 1: random data\n", "ax1 = plt.subplot(2, 1, 1)\n", "plt.scatter(np.random.rand(5), np.random.rand(5), label='data 1')\n", "plt.scatter(np.random.rand(5), np.random.rand(5), label='data 2')\n", "plt.legend(bbox_to_anchor=(0., 1.02, 1., .102), loc=3,\n", " ncol=2, mode='expand', borderaxespad=0.) # google `pyplot legend` for many more legend options\n", "\n", "plt.xlabel('x data for random') # method 1 for setting axes labels\n", "plt.ylabel('y data for random')\n", "\n", "\n", "# AXES 2: line\n", "ax2 = plt.subplot(2, 1, 2) # second axes: a line\n", "plt.plot(range(4), range(4))\n", "\n", "ax2.set_xlabel('x data for line') # method 2 for setting axes labels\n", "ax2.set_ylabel('y data for line')\n", "\n", "plt.suptitle('This is a title.')\n", "# plt.tight_layout(rect=[0, 0, 1, 0.90]) # matplotlib is not great at figure placement, so scale subplots manually" ] }, { "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.6.1" } }, "nbformat": 4, "nbformat_minor": 2 }