.. DO NOT EDIT.
.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY.
.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE:
.. "tutorials/advanced/blitting.py"
.. LINE NUMBERS ARE GIVEN BELOW.

.. only:: html

    .. note::
        :class: sphx-glr-download-link-note

        Click :ref:`here <sphx_glr_download_tutorials_advanced_blitting.py>`
        to download the full example code

.. rst-class:: sphx-glr-example-title

.. _sphx_glr_tutorials_advanced_blitting.py:


=================
Blitting tutorial
=================

'Blitting' is a `standard technique
<https://en.wikipedia.org/wiki/Bit_blit>`__ in raster graphics that,
in the context of Matplotlib, can be used to (drastically) improve
performance of interactive figures. For example, the
:mod:`~.animation` and :mod:`~.widgets` modules use blitting
internally. Here, we demonstrate how to implement your own blitting, outside
of these classes.

The source of the performance gains is simply not re-doing work we do
not have to.  If the limits of an Axes have not changed, then there is
no need to re-draw all of the ticks and tick-labels (particularly
because text is one of the more expensive things to render).

The procedure to save our work is roughly:

- draw the figure, but exclude any artists marked as 'animated'
- save a copy of the RBGA buffer

In the future, to update the 'animated' artists we

- restore our copy of the RGBA buffer
- redraw only the animated artists
- show the resulting image on the screen

thus saving us from having to re-draw everything which is _not_
animated.  One consequence of this procedure is that your animated
artists are always drawn at a higher z-order than the static artists.

Not all backends support blitting.  You can check if a given canvas does via
the `.FigureCanvasBase.supports_blit` property.

.. warning::

   This code does not work with the OSX backend (but does work with other
   GUI backends on mac).

Minimal example
---------------

We can use the `.FigureCanvasAgg` methods
`~.FigureCanvasAgg.copy_from_bbox` and
`~.FigureCanvasAgg.restore_region` in conjunction with setting
``animated=True`` on our artist to implement a minimal example that
uses blitting to accelerate rendering

.. GENERATED FROM PYTHON SOURCE LINES 52-99

.. code-block:: default


    import matplotlib.pyplot as plt
    import numpy as np

    x = np.linspace(0, 2 * np.pi, 100)

    fig, ax = plt.subplots()

    # animated=True tells matplotlib to only draw the artist when we
    # explicitly request it
    (ln,) = ax.plot(x, np.sin(x), animated=True)

    # make sure the window is raised, but the script keeps going
    plt.show(block=False)

    # stop to admire our empty window axes and ensure it is rendered at
    # least once.
    #
    # We need to fully draw the figure at its final size on the screen
    # before we continue on so that :
    #  a) we have the correctly sized and drawn background to grab
    #  b) we have a cached renderer so that ``ax.draw_artist`` works
    # so we spin the event loop to let the backend process any pending operations
    plt.pause(0.1)

    # get copy of entire figure (everything inside fig.bbox) sans animated artist
    bg = fig.canvas.copy_from_bbox(fig.bbox)
    # draw the animated artist, this uses a cached renderer
    ax.draw_artist(ln)
    # show the result to the screen, this pushes the updated RGBA buffer from the
    # renderer to the GUI framework so you can see it
    fig.canvas.blit(fig.bbox)

    for j in range(100):
        # reset the background back in the canvas state, screen unchanged
        fig.canvas.restore_region(bg)
        # update the artist, neither the canvas state nor the screen have changed
        ln.set_ydata(np.sin(x + (j / 100) * np.pi))
        # re-render the artist, updating the canvas state, but not the screen
        ax.draw_artist(ln)
        # copy the image to the GUI state, but screen might not changed yet
        fig.canvas.blit(fig.bbox)
        # flush any pending GUI events, re-painting the screen if needed
        fig.canvas.flush_events()
        # you can put a pause in if you want to slow things down
        # plt.pause(.1)




.. image:: /tutorials/advanced/images/sphx_glr_blitting_001.png
    :alt: blitting
    :class: sphx-glr-single-img





.. GENERATED FROM PYTHON SOURCE LINES 100-116

This example works and shows a simple animation, however because we
are only grabbing the background once, if the size of the figure in
pixels changes (due to either the size or dpi of the figure
changing) , the background will be invalid and result in incorrect
(but sometimes cool looking!) images.  There is also a global
variable and a fair amount of boiler plate which suggests we should
wrap this in a class.

Class-based example
-------------------

