嵌入WebAgg

此示例演示如何在您自己的Web应用程序和框架中嵌入matplotlib WebAgg交互式绘图。 基于龙卷风的服务器“侧面”是基于龙卷风的服务器。

使用的框架必须支持Web套接字。

  1. import io
  2. try:
  3. import tornado
  4. except ImportError:
  5. raise RuntimeError("This example requires tornado.")
  6. import tornado.web
  7. import tornado.httpserver
  8. import tornado.ioloop
  9. import tornado.websocket
  10. from matplotlib.backends.backend_webagg_core import (
  11. FigureManagerWebAgg, new_figure_manager_given_figure)
  12. from matplotlib.figure import Figure
  13. import numpy as np
  14. import json
  15. def create_figure():
  16. """
  17. Creates a simple example figure.
  18. """
  19. fig = Figure()
  20. a = fig.add_subplot(111)
  21. t = np.arange(0.0, 3.0, 0.01)
  22. s = np.sin(2 * np.pi * t)
  23. a.plot(t, s)
  24. return fig
  25. # The following is the content of the web page. You would normally
  26. # generate this using some sort of template facility in your web
  27. # framework, but here we just use Python string formatting.
  28. html_content = """
  29. <html>
  30. <head>
  31. <!-- TODO: There should be a way to include all of the required javascript
  32. and CSS so matplotlib can add to the set in the future if it
  33. needs to. -->
  34. <link rel="stylesheet" href="_static/css/page.css" type="text/css">
  35. <link rel="stylesheet" href="_static/css/boilerplate.css" type="text/css" />
  36. <link rel="stylesheet" href="_static/css/fbm.css" type="text/css" />
  37. <link rel="stylesheet" href="_static/jquery/css/themes/base/jquery-ui.min.css" >
  38. <script src="_static/jquery/js/jquery-1.11.3.min.js"></script>
  39. <script src="_static/jquery/js/jquery-ui.min.js"></script>
  40. <script src="mpl.js"></script>
  41. <script>
  42. /* This is a callback that is called when the user saves
  43. (downloads) a file. Its purpose is really to map from a
  44. figure and file format to a url in the application. */
  45. function ondownload(figure, format) {
  46. window.open('download.' + format, '_blank');
  47. };
  48. $(document).ready(
  49. function() {
  50. /* It is up to the application to provide a websocket that the figure
  51. will use to communicate to the server. This websocket object can
  52. also be a "fake" websocket that underneath multiplexes messages
  53. from multiple figures, if necessary. */
  54. var websocket_type = mpl.get_websocket_type();
  55. var websocket = new websocket_type("%(ws_uri)sws");
  56. // mpl.figure creates a new figure on the webpage.
  57. var fig = new mpl.figure(
  58. // A unique numeric identifier for the figure
  59. %(fig_id)s,
  60. // A websocket object (or something that behaves like one)
  61. websocket,
  62. // A function called when a file type is selected for download
  63. ondownload,
  64. // The HTML element in which to place the figure
  65. $('div#figure'));
  66. }
  67. );
  68. </script>
  69. <title>matplotlib</title>
  70. </head>
  71. <body>
  72. <div id="figure">
  73. </div>
  74. </body>
  75. </html>
  76. """
  77. class MyApplication(tornado.web.Application):
  78. class MainPage(tornado.web.RequestHandler):
  79. """
  80. Serves the main HTML page.
  81. """
  82. def get(self):
  83. manager = self.application.manager
  84. ws_uri = "ws://{req.host}/".format(req=self.request)
  85. content = html_content % {
  86. "ws_uri": ws_uri, "fig_id": manager.num}
  87. self.write(content)
  88. class MplJs(tornado.web.RequestHandler):
  89. """
  90. Serves the generated matplotlib javascript file. The content
  91. is dynamically generated based on which toolbar functions the
  92. user has defined. Call `FigureManagerWebAgg` to get its
  93. content.
  94. """
  95. def get(self):
  96. self.set_header('Content-Type', 'application/javascript')
  97. js_content = FigureManagerWebAgg.get_javascript()
  98. self.write(js_content)
  99. class Download(tornado.web.RequestHandler):
  100. """
  101. Handles downloading of the figure in various file formats.
  102. """
  103. def get(self, fmt):
  104. manager = self.application.manager
  105. mimetypes = {
  106. 'ps': 'application/postscript',
  107. 'eps': 'application/postscript',
  108. 'pdf': 'application/pdf',
  109. 'svg': 'image/svg+xml',
  110. 'png': 'image/png',
  111. 'jpeg': 'image/jpeg',
  112. 'tif': 'image/tiff',
  113. 'emf': 'application/emf'
  114. }
  115. self.set_header('Content-Type', mimetypes.get(fmt, 'binary'))
  116. buff = io.BytesIO()
  117. manager.canvas.figure.savefig(buff, format=fmt)
  118. self.write(buff.getvalue())
  119. class WebSocket(tornado.websocket.WebSocketHandler):
  120. """
  121. A websocket for interactive communication between the plot in
  122. the browser and the server.
  123. In addition to the methods required by tornado, it is required to
  124. have two callback methods:
  125. - ``send_json(json_content)`` is called by matplotlib when
  126. it needs to send json to the browser. `json_content` is
  127. a JSON tree (Python dictionary), and it is the responsibility
  128. of this implementation to encode it as a string to send over
  129. the socket.
  130. - ``send_binary(blob)`` is called to send binary image data
  131. to the browser.
  132. """
  133. supports_binary = True
  134. def open(self):
  135. # Register the websocket with the FigureManager.
  136. manager = self.application.manager
  137. manager.add_web_socket(self)
  138. if hasattr(self, 'set_nodelay'):
  139. self.set_nodelay(True)
  140. def on_close(self):
  141. # When the socket is closed, deregister the websocket with
  142. # the FigureManager.
  143. manager = self.application.manager
  144. manager.remove_web_socket(self)
  145. def on_message(self, message):
  146. # The 'supports_binary' message is relevant to the
  147. # websocket itself. The other messages get passed along
  148. # to matplotlib as-is.
  149. # Every message has a "type" and a "figure_id".
  150. message = json.loads(message)
  151. if message['type'] == 'supports_binary':
  152. self.supports_binary = message['value']
  153. else:
  154. manager = self.application.manager
  155. manager.handle_json(message)
  156. def send_json(self, content):
  157. self.write_message(json.dumps(content))
  158. def send_binary(self, blob):
  159. if self.supports_binary:
  160. self.write_message(blob, binary=True)
  161. else:
  162. data_uri = "data:image/png;base64,{0}".format(
  163. blob.encode('base64').replace('\n', ''))
  164. self.write_message(data_uri)
  165. def __init__(self, figure):
  166. self.figure = figure
  167. self.manager = new_figure_manager_given_figure(id(figure), figure)
  168. super().__init__([
  169. # Static files for the CSS and JS
  170. (r'/_static/(.*)',
  171. tornado.web.StaticFileHandler,
  172. {'path': FigureManagerWebAgg.get_static_file_path()}),
  173. # The page that contains all of the pieces
  174. ('/', self.MainPage),
  175. ('/mpl.js', self.MplJs),
  176. # Sends images and events to the browser, and receives
  177. # events from the browser
  178. ('/ws', self.WebSocket),
  179. # Handles the downloading (i.e., saving) of static images
  180. (r'/download.([a-z0-9.]+)', self.Download),
  181. ])
  182. if __name__ == "__main__":
  183. figure = create_figure()
  184. application = MyApplication(figure)
  185. http_server = tornado.httpserver.HTTPServer(application)
  186. http_server.listen(8080)
  187. print("http://127.0.0.1:8080/")
  188. print("Press Ctrl+C to quit")
  189. tornado.ioloop.IOLoop.instance().start()

下载这个示例