diff --git a/lib/matplotlib/backends/backend_webagg_core.py b/lib/matplotlib/backends/backend_webagg_core.py index d8016e537563..9d498c830b51 100644 --- a/lib/matplotlib/backends/backend_webagg_core.py +++ b/lib/matplotlib/backends/backend_webagg_core.py @@ -118,7 +118,7 @@ def _handle_key(key): class FigureCanvasWebAggCore(backend_agg.FigureCanvasAgg): - supports_blit = False + supports_blit = True def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -153,6 +153,10 @@ def draw(self): finally: self.manager.refresh_all() # Swap the frames. + def blit(self, bbox=None): + self._png_is_old = True + self.manager.refresh_all() + def draw_idle(self): self.send_event("draw") @@ -189,18 +193,14 @@ def get_diff_image(self): output = buff else: self.set_image_mode('diff') - last_buffer = (np.frombuffer(self._last_renderer.buffer_rgba(), - dtype=np.uint32) - .reshape((renderer.height, renderer.width))) - diff = buff != last_buffer + diff = buff != self._last_buff output = np.where(diff, buff, 0) buf = BytesIO() data = output.view(dtype=np.uint8).reshape((*output.shape, 4)) Image.fromarray(data).save(buf, format="png") - # Swap the renderer frames - self._renderer, self._last_renderer = ( - self._last_renderer, renderer) + # store the current buffer so we can compute the next diff + np.copyto(self._last_buff, buff) self._force_full = False self._png_is_old = False return buf.getvalue() @@ -220,9 +220,10 @@ def get_renderer(self, cleared=None): if need_new_renderer: self._renderer = backend_agg.RendererAgg( w, h, self.figure.dpi) - self._last_renderer = backend_agg.RendererAgg( - w, h, self.figure.dpi) self._lastKey = key + self._last_buff = np.copy(np.frombuffer( + self._renderer.buffer_rgba(), dtype=np.uint32 + ).reshape((self._renderer.height, self._renderer.width))) elif cleared: self._renderer.clear() diff --git a/lib/matplotlib/backends/web_backend/nbagg_uat.ipynb b/lib/matplotlib/backends/web_backend/nbagg_uat.ipynb index 8ac7434e53e1..a914e7190664 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 imp import reload" @@ -23,9 +21,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "import matplotlib\n", @@ -49,9 +45,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "import matplotlib.backends.backend_webagg_core\n", @@ -78,9 +72,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "plt.plot([3, 2, 1])\n", @@ -99,9 +91,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "print(matplotlib.backends.backend_nbagg.connection_info())" @@ -119,12 +109,11 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ - "plt.close(fig1)" + "plt.close(fig1)\n", + "plt.close('all')" ] }, { @@ -140,9 +129,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "plt.plot(range(10))" @@ -160,9 +147,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "print(matplotlib.backends.backend_nbagg.connection_info())" @@ -180,9 +165,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "plt.show()\n", @@ -203,9 +186,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "plt.interactive(True)\n", @@ -223,9 +204,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "plt.plot(range(3))" @@ -241,9 +220,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "print(matplotlib.backends.backend_nbagg.connection_info())" @@ -259,9 +236,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "plt.interactive(False)" @@ -279,9 +254,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "plt.gcf().canvas.manager.reshow()" @@ -310,9 +283,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "fig = plt.figure()\n", @@ -335,9 +306,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "from matplotlib.backends.backend_nbagg import new_figure_manager,show\n", @@ -361,9 +330,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "import matplotlib.animation as animation\n", @@ -384,7 +351,7 @@ " return line,\n", "\n", "ani = animation.FuncAnimation(fig, animate, np.arange(1, 200), init_func=init,\n", - " interval=32., blit=True)\n", + " interval=100., blit=True)\n", "plt.show()" ] }, @@ -404,9 +371,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "import matplotlib\n", @@ -430,9 +395,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "import itertools\n", @@ -477,9 +440,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "import time\n", @@ -506,9 +467,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", @@ -531,9 +490,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", @@ -549,9 +506,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", @@ -569,7 +524,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### UAT17 - stopping figure when removed from DOM\n", + "### UAT 18 - 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" ] @@ -577,9 +532,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", @@ -597,20 +550,59 @@ { "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 19 - Blitting\n", + "\n", + "Clicking on the figure should plot a green horizontal line moving up the axes." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import itertools\n", + "\n", + "cnt = itertools.count()\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) % 10) * 0.1\n", + " ln.set_ydata([cur_y, cur_y])\n", + " ax.draw_artist(ln)\n", + " ax.figure.canvas.blit(ax.bbox)\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", + "\n", + "ax.figure.canvas.mpl_connect('button_press_event', onclick_handle)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, "outputs": [], "source": [] } @@ -631,9 +623,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.4.3" + "version": "3.8.5" } }, "nbformat": 4, - "nbformat_minor": 0 + "nbformat_minor": 1 }