From 6edde3de0c0f799d4c7415efdba93a0a9f8a5a80 Mon Sep 17 00:00:00 2001 From: Michael Bewley Date: Mon, 29 Jan 2018 14:28:07 +1100 Subject: [PATCH 1/5] Added blitting support for jupyter notebooks (server side only), based on @blink1073's code. --- lib/matplotlib/backends/backend_webagg_core.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/backends/backend_webagg_core.py b/lib/matplotlib/backends/backend_webagg_core.py index e75014b1e632..fd90e4bfd62f 100644 --- a/lib/matplotlib/backends/backend_webagg_core.py +++ b/lib/matplotlib/backends/backend_webagg_core.py @@ -121,7 +121,7 @@ def _handle_key(key): class FigureCanvasWebAggCore(backend_agg.FigureCanvasAgg): - supports_blit = False + supports_blit = True def __init__(self, *args, **kwargs): backend_agg.FigureCanvasAgg.__init__(self, *args, **kwargs) @@ -162,9 +162,17 @@ def draw(self): # Swap the frames self.manager.refresh_all() + def blit(self, bbox=None): + self._png_is_old = True + self.manager.refresh_all() + def draw_idle(self): self.send_event("draw") + def copy_from_bbox(self, bbox): + renderer = self._last_renderer + return renderer.copy_from_bbox(bbox) + def set_image_mode(self, mode): """ Set the image mode for any subsequent images which will be sent @@ -215,6 +223,11 @@ def get_diff_image(self): # Swap the renderer frames self._renderer, self._last_renderer = ( self._last_renderer, renderer) + + self.figure._cachedRenderer = self._renderer + for ax in self.figure.axes: + ax._cachedRenderer = self._renderer + self._force_full = False self._png_is_old = False return buff @@ -393,6 +406,9 @@ def set_cursor(self, cursor): self.canvas.send_event("cursor", cursor=cursor) self.cursor = cursor + def dynamic_update(self): + self.canvas.draw_idle() + def draw_rubberband(self, event, x0, y0, x1, y1): self.canvas.send_event( "rubberband", x0=x0, y0=y0, x1=x1, y1=y1) From 5e93fe1df17352933294383dad498b23be6e1bd1 Mon Sep 17 00:00:00 2001 From: Michael Bewley Date: Mon, 29 Jan 2018 14:30:13 +1100 Subject: [PATCH 2/5] Added UAT notebook from on #4290 (after updating format for compatibility with latest jupyter notebook). --- lib/matplotlib/backends/nbagg_uat.ipynb | 480 ++++++++++++++++++++++++ 1 file changed, 480 insertions(+) create mode 100644 lib/matplotlib/backends/nbagg_uat.ipynb diff --git a/lib/matplotlib/backends/nbagg_uat.ipynb b/lib/matplotlib/backends/nbagg_uat.ipynb new file mode 100644 index 000000000000..5cc6ced0a608 --- /dev/null +++ b/lib/matplotlib/backends/nbagg_uat.ipynb @@ -0,0 +1,480 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import print_function\n", + "from imp import reload" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## UAT for NbAgg backend.\n", + "\n", + "The first line simply reloads matplotlib, uses the nbagg backend and then reloads the backend, just to ensure we have the latest modification to the backend code. Note: The underlying JavaScript will not be updated by this process, so a refresh of the browser after clearing the output and saving is necessary to clear everything fully." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib\n", + "reload(matplotlib)\n", + "\n", + "matplotlib.use('nbagg')\n", + "\n", + "import matplotlib.backends.backend_nbagg\n", + "reload(matplotlib.backends.backend_nbagg)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### UAT 1 - Simple figure creation using pyplot\n", + "\n", + "Should produce a figure window which is interactive with the pan and zoom buttons. (Do not press the close button, but any others may be used)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.backends.backend_webagg_core\n", + "reload(matplotlib.backends.backend_webagg_core)\n", + "\n", + "import matplotlib.pyplot as plt\n", + "plt.interactive(False)\n", + "\n", + "fig1 = plt.figure()\n", + "plt.plot(range(10))\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### UAT 2 - Creation of another figure, without the need to do plt.figure.\n", + "\n", + "As above, a new figure should be created." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plt.plot([3, 2, 1])\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### UAT 3 - Connection info\n", + "\n", + "The printout should show that there are two figures which have active CommSockets, and no figures pending show." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(matplotlib.backends.backend_nbagg.connection_info())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### UAT 4 - Closing figures\n", + "\n", + "Closing a specific figure instance should turn the figure into a plain image - the UI should have been removed. In this case, scroll back to the first figure and assert this is the case." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plt.close(fig1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### UAT 5 - No show without plt.show in non-interactive mode\n", + "\n", + "Simply doing a plt.plot should not show a new figure, nor indeed update an existing one (easily verified in UAT 6).\n", + "The output should simply be a list of Line2D instances." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plt.plot(range(10))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### UAT 6 - Connection information\n", + "\n", + "We just created a new figure, but didn't show it. Connection info should no longer have \"Figure 1\" (as we closed it in UAT 4) and should have figure 2 and 3, with Figure 3 without any connections. There should be 1 figure pending." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "print(matplotlib.backends.backend_nbagg.connection_info())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### UAT 7 - Show of previously created figure\n", + "\n", + "We should be able to show a figure we've previously created. The following should produce two figure windows." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plt.show()\n", + "plt.figure()\n", + "plt.plot(range(5))\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### UAT 8 - Interactive mode\n", + "\n", + "In interactive mode, creating a line should result in a figure being shown." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plt.interactive(True)\n", + "plt.figure()\n", + "plt.plot([3, 2, 1])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Subsequent lines should be added to the existing figure, rather than creating a new one." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plt.plot(range(3))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Disable interactive mode again." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plt.interactive(False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### UAT 9 - Multiple shows\n", + "\n", + "Unlike most of the other matplotlib backends, we may want to see a figure multiple times (with or without synchronisation between the views, though the former is not yet implemented). Assert that plt.gcf().canvas.manager.reshow() results in another figure window which is synchronised upon pan & zoom." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plt.gcf().canvas.manager.reshow()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### UAT 10 - Saving notebook\n", + "\n", + "Saving the notebook (with CTRL+S or File->Save) should result in the saved notebook having static versions of the figues embedded within. The image should be the last update from user interaction and interactive plotting. (check by converting with ``ipython nbconvert ``)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### UAT 11 - Creation of a new figure on second show\n", + "\n", + "Create a figure, show it, then create a new axes and show it. The result should be a new figure.\n", + "\n", + "**BUG: Sometimes this doesn't work - not sure why (@pelson).**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "fig = plt.figure()\n", + "plt.axes()\n", + "plt.show()\n", + "\n", + "plt.plot([1, 2, 3])\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### UAT 12 - OO interface\n", + "\n", + "Should produce a new figure and plot it." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from matplotlib.backends.backend_nbagg import new_figure_manager,show\n", + "\n", + "manager = new_figure_manager(1000)\n", + "fig = manager.canvas.figure\n", + "ax = fig.add_subplot(1,1,1)\n", + "ax.plot([1,2,3])\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## UAT 13 - Animation\n", + "\n", + "The following should generate an animated line:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.animation as animation\n", + "import numpy as np\n", + "\n", + "fig, ax = plt.subplots()\n", + "\n", + "x = np.arange(0, 2*np.pi, 0.01) # x-array\n", + "line, = ax.plot(x, np.sin(x))\n", + "\n", + "def animate(i):\n", + " line.set_ydata(np.sin(x+i/10.0)) # update the data\n", + " return line,\n", + "\n", + "#Init only required for blitting to give a clean slate.\n", + "def init():\n", + " line.set_ydata(np.ma.array(x, mask=True))\n", + " return line,\n", + "\n", + "ani = animation.FuncAnimation(fig, animate, np.arange(1, 200), init_func=init,\n", + " interval=32., blit=True)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### UAT 14 - Keyboard shortcuts in IPython after close of figure\n", + "\n", + "After closing the previous figure (with the close button above the figure) the IPython keyboard shortcuts should still function.\n", + "\n", + "### UAT 15 - Figure face colours\n", + "\n", + "The nbagg honours all colours appart from that of the figure.patch. The two plots below should produce a figure with a transparent background and a red background respectively (check the transparency by closing the figure, and dragging the resulting image over other content). There should be no yellow figure." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib\n", + "matplotlib.rcParams.update({'figure.facecolor': 'red',\n", + " 'savefig.facecolor': 'yellow'})\n", + "plt.figure()\n", + "plt.plot([3, 2, 1])\n", + "\n", + "with matplotlib.rc_context({'nbagg.transparent': False}):\n", + " plt.figure()\n", + "\n", + "plt.plot([3, 2, 1])\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### UAT 16 - Events\n", + "\n", + "Pressing any keyboard key or mouse button (or scrolling) should cycle the line line while the figure has focus. The figure should have focus by default when it is created and re-gain it by clicking on the canvas. Clicking anywhere outside of the figure should release focus, but moving the mouse out of the figure should not release focus." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import itertools\n", + "fig, ax = plt.subplots()\n", + "x = np.linspace(0,10,10000)\n", + "y = np.sin(x)\n", + "ln, = ax.plot(x,y)\n", + "evt = []\n", + "colors = iter(itertools.cycle(['r', 'g', 'b', 'k', 'c']))\n", + "def on_event(event):\n", + " if event.name.startswith('key'):\n", + " fig.suptitle('%s: %s' % (event.name, event.key))\n", + " elif event.name == 'scroll_event':\n", + " fig.suptitle('%s: %s' % (event.name, event.step))\n", + " else:\n", + " fig.suptitle('%s: %s' % (event.name, event.button))\n", + " evt.append(event)\n", + " ln.set_color(next(colors))\n", + " fig.canvas.draw()\n", + " fig.canvas.draw_idle()\n", + "\n", + "fig.canvas.mpl_connect('button_press_event', on_event)\n", + "fig.canvas.mpl_connect('button_release_event', on_event)\n", + "fig.canvas.mpl_connect('scroll_event', on_event)\n", + "fig.canvas.mpl_connect('key_press_event', on_event)\n", + "fig.canvas.mpl_connect('key_release_event', on_event)\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### UAT 17 - Blitting\n", + "\n", + "Clicking on the figure should plot a green horizontal line moving up the axes.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "prev_artist = None\n", + "cur_y = 0.1\n", + "\n", + "def onclick_handle(event):\n", + " \"\"\"Should draw elevating green line on each mouse click\"\"\"\n", + " global ax, bg, prev_artist, cur_y\n", + " ax.figure.canvas.restore_region(bg)\n", + " if prev_artist:\n", + " prev_artist.remove()\n", + " cur_y += 0.1\n", + " prev_artist = ax.plot([0, 1], [cur_y, cur_y], 'g')[0]\n", + " ax.draw_artist(prev_artist)\n", + " ax.figure.canvas.blit(ax.bbox)\n", + " print(\"Drew line\")\n", + "\n", + "fig, ax = plt.subplots()\n", + "ax.plot([0, 1], [0, 1], 'r')\n", + "plt.show()\n", + "ax.figure.canvas.draw()\n", + "bg = ax.figure.canvas.copy_from_bbox(ax.bbox)\n", + "ax.figure.canvas.mpl_connect('button_press_event', onclick_handle)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.3" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} From 1c4c1c2631f0904cdd0d9777e8de187d0314d515 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 10 Feb 2018 18:49:03 -0500 Subject: [PATCH 3/5] TST: merge the two nbagg_uat.ipynb files --- lib/matplotlib/backends/nbagg_uat.ipynb | 480 ------------------ .../backends/web_backend/nbagg_uat.ipynb | 157 +++--- 2 files changed, 74 insertions(+), 563 deletions(-) delete mode 100644 lib/matplotlib/backends/nbagg_uat.ipynb diff --git a/lib/matplotlib/backends/nbagg_uat.ipynb b/lib/matplotlib/backends/nbagg_uat.ipynb deleted file mode 100644 index 5cc6ced0a608..000000000000 --- a/lib/matplotlib/backends/nbagg_uat.ipynb +++ /dev/null @@ -1,480 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from __future__ import print_function\n", - "from imp import reload" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## UAT for NbAgg backend.\n", - "\n", - "The first line simply reloads matplotlib, uses the nbagg backend and then reloads the backend, just to ensure we have the latest modification to the backend code. Note: The underlying JavaScript will not be updated by this process, so a refresh of the browser after clearing the output and saving is necessary to clear everything fully." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import matplotlib\n", - "reload(matplotlib)\n", - "\n", - "matplotlib.use('nbagg')\n", - "\n", - "import matplotlib.backends.backend_nbagg\n", - "reload(matplotlib.backends.backend_nbagg)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### UAT 1 - Simple figure creation using pyplot\n", - "\n", - "Should produce a figure window which is interactive with the pan and zoom buttons. (Do not press the close button, but any others may be used)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import matplotlib.backends.backend_webagg_core\n", - "reload(matplotlib.backends.backend_webagg_core)\n", - "\n", - "import matplotlib.pyplot as plt\n", - "plt.interactive(False)\n", - "\n", - "fig1 = plt.figure()\n", - "plt.plot(range(10))\n", - "\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### UAT 2 - Creation of another figure, without the need to do plt.figure.\n", - "\n", - "As above, a new figure should be created." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plt.plot([3, 2, 1])\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### UAT 3 - Connection info\n", - "\n", - "The printout should show that there are two figures which have active CommSockets, and no figures pending show." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(matplotlib.backends.backend_nbagg.connection_info())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### UAT 4 - Closing figures\n", - "\n", - "Closing a specific figure instance should turn the figure into a plain image - the UI should have been removed. In this case, scroll back to the first figure and assert this is the case." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plt.close(fig1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### UAT 5 - No show without plt.show in non-interactive mode\n", - "\n", - "Simply doing a plt.plot should not show a new figure, nor indeed update an existing one (easily verified in UAT 6).\n", - "The output should simply be a list of Line2D instances." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plt.plot(range(10))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### UAT 6 - Connection information\n", - "\n", - "We just created a new figure, but didn't show it. Connection info should no longer have \"Figure 1\" (as we closed it in UAT 4) and should have figure 2 and 3, with Figure 3 without any connections. There should be 1 figure pending." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(matplotlib.backends.backend_nbagg.connection_info())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### UAT 7 - Show of previously created figure\n", - "\n", - "We should be able to show a figure we've previously created. The following should produce two figure windows." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plt.show()\n", - "plt.figure()\n", - "plt.plot(range(5))\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### UAT 8 - Interactive mode\n", - "\n", - "In interactive mode, creating a line should result in a figure being shown." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plt.interactive(True)\n", - "plt.figure()\n", - "plt.plot([3, 2, 1])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Subsequent lines should be added to the existing figure, rather than creating a new one." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plt.plot(range(3))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Disable interactive mode again." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plt.interactive(False)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### UAT 9 - Multiple shows\n", - "\n", - "Unlike most of the other matplotlib backends, we may want to see a figure multiple times (with or without synchronisation between the views, though the former is not yet implemented). Assert that plt.gcf().canvas.manager.reshow() results in another figure window which is synchronised upon pan & zoom." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plt.gcf().canvas.manager.reshow()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### UAT 10 - Saving notebook\n", - "\n", - "Saving the notebook (with CTRL+S or File->Save) should result in the saved notebook having static versions of the figues embedded within. The image should be the last update from user interaction and interactive plotting. (check by converting with ``ipython nbconvert ``)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### UAT 11 - Creation of a new figure on second show\n", - "\n", - "Create a figure, show it, then create a new axes and show it. The result should be a new figure.\n", - "\n", - "**BUG: Sometimes this doesn't work - not sure why (@pelson).**" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "fig = plt.figure()\n", - "plt.axes()\n", - "plt.show()\n", - "\n", - "plt.plot([1, 2, 3])\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### UAT 12 - OO interface\n", - "\n", - "Should produce a new figure and plot it." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from matplotlib.backends.backend_nbagg import new_figure_manager,show\n", - "\n", - "manager = new_figure_manager(1000)\n", - "fig = manager.canvas.figure\n", - "ax = fig.add_subplot(1,1,1)\n", - "ax.plot([1,2,3])\n", - "fig.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## UAT 13 - Animation\n", - "\n", - "The following should generate an animated line:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import matplotlib.animation as animation\n", - "import numpy as np\n", - "\n", - "fig, ax = plt.subplots()\n", - "\n", - "x = np.arange(0, 2*np.pi, 0.01) # x-array\n", - "line, = ax.plot(x, np.sin(x))\n", - "\n", - "def animate(i):\n", - " line.set_ydata(np.sin(x+i/10.0)) # update the data\n", - " return line,\n", - "\n", - "#Init only required for blitting to give a clean slate.\n", - "def init():\n", - " line.set_ydata(np.ma.array(x, mask=True))\n", - " return line,\n", - "\n", - "ani = animation.FuncAnimation(fig, animate, np.arange(1, 200), init_func=init,\n", - " interval=32., blit=True)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### UAT 14 - Keyboard shortcuts in IPython after close of figure\n", - "\n", - "After closing the previous figure (with the close button above the figure) the IPython keyboard shortcuts should still function.\n", - "\n", - "### UAT 15 - Figure face colours\n", - "\n", - "The nbagg honours all colours appart from that of the figure.patch. The two plots below should produce a figure with a transparent background and a red background respectively (check the transparency by closing the figure, and dragging the resulting image over other content). There should be no yellow figure." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import matplotlib\n", - "matplotlib.rcParams.update({'figure.facecolor': 'red',\n", - " 'savefig.facecolor': 'yellow'})\n", - "plt.figure()\n", - "plt.plot([3, 2, 1])\n", - "\n", - "with matplotlib.rc_context({'nbagg.transparent': False}):\n", - " plt.figure()\n", - "\n", - "plt.plot([3, 2, 1])\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### UAT 16 - Events\n", - "\n", - "Pressing any keyboard key or mouse button (or scrolling) should cycle the line line while the figure has focus. The figure should have focus by default when it is created and re-gain it by clicking on the canvas. Clicking anywhere outside of the figure should release focus, but moving the mouse out of the figure should not release focus." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import itertools\n", - "fig, ax = plt.subplots()\n", - "x = np.linspace(0,10,10000)\n", - "y = np.sin(x)\n", - "ln, = ax.plot(x,y)\n", - "evt = []\n", - "colors = iter(itertools.cycle(['r', 'g', 'b', 'k', 'c']))\n", - "def on_event(event):\n", - " if event.name.startswith('key'):\n", - " fig.suptitle('%s: %s' % (event.name, event.key))\n", - " elif event.name == 'scroll_event':\n", - " fig.suptitle('%s: %s' % (event.name, event.step))\n", - " else:\n", - " fig.suptitle('%s: %s' % (event.name, event.button))\n", - " evt.append(event)\n", - " ln.set_color(next(colors))\n", - " fig.canvas.draw()\n", - " fig.canvas.draw_idle()\n", - "\n", - "fig.canvas.mpl_connect('button_press_event', on_event)\n", - "fig.canvas.mpl_connect('button_release_event', on_event)\n", - "fig.canvas.mpl_connect('scroll_event', on_event)\n", - "fig.canvas.mpl_connect('key_press_event', on_event)\n", - "fig.canvas.mpl_connect('key_release_event', on_event)\n", - "\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### UAT 17 - Blitting\n", - "\n", - "Clicking on the figure should plot a green horizontal line moving up the axes.\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import matplotlib.pyplot as plt\n", - "\n", - "prev_artist = None\n", - "cur_y = 0.1\n", - "\n", - "def onclick_handle(event):\n", - " \"\"\"Should draw elevating green line on each mouse click\"\"\"\n", - " global ax, bg, prev_artist, cur_y\n", - " ax.figure.canvas.restore_region(bg)\n", - " if prev_artist:\n", - " prev_artist.remove()\n", - " cur_y += 0.1\n", - " prev_artist = ax.plot([0, 1], [cur_y, cur_y], 'g')[0]\n", - " ax.draw_artist(prev_artist)\n", - " ax.figure.canvas.blit(ax.bbox)\n", - " print(\"Drew line\")\n", - "\n", - "fig, ax = plt.subplots()\n", - "ax.plot([0, 1], [0, 1], 'r')\n", - "plt.show()\n", - "ax.figure.canvas.draw()\n", - "bg = ax.figure.canvas.copy_from_bbox(ax.bbox)\n", - "ax.figure.canvas.mpl_connect('button_press_event', onclick_handle)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.3" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/lib/matplotlib/backends/web_backend/nbagg_uat.ipynb b/lib/matplotlib/backends/web_backend/nbagg_uat.ipynb index dfb264c37591..2f3131dbc976 100644 --- a/lib/matplotlib/backends/web_backend/nbagg_uat.ipynb +++ b/lib/matplotlib/backends/web_backend/nbagg_uat.ipynb @@ -3,9 +3,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "from __future__ import print_function\n", @@ -24,12 +22,11 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "import matplotlib\n", + "\n", "reload(matplotlib)\n", "\n", "matplotlib.use('nbagg')\n", @@ -50,16 +47,14 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "import matplotlib.backends.backend_webagg_core\n", "reload(matplotlib.backends.backend_webagg_core)\n", "\n", "import matplotlib.pyplot as plt\n", - "plt.interactive(False)\n", + "plt.interactive(True)\n", "\n", "fig1 = plt.figure()\n", "plt.plot(range(10))\n", @@ -79,9 +74,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "plt.plot([3, 2, 1])\n", @@ -100,9 +93,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "print(matplotlib.backends.backend_nbagg.connection_info())" @@ -120,9 +111,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "plt.close(fig1)" @@ -141,9 +130,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "plt.plot(range(10))" @@ -161,9 +148,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "print(matplotlib.backends.backend_nbagg.connection_info())" @@ -181,9 +166,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "plt.show()\n", @@ -204,9 +187,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "plt.interactive(True)\n", @@ -224,9 +205,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "plt.plot(range(3))" @@ -242,9 +221,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "print(matplotlib.backends.backend_nbagg.connection_info())" @@ -260,9 +237,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "plt.interactive(False)" @@ -280,9 +255,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "plt.gcf().canvas.manager.reshow()" @@ -311,9 +284,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "fig = plt.figure()\n", @@ -336,9 +307,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "from matplotlib.backends.backend_nbagg import new_figure_manager,show\n", @@ -362,9 +331,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "import matplotlib.animation as animation\n", @@ -405,9 +372,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "import matplotlib\n", @@ -431,9 +396,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "import itertools\n", @@ -478,9 +441,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "import time\n", @@ -507,9 +468,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", @@ -532,9 +491,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", @@ -550,9 +507,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", @@ -570,7 +525,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### UAT17 - stopping figure when removed from DOM\n", + "### UAT18 - stopping figure when removed from DOM\n", "\n", "When the div that contains from the figure is removed from the DOM the figure should shut down it's comm, and if the python-side figure has no more active comms, it should destroy the figure. Repeatedly running the cell below should always have the same figure number" ] @@ -578,9 +533,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", @@ -598,29 +551,67 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "fig.canvas.manager.reshow()" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": { "collapsed": true }, + "source": [ + "### UAT 17 - Blitting\n", + "\n", + "\n", + "Clicking on the figure should plot a green horizontal line moving up the axes.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import itertools\n", + "\n", + "cnt = itertools.count()\n", + "\n", + "\n", + "def onclick_handle(event):\n", + " \"\"\"Should draw elevating green line on each mouse click\"\"\"\n", + " ax.figure.canvas.restore_region(bg)\n", + "\n", + " cur_y = next(cnt) * 0.1\n", + " ln.set_ydata([cur_y, cur_y])\n", + " ax.draw_artist(ln)\n", + " ax.figure.canvas.blit(ax.bbox)\n", + "\n", + "\n", + "fig, ax = plt.subplots()\n", + "ax.plot([0, 1], [0, 1], 'r')\n", + "ln, = ax.plot([0, 1], [0, 0], 'g', animated=True)\n", + "plt.show()\n", + "ax.figure.canvas.draw()\n", + "bg = ax.figure.canvas.copy_from_bbox(ax.bbox)\n", + "ax.figure.canvas.mpl_connect('button_press_event', onclick_handle)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "dd36", "language": "python", - "name": "python3" + "name": "myenv" }, "language_info": { "codemirror_mode": { @@ -632,9 +623,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.4.3" + "version": "3.6.4" } }, "nbformat": 4, - "nbformat_minor": 0 + "nbformat_minor": 1 } From 8322e22072f44b9de577bd3b21ced38d565365ac Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 10 Feb 2018 18:49:24 -0500 Subject: [PATCH 4/5] MNT: do not add deprecated helper method Version on base-class is identical --- lib/matplotlib/backends/backend_webagg_core.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/matplotlib/backends/backend_webagg_core.py b/lib/matplotlib/backends/backend_webagg_core.py index fd90e4bfd62f..4d7c942f17f2 100644 --- a/lib/matplotlib/backends/backend_webagg_core.py +++ b/lib/matplotlib/backends/backend_webagg_core.py @@ -406,9 +406,6 @@ def set_cursor(self, cursor): self.canvas.send_event("cursor", cursor=cursor) self.cursor = cursor - def dynamic_update(self): - self.canvas.draw_idle() - def draw_rubberband(self, event, x0, y0, x1, y1): self.canvas.send_event( "rubberband", x0=x0, y0=y0, x1=x1, y1=y1) From 0111e12e3d2e5fdbd307f2732e4e21d890bfb228 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sat, 10 Feb 2018 18:58:36 -0500 Subject: [PATCH 5/5] TST: tweak the blitting UAT a bit Grab the background after the first draw to avoid issue with hi-dpi screens. --- lib/matplotlib/backends/web_backend/nbagg_uat.ipynb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/backends/web_backend/nbagg_uat.ipynb b/lib/matplotlib/backends/web_backend/nbagg_uat.ipynb index 2f3131dbc976..c2662722e995 100644 --- a/lib/matplotlib/backends/web_backend/nbagg_uat.ipynb +++ b/lib/matplotlib/backends/web_backend/nbagg_uat.ipynb @@ -578,13 +578,16 @@ "import itertools\n", "\n", "cnt = itertools.count()\n", - "\n", + "bg = None\n", "\n", "def onclick_handle(event):\n", " \"\"\"Should draw elevating green line on each mouse click\"\"\"\n", + " global bg\n", + " if bg is None:\n", + " bg = ax.figure.canvas.copy_from_bbox(ax.bbox) \n", " ax.figure.canvas.restore_region(bg)\n", "\n", - " cur_y = next(cnt) * 0.1\n", + " cur_y = (next(cnt) % 10) * 0.1\n", " ln.set_ydata([cur_y, cur_y])\n", " ax.draw_artist(ln)\n", " ax.figure.canvas.blit(ax.bbox)\n", @@ -595,7 +598,7 @@ "ln, = ax.plot([0, 1], [0, 0], 'g', animated=True)\n", "plt.show()\n", "ax.figure.canvas.draw()\n", - "bg = ax.figure.canvas.copy_from_bbox(ax.bbox)\n", + "\n", "ax.figure.canvas.mpl_connect('button_press_event', onclick_handle)" ] },