@@ -161,9 +161,9 @@ def get_diff_image(self):
161
161
# The buffer is created as type uint32 so that entire
162
162
# pixels can be compared in one numpy call, rather than
163
163
# needing to compare each plane separately.
164
- buffer = np .frombuffer (
164
+ buff = np .frombuffer (
165
165
self ._renderer .buffer_rgba (), dtype = np .uint32 )
166
- buffer .shape = (
166
+ buff .shape = (
167
167
self ._renderer .height , self ._renderer .width )
168
168
169
169
if not self ._force_full :
@@ -172,10 +172,10 @@ def get_diff_image(self):
172
172
last_buffer .shape = (
173
173
self ._renderer .height , self ._renderer .width )
174
174
175
- diff = buffer != last_buffer
176
- output = np .where (diff , buffer , 0 )
175
+ diff = buff != last_buffer
176
+ output = np .where (diff , buff , 0 )
177
177
else :
178
- output = buffer
178
+ output = buff
179
179
180
180
# Clear out the PNG data buffer rather than recreating it
181
181
# each time. This reduces the number of memory
@@ -198,27 +198,30 @@ def get_diff_image(self):
198
198
return self ._png_buffer .getvalue ()
199
199
200
200
def get_renderer (self ):
201
- l , b , w , h = self .figure .bbox .bounds
201
+ # Mirrors super.get_renderer, but caches the old one
202
+ # so that we can do things such as prodce a diff image
203
+ # in get_diff_image
204
+ _ , _ , w , h = self .figure .bbox .bounds
202
205
key = w , h , self .figure .dpi
203
206
try :
204
207
self ._lastKey , self ._renderer
205
208
except AttributeError :
206
209
need_new_renderer = True
207
210
else :
208
211
need_new_renderer = (self ._lastKey != key )
209
-
212
+
210
213
if need_new_renderer :
211
214
self ._renderer = backend_agg .RendererAgg (
212
215
w , h , self .figure .dpi )
213
216
self ._last_renderer = backend_agg .RendererAgg (
214
217
w , h , self .figure .dpi )
215
218
self ._lastKey = key
216
-
219
+
217
220
return self ._renderer
218
221
219
222
def handle_event (self , event ):
220
- type = event ['type' ]
221
- if type in ('button_press' , 'button_release' , 'motion_notify' ):
223
+ e_type = event ['type' ]
224
+ if e_type in ('button_press' , 'button_release' , 'motion_notify' ):
222
225
x = event ['x' ]
223
226
y = event ['y' ]
224
227
y = self .get_renderer ().height - y
@@ -234,23 +237,24 @@ def handle_event(self, event):
234
237
if button == 2 :
235
238
button = 3
236
239
237
- if type == 'button_press' :
240
+ if e_type == 'button_press' :
238
241
self .button_press_event (x , y , button )
239
- elif type == 'button_release' :
242
+ elif e_type == 'button_release' :
240
243
self .button_release_event (x , y , button )
241
- elif type == 'motion_notify' :
244
+ elif e_type == 'motion_notify' :
242
245
self .motion_notify_event (x , y )
243
- elif type in ('key_press' , 'key_release' ):
246
+ elif e_type in ('key_press' , 'key_release' ):
244
247
key = event ['key' ]
245
248
246
- if type == 'key_press' :
249
+ if e_type == 'key_press' :
247
250
self .key_press_event (key )
248
- elif type == 'key_release' :
251
+ elif e_type == 'key_release' :
249
252
self .key_release_event (key )
250
- elif type == 'toolbar_button' :
253
+ elif e_type == 'toolbar_button' :
254
+ print ('Toolbar button pressed: ' , event ['name' ])
251
255
# TODO: Be more suspicious of the input
252
256
getattr (self .toolbar , event ['name' ])()
253
- elif type == 'refresh' :
257
+ elif e_type == 'refresh' :
254
258
self ._force_full = True
255
259
self .draw_idle ()
256
260
@@ -306,24 +310,23 @@ def resize(self, w, h):
306
310
307
311
308
312
class NavigationToolbar2WebAgg (backend_bases .NavigationToolbar2 ):
309
- toolitems = list (backend_bases .NavigationToolbar2 .toolitems [:6 ]) + [
310
- ('Download' , 'Download plot' , 'filesave' , 'download' )
311
- ]
313
+ _jquery_icon_classes = {'home' : 'ui-icon ui-icon-home' ,
314
+ 'back' : 'ui-icon ui-icon-circle-arrow-w' ,
315
+ 'forward' : 'ui-icon ui-icon-circle-arrow-e' ,
316
+ 'zoom_to_rect' : 'ui-icon ui-icon-search' ,
317
+ 'move' : 'ui-icon ui-icon-arrow-4' ,
318
+ 'filesave' : 'ui-icon ui-icon-disk' ,
319
+ None : None
320
+ }
312
321
313
322
def _init_toolbar (self ):
314
- jqueryui_icons = [
315
- 'ui-icon ui-icon-home' ,
316
- 'ui-icon ui-icon-circle-arrow-w' ,
317
- 'ui-icon ui-icon-circle-arrow-e' ,
318
- None ,
319
- 'ui-icon ui-icon-arrow-4' ,
320
- 'ui-icon ui-icon-search' ,
321
- 'ui-icon ui-icon-disk'
322
- ]
323
- for index , item in enumerate (self .toolitems ):
324
- if item [0 ] is not None :
325
- self .toolitems [index ] = (
326
- item [0 ], item [1 ], jqueryui_icons [index ], item [3 ])
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 )
329
+
327
330
self .message = ''
328
331
self .cursor = 0
329
332
@@ -356,20 +359,39 @@ def release_zoom(self, event):
356
359
class WebAggApplication (tornado .web .Application ):
357
360
initialized = False
358
361
started = False
362
+
363
+ _mpl_data_path = os .path .join (os .path .dirname (os .path .dirname (__file__ )),
364
+ 'mpl-data' )
365
+ _mpl_dirs = {'mpl-data' : _mpl_data_path ,
366
+ 'images' : os .path .join (_mpl_data_path , 'images' ),
367
+ 'web_backend' : os .path .join (os .path .dirname (__file__ ),
368
+ 'web_backend' )}
359
369
360
370
class FavIcon (tornado .web .RequestHandler ):
361
371
def get (self ):
362
372
self .set_header ('Content-Type' , 'image/png' )
363
- with open (os .path .join (
364
- os .path .dirname (__file__ ),
365
- '../mpl-data/images/matplotlib.png' )) as fd :
373
+ with open (os .path .join (self ._mpl_dirs ['images' ],
374
+ 'matplotlib.png' )) as fd :
366
375
self .write (fd .read ())
367
376
368
- class IndexPage (tornado .web .RequestHandler ):
377
+ class FigurePage (tornado .web .RequestHandler ):
378
+ def get (self , fignum ):
379
+ with open (os .path .join (WebAggApplication ._mpl_dirs ['web_backend' ],
380
+ 'index.html' )) as fd :
381
+ tpl = fd .read ()
382
+
383
+ fignum = int (fignum )
384
+ manager = Gcf .get_fig_manager (fignum )
385
+
386
+ t = tornado .template .Template (tpl )
387
+ self .write (t .generate (
388
+ toolitems = NavigationToolbar2WebAgg .toolitems ,
389
+ canvas = manager .canvas ))
390
+
391
+ class MPLInterfaceJS (tornado .web .RequestHandler ):
369
392
def get (self , fignum ):
370
- with open (os .path .join (
371
- os .path .dirname (__file__ ),
372
- 'web_backend' , 'index.html' )) as fd :
393
+ with open (os .path .join (WebAggApplication ._mpl_dirs ['web_backend' ],
394
+ 'mpl_interface.js' )) as fd :
373
395
tpl = fd .read ()
374
396
375
397
fignum = int (fignum )
@@ -381,7 +403,7 @@ def get(self, fignum):
381
403
canvas = manager .canvas ))
382
404
383
405
class Download (tornado .web .RequestHandler ):
384
- def get (self , fignum , format ):
406
+ def get (self , fignum , fmt ):
385
407
self .fignum = int (fignum )
386
408
manager = Gcf .get_fig_manager (self .fignum )
387
409
@@ -397,11 +419,11 @@ def get(self, fignum, format):
397
419
'emf' : 'application/emf'
398
420
}
399
421
400
- self .set_header ('Content-Type' , mimetypes .get (format , 'binary' ))
422
+ self .set_header ('Content-Type' , mimetypes .get (fmt , 'binary' ))
401
423
402
- buffer = io .BytesIO ()
403
- manager .canvas .print_figure (buffer , format = format )
404
- self .write (buffer .getvalue ())
424
+ buff = io .BytesIO ()
425
+ manager .canvas .print_figure (buff , format = fmt )
426
+ self .write (buff .getvalue ())
405
427
406
428
class WebSocket (tornado .websocket .WebSocketHandler ):
407
429
supports_binary = True
@@ -410,7 +432,7 @@ def open(self, fignum):
410
432
self .fignum = int (fignum )
411
433
manager = Gcf .get_fig_manager (self .fignum )
412
434
manager .add_web_socket (self )
413
- l , b , w , h = manager .canvas .figure .bbox .bounds
435
+ _ , _ , w , h = manager .canvas .figure .bbox .bounds
414
436
manager .resize (w , h )
415
437
self .on_message ('{"type":"refresh"}' )
416
438
@@ -448,37 +470,42 @@ def __init__(self):
448
470
# Static files for the CSS and JS
449
471
(r'/static/(.*)' ,
450
472
tornado .web .StaticFileHandler ,
451
- {'path' :
452
- os . path . join ( os . path . dirname ( __file__ ), 'web_backend' )}),
473
+ {'path' : self . _mpl_dirs [ 'web_backend' ]}),
474
+
453
475
# Static images for toolbar buttons
454
476
(r'/images/(.*)' ,
455
477
tornado .web .StaticFileHandler ,
456
- {'path' :
457
- os . path . join ( os . path . dirname ( __file__ ), '../mpl-data/images' )}),
478
+ {'path' : self . _mpl_dirs [ 'images' ]}),
479
+
458
480
(r'/static/jquery/css/themes/base/(.*)' ,
459
481
tornado .web .StaticFileHandler ,
460
- {'path' :
461
- os . path . join ( os . path . dirname ( __file__ ),
462
- 'web_backend/jquery/css/themes/base' )}),
482
+ {'path' : os . path . join ( self . _mpl_dirs [ 'web_backend' ], 'jquery' ,
483
+ 'css' , 'themes' , 'base' )} ),
484
+
463
485
(r'/static/jquery/css/themes/base/images/(.*)' ,
464
486
tornado .web .StaticFileHandler ,
465
- {'path' :
466
- os . path . join ( os . path . dirname ( __file__ ),
467
- 'web_backend/jquery/css/themes/base/images' )}),
487
+ {'path' : os . path . join ( self . _mpl_dirs [ 'web_backend' ], 'jquery' ,
488
+ 'css' , 'themes' , 'base' , 'images' )} ),
489
+
468
490
(r'/static/jquery/js/(.*)' , tornado .web .StaticFileHandler ,
469
- {'path' :
470
- os . path . join ( os . path . dirname ( __file__ ),
471
- 'web_backend/jquery/js' )}),
491
+ {'path' : os . path . join ( self . _mpl_dirs [ 'web_backend' ],
492
+ 'jquery' , 'js' )} ),
493
+
472
494
(r'/static/css/(.*)' , tornado .web .StaticFileHandler ,
473
- {'path' :
474
- os . path . join ( os . path . dirname ( __file__ ), 'web_backend/css' )}),
495
+ {'path' : os . path . join ( self . _mpl_dirs [ 'web_backend' ], 'css' )}),
496
+
475
497
# An MPL favicon
476
498
(r'/favicon.ico' , self .FavIcon ),
499
+
477
500
# The page that contains all of the pieces
478
- (r'/([0-9]+)/' , self .IndexPage ),
501
+ (r'/([0-9]+)/?' , self .FigurePage ),
502
+
503
+ (r'/([0-9]+)/mpl_interface.js' , self .MPLInterfaceJS ),
504
+
479
505
# Sends images and events to the browser, and receives
480
506
# events from the browser
481
507
(r'/([0-9]+)/ws' , self .WebSocket ),
508
+
482
509
# Handles the downloading (i.e., saving) of static images
483
510
(r'/([0-9]+)/download.([a-z]+)' , self .Download )
484
511
])
0 commit comments