##// END OF EJS Templates
define InlineBackend configurable in its own file...
MinRK -
Show More
@@ -0,0 +1,85
1 """Configurable for configuring the IPython inline backend
2
3 This module does not import anything from matplotlib.
4 """
5 #-----------------------------------------------------------------------------
6 # Copyright (C) 2011 The IPython Development Team
7 #
8 # Distributed under the terms of the BSD License. The full license is in
9 # the file COPYING, distributed as part of this software.
10 #-----------------------------------------------------------------------------
11
12 #-----------------------------------------------------------------------------
13 # Imports
14 #-----------------------------------------------------------------------------
15
16 from IPython.config.configurable import SingletonConfigurable
17 from IPython.utils.traitlets import Dict, Instance, CaselessStrEnum, Bool
18 from IPython.utils.warn import warn
19
20 #-----------------------------------------------------------------------------
21 # Configurable for inline backend options
22 #-----------------------------------------------------------------------------
23
24 # inherit from InlineBackendConfig for deprecation purposes
25 class InlineBackendConfig(SingletonConfigurable):
26 pass
27
28 class InlineBackend(InlineBackendConfig):
29 """An object to store configuration of the inline backend."""
30
31 def _config_changed(self, name, old, new):
32 # warn on change of renamed config section
33 if new.InlineBackendConfig != old.InlineBackendConfig:
34 warn("InlineBackendConfig has been renamed to InlineBackend")
35 super(InlineBackend, self)._config_changed(name, old, new)
36
37 # The typical default figure size is too large for inline use,
38 # so we shrink the figure size to 6x4, and tweak fonts to
39 # make that fit.
40 rc = Dict({'figure.figsize': (6.0,4.0),
41 # play nicely with white background in the Qt and notebook frontend
42 'figure.facecolor': 'white',
43 'figure.edgecolor': 'white',
44 # 12pt labels get cutoff on 6x4 logplots, so use 10pt.
45 'font.size': 10,
46 # 72 dpi matches SVG/qtconsole
47 # this only affects PNG export, as SVG has no dpi setting
48 'savefig.dpi': 72,
49 # 10pt still needs a little more room on the xlabel:
50 'figure.subplot.bottom' : .125
51 }, config=True,
52 help="""Subset of matplotlib rcParams that should be different for the
53 inline backend."""
54 )
55
56 figure_format = CaselessStrEnum(['svg', 'png', 'retina'], default_value='png', config=True,
57 help="The image format for figures with the inline backend.")
58
59 def _figure_format_changed(self, name, old, new):
60 from IPython.core.pylabtools import select_figure_format
61 if self.shell is None:
62 return
63 else:
64 select_figure_format(self.shell, new)
65
66 close_figures = Bool(True, config=True,
67 help="""Close all figures at the end of each cell.
68
69 When True, ensures that each cell starts with no active figures, but it
70 also means that one must keep track of references in order to edit or
71 redraw figures in subsequent cells. This mode is ideal for the notebook,
72 where residual plots from other cells might be surprising.
73
74 When False, one must call figure() to create new figures. This means
75 that gcf() and getfigs() can reference figures created in other cells,
76 and the active figure can continue to be edited with pylab/pyplot
77 methods that reference the current active figure. This mode facilitates
78 iterative editing of figures, and behaves most consistently with
79 other matplotlib backends, but figure barriers between cells must
80 be explicit.
81 """)
82
83 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
84
85
@@ -1,393 +1,387
1 1 """ A minimal application base mixin for all ZMQ based IPython frontends.
2 2
3 3 This is not a complete console app, as subprocess will not be able to receive
4 4 input, there is no real readline support, among other limitations. This is a
5 5 refactoring of what used to be the IPython/qt/console/qtconsoleapp.py
6 6
7 7 Authors:
8 8
9 9 * Evan Patterson
10 10 * Min RK
11 11 * Erik Tollerud
12 12 * Fernando Perez
13 13 * Bussonnier Matthias
14 14 * Thomas Kluyver
15 15 * Paul Ivanov
16 16
17 17 """
18 18
19 19 #-----------------------------------------------------------------------------
20 20 # Imports
21 21 #-----------------------------------------------------------------------------
22 22
23 23 # stdlib imports
24 24 import atexit
25 25 import json
26 26 import os
27 27 import signal
28 28 import sys
29 29 import uuid
30 30
31 31
32 32 # Local imports
33 33 from IPython.config.application import boolean_flag
34 34 from IPython.core.profiledir import ProfileDir
35 35 from IPython.kernel.blocking import BlockingKernelClient
36 36 from IPython.kernel import KernelManager
37 37 from IPython.kernel import tunnel_to_kernel, find_connection_file, swallow_argv
38 38 from IPython.utils.path import filefind
39 39 from IPython.utils.py3compat import str_to_bytes
40 40 from IPython.utils.traitlets import (
41 41 Dict, List, Unicode, CUnicode, Int, CBool, Any
42 42 )
43 43 from IPython.kernel.zmq.kernelapp import (
44 44 kernel_flags,
45 45 kernel_aliases,
46 46 IPKernelApp
47 47 )
48 from IPython.kernel.zmq.pylab.config import InlineBackend
48 49 from IPython.kernel.zmq.session import Session, default_secure
49 50 from IPython.kernel.zmq.zmqshell import ZMQInteractiveShell
50 51 from IPython.kernel.connect import ConnectionFileMixin
51 52
52 53 #-----------------------------------------------------------------------------
53 54 # Network Constants
54 55 #-----------------------------------------------------------------------------
55 56
56 57 from IPython.utils.localinterfaces import localhost
57 58
58 59 #-----------------------------------------------------------------------------
59 60 # Globals
60 61 #-----------------------------------------------------------------------------
61 62
62 63
63 64 #-----------------------------------------------------------------------------
64 65 # Aliases and Flags
65 66 #-----------------------------------------------------------------------------
66 67
67 68 flags = dict(kernel_flags)
68 69
69 70 # the flags that are specific to the frontend
70 71 # these must be scrubbed before being passed to the kernel,
71 72 # or it will raise an error on unrecognized flags
72 73 app_flags = {
73 74 'existing' : ({'IPythonConsoleApp' : {'existing' : 'kernel*.json'}},
74 75 "Connect to an existing kernel. If no argument specified, guess most recent"),
75 76 }
76 77 app_flags.update(boolean_flag(
77 78 'confirm-exit', 'IPythonConsoleApp.confirm_exit',
78 79 """Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
79 80 to force a direct exit without any confirmation.
80 81 """,
81 82 """Don't prompt the user when exiting. This will terminate the kernel
82 83 if it is owned by the frontend, and leave it alive if it is external.
83 84 """
84 85 ))
85 86 flags.update(app_flags)
86 87
87 88 aliases = dict(kernel_aliases)
88 89
89 90 # also scrub aliases from the frontend
90 91 app_aliases = dict(
91 92 ip = 'IPythonConsoleApp.ip',
92 93 transport = 'IPythonConsoleApp.transport',
93 94 hb = 'IPythonConsoleApp.hb_port',
94 95 shell = 'IPythonConsoleApp.shell_port',
95 96 iopub = 'IPythonConsoleApp.iopub_port',
96 97 stdin = 'IPythonConsoleApp.stdin_port',
97 98 existing = 'IPythonConsoleApp.existing',
98 99 f = 'IPythonConsoleApp.connection_file',
99 100
100 101
101 102 ssh = 'IPythonConsoleApp.sshserver',
102 103 )
103 104 aliases.update(app_aliases)
104 105
105 106 #-----------------------------------------------------------------------------
106 107 # Classes
107 108 #-----------------------------------------------------------------------------
108 109
109 110 #-----------------------------------------------------------------------------
110 111 # IPythonConsole
111 112 #-----------------------------------------------------------------------------
112 113
113 classes = [IPKernelApp, ZMQInteractiveShell, KernelManager, ProfileDir, Session]
114
115 try:
116 from IPython.kernel.zmq.pylab.backend_inline import InlineBackend
117 except ImportError:
118 pass
119 else:
120 classes.append(InlineBackend)
114 classes = [IPKernelApp, ZMQInteractiveShell, KernelManager, ProfileDir, Session, InlineBackend]
121 115
122 116 class IPythonConsoleApp(ConnectionFileMixin):
123 117 name = 'ipython-console-mixin'
124 118
125 119 description = """
126 120 The IPython Mixin Console.
127 121
128 122 This class contains the common portions of console client (QtConsole,
129 123 ZMQ-based terminal console, etc). It is not a full console, in that
130 124 launched terminal subprocesses will not be able to accept input.
131 125
132 126 The Console using this mixing supports various extra features beyond
133 127 the single-process Terminal IPython shell, such as connecting to
134 128 existing kernel, via:
135 129
136 130 ipython <appname> --existing
137 131
138 132 as well as tunnel via SSH
139 133
140 134 """
141 135
142 136 classes = classes
143 137 flags = Dict(flags)
144 138 aliases = Dict(aliases)
145 139 kernel_manager_class = KernelManager
146 140 kernel_client_class = BlockingKernelClient
147 141
148 142 kernel_argv = List(Unicode)
149 143 # frontend flags&aliases to be stripped when building kernel_argv
150 144 frontend_flags = Any(app_flags)
151 145 frontend_aliases = Any(app_aliases)
152 146
153 147 # create requested profiles by default, if they don't exist:
154 148 auto_create = CBool(True)
155 149 # connection info:
156 150
157 151 sshserver = Unicode('', config=True,
158 152 help="""The SSH server to use to connect to the kernel.""")
159 153 sshkey = Unicode('', config=True,
160 154 help="""Path to the ssh key to use for logging in to the ssh server.""")
161 155
162 156 hb_port = Int(0, config=True,
163 157 help="set the heartbeat port [default: random]")
164 158 shell_port = Int(0, config=True,
165 159 help="set the shell (ROUTER) port [default: random]")
166 160 iopub_port = Int(0, config=True,
167 161 help="set the iopub (PUB) port [default: random]")
168 162 stdin_port = Int(0, config=True,
169 163 help="set the stdin (DEALER) port [default: random]")
170 164 connection_file = Unicode('', config=True,
171 165 help="""JSON file in which to store connection info [default: kernel-<pid>.json]
172 166
173 167 This file will contain the IP, ports, and authentication key needed to connect
174 168 clients to this kernel. By default, this file will be created in the security-dir
175 169 of the current profile, but can be specified by absolute path.
176 170 """)
177 171 def _connection_file_default(self):
178 172 return 'kernel-%i.json' % os.getpid()
179 173
180 174 existing = CUnicode('', config=True,
181 175 help="""Connect to an already running kernel""")
182 176
183 177 confirm_exit = CBool(True, config=True,
184 178 help="""
185 179 Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
186 180 to force a direct exit without any confirmation.""",
187 181 )
188 182
189 183
190 184 def build_kernel_argv(self, argv=None):
191 185 """build argv to be passed to kernel subprocess"""
192 186 if argv is None:
193 187 argv = sys.argv[1:]
194 188 self.kernel_argv = swallow_argv(argv, self.frontend_aliases, self.frontend_flags)
195 189 # kernel should inherit default config file from frontend
196 190 self.kernel_argv.append("--IPKernelApp.parent_appname='%s'" % self.name)
197 191
198 192 def init_connection_file(self):
199 193 """find the connection file, and load the info if found.
200 194
201 195 The current working directory and the current profile's security
202 196 directory will be searched for the file if it is not given by
203 197 absolute path.
204 198
205 199 When attempting to connect to an existing kernel and the `--existing`
206 200 argument does not match an existing file, it will be interpreted as a
207 201 fileglob, and the matching file in the current profile's security dir
208 202 with the latest access time will be used.
209 203
210 204 After this method is called, self.connection_file contains the *full path*
211 205 to the connection file, never just its name.
212 206 """
213 207 if self.existing:
214 208 try:
215 209 cf = find_connection_file(self.existing)
216 210 except Exception:
217 211 self.log.critical("Could not find existing kernel connection file %s", self.existing)
218 212 self.exit(1)
219 213 self.log.info("Connecting to existing kernel: %s" % cf)
220 214 self.connection_file = cf
221 215 else:
222 216 # not existing, check if we are going to write the file
223 217 # and ensure that self.connection_file is a full path, not just the shortname
224 218 try:
225 219 cf = find_connection_file(self.connection_file)
226 220 except Exception:
227 221 # file might not exist
228 222 if self.connection_file == os.path.basename(self.connection_file):
229 223 # just shortname, put it in security dir
230 224 cf = os.path.join(self.profile_dir.security_dir, self.connection_file)
231 225 else:
232 226 cf = self.connection_file
233 227 self.connection_file = cf
234 228
235 229 # should load_connection_file only be used for existing?
236 230 # as it is now, this allows reusing ports if an existing
237 231 # file is requested
238 232 try:
239 233 self.load_connection_file()
240 234 except Exception:
241 235 self.log.error("Failed to load connection file: %r", self.connection_file, exc_info=True)
242 236 self.exit(1)
243 237
244 238 def load_connection_file(self):
245 239 """load ip/port/hmac config from JSON connection file"""
246 240 # this is identical to IPKernelApp.load_connection_file
247 241 # perhaps it can be centralized somewhere?
248 242 try:
249 243 fname = filefind(self.connection_file, ['.', self.profile_dir.security_dir])
250 244 except IOError:
251 245 self.log.debug("Connection File not found: %s", self.connection_file)
252 246 return
253 247 self.log.debug(u"Loading connection file %s", fname)
254 248 with open(fname) as f:
255 249 cfg = json.load(f)
256 250 self.transport = cfg.get('transport', 'tcp')
257 251 self.ip = cfg.get('ip', localhost())
258 252
259 253 for channel in ('hb', 'shell', 'iopub', 'stdin', 'control'):
260 254 name = channel + '_port'
261 255 if getattr(self, name) == 0 and name in cfg:
262 256 # not overridden by config or cl_args
263 257 setattr(self, name, cfg[name])
264 258 if 'key' in cfg:
265 259 self.config.Session.key = str_to_bytes(cfg['key'])
266 260 if 'signature_scheme' in cfg:
267 261 self.config.Session.signature_scheme = cfg['signature_scheme']
268 262
269 263 def init_ssh(self):
270 264 """set up ssh tunnels, if needed."""
271 265 if not self.existing or (not self.sshserver and not self.sshkey):
272 266 return
273 267 self.load_connection_file()
274 268
275 269 transport = self.transport
276 270 ip = self.ip
277 271
278 272 if transport != 'tcp':
279 273 self.log.error("Can only use ssh tunnels with TCP sockets, not %s", transport)
280 274 sys.exit(-1)
281 275
282 276 if self.sshkey and not self.sshserver:
283 277 # specifying just the key implies that we are connecting directly
284 278 self.sshserver = ip
285 279 ip = localhost()
286 280
287 281 # build connection dict for tunnels:
288 282 info = dict(ip=ip,
289 283 shell_port=self.shell_port,
290 284 iopub_port=self.iopub_port,
291 285 stdin_port=self.stdin_port,
292 286 hb_port=self.hb_port
293 287 )
294 288
295 289 self.log.info("Forwarding connections to %s via %s"%(ip, self.sshserver))
296 290
297 291 # tunnels return a new set of ports, which will be on localhost:
298 292 self.ip = localhost()
299 293 try:
300 294 newports = tunnel_to_kernel(info, self.sshserver, self.sshkey)
301 295 except:
302 296 # even catch KeyboardInterrupt
303 297 self.log.error("Could not setup tunnels", exc_info=True)
304 298 self.exit(1)
305 299
306 300 self.shell_port, self.iopub_port, self.stdin_port, self.hb_port = newports
307 301
308 302 cf = self.connection_file
309 303 base,ext = os.path.splitext(cf)
310 304 base = os.path.basename(base)
311 305 self.connection_file = os.path.basename(base)+'-ssh'+ext
312 306 self.log.critical("To connect another client via this tunnel, use:")
313 307 self.log.critical("--existing %s" % self.connection_file)
314 308
315 309 def _new_connection_file(self):
316 310 cf = ''
317 311 while not cf:
318 312 # we don't need a 128b id to distinguish kernels, use more readable
319 313 # 48b node segment (12 hex chars). Users running more than 32k simultaneous
320 314 # kernels can subclass.
321 315 ident = str(uuid.uuid4()).split('-')[-1]
322 316 cf = os.path.join(self.profile_dir.security_dir, 'kernel-%s.json' % ident)
323 317 # only keep if it's actually new. Protect against unlikely collision
324 318 # in 48b random search space
325 319 cf = cf if not os.path.exists(cf) else ''
326 320 return cf
327 321
328 322 def init_kernel_manager(self):
329 323 # Don't let Qt or ZMQ swallow KeyboardInterupts.
330 324 if self.existing:
331 325 self.kernel_manager = None
332 326 return
333 327 signal.signal(signal.SIGINT, signal.SIG_DFL)
334 328
335 329 # Create a KernelManager and start a kernel.
336 330 self.kernel_manager = self.kernel_manager_class(
337 331 ip=self.ip,
338 332 transport=self.transport,
339 333 shell_port=self.shell_port,
340 334 iopub_port=self.iopub_port,
341 335 stdin_port=self.stdin_port,
342 336 hb_port=self.hb_port,
343 337 connection_file=self.connection_file,
344 338 parent=self,
345 339 )
346 340 self.kernel_manager.client_factory = self.kernel_client_class
347 341 self.kernel_manager.start_kernel(extra_arguments=self.kernel_argv)
348 342 atexit.register(self.kernel_manager.cleanup_ipc_files)
349 343
350 344 if self.sshserver:
351 345 # ssh, write new connection file
352 346 self.kernel_manager.write_connection_file()
353 347
354 348 # in case KM defaults / ssh writing changes things:
355 349 km = self.kernel_manager
356 350 self.shell_port=km.shell_port
357 351 self.iopub_port=km.iopub_port
358 352 self.stdin_port=km.stdin_port
359 353 self.hb_port=km.hb_port
360 354 self.connection_file = km.connection_file
361 355
362 356 atexit.register(self.kernel_manager.cleanup_connection_file)
363 357
364 358 def init_kernel_client(self):
365 359 if self.kernel_manager is not None:
366 360 self.kernel_client = self.kernel_manager.client()
367 361 else:
368 362 self.kernel_client = self.kernel_client_class(
369 363 ip=self.ip,
370 364 transport=self.transport,
371 365 shell_port=self.shell_port,
372 366 iopub_port=self.iopub_port,
373 367 stdin_port=self.stdin_port,
374 368 hb_port=self.hb_port,
375 369 connection_file=self.connection_file,
376 370 parent=self,
377 371 )
378 372
379 373 self.kernel_client.start_channels()
380 374
381 375
382 376
383 377 def initialize(self, argv=None):
384 378 """
385 379 Classes which mix this class in should call:
386 380 IPythonConsoleApp.initialize(self,argv)
387 381 """
388 382 self.init_connection_file()
389 383 default_secure(self.config)
390 384 self.init_ssh()
391 385 self.init_kernel_manager()
392 386 self.init_kernel_client()
393 387
@@ -1,214 +1,153
1 """Produce SVG versions of active plots for display by the rich Qt frontend.
2 """
1 """A matplotlib backend for publishing figures via display_data"""
2 #-----------------------------------------------------------------------------
3 # Copyright (C) 2011 The IPython Development Team
4 #
5 # Distributed under the terms of the BSD License. The full license is in
6 # the file COPYING, distributed as part of this software.
7 #-----------------------------------------------------------------------------
8
3 9 #-----------------------------------------------------------------------------
4 10 # Imports
5 11 #-----------------------------------------------------------------------------
6 12 from __future__ import print_function
7 13
8 14 # Third-party imports
9 15 import matplotlib
10 from matplotlib.backends.backend_agg import new_figure_manager, FigureCanvasAgg
16 from matplotlib.backends.backend_agg import FigureCanvasAgg
11 17 from matplotlib._pylab_helpers import Gcf
12 18
13 # Local imports.
14 from IPython.config.configurable import SingletonConfigurable
19 # Local imports
20 from IPython.core.getipython import get_ipython
15 21 from IPython.core.display import display
16 from IPython.core.displaypub import publish_display_data
17 from IPython.core.pylabtools import print_figure, select_figure_format
18 from IPython.utils.traitlets import Dict, Instance, CaselessStrEnum, Bool
19 from IPython.utils.warn import warn
20
21 #-----------------------------------------------------------------------------
22 # Configurable for inline backend options
23 #-----------------------------------------------------------------------------
24 # inherit from InlineBackendConfig for deprecation purposes
25 class InlineBackendConfig(SingletonConfigurable):
26 pass
27
28 class InlineBackend(InlineBackendConfig):
29 """An object to store configuration of the inline backend."""
30
31 def _config_changed(self, name, old, new):
32 # warn on change of renamed config section
33 if new.InlineBackendConfig != old.InlineBackendConfig:
34 warn("InlineBackendConfig has been renamed to InlineBackend")
35 super(InlineBackend, self)._config_changed(name, old, new)
36
37 # The typical default figure size is too large for inline use,
38 # so we shrink the figure size to 6x4, and tweak fonts to
39 # make that fit.
40 rc = Dict({'figure.figsize': (6.0,4.0),
41 # play nicely with white background in the Qt and notebook frontend
42 'figure.facecolor': 'white',
43 'figure.edgecolor': 'white',
44 # 12pt labels get cutoff on 6x4 logplots, so use 10pt.
45 'font.size': 10,
46 # 72 dpi matches SVG/qtconsole
47 # this only affects PNG export, as SVG has no dpi setting
48 'savefig.dpi': 72,
49 # 10pt still needs a little more room on the xlabel:
50 'figure.subplot.bottom' : .125
51 }, config=True,
52 help="""Subset of matplotlib rcParams that should be different for the
53 inline backend."""
54 )
55
56 figure_format = CaselessStrEnum(['svg', 'png', 'retina'], default_value='png', config=True,
57 help="The image format for figures with the inline backend.")
58
59 def _figure_format_changed(self, name, old, new):
60 if self.shell is None:
61 return
62 else:
63 select_figure_format(self.shell, new)
64
65 close_figures = Bool(True, config=True,
66 help="""Close all figures at the end of each cell.
67
68 When True, ensures that each cell starts with no active figures, but it
69 also means that one must keep track of references in order to edit or
70 redraw figures in subsequent cells. This mode is ideal for the notebook,
71 where residual plots from other cells might be surprising.
72
73 When False, one must call figure() to create new figures. This means
74 that gcf() and getfigs() can reference figures created in other cells,
75 and the active figure can continue to be edited with pylab/pyplot
76 methods that reference the current active figure. This mode facilitates
77 iterative editing of figures, and behaves most consistently with
78 other matplotlib backends, but figure barriers between cells must
79 be explicit.
80 """)
81
82 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
83 22
23 from .config import InlineBackend
84 24
85 25 #-----------------------------------------------------------------------------
86 26 # Functions
87 27 #-----------------------------------------------------------------------------
88 28
89 29 def show(close=None):
90 30 """Show all figures as SVG/PNG payloads sent to the IPython clients.
91 31
92 32 Parameters
93 33 ----------
94 34 close : bool, optional
95 35 If true, a ``plt.close('all')`` call is automatically issued after
96 36 sending all the figures. If this is set, the figures will entirely
97 37 removed from the internal list of figures.
98 38 """
99 39 if close is None:
100 40 close = InlineBackend.instance().close_figures
101 41 try:
102 42 for figure_manager in Gcf.get_all_fig_managers():
103 43 display(figure_manager.canvas.figure)
104 44 finally:
105 45 show._to_draw = []
106 46 if close:
107 47 matplotlib.pyplot.close('all')
108 48
109 49
110
111 50 # This flag will be reset by draw_if_interactive when called
112 51 show._draw_called = False
113 52 # list of figures to draw when flush_figures is called
114 53 show._to_draw = []
115 54
116 55
117 56 def draw_if_interactive():
118 57 """
119 58 Is called after every pylab drawing command
120 59 """
121 60 # signal that the current active figure should be sent at the end of
122 61 # execution. Also sets the _draw_called flag, signaling that there will be
123 62 # something to send. At the end of the code execution, a separate call to
124 63 # flush_figures() will act upon these values
125 64 manager = Gcf.get_active()
126 65 if manager is None:
127 66 return
128 67 fig = manager.canvas.figure
129 68
130 69 # Hack: matplotlib FigureManager objects in interacive backends (at least
131 70 # in some of them) monkeypatch the figure object and add a .show() method
132 71 # to it. This applies the same monkeypatch in order to support user code
133 72 # that might expect `.show()` to be part of the official API of figure
134 73 # objects.
135 74 # For further reference:
136 75 # https://github.com/ipython/ipython/issues/1612
137 76 # https://github.com/matplotlib/matplotlib/issues/835
138 77
139 78 if not hasattr(fig, 'show'):
140 79 # Queue up `fig` for display
141 80 fig.show = lambda *a: display(fig)
142 81
143 82 # If matplotlib was manually set to non-interactive mode, this function
144 83 # should be a no-op (otherwise we'll generate duplicate plots, since a user
145 84 # who set ioff() manually expects to make separate draw/show calls).
146 85 if not matplotlib.is_interactive():
147 86 return
148 87
149 88 # ensure current figure will be drawn, and each subsequent call
150 89 # of draw_if_interactive() moves the active figure to ensure it is
151 90 # drawn last
152 91 try:
153 92 show._to_draw.remove(fig)
154 93 except ValueError:
155 94 # ensure it only appears in the draw list once
156 95 pass
157 96 # Queue up the figure for drawing in next show() call
158 97 show._to_draw.append(fig)
159 98 show._draw_called = True
160 99
161 100
162 101 def flush_figures():
163 102 """Send all figures that changed
164 103
165 104 This is meant to be called automatically and will call show() if, during
166 105 prior code execution, there had been any calls to draw_if_interactive.
167 106
168 107 This function is meant to be used as a post_execute callback in IPython,
169 108 so user-caused errors are handled with showtraceback() instead of being
170 109 allowed to raise. If this function is not called from within IPython,
171 110 then these exceptions will raise.
172 111 """
173 112 if not show._draw_called:
174 113 return
175 114
176 115 if InlineBackend.instance().close_figures:
177 116 # ignore the tracking, just draw and close all figures
178 117 try:
179 118 return show(True)
180 119 except Exception as e:
181 120 # safely show traceback if in IPython, else raise
182 121 try:
183 122 get_ipython
184 123 except NameError:
185 124 raise e
186 125 else:
187 126 get_ipython().showtraceback()
188 127 return
189 128 try:
190 129 # exclude any figures that were closed:
191 130 active = set([fm.canvas.figure for fm in Gcf.get_all_fig_managers()])
192 131 for fig in [ fig for fig in show._to_draw if fig in active ]:
193 132 try:
194 133 display(fig)
195 134 except Exception as e:
196 135 # safely show traceback if in IPython, else raise
197 136 try:
198 137 get_ipython
199 138 except NameError:
200 139 raise e
201 140 else:
202 141 get_ipython().showtraceback()
203 142 break
204 143 finally:
205 144 # clear flags for next round
206 145 show._to_draw = []
207 146 show._draw_called = False
208 147
209 148
210 149 # Changes to matplotlib in version 1.2 requires a mpl backend to supply a default
211 150 # figurecanvas. This is set here to a Agg canvas
212 151 # See https://github.com/matplotlib/matplotlib/pull/1125
213 152 FigureCanvas = FigureCanvasAgg
214 153
General Comments 0
You need to be logged in to leave comments. Login now