From 3372f9bd3e94b20cdff03c7017ac0972ed51cce5 Mon Sep 17 00:00:00 2001 From: Ian Hunt-Isaak Date: Wed, 16 Sep 2020 02:35:55 -0400 Subject: [PATCH] Add examples + explanations (#244) * expand examples - show setting image data + add explanation of `ioff` and `Output` - update github link - mention display(fig) for a static embed - remove stale example and add comment to main example * link to example notebook from readme --- README.md | 1 + examples/header_visible.ipynb | 98 -------------- examples/ipympl.ipynb | 244 +++++++++++++++++++++++++++++++++- 3 files changed, 240 insertions(+), 103 deletions(-) delete mode 100644 examples/header_visible.ipynb diff --git a/README.md b/README.md index d247f505..df9526b6 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ magic: ``` ## Example +See the [example notebook](https://github.com/matplotlib/ipympl/blob/master/examples/ipympl.ipynb) for more! ![matplotlib screencast](matplotlib.gif) diff --git a/examples/header_visible.ipynb b/examples/header_visible.ipynb deleted file mode 100644 index 5544f08d..00000000 --- a/examples/header_visible.ipynb +++ /dev/null @@ -1,98 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib widget" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "ExecuteTime": { - "end_time": "2019-08-07T08:07:06.908562Z", - "start_time": "2019-08-07T08:07:06.602919Z" - } - }, - "outputs": [], - "source": [ - "import matplotlib.pyplot as plt" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "fig, ax = plt.subplots()\n", - "fig.canvas.layout.width = '7in'\n", - "fig.canvas.layout.height= '5in'\n", - "\n", - "# if I hide the header here, I get a libpng error\n", - "# fig.canvas.header_visible = False\n", - "\n", - "ax.plot([1,2,3], [4,5,3])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# hiding after rendering works\n", - "fig.canvas.header_visible = False" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# hiding together with calls to toolbar options, work.\n", - "fig, ax = plt.subplots()\n", - "fig.canvas.layout.width = '7in'\n", - "fig.canvas.layout.height= '5in'\n", - "\n", - "fig.canvas.toolbar_visible = False\n", - "fig.canvas.header_visible = False\n", - "\n", - "ax.plot([1,2,3], [4,5,3])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "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.7.3" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/examples/ipympl.ipynb b/examples/ipympl.ipynb index 06014e13..9d80dcc8 100644 --- a/examples/ipympl.ipynb +++ b/examples/ipympl.ipynb @@ -8,7 +8,7 @@ "\n", "Enabling interaction with matplotlib charts in the Jupyter notebook and JupyterLab\n", "\n", - "https://github.com/matplotlib/jupyter-matplotlib" + "https://github.com/matplotlib/ipympl" ] }, { @@ -44,7 +44,7 @@ "outputs": [], "source": [ "fig.canvas.toolbar_visible = False\n", - "fig.canvas.header_visible = False" + "fig.canvas.header_visible = False # Hide the Figure name at the top of the figure" ] }, { @@ -71,16 +71,41 @@ "metadata": {}, "outputs": [], "source": [ + "# If true then scrolling while the mouse is over the canvas will not move the entire notebook\n", "fig.canvas.capture_scroll = True" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can also call `display` on `fig.canvas` to display the interactive plot anywhere in the notebooke" + ] + }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "fig.canvas" + "fig.canvas.toolbar_visible = True\n", + "display(fig.canvas)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Or you can `display(fig)` to embed the current plot as a png" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "display(fig)" ] }, { @@ -174,7 +199,111 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Interactions with other widgets and layouting" + "# Interactions with other widgets and layouting\n", + "\n", + "When you want to embed the figure into a layout of other widgets you should call `plt.ioff()` before creating the figure otherwise code inside of `plt.figure()` will display the canvas automatically and outside of your layout. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Without using `ioff`\n", + "\n", + "Here we will end up with the figure being displayed twice. The button won't do anything it just placed as an example of layouting." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import ipywidgets as widgets\n", + "\n", + "# ensure we are interactive mode \n", + "# this is default but if this notebook is executed out of order it may have been turned off\n", + "plt.ion()\n", + "\n", + "\n", + "fig = plt.figure()\n", + "ax = fig.gca()\n", + "ax.imshow(Z)\n", + "\n", + "widgets.AppLayout(\n", + " center=fig.canvas,\n", + " footer=widgets.Button(icon='check'),\n", + " pane_heights=[0, 6, 1]\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Fixing the double display with `ioff`\n", + "\n", + "If we make sure interactive mode is off when we create the figure then the figure will only display where we want it to.\n", + "\n", + "There is ongoing work to allow usage of `ioff` as a context manager, see the [ipympl issue](https://github.com/matplotlib/ipympl/issues/220) and the [matplotlib issue](https://github.com/matplotlib/matplotlib/issues/17013)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "fig = plt.figure()\n", + "plt.ion()\n", + "\n", + "ax = fig.gca()\n", + "ax.imshow(Z)\n", + "\n", + "widgets.AppLayout(\n", + " center=fig.canvas,\n", + " footer=widgets.Button(icon='check'),\n", + " pane_heights=[0, 6, 1]\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Fixing the double display with `ipywidgets.Output`\n", + "\n", + "Using `plt.ioff` use matplotlib to avoid the double display of the plot. You can also use `ipywidgets.Output` to capture the plot display to prevent this" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "out = widgets.Output()\n", + "with out:\n", + " fig = plt.figure()\n", + "\n", + "ax = fig.gca()\n", + "ax.imshow(Z)\n", + "\n", + "widgets.AppLayout(\n", + " center=out,\n", + " footer=widgets.Button(icon='check'),\n", + " pane_heights=[0, 6, 1]\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Interacting with other widgets\n", + "\n", + "## Changing a line plot with a slide" ] }, { @@ -226,6 +355,104 @@ " pane_heights=[0, 6, 1]\n", ")" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Update image data in a performant manner\n", + "\n", + "Two useful tricks to improve performance when updating an image displayed with matplolib are to:\n", + "1. Use the `set_data` method instead of calling imshow\n", + "2. Precompute and then index the array" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# precomputing all images\n", + "x = np.linspace(0,np.pi,200)\n", + "y = np.linspace(0,10,200)\n", + "X,Y = np.meshgrid(x,y)\n", + "parameter = np.linspace(-5,5)\n", + "example_image_stack = np.sin(X)[None,:,:]+np.exp(np.cos(Y[None,:,:]*parameter[:,None,None]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "fig = plt.figure()\n", + "plt.ion()\n", + "im = plt.imshow(example_image_stack[0])\n", + "\n", + "def update(change):\n", + " im.set_data(example_image_stack[change['new']])\n", + " fig.canvas.draw_idle()\n", + " \n", + " \n", + "slider = widgets.IntSlider(value=0, min=0, max=len(parameter)-1)\n", + "slider.observe(update, names='value')\n", + "widgets.VBox([slider, fig.canvas])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Debugging widget updates and matplotlib callbacks\n", + "\n", + "If an error is raised in the `update` function then will not always display in the notebook which can make debugging difficult. This same issue is also true for matplotlib callbacks on user events such as mousemovement, for example see [issue](https://github.com/matplotlib/ipympl/issues/116). There are two ways to see the output:\n", + "1. In jupyterlab the output will show up in the Log Console (View > Show Log Console)\n", + "2. using `ipywidgets.Output`\n", + "\n", + "Here is an example of using an `Output` to capture errors in the update function from the previous example. To induce errors we changed the slider limits so that out of bounds errors will occur:\n", + "\n", + "From: `slider = widgets.IntSlider(value=0, min=0, max=len(parameter)-1)`\n", + "\n", + "To: `slider = widgets.IntSlider(value=0, min=0, max=len(parameter)+10)`\n", + "\n", + "If you move the slider all the way to the right you should see errors from the Output widget" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plt.ioff()\n", + "fig = plt.figure()\n", + "plt.ion()\n", + "im = plt.imshow(example_image_stack[0])\n", + "\n", + "out = widgets.Output()\n", + "@out.capture()\n", + "def update(change):\n", + " with out:\n", + " if change['name'] == 'value':\n", + " im.set_data(example_image_stack[change['new']])\n", + " fig.canvas.draw_idle\n", + " \n", + " \n", + "slider = widgets.IntSlider(value=0, min=0, max=len(parameter)+10)\n", + "slider.observe(update)\n", + "display(widgets.VBox([slider, fig.canvas]))\n", + "display(out)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { @@ -244,7 +471,14 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.1" + "version": "3.7.8" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": {}, + "version_major": 2, + "version_minor": 0 + } } }, "nbformat": 4,