We can use a class to encapsulate the boilerplate logic and state of
restoring the background, drawing the artists, and then blitting the
result to the screen.  Additionally, we can use the ``'draw_event'``
callback to capture a new background whenever a full re-draw
happens to handle resizes correctly.

.. GENERATED FROM PYTHON SOURCE LINES 116-191

.. code-block:: default



    class BlitManager:
        def __init__(self, canvas, animated_artists=()):
            """
            Parameters
            ----------
            canvas : FigureCanvasAgg
                The canvas to work with, this only works for sub-classes of the Agg
                canvas which have the `~FigureCanvasAgg.copy_from_bbox` and
                `~FigureCanvasAgg.restore_region` methods.

            animated_artists : Iterable[Artist]
                List of the artists to manage
            """
            self.canvas = canvas
            self._bg = None
            self._artists = []

            for a in animated_artists:
                self.add_artist(a)
            # grab the background on every draw
            self.cid = canvas.mpl_connect("draw_event", self.on_draw)

        def on_draw(self, event):
            """Callback to register with 'draw_event'."""
            cv = self.canvas
            if event is not None:
                if event.canvas != cv:
                    raise RuntimeError
            self._bg = cv.copy_from_bbox(cv.figure.bbox)
            self._draw_animated()

        def add_artist(self, art):
            """
            Add an artist to be managed.

            Parameters
            ----------
            art : Artist

                The artist to be added.  Will be set to 'animated' (just
                to be safe).  *art* must be in the figure associated with
                the canvas this class is managing.

            """
            if art.figure != self.canvas.figure:
                raise RuntimeError
            art.set_animated(True)
            self._artists.append(art)

        def _draw_animated(self):
            """Draw all of the animated artists."""
            fig = self.canvas.figure
            for a in self._artists:
                fig.draw_artist(a)

        def update(self):
            """Update the screen with animated artists."""
            cv = self.canvas
            fig = cv.figure
            # paranoia in case we missed the draw event,
            if self._bg is None:
                self.on_draw(None)
            else:
                # restore the background
                cv.restore_region(self._bg)
                # draw all of the animated artists
                self._draw_animated()
                # update the GUI state
                cv.blit(fig.bbox)
            # let the GUI event loop process anything it has to do
            cv.flush_events()









.. GENERATED FROM PYTHON SOURCE LINES 192-194

Here is how we would use our class.  This is a slightly more complicated
example than the first case as we add a text frame counter as well.

.. GENERATED FROM PYTHON SOURCE LINES 194-222

.. code-block:: default


    # make a new figure
    fig, ax = plt.subplots()
    # add a line
    (ln,) = ax.plot(x, np.sin(x), animated=True)
    # add a frame number
    fr_number = ax.annotate(
        "0",
        (0, 1),
        xycoords="axes fraction",
        xytext=(10, -10),
        textcoords="offset points",
        ha="left",
        va="top",
        animated=True,
    )
    bm = BlitManager(fig.canvas, [ln, fr_number])
    # make sure our window is on the screen and drawn
    plt.show(block=False)
    plt.pause(.1)

    for j in range(100):
        # update the artists
        ln.set_ydata(np.sin(x + (j / 100) * np.pi))
        fr_number.set_text("frame: {j}".format(j=j))
        # tell the blitting manager to do it's thing
        bm.update()




.. image:: /tutorials/advanced/images/sphx_glr_blitting_002.png
    :alt: blitting
    :class: sphx-glr-single-img





.. GENERATED FROM PYTHON SOURCE LINES 223-225

This class does not depend on `.pyplot` and is suitable to embed
into larger GUI application.


.. rst-class:: sphx-glr-timing

   **Total running time of the script:** ( 0 minutes  2.025 seconds)


.. _sphx_glr_download_tutorials_advanced_blitting.py:


.. only :: html

 .. container:: sphx-glr-footer
    :class: sphx-glr-footer-example



  .. container:: sphx-glr-download sphx-glr-download-python

     :download:`Download Python source code: blitting.py <blitting.py>`



  .. container:: sphx-glr-download sphx-glr-download-jupyter

     :download:`Download Jupyter notebook: blitting.ipynb <blitting.ipynb>`


.. only:: html

 .. rst-class:: sphx-glr-signature

    Keywords: matplotlib code example, codex, python plot, pyplot
    `Gallery generated by Sphinx-Gallery
    <https://sphinx-gallery.readthedocs.io>`_