Skip to content

Commit 1d828a5

Browse files
committed
Finished WebAgg JS objectification & generalisations.
1 parent c397dcb commit 1d828a5

File tree

8 files changed

+237
-121
lines changed

8 files changed

+237
-121
lines changed

examples/animation/double_pendulum_animated.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
# Double pendulum formula translated from the C code at
22
# http://www.physics.usyd.edu.au/~wheat/dpend_html/solve_dpend.c
33

4-
import matplotlib
5-
matplotlib.use('webagg')
6-
74
from numpy import sin, cos, pi, array
85
import numpy as np
96
import matplotlib.pyplot as plt
@@ -63,7 +60,7 @@ def derivs(state, t):
6360
x2 = L2*sin(y[:,2]) + x1
6461
y2 = -L2*cos(y[:,2]) + y1
6562

66-
fig = plt.figure(figsize=[12, 5])
63+
fig = plt.figure()
6764
ax = fig.add_subplot(111, autoscale_on=False, xlim=(-2, 2), ylim=(-2, 2))
6865
ax.grid()
6966

lib/matplotlib/_pylab_helpers.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import sys, gc
77

88
import atexit
9-
import traceback
109

1110

1211
def error_msg(msg):

lib/matplotlib/backends/backend_webagg.py

Lines changed: 82 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,16 @@ def draw_if_interactive():
4444
class Show(backend_bases.ShowBase):
4545
def mainloop(self):
4646
WebAggApplication.initialize()
47-
for manager in Gcf.get_all_fig_managers():
48-
url = "http://127.0.0.1:{0}/{1}/".format(
49-
WebAggApplication.port, manager.num)
50-
if rcParams['webagg.open_in_browser']:
51-
import webbrowser
52-
webbrowser.open(url)
53-
else:
54-
print("To view figure, visit {0}".format(url))
47+
48+
url = "http://127.0.0.1:{port}{prefix}".format(
49+
port=WebAggApplication.port,
50+
prefix=WebAggApplication.url_prefix)
51+
52+
if rcParams['webagg.open_in_browser']:
53+
import webbrowser
54+
webbrowser.open(url)
55+
else:
56+
print("To view figure, visit {0}".format(url))
5557

5658
WebAggApplication.start()
5759

@@ -315,17 +317,21 @@ class NavigationToolbar2WebAgg(backend_bases.NavigationToolbar2):
315317
'forward': 'ui-icon ui-icon-circle-arrow-e',
316318
'zoom_to_rect': 'ui-icon ui-icon-search',
317319
'move': 'ui-icon ui-icon-arrow-4',
318-
'filesave': 'ui-icon ui-icon-disk',
320+
'download': 'ui-icon ui-icon-disk',
319321
None: None
320322
}
321323

322324
def _init_toolbar(self):
323-
NavigationToolbar2WebAgg.toolitems = tuple(
324-
(text, tooltip_text,
325-
self._jquery_icon_classes[image_file], name_of_method)
326-
for text, tooltip_text, image_file, name_of_method
327-
in backend_bases.NavigationToolbar2.toolitems
328-
if image_file in self._jquery_icon_classes)
325+
# Use the standard toolbar items + download button
326+
toolitems = (backend_bases.NavigationToolbar2.toolitems +
327+
(('Download', 'Download plot', 'download', 'download'),))
328+
329+
NavigationToolbar2WebAgg.toolitems = \
330+
tuple(
331+
(text, tooltip_text, self._jquery_icon_classes[image_file],
332+
name_of_method)
333+
for text, tooltip_text, image_file, name_of_method
334+
in toolitems if image_file in self._jquery_icon_classes)
329335

330336
self.message = ''
331337
self.cursor = 0
@@ -374,20 +380,52 @@ def get(self):
374380
'matplotlib.png')) as fd:
375381
self.write(fd.read())
376382

377-
class FigurePage(tornado.web.RequestHandler):
383+
class SingleFigurePage(tornado.web.RequestHandler):
384+
def __init__(self, application, request, **kwargs):
385+
self.url_prefix = kwargs.pop('url_prefix', '')
386+
return tornado.web.RequestHandler.__init__(self, application,
387+
request, **kwargs)
388+
378389
def get(self, fignum):
379390
with open(os.path.join(WebAggApplication._mpl_dirs['web_backend'],
380-
'index.html')) as fd:
391+
'single_figure.html')) as fd:
381392
tpl = fd.read()
382393

