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

.. only:: html

    .. meta::
        :keywords: codex

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

        :ref:`Go to the end <sphx_glr_download_gallery_user_interfaces_embedding_webagg_sgskip.py>`
        to download the full example code.

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

.. _sphx_glr_gallery_user_interfaces_embedding_webagg_sgskip.py:


================
Embedding WebAgg
================

This example demonstrates how to embed Matplotlib WebAgg interactive plotting
in your own web application and framework.  It is not necessary to do all this
if you merely want to display a plot in a browser or use Matplotlib's built-in
Tornado-based server "on the side".

The framework being used must support web sockets.

.. GENERATED FROM PYTHON SOURCE LINES 13-275

.. code-block:: Python


    import argparse
    import io
    import json
    import mimetypes
    from pathlib import Path
    import signal
    import socket

    try:
        import tornado
    except ImportError as err:
        raise RuntimeError("This example requires tornado.") from err
    import tornado.httpserver
    import tornado.ioloop
    import tornado.web
    import tornado.websocket

    import numpy as np

    import matplotlib as mpl
    from matplotlib.backends.backend_webagg import (
        FigureManagerWebAgg, new_figure_manager_given_figure)
    from matplotlib.figure import Figure


    def create_figure():
        """
        Creates a simple example figure.
        """
        fig = Figure()
        ax = fig.add_subplot()
        t = np.arange(0.0, 3.0, 0.01)
        s = np.sin(2 * np.pi * t)
        ax.plot(t, s)
        return fig


    # The following is the content of the web page.  You would normally
    # generate this using some sort of template facility in your web
    # framework, but here we just use Python string formatting.
    html_content = """<!DOCTYPE html>
    <html lang="en">
      <head>
        <!-- TODO: There should be a way to include all of the required javascript
                   and CSS so matplotlib can add to the set in the future if it
                   needs to. -->
        <link rel="stylesheet" href="_static/css/page.css" type="text/css">
        <link rel="stylesheet" href="_static/css/boilerplate.css" type="text/css">
        <link rel="stylesheet" href="_static/css/fbm.css" type="text/css">
        <link rel="stylesheet" href="_static/css/mpl.css" type="text/css">
        <script src="mpl.js"></script>

        <script>
          /* This is a callback that is called when the user saves
             (downloads) a file.  Its purpose is really to map from a
             figure and file format to a url in the application. */
          function ondownload(figure, format) {
            window.open('download.' + format, '_blank');
          };

          function ready(fn) {
            if (document.readyState != "loading") {
              fn();
            } else {
              document.addEventListener("DOMContentLoaded", fn);
            }
          }

          ready(
            function() {
              /* It is up to the application to provide a websocket that the figure
                 will use to communicate to the server.  This websocket object can
                 also be a "fake" websocket that underneath multiplexes messages
                 from multiple figures, if necessary. */
              var websocket_type = mpl.get_websocket_type();
              var websocket = new websocket_type("%(ws_uri)sws");

              // mpl.figure creates a new figure on the webpage.
              var fig = new mpl.figure(
                  // A unique numeric identifier for the figure
                  %(fig_id)s,
                  // A websocket object (or something that behaves like one)
                  websocket,
                  // A function called when a file type is selected for download
                  ondownload,
                  // The HTML element in which to place the figure
                  document.getElementById("figure"));
            }
          );
        </script>

        <title>matplotlib</title>
      </head>

      <body>
        <div id="figure">
        </div>
      </body>
    </html>
    """


    class MyApplication(tornado.web.Application):
        class MainPage(tornado.web.RequestHandler):
            """
            Serves the main HTML page.
            """

            def get(self):
                manager = self.application.manager
                ws_uri = f"ws://{self.request.host}/"
                content = html_content % {
                    "ws_uri": ws_uri, "fig_id": manager.num}
                self.write(content)

        class MplJs(tornado.web.RequestHandler):
            """
            Serves the generated matplotlib javascript file.  The content
            is dynamically generated based on which toolbar functions the
            user has defined.  Call `FigureManagerWebAgg` to get its
            content.
            """

            def get(self):
                self.set_header('Content-Type', 'application/javascript')
                js_content = FigureManagerWebAgg.get_javascript()

                self.write(js_content)

        class Download(tornado.web.RequestHandler):
            """
            Handles downloading of the figure in various file formats.
            """

            def get(self, fmt):
                manager = self.application.manager
                self.set_header(
                    'Content-Type', mimetypes.types_map.get(fmt, 'binary'))
                buff = io.BytesIO()
                manager.canvas.figure.savefig(buff, format=fmt)
                self.write(buff.getvalue())

        class WebSocket(tornado.websocket.WebSocketHandler):
            """
            A websocket for interactive communication between the plot in
            the browser and the server.

            In addition to the methods required by tornado, it is required to
            have two callback methods:

                - ``send_json(json_content)`` is called by matplotlib when
                  it needs to send json to the browser.  `json_content` is
                  a JSON tree (Python dictionary), and it is the responsibility
                  of this implementation to encode it as a string to send over
                  the socket.

                - ``send_binary(blob)`` is called to send binary image data
                  to the browser.
            """
            supports_binary = True

            def open(self):
                # Register the websocket with the FigureManager.
                manager = self.application.manager
                manager.add_web_socket(self)
                if hasattr(self, 'set_nodelay'):
                    self.set_nodelay(True)

            def on_close(self):
                # When the socket is closed, deregister the websocket with
                # the FigureManager.
                manager = self.application.manager
                manager.remove_web_socket(self)

            def on_message(self, message):
                # The 'supports_binary' message is relevant to the
                # websocket itself.  The other messages get passed along
                # to matplotlib as-is.

                # Every message has a "type" and a "figure_id".
                message = json.loads(message)
                if message['type'] == 'supports_binary':
                    self.supports_binary = message['value']
                else:
                    manager = self.application.manager
                    manager.handle_json(message)

            def send_json(self, content):
                self.write_message(json.dumps(content))

            def send_binary(self, blob):
                if self.supports_binary:
                    self.write_message(blob, binary=True)
                else:
                    data_uri = ("data:image/png;base64," +
                                blob.encode('base64').replace('\n', ''))
                    self.write_message(data_uri)

        def __init__(self, figure):
            self.figure = figure
            self.manager = new_figure_manager_given_figure(id(figure), figure)

            super().__init__([
                # Static files for the CSS and JS
                (r'/_static/(.*)',
                 tornado.web.StaticFileHandler,
                 {'path': FigureManagerWebAgg.get_static_file_path()}),

                # Static images for the toolbar
                (r'/_images/(.*)',
                 tornado.web.StaticFileHandler,
                 {'path': Path(mpl.get_data_path(), 'images')}),

                # The page that contains all of the pieces
                ('/', self.MainPage),

                ('/mpl.js', self.MplJs),

                # Sends images and events to the browser, and receives
                # events from the browser
                ('/ws', self.WebSocket),

                # Handles the downloading (i.e., saving) of static images
                (r'/download.([a-z0-9.]+)', self.Download),
            ])


    if __name__ == "__main__":
        parser = argparse.ArgumentParser()
        parser.add_argument('-p', '--port', type=int, default=8080,
                            help='Port to listen on (0 for a random port).')
        args = parser.parse_args()

        figure = create_figure()
        application = MyApplication(figure)

        http_server = tornado.httpserver.HTTPServer(application)
        sockets = tornado.netutil.bind_sockets(args.port, '')
        http_server.add_sockets(sockets)

        for s in sockets:
            addr, port = s.getsockname()[:2]
            if s.family is socket.AF_INET6:
                addr = f'[{addr}]'
            print(f"Listening on http://{addr}:{port}/")
        print("Press Ctrl+C to quit")

        ioloop = tornado.ioloop.IOLoop.instance()

        def shutdown():
            ioloop.stop()
            print("Server stopped")

        old_handler = signal.signal(
            signal.SIGINT,
            lambda sig, frame: ioloop.add_callback_from_signal(shutdown))

        try:
            ioloop.start()
        finally:
            signal.signal(signal.SIGINT, old_handler)


.. _sphx_glr_download_gallery_user_interfaces_embedding_webagg_sgskip.py:

.. only:: html

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

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

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

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

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

    .. container:: sphx-glr-download sphx-glr-download-zip

      :download:`Download zipped: embedding_webagg_sgskip.zip <embedding_webagg_sgskip.zip>`


.. only:: html

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

    `Gallery generated by Sphinx-Gallery <https://sphinx-gallery.github.io>`_
