5
5
import os
6
6
from uuid import uuid4 as uuid
7
7
8
+ import tornado .ioloop
9
+
8
10
from IPython .display import display , Javascript , HTML
9
11
from IPython .kernel .comm import Comm
10
12
11
13
from matplotlib .figure import Figure
12
14
from matplotlib .backends .backend_webagg_core import (FigureManagerWebAgg ,
13
15
FigureCanvasWebAggCore ,
14
16
NavigationToolbar2WebAgg )
15
- from matplotlib .backend_bases import ShowBase , NavigationToolbar2
17
+ from matplotlib .backend_bases import ShowBase , NavigationToolbar2 , TimerBase
16
18
17
19
18
20
class Show (ShowBase ):
19
21
def __call__ (self , block = None ):
20
- import matplotlib ._pylab_helpers as pylab_helpers
22
+ from matplotlib ._pylab_helpers import Gcf
21
23
from matplotlib import is_interactive
22
24
23
- managers = pylab_helpers . Gcf .get_all_fig_managers ()
25
+ managers = Gcf .get_all_fig_managers ()
24
26
if not managers :
25
27
return
26
28
27
- interactive = is_interactive ()
28
-
29
29
for manager in managers :
30
30
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 )
33
34
34
35
35
36
show = Show ()
@@ -48,19 +49,18 @@ def draw_if_interactive():
48
49
def connection_info ():
49
50
"""
50
51
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.
52
54
53
55
"""
54
- # TODO: Make this useful!
55
- import matplotlib ._pylab_helpers as pylab_helpers
56
+ from matplotlib ._pylab_helpers import Gcf
56
57
result = []
57
- for manager in pylab_helpers . Gcf .get_all_fig_managers ():
58
+ for manager in Gcf .get_all_fig_managers ():
58
59
fig = manager .canvas .figure
59
60
result .append ('{} - {}' .format ((fig .get_label () or
60
61
"Figure {0}" .format (manager .num )),
61
62
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 )))
64
64
return '\n ' .join (result )
65
65
66
66
@@ -93,7 +93,8 @@ def __init__(self, canvas, num):
93
93
94
94
def display_js (self ):
95
95
# 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?).
97
98
display (Javascript (FigureManagerNbAgg .get_javascript ()))
98
99
99
100
def show (self ):
@@ -105,6 +106,10 @@ def show(self):
105
106
self ._shown = True
106
107
107
108
def reshow (self ):
109
+ """
110
+ A special method to re-show the figure in the notebook.
111
+
112
+ """
108
113
self ._shown = False
109
114
self .show ()
110
115
@@ -137,6 +142,43 @@ def destroy(self):
137
142
for comm in self .web_sockets .copy ():
138
143
comm .on_close ()
139
144
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
+
140
182
141
183
def new_figure_manager (num , * args , ** kwargs ):
142
184
"""
@@ -151,7 +193,7 @@ def new_figure_manager_given_figure(num, figure):
151
193
"""
152
194
Create a new figure manager instance for the given figure.
153
195
"""
154
- canvas = FigureCanvasWebAggCore (figure )
196
+ canvas = FigureCanvasNbAgg (figure )
155
197
manager = FigureManagerNbAgg (canvas , num )
156
198
return manager
157
199
@@ -170,6 +212,8 @@ def __init__(self, manager):
170
212
self .supports_binary = None
171
213
self .manager = manager
172
214
self .uuid = str (uuid ())
215
+ # Publish an output area with a unique ID. The javascript can then
216
+ # hook into this area.
173
217
display (HTML ("<div id=%r></div>" % self .uuid ))
174
218
try :
175
219
self .comm = Comm ('matplotlib' , data = {'id' : self .uuid })
@@ -178,12 +222,17 @@ def __init__(self, manager):
178
222
'instance. Are you in the IPython notebook?' )
179
223
self .comm .on_msg (self .on_message )
180
224
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
+
181
231
def on_close (self ):
182
232
# When the socket is closed, deregister the websocket with
183
233
# the FigureManager.
184
- if self .comm in self .manager .web_sockets :
185
- self .manager .remove_web_socket (self )
186
234
self .comm .close ()
235
+ self .manager .clearup_closed ()
187
236
188
237
def send_json (self , content ):
189
238
self .comm .send ({'data' : json .dumps (content )})
0 commit comments