383394
fignum = int(fignum)
384395
manager = Gcf.get_fig_manager(fignum)
385-
396+
397+
ws_uri = 'ws://{req.host}{prefix}/'.format(req=self.request,
398+
prefix=self.url_prefix)
386399
t = tornado.template.Template(tpl)
387400
self.write(t.generate(
401+
prefix=self.url_prefix,
402+
ws_uri=ws_uri,
403+
fig_id=fignum,
388404
toolitems=NavigationToolbar2WebAgg.toolitems,
389405
canvas=manager.canvas))
390406

407+
class AllFiguresPage(tornado.web.RequestHandler):
408+
def __init__(self, application, request, **kwargs):
409+
self.url_prefix = kwargs.pop('url_prefix', '')
410+
return tornado.web.RequestHandler.__init__(self, application,
411+
request, **kwargs)
412+
413+
def get(self):
414+
with open(os.path.join(WebAggApplication._mpl_dirs['web_backend'],
415+
'all_figures.html')) as fd:
416+
tpl = fd.read()
417+
418+
ws_uri = 'ws://{req.host}{prefix}/'.format(req=self.request,
419+
prefix=self.url_prefix)
420+
t = tornado.template.Template(tpl)
421+
422+
self.write(t.generate(
423+
prefix=self.url_prefix,
424+
ws_uri=ws_uri,
425+
figures = sorted(list(Gcf.figs.items()), key=lambda item: item[0]),
426+
toolitems=NavigationToolbar2WebAgg.toolitems))
427+
428+
391429
class MPLInterfaceJS(tornado.web.RequestHandler):
392430
def get(self, fignum):
393431
with open(os.path.join(WebAggApplication._mpl_dirs['web_backend'],
@@ -465,57 +503,69 @@ def send_image(self):
465503
diff.encode('base64').replace('\n', ''))
466504
self.write_message(data_uri)
467505

468-
def __init__(self):
506+
def __init__(self, url_prefix=''):
507+
if url_prefix:
508+
assert url_prefix[0] == '/' and url_prefix[-1] != '/', \
509+
'url_prefix must start with a "/" and not end with one.'
510+
469511
super(WebAggApplication, self).__init__([
470512
# Static files for the CSS and JS
471-
(r'/static/(.*)',
513+
(url_prefix + r'/_static/(.*)',
472514
tornado.web.StaticFileHandler,
473515
{'path': self._mpl_dirs['web_backend']}),
474516

475517
# Static images for toolbar buttons
476-
(r'/images/(.*)',
518+
(url_prefix + r'/_static/images/(.*)',
477519
tornado.web.StaticFileHandler,
478520
{'path': self._mpl_dirs['images']}),
479521

480-
(r'/static/jquery/css/themes/base/(.*)',
522+
(url_prefix + r'/_static/jquery/css/themes/base/(.*)',
481523
tornado.web.StaticFileHandler,
482524
{'path': os.path.join(self._mpl_dirs['web_backend'], 'jquery',
483525
'css', 'themes', 'base')}),
484526

485-
(r'/static/jquery/css/themes/base/images/(.*)',
527+
(url_prefix + r'/_static/jquery/css/themes/base/images/(.*)',
486528
tornado.web.StaticFileHandler,
487529
{'path': os.path.join(self._mpl_dirs['web_backend'], 'jquery',
488530
'css', 'themes', 'base', 'images')}),
489531

490-
(r'/static/jquery/js/(.*)', tornado.web.StaticFileHandler,
532+
(url_prefix + r'/_static/jquery/js/(.*)', tornado.web.StaticFileHandler,
491533
{'path': os.path.join(self._mpl_dirs['web_backend'],
492534
'jquery', 'js')}),
493535

494-
(r'/static/css/(.*)', tornado.web.StaticFileHandler,
536+
(url_prefix + r'/_static/css/(.*)', tornado.web.StaticFileHandler,
495537
{'path': os.path.join(self._mpl_dirs['web_backend'], 'css')}),
496538

497539
# An MPL favicon
498-
(r'/favicon.ico', self.FavIcon),
540+
(url_prefix + r'/favicon.ico', self.FavIcon),
499541

500542
# The page that contains all of the pieces
501-
(r'/([0-9]+)/?', self.FigurePage),
543+
(url_prefix + r'/([0-9]+)', self.SingleFigurePage,
544+
{'url_prefix': url_prefix}),
502545

503-
(r'/([0-9]+)/mpl_interface.js', self.MPLInterfaceJS),
546+
(url_prefix + r'/([0-9]+)/mpl_interface.js', self.MPLInterfaceJS),
504547

505548
# Sends images and events to the browser, and receives
506549
# events from the browser
507-
(r'/([0-9]+)/ws', self.WebSocket),
550+
(url_prefix + r'/([0-9]+)/ws', self.WebSocket),
508551

509552
# Handles the downloading (i.e., saving) of static images
510-
(r'/([0-9]+)/download.([a-z]+)', self.Download)
553+
(url_prefix + r'/([0-9]+)/download.([a-z]+)', self.Download),
554+
555+
# The page that contains all of the figures
556+
(url_prefix + r'/?', self.AllFiguresPage,
557+
{'url_prefix': url_prefix}),
511558
])
512559

513560
@classmethod
514-
def initialize(cls):
561+
def initialize(cls, url_prefix=''):
515562
if cls.initialized:
516563
return
517564

518-
app = cls()
565+
# Create the class instance
566+
app = cls(url_prefix=url_prefix)
567+
568+
cls.url_prefix = url_prefix
519569

520570
# This port selection algorithm is borrowed, more or less
521571
# verbatim, from IPython.
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<html>
2+
<head>
3+
<link rel="stylesheet" href="{{ prefix }}/_static/css/page.css" type="text/css">
4+
<link rel="stylesheet" href="{{ prefix }}/_static/css/boilerplate.css" type="text/css" />
5+
<link rel="stylesheet" href="{{ prefix }}/_static/css/fbm.css" type="text/css" />
6+
<link rel="stylesheet" href="{{ prefix }}/_static/jquery/css/themes/base/jquery-ui.min.css" >
7+
<script src="{{ prefix }}/_static/jquery/js/jquery-1.7.1.min.js"></script>
8+
<script src="{{ prefix }}/_static/jquery/js/jquery-ui.min.js"></script>
9+
<script src="{{ prefix }}/_static/mpl.js"></script>
10+
<script src="{{ prefix }}/{{ str(figures[0][0]) }}/mpl_interface.js"></script>
11+
12+
<script>
13+
var websocket_url_prefix = "{{ ws_uri }}";
14+
var figures = new Array();
15+
16+
{% for (fig_id, _) in figures %}
17+
$(document).ready(
18+
function() {
19+
fig = new figure({{ repr(str(fig_id)) }}, websocket_url_prefix);
20+
figures.push(fig);
21+
22+
fig.focus_on_mouseover = true;
23+
24+
var toolbar_prefix = '{{ str(fig_id).replace(' ', '') }}-toolbar';
25+
init_mpl_toolbar(fig, toolbar_prefix);
26+
27+
var statusbar_prefix = '{{ str(fig_id).replace(' ', '') }}-statusbar';
28+
var status_id = init_mpl_statusbar(toolbar_prefix, statusbar_prefix);
29+
30+
var canvas_prefix = '{{ str(fig_id).replace(' ', '') }}-canvas';
31+
init_mpl_canvas(fig, '{{ str(fig_id).replace(' ', '') }}-canvas-div', canvas_prefix);
32+
33+
fig.finalize(canvas_prefix, toolbar_prefix, statusbar_prefix);
34+
35+
$(fig.canvas).attr('tabindex', {{ fig_id }});
36+
}
37+
);
38+
39+
{% end %}
40+
</script>
41+
42+
<title>MPL | WebAgg current figures</title>
43+
44+
</head>
45+
<body>
46+
<div id="mpl-warnings" class="mpl-warnings"></div>
47+
{% for (fig_id, fig_manager) in figures %}
48+
{% set fig_label='Figure: {}'.format(fig_manager.canvas.figure.get_label()) %}
49+
50+
{% if fig_label == 'Figure: ' %}
51+
{% set fig_label="Figure {}".format(fig_id) %}
52+
{% end %}
53+
54+
<div style="margin: 25px 100px;">
55+
<h2>
56+
<a href="{{ prefix }}/{{ str(fig_id) }}">
57+
{{ fig_label }}
58+
59+
</a>
60+
</h2>
61+
<div id="{{ str(fig_id).replace(' ', '') }}-canvas-div"></div>
62+
<div id="{{ str(fig_id).replace(' ', '') }}-toolbar" style="width: 700px;"></div>
63+
</div>
64+
{% end %}
65+
66+
</body>
67+
</html>

lib/matplotlib/backends/web_backend/index.html

Lines changed: 0 additions & 67 deletions
This file was deleted.

0 commit comments

Comments
 (0)