Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Finished WebAgg JS objectification & generalisations.

  • Loading branch information...
commit 1d828a518d033757c37f803b8b166e80d9fe3dde 1 parent c397dcb
@pelson pelson authored
View
5 examples/animation/double_pendulum_animated.py
@@ -1,9 +1,6 @@
# Double pendulum formula translated from the C code at
# http://www.physics.usyd.edu.au/~wheat/dpend_html/solve_dpend.c
-import matplotlib
-matplotlib.use('webagg')
-
from numpy import sin, cos, pi, array
import numpy as np
import matplotlib.pyplot as plt
@@ -63,7 +60,7 @@ def derivs(state, t):
x2 = L2*sin(y[:,2]) + x1
y2 = -L2*cos(y[:,2]) + y1
-fig = plt.figure(figsize=[12, 5])
+fig = plt.figure()
ax = fig.add_subplot(111, autoscale_on=False, xlim=(-2, 2), ylim=(-2, 2))
ax.grid()
View
1  lib/matplotlib/_pylab_helpers.py
@@ -6,7 +6,6 @@
import sys, gc
import atexit
-import traceback
def error_msg(msg):
View
114 lib/matplotlib/backends/backend_webagg.py
@@ -44,14 +44,16 @@ def draw_if_interactive():
class Show(backend_bases.ShowBase):
def mainloop(self):
WebAggApplication.initialize()
- for manager in Gcf.get_all_fig_managers():
- url = "http://127.0.0.1:{0}/{1}/".format(
- WebAggApplication.port, manager.num)
- if rcParams['webagg.open_in_browser']:
- import webbrowser
- webbrowser.open(url)
- else:
- print("To view figure, visit {0}".format(url))
+
+ url = "http://127.0.0.1:{port}{prefix}".format(
+ port=WebAggApplication.port,
+ prefix=WebAggApplication.url_prefix)
+
+ if rcParams['webagg.open_in_browser']:
+ import webbrowser
+ webbrowser.open(url)
+ else:
+ print("To view figure, visit {0}".format(url))
WebAggApplication.start()
@@ -315,17 +317,21 @@ class NavigationToolbar2WebAgg(backend_bases.NavigationToolbar2):
'forward': 'ui-icon ui-icon-circle-arrow-e',
'zoom_to_rect': 'ui-icon ui-icon-search',
'move': 'ui-icon ui-icon-arrow-4',
- 'filesave': 'ui-icon ui-icon-disk',
+ 'download': 'ui-icon ui-icon-disk',
None: None
}
def _init_toolbar(self):
- NavigationToolbar2WebAgg.toolitems = tuple(
- (text, tooltip_text,
- self._jquery_icon_classes[image_file], name_of_method)
- for text, tooltip_text, image_file, name_of_method
- in backend_bases.NavigationToolbar2.toolitems
- if image_file in self._jquery_icon_classes)
+ # Use the standard toolbar items + download button
+ toolitems = (backend_bases.NavigationToolbar2.toolitems +
+ (('Download', 'Download plot', 'download', 'download'),))
+
+ NavigationToolbar2WebAgg.toolitems = \
+ tuple(
+ (text, tooltip_text, self._jquery_icon_classes[image_file],
+ name_of_method)
+ for text, tooltip_text, image_file, name_of_method
+ in toolitems if image_file in self._jquery_icon_classes)
self.message = ''
self.cursor = 0
@@ -374,20 +380,52 @@ def get(self):
'matplotlib.png')) as fd:
self.write(fd.read())
- class FigurePage(tornado.web.RequestHandler):
+ class SingleFigurePage(tornado.web.RequestHandler):
+ def __init__(self, application, request, **kwargs):
+ self.url_prefix = kwargs.pop('url_prefix', '')
+ return tornado.web.RequestHandler.__init__(self, application,
+ request, **kwargs)
+
def get(self, fignum):
with open(os.path.join(WebAggApplication._mpl_dirs['web_backend'],
- 'index.html')) as fd:
+ 'single_figure.html')) as fd:
tpl = fd.read()
fignum = int(fignum)
manager = Gcf.get_fig_manager(fignum)
-
+
+ ws_uri = 'ws://{req.host}{prefix}/'.format(req=self.request,
+ prefix=self.url_prefix)
t = tornado.template.Template(tpl)
self.write(t.generate(
+ prefix=self.url_prefix,
+ ws_uri=ws_uri,
+ fig_id=fignum,
toolitems=NavigationToolbar2WebAgg.toolitems,
canvas=manager.canvas))
+ class AllFiguresPage(tornado.web.RequestHandler):
+ def __init__(self, application, request, **kwargs):
+ self.url_prefix = kwargs.pop('url_prefix', '')
+ return tornado.web.RequestHandler.__init__(self, application,
+ request, **kwargs)
+
+ def get(self):
+ with open(os.path.join(WebAggApplication._mpl_dirs['web_backend'],
+ 'all_figures.html')) as fd:
+ tpl = fd.read()
+
+ ws_uri = 'ws://{req.host}{prefix}/'.format(req=self.request,
+ prefix=self.url_prefix)
+ t = tornado.template.Template(tpl)
+
+ self.write(t.generate(
+ prefix=self.url_prefix,
+ ws_uri=ws_uri,
+ figures = sorted(list(Gcf.figs.items()), key=lambda item: item[0]),
+ toolitems=NavigationToolbar2WebAgg.toolitems))
+
+
class MPLInterfaceJS(tornado.web.RequestHandler):
def get(self, fignum):
with open(os.path.join(WebAggApplication._mpl_dirs['web_backend'],
@@ -465,57 +503,69 @@ def send_image(self):
diff.encode('base64').replace('\n', ''))
self.write_message(data_uri)
- def __init__(self):
+ def __init__(self, url_prefix=''):
+ if url_prefix:
+ assert url_prefix[0] == '/' and url_prefix[-1] != '/', \
+ 'url_prefix must start with a "/" and not end with one.'
+
super(WebAggApplication, self).__init__([
# Static files for the CSS and JS
- (r'/static/(.*)',
+ (url_prefix + r'/_static/(.*)',
tornado.web.StaticFileHandler,
{'path': self._mpl_dirs['web_backend']}),
# Static images for toolbar buttons
- (r'/images/(.*)',
+ (url_prefix + r'/_static/images/(.*)',
tornado.web.StaticFileHandler,
{'path': self._mpl_dirs['images']}),
- (r'/static/jquery/css/themes/base/(.*)',
+ (url_prefix + r'/_static/jquery/css/themes/base/(.*)',
tornado.web.StaticFileHandler,
{'path': os.path.join(self._mpl_dirs['web_backend'], 'jquery',
'css', 'themes', 'base')}),
- (r'/static/jquery/css/themes/base/images/(.*)',
+ (url_prefix + r'/_static/jquery/css/themes/base/images/(.*)',
tornado.web.StaticFileHandler,
{'path': os.path.join(self._mpl_dirs['web_backend'], 'jquery',
'css', 'themes', 'base', 'images')}),
- (r'/static/jquery/js/(.*)', tornado.web.StaticFileHandler,
+ (url_prefix + r'/_static/jquery/js/(.*)', tornado.web.StaticFileHandler,
{'path': os.path.join(self._mpl_dirs['web_backend'],
'jquery', 'js')}),
- (r'/static/css/(.*)', tornado.web.StaticFileHandler,
+ (url_prefix + r'/_static/css/(.*)', tornado.web.StaticFileHandler,
{'path': os.path.join(self._mpl_dirs['web_backend'], 'css')}),
# An MPL favicon
- (r'/favicon.ico', self.FavIcon),
+ (url_prefix + r'/favicon.ico', self.FavIcon),
# The page that contains all of the pieces
- (r'/([0-9]+)/?', self.FigurePage),
+ (url_prefix + r'/([0-9]+)', self.SingleFigurePage,
+ {'url_prefix': url_prefix}),
- (r'/([0-9]+)/mpl_interface.js', self.MPLInterfaceJS),
+ (url_prefix + r'/([0-9]+)/mpl_interface.js', self.MPLInterfaceJS),
# Sends images and events to the browser, and receives
# events from the browser
- (r'/([0-9]+)/ws', self.WebSocket),
+ (url_prefix + r'/([0-9]+)/ws', self.WebSocket),
# Handles the downloading (i.e., saving) of static images
- (r'/([0-9]+)/download.([a-z]+)', self.Download)
+ (url_prefix + r'/([0-9]+)/download.([a-z]+)', self.Download),
+
+ # The page that contains all of the figures
+ (url_prefix + r'/?', self.AllFiguresPage,
+ {'url_prefix': url_prefix}),
])
@classmethod
- def initialize(cls):
+ def initialize(cls, url_prefix=''):
if cls.initialized:
return
- app = cls()
+ # Create the class instance
+ app = cls(url_prefix=url_prefix)
+
+ cls.url_prefix = url_prefix
# This port selection algorithm is borrowed, more or less
# verbatim, from IPython.
View
67 lib/matplotlib/backends/web_backend/all_figures.html
@@ -0,0 +1,67 @@
+<html>
+ <head>
+ <link rel="stylesheet" href="{{ prefix }}/_static/css/page.css" type="text/css">
+ <link rel="stylesheet" href="{{ prefix }}/_static/css/boilerplate.css" type="text/css" />
+ <link rel="stylesheet" href="{{ prefix }}/_static/css/fbm.css" type="text/css" />
+ <link rel="stylesheet" href="{{ prefix }}/_static/jquery/css/themes/base/jquery-ui.min.css" >
+ <script src="{{ prefix }}/_static/jquery/js/jquery-1.7.1.min.js"></script>
+ <script src="{{ prefix }}/_static/jquery/js/jquery-ui.min.js"></script>
+ <script src="{{ prefix }}/_static/mpl.js"></script>
+ <script src="{{ prefix }}/{{ str(figures[0][0]) }}/mpl_interface.js"></script>
+
+ <script>
+ var websocket_url_prefix = "{{ ws_uri }}";
+ var figures = new Array();
+
+ {% for (fig_id, _) in figures %}
+ $(document).ready(
+ function() {
+ fig = new figure({{ repr(str(fig_id)) }}, websocket_url_prefix);
+ figures.push(fig);
+
+ fig.focus_on_mouseover = true;
+
+ var toolbar_prefix = '{{ str(fig_id).replace(' ', '') }}-toolbar';
+ init_mpl_toolbar(fig, toolbar_prefix);
+
+ var statusbar_prefix = '{{ str(fig_id).replace(' ', '') }}-statusbar';
+ var status_id = init_mpl_statusbar(toolbar_prefix, statusbar_prefix);
+
+ var canvas_prefix = '{{ str(fig_id).replace(' ', '') }}-canvas';
+ init_mpl_canvas(fig, '{{ str(fig_id).replace(' ', '') }}-canvas-div', canvas_prefix);
+
+ fig.finalize(canvas_prefix, toolbar_prefix, statusbar_prefix);
+
+ $(fig.canvas).attr('tabindex', {{ fig_id }});
+ }
+ );
+
+ {% end %}
+ </script>
+
+ <title>MPL | WebAgg current figures</title>
+
+ </head>
+ <body>
+ <div id="mpl-warnings" class="mpl-warnings"></div>
+ {% for (fig_id, fig_manager) in figures %}
+ {% set fig_label='Figure: {}'.format(fig_manager.canvas.figure.get_label()) %}
+
+ {% if fig_label == 'Figure: ' %}
+ {% set fig_label="Figure {}".format(fig_id) %}
+ {% end %}
+
+ <div style="margin: 25px 100px;">
+ <h2>
+ <a href="{{ prefix }}/{{ str(fig_id) }}">
+ {{ fig_label }}
+
+ </a>
+ </h2>
+ <div id="{{ str(fig_id).replace(' ', '') }}-canvas-div"></div>
+ <div id="{{ str(fig_id).replace(' ', '') }}-toolbar" style="width: 700px;"></div>
+ </div>
+ {% end %}
+
+ </body>
+</html>
View
67 lib/matplotlib/backends/web_backend/index.html
@@ -1,67 +0,0 @@
-<html>
- <head>
- <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/jquery/css/themes/base/jquery-ui.min.css" >
- <script src="/static/jquery/js/jquery-1.7.1.min.js"></script>
- <script src="/static/jquery/js/jquery-ui.min.js"></script>
- <script src="/static/mpl.js"></script>
- <script src="/1/mpl_interface.js"></script>
- <script>
-
- var websocket_url_prefix = 'ws://127.0.0.1:8989/';
- var figure_id = '1';
-
- $(document).ready(
- function() {
- fig = new figure('1', websocket_url_prefix);
- init_mpl_toolbar(fig, 'toolbar');
-
- var statusbar_prefix = 'wibble';
- var status_id = init_mpl_statusbar('toolbar', statusbar_prefix);
-
- var canvas_prefix = 'mpl-foo';
- init_mpl_canvas(fig, 'my-canvas-div', canvas_prefix);
-
- fig.finalize(canvas_prefix, statusbar_prefix);
- }
- );
-
-
- $(document).ready(
- function() {
- fig2 = new figure('2', websocket_url_prefix);
- init_mpl_toolbar(fig2, 'toolbar2');
-
- var statusbar_prefix2 = 'wibble2';
- var status_id2 = init_mpl_statusbar('toolbar2', statusbar_prefix2);
-
- var canvas_prefix2 = 'mpl-foo2';
- init_mpl_canvas(fig2, 'my-canvas-div2', canvas_prefix2);
-
- fig2.finalize(canvas_prefix2, statusbar_prefix2);
- }
- );
-
-
- // body: onkeydown="key_event(event, 'key_press')" onkeyup="key_event(event, 'key_release')" ???
-
- </script>
- </head>
- <body>
- <div style="margin: 10px 100px;">
- <div id="mpl-warnings" class="mpl-warnings"></div>
- <div id="my-canvas-div"></div>
- <div id="toolbar" style="width: 500px;"></div>
- </div>
-
-
- <div style="margin: 10px 100px;">
- <div id="mpl-warnings2" class="mpl-warnings"></div>
- <div id="my-canvas-div2"></div>
- <div id="toolbar2" style="width: 500px;"></div>
- </div>
-
- </body>
-</html>
View
28 lib/matplotlib/backends/web_backend/mpl.js
@@ -32,9 +32,13 @@ function figure(fig_id, websocket_url_prefix) {
this.canvas = undefined;
this.rubberband_canvas = undefined;
this.rubberband_context = undefined;
+ this.format_dropdown = undefined;
+
+ this.focus_on_mousover = false;
+
}
-figure.prototype.finalize = function (canvas_id_prefix, message_id_prefix) {
+figure.prototype.finalize = function (canvas_id_prefix, toolbar_id_prefix, message_id_prefix) {
// resizing_div_id might be the canvas or a containing div for more control of display
var canvas_id = canvas_id_prefix + '-canvas';
@@ -48,6 +52,8 @@ figure.prototype.finalize = function (canvas_id_prefix, message_id_prefix) {
this.rubberband_context = this.rubberband_canvas.getContext("2d");
this.rubberband_context.strokeStyle = "#000000";
+ this.format_dropdown = document.getElementById(toolbar_id_prefix + '-format_picker');
+
this.ws.onopen = function () {
this.ws.send(JSON.stringify(
{type: 'supports_binary',
@@ -170,6 +176,12 @@ function findPos(obj) {
figure.prototype.mouse_event = function(event, name) {
var canvas_pos = findPos(this.canvas)
+
+ if (this.focus_on_mouseover && name === 'motion_notify')
+ {
+ this.canvas.focus();
+ }
+
var x = event.pageX - canvas_pos.x;
var y = event.pageY - canvas_pos.y;
@@ -209,19 +221,19 @@ figure.prototype.key_event = function(event, name) {
figure.prototype.toolbar_button_onclick = function(name) {
if (name == 'download') {
- var format_dropdown = document.getElementById("mpl-format");
+ var format_dropdown = this.format_dropdown;
var format = format_dropdown.options[format_dropdown.selectedIndex].value;
- window.open('download.' + format, '_blank');
+ window.open(this.id + '/download.' + format, '_blank');
} else {
this.ws.send(JSON.stringify(
{type: "toolbar_button",
"name": name}));
}
-}
+};
-function toolbar_button_onmouseover(name) {
- var message = document.getElementById("mpl-message");
- message.textContent = name;
-}
+
+figure.prototype.toolbar_button_onmouseover = function(tooltip) {
+ this.message.textContent = tooltip;
+};
View
19 lib/matplotlib/backends/web_backend/mpl_interface.js
@@ -17,6 +17,11 @@ function init_mpl_canvas(fig, canvas_div_id, id_prefix) {
canvas.attr('style', "left: 0; top: 0; z-index: 0;")
canvas.attr('width', '800');
canvas.attr('height', '800');
+
+ function canvas_keyboard_event(event) { return fig.key_event(event, event['data']); }
+ canvas.keydown('key_press', canvas_keyboard_event);
+ canvas.keyup('key_release', canvas_keyboard_event);
+
canvas_div.append(canvas);
// create a second canvas which floats on top of the first.
@@ -25,17 +30,12 @@ function init_mpl_canvas(fig, canvas_div_id, id_prefix) {
rubberband.attr('width', '800');
rubberband.attr('height', '800');
function mouse_event_fn(event) {
- return fig.mouse_event(event, event['data'], id_prefix + '-canvas');
+ return fig.mouse_event(event, event['data']);
}
rubberband.mousedown('button_press', mouse_event_fn);
rubberband.mouseup('button_release', mouse_event_fn);
rubberband.mousemove('motion_notify', mouse_event_fn);
canvas_div.append(rubberband);
-
- //var ctx = rubberband[0].getContext('2d');
- //ctx.fillStyle="#FF0000";
- //ctx.fillRect(0,0,150,75);
-
};
@@ -51,12 +51,12 @@ function init_mpl_toolbar(fig, nav_container_id, nav_elem_id_prefix) {
// Adds a navigation toolbar to the object found with the given jquery query string
if (nav_elem_id_prefix === undefined) {
- nav_elem_id_prefix = '';
+ nav_elem_id_prefix = nav_container_id;
}
// Define a callback function for later on.
function toolbar_event(event) { return fig.toolbar_button_onclick(event['data']); }
-
+ function toolbar_mouse_event(event) { return fig.toolbar_button_onmouseover(event['data']); }
var nav_element = $(document.getElementById(nav_container_id));
@@ -77,6 +77,7 @@ function init_mpl_toolbar(fig, nav_container_id, nav_elem_id_prefix) {
button.attr('role', 'button');
button.attr('aria-disabled', 'false');
button.click(method_name, toolbar_event);
+ button.mouseover(tooltip, toolbar_mouse_event);
var icon_img = $('<span/>');
icon_img.addClass('ui-button-icon-primary ui-icon');
@@ -95,7 +96,7 @@ function init_mpl_toolbar(fig, nav_container_id, nav_elem_id_prefix) {
var fmt_picker_span = $('<span/>');
- var fmt_picker = $('<select/>', {id: nav_elem_id_prefix + 'format_picker'});
+ var fmt_picker = $('<select/>', {id: nav_elem_id_prefix + '-format_picker'});
fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');
fmt_picker_span.append(fmt_picker);
nav_element.append(fmt_picker_span);
View
57 lib/matplotlib/backends/web_backend/single_figure.html
@@ -0,0 +1,57 @@
+<html>
+ <head>
+ <link rel="stylesheet" href="{{ prefix }}/_static/css/page.css" type="text/css">
+ <link rel="stylesheet" href="{{ prefix }}/_static/css/boilerplate.css" type="text/css" />
+ <link rel="stylesheet" href="{{ prefix }}/_static/css/fbm.css" type="text/css" />
+ <link rel="stylesheet" href="{{ prefix }}/_static/jquery/css/themes/base/jquery-ui.min.css" >
+ <script src="{{ prefix }}/_static/jquery/js/jquery-1.7.1.min.js"></script>
+ <script src="{{ prefix }}/_static/jquery/js/jquery-ui.min.js"></script>
+ <script src="{{ prefix }}/_static/mpl.js"></script>
+ <script src="{{ prefix }}/1/mpl_interface.js"></script>
+
+ <script>
+ var websocket_url_prefix = "{{ ws_uri }}";
+ var fig = new figure({{ repr(str(fig_id)) }}, websocket_url_prefix);
+
+ $(document).ready(
+ function() {
+ var toolbar_prefix = 'fig-toolbar';
+ init_mpl_toolbar(fig, toolbar_prefix);
+
+ var statusbar_prefix = 'statusbar';
+ var status_id = init_mpl_statusbar(toolbar_prefix, statusbar_prefix);
+
+ var canvas_prefix = 'figure';
+ init_mpl_canvas(fig, 'fig-canvas-div', canvas_prefix);
+
+ fig.finalize(canvas_prefix, toolbar_prefix, statusbar_prefix);
+ // fig.canvas.focus();
+ // Let the top level document handle key events.
+ fig.canvas.unbind('keydown');
+ fig.canvas.unbind('keyup');
+ }
+ );
+ </script>
+
+{% set fig_label='Figure: {}'.format(canvas.figure.get_label()) %}
+
+{% if fig_label == 'Figure: ' %}
+{% set fig_label="Figure {}".format(fig_id) %}
+{% end %}
+
+ <title>MPL | {{ fig_label }}</title>
+
+
+ </head>
+ <body onkeydown="fig.key_event(event, 'key_press');" onkeyup="fig.key_event(event, 'key_release');">
+ <div style="margin: 10px 100px;">
+ <h1>
+ {{ fig_label }}
+ </h1>
+ <div id="mpl-warnings" class="mpl-warnings"></div>
+ <div id="fig-canvas-div"></div>
+ <div id="fig-toolbar" style="width: 600px;"></div>
+ </div>
+
+ </body>
+</html>
Please sign in to comment.
Something went wrong with that request. Please try again.