{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Pulsebuilding tutorial\n",
"\n",
"## Table of Contents \n",
"\n",
" * [Lingo](#lingo)\n",
" * [Blueprints](#blueprints)\n",
" * [Basic blueprinting](#basicbp)\n",
" * [Markers](#markers)\n",
" * [Modifying blueprints](#bp-modify)\n",
" * [Special segments](#specialsegments)\n",
" * [Timesteps](#timesteps)\n",
"\n",
" * [Elements](#elements)\n",
" * [Sequences](#sequences)\n",
" * [Tektronix AWG 5014 output](#awg)\n",
" * [Delays and filter compensation](#compensation)\n",
" * [Sequences varying parameters](#seq-vary)\n",
"\n",
"## Lingo \n",
"\n",
"Let's settle on a vocabulary. At the highest level, we construct **sequences**. These sequences will eventually be uploaded to an AWG, e.g. the Tektronix AWG 5014. Each sequence consists of several **elements** than again consist of a number of **channels**. On each channel reside a **waveform** and two **markers**. The waveform and markers may either be added as numpy arrays or as **blueprint**. A blueprint is a set of instructions for making a waveform and two markers and consists of several **segments**\n",
"\n",
"That is to say, the food chain goes: segment -> blueprint -> element -> sequence.\n",
"\n",
"\n",
"### Segments\n",
"\n",
"#### Normal segments\n",
"\n",
"A normal segment consists of a _unique_ name, a function object, a tuple of arguments to the function, and a **duration**. \n",
"\n",
" * The name: can be provided by the user or omitted. If omitted, the segment will get the name of its function. Since all names must be unique, the blueprint _appends numbers_ to names if they occur more than once. The numbers are appended chronologically throughout the blueprint. See example below. Note that valid input (base) names are strings NOT ending in a number. Thus, 'pi/2pulse' is valid, whereas 'pulsepi/2' is not.\n",
" \n",
" * The function: must be a python function taking at least two arguments; the sample rate and the segment duration. If the function takes other arguments (such as ramp slope, frequency, etc.) sample rate and duration arguments must be the last positional arguments. Keyword arguments are currently not allowed. See example at the very end.\n",
" \n",
" * The arguments: are in a tuple of $n-2$ arguments for a function taking $n$ arguments, i.e. specifying everything but the sample rate and duration.\n",
" \n",
"* The duration is a single number giving the desired duration of the segment in seconds. Some responsibility for making this number sensible with respect to the sample rate rests on the user.\n",
"\n",
"#### Special segments\n",
"\n",
"A special segment has a (protected) name and a number of arguments. So far, two special segments exist.\n",
" \n",
" * `waituntil`, args [time (int)]: When put in a blueprint, this function ensures that the _next_ segment starts at the absolute time `time` after the start of the element. It does so by filling any excess time with zeros. It fails if the previous segment will finish after time `time`.\n",
" \n",
" * `makemeanfit`. Not implemented yet. Will make the mean of the blueprint be a specified number. Will (eventually) exist in several versions, e.g. one achieving the goal by adding an offset, another by adding an appropriate DC segment at the end of the blueprint.\n",
"\n",
"### Blueprints\n",
"\n",
"Consist of a number of segments. Has an associated sample rate.\n",
"\n",
"### Elements\n",
"\n",
"Has a number of blueprints on each channel. Can have an arbitraty amount of integer-indexed channels, but the blueprint on each channel must have the same number of points and the same total duration as all the other blueprints.\n",
"\n",
"### Sequences\n",
"\n",
"Have an associated sample rate. \n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"#\n",
"# IMPORTS\n",
"#\n",
"%matplotlib inline\n",
"import matplotlib as mpl\n",
"import numpy as np\n",
"\n",
"import broadbean as bb\n",
"from broadbean.plotting import plotter\n",
"\n",
"mpl.rcParams[\"figure.figsize\"] = (8, 3)\n",
"mpl.rcParams[\"figure.subplot.bottom\"] = 0.15"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Blueprints \n",
"\n",
"## Basic blueprinting \n",
"([back to ToC](#toc))\n",
"\n",
"In this section we show how to construct basic blueprints. The units of the vertical axis is **volts** and the units of the horizontal axis (the durations) is **seconds**."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Legend: Name, function, arguments, timesteps, durations\n",
"Segment 1: \"ramp\", PulseAtoms.ramp, (0, 0.001), 3e-06\n",
"----------\n",
"Legend: Name, function, arguments, timesteps, durations\n",
"Segment 1: \"ramp\", PulseAtoms.ramp, (0, 0.001), 3e-06\n",
"Segment 2: \"mysine\", PulseAtoms.sine, (500000.0, 0.001, 0.001, 0), 2e-06\n",
"Segment 3: \"ramp2\", PulseAtoms.ramp, (0.001, 0), 3e-06\n",
"Segment 4: \"myfunc\", PulseAtoms.arb_func, ( at 0x000001B2945D0280>, {'ampl': 500000000.0}), 2e-06\n",
"----------\n"
]
}
],
"source": [
"# The pulsebuilding module comes with a (small) collection of functions appropriate for being segments.\n",
"ramp = bb.PulseAtoms.ramp # args: start, stop\n",
"sine = bb.PulseAtoms.sine # args: freq, ampl, off, phase\n",
"arb_func = bb.PulseAtoms.arb_func # args provided in a dict\n",
"\n",
"# make a blueprint\n",
"\n",
"# The blueprint takes no arguments\n",
"bp1 = bb.BluePrint() # Do-nothing initialisation\n",
"\n",
"# the blueprint is filled via the insertSegment method\n",
"# Call signature: position in the blueprint, function, args, name, duration\n",
"bp1.insertSegment(0, ramp, (0, 1e-3), name=\"\", dur=3e-6)\n",
"\n",
"# A sample rate can be set (Sa/S). Without a sample rate, we can not plot the blueprint.\n",
"bp1.setSR(1e9)\n",
"\n",
"# The blueprint can be inspected. Note that the segment was auto-named 'ramp'\n",
"bp1.showPrint()\n",
"\n",
"# more segments can be added...\n",
"bp1.insertSegment(1, sine, (5e5, 1e-3, 1e-3, 0), name=\"mysine\", dur=2e-6)\n",
"bp1.insertSegment(2, ramp, (1e-3, 0), name=\"\", dur=3e-6)\n",
"bp1.insertSegment(\n",
" 3, arb_func, (lambda t, ampl: ampl * t * t, {\"ampl\": 5e8}), name=\"myfunc\", dur=2e-6\n",
")\n",
"\n",
"# ... and reinspected\n",
"bp1.showPrint()"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# For easy overview, we may plot the blueprint\n",
"plotter(bp1)\n",
"\n",
"# The two bleak lines (red, blue) above the graph represent the channel markers.\n",
"# They are described below."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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hkv5a/Too4/iwQcfxJEjR3DXXXfhE5/4BBKJBN71rnfZ3TQicqGFhQVcuXJl+etr167h1KlTaGxsRG9vL9773vfiIx/5CLZv344tW7bgT/7kT9DZ2bmcuU9EtJqHH34Yzz77LP793/8dNTU1y/tG6+rqEI1GUVdXh4ceegiPPvooGhsbUVtbi/e85z245557bMvIB1gual0+9alP4eMf/zhGR0dx55134sknn8Tdd99td7OIyIW+973v4Sd+4iek6w8++CCeeeYZ6LqOD3/4w/ibv/kbzM7O4sd+7Mfw6U9/Gjt27LChtUTkNj6fz/T63/7t3+I3fuM3AOQL7L/vfe/DF77wBaTTadx333349Kc/betSPgNTIiIiInIE7jElIiIiIkdgYEpEREREjsDAlIiIiIgcgYEpERERETkCA1MiIiIicgQGpkRERETkCAxMiYiIiMgRGJgSERERkSMwMCUiWsXU1BRaW1tx/fr1Db3OG9/4Rrz3ve8tSZtK5R3veAf+6q/+yu5mEBEt48lPRESrePTRRzE/P4+nn356Q68zPT2NYDCImpqaErVs4/r7+/H6178e165dQ11dnd3NISLijCkRUSGLi4v43Oc+h4ceemjDr9XY2Fh0UKrrOnK53IbbINq3bx+2bduGf/iHfyj5axMRFYOBKRFRAV/96lcRDofxmte8Zvna9773Pfh8PnzjG9/AwYMHEY1G8ZM/+ZMYHx/H1772NezevRu1tbX41V/9VSwuLi4/T1zKT6fT+MAHPoCenh6Ew2Hccccd+NznPmf4GV/72tdw+PBhhMNh/OAHP0A6ncbv/u7vorW1FZFIBD/2Yz+GY8eOrfoePv3pT2P79u2IRCJoa2vD/fffb/j+2972NvzTP/1TCT4tIqKNC9jdACIip3rhhRdw+PBh0+/96Z/+KT71qU+hqqoKDzzwAB544AGEw2E8++yzWFhYwM///M/jk5/8JD7wgQ+YPv/Xf/3X8eKLL+LJJ5/EgQMHcO3aNUxOThoe84d/+If4y7/8S2zduhUNDQ34gz/4A3z5y1/G3/3d32HTpk144okncN999+HKlStobGyUfsbx48fxu7/7u/j7v/97vPa1r8X09DReeOEFw2PuuusufPSjH0U6nUY4HC7ykyIiKg0GpkREBdy4cQOdnZ2m3/vIRz6C173udQCAhx56CI899hiuXr2KrVu3AgDuv/9+fPe73zUNTC9duoQvfelL+Na3voV7770XAJaft9L/+3//D29605sAAIlEAp/5zGfwzDPP4C1veQsA4Omnn8a3vvUtfO5zn8P73/9+6fkDAwOIxWL42Z/9WdTU1GDTpk04ePCg4TGdnZ3IZDIYHR3Fpk2b1vrREBFZgkv5REQFJJNJRCIR0+/t379/+f/b2tpQVVVlCC7b2towPj5u+txTp07B7/fjDW94w6o//8iRI8v/f/XqVWSz2eVgGACCwSDuuusunD9/3vT5b3rTm7Bp0yZs3boV73znO/GP//iPhu0FABCNRgFAuk5EZAcGpkREBTQ3N2NmZsb0e8FgcPn/fT6f4eula5qmmT53KRi8nVgstsaWmqupqcHJkyfxhS98AR0dHfjQhz6EAwcOYHZ2dvkx09PTAICWlpYN/SwiolJgYEpEVMDBgwdx7ty5kr9uX18fNE3D888/v+bnbNu2DaFQCP/zP/+zfC2bzeLYsWPYs2dPwecFAgHce++9eOKJJ3D69Glcv34d3/nOd5a/39/fj+7ubjQ3Nxf3ZoiISoh7TImICrjvvvvw2GOPYWZmBg0NDSV73c2bN+PBBx/Eb/7mby4nP924cQPj4+N44IEHTJ8Ti8Xw27/923j/+9+PxsZG9Pb24oknnsDi4mLBclZf+cpX8Morr+D1r389Ghoa8NWvfhWapmHnzp3Lj3nhhRfw5je/uWTvjYhoIxiYEhEV0NfXh0OHDuFLX/oS3v3ud5f0tT/zmc/ggx/8IH7nd34HU1NT6O3txQc/+MFVn/Oxj30Mmqbhne98J+bn53HkyBF84xvfKBg019fX41//9V/xp3/6p0ilUti+fTu+8IUvYO/evQCAVCqFf/u3f8PXv/71kr43IqJi8eQnIqJV/Nd//Rfe//73o7+/H4rird1Pn/nMZ/Dcc8/hm9/8pt1NISICwBlTIqJV/czP/AwuX76MoaEh9PT02N2ckgoGg/jkJz9pdzOIiJZxxpSIiIiIHMFb61JERERE5FoMTImIiIjIERiYEhEREZEjMDAlIiIiIkdgYEpEREREjsDAlIiIiIgcgYEpERERETkCA1MiIiIicgQGpkRERETkCP8fP0ogRU+GBtwAAAAASUVORK5CYII=",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"image/png": "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",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Blueprints may be added together\n",
"bp2 = bp1 + bp1\n",
"plotter(bp2)\n",
"\n",
"# Segments may be removed from a blueprint. They are removed by name.\n",
"bp2.removeSegment(\"ramp2\")\n",
"plotter(bp2)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Number of points in blueprint: 10000\n",
"Length of blueprint in seconds: 9.999999999999999e-06\n",
"Number of segments in blueprint: 4\n"
]
}
],
"source": [
"# A blueprint has a handful of different lengths one may check\n",
"print(f\"Number of points in blueprint: {bp1.points}\")\n",
"print(f\"Length of blueprint in seconds: {bp1.duration}\")\n",
"print(f\"Number of segments in blueprint: {bp1.length_segments}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Markers \n",
"([back to ToC](#toc))\n",
"\n",
"All markers are OFF by default. Markers can be added to a blueprint (switched ON) in two different ways. Either a marker is specified by its ON time in *absolute time* or by its ON time *relative* to a certain segment."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"application/javascript": "/* Put everything inside the global mpl namespace */\nwindow.mpl = {};\n\n\nmpl.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\nmpl.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 = $('
');\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\nmpl.figure.prototype._init_header = function() {\n var titlebar = $(\n '
');\n var titletext = $(\n '
');\n titlebar.append(titletext)\n this.root.append(titlebar);\n this.header = titletext[0];\n}\n\n\n\nmpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n\n}\n\n\nmpl.figure.prototype._root_extra_style = function(canvas_div) {\n\n}\n\nmpl.figure.prototype._init_canvas = function() {\n var fig = this;\n\n var canvas_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 = $(' ');\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 = $(' ');\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\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('
')\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 = $(' ');\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 = $(' ');\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 = $(' ');\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 = $(' ');\n\n var fmt_picker = $(' ');\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 ' ', {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 = $('');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n}\n\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.figure.prototype.handle_figure_label = function(fig, msg) {\n // Updates the figure title.\n fig.header.textContent = msg['label'];\n}\n\nmpl.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\nmpl.figure.prototype.handle_message = function(fig, msg) {\n fig.message.textContent = msg['message'];\n}\n\nmpl.figure.prototype.handle_draw = function(fig, msg) {\n // Request the server to send over a new figure.\n fig.send_draw_message();\n}\n\nmpl.figure.prototype.handle_image_mode = function(fig, msg) {\n fig.image_mode = msg['mode'];\n}\n\nmpl.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.\nmpl.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\nmpl.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 */\nfunction 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\nmpl.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\nmpl.figure.prototype._key_event_extra = function(event, name) {\n // Handle any extra behaviour associated with a key event\n}\n\nmpl.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\nmpl.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\nmpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n this.message.textContent = tooltip;\n};\nmpl.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\nmpl.extensions = [\"eps\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\"];\n\nmpl.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\nmpl.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(\"
\");\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\nmpl.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(' ');\n fig.close_ws(fig, msg);\n}\n\nmpl.figure.prototype.close_ws = function(fig, msg){\n fig.send_message('closing', msg);\n // fig.ws.close()\n}\n\nmpl.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'] = ' ';\n}\n\nmpl.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\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('
')\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 = $(' ');\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 = $(' ');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n\n // Add the close button to the window.\n var buttongrp = $('
');\n var 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\nmpl.figure.prototype._root_extra_style = function(el){\n var fig = this\n el.on(\"remove\", function(){\n\tfig.close_ws(fig, {});\n });\n}\n\nmpl.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\nmpl.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\nmpl.figure.prototype.handle_save = function(fig, msg) {\n fig.ondownload(fig, null);\n}\n\n\nmpl.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= 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.\nif (IPython.notebook.kernel != null) {\n IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n}\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
" "
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Absolute time marker specification\n",
"\n",
"# The blueprint has a list of tuples for each marker. The tuples are (switch_on_time, duration)\n",
"\n",
"# create a blueprint\n",
"bp_atm = bb.BluePrint()\n",
"bp_atm.setSR(100)\n",
"bp_atm.insertSegment(0, ramp, (0, 1), dur=3)\n",
"bp_atm.insertSegment(1, sine, (0.5, 1, 1, 0), dur=2)\n",
"bp_atm.insertSegment(2, ramp, (1, 0), dur=3)\n",
"\n",
"# specify markers in absolute time\n",
"bp_atm.marker1 = [(1, 0.5), (2, 0.5)]\n",
"bp_atm.marker2 = [(1.5, 0.2), (2.5, 0.1)]\n",
"\n",
"plotter(bp_atm)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"application/javascript": "/* Put everything inside the global mpl namespace */\nwindow.mpl = {};\n\n\nmpl.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\nmpl.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 = $('
');\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\nmpl.figure.prototype._init_header = function() {\n var titlebar = $(\n '
');\n var titletext = $(\n '
');\n titlebar.append(titletext)\n this.root.append(titlebar);\n this.header = titletext[0];\n}\n\n\n\nmpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n\n}\n\n\nmpl.figure.prototype._root_extra_style = function(canvas_div) {\n\n}\n\nmpl.figure.prototype._init_canvas = function() {\n var fig = this;\n\n var canvas_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 = $(' ');\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 = $(' ');\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\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('
')\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 = $(' ');\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 = $(' ');\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 = $(' ');\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 = $(' ');\n\n var fmt_picker = $(' ');\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 ' ', {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 = $('');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n}\n\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.figure.prototype.handle_figure_label = function(fig, msg) {\n // Updates the figure title.\n fig.header.textContent = msg['label'];\n}\n\nmpl.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\nmpl.figure.prototype.handle_message = function(fig, msg) {\n fig.message.textContent = msg['message'];\n}\n\nmpl.figure.prototype.handle_draw = function(fig, msg) {\n // Request the server to send over a new figure.\n fig.send_draw_message();\n}\n\nmpl.figure.prototype.handle_image_mode = function(fig, msg) {\n fig.image_mode = msg['mode'];\n}\n\nmpl.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.\nmpl.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\nmpl.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 */\nfunction 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\nmpl.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\nmpl.figure.prototype._key_event_extra = function(event, name) {\n // Handle any extra behaviour associated with a key event\n}\n\nmpl.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\nmpl.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\nmpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n this.message.textContent = tooltip;\n};\nmpl.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\nmpl.extensions = [\"eps\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\"];\n\nmpl.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\nmpl.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(\"
\");\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\nmpl.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(' ');\n fig.close_ws(fig, msg);\n}\n\nmpl.figure.prototype.close_ws = function(fig, msg){\n fig.send_message('closing', msg);\n // fig.ws.close()\n}\n\nmpl.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'] = ' ';\n}\n\nmpl.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\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('
')\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 = $(' ');\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 = $(' ');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n\n // Add the close button to the window.\n var buttongrp = $('
');\n var 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\nmpl.figure.prototype._root_extra_style = function(el){\n var fig = this\n el.on(\"remove\", function(){\n\tfig.close_ws(fig, {});\n });\n}\n\nmpl.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\nmpl.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\nmpl.figure.prototype.handle_save = function(fig, msg) {\n fig.ondownload(fig, null);\n}\n\n\nmpl.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= 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.\nif (IPython.notebook.kernel != null) {\n IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n}\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
" "
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/javascript": "/* Put everything inside the global mpl namespace */\nwindow.mpl = {};\n\n\nmpl.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\nmpl.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 = $('
');\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\nmpl.figure.prototype._init_header = function() {\n var titlebar = $(\n '
');\n var titletext = $(\n '
');\n titlebar.append(titletext)\n this.root.append(titlebar);\n this.header = titletext[0];\n}\n\n\n\nmpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n\n}\n\n\nmpl.figure.prototype._root_extra_style = function(canvas_div) {\n\n}\n\nmpl.figure.prototype._init_canvas = function() {\n var fig = this;\n\n var canvas_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 = $(' ');\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 = $(' ');\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\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('
')\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 = $(' ');\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 = $(' ');\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 = $(' ');\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 = $(' ');\n\n var fmt_picker = $(' ');\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 ' ', {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 = $('');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n}\n\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.figure.prototype.handle_figure_label = function(fig, msg) {\n // Updates the figure title.\n fig.header.textContent = msg['label'];\n}\n\nmpl.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\nmpl.figure.prototype.handle_message = function(fig, msg) {\n fig.message.textContent = msg['message'];\n}\n\nmpl.figure.prototype.handle_draw = function(fig, msg) {\n // Request the server to send over a new figure.\n fig.send_draw_message();\n}\n\nmpl.figure.prototype.handle_image_mode = function(fig, msg) {\n fig.image_mode = msg['mode'];\n}\n\nmpl.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.\nmpl.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\nmpl.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 */\nfunction 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\nmpl.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\nmpl.figure.prototype._key_event_extra = function(event, name) {\n // Handle any extra behaviour associated with a key event\n}\n\nmpl.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\nmpl.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\nmpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n this.message.textContent = tooltip;\n};\nmpl.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\nmpl.extensions = [\"eps\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\"];\n\nmpl.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\nmpl.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(\"
\");\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\nmpl.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(' ');\n fig.close_ws(fig, msg);\n}\n\nmpl.figure.prototype.close_ws = function(fig, msg){\n fig.send_message('closing', msg);\n // fig.ws.close()\n}\n\nmpl.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'] = ' ';\n}\n\nmpl.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\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('
')\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 = $(' ');\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 = $(' ');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n\n // Add the close button to the window.\n var buttongrp = $('
');\n var 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\nmpl.figure.prototype._root_extra_style = function(el){\n var fig = this\n el.on(\"remove\", function(){\n\tfig.close_ws(fig, {});\n });\n}\n\nmpl.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\nmpl.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\nmpl.figure.prototype.handle_save = function(fig, msg) {\n fig.ondownload(fig, null);\n}\n\n\nmpl.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= 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.\nif (IPython.notebook.kernel != null) {\n IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n}\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
" "
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Relative time marker specification\n",
"\n",
"bp_rtm = bb.BluePrint()\n",
"bp_rtm.setSR(100)\n",
"bp_rtm.insertSegment(0, ramp, (0, 1), dur=1)\n",
"bp_rtm.insertSegment(1, ramp, (1, 1), dur=1)\n",
"bp_rtm.insertSegment(\n",
" 2, sine, (1.675, 1, 0, np.pi / 2), dur=1.5, name=\"mysine\"\n",
") # This is the important segment\n",
"# make marker 1 go ON a bit before the sine comes on\n",
"bp_rtm.setSegmentMarker(\n",
" \"mysine\", (-0.1, 0.2), 1\n",
") # segment name, (delay, duration), markerID\n",
"# make marker 2 go ON halfway through the sine\n",
"bp_rtm.setSegmentMarker(\"mysine\", (0.75, 0.1), 2)\n",
"\n",
"plotter(bp_rtm)\n",
"\n",
"# Even if we insert segments before and after the sine, the markers \"stick\" to the sine segment\n",
"bp_rtm.insertSegment(0, ramp, (0, 0), dur=1)\n",
"bp_rtm.insertSegment(-1, ramp, (0, 0.2), dur=1)\n",
"\n",
"plotter(bp_rtm)\n",
"\n",
"\n",
"# NB: the two different ways of inputting markers will never directly conflict, since one only specifies when to turn\n",
"# markers ON. It is up to the user to ensure that markers switch off again as expected, i.e. that different marker\n",
"# specifications do not overlap."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Modifying blueprints \n",
"([back to ToC](#toc))"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"application/javascript": "/* Put everything inside the global mpl namespace */\nwindow.mpl = {};\n\n\nmpl.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\nmpl.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 = $('
');\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\nmpl.figure.prototype._init_header = function() {\n var titlebar = $(\n '
');\n var titletext = $(\n '
');\n titlebar.append(titletext)\n this.root.append(titlebar);\n this.header = titletext[0];\n}\n\n\n\nmpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n\n}\n\n\nmpl.figure.prototype._root_extra_style = function(canvas_div) {\n\n}\n\nmpl.figure.prototype._init_canvas = function() {\n var fig = this;\n\n var canvas_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 = $(' ');\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 = $(' ');\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\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('
')\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 = $(' ');\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 = $(' ');\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 = $(' ');\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 = $(' ');\n\n var fmt_picker = $(' ');\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 ' ', {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 = $('');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n}\n\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.figure.prototype.handle_figure_label = function(fig, msg) {\n // Updates the figure title.\n fig.header.textContent = msg['label'];\n}\n\nmpl.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\nmpl.figure.prototype.handle_message = function(fig, msg) {\n fig.message.textContent = msg['message'];\n}\n\nmpl.figure.prototype.handle_draw = function(fig, msg) {\n // Request the server to send over a new figure.\n fig.send_draw_message();\n}\n\nmpl.figure.prototype.handle_image_mode = function(fig, msg) {\n fig.image_mode = msg['mode'];\n}\n\nmpl.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.\nmpl.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\nmpl.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 */\nfunction 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\nmpl.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\nmpl.figure.prototype._key_event_extra = function(event, name) {\n // Handle any extra behaviour associated with a key event\n}\n\nmpl.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\nmpl.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\nmpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n this.message.textContent = tooltip;\n};\nmpl.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\nmpl.extensions = [\"eps\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\"];\n\nmpl.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\nmpl.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(\"
\");\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\nmpl.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(' ');\n fig.close_ws(fig, msg);\n}\n\nmpl.figure.prototype.close_ws = function(fig, msg){\n fig.send_message('closing', msg);\n // fig.ws.close()\n}\n\nmpl.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'] = ' ';\n}\n\nmpl.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\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('
')\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 = $(' ');\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 = $(' ');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n\n // Add the close button to the window.\n var buttongrp = $('
');\n var 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\nmpl.figure.prototype._root_extra_style = function(el){\n var fig = this\n el.on(\"remove\", function(){\n\tfig.close_ws(fig, {});\n });\n}\n\nmpl.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\nmpl.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\nmpl.figure.prototype.handle_save = function(fig, msg) {\n fig.ondownload(fig, null);\n}\n\n\nmpl.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= 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.\nif (IPython.notebook.kernel != null) {\n IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n}\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
" "
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/javascript": "/* Put everything inside the global mpl namespace */\nwindow.mpl = {};\n\n\nmpl.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\nmpl.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 = $('
');\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\nmpl.figure.prototype._init_header = function() {\n var titlebar = $(\n '
');\n var titletext = $(\n '
');\n titlebar.append(titletext)\n this.root.append(titlebar);\n this.header = titletext[0];\n}\n\n\n\nmpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n\n}\n\n\nmpl.figure.prototype._root_extra_style = function(canvas_div) {\n\n}\n\nmpl.figure.prototype._init_canvas = function() {\n var fig = this;\n\n var canvas_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 = $(' ');\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 = $(' ');\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\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('
')\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 = $(' ');\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 = $(' ');\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 = $(' ');\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 = $(' ');\n\n var fmt_picker = $(' ');\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 ' ', {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 = $('');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n}\n\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.figure.prototype.handle_figure_label = function(fig, msg) {\n // Updates the figure title.\n fig.header.textContent = msg['label'];\n}\n\nmpl.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\nmpl.figure.prototype.handle_message = function(fig, msg) {\n fig.message.textContent = msg['message'];\n}\n\nmpl.figure.prototype.handle_draw = function(fig, msg) {\n // Request the server to send over a new figure.\n fig.send_draw_message();\n}\n\nmpl.figure.prototype.handle_image_mode = function(fig, msg) {\n fig.image_mode = msg['mode'];\n}\n\nmpl.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.\nmpl.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\nmpl.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 */\nfunction 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\nmpl.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\nmpl.figure.prototype._key_event_extra = function(event, name) {\n // Handle any extra behaviour associated with a key event\n}\n\nmpl.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\nmpl.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\nmpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n this.message.textContent = tooltip;\n};\nmpl.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\nmpl.extensions = [\"eps\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\"];\n\nmpl.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\nmpl.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(\"
\");\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\nmpl.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(' ');\n fig.close_ws(fig, msg);\n}\n\nmpl.figure.prototype.close_ws = function(fig, msg){\n fig.send_message('closing', msg);\n // fig.ws.close()\n}\n\nmpl.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'] = ' ';\n}\n\nmpl.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\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('
')\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 = $(' ');\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 = $(' ');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n\n // Add the close button to the window.\n var buttongrp = $('
');\n var 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\nmpl.figure.prototype._root_extra_style = function(el){\n var fig = this\n el.on(\"remove\", function(){\n\tfig.close_ws(fig, {});\n });\n}\n\nmpl.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\nmpl.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\nmpl.figure.prototype.handle_save = function(fig, msg) {\n fig.ondownload(fig, null);\n}\n\n\nmpl.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= 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.\nif (IPython.notebook.kernel != null) {\n IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n}\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
" "
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/javascript": "/* Put everything inside the global mpl namespace */\nwindow.mpl = {};\n\n\nmpl.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\nmpl.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 = $('
');\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\nmpl.figure.prototype._init_header = function() {\n var titlebar = $(\n '
');\n var titletext = $(\n '
');\n titlebar.append(titletext)\n this.root.append(titlebar);\n this.header = titletext[0];\n}\n\n\n\nmpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n\n}\n\n\nmpl.figure.prototype._root_extra_style = function(canvas_div) {\n\n}\n\nmpl.figure.prototype._init_canvas = function() {\n var fig = this;\n\n var canvas_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 = $(' ');\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 = $(' ');\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\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('
')\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 = $(' ');\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 = $(' ');\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 = $(' ');\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 = $(' ');\n\n var fmt_picker = $(' ');\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 ' ', {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 = $('');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n}\n\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.figure.prototype.handle_figure_label = function(fig, msg) {\n // Updates the figure title.\n fig.header.textContent = msg['label'];\n}\n\nmpl.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\nmpl.figure.prototype.handle_message = function(fig, msg) {\n fig.message.textContent = msg['message'];\n}\n\nmpl.figure.prototype.handle_draw = function(fig, msg) {\n // Request the server to send over a new figure.\n fig.send_draw_message();\n}\n\nmpl.figure.prototype.handle_image_mode = function(fig, msg) {\n fig.image_mode = msg['mode'];\n}\n\nmpl.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.\nmpl.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\nmpl.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 */\nfunction 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\nmpl.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\nmpl.figure.prototype._key_event_extra = function(event, name) {\n // Handle any extra behaviour associated with a key event\n}\n\nmpl.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\nmpl.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\nmpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n this.message.textContent = tooltip;\n};\nmpl.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\nmpl.extensions = [\"eps\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\"];\n\nmpl.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\nmpl.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(\"
\");\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\nmpl.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(' ');\n fig.close_ws(fig, msg);\n}\n\nmpl.figure.prototype.close_ws = function(fig, msg){\n fig.send_message('closing', msg);\n // fig.ws.close()\n}\n\nmpl.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'] = ' ';\n}\n\nmpl.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\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('
')\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 = $(' ');\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 = $(' ');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n\n // Add the close button to the window.\n var buttongrp = $('
');\n var 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\nmpl.figure.prototype._root_extra_style = function(el){\n var fig = this\n el.on(\"remove\", function(){\n\tfig.close_ws(fig, {});\n });\n}\n\nmpl.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\nmpl.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\nmpl.figure.prototype.handle_save = function(fig, msg) {\n fig.ondownload(fig, null);\n}\n\n\nmpl.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= 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.\nif (IPython.notebook.kernel != null) {\n IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n}\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
" "
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# An essential feature of blueprints is that they can be modified\n",
"\n",
"bp_mod = bb.BluePrint()\n",
"bp_mod.setSR(100)\n",
"\n",
"bp_mod.insertSegment(0, ramp, (0, 0), name=\"before\", dur=1)\n",
"bp_mod.insertSegment(1, ramp, (1, 1), name=\"plateau\", dur=1)\n",
"bp_mod.insertSegment(2, ramp, (0, 0), name=\"after\", dur=1)\n",
"\n",
"plotter(bp_mod)\n",
"\n",
"# Functional arguments can be changed\n",
"\n",
"# They are looked up by segment name\n",
"bp_mod.changeArg(\n",
" \"before\", \"stop\", 1\n",
") # the argument to change may either be the argument name or its position\n",
"bp_mod.changeArg(\"after\", 0, 1)\n",
"\n",
"plotter(bp_mod)\n",
"\n",
"# Durations can also be changed\n",
"bp_mod.changeDuration(\"plateau\", 2)\n",
"\n",
"plotter(bp_mod)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Special segments \n",
"([back to ToC](#toc))"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"application/javascript": "/* Put everything inside the global mpl namespace */\nwindow.mpl = {};\n\n\nmpl.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\nmpl.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 = $('
');\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\nmpl.figure.prototype._init_header = function() {\n var titlebar = $(\n '
');\n var titletext = $(\n '
');\n titlebar.append(titletext)\n this.root.append(titlebar);\n this.header = titletext[0];\n}\n\n\n\nmpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n\n}\n\n\nmpl.figure.prototype._root_extra_style = function(canvas_div) {\n\n}\n\nmpl.figure.prototype._init_canvas = function() {\n var fig = this;\n\n var canvas_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 = $(' ');\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 = $(' ');\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\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('
')\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 = $(' ');\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 = $(' ');\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 = $(' ');\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 = $(' ');\n\n var fmt_picker = $(' ');\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 ' ', {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 = $('');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n}\n\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.figure.prototype.handle_figure_label = function(fig, msg) {\n // Updates the figure title.\n fig.header.textContent = msg['label'];\n}\n\nmpl.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\nmpl.figure.prototype.handle_message = function(fig, msg) {\n fig.message.textContent = msg['message'];\n}\n\nmpl.figure.prototype.handle_draw = function(fig, msg) {\n // Request the server to send over a new figure.\n fig.send_draw_message();\n}\n\nmpl.figure.prototype.handle_image_mode = function(fig, msg) {\n fig.image_mode = msg['mode'];\n}\n\nmpl.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.\nmpl.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\nmpl.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 */\nfunction 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\nmpl.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\nmpl.figure.prototype._key_event_extra = function(event, name) {\n // Handle any extra behaviour associated with a key event\n}\n\nmpl.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\nmpl.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\nmpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n this.message.textContent = tooltip;\n};\nmpl.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\nmpl.extensions = [\"eps\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\"];\n\nmpl.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\nmpl.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(\"
\");\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\nmpl.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(' ');\n fig.close_ws(fig, msg);\n}\n\nmpl.figure.prototype.close_ws = function(fig, msg){\n fig.send_message('closing', msg);\n // fig.ws.close()\n}\n\nmpl.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'] = ' ';\n}\n\nmpl.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\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('
')\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 = $(' ');\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 = $(' ');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n\n // Add the close button to the window.\n var buttongrp = $('
');\n var 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\nmpl.figure.prototype._root_extra_style = function(el){\n var fig = this\n el.on(\"remove\", function(){\n\tfig.close_ws(fig, {});\n });\n}\n\nmpl.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\nmpl.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\nmpl.figure.prototype.handle_save = function(fig, msg) {\n fig.ondownload(fig, null);\n}\n\n\nmpl.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= 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.\nif (IPython.notebook.kernel != null) {\n IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n}\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
" "
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/javascript": "/* Put everything inside the global mpl namespace */\nwindow.mpl = {};\n\n\nmpl.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\nmpl.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 = $('
');\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\nmpl.figure.prototype._init_header = function() {\n var titlebar = $(\n '
');\n var titletext = $(\n '
');\n titlebar.append(titletext)\n this.root.append(titlebar);\n this.header = titletext[0];\n}\n\n\n\nmpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n\n}\n\n\nmpl.figure.prototype._root_extra_style = function(canvas_div) {\n\n}\n\nmpl.figure.prototype._init_canvas = function() {\n var fig = this;\n\n var canvas_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 = $(' ');\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 = $(' ');\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\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('
')\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 = $(' ');\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 = $(' ');\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 = $(' ');\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 = $(' ');\n\n var fmt_picker = $(' ');\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 ' ', {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 = $('');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n}\n\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.figure.prototype.handle_figure_label = function(fig, msg) {\n // Updates the figure title.\n fig.header.textContent = msg['label'];\n}\n\nmpl.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\nmpl.figure.prototype.handle_message = function(fig, msg) {\n fig.message.textContent = msg['message'];\n}\n\nmpl.figure.prototype.handle_draw = function(fig, msg) {\n // Request the server to send over a new figure.\n fig.send_draw_message();\n}\n\nmpl.figure.prototype.handle_image_mode = function(fig, msg) {\n fig.image_mode = msg['mode'];\n}\n\nmpl.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.\nmpl.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\nmpl.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 */\nfunction 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\nmpl.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\nmpl.figure.prototype._key_event_extra = function(event, name) {\n // Handle any extra behaviour associated with a key event\n}\n\nmpl.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\nmpl.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\nmpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n this.message.textContent = tooltip;\n};\nmpl.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\nmpl.extensions = [\"eps\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\"];\n\nmpl.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\nmpl.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(\"
\");\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\nmpl.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(' ');\n fig.close_ws(fig, msg);\n}\n\nmpl.figure.prototype.close_ws = function(fig, msg){\n fig.send_message('closing', msg);\n // fig.ws.close()\n}\n\nmpl.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'] = ' ';\n}\n\nmpl.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\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('
')\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 = $(' ');\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 = $(' ');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n\n // Add the close button to the window.\n var buttongrp = $('
');\n var 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\nmpl.figure.prototype._root_extra_style = function(el){\n var fig = this\n el.on(\"remove\", function(){\n\tfig.close_ws(fig, {});\n });\n}\n\nmpl.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\nmpl.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\nmpl.figure.prototype.handle_save = function(fig, msg) {\n fig.ondownload(fig, null);\n}\n\n\nmpl.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= 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.\nif (IPython.notebook.kernel != null) {\n IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n}\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
" "
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# The 'waituntil' segment fills up a part of the blueprint with zeros\n",
"\n",
"# Example: a square pulse, then waiting until 5 s exactly and then a new sine\n",
"\n",
"bp_wait = bb.BluePrint()\n",
"bp_wait.setSR(100)\n",
"\n",
"bp_wait.insertSegment(0, ramp, (0, 0), dur=1)\n",
"bp_wait.insertSegment(1, ramp, (1, 1), name=\"plateau\", dur=1)\n",
"# function must be sthe string 'waituntil', the argument is the ABSOLUTE time to wait until\n",
"bp_wait.insertSegment(2, \"waituntil\", (5,))\n",
"bp_wait.insertSegment(3, sine, (1, 0.1, 0, -np.pi / 2), dur=1)\n",
"plotter(bp_wait)\n",
"\n",
"# If we make the square pulse longer, the sine still occurs at 5 s\n",
"bp_wait.changeDuration(\"plateau\", 1.5)\n",
"plotter(bp_wait)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Elements \n",
"([back to ToC](#toc))\n",
"\n",
"Elements are containers containing blueprints. A valid element consists of blueprints that all have the same number of points and the same overall duration."
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"application/javascript": "/* Put everything inside the global mpl namespace */\nwindow.mpl = {};\n\n\nmpl.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\nmpl.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 = $('
');\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\nmpl.figure.prototype._init_header = function() {\n var titlebar = $(\n '
');\n var titletext = $(\n '
');\n titlebar.append(titletext)\n this.root.append(titlebar);\n this.header = titletext[0];\n}\n\n\n\nmpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n\n}\n\n\nmpl.figure.prototype._root_extra_style = function(canvas_div) {\n\n}\n\nmpl.figure.prototype._init_canvas = function() {\n var fig = this;\n\n var canvas_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 = $(' ');\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 = $(' ');\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\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('
')\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 = $(' ');\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 = $(' ');\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 = $(' ');\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 = $(' ');\n\n var fmt_picker = $(' ');\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 ' ', {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 = $('');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n}\n\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.figure.prototype.handle_figure_label = function(fig, msg) {\n // Updates the figure title.\n fig.header.textContent = msg['label'];\n}\n\nmpl.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\nmpl.figure.prototype.handle_message = function(fig, msg) {\n fig.message.textContent = msg['message'];\n}\n\nmpl.figure.prototype.handle_draw = function(fig, msg) {\n // Request the server to send over a new figure.\n fig.send_draw_message();\n}\n\nmpl.figure.prototype.handle_image_mode = function(fig, msg) {\n fig.image_mode = msg['mode'];\n}\n\nmpl.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.\nmpl.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\nmpl.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 */\nfunction 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\nmpl.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\nmpl.figure.prototype._key_event_extra = function(event, name) {\n // Handle any extra behaviour associated with a key event\n}\n\nmpl.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\nmpl.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\nmpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n this.message.textContent = tooltip;\n};\nmpl.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\nmpl.extensions = [\"eps\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\"];\n\nmpl.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\nmpl.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(\"
\");\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\nmpl.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(' ');\n fig.close_ws(fig, msg);\n}\n\nmpl.figure.prototype.close_ws = function(fig, msg){\n fig.send_message('closing', msg);\n // fig.ws.close()\n}\n\nmpl.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'] = ' ';\n}\n\nmpl.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\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('
')\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 = $(' ');\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 = $(' ');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n\n // Add the close button to the window.\n var buttongrp = $('
');\n var 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\nmpl.figure.prototype._root_extra_style = function(el){\n var fig = this\n el.on(\"remove\", function(){\n\tfig.close_ws(fig, {});\n });\n}\n\nmpl.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\nmpl.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\nmpl.figure.prototype.handle_save = function(fig, msg) {\n fig.ondownload(fig, null);\n}\n\n\nmpl.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= 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.\nif (IPython.notebook.kernel != null) {\n IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n}\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
" "
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Example 1\n",
"\n",
"# Create the blueprints\n",
"bp_square = bb.BluePrint()\n",
"bp_square.setSR(1e9)\n",
"bp_square.insertSegment(0, ramp, (0, 0), dur=0.1e-6)\n",
"bp_square.insertSegment(1, ramp, (10e-3, 10e-3), name=\"top\", dur=0.1e-6)\n",
"bp_square.insertSegment(2, ramp, (0, 0), dur=0.1e-6)\n",
"bp_boxes = bp_square + bp_square\n",
"#\n",
"bp_sine = bb.BluePrint()\n",
"bp_sine.setSR(1e9)\n",
"bp_sine.insertSegment(0, sine, (3.333e6, 25e-3, 0, 0), dur=0.3e-6)\n",
"bp_sineandboxes = bp_sine + bp_square\n",
"\n",
"# Now we create an element and add the blueprints to channel 1 and 3, respectively\n",
"elem1 = bb.Element()\n",
"elem1.addBluePrint(1, bp_boxes)\n",
"elem1.addBluePrint(3, bp_sineandboxes)\n",
"\n",
"# We can check the validity of the element\n",
"elem1.validateDurations() # raises an ElementDurationError if something is wrong. If all is OK, does nothing.\n",
"\n",
"# And we can plot the element\n",
"plotter(elem1)"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Designated channels: [1, 3]\n",
"Total duration: 6e-07 s\n",
"Sample rate: 1000000000.0 (Sa/S)\n"
]
}
],
"source": [
"# An element has several features\n",
"print(f\"Designated channels: {elem1.channels}\")\n",
"print(f\"Total duration: {elem1.duration} s\")\n",
"print(f\"Sample rate: {elem1.SR} (Sa/S)\")"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"application/javascript": "/* Put everything inside the global mpl namespace */\nwindow.mpl = {};\n\n\nmpl.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\nmpl.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 = $('
');\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\nmpl.figure.prototype._init_header = function() {\n var titlebar = $(\n '
');\n var titletext = $(\n '
');\n titlebar.append(titletext)\n this.root.append(titlebar);\n this.header = titletext[0];\n}\n\n\n\nmpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n\n}\n\n\nmpl.figure.prototype._root_extra_style = function(canvas_div) {\n\n}\n\nmpl.figure.prototype._init_canvas = function() {\n var fig = this;\n\n var canvas_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 = $(' ');\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 = $(' ');\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\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('
')\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 = $(' ');\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 = $(' ');\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 = $(' ');\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 = $(' ');\n\n var fmt_picker = $(' ');\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 ' ', {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 = $('');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n}\n\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.figure.prototype.handle_figure_label = function(fig, msg) {\n // Updates the figure title.\n fig.header.textContent = msg['label'];\n}\n\nmpl.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\nmpl.figure.prototype.handle_message = function(fig, msg) {\n fig.message.textContent = msg['message'];\n}\n\nmpl.figure.prototype.handle_draw = function(fig, msg) {\n // Request the server to send over a new figure.\n fig.send_draw_message();\n}\n\nmpl.figure.prototype.handle_image_mode = function(fig, msg) {\n fig.image_mode = msg['mode'];\n}\n\nmpl.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.\nmpl.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\nmpl.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 */\nfunction 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\nmpl.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\nmpl.figure.prototype._key_event_extra = function(event, name) {\n // Handle any extra behaviour associated with a key event\n}\n\nmpl.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\nmpl.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\nmpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n this.message.textContent = tooltip;\n};\nmpl.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\nmpl.extensions = [\"eps\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\"];\n\nmpl.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\nmpl.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(\"
\");\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\nmpl.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(' ');\n fig.close_ws(fig, msg);\n}\n\nmpl.figure.prototype.close_ws = function(fig, msg){\n fig.send_message('closing', msg);\n // fig.ws.close()\n}\n\nmpl.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'] = ' ';\n}\n\nmpl.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\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('
')\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 = $(' ');\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 = $(' ');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n\n // Add the close button to the window.\n var buttongrp = $('
');\n var 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\nmpl.figure.prototype._root_extra_style = function(el){\n var fig = this\n el.on(\"remove\", function(){\n\tfig.close_ws(fig, {});\n });\n}\n\nmpl.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\nmpl.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\nmpl.figure.prototype.handle_save = function(fig, msg) {\n fig.ondownload(fig, null);\n}\n\n\nmpl.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= 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.\nif (IPython.notebook.kernel != null) {\n IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n}\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
" "
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# We can modify the blueprints of an element through the element object\n",
"\n",
"# Change the sine freq\n",
"elem1.changeArg(\n",
" 3, \"sine\", \"freq\", 6.67e6\n",
") # Call signature: channel, segment name, argument, new_value\n",
"\n",
"# make the second plateaus last longer\n",
"elem1.changeDuration(\n",
" 1, \"top2\", 0.2e-6\n",
") # In this blueprint, the second top is called top2\n",
"elem1.changeDuration(3, \"top\", 0.2e-6)\n",
"\n",
"plotter(elem1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Sequences \n",
"([back to ToC](#toc))\n",
"\n",
"Finally, we have reached the top level of the module: sequences. Unsurprisingly, sequences are containers containing elements. All elements in a sequence must specify the same channels."
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [],
"source": [
"seq1 = bb.Sequence()\n",
"\n",
"# We fill up the sequence by adding elements at different sequence positions.\n",
"# A valid sequence is filled from 1 to N with NO HOLES, i.e. if position 4 is filled, so must be position 1, 2, and 3\n",
"\n",
"#\n",
"# Make blueprints, make elements\n",
"\n",
"# Create the blueprints\n",
"bp_square = bb.BluePrint()\n",
"bp_square.setSR(1e9)\n",
"bp_square.insertSegment(0, ramp, (0, 0), dur=100e-9)\n",
"bp_square.insertSegment(1, ramp, (1e-3, 1e-3), name=\"top\", dur=100e-9)\n",
"bp_square.insertSegment(2, ramp, (0, 0), dur=100e-9)\n",
"bp_boxes = bp_square + bp_square\n",
"#\n",
"bp_sine = bb.BluePrint()\n",
"bp_sine.setSR(1e9)\n",
"bp_sine.insertSegment(0, sine, (3.333e6, 1.5e-3, 0, 0), dur=300e-9)\n",
"bp_sineandboxes = bp_sine + bp_square\n",
"\n",
"# create elements\n",
"elem1 = bb.Element()\n",
"elem1.addBluePrint(1, bp_boxes)\n",
"elem1.addBluePrint(3, bp_sineandboxes)\n",
"#\n",
"elem2 = bb.Element()\n",
"elem2.addBluePrint(3, bp_boxes)\n",
"elem2.addBluePrint(1, bp_sineandboxes)\n",
"\n",
"# Fill up the sequence\n",
"seq1.addElement(1, elem1) # Call signature: seq. pos., element\n",
"seq1.addElement(2, elem2)\n",
"seq1.addElement(3, elem1)\n",
"\n",
"# set its sample rate\n",
"seq1.setSR(elem1.SR)"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Help on method changeArg in module broadbean.broadbean:\n",
"\n",
"changeArg(channel:Union[str, int], name:str, arg:Union[str, int], value:Union[int, float], replaceeverywhere:bool=False) -> None method of broadbean.broadbean.Element instance\n",
" Change the argument of a function of the blueprint on the specified\n",
" channel.\n",
" \n",
" Args:\n",
" channel: The channel where the blueprint sits.\n",
" name: The name of the segment in which to change an argument\n",
" arg: Either the position (int) or name (str) of\n",
" the argument to change\n",
" value: The new value of the argument\n",
" replaceeverywhere: If True, the same argument is overwritten\n",
" in ALL segments where the name matches. E.g. 'gaussian1' will\n",
" match 'gaussian', 'gaussian2', etc. If False, only the segment\n",
" with exact name match gets a replacement.\n",
" \n",
" Raises:\n",
" ValueError: If the specified channel has no blueprint.\n",
" ValueError: If the argument can not be matched (either the argument\n",
" name does not match or the argument number is wrong).\n",
"\n"
]
}
],
"source": [
"help(seq1.element(1).changeArg)"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"data": {
"application/javascript": "/* Put everything inside the global mpl namespace */\nwindow.mpl = {};\n\n\nmpl.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\nmpl.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 = $('
');\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\nmpl.figure.prototype._init_header = function() {\n var titlebar = $(\n '
');\n var titletext = $(\n '
');\n titlebar.append(titletext)\n this.root.append(titlebar);\n this.header = titletext[0];\n}\n\n\n\nmpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n\n}\n\n\nmpl.figure.prototype._root_extra_style = function(canvas_div) {\n\n}\n\nmpl.figure.prototype._init_canvas = function() {\n var fig = this;\n\n var canvas_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 = $(' ');\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 = $(' ');\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\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('
')\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 = $(' ');\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 = $(' ');\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 = $(' ');\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 = $(' ');\n\n var fmt_picker = $(' ');\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 ' ', {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 = $('');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n}\n\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.figure.prototype.handle_figure_label = function(fig, msg) {\n // Updates the figure title.\n fig.header.textContent = msg['label'];\n}\n\nmpl.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\nmpl.figure.prototype.handle_message = function(fig, msg) {\n fig.message.textContent = msg['message'];\n}\n\nmpl.figure.prototype.handle_draw = function(fig, msg) {\n // Request the server to send over a new figure.\n fig.send_draw_message();\n}\n\nmpl.figure.prototype.handle_image_mode = function(fig, msg) {\n fig.image_mode = msg['mode'];\n}\n\nmpl.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.\nmpl.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\nmpl.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 */\nfunction 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\nmpl.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\nmpl.figure.prototype._key_event_extra = function(event, name) {\n // Handle any extra behaviour associated with a key event\n}\n\nmpl.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\nmpl.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\nmpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n this.message.textContent = tooltip;\n};\nmpl.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\nmpl.extensions = [\"eps\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\"];\n\nmpl.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\nmpl.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(\"
\");\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\nmpl.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(' ');\n fig.close_ws(fig, msg);\n}\n\nmpl.figure.prototype.close_ws = function(fig, msg){\n fig.send_message('closing', msg);\n // fig.ws.close()\n}\n\nmpl.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'] = ' ';\n}\n\nmpl.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\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('
')\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 = $(' ');\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 = $(' ');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n\n // Add the close button to the window.\n var buttongrp = $('
');\n var 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\nmpl.figure.prototype._root_extra_style = function(el){\n var fig = this\n el.on(\"remove\", function(){\n\tfig.close_ws(fig, {});\n });\n}\n\nmpl.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\nmpl.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\nmpl.figure.prototype.handle_save = function(fig, msg) {\n fig.ondownload(fig, null);\n}\n\n\nmpl.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= 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.\nif (IPython.notebook.kernel != null) {\n IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n}\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
" "
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# The sequence can be validated\n",
"seq1.checkConsistency() # returns True if all is well, raises errors if not\n",
"\n",
"# And the sequence can (if valid) be plotted\n",
"plotter(seq1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Tektronix AWG 5014 output \n",
"([back to ToC](#toc))\n",
"\n",
"The sequence object can output a tuple matching the call signature of the QCoDeS Tektronix AWG 5014 driver.\n",
"\n",
"For the translation from voltage to AWG unsigned integer format to be correct, the voltage ranges and offsets of the AWG channels must be specified (NB: This will **NOT** work if the channels on the AWG are in high/low mode).\n",
"\n",
"Furthermore, the AWG sequencer options should be specified for each sequence element."
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {
"scrolled": false
},
"outputs": [
{
"data": {
"application/javascript": "/* Put everything inside the global mpl namespace */\nwindow.mpl = {};\n\n\nmpl.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\nmpl.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 = $('
');\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\nmpl.figure.prototype._init_header = function() {\n var titlebar = $(\n '
');\n var titletext = $(\n '
');\n titlebar.append(titletext)\n this.root.append(titlebar);\n this.header = titletext[0];\n}\n\n\n\nmpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n\n}\n\n\nmpl.figure.prototype._root_extra_style = function(canvas_div) {\n\n}\n\nmpl.figure.prototype._init_canvas = function() {\n var fig = this;\n\n var canvas_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 = $(' ');\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 = $(' ');\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\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('
')\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 = $(' ');\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 = $(' ');\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 = $(' ');\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 = $(' ');\n\n var fmt_picker = $(' ');\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 ' ', {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 = $('');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n}\n\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.figure.prototype.handle_figure_label = function(fig, msg) {\n // Updates the figure title.\n fig.header.textContent = msg['label'];\n}\n\nmpl.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\nmpl.figure.prototype.handle_message = function(fig, msg) {\n fig.message.textContent = msg['message'];\n}\n\nmpl.figure.prototype.handle_draw = function(fig, msg) {\n // Request the server to send over a new figure.\n fig.send_draw_message();\n}\n\nmpl.figure.prototype.handle_image_mode = function(fig, msg) {\n fig.image_mode = msg['mode'];\n}\n\nmpl.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.\nmpl.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\nmpl.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 */\nfunction 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\nmpl.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\nmpl.figure.prototype._key_event_extra = function(event, name) {\n // Handle any extra behaviour associated with a key event\n}\n\nmpl.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\nmpl.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\nmpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n this.message.textContent = tooltip;\n};\nmpl.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\nmpl.extensions = [\"eps\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\"];\n\nmpl.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\nmpl.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(\"
\");\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\nmpl.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(' ');\n fig.close_ws(fig, msg);\n}\n\nmpl.figure.prototype.close_ws = function(fig, msg){\n fig.send_message('closing', msg);\n // fig.ws.close()\n}\n\nmpl.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'] = ' ';\n}\n\nmpl.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\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('
')\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 = $(' ');\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 = $(' ');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n\n // Add the close button to the window.\n var buttongrp = $('
');\n var 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\nmpl.figure.prototype._root_extra_style = function(el){\n var fig = this\n el.on(\"remove\", function(){\n\tfig.close_ws(fig, {});\n });\n}\n\nmpl.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\nmpl.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\nmpl.figure.prototype.handle_save = function(fig, msg) {\n fig.ondownload(fig, null);\n}\n\n\nmpl.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= 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.\nif (IPython.notebook.kernel != null) {\n IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n}\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
" "
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"seq1.setChannelAmplitude(1, 10e-3) # Call signature: channel, amplitude (peak-to-peak)\n",
"seq1.setChannelOffset(1, 0)\n",
"seq1.setChannelAmplitude(3, 10e-3)\n",
"seq1.setChannelOffset(3, 0)\n",
"\n",
"# Here we repeat each element twice and then proceed to the next, wrapping over at the end\n",
"seq1.setSequencingTriggerWait(1, 0)\n",
"seq1.setSequencingNumberOfRepetitions(1, 2)\n",
"seq1.setSequencingEventJumpTarget(1, 0)\n",
"seq1.setSequencingGoto(1, 2)\n",
"#\n",
"seq1.setSequencingTriggerWait(2, 0)\n",
"seq1.setSequencingNumberOfRepetitions(2, 2)\n",
"seq1.setSequencingEventJumpTarget(2, 0)\n",
"seq1.setSequencingGoto(1, 3)\n",
"#\n",
"seq1.setSequencingTriggerWait(3, 0)\n",
"seq1.setSequencingNumberOfRepetitions(3, 2)\n",
"seq1.setSequencingEventJumpTarget(3, 0)\n",
"seq1.setSequencingGoto(3, 1)\n",
"\n",
"# then we may finally get the \"package\" to give the QCoDeS driver for upload\n",
"package = seq1.outputForAWGFile()\n",
"\n",
"# Note that the sequencing information is included in the plot in a way mimicking the\n",
"# way the display of the Tektronix AWG 5014\n",
"plotter(seq1)"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[1, 3]"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# The package is a SLICEABLE object\n",
"# By slicing and indexing it, one may retrieve different parts of the sequence\n",
"\n",
"chan1_awg_input = package[0] # returns a tuple yielding an awg file with channel 1\n",
"chan3_awg_input = package[1] # returns a tuple yielding an awg file with channel 3\n",
"\n",
"both_chans_awg_input = package[\n",
" :\n",
"] # returns a tuple yielding an awg file with both channels\n",
"\n",
"# This may be useful to make one big sequence for one experiment and then uploading part of it to one awg\n",
"# and part of it to another (since physical awg's usually don't have enough channels for a big experiment)\n",
"\n",
"# To see how the channels are counted, look up the channels\n",
"package.channels"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [],
"source": [
"## Example of uploading the sequence (requires having qcodes installed, see https://github.com/QCoDeS/Qcodes)\n",
"\n",
"# from qcodes.instrument_drivers.tektronix.AWG5014 import Tektronix_AWG5014\n",
"# awg = Tektronix_AWG5014('AWG1', 'TCPIP0::172.20.3.57::inst0::INSTR', timeout=40)\n",
"# awg.make_send_and_load_awg_file(*package[:])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Delays and filter compensation \n",
"([back to ToC](#toc))\n",
"\n",
"In a real experimental setting, the signal transmission line may distort and/or delay the pulse sequence. The Sequence object can perform some compensation for this when making the `.awg` file."
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [],
"source": [
"# To delay channel 1 with respect to the other channels, set its delay\n",
"seq1.setChannelDelay(1, 0)\n",
"seq1.setChannelDelay(3, 123e-9)\n",
"\n",
"# To apply for a high pass filter with a cut-off frequency of 1 MHz on channel 3, we can do\n",
"seq1.setChannelFilterCompensation(3, \"HP\", order=1, f_cut=1e6)\n",
"# or, equivalently,\n",
"seq1.setChannelFilterCompensation(3, \"HP\", order=1, tau=1e-6)\n",
"\n",
"# Note that setting the filter compensation may invalidate the sequence in the sense that the specified voltage ranges\n",
"# on the AWG may have become too small. The function outputForAWGFile will warn you if this is the case.\n",
"\n",
"newpackage = seq1.outputForAWGFile()"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [
{
"data": {
"application/javascript": "/* Put everything inside the global mpl namespace */\nwindow.mpl = {};\n\n\nmpl.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\nmpl.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 = $('
');\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\nmpl.figure.prototype._init_header = function() {\n var titlebar = $(\n '
');\n var titletext = $(\n '
');\n titlebar.append(titletext)\n this.root.append(titlebar);\n this.header = titletext[0];\n}\n\n\n\nmpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n\n}\n\n\nmpl.figure.prototype._root_extra_style = function(canvas_div) {\n\n}\n\nmpl.figure.prototype._init_canvas = function() {\n var fig = this;\n\n var canvas_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 = $(' ');\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 = $(' ');\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\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('
')\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 = $(' ');\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 = $(' ');\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 = $(' ');\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 = $(' ');\n\n var fmt_picker = $(' ');\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 ' ', {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 = $('');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n}\n\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.figure.prototype.handle_figure_label = function(fig, msg) {\n // Updates the figure title.\n fig.header.textContent = msg['label'];\n}\n\nmpl.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\nmpl.figure.prototype.handle_message = function(fig, msg) {\n fig.message.textContent = msg['message'];\n}\n\nmpl.figure.prototype.handle_draw = function(fig, msg) {\n // Request the server to send over a new figure.\n fig.send_draw_message();\n}\n\nmpl.figure.prototype.handle_image_mode = function(fig, msg) {\n fig.image_mode = msg['mode'];\n}\n\nmpl.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.\nmpl.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\nmpl.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 */\nfunction 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\nmpl.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\nmpl.figure.prototype._key_event_extra = function(event, name) {\n // Handle any extra behaviour associated with a key event\n}\n\nmpl.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\nmpl.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\nmpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n this.message.textContent = tooltip;\n};\nmpl.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\nmpl.extensions = [\"eps\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\"];\n\nmpl.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\nmpl.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(\"
\");\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\nmpl.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(' ');\n fig.close_ws(fig, msg);\n}\n\nmpl.figure.prototype.close_ws = function(fig, msg){\n fig.send_message('closing', msg);\n // fig.ws.close()\n}\n\nmpl.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'] = ' ';\n}\n\nmpl.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\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('
')\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 = $(' ');\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 = $(' ');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n\n // Add the close button to the window.\n var buttongrp = $('
');\n var 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\nmpl.figure.prototype._root_extra_style = function(el){\n var fig = this\n el.on(\"remove\", function(){\n\tfig.close_ws(fig, {});\n });\n}\n\nmpl.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\nmpl.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\nmpl.figure.prototype.handle_save = function(fig, msg) {\n fig.ondownload(fig, null);\n}\n\n\nmpl.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= 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.\nif (IPython.notebook.kernel != null) {\n IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n}\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
" "
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# For sanity checking, it may be helpful to see how the compensated waveforms look.\n",
"# The plotter function can display the delays and filter compensations\n",
"\n",
"plotter(seq1, apply_filters=True, apply_delays=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Sequences varying parameters \n",
"([back to ToC](#toc))\n",
"\n",
"The module contains a few wrapper functions to easily generate sequences with some parameter(s) varying throughout the sequence. First two examples where a Base element is provided and varied, then an example where an existing Sequence is repeated."
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [
{
"data": {
"application/javascript": "/* Put everything inside the global mpl namespace */\nwindow.mpl = {};\n\n\nmpl.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\nmpl.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 = $('
');\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\nmpl.figure.prototype._init_header = function() {\n var titlebar = $(\n '
');\n var titletext = $(\n '
');\n titlebar.append(titletext)\n this.root.append(titlebar);\n this.header = titletext[0];\n}\n\n\n\nmpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n\n}\n\n\nmpl.figure.prototype._root_extra_style = function(canvas_div) {\n\n}\n\nmpl.figure.prototype._init_canvas = function() {\n var fig = this;\n\n var canvas_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 = $(' ');\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 = $(' ');\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\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('
')\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 = $(' ');\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 = $(' ');\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 = $(' ');\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 = $(' ');\n\n var fmt_picker = $(' ');\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 ' ', {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 = $('');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n}\n\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.figure.prototype.handle_figure_label = function(fig, msg) {\n // Updates the figure title.\n fig.header.textContent = msg['label'];\n}\n\nmpl.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\nmpl.figure.prototype.handle_message = function(fig, msg) {\n fig.message.textContent = msg['message'];\n}\n\nmpl.figure.prototype.handle_draw = function(fig, msg) {\n // Request the server to send over a new figure.\n fig.send_draw_message();\n}\n\nmpl.figure.prototype.handle_image_mode = function(fig, msg) {\n fig.image_mode = msg['mode'];\n}\n\nmpl.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.\nmpl.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\nmpl.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 */\nfunction 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\nmpl.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\nmpl.figure.prototype._key_event_extra = function(event, name) {\n // Handle any extra behaviour associated with a key event\n}\n\nmpl.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\nmpl.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\nmpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n this.message.textContent = tooltip;\n};\nmpl.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\nmpl.extensions = [\"eps\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\"];\n\nmpl.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\nmpl.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(\"
\");\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\nmpl.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(' ');\n fig.close_ws(fig, msg);\n}\n\nmpl.figure.prototype.close_ws = function(fig, msg){\n fig.send_message('closing', msg);\n // fig.ws.close()\n}\n\nmpl.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'] = ' ';\n}\n\nmpl.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\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('
')\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 = $(' ');\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 = $(' ');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n\n // Add the close button to the window.\n var buttongrp = $('
');\n var 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\nmpl.figure.prototype._root_extra_style = function(el){\n var fig = this\n el.on(\"remove\", function(){\n\tfig.close_ws(fig, {});\n });\n}\n\nmpl.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\nmpl.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\nmpl.figure.prototype.handle_save = function(fig, msg) {\n fig.ondownload(fig, null);\n}\n\n\nmpl.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= 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.\nif (IPython.notebook.kernel != null) {\n IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n}\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
" "
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/javascript": "/* Put everything inside the global mpl namespace */\nwindow.mpl = {};\n\n\nmpl.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\nmpl.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 = $('
');\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\nmpl.figure.prototype._init_header = function() {\n var titlebar = $(\n '
');\n var titletext = $(\n '
');\n titlebar.append(titletext)\n this.root.append(titlebar);\n this.header = titletext[0];\n}\n\n\n\nmpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n\n}\n\n\nmpl.figure.prototype._root_extra_style = function(canvas_div) {\n\n}\n\nmpl.figure.prototype._init_canvas = function() {\n var fig = this;\n\n var canvas_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 = $(' ');\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 = $(' ');\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\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('
')\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 = $(' ');\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 = $(' ');\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 = $(' ');\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 = $(' ');\n\n var fmt_picker = $(' ');\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 ' ', {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 = $('');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n}\n\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.figure.prototype.handle_figure_label = function(fig, msg) {\n // Updates the figure title.\n fig.header.textContent = msg['label'];\n}\n\nmpl.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\nmpl.figure.prototype.handle_message = function(fig, msg) {\n fig.message.textContent = msg['message'];\n}\n\nmpl.figure.prototype.handle_draw = function(fig, msg) {\n // Request the server to send over a new figure.\n fig.send_draw_message();\n}\n\nmpl.figure.prototype.handle_image_mode = function(fig, msg) {\n fig.image_mode = msg['mode'];\n}\n\nmpl.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.\nmpl.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\nmpl.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 */\nfunction 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\nmpl.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\nmpl.figure.prototype._key_event_extra = function(event, name) {\n // Handle any extra behaviour associated with a key event\n}\n\nmpl.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\nmpl.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\nmpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n this.message.textContent = tooltip;\n};\nmpl.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\nmpl.extensions = [\"eps\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\"];\n\nmpl.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\nmpl.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(\"
\");\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\nmpl.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(' ');\n fig.close_ws(fig, msg);\n}\n\nmpl.figure.prototype.close_ws = function(fig, msg){\n fig.send_message('closing', msg);\n // fig.ws.close()\n}\n\nmpl.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'] = ' ';\n}\n\nmpl.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\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('
')\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 = $(' ');\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 = $(' ');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n\n // Add the close button to the window.\n var buttongrp = $('
');\n var 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\nmpl.figure.prototype._root_extra_style = function(el){\n var fig = this\n el.on(\"remove\", function(){\n\tfig.close_ws(fig, {});\n });\n}\n\nmpl.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\nmpl.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\nmpl.figure.prototype.handle_save = function(fig, msg) {\n fig.ondownload(fig, null);\n}\n\n\nmpl.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= 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.\nif (IPython.notebook.kernel != null) {\n IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n}\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
" "
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Example 1: vary the duration of a square pulse\n",
"\n",
"# First, we make a basic element, in this example containing just a single blueprint\n",
"basebp = bb.BluePrint()\n",
"basebp.insertSegment(0, ramp, (0, 0), dur=0.5)\n",
"basebp.insertSegment(1, ramp, (1, 1), dur=1, name=\"varyme\")\n",
"basebp.insertSegment(2, \"waituntil\", 5)\n",
"basebp.setSR(100)\n",
"\n",
"baseelem = bb.Element()\n",
"baseelem.addBluePrint(1, basebp)\n",
"\n",
"plotter(baseelem)\n",
"\n",
"# Now we make a 5-step sequence varying the duration of the high level\n",
"# The inputs are lists, since several things can be varied (see Example 2)\n",
"channels = [1]\n",
"names = [\"varyme\"]\n",
"args = [\"duration\"]\n",
"iters = [[1, 1.5, 2, 2.5, 3]]\n",
"\n",
"seq1 = bb.makeVaryingSequence(baseelem, channels, names, args, iters)\n",
"plotter(seq1)"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {},
"outputs": [
{
"data": {
"application/javascript": "/* Put everything inside the global mpl namespace */\nwindow.mpl = {};\n\n\nmpl.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\nmpl.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 = $('
');\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\nmpl.figure.prototype._init_header = function() {\n var titlebar = $(\n '
');\n var titletext = $(\n '
');\n titlebar.append(titletext)\n this.root.append(titlebar);\n this.header = titletext[0];\n}\n\n\n\nmpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n\n}\n\n\nmpl.figure.prototype._root_extra_style = function(canvas_div) {\n\n}\n\nmpl.figure.prototype._init_canvas = function() {\n var fig = this;\n\n var canvas_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 = $(' ');\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 = $(' ');\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\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('
')\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 = $(' ');\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 = $(' ');\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 = $(' ');\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 = $(' ');\n\n var fmt_picker = $(' ');\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 ' ', {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 = $('');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n}\n\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.figure.prototype.handle_figure_label = function(fig, msg) {\n // Updates the figure title.\n fig.header.textContent = msg['label'];\n}\n\nmpl.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\nmpl.figure.prototype.handle_message = function(fig, msg) {\n fig.message.textContent = msg['message'];\n}\n\nmpl.figure.prototype.handle_draw = function(fig, msg) {\n // Request the server to send over a new figure.\n fig.send_draw_message();\n}\n\nmpl.figure.prototype.handle_image_mode = function(fig, msg) {\n fig.image_mode = msg['mode'];\n}\n\nmpl.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.\nmpl.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\nmpl.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 */\nfunction 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\nmpl.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\nmpl.figure.prototype._key_event_extra = function(event, name) {\n // Handle any extra behaviour associated with a key event\n}\n\nmpl.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\nmpl.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\nmpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n this.message.textContent = tooltip;\n};\nmpl.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\nmpl.extensions = [\"eps\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\"];\n\nmpl.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\nmpl.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(\"
\");\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\nmpl.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(' ');\n fig.close_ws(fig, msg);\n}\n\nmpl.figure.prototype.close_ws = function(fig, msg){\n fig.send_message('closing', msg);\n // fig.ws.close()\n}\n\nmpl.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'] = ' ';\n}\n\nmpl.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\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('
')\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 = $(' ');\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 = $(' ');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n\n // Add the close button to the window.\n var buttongrp = $('
');\n var 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\nmpl.figure.prototype._root_extra_style = function(el){\n var fig = this\n el.on(\"remove\", function(){\n\tfig.close_ws(fig, {});\n });\n}\n\nmpl.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\nmpl.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\nmpl.figure.prototype.handle_save = function(fig, msg) {\n fig.ondownload(fig, null);\n}\n\n\nmpl.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= 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.\nif (IPython.notebook.kernel != null) {\n IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n}\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
" "
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Example 2: Vary the duration AND the high level\n",
"\n",
"# It is straighforward to vary several things throughout the sequence\n",
"\n",
"# We make the same base element as in Example 1\n",
"basebp = bb.BluePrint()\n",
"basebp.insertSegment(0, ramp, (0, 0), dur=0.5)\n",
"basebp.insertSegment(1, ramp, (1, 1), dur=1, name=\"varyme\")\n",
"basebp.insertSegment(2, \"waituntil\", 5)\n",
"basebp.setSR(100)\n",
"\n",
"baseelem = bb.Element()\n",
"baseelem.addBluePrint(1, basebp)\n",
"\n",
"# Now we make a 5-step sequence varying the duration AND the high level\n",
"# We thus vary 3 things, a duration, a ramp start, and a ramp stop\n",
"channels = [1, 1, 1]\n",
"names = [\"varyme\", \"varyme\", \"varyme\"]\n",
"args = [\"duration\", \"start\", \"stop\"]\n",
"iters = [[1, 1.5, 2, 2.5, 3], [1, 0.8, 0.7, 0.6, 0.5], [1, 0.8, 0.7, 0.6, 0.5]]\n",
"\n",
"seq2 = bb.makeVaryingSequence(baseelem, channels, names, args, iters)\n",
"plotter(seq2)"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/Users/william/sourcecodes/pulsebuilding/broadbean/broadbean.py:1425: UserWarning: Deprecation warning. This function is only compatible with AWG5014 output and will be removed. Please use the specific setSequencingXXX methods.\n",
" warnings.warn('Deprecation warning. This function is only compatible '\n"
]
},
{
"data": {
"application/javascript": "/* Put everything inside the global mpl namespace */\nwindow.mpl = {};\n\n\nmpl.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\nmpl.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 = $('
');\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\nmpl.figure.prototype._init_header = function() {\n var titlebar = $(\n '
');\n var titletext = $(\n '
');\n titlebar.append(titletext)\n this.root.append(titlebar);\n this.header = titletext[0];\n}\n\n\n\nmpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n\n}\n\n\nmpl.figure.prototype._root_extra_style = function(canvas_div) {\n\n}\n\nmpl.figure.prototype._init_canvas = function() {\n var fig = this;\n\n var canvas_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 = $(' ');\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 = $(' ');\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\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('
')\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 = $(' ');\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 = $(' ');\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 = $(' ');\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 = $(' ');\n\n var fmt_picker = $(' ');\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 ' ', {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 = $('');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n}\n\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.figure.prototype.handle_figure_label = function(fig, msg) {\n // Updates the figure title.\n fig.header.textContent = msg['label'];\n}\n\nmpl.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\nmpl.figure.prototype.handle_message = function(fig, msg) {\n fig.message.textContent = msg['message'];\n}\n\nmpl.figure.prototype.handle_draw = function(fig, msg) {\n // Request the server to send over a new figure.\n fig.send_draw_message();\n}\n\nmpl.figure.prototype.handle_image_mode = function(fig, msg) {\n fig.image_mode = msg['mode'];\n}\n\nmpl.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.\nmpl.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\nmpl.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 */\nfunction 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\nmpl.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\nmpl.figure.prototype._key_event_extra = function(event, name) {\n // Handle any extra behaviour associated with a key event\n}\n\nmpl.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\nmpl.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\nmpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n this.message.textContent = tooltip;\n};\nmpl.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\nmpl.extensions = [\"eps\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\"];\n\nmpl.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\nmpl.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(\"
\");\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\nmpl.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(' ');\n fig.close_ws(fig, msg);\n}\n\nmpl.figure.prototype.close_ws = function(fig, msg){\n fig.send_message('closing', msg);\n // fig.ws.close()\n}\n\nmpl.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'] = ' ';\n}\n\nmpl.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\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('
')\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 = $(' ');\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 = $(' ');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n\n // Add the close button to the window.\n var buttongrp = $('
');\n var 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\nmpl.figure.prototype._root_extra_style = function(el){\n var fig = this\n el.on(\"remove\", function(){\n\tfig.close_ws(fig, {});\n });\n}\n\nmpl.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\nmpl.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\nmpl.figure.prototype.handle_save = function(fig, msg) {\n fig.ondownload(fig, null);\n}\n\n\nmpl.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= 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.\nif (IPython.notebook.kernel != null) {\n IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n}\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
" "
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"/Users/william/.pyenv/versions/3.6.0/envs/qcodesdevel/lib/python3.6/site-packages/matplotlib/pyplot.py:524: RuntimeWarning: More than 20 figures have been opened. Figures created through the pyplot interface (`matplotlib.pyplot.figure`) are retained until explicitly closed and may consume too much memory. (To control this warning, see the rcParam `figure.max_open_warning`).\n",
" max_open_warning, RuntimeWarning)\n"
]
},
{
"data": {
"application/javascript": "/* Put everything inside the global mpl namespace */\nwindow.mpl = {};\n\n\nmpl.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\nmpl.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 = $('
');\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\nmpl.figure.prototype._init_header = function() {\n var titlebar = $(\n '
');\n var titletext = $(\n '
');\n titlebar.append(titletext)\n this.root.append(titlebar);\n this.header = titletext[0];\n}\n\n\n\nmpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n\n}\n\n\nmpl.figure.prototype._root_extra_style = function(canvas_div) {\n\n}\n\nmpl.figure.prototype._init_canvas = function() {\n var fig = this;\n\n var canvas_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 = $(' ');\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 = $(' ');\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\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('
')\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 = $(' ');\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 = $(' ');\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 = $(' ');\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 = $(' ');\n\n var fmt_picker = $(' ');\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 ' ', {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 = $('');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n}\n\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.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\nmpl.figure.prototype.handle_figure_label = function(fig, msg) {\n // Updates the figure title.\n fig.header.textContent = msg['label'];\n}\n\nmpl.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\nmpl.figure.prototype.handle_message = function(fig, msg) {\n fig.message.textContent = msg['message'];\n}\n\nmpl.figure.prototype.handle_draw = function(fig, msg) {\n // Request the server to send over a new figure.\n fig.send_draw_message();\n}\n\nmpl.figure.prototype.handle_image_mode = function(fig, msg) {\n fig.image_mode = msg['mode'];\n}\n\nmpl.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.\nmpl.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\nmpl.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 */\nfunction 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\nmpl.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\nmpl.figure.prototype._key_event_extra = function(event, name) {\n // Handle any extra behaviour associated with a key event\n}\n\nmpl.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\nmpl.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\nmpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n this.message.textContent = tooltip;\n};\nmpl.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\nmpl.extensions = [\"eps\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\"];\n\nmpl.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\nmpl.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(\"
\");\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\nmpl.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(' ');\n fig.close_ws(fig, msg);\n}\n\nmpl.figure.prototype.close_ws = function(fig, msg){\n fig.send_message('closing', msg);\n // fig.ws.close()\n}\n\nmpl.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'] = ' ';\n}\n\nmpl.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\nmpl.figure.prototype._init_toolbar = function() {\n var fig = this;\n\n var nav_element = $('
')\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 = $(' ');\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 = $(' ');\n nav_element.append(status_bar);\n this.message = status_bar[0];\n\n // Add the close button to the window.\n var buttongrp = $('
');\n var 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\nmpl.figure.prototype._root_extra_style = function(el){\n var fig = this\n el.on(\"remove\", function(){\n\tfig.close_ws(fig, {});\n });\n}\n\nmpl.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\nmpl.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\nmpl.figure.prototype.handle_save = function(fig, msg) {\n fig.ondownload(fig, null);\n}\n\n\nmpl.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= 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.\nif (IPython.notebook.kernel != null) {\n IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n}\n",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
" "
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Example 3: Modify the high level of a square pulse inside a sequence\n",
"\n",
"#\n",
"\n",
"pulsebp = bb.BluePrint()\n",
"pulsebp.insertSegment(0, ramp, (0, 0), dur=5e-4)\n",
"pulsebp.insertSegment(1, ramp, (1, 1), dur=1e-3, name=\"varyme\")\n",
"pulsebp.insertSegment(2, \"waituntil\", 2e-3)\n",
"pulsebp.setSR(1e6)\n",
"\n",
"sinebp = bb.BluePrint()\n",
"sinebp.insertSegment(0, sine, (0.2e3, 0.5, 0.5, 0), dur=10e-3)\n",
"sinebp.setSR(1e6)\n",
"\n",
"elem1 = bb.Element()\n",
"elem1.addBluePrint(1, pulsebp)\n",
"\n",
"elem2 = bb.Element()\n",
"elem2.addBluePrint(1, sinebp)\n",
"\n",
"baseseq = bb.Sequence()\n",
"baseseq.setSR(1e6)\n",
"baseseq.addElement(1, elem1)\n",
"baseseq.addElement(2, elem2)\n",
"\n",
"baseseq.setSequenceSettings(1, 0, 20, 0, 0)\n",
"baseseq.setSequenceSettings(2, 0, 1, 0, 1)\n",
"\n",
"plotter(baseseq)\n",
"\n",
"# now vary this sequence\n",
"\n",
"poss = [1, 1]\n",
"channels = [1, 1]\n",
"names = [\"varyme\", \"varyme\"]\n",
"args = [\"start\", \"stop\"]\n",
"iters = [[1, 0.75, 0.5], [1, 0.75, 0.5]]\n",
"\n",
"newseq = bb.repeatAndVarySequence(baseseq, poss, channels, names, args, iters)\n",
"plotter(newseq)"
]
},
{
"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.10.11"
}
},
"nbformat": 4,
"nbformat_minor": 2
}