@@ -44,14 +44,16 @@ def draw_if_interactive():
44
44
class Show (backend_bases .ShowBase ):
45
45
def mainloop (self ):
46
46
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 ))
55
57
56
58
WebAggApplication .start ()
57
59
@@ -315,17 +317,21 @@ class NavigationToolbar2WebAgg(backend_bases.NavigationToolbar2):
315
317
'forward' : 'ui-icon ui-icon-circle-arrow-e' ,
316
318
'zoom_to_rect' : 'ui-icon ui-icon-search' ,
317
319
'move' : 'ui-icon ui-icon-arrow-4' ,
318
- 'filesave ' : 'ui-icon ui-icon-disk' ,
320
+ 'download ' : 'ui-icon ui-icon-disk' ,
319
321
None : None
320
322
}
321
323
322
324
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 )
329
335
330
336
self .message = ''
331
337
self .cursor = 0
@@ -374,20 +380,52 @@ def get(self):
374
380
'matplotlib.png' )) as fd :
375
381
self .write (fd .read ())
376
382
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
+
378
389
def get (self , fignum ):
379
390
with open (os .path .join (WebAggApplication ._mpl_dirs ['web_backend' ],
380
- 'index .html' )) as fd :
391
+ 'single_figure .html' )) as fd :
381
392
tpl = fd .read ()
382
393
383
394
fignum = int (fignum )
384
395
manager = Gcf .get_fig_manager (fignum )
385
-
396
+
397
+ ws_uri = 'ws://{req.host}{prefix}/' .format (req = self .request ,
398
+ prefix = self .url_prefix )
386
399
t = tornado .template .Template (tpl )
387
400
self .write (t .generate (
401
+ prefix = self .url_prefix ,
402
+ ws_uri = ws_uri ,
403
+ fig_id = fignum ,
388
404
toolitems = NavigationToolbar2WebAgg .toolitems ,
389
405
canvas = manager .canvas ))
390
406
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
+
391
429
class MPLInterfaceJS (tornado .web .RequestHandler ):
392
430
def get (self , fignum ):
393
431
with open (os .path .join (WebAggApplication ._mpl_dirs ['web_backend' ],
@@ -465,57 +503,69 @@ def send_image(self):
465
503
diff .encode ('base64' ).replace ('\n ' , '' ))
466
504
self .write_message (data_uri )
467
505
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
+
469
511
super (WebAggApplication , self ).__init__ ([
470
512
# Static files for the CSS and JS
471
- (r'/static /(.*)' ,
513
+ (url_prefix + r'/_static /(.*)' ,
472
514
tornado .web .StaticFileHandler ,
473
515
{'path' : self ._mpl_dirs ['web_backend' ]}),
474
516
475
517
# Static images for toolbar buttons
476
- (r' /images/(.*)' ,
518
+ (url_prefix + r'/_static /images/(.*)' ,
477
519
tornado .web .StaticFileHandler ,
478
520
{'path' : self ._mpl_dirs ['images' ]}),
479
521
480
- (r'/static /jquery/css/themes/base/(.*)' ,
522
+ (url_prefix + r'/_static /jquery/css/themes/base/(.*)' ,
481
523
tornado .web .StaticFileHandler ,
482
524
{'path' : os .path .join (self ._mpl_dirs ['web_backend' ], 'jquery' ,
483
525
'css' , 'themes' , 'base' )}),
484
526
485
- (r'/static /jquery/css/themes/base/images/(.*)' ,
527
+ (url_prefix + r'/_static /jquery/css/themes/base/images/(.*)' ,
486
528
tornado .web .StaticFileHandler ,
487
529
{'path' : os .path .join (self ._mpl_dirs ['web_backend' ], 'jquery' ,
488
530
'css' , 'themes' , 'base' , 'images' )}),
489
531
490
- (r'/static /jquery/js/(.*)' , tornado .web .StaticFileHandler ,
532
+ (url_prefix + r'/_static /jquery/js/(.*)' , tornado .web .StaticFileHandler ,
491
533
{'path' : os .path .join (self ._mpl_dirs ['web_backend' ],
492
534
'jquery' , 'js' )}),
493
535
494
- (r'/static /css/(.*)' , tornado .web .StaticFileHandler ,
536
+ (url_prefix + r'/_static /css/(.*)' , tornado .web .StaticFileHandler ,
495
537
{'path' : os .path .join (self ._mpl_dirs ['web_backend' ], 'css' )}),
496
538
497
539
# An MPL favicon
498
- (r'/favicon.ico' , self .FavIcon ),
540
+ (url_prefix + r'/favicon.ico' , self .FavIcon ),
499
541
500
542
# 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 }),
502
545
503
- (r'/([0-9]+)/mpl_interface.js' , self .MPLInterfaceJS ),
546
+ (url_prefix + r'/([0-9]+)/mpl_interface.js' , self .MPLInterfaceJS ),
504
547
505
548
# Sends images and events to the browser, and receives
506
549
# events from the browser
507
- (r'/([0-9]+)/ws' , self .WebSocket ),
550
+ (url_prefix + r'/([0-9]+)/ws' , self .WebSocket ),
508
551
509
552
# 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 }),
511
558
])
512
559
513
560
@classmethod
514
- def initialize (cls ):
561
+ def initialize (cls , url_prefix = '' ):
515
562
if cls .initialized :
516
563
return
517
564
518
- app = cls ()
565
+ # Create the class instance
566
+ app = cls (url_prefix = url_prefix )
567
+
568
+ cls .url_prefix = url_prefix
519
569
520
570
# This port selection algorithm is borrowed, more or less
521
571
# verbatim, from IPython.
0 commit comments