55import os
66from uuid import uuid4 as uuid
77
8+ import tornado .ioloop
9+
810from IPython .display import display , Javascript , HTML
911from IPython .kernel .comm import Comm
1012
1113from matplotlib .figure import Figure
1214from matplotlib .backends .backend_webagg_core import (FigureManagerWebAgg ,
1315 FigureCanvasWebAggCore ,
1416 NavigationToolbar2WebAgg )
15- from matplotlib .backend_bases import ShowBase , NavigationToolbar2
17+ from matplotlib .backend_bases import ShowBase , NavigationToolbar2 , TimerBase
1618
1719
1820class Show (ShowBase ):
1921 def __call__ (self , block = None ):
20- import matplotlib ._pylab_helpers as pylab_helpers
22+ from matplotlib ._pylab_helpers import Gcf
2123 from matplotlib import is_interactive
2224
23- managers = pylab_helpers . Gcf .get_all_fig_managers ()
25+ managers = Gcf .get_all_fig_managers ()
2426 if not managers :
2527 return
2628
27- interactive = is_interactive ()
28-
2929 for manager in managers :
3030 manager .show ()
31- if not interactive and manager in pylab_helpers .Gcf ._activeQue :
32- pylab_helpers .Gcf ._activeQue .remove (manager )
31+
32+ if not is_interactive () and manager in Gcf ._activeQue :
33+ Gcf ._activeQue .remove (manager )
3334
3435
3536show = Show ()
@@ -48,19 +49,18 @@ def draw_if_interactive():
4849def connection_info ():
4950 """
5051 Return a string showing the figure and connection status for
51- the backend.
52+ the backend. This is intended as a diagnostic tool, and not for general
53+ use.
5254
5355 """
54- # TODO: Make this useful!
55- import matplotlib ._pylab_helpers as pylab_helpers
56+ from matplotlib ._pylab_helpers import Gcf
5657 result = []
57- for manager in pylab_helpers . Gcf .get_all_fig_managers ():
58+ for manager in Gcf .get_all_fig_managers ():
5859 fig = manager .canvas .figure
5960 result .append ('{} - {}' .format ((fig .get_label () or
6061 "Figure {0}" .format (manager .num )),
6162 manager .web_sockets ))
62- result .append ('Figures pending show: ' +
63- str (len (pylab_helpers .Gcf ._activeQue )))
63+ result .append ('Figures pending show: {}' .format (len (Gcf ._activeQue )))
6464 return '\n ' .join (result )
6565
6666
@@ -93,7 +93,8 @@ def __init__(self, canvas, num):
9393
9494 def display_js (self ):
9595 # XXX How to do this just once? It has to deal with multiple
96- # browser instances using the same kernel.
96+ # browser instances using the same kernel (require.js - but the
97+ # file isn't static?).
9798 display (Javascript (FigureManagerNbAgg .get_javascript ()))
9899
99100 def show (self ):
@@ -105,6 +106,10 @@ def show(self):
105106 self ._shown = True
106107
107108 def reshow (self ):
109+ """
110+ A special method to re-show the figure in the notebook.
111+
112+ """
108113 self ._shown = False
109114 self .show ()
110115
@@ -137,6 +142,43 @@ def destroy(self):
137142 for comm in self .web_sockets .copy ():
138143 comm .on_close ()
139144
145+ def clearup_closed (self ):
146+ """Clear up any closed Comms."""
147+ self .web_sockets = set ([socket for socket in self .web_sockets
148+ if not socket .is_open ()])
149+
150+
151+ class TimerTornado (TimerBase ):
152+ def _timer_start (self ):
153+ import datetime
154+ self ._timer_stop ()
155+ if self ._single :
156+ ioloop = tornado .ioloop .IOLoop .instance ()
157+ self ._timer = ioloop .add_timeout (
158+ datetime .timedelta (milliseconds = self .interval ),
159+ self ._on_timer )
160+ else :
161+ self ._timer = tornado .ioloop .PeriodicCallback (
162+ self ._on_timer ,
163+ self .interval )
164+ self ._timer .start ()
165+
166+ def _timer_stop (self ):
167+ if self ._timer is not None :
168+ self ._timer .stop ()
169+ self ._timer = None
170+
171+ def _timer_set_interval (self ):
172+ # Only stop and restart it if the timer has already been started
173+ if self ._timer is not None :
174+ self ._timer_stop ()
175+ self ._timer_start ()
176+
177+
178+ class FigureCanvasNbAgg (FigureCanvasWebAggCore ):
179+ def new_timer (self , * args , ** kwargs ):
180+ return TimerTornado (* args , ** kwargs )
181+
140182
141183def new_figure_manager (num , * args , ** kwargs ):
142184 """
@@ -151,7 +193,7 @@ def new_figure_manager_given_figure(num, figure):
151193 """
152194 Create a new figure manager instance for the given figure.
153195 """
154- canvas = FigureCanvasWebAggCore (figure )
196+ canvas = FigureCanvasNbAgg (figure )
155197 manager = FigureManagerNbAgg (canvas , num )
156198 return manager
157199
@@ -170,6 +212,8 @@ def __init__(self, manager):
170212 self .supports_binary = None
171213 self .manager = manager
172214 self .uuid = str (uuid ())
215+ # Publish an output area with a unique ID. The javascript can then
216+ # hook into this area.
173217 display (HTML ("<div id=%r></div>" % self .uuid ))
174218 try :
175219 self .comm = Comm ('matplotlib' , data = {'id' : self .uuid })
@@ -178,12 +222,17 @@ def __init__(self, manager):
178222 'instance. Are you in the IPython notebook?' )
179223 self .comm .on_msg (self .on_message )
180224
225+ manager = self .manager
226+ self .comm .on_close (lambda close_message : manager .clearup_closed ())
227+
228+ def is_open (self ):
229+ return not self .comm ._closed
230+
181231 def on_close (self ):
182232 # When the socket is closed, deregister the websocket with
183233 # the FigureManager.
184- if self .comm in self .manager .web_sockets :
185- self .manager .remove_web_socket (self )
186234 self .comm .close ()
235+ self .manager .clearup_closed ()
187236
188237 def send_json (self , content ):
189238 self .comm .send ({'data' : json .dumps (content )})
0 commit comments