##// END OF EJS Templates
%lsmagic returns a RichRepr of the magics dict...
MinRK -
Show More
@@ -1,693 +1,677 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """Magic functions for InteractiveShell.
2 """Magic functions for InteractiveShell.
3 """
3 """
4
4
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6 # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de> and
6 # Copyright (C) 2001 Janko Hauser <jhauser@zscout.de> and
7 # Copyright (C) 2001 Fernando Perez <fperez@colorado.edu>
7 # Copyright (C) 2001 Fernando Perez <fperez@colorado.edu>
8 # Copyright (C) 2008 The IPython Development Team
8 # Copyright (C) 2008 The IPython Development Team
9
9
10 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
11 # the file COPYING, distributed as part of this software.
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 # Stdlib
17 # Stdlib
18 import json
18 import json
19 import os
19 import os
20 import re
20 import re
21 import sys
21 import sys
22 import types
22 import types
23 from getopt import getopt, GetoptError
23 from getopt import getopt, GetoptError
24
24
25 # Our own
25 # Our own
26 from IPython.config.configurable import Configurable
26 from IPython.config.configurable import Configurable
27 from IPython.core import oinspect
27 from IPython.core import oinspect
28 from IPython.core.error import UsageError
28 from IPython.core.error import UsageError
29 from IPython.core.inputsplitter import ESC_MAGIC, ESC_MAGIC2
29 from IPython.core.inputsplitter import ESC_MAGIC, ESC_MAGIC2
30 from IPython.external.decorator import decorator
30 from IPython.external.decorator import decorator
31 from IPython.utils.ipstruct import Struct
31 from IPython.utils.ipstruct import Struct
32 from IPython.utils.process import arg_split
32 from IPython.utils.process import arg_split
33 from IPython.utils.text import dedent
33 from IPython.utils.text import dedent
34 from IPython.utils.traitlets import Bool, Dict, Instance, MetaHasTraits
34 from IPython.utils.traitlets import Bool, Dict, Instance, MetaHasTraits
35 from IPython.utils.warn import error
35 from IPython.utils.warn import error
36
36
37 #-----------------------------------------------------------------------------
37 #-----------------------------------------------------------------------------
38 # Globals
38 # Globals
39 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
40
40
41 # A dict we'll use for each class that has magics, used as temporary storage to
41 # A dict we'll use for each class that has magics, used as temporary storage to
42 # pass information between the @line/cell_magic method decorators and the
42 # pass information between the @line/cell_magic method decorators and the
43 # @magics_class class decorator, because the method decorators have no
43 # @magics_class class decorator, because the method decorators have no
44 # access to the class when they run. See for more details:
44 # access to the class when they run. See for more details:
45 # http://stackoverflow.com/questions/2366713/can-a-python-decorator-of-an-instance-method-access-the-class
45 # http://stackoverflow.com/questions/2366713/can-a-python-decorator-of-an-instance-method-access-the-class
46
46
47 magics = dict(line={}, cell={})
47 magics = dict(line={}, cell={})
48
48
49 magic_kinds = ('line', 'cell')
49 magic_kinds = ('line', 'cell')
50 magic_spec = ('line', 'cell', 'line_cell')
50 magic_spec = ('line', 'cell', 'line_cell')
51 magic_escapes = dict(line=ESC_MAGIC, cell=ESC_MAGIC2)
51 magic_escapes = dict(line=ESC_MAGIC, cell=ESC_MAGIC2)
52
52
53 #-----------------------------------------------------------------------------
53 #-----------------------------------------------------------------------------
54 # Utility classes and functions
54 # Utility classes and functions
55 #-----------------------------------------------------------------------------
55 #-----------------------------------------------------------------------------
56
56
57 class Bunch: pass
57 class Bunch: pass
58
58
59
59
60 def on_off(tag):
60 def on_off(tag):
61 """Return an ON/OFF string for a 1/0 input. Simple utility function."""
61 """Return an ON/OFF string for a 1/0 input. Simple utility function."""
62 return ['OFF','ON'][tag]
62 return ['OFF','ON'][tag]
63
63
64
64
65 def compress_dhist(dh):
65 def compress_dhist(dh):
66 """Compress a directory history into a new one with at most 20 entries.
66 """Compress a directory history into a new one with at most 20 entries.
67
67
68 Return a new list made from the first and last 10 elements of dhist after
68 Return a new list made from the first and last 10 elements of dhist after
69 removal of duplicates.
69 removal of duplicates.
70 """
70 """
71 head, tail = dh[:-10], dh[-10:]
71 head, tail = dh[:-10], dh[-10:]
72
72
73 newhead = []
73 newhead = []
74 done = set()
74 done = set()
75 for h in head:
75 for h in head:
76 if h in done:
76 if h in done:
77 continue
77 continue
78 newhead.append(h)
78 newhead.append(h)
79 done.add(h)
79 done.add(h)
80
80
81 return newhead + tail
81 return newhead + tail
82
82
83
83
84 def needs_local_scope(func):
84 def needs_local_scope(func):
85 """Decorator to mark magic functions which need to local scope to run."""
85 """Decorator to mark magic functions which need to local scope to run."""
86 func.needs_local_scope = True
86 func.needs_local_scope = True
87 return func
87 return func
88
88
89 #-----------------------------------------------------------------------------
89 #-----------------------------------------------------------------------------
90 # Class and method decorators for registering magics
90 # Class and method decorators for registering magics
91 #-----------------------------------------------------------------------------
91 #-----------------------------------------------------------------------------
92
92
93 def magics_class(cls):
93 def magics_class(cls):
94 """Class decorator for all subclasses of the main Magics class.
94 """Class decorator for all subclasses of the main Magics class.
95
95
96 Any class that subclasses Magics *must* also apply this decorator, to
96 Any class that subclasses Magics *must* also apply this decorator, to
97 ensure that all the methods that have been decorated as line/cell magics
97 ensure that all the methods that have been decorated as line/cell magics
98 get correctly registered in the class instance. This is necessary because
98 get correctly registered in the class instance. This is necessary because
99 when method decorators run, the class does not exist yet, so they
99 when method decorators run, the class does not exist yet, so they
100 temporarily store their information into a module global. Application of
100 temporarily store their information into a module global. Application of
101 this class decorator copies that global data to the class instance and
101 this class decorator copies that global data to the class instance and
102 clears the global.
102 clears the global.
103
103
104 Obviously, this mechanism is not thread-safe, which means that the
104 Obviously, this mechanism is not thread-safe, which means that the
105 *creation* of subclasses of Magic should only be done in a single-thread
105 *creation* of subclasses of Magic should only be done in a single-thread
106 context. Instantiation of the classes has no restrictions. Given that
106 context. Instantiation of the classes has no restrictions. Given that
107 these classes are typically created at IPython startup time and before user
107 these classes are typically created at IPython startup time and before user
108 application code becomes active, in practice this should not pose any
108 application code becomes active, in practice this should not pose any
109 problems.
109 problems.
110 """
110 """
111 cls.registered = True
111 cls.registered = True
112 cls.magics = dict(line = magics['line'],
112 cls.magics = dict(line = magics['line'],
113 cell = magics['cell'])
113 cell = magics['cell'])
114 magics['line'] = {}
114 magics['line'] = {}
115 magics['cell'] = {}
115 magics['cell'] = {}
116 return cls
116 return cls
117
117
118
118
119 def record_magic(dct, magic_kind, magic_name, func):
119 def record_magic(dct, magic_kind, magic_name, func):
120 """Utility function to store a function as a magic of a specific kind.
120 """Utility function to store a function as a magic of a specific kind.
121
121
122 Parameters
122 Parameters
123 ----------
123 ----------
124 dct : dict
124 dct : dict
125 A dictionary with 'line' and 'cell' subdicts.
125 A dictionary with 'line' and 'cell' subdicts.
126
126
127 magic_kind : str
127 magic_kind : str
128 Kind of magic to be stored.
128 Kind of magic to be stored.
129
129
130 magic_name : str
130 magic_name : str
131 Key to store the magic as.
131 Key to store the magic as.
132
132
133 func : function
133 func : function
134 Callable object to store.
134 Callable object to store.
135 """
135 """
136 if magic_kind == 'line_cell':
136 if magic_kind == 'line_cell':
137 dct['line'][magic_name] = dct['cell'][magic_name] = func
137 dct['line'][magic_name] = dct['cell'][magic_name] = func
138 else:
138 else:
139 dct[magic_kind][magic_name] = func
139 dct[magic_kind][magic_name] = func
140
140
141
141
142 def validate_type(magic_kind):
142 def validate_type(magic_kind):
143 """Ensure that the given magic_kind is valid.
143 """Ensure that the given magic_kind is valid.
144
144
145 Check that the given magic_kind is one of the accepted spec types (stored
145 Check that the given magic_kind is one of the accepted spec types (stored
146 in the global `magic_spec`), raise ValueError otherwise.
146 in the global `magic_spec`), raise ValueError otherwise.
147 """
147 """
148 if magic_kind not in magic_spec:
148 if magic_kind not in magic_spec:
149 raise ValueError('magic_kind must be one of %s, %s given' %
149 raise ValueError('magic_kind must be one of %s, %s given' %
150 magic_kinds, magic_kind)
150 magic_kinds, magic_kind)
151
151
152
152
153 # The docstrings for the decorator below will be fairly similar for the two
153 # The docstrings for the decorator below will be fairly similar for the two
154 # types (method and function), so we generate them here once and reuse the
154 # types (method and function), so we generate them here once and reuse the
155 # templates below.
155 # templates below.
156 _docstring_template = \
156 _docstring_template = \
157 """Decorate the given {0} as {1} magic.
157 """Decorate the given {0} as {1} magic.
158
158
159 The decorator can be used with or without arguments, as follows.
159 The decorator can be used with or without arguments, as follows.
160
160
161 i) without arguments: it will create a {1} magic named as the {0} being
161 i) without arguments: it will create a {1} magic named as the {0} being
162 decorated::
162 decorated::
163
163
164 @deco
164 @deco
165 def foo(...)
165 def foo(...)
166
166
167 will create a {1} magic named `foo`.
167 will create a {1} magic named `foo`.
168
168
169 ii) with one string argument: which will be used as the actual name of the
169 ii) with one string argument: which will be used as the actual name of the
170 resulting magic::
170 resulting magic::
171
171
172 @deco('bar')
172 @deco('bar')
173 def foo(...)
173 def foo(...)
174
174
175 will create a {1} magic named `bar`.
175 will create a {1} magic named `bar`.
176 """
176 """
177
177
178 # These two are decorator factories. While they are conceptually very similar,
178 # These two are decorator factories. While they are conceptually very similar,
179 # there are enough differences in the details that it's simpler to have them
179 # there are enough differences in the details that it's simpler to have them
180 # written as completely standalone functions rather than trying to share code
180 # written as completely standalone functions rather than trying to share code
181 # and make a single one with convoluted logic.
181 # and make a single one with convoluted logic.
182
182
183 def _method_magic_marker(magic_kind):
183 def _method_magic_marker(magic_kind):
184 """Decorator factory for methods in Magics subclasses.
184 """Decorator factory for methods in Magics subclasses.
185 """
185 """
186
186
187 validate_type(magic_kind)
187 validate_type(magic_kind)
188
188
189 # This is a closure to capture the magic_kind. We could also use a class,
189 # This is a closure to capture the magic_kind. We could also use a class,
190 # but it's overkill for just that one bit of state.
190 # but it's overkill for just that one bit of state.
191 def magic_deco(arg):
191 def magic_deco(arg):
192 call = lambda f, *a, **k: f(*a, **k)
192 call = lambda f, *a, **k: f(*a, **k)
193
193
194 if callable(arg):
194 if callable(arg):
195 # "Naked" decorator call (just @foo, no args)
195 # "Naked" decorator call (just @foo, no args)
196 func = arg
196 func = arg
197 name = func.func_name
197 name = func.func_name
198 retval = decorator(call, func)
198 retval = decorator(call, func)
199 record_magic(magics, magic_kind, name, name)
199 record_magic(magics, magic_kind, name, name)
200 elif isinstance(arg, basestring):
200 elif isinstance(arg, basestring):
201 # Decorator called with arguments (@foo('bar'))
201 # Decorator called with arguments (@foo('bar'))
202 name = arg
202 name = arg
203 def mark(func, *a, **kw):
203 def mark(func, *a, **kw):
204 record_magic(magics, magic_kind, name, func.func_name)
204 record_magic(magics, magic_kind, name, func.func_name)
205 return decorator(call, func)
205 return decorator(call, func)
206 retval = mark
206 retval = mark
207 else:
207 else:
208 raise TypeError("Decorator can only be called with "
208 raise TypeError("Decorator can only be called with "
209 "string or function")
209 "string or function")
210 return retval
210 return retval
211
211
212 # Ensure the resulting decorator has a usable docstring
212 # Ensure the resulting decorator has a usable docstring
213 magic_deco.__doc__ = _docstring_template.format('method', magic_kind)
213 magic_deco.__doc__ = _docstring_template.format('method', magic_kind)
214 return magic_deco
214 return magic_deco
215
215
216
216
217 def _function_magic_marker(magic_kind):
217 def _function_magic_marker(magic_kind):
218 """Decorator factory for standalone functions.
218 """Decorator factory for standalone functions.
219 """
219 """
220 validate_type(magic_kind)
220 validate_type(magic_kind)
221
221
222 # This is a closure to capture the magic_kind. We could also use a class,
222 # This is a closure to capture the magic_kind. We could also use a class,
223 # but it's overkill for just that one bit of state.
223 # but it's overkill for just that one bit of state.
224 def magic_deco(arg):
224 def magic_deco(arg):
225 call = lambda f, *a, **k: f(*a, **k)
225 call = lambda f, *a, **k: f(*a, **k)
226
226
227 # Find get_ipython() in the caller's namespace
227 # Find get_ipython() in the caller's namespace
228 caller = sys._getframe(1)
228 caller = sys._getframe(1)
229 for ns in ['f_locals', 'f_globals', 'f_builtins']:
229 for ns in ['f_locals', 'f_globals', 'f_builtins']:
230 get_ipython = getattr(caller, ns).get('get_ipython')
230 get_ipython = getattr(caller, ns).get('get_ipython')
231 if get_ipython is not None:
231 if get_ipython is not None:
232 break
232 break
233 else:
233 else:
234 raise NameError('Decorator can only run in context where '
234 raise NameError('Decorator can only run in context where '
235 '`get_ipython` exists')
235 '`get_ipython` exists')
236
236
237 ip = get_ipython()
237 ip = get_ipython()
238
238
239 if callable(arg):
239 if callable(arg):
240 # "Naked" decorator call (just @foo, no args)
240 # "Naked" decorator call (just @foo, no args)
241 func = arg
241 func = arg
242 name = func.func_name
242 name = func.func_name
243 ip.register_magic_function(func, magic_kind, name)
243 ip.register_magic_function(func, magic_kind, name)
244 retval = decorator(call, func)
244 retval = decorator(call, func)
245 elif isinstance(arg, basestring):
245 elif isinstance(arg, basestring):
246 # Decorator called with arguments (@foo('bar'))
246 # Decorator called with arguments (@foo('bar'))
247 name = arg
247 name = arg
248 def mark(func, *a, **kw):
248 def mark(func, *a, **kw):
249 ip.register_magic_function(func, magic_kind, name)
249 ip.register_magic_function(func, magic_kind, name)
250 return decorator(call, func)
250 return decorator(call, func)
251 retval = mark
251 retval = mark
252 else:
252 else:
253 raise TypeError("Decorator can only be called with "
253 raise TypeError("Decorator can only be called with "
254 "string or function")
254 "string or function")
255 return retval
255 return retval
256
256
257 # Ensure the resulting decorator has a usable docstring
257 # Ensure the resulting decorator has a usable docstring
258 ds = _docstring_template.format('function', magic_kind)
258 ds = _docstring_template.format('function', magic_kind)
259
259
260 ds += dedent("""
260 ds += dedent("""
261 Note: this decorator can only be used in a context where IPython is already
261 Note: this decorator can only be used in a context where IPython is already
262 active, so that the `get_ipython()` call succeeds. You can therefore use
262 active, so that the `get_ipython()` call succeeds. You can therefore use
263 it in your startup files loaded after IPython initializes, but *not* in the
263 it in your startup files loaded after IPython initializes, but *not* in the
264 IPython configuration file itself, which is executed before IPython is
264 IPython configuration file itself, which is executed before IPython is
265 fully up and running. Any file located in the `startup` subdirectory of
265 fully up and running. Any file located in the `startup` subdirectory of
266 your configuration profile will be OK in this sense.
266 your configuration profile will be OK in this sense.
267 """)
267 """)
268
268
269 magic_deco.__doc__ = ds
269 magic_deco.__doc__ = ds
270 return magic_deco
270 return magic_deco
271
271
272
272
273 # Create the actual decorators for public use
273 # Create the actual decorators for public use
274
274
275 # These three are used to decorate methods in class definitions
275 # These three are used to decorate methods in class definitions
276 line_magic = _method_magic_marker('line')
276 line_magic = _method_magic_marker('line')
277 cell_magic = _method_magic_marker('cell')
277 cell_magic = _method_magic_marker('cell')
278 line_cell_magic = _method_magic_marker('line_cell')
278 line_cell_magic = _method_magic_marker('line_cell')
279
279
280 # These three decorate standalone functions and perform the decoration
280 # These three decorate standalone functions and perform the decoration
281 # immediately. They can only run where get_ipython() works
281 # immediately. They can only run where get_ipython() works
282 register_line_magic = _function_magic_marker('line')
282 register_line_magic = _function_magic_marker('line')
283 register_cell_magic = _function_magic_marker('cell')
283 register_cell_magic = _function_magic_marker('cell')
284 register_line_cell_magic = _function_magic_marker('line_cell')
284 register_line_cell_magic = _function_magic_marker('line_cell')
285
285
286 #-----------------------------------------------------------------------------
286 #-----------------------------------------------------------------------------
287 # Core Magic classes
287 # Core Magic classes
288 #-----------------------------------------------------------------------------
288 #-----------------------------------------------------------------------------
289
289
290 class MagicsManager(Configurable):
290 class MagicsManager(Configurable):
291 """Object that handles all magic-related functionality for IPython.
291 """Object that handles all magic-related functionality for IPython.
292 """
292 """
293 # Non-configurable class attributes
293 # Non-configurable class attributes
294
294
295 # A two-level dict, first keyed by magic type, then by magic function, and
295 # A two-level dict, first keyed by magic type, then by magic function, and
296 # holding the actual callable object as value. This is the dict used for
296 # holding the actual callable object as value. This is the dict used for
297 # magic function dispatch
297 # magic function dispatch
298 magics = Dict
298 magics = Dict
299
299
300 # A registry of the original objects that we've been given holding magics.
300 # A registry of the original objects that we've been given holding magics.
301 registry = Dict
301 registry = Dict
302
302
303 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
303 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
304
304
305 auto_magic = Bool(True, config=True, help=
305 auto_magic = Bool(True, config=True, help=
306 "Automatically call line magics without requiring explicit % prefix")
306 "Automatically call line magics without requiring explicit % prefix")
307
307
308 def _auto_magic_changed(self, name, value):
308 def _auto_magic_changed(self, name, value):
309 self.shell.automagic = value
309 self.shell.automagic = value
310
310
311 _auto_status = [
311 _auto_status = [
312 'Automagic is OFF, % prefix IS needed for line magics.',
312 'Automagic is OFF, % prefix IS needed for line magics.',
313 'Automagic is ON, % prefix IS NOT needed for line magics.']
313 'Automagic is ON, % prefix IS NOT needed for line magics.']
314
314
315 user_magics = Instance('IPython.core.magics.UserMagics')
315 user_magics = Instance('IPython.core.magics.UserMagics')
316
316
317 def __init__(self, shell=None, config=None, user_magics=None, **traits):
317 def __init__(self, shell=None, config=None, user_magics=None, **traits):
318
318
319 super(MagicsManager, self).__init__(shell=shell, config=config,
319 super(MagicsManager, self).__init__(shell=shell, config=config,
320 user_magics=user_magics, **traits)
320 user_magics=user_magics, **traits)
321 self.magics = dict(line={}, cell={})
321 self.magics = dict(line={}, cell={})
322 # Let's add the user_magics to the registry for uniformity, so *all*
322 # Let's add the user_magics to the registry for uniformity, so *all*
323 # registered magic containers can be found there.
323 # registered magic containers can be found there.
324 self.registry[user_magics.__class__.__name__] = user_magics
324 self.registry[user_magics.__class__.__name__] = user_magics
325
325
326 def auto_status(self):
326 def auto_status(self):
327 """Return descriptive string with automagic status."""
327 """Return descriptive string with automagic status."""
328 return self._auto_status[self.auto_magic]
328 return self._auto_status[self.auto_magic]
329
329
330 def lsmagic_info(self):
331 """Return the magics as a list of dicts"""
332 magic_list = []
333 for m_type in self.magics:
334 for m_name,mgc in self.magics[m_type].items():
335 try:
336 magic_list.append({'name':m_name,'type':m_type,'class':mgc.im_class.__name__})
337 except AttributeError:
338 magic_list.append({'name':m_name,'type':m_type,'class':'Other'})
339 return magic_list
340
341 def lsmagic_json(self):
342 """Wrap lsmagic_info() in a JSON object"""
343 from IPython.display import JSON
344 return JSON(json.dumps(self.lsmagic_info()))
345
346 def lsmagic(self):
330 def lsmagic(self):
347 """Return a dict of currently available magic functions.
331 """Return a dict of currently available magic functions.
348
332
349 The return dict has the keys 'line' and 'cell', corresponding to the
333 The return dict has the keys 'line' and 'cell', corresponding to the
350 two types of magics we support. Each value is a list of names.
334 two types of magics we support. Each value is a list of names.
351 """
335 """
352 return self.magics
336 return self.magics
353
337
354 def lsmagic_docs(self, brief=False, missing=''):
338 def lsmagic_docs(self, brief=False, missing=''):
355 """Return dict of documentation of magic functions.
339 """Return dict of documentation of magic functions.
356
340
357 The return dict has the keys 'line' and 'cell', corresponding to the
341 The return dict has the keys 'line' and 'cell', corresponding to the
358 two types of magics we support. Each value is a dict keyed by magic
342 two types of magics we support. Each value is a dict keyed by magic
359 name whose value is the function docstring. If a docstring is
343 name whose value is the function docstring. If a docstring is
360 unavailable, the value of `missing` is used instead.
344 unavailable, the value of `missing` is used instead.
361
345
362 If brief is True, only the first line of each docstring will be returned.
346 If brief is True, only the first line of each docstring will be returned.
363 """
347 """
364 docs = {}
348 docs = {}
365 for m_type in self.magics:
349 for m_type in self.magics:
366 m_docs = {}
350 m_docs = {}
367 for m_name, m_func in self.magics[m_type].iteritems():
351 for m_name, m_func in self.magics[m_type].iteritems():
368 if m_func.__doc__:
352 if m_func.__doc__:
369 if brief:
353 if brief:
370 m_docs[m_name] = m_func.__doc__.split('\n', 1)[0]
354 m_docs[m_name] = m_func.__doc__.split('\n', 1)[0]
371 else:
355 else:
372 m_docs[m_name] = m_func.__doc__.rstrip()
356 m_docs[m_name] = m_func.__doc__.rstrip()
373 else:
357 else:
374 m_docs[m_name] = missing
358 m_docs[m_name] = missing
375 docs[m_type] = m_docs
359 docs[m_type] = m_docs
376 return docs
360 return docs
377
361
378 def register(self, *magic_objects):
362 def register(self, *magic_objects):
379 """Register one or more instances of Magics.
363 """Register one or more instances of Magics.
380
364
381 Take one or more classes or instances of classes that subclass the main
365 Take one or more classes or instances of classes that subclass the main
382 `core.Magic` class, and register them with IPython to use the magic
366 `core.Magic` class, and register them with IPython to use the magic
383 functions they provide. The registration process will then ensure that
367 functions they provide. The registration process will then ensure that
384 any methods that have decorated to provide line and/or cell magics will
368 any methods that have decorated to provide line and/or cell magics will
385 be recognized with the `%x`/`%%x` syntax as a line/cell magic
369 be recognized with the `%x`/`%%x` syntax as a line/cell magic
386 respectively.
370 respectively.
387
371
388 If classes are given, they will be instantiated with the default
372 If classes are given, they will be instantiated with the default
389 constructor. If your classes need a custom constructor, you should
373 constructor. If your classes need a custom constructor, you should
390 instanitate them first and pass the instance.
374 instanitate them first and pass the instance.
391
375
392 The provided arguments can be an arbitrary mix of classes and instances.
376 The provided arguments can be an arbitrary mix of classes and instances.
393
377
394 Parameters
378 Parameters
395 ----------
379 ----------
396 magic_objects : one or more classes or instances
380 magic_objects : one or more classes or instances
397 """
381 """
398 # Start by validating them to ensure they have all had their magic
382 # Start by validating them to ensure they have all had their magic
399 # methods registered at the instance level
383 # methods registered at the instance level
400 for m in magic_objects:
384 for m in magic_objects:
401 if not m.registered:
385 if not m.registered:
402 raise ValueError("Class of magics %r was constructed without "
386 raise ValueError("Class of magics %r was constructed without "
403 "the @register_magics class decorator")
387 "the @register_magics class decorator")
404 if type(m) in (type, MetaHasTraits):
388 if type(m) in (type, MetaHasTraits):
405 # If we're given an uninstantiated class
389 # If we're given an uninstantiated class
406 m = m(shell=self.shell)
390 m = m(shell=self.shell)
407
391
408 # Now that we have an instance, we can register it and update the
392 # Now that we have an instance, we can register it and update the
409 # table of callables
393 # table of callables
410 self.registry[m.__class__.__name__] = m
394 self.registry[m.__class__.__name__] = m
411 for mtype in magic_kinds:
395 for mtype in magic_kinds:
412 self.magics[mtype].update(m.magics[mtype])
396 self.magics[mtype].update(m.magics[mtype])
413
397
414 def register_function(self, func, magic_kind='line', magic_name=None):
398 def register_function(self, func, magic_kind='line', magic_name=None):
415 """Expose a standalone function as magic function for IPython.
399 """Expose a standalone function as magic function for IPython.
416
400
417 This will create an IPython magic (line, cell or both) from a
401 This will create an IPython magic (line, cell or both) from a
418 standalone function. The functions should have the following
402 standalone function. The functions should have the following
419 signatures:
403 signatures:
420
404
421 * For line magics: `def f(line)`
405 * For line magics: `def f(line)`
422 * For cell magics: `def f(line, cell)`
406 * For cell magics: `def f(line, cell)`
423 * For a function that does both: `def f(line, cell=None)`
407 * For a function that does both: `def f(line, cell=None)`
424
408
425 In the latter case, the function will be called with `cell==None` when
409 In the latter case, the function will be called with `cell==None` when
426 invoked as `%f`, and with cell as a string when invoked as `%%f`.
410 invoked as `%f`, and with cell as a string when invoked as `%%f`.
427
411
428 Parameters
412 Parameters
429 ----------
413 ----------
430 func : callable
414 func : callable
431 Function to be registered as a magic.
415 Function to be registered as a magic.
432
416
433 magic_kind : str
417 magic_kind : str
434 Kind of magic, one of 'line', 'cell' or 'line_cell'
418 Kind of magic, one of 'line', 'cell' or 'line_cell'
435
419
436 magic_name : optional str
420 magic_name : optional str
437 If given, the name the magic will have in the IPython namespace. By
421 If given, the name the magic will have in the IPython namespace. By
438 default, the name of the function itself is used.
422 default, the name of the function itself is used.
439 """
423 """
440
424
441 # Create the new method in the user_magics and register it in the
425 # Create the new method in the user_magics and register it in the
442 # global table
426 # global table
443 validate_type(magic_kind)
427 validate_type(magic_kind)
444 magic_name = func.func_name if magic_name is None else magic_name
428 magic_name = func.func_name if magic_name is None else magic_name
445 setattr(self.user_magics, magic_name, func)
429 setattr(self.user_magics, magic_name, func)
446 record_magic(self.magics, magic_kind, magic_name, func)
430 record_magic(self.magics, magic_kind, magic_name, func)
447
431
448 def define_magic(self, name, func):
432 def define_magic(self, name, func):
449 """[Deprecated] Expose own function as magic function for IPython.
433 """[Deprecated] Expose own function as magic function for IPython.
450
434
451 Example::
435 Example::
452
436
453 def foo_impl(self, parameter_s=''):
437 def foo_impl(self, parameter_s=''):
454 'My very own magic!. (Use docstrings, IPython reads them).'
438 'My very own magic!. (Use docstrings, IPython reads them).'
455 print 'Magic function. Passed parameter is between < >:'
439 print 'Magic function. Passed parameter is between < >:'
456 print '<%s>' % parameter_s
440 print '<%s>' % parameter_s
457 print 'The self object is:', self
441 print 'The self object is:', self
458
442
459 ip.define_magic('foo',foo_impl)
443 ip.define_magic('foo',foo_impl)
460 """
444 """
461 meth = types.MethodType(func, self.user_magics)
445 meth = types.MethodType(func, self.user_magics)
462 setattr(self.user_magics, name, meth)
446 setattr(self.user_magics, name, meth)
463 record_magic(self.magics, 'line', name, meth)
447 record_magic(self.magics, 'line', name, meth)
464
448
465 def register_alias(self, alias_name, magic_name, magic_kind='line'):
449 def register_alias(self, alias_name, magic_name, magic_kind='line'):
466 """Register an alias to a magic function.
450 """Register an alias to a magic function.
467
451
468 The alias is an instance of :class:`MagicAlias`, which holds the
452 The alias is an instance of :class:`MagicAlias`, which holds the
469 name and kind of the magic it should call. Binding is done at
453 name and kind of the magic it should call. Binding is done at
470 call time, so if the underlying magic function is changed the alias
454 call time, so if the underlying magic function is changed the alias
471 will call the new function.
455 will call the new function.
472
456
473 Parameters
457 Parameters
474 ----------
458 ----------
475 alias_name : str
459 alias_name : str
476 The name of the magic to be registered.
460 The name of the magic to be registered.
477
461
478 magic_name : str
462 magic_name : str
479 The name of an existing magic.
463 The name of an existing magic.
480
464
481 magic_kind : str
465 magic_kind : str
482 Kind of magic, one of 'line' or 'cell'
466 Kind of magic, one of 'line' or 'cell'
483 """
467 """
484
468
485 # `validate_type` is too permissive, as it allows 'line_cell'
469 # `validate_type` is too permissive, as it allows 'line_cell'
486 # which we do not handle.
470 # which we do not handle.
487 if magic_kind not in magic_kinds:
471 if magic_kind not in magic_kinds:
488 raise ValueError('magic_kind must be one of %s, %s given' %
472 raise ValueError('magic_kind must be one of %s, %s given' %
489 magic_kinds, magic_kind)
473 magic_kinds, magic_kind)
490
474
491 alias = MagicAlias(self.shell, magic_name, magic_kind)
475 alias = MagicAlias(self.shell, magic_name, magic_kind)
492 setattr(self.user_magics, alias_name, alias)
476 setattr(self.user_magics, alias_name, alias)
493 record_magic(self.magics, magic_kind, alias_name, alias)
477 record_magic(self.magics, magic_kind, alias_name, alias)
494
478
495 # Key base class that provides the central functionality for magics.
479 # Key base class that provides the central functionality for magics.
496
480
497 class Magics(object):
481 class Magics(object):
498 """Base class for implementing magic functions.
482 """Base class for implementing magic functions.
499
483
500 Shell functions which can be reached as %function_name. All magic
484 Shell functions which can be reached as %function_name. All magic
501 functions should accept a string, which they can parse for their own
485 functions should accept a string, which they can parse for their own
502 needs. This can make some functions easier to type, eg `%cd ../`
486 needs. This can make some functions easier to type, eg `%cd ../`
503 vs. `%cd("../")`
487 vs. `%cd("../")`
504
488
505 Classes providing magic functions need to subclass this class, and they
489 Classes providing magic functions need to subclass this class, and they
506 MUST:
490 MUST:
507
491
508 - Use the method decorators `@line_magic` and `@cell_magic` to decorate
492 - Use the method decorators `@line_magic` and `@cell_magic` to decorate
509 individual methods as magic functions, AND
493 individual methods as magic functions, AND
510
494
511 - Use the class decorator `@magics_class` to ensure that the magic
495 - Use the class decorator `@magics_class` to ensure that the magic
512 methods are properly registered at the instance level upon instance
496 methods are properly registered at the instance level upon instance
513 initialization.
497 initialization.
514
498
515 See :mod:`magic_functions` for examples of actual implementation classes.
499 See :mod:`magic_functions` for examples of actual implementation classes.
516 """
500 """
517 # Dict holding all command-line options for each magic.
501 # Dict holding all command-line options for each magic.
518 options_table = None
502 options_table = None
519 # Dict for the mapping of magic names to methods, set by class decorator
503 # Dict for the mapping of magic names to methods, set by class decorator
520 magics = None
504 magics = None
521 # Flag to check that the class decorator was properly applied
505 # Flag to check that the class decorator was properly applied
522 registered = False
506 registered = False
523 # Instance of IPython shell
507 # Instance of IPython shell
524 shell = None
508 shell = None
525
509
526 def __init__(self, shell):
510 def __init__(self, shell):
527 if not(self.__class__.registered):
511 if not(self.__class__.registered):
528 raise ValueError('Magics subclass without registration - '
512 raise ValueError('Magics subclass without registration - '
529 'did you forget to apply @magics_class?')
513 'did you forget to apply @magics_class?')
530 self.shell = shell
514 self.shell = shell
531 self.options_table = {}
515 self.options_table = {}
532 # The method decorators are run when the instance doesn't exist yet, so
516 # The method decorators are run when the instance doesn't exist yet, so
533 # they can only record the names of the methods they are supposed to
517 # they can only record the names of the methods they are supposed to
534 # grab. Only now, that the instance exists, can we create the proper
518 # grab. Only now, that the instance exists, can we create the proper
535 # mapping to bound methods. So we read the info off the original names
519 # mapping to bound methods. So we read the info off the original names
536 # table and replace each method name by the actual bound method.
520 # table and replace each method name by the actual bound method.
537 # But we mustn't clobber the *class* mapping, in case of multiple instances.
521 # But we mustn't clobber the *class* mapping, in case of multiple instances.
538 class_magics = self.magics
522 class_magics = self.magics
539 self.magics = {}
523 self.magics = {}
540 for mtype in magic_kinds:
524 for mtype in magic_kinds:
541 tab = self.magics[mtype] = {}
525 tab = self.magics[mtype] = {}
542 cls_tab = class_magics[mtype]
526 cls_tab = class_magics[mtype]
543 for magic_name, meth_name in cls_tab.iteritems():
527 for magic_name, meth_name in cls_tab.iteritems():
544 if isinstance(meth_name, basestring):
528 if isinstance(meth_name, basestring):
545 # it's a method name, grab it
529 # it's a method name, grab it
546 tab[magic_name] = getattr(self, meth_name)
530 tab[magic_name] = getattr(self, meth_name)
547 else:
531 else:
548 # it's the real thing
532 # it's the real thing
549 tab[magic_name] = meth_name
533 tab[magic_name] = meth_name
550
534
551 def arg_err(self,func):
535 def arg_err(self,func):
552 """Print docstring if incorrect arguments were passed"""
536 """Print docstring if incorrect arguments were passed"""
553 print 'Error in arguments:'
537 print 'Error in arguments:'
554 print oinspect.getdoc(func)
538 print oinspect.getdoc(func)
555
539
556 def format_latex(self, strng):
540 def format_latex(self, strng):
557 """Format a string for latex inclusion."""
541 """Format a string for latex inclusion."""
558
542
559 # Characters that need to be escaped for latex:
543 # Characters that need to be escaped for latex:
560 escape_re = re.compile(r'(%|_|\$|#|&)',re.MULTILINE)
544 escape_re = re.compile(r'(%|_|\$|#|&)',re.MULTILINE)
561 # Magic command names as headers:
545 # Magic command names as headers:
562 cmd_name_re = re.compile(r'^(%s.*?):' % ESC_MAGIC,
546 cmd_name_re = re.compile(r'^(%s.*?):' % ESC_MAGIC,
563 re.MULTILINE)
547 re.MULTILINE)
564 # Magic commands
548 # Magic commands
565 cmd_re = re.compile(r'(?P<cmd>%s.+?\b)(?!\}\}:)' % ESC_MAGIC,
549 cmd_re = re.compile(r'(?P<cmd>%s.+?\b)(?!\}\}:)' % ESC_MAGIC,
566 re.MULTILINE)
550 re.MULTILINE)
567 # Paragraph continue
551 # Paragraph continue
568 par_re = re.compile(r'\\$',re.MULTILINE)
552 par_re = re.compile(r'\\$',re.MULTILINE)
569
553
570 # The "\n" symbol
554 # The "\n" symbol
571 newline_re = re.compile(r'\\n')
555 newline_re = re.compile(r'\\n')
572
556
573 # Now build the string for output:
557 # Now build the string for output:
574 #strng = cmd_name_re.sub(r'\n\\texttt{\\textsl{\\large \1}}:',strng)
558 #strng = cmd_name_re.sub(r'\n\\texttt{\\textsl{\\large \1}}:',strng)
575 strng = cmd_name_re.sub(r'\n\\bigskip\n\\texttt{\\textbf{ \1}}:',
559 strng = cmd_name_re.sub(r'\n\\bigskip\n\\texttt{\\textbf{ \1}}:',
576 strng)
560 strng)
577 strng = cmd_re.sub(r'\\texttt{\g<cmd>}',strng)
561 strng = cmd_re.sub(r'\\texttt{\g<cmd>}',strng)
578 strng = par_re.sub(r'\\\\',strng)
562 strng = par_re.sub(r'\\\\',strng)
579 strng = escape_re.sub(r'\\\1',strng)
563 strng = escape_re.sub(r'\\\1',strng)
580 strng = newline_re.sub(r'\\textbackslash{}n',strng)
564 strng = newline_re.sub(r'\\textbackslash{}n',strng)
581 return strng
565 return strng
582
566
583 def parse_options(self, arg_str, opt_str, *long_opts, **kw):
567 def parse_options(self, arg_str, opt_str, *long_opts, **kw):
584 """Parse options passed to an argument string.
568 """Parse options passed to an argument string.
585
569
586 The interface is similar to that of getopt(), but it returns back a
570 The interface is similar to that of getopt(), but it returns back a
587 Struct with the options as keys and the stripped argument string still
571 Struct with the options as keys and the stripped argument string still
588 as a string.
572 as a string.
589
573
590 arg_str is quoted as a true sys.argv vector by using shlex.split.
574 arg_str is quoted as a true sys.argv vector by using shlex.split.
591 This allows us to easily expand variables, glob files, quote
575 This allows us to easily expand variables, glob files, quote
592 arguments, etc.
576 arguments, etc.
593
577
594 Options:
578 Options:
595 -mode: default 'string'. If given as 'list', the argument string is
579 -mode: default 'string'. If given as 'list', the argument string is
596 returned as a list (split on whitespace) instead of a string.
580 returned as a list (split on whitespace) instead of a string.
597
581
598 -list_all: put all option values in lists. Normally only options
582 -list_all: put all option values in lists. Normally only options
599 appearing more than once are put in a list.
583 appearing more than once are put in a list.
600
584
601 -posix (True): whether to split the input line in POSIX mode or not,
585 -posix (True): whether to split the input line in POSIX mode or not,
602 as per the conventions outlined in the shlex module from the
586 as per the conventions outlined in the shlex module from the
603 standard library."""
587 standard library."""
604
588
605 # inject default options at the beginning of the input line
589 # inject default options at the beginning of the input line
606 caller = sys._getframe(1).f_code.co_name
590 caller = sys._getframe(1).f_code.co_name
607 arg_str = '%s %s' % (self.options_table.get(caller,''),arg_str)
591 arg_str = '%s %s' % (self.options_table.get(caller,''),arg_str)
608
592
609 mode = kw.get('mode','string')
593 mode = kw.get('mode','string')
610 if mode not in ['string','list']:
594 if mode not in ['string','list']:
611 raise ValueError('incorrect mode given: %s' % mode)
595 raise ValueError('incorrect mode given: %s' % mode)
612 # Get options
596 # Get options
613 list_all = kw.get('list_all',0)
597 list_all = kw.get('list_all',0)
614 posix = kw.get('posix', os.name == 'posix')
598 posix = kw.get('posix', os.name == 'posix')
615 strict = kw.get('strict', True)
599 strict = kw.get('strict', True)
616
600
617 # Check if we have more than one argument to warrant extra processing:
601 # Check if we have more than one argument to warrant extra processing:
618 odict = {} # Dictionary with options
602 odict = {} # Dictionary with options
619 args = arg_str.split()
603 args = arg_str.split()
620 if len(args) >= 1:
604 if len(args) >= 1:
621 # If the list of inputs only has 0 or 1 thing in it, there's no
605 # If the list of inputs only has 0 or 1 thing in it, there's no
622 # need to look for options
606 # need to look for options
623 argv = arg_split(arg_str, posix, strict)
607 argv = arg_split(arg_str, posix, strict)
624 # Do regular option processing
608 # Do regular option processing
625 try:
609 try:
626 opts,args = getopt(argv, opt_str, long_opts)
610 opts,args = getopt(argv, opt_str, long_opts)
627 except GetoptError as e:
611 except GetoptError as e:
628 raise UsageError('%s ( allowed: "%s" %s)' % (e.msg,opt_str,
612 raise UsageError('%s ( allowed: "%s" %s)' % (e.msg,opt_str,
629 " ".join(long_opts)))
613 " ".join(long_opts)))
630 for o,a in opts:
614 for o,a in opts:
631 if o.startswith('--'):
615 if o.startswith('--'):
632 o = o[2:]
616 o = o[2:]
633 else:
617 else:
634 o = o[1:]
618 o = o[1:]
635 try:
619 try:
636 odict[o].append(a)
620 odict[o].append(a)
637 except AttributeError:
621 except AttributeError:
638 odict[o] = [odict[o],a]
622 odict[o] = [odict[o],a]
639 except KeyError:
623 except KeyError:
640 if list_all:
624 if list_all:
641 odict[o] = [a]
625 odict[o] = [a]
642 else:
626 else:
643 odict[o] = a
627 odict[o] = a
644
628
645 # Prepare opts,args for return
629 # Prepare opts,args for return
646 opts = Struct(odict)
630 opts = Struct(odict)
647 if mode == 'string':
631 if mode == 'string':
648 args = ' '.join(args)
632 args = ' '.join(args)
649
633
650 return opts,args
634 return opts,args
651
635
652 def default_option(self, fn, optstr):
636 def default_option(self, fn, optstr):
653 """Make an entry in the options_table for fn, with value optstr"""
637 """Make an entry in the options_table for fn, with value optstr"""
654
638
655 if fn not in self.lsmagic():
639 if fn not in self.lsmagic():
656 error("%s is not a magic function" % fn)
640 error("%s is not a magic function" % fn)
657 self.options_table[fn] = optstr
641 self.options_table[fn] = optstr
658
642
659 class MagicAlias(object):
643 class MagicAlias(object):
660 """An alias to another magic function.
644 """An alias to another magic function.
661
645
662 An alias is determined by its magic name and magic kind. Lookup
646 An alias is determined by its magic name and magic kind. Lookup
663 is done at call time, so if the underlying magic changes the alias
647 is done at call time, so if the underlying magic changes the alias
664 will call the new function.
648 will call the new function.
665
649
666 Use the :meth:`MagicsManager.register_alias` method or the
650 Use the :meth:`MagicsManager.register_alias` method or the
667 `%alias_magic` magic function to create and register a new alias.
651 `%alias_magic` magic function to create and register a new alias.
668 """
652 """
669 def __init__(self, shell, magic_name, magic_kind):
653 def __init__(self, shell, magic_name, magic_kind):
670 self.shell = shell
654 self.shell = shell
671 self.magic_name = magic_name
655 self.magic_name = magic_name
672 self.magic_kind = magic_kind
656 self.magic_kind = magic_kind
673
657
674 self.pretty_target = '%s%s' % (magic_escapes[self.magic_kind], self.magic_name)
658 self.pretty_target = '%s%s' % (magic_escapes[self.magic_kind], self.magic_name)
675 self.__doc__ = "Alias for `%s`." % self.pretty_target
659 self.__doc__ = "Alias for `%s`." % self.pretty_target
676
660
677 self._in_call = False
661 self._in_call = False
678
662
679 def __call__(self, *args, **kwargs):
663 def __call__(self, *args, **kwargs):
680 """Call the magic alias."""
664 """Call the magic alias."""
681 fn = self.shell.find_magic(self.magic_name, self.magic_kind)
665 fn = self.shell.find_magic(self.magic_name, self.magic_kind)
682 if fn is None:
666 if fn is None:
683 raise UsageError("Magic `%s` not found." % self.pretty_target)
667 raise UsageError("Magic `%s` not found." % self.pretty_target)
684
668
685 # Protect against infinite recursion.
669 # Protect against infinite recursion.
686 if self._in_call:
670 if self._in_call:
687 raise UsageError("Infinite recursion detected; "
671 raise UsageError("Infinite recursion detected; "
688 "magic aliases cannot call themselves.")
672 "magic aliases cannot call themselves.")
689 self._in_call = True
673 self._in_call = True
690 try:
674 try:
691 return fn(*args, **kwargs)
675 return fn(*args, **kwargs)
692 finally:
676 finally:
693 self._in_call = False
677 self._in_call = False
@@ -1,612 +1,648 b''
1 """Implementation of basic magic functions.
1 """Implementation of basic magic functions.
2 """
2 """
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Copyright (c) 2012 The IPython Development Team.
4 # Copyright (c) 2012 The IPython Development Team.
5 #
5 #
6 # Distributed under the terms of the Modified BSD License.
6 # Distributed under the terms of the Modified BSD License.
7 #
7 #
8 # The full license is in the file COPYING.txt, distributed with this software.
8 # The full license is in the file COPYING.txt, distributed with this software.
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Imports
12 # Imports
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 from __future__ import print_function
14 from __future__ import print_function
15
15
16 # Stdlib
16 # Stdlib
17 import io
17 import io
18 import json
18 import sys
19 import sys
19 from pprint import pformat
20 from pprint import pformat
20
21
21 # Our own packages
22 # Our own packages
22 from IPython.core import magic_arguments
23 from IPython.core import magic_arguments
23 from IPython.core.error import UsageError
24 from IPython.core.error import UsageError
24 from IPython.core.magic import Magics, magics_class, line_magic, magic_escapes
25 from IPython.core.magic import Magics, magics_class, line_magic, magic_escapes
25 from IPython.utils.text import format_screen, dedent, indent
26 from IPython.utils.text import format_screen, dedent, indent
26 from IPython.core import magic_arguments, page
27 from IPython.core import magic_arguments, page
27 from IPython.testing.skipdoctest import skip_doctest
28 from IPython.testing.skipdoctest import skip_doctest
28 from IPython.utils.ipstruct import Struct
29 from IPython.utils.ipstruct import Struct
29 from IPython.utils.path import unquote_filename
30 from IPython.utils.path import unquote_filename
30 from IPython.utils.warn import warn, error
31 from IPython.utils.warn import warn, error
31
32
32 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
33 # Magics class implementation
34 # Magics class implementation
34 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
35
36
37 class MagicsDisplay(object):
38 def __init__(self, magics_manager):
39 self.magics_manager = magics_manager
40
41 def _lsmagic(self):
42 """The main implementation of the %lsmagic"""
43 mesc = magic_escapes['line']
44 cesc = magic_escapes['cell']
45 mman = self.magics_manager
46 magics = mman.lsmagic()
47 out = ['Available line magics:',
48 mesc + (' '+mesc).join(sorted(magics['line'])),
49 '',
50 'Available cell magics:',
51 cesc + (' '+cesc).join(sorted(magics['cell'])),
52 '',
53 mman.auto_status()]
54 return '\n'.join(out)
55
56 def _repr_pretty_(self, p, cycle):
57 p.text(self._lsmagic())
58
59 def __str__(self):
60 return self._lsmagic()
61
62 def _jsonable(self):
63 """turn magics dict into jsonable dict of the same structure
64
65 replaces object instances with their class names as strings
66 """
67 magic_dict = {}
68 mman = self.magics_manager
69 magics = mman.lsmagic()
70 for key, subdict in magics.items():
71 d = {}
72 magic_dict[key] = d
73 for name, obj in subdict.items():
74 try:
75 classname = obj.im_class.__name__
76 except AttributeError:
77 classname = 'Other'
78
79 d[name] = classname
80 return magic_dict
81
82 def _repr_json_(self):
83 return json.dumps(self._jsonable())
84
85
36 @magics_class
86 @magics_class
37 class BasicMagics(Magics):
87 class BasicMagics(Magics):
38 """Magics that provide central IPython functionality.
88 """Magics that provide central IPython functionality.
39
89
40 These are various magics that don't fit into specific categories but that
90 These are various magics that don't fit into specific categories but that
41 are all part of the base 'IPython experience'."""
91 are all part of the base 'IPython experience'."""
42
92
43 @magic_arguments.magic_arguments()
93 @magic_arguments.magic_arguments()
44 @magic_arguments.argument(
94 @magic_arguments.argument(
45 '-l', '--line', action='store_true',
95 '-l', '--line', action='store_true',
46 help="""Create a line magic alias."""
96 help="""Create a line magic alias."""
47 )
97 )
48 @magic_arguments.argument(
98 @magic_arguments.argument(
49 '-c', '--cell', action='store_true',
99 '-c', '--cell', action='store_true',
50 help="""Create a cell magic alias."""
100 help="""Create a cell magic alias."""
51 )
101 )
52 @magic_arguments.argument(
102 @magic_arguments.argument(
53 'name',
103 'name',
54 help="""Name of the magic to be created."""
104 help="""Name of the magic to be created."""
55 )
105 )
56 @magic_arguments.argument(
106 @magic_arguments.argument(
57 'target',
107 'target',
58 help="""Name of the existing line or cell magic."""
108 help="""Name of the existing line or cell magic."""
59 )
109 )
60 @line_magic
110 @line_magic
61 def alias_magic(self, line=''):
111 def alias_magic(self, line=''):
62 """Create an alias for an existing line or cell magic.
112 """Create an alias for an existing line or cell magic.
63
113
64 Examples
114 Examples
65 --------
115 --------
66 ::
116 ::
67 In [1]: %alias_magic t timeit
117 In [1]: %alias_magic t timeit
68 Created `%t` as an alias for `%timeit`.
118 Created `%t` as an alias for `%timeit`.
69 Created `%%t` as an alias for `%%timeit`.
119 Created `%%t` as an alias for `%%timeit`.
70
120
71 In [2]: %t -n1 pass
121 In [2]: %t -n1 pass
72 1 loops, best of 3: 954 ns per loop
122 1 loops, best of 3: 954 ns per loop
73
123
74 In [3]: %%t -n1
124 In [3]: %%t -n1
75 ...: pass
125 ...: pass
76 ...:
126 ...:
77 1 loops, best of 3: 954 ns per loop
127 1 loops, best of 3: 954 ns per loop
78
128
79 In [4]: %alias_magic --cell whereami pwd
129 In [4]: %alias_magic --cell whereami pwd
80 UsageError: Cell magic function `%%pwd` not found.
130 UsageError: Cell magic function `%%pwd` not found.
81 In [5]: %alias_magic --line whereami pwd
131 In [5]: %alias_magic --line whereami pwd
82 Created `%whereami` as an alias for `%pwd`.
132 Created `%whereami` as an alias for `%pwd`.
83
133
84 In [6]: %whereami
134 In [6]: %whereami
85 Out[6]: u'/home/testuser'
135 Out[6]: u'/home/testuser'
86 """
136 """
87 args = magic_arguments.parse_argstring(self.alias_magic, line)
137 args = magic_arguments.parse_argstring(self.alias_magic, line)
88 shell = self.shell
138 shell = self.shell
89 mman = self.shell.magics_manager
139 mman = self.shell.magics_manager
90 escs = ''.join(magic_escapes.values())
140 escs = ''.join(magic_escapes.values())
91
141
92 target = args.target.lstrip(escs)
142 target = args.target.lstrip(escs)
93 name = args.name.lstrip(escs)
143 name = args.name.lstrip(escs)
94
144
95 # Find the requested magics.
145 # Find the requested magics.
96 m_line = shell.find_magic(target, 'line')
146 m_line = shell.find_magic(target, 'line')
97 m_cell = shell.find_magic(target, 'cell')
147 m_cell = shell.find_magic(target, 'cell')
98 if args.line and m_line is None:
148 if args.line and m_line is None:
99 raise UsageError('Line magic function `%s%s` not found.' %
149 raise UsageError('Line magic function `%s%s` not found.' %
100 (magic_escapes['line'], target))
150 (magic_escapes['line'], target))
101 if args.cell and m_cell is None:
151 if args.cell and m_cell is None:
102 raise UsageError('Cell magic function `%s%s` not found.' %
152 raise UsageError('Cell magic function `%s%s` not found.' %
103 (magic_escapes['cell'], target))
153 (magic_escapes['cell'], target))
104
154
105 # If --line and --cell are not specified, default to the ones
155 # If --line and --cell are not specified, default to the ones
106 # that are available.
156 # that are available.
107 if not args.line and not args.cell:
157 if not args.line and not args.cell:
108 if not m_line and not m_cell:
158 if not m_line and not m_cell:
109 raise UsageError(
159 raise UsageError(
110 'No line or cell magic with name `%s` found.' % target
160 'No line or cell magic with name `%s` found.' % target
111 )
161 )
112 args.line = bool(m_line)
162 args.line = bool(m_line)
113 args.cell = bool(m_cell)
163 args.cell = bool(m_cell)
114
164
115 if args.line:
165 if args.line:
116 mman.register_alias(name, target, 'line')
166 mman.register_alias(name, target, 'line')
117 print('Created `%s%s` as an alias for `%s%s`.' % (
167 print('Created `%s%s` as an alias for `%s%s`.' % (
118 magic_escapes['line'], name,
168 magic_escapes['line'], name,
119 magic_escapes['line'], target))
169 magic_escapes['line'], target))
120
170
121 if args.cell:
171 if args.cell:
122 mman.register_alias(name, target, 'cell')
172 mman.register_alias(name, target, 'cell')
123 print('Created `%s%s` as an alias for `%s%s`.' % (
173 print('Created `%s%s` as an alias for `%s%s`.' % (
124 magic_escapes['cell'], name,
174 magic_escapes['cell'], name,
125 magic_escapes['cell'], target))
175 magic_escapes['cell'], target))
126
176
127 def _lsmagic(self):
128 mesc = magic_escapes['line']
129 cesc = magic_escapes['cell']
130 mman = self.shell.magics_manager
131 magics = mman.lsmagic()
132 out = ['Available line magics:',
133 mesc + (' '+mesc).join(sorted(magics['line'])),
134 '',
135 'Available cell magics:',
136 cesc + (' '+cesc).join(sorted(magics['cell'])),
137 '',
138 mman.auto_status()]
139 return '\n'.join(out)
140
141 @line_magic
177 @line_magic
142 def lsmagic(self, parameter_s=''):
178 def lsmagic(self, parameter_s=''):
143 """List currently available magic functions."""
179 """List currently available magic functions."""
144 print(self._lsmagic())
180 return MagicsDisplay(self.shell.magics_manager)
145
181
146 def _magic_docs(self, brief=False, rest=False):
182 def _magic_docs(self, brief=False, rest=False):
147 """Return docstrings from magic functions."""
183 """Return docstrings from magic functions."""
148 mman = self.shell.magics_manager
184 mman = self.shell.magics_manager
149 docs = mman.lsmagic_docs(brief, missing='No documentation')
185 docs = mman.lsmagic_docs(brief, missing='No documentation')
150
186
151 if rest:
187 if rest:
152 format_string = '**%s%s**::\n\n%s\n\n'
188 format_string = '**%s%s**::\n\n%s\n\n'
153 else:
189 else:
154 format_string = '%s%s:\n%s\n'
190 format_string = '%s%s:\n%s\n'
155
191
156 return ''.join(
192 return ''.join(
157 [format_string % (magic_escapes['line'], fname,
193 [format_string % (magic_escapes['line'], fname,
158 indent(dedent(fndoc)))
194 indent(dedent(fndoc)))
159 for fname, fndoc in sorted(docs['line'].items())]
195 for fname, fndoc in sorted(docs['line'].items())]
160 +
196 +
161 [format_string % (magic_escapes['cell'], fname,
197 [format_string % (magic_escapes['cell'], fname,
162 indent(dedent(fndoc)))
198 indent(dedent(fndoc)))
163 for fname, fndoc in sorted(docs['cell'].items())]
199 for fname, fndoc in sorted(docs['cell'].items())]
164 )
200 )
165
201
166 @line_magic
202 @line_magic
167 def magic(self, parameter_s=''):
203 def magic(self, parameter_s=''):
168 """Print information about the magic function system.
204 """Print information about the magic function system.
169
205
170 Supported formats: -latex, -brief, -rest
206 Supported formats: -latex, -brief, -rest
171 """
207 """
172
208
173 mode = ''
209 mode = ''
174 try:
210 try:
175 mode = parameter_s.split()[0][1:]
211 mode = parameter_s.split()[0][1:]
176 if mode == 'rest':
212 if mode == 'rest':
177 rest_docs = []
213 rest_docs = []
178 except IndexError:
214 except IndexError:
179 pass
215 pass
180
216
181 brief = (mode == 'brief')
217 brief = (mode == 'brief')
182 rest = (mode == 'rest')
218 rest = (mode == 'rest')
183 magic_docs = self._magic_docs(brief, rest)
219 magic_docs = self._magic_docs(brief, rest)
184
220
185 if mode == 'latex':
221 if mode == 'latex':
186 print(self.format_latex(magic_docs))
222 print(self.format_latex(magic_docs))
187 return
223 return
188 else:
224 else:
189 magic_docs = format_screen(magic_docs)
225 magic_docs = format_screen(magic_docs)
190
226
191 out = ["""
227 out = ["""
192 IPython's 'magic' functions
228 IPython's 'magic' functions
193 ===========================
229 ===========================
194
230
195 The magic function system provides a series of functions which allow you to
231 The magic function system provides a series of functions which allow you to
196 control the behavior of IPython itself, plus a lot of system-type
232 control the behavior of IPython itself, plus a lot of system-type
197 features. There are two kinds of magics, line-oriented and cell-oriented.
233 features. There are two kinds of magics, line-oriented and cell-oriented.
198
234
199 Line magics are prefixed with the % character and work much like OS
235 Line magics are prefixed with the % character and work much like OS
200 command-line calls: they get as an argument the rest of the line, where
236 command-line calls: they get as an argument the rest of the line, where
201 arguments are passed without parentheses or quotes. For example, this will
237 arguments are passed without parentheses or quotes. For example, this will
202 time the given statement::
238 time the given statement::
203
239
204 %timeit range(1000)
240 %timeit range(1000)
205
241
206 Cell magics are prefixed with a double %%, and they are functions that get as
242 Cell magics are prefixed with a double %%, and they are functions that get as
207 an argument not only the rest of the line, but also the lines below it in a
243 an argument not only the rest of the line, but also the lines below it in a
208 separate argument. These magics are called with two arguments: the rest of the
244 separate argument. These magics are called with two arguments: the rest of the
209 call line and the body of the cell, consisting of the lines below the first.
245 call line and the body of the cell, consisting of the lines below the first.
210 For example::
246 For example::
211
247
212 %%timeit x = numpy.random.randn((100, 100))
248 %%timeit x = numpy.random.randn((100, 100))
213 numpy.linalg.svd(x)
249 numpy.linalg.svd(x)
214
250
215 will time the execution of the numpy svd routine, running the assignment of x
251 will time the execution of the numpy svd routine, running the assignment of x
216 as part of the setup phase, which is not timed.
252 as part of the setup phase, which is not timed.
217
253
218 In a line-oriented client (the terminal or Qt console IPython), starting a new
254 In a line-oriented client (the terminal or Qt console IPython), starting a new
219 input with %% will automatically enter cell mode, and IPython will continue
255 input with %% will automatically enter cell mode, and IPython will continue
220 reading input until a blank line is given. In the notebook, simply type the
256 reading input until a blank line is given. In the notebook, simply type the
221 whole cell as one entity, but keep in mind that the %% escape can only be at
257 whole cell as one entity, but keep in mind that the %% escape can only be at
222 the very start of the cell.
258 the very start of the cell.
223
259
224 NOTE: If you have 'automagic' enabled (via the command line option or with the
260 NOTE: If you have 'automagic' enabled (via the command line option or with the
225 %automagic function), you don't need to type in the % explicitly for line
261 %automagic function), you don't need to type in the % explicitly for line
226 magics; cell magics always require an explicit '%%' escape. By default,
262 magics; cell magics always require an explicit '%%' escape. By default,
227 IPython ships with automagic on, so you should only rarely need the % escape.
263 IPython ships with automagic on, so you should only rarely need the % escape.
228
264
229 Example: typing '%cd mydir' (without the quotes) changes you working directory
265 Example: typing '%cd mydir' (without the quotes) changes you working directory
230 to 'mydir', if it exists.
266 to 'mydir', if it exists.
231
267
232 For a list of the available magic functions, use %lsmagic. For a description
268 For a list of the available magic functions, use %lsmagic. For a description
233 of any of them, type %magic_name?, e.g. '%cd?'.
269 of any of them, type %magic_name?, e.g. '%cd?'.
234
270
235 Currently the magic system has the following functions:""",
271 Currently the magic system has the following functions:""",
236 magic_docs,
272 magic_docs,
237 "Summary of magic functions (from %slsmagic):" % magic_escapes['line'],
273 "Summary of magic functions (from %slsmagic):" % magic_escapes['line'],
238 self._lsmagic(),
274 self._lsmagic(),
239 ]
275 ]
240 page.page('\n'.join(out))
276 page.page('\n'.join(out))
241
277
242
278
243 @line_magic
279 @line_magic
244 def page(self, parameter_s=''):
280 def page(self, parameter_s=''):
245 """Pretty print the object and display it through a pager.
281 """Pretty print the object and display it through a pager.
246
282
247 %page [options] OBJECT
283 %page [options] OBJECT
248
284
249 If no object is given, use _ (last output).
285 If no object is given, use _ (last output).
250
286
251 Options:
287 Options:
252
288
253 -r: page str(object), don't pretty-print it."""
289 -r: page str(object), don't pretty-print it."""
254
290
255 # After a function contributed by Olivier Aubert, slightly modified.
291 # After a function contributed by Olivier Aubert, slightly modified.
256
292
257 # Process options/args
293 # Process options/args
258 opts, args = self.parse_options(parameter_s, 'r')
294 opts, args = self.parse_options(parameter_s, 'r')
259 raw = 'r' in opts
295 raw = 'r' in opts
260
296
261 oname = args and args or '_'
297 oname = args and args or '_'
262 info = self.shell._ofind(oname)
298 info = self.shell._ofind(oname)
263 if info['found']:
299 if info['found']:
264 txt = (raw and str or pformat)( info['obj'] )
300 txt = (raw and str or pformat)( info['obj'] )
265 page.page(txt)
301 page.page(txt)
266 else:
302 else:
267 print('Object `%s` not found' % oname)
303 print('Object `%s` not found' % oname)
268
304
269 @line_magic
305 @line_magic
270 def profile(self, parameter_s=''):
306 def profile(self, parameter_s=''):
271 """Print your currently active IPython profile."""
307 """Print your currently active IPython profile."""
272 from IPython.core.application import BaseIPythonApplication
308 from IPython.core.application import BaseIPythonApplication
273 if BaseIPythonApplication.initialized():
309 if BaseIPythonApplication.initialized():
274 print(BaseIPythonApplication.instance().profile)
310 print(BaseIPythonApplication.instance().profile)
275 else:
311 else:
276 error("profile is an application-level value, but you don't appear to be in an IPython application")
312 error("profile is an application-level value, but you don't appear to be in an IPython application")
277
313
278 @line_magic
314 @line_magic
279 def pprint(self, parameter_s=''):
315 def pprint(self, parameter_s=''):
280 """Toggle pretty printing on/off."""
316 """Toggle pretty printing on/off."""
281 ptformatter = self.shell.display_formatter.formatters['text/plain']
317 ptformatter = self.shell.display_formatter.formatters['text/plain']
282 ptformatter.pprint = bool(1 - ptformatter.pprint)
318 ptformatter.pprint = bool(1 - ptformatter.pprint)
283 print('Pretty printing has been turned',
319 print('Pretty printing has been turned',
284 ['OFF','ON'][ptformatter.pprint])
320 ['OFF','ON'][ptformatter.pprint])
285
321
286 @line_magic
322 @line_magic
287 def colors(self, parameter_s=''):
323 def colors(self, parameter_s=''):
288 """Switch color scheme for prompts, info system and exception handlers.
324 """Switch color scheme for prompts, info system and exception handlers.
289
325
290 Currently implemented schemes: NoColor, Linux, LightBG.
326 Currently implemented schemes: NoColor, Linux, LightBG.
291
327
292 Color scheme names are not case-sensitive.
328 Color scheme names are not case-sensitive.
293
329
294 Examples
330 Examples
295 --------
331 --------
296 To get a plain black and white terminal::
332 To get a plain black and white terminal::
297
333
298 %colors nocolor
334 %colors nocolor
299 """
335 """
300 def color_switch_err(name):
336 def color_switch_err(name):
301 warn('Error changing %s color schemes.\n%s' %
337 warn('Error changing %s color schemes.\n%s' %
302 (name, sys.exc_info()[1]))
338 (name, sys.exc_info()[1]))
303
339
304
340
305 new_scheme = parameter_s.strip()
341 new_scheme = parameter_s.strip()
306 if not new_scheme:
342 if not new_scheme:
307 raise UsageError(
343 raise UsageError(
308 "%colors: you must specify a color scheme. See '%colors?'")
344 "%colors: you must specify a color scheme. See '%colors?'")
309 return
345 return
310 # local shortcut
346 # local shortcut
311 shell = self.shell
347 shell = self.shell
312
348
313 import IPython.utils.rlineimpl as readline
349 import IPython.utils.rlineimpl as readline
314
350
315 if not shell.colors_force and \
351 if not shell.colors_force and \
316 not readline.have_readline and \
352 not readline.have_readline and \
317 (sys.platform == "win32" or sys.platform == "cli"):
353 (sys.platform == "win32" or sys.platform == "cli"):
318 msg = """\
354 msg = """\
319 Proper color support under MS Windows requires the pyreadline library.
355 Proper color support under MS Windows requires the pyreadline library.
320 You can find it at:
356 You can find it at:
321 http://ipython.org/pyreadline.html
357 http://ipython.org/pyreadline.html
322 Gary's readline needs the ctypes module, from:
358 Gary's readline needs the ctypes module, from:
323 http://starship.python.net/crew/theller/ctypes
359 http://starship.python.net/crew/theller/ctypes
324 (Note that ctypes is already part of Python versions 2.5 and newer).
360 (Note that ctypes is already part of Python versions 2.5 and newer).
325
361
326 Defaulting color scheme to 'NoColor'"""
362 Defaulting color scheme to 'NoColor'"""
327 new_scheme = 'NoColor'
363 new_scheme = 'NoColor'
328 warn(msg)
364 warn(msg)
329
365
330 # readline option is 0
366 # readline option is 0
331 if not shell.colors_force and not shell.has_readline:
367 if not shell.colors_force and not shell.has_readline:
332 new_scheme = 'NoColor'
368 new_scheme = 'NoColor'
333
369
334 # Set prompt colors
370 # Set prompt colors
335 try:
371 try:
336 shell.prompt_manager.color_scheme = new_scheme
372 shell.prompt_manager.color_scheme = new_scheme
337 except:
373 except:
338 color_switch_err('prompt')
374 color_switch_err('prompt')
339 else:
375 else:
340 shell.colors = \
376 shell.colors = \
341 shell.prompt_manager.color_scheme_table.active_scheme_name
377 shell.prompt_manager.color_scheme_table.active_scheme_name
342 # Set exception colors
378 # Set exception colors
343 try:
379 try:
344 shell.InteractiveTB.set_colors(scheme = new_scheme)
380 shell.InteractiveTB.set_colors(scheme = new_scheme)
345 shell.SyntaxTB.set_colors(scheme = new_scheme)
381 shell.SyntaxTB.set_colors(scheme = new_scheme)
346 except:
382 except:
347 color_switch_err('exception')
383 color_switch_err('exception')
348
384
349 # Set info (for 'object?') colors
385 # Set info (for 'object?') colors
350 if shell.color_info:
386 if shell.color_info:
351 try:
387 try:
352 shell.inspector.set_active_scheme(new_scheme)
388 shell.inspector.set_active_scheme(new_scheme)
353 except:
389 except:
354 color_switch_err('object inspector')
390 color_switch_err('object inspector')
355 else:
391 else:
356 shell.inspector.set_active_scheme('NoColor')
392 shell.inspector.set_active_scheme('NoColor')
357
393
358 @line_magic
394 @line_magic
359 def xmode(self, parameter_s=''):
395 def xmode(self, parameter_s=''):
360 """Switch modes for the exception handlers.
396 """Switch modes for the exception handlers.
361
397
362 Valid modes: Plain, Context and Verbose.
398 Valid modes: Plain, Context and Verbose.
363
399
364 If called without arguments, acts as a toggle."""
400 If called without arguments, acts as a toggle."""
365
401
366 def xmode_switch_err(name):
402 def xmode_switch_err(name):
367 warn('Error changing %s exception modes.\n%s' %
403 warn('Error changing %s exception modes.\n%s' %
368 (name,sys.exc_info()[1]))
404 (name,sys.exc_info()[1]))
369
405
370 shell = self.shell
406 shell = self.shell
371 new_mode = parameter_s.strip().capitalize()
407 new_mode = parameter_s.strip().capitalize()
372 try:
408 try:
373 shell.InteractiveTB.set_mode(mode=new_mode)
409 shell.InteractiveTB.set_mode(mode=new_mode)
374 print('Exception reporting mode:',shell.InteractiveTB.mode)
410 print('Exception reporting mode:',shell.InteractiveTB.mode)
375 except:
411 except:
376 xmode_switch_err('user')
412 xmode_switch_err('user')
377
413
378 @line_magic
414 @line_magic
379 def quickref(self,arg):
415 def quickref(self,arg):
380 """ Show a quick reference sheet """
416 """ Show a quick reference sheet """
381 from IPython.core.usage import quick_reference
417 from IPython.core.usage import quick_reference
382 qr = quick_reference + self._magic_docs(brief=True)
418 qr = quick_reference + self._magic_docs(brief=True)
383 page.page(qr)
419 page.page(qr)
384
420
385 @line_magic
421 @line_magic
386 def doctest_mode(self, parameter_s=''):
422 def doctest_mode(self, parameter_s=''):
387 """Toggle doctest mode on and off.
423 """Toggle doctest mode on and off.
388
424
389 This mode is intended to make IPython behave as much as possible like a
425 This mode is intended to make IPython behave as much as possible like a
390 plain Python shell, from the perspective of how its prompts, exceptions
426 plain Python shell, from the perspective of how its prompts, exceptions
391 and output look. This makes it easy to copy and paste parts of a
427 and output look. This makes it easy to copy and paste parts of a
392 session into doctests. It does so by:
428 session into doctests. It does so by:
393
429
394 - Changing the prompts to the classic ``>>>`` ones.
430 - Changing the prompts to the classic ``>>>`` ones.
395 - Changing the exception reporting mode to 'Plain'.
431 - Changing the exception reporting mode to 'Plain'.
396 - Disabling pretty-printing of output.
432 - Disabling pretty-printing of output.
397
433
398 Note that IPython also supports the pasting of code snippets that have
434 Note that IPython also supports the pasting of code snippets that have
399 leading '>>>' and '...' prompts in them. This means that you can paste
435 leading '>>>' and '...' prompts in them. This means that you can paste
400 doctests from files or docstrings (even if they have leading
436 doctests from files or docstrings (even if they have leading
401 whitespace), and the code will execute correctly. You can then use
437 whitespace), and the code will execute correctly. You can then use
402 '%history -t' to see the translated history; this will give you the
438 '%history -t' to see the translated history; this will give you the
403 input after removal of all the leading prompts and whitespace, which
439 input after removal of all the leading prompts and whitespace, which
404 can be pasted back into an editor.
440 can be pasted back into an editor.
405
441
406 With these features, you can switch into this mode easily whenever you
442 With these features, you can switch into this mode easily whenever you
407 need to do testing and changes to doctests, without having to leave
443 need to do testing and changes to doctests, without having to leave
408 your existing IPython session.
444 your existing IPython session.
409 """
445 """
410
446
411 # Shorthands
447 # Shorthands
412 shell = self.shell
448 shell = self.shell
413 pm = shell.prompt_manager
449 pm = shell.prompt_manager
414 meta = shell.meta
450 meta = shell.meta
415 disp_formatter = self.shell.display_formatter
451 disp_formatter = self.shell.display_formatter
416 ptformatter = disp_formatter.formatters['text/plain']
452 ptformatter = disp_formatter.formatters['text/plain']
417 # dstore is a data store kept in the instance metadata bag to track any
453 # dstore is a data store kept in the instance metadata bag to track any
418 # changes we make, so we can undo them later.
454 # changes we make, so we can undo them later.
419 dstore = meta.setdefault('doctest_mode',Struct())
455 dstore = meta.setdefault('doctest_mode',Struct())
420 save_dstore = dstore.setdefault
456 save_dstore = dstore.setdefault
421
457
422 # save a few values we'll need to recover later
458 # save a few values we'll need to recover later
423 mode = save_dstore('mode',False)
459 mode = save_dstore('mode',False)
424 save_dstore('rc_pprint',ptformatter.pprint)
460 save_dstore('rc_pprint',ptformatter.pprint)
425 save_dstore('xmode',shell.InteractiveTB.mode)
461 save_dstore('xmode',shell.InteractiveTB.mode)
426 save_dstore('rc_separate_out',shell.separate_out)
462 save_dstore('rc_separate_out',shell.separate_out)
427 save_dstore('rc_separate_out2',shell.separate_out2)
463 save_dstore('rc_separate_out2',shell.separate_out2)
428 save_dstore('rc_prompts_pad_left',pm.justify)
464 save_dstore('rc_prompts_pad_left',pm.justify)
429 save_dstore('rc_separate_in',shell.separate_in)
465 save_dstore('rc_separate_in',shell.separate_in)
430 save_dstore('rc_active_types',disp_formatter.active_types)
466 save_dstore('rc_active_types',disp_formatter.active_types)
431 save_dstore('prompt_templates',(pm.in_template, pm.in2_template, pm.out_template))
467 save_dstore('prompt_templates',(pm.in_template, pm.in2_template, pm.out_template))
432
468
433 if mode == False:
469 if mode == False:
434 # turn on
470 # turn on
435 pm.in_template = '>>> '
471 pm.in_template = '>>> '
436 pm.in2_template = '... '
472 pm.in2_template = '... '
437 pm.out_template = ''
473 pm.out_template = ''
438
474
439 # Prompt separators like plain python
475 # Prompt separators like plain python
440 shell.separate_in = ''
476 shell.separate_in = ''
441 shell.separate_out = ''
477 shell.separate_out = ''
442 shell.separate_out2 = ''
478 shell.separate_out2 = ''
443
479
444 pm.justify = False
480 pm.justify = False
445
481
446 ptformatter.pprint = False
482 ptformatter.pprint = False
447 disp_formatter.active_types = ['text/plain']
483 disp_formatter.active_types = ['text/plain']
448
484
449 shell.magic('xmode Plain')
485 shell.magic('xmode Plain')
450 else:
486 else:
451 # turn off
487 # turn off
452 pm.in_template, pm.in2_template, pm.out_template = dstore.prompt_templates
488 pm.in_template, pm.in2_template, pm.out_template = dstore.prompt_templates
453
489
454 shell.separate_in = dstore.rc_separate_in
490 shell.separate_in = dstore.rc_separate_in
455
491
456 shell.separate_out = dstore.rc_separate_out
492 shell.separate_out = dstore.rc_separate_out
457 shell.separate_out2 = dstore.rc_separate_out2
493 shell.separate_out2 = dstore.rc_separate_out2
458
494
459 pm.justify = dstore.rc_prompts_pad_left
495 pm.justify = dstore.rc_prompts_pad_left
460
496
461 ptformatter.pprint = dstore.rc_pprint
497 ptformatter.pprint = dstore.rc_pprint
462 disp_formatter.active_types = dstore.rc_active_types
498 disp_formatter.active_types = dstore.rc_active_types
463
499
464 shell.magic('xmode ' + dstore.xmode)
500 shell.magic('xmode ' + dstore.xmode)
465
501
466 # Store new mode and inform
502 # Store new mode and inform
467 dstore.mode = bool(1-int(mode))
503 dstore.mode = bool(1-int(mode))
468 mode_label = ['OFF','ON'][dstore.mode]
504 mode_label = ['OFF','ON'][dstore.mode]
469 print('Doctest mode is:', mode_label)
505 print('Doctest mode is:', mode_label)
470
506
471 @line_magic
507 @line_magic
472 def gui(self, parameter_s=''):
508 def gui(self, parameter_s=''):
473 """Enable or disable IPython GUI event loop integration.
509 """Enable or disable IPython GUI event loop integration.
474
510
475 %gui [GUINAME]
511 %gui [GUINAME]
476
512
477 This magic replaces IPython's threaded shells that were activated
513 This magic replaces IPython's threaded shells that were activated
478 using the (pylab/wthread/etc.) command line flags. GUI toolkits
514 using the (pylab/wthread/etc.) command line flags. GUI toolkits
479 can now be enabled at runtime and keyboard
515 can now be enabled at runtime and keyboard
480 interrupts should work without any problems. The following toolkits
516 interrupts should work without any problems. The following toolkits
481 are supported: wxPython, PyQt4, PyGTK, Tk and Cocoa (OSX)::
517 are supported: wxPython, PyQt4, PyGTK, Tk and Cocoa (OSX)::
482
518
483 %gui wx # enable wxPython event loop integration
519 %gui wx # enable wxPython event loop integration
484 %gui qt4|qt # enable PyQt4 event loop integration
520 %gui qt4|qt # enable PyQt4 event loop integration
485 %gui gtk # enable PyGTK event loop integration
521 %gui gtk # enable PyGTK event loop integration
486 %gui gtk3 # enable Gtk3 event loop integration
522 %gui gtk3 # enable Gtk3 event loop integration
487 %gui tk # enable Tk event loop integration
523 %gui tk # enable Tk event loop integration
488 %gui osx # enable Cocoa event loop integration
524 %gui osx # enable Cocoa event loop integration
489 # (requires %matplotlib 1.1)
525 # (requires %matplotlib 1.1)
490 %gui # disable all event loop integration
526 %gui # disable all event loop integration
491
527
492 WARNING: after any of these has been called you can simply create
528 WARNING: after any of these has been called you can simply create
493 an application object, but DO NOT start the event loop yourself, as
529 an application object, but DO NOT start the event loop yourself, as
494 we have already handled that.
530 we have already handled that.
495 """
531 """
496 opts, arg = self.parse_options(parameter_s, '')
532 opts, arg = self.parse_options(parameter_s, '')
497 if arg=='': arg = None
533 if arg=='': arg = None
498 try:
534 try:
499 return self.shell.enable_gui(arg)
535 return self.shell.enable_gui(arg)
500 except Exception as e:
536 except Exception as e:
501 # print simple error message, rather than traceback if we can't
537 # print simple error message, rather than traceback if we can't
502 # hook up the GUI
538 # hook up the GUI
503 error(str(e))
539 error(str(e))
504
540
505 @skip_doctest
541 @skip_doctest
506 @line_magic
542 @line_magic
507 def precision(self, s=''):
543 def precision(self, s=''):
508 """Set floating point precision for pretty printing.
544 """Set floating point precision for pretty printing.
509
545
510 Can set either integer precision or a format string.
546 Can set either integer precision or a format string.
511
547
512 If numpy has been imported and precision is an int,
548 If numpy has been imported and precision is an int,
513 numpy display precision will also be set, via ``numpy.set_printoptions``.
549 numpy display precision will also be set, via ``numpy.set_printoptions``.
514
550
515 If no argument is given, defaults will be restored.
551 If no argument is given, defaults will be restored.
516
552
517 Examples
553 Examples
518 --------
554 --------
519 ::
555 ::
520
556
521 In [1]: from math import pi
557 In [1]: from math import pi
522
558
523 In [2]: %precision 3
559 In [2]: %precision 3
524 Out[2]: u'%.3f'
560 Out[2]: u'%.3f'
525
561
526 In [3]: pi
562 In [3]: pi
527 Out[3]: 3.142
563 Out[3]: 3.142
528
564
529 In [4]: %precision %i
565 In [4]: %precision %i
530 Out[4]: u'%i'
566 Out[4]: u'%i'
531
567
532 In [5]: pi
568 In [5]: pi
533 Out[5]: 3
569 Out[5]: 3
534
570
535 In [6]: %precision %e
571 In [6]: %precision %e
536 Out[6]: u'%e'
572 Out[6]: u'%e'
537
573
538 In [7]: pi**10
574 In [7]: pi**10
539 Out[7]: 9.364805e+04
575 Out[7]: 9.364805e+04
540
576
541 In [8]: %precision
577 In [8]: %precision
542 Out[8]: u'%r'
578 Out[8]: u'%r'
543
579
544 In [9]: pi**10
580 In [9]: pi**10
545 Out[9]: 93648.047476082982
581 Out[9]: 93648.047476082982
546 """
582 """
547 ptformatter = self.shell.display_formatter.formatters['text/plain']
583 ptformatter = self.shell.display_formatter.formatters['text/plain']
548 ptformatter.float_precision = s
584 ptformatter.float_precision = s
549 return ptformatter.float_format
585 return ptformatter.float_format
550
586
551 @magic_arguments.magic_arguments()
587 @magic_arguments.magic_arguments()
552 @magic_arguments.argument(
588 @magic_arguments.argument(
553 '-e', '--export', action='store_true', default=False,
589 '-e', '--export', action='store_true', default=False,
554 help='Export IPython history as a notebook. The filename argument '
590 help='Export IPython history as a notebook. The filename argument '
555 'is used to specify the notebook name and format. For example '
591 'is used to specify the notebook name and format. For example '
556 'a filename of notebook.ipynb will result in a notebook name '
592 'a filename of notebook.ipynb will result in a notebook name '
557 'of "notebook" and a format of "json". Likewise using a ".py" '
593 'of "notebook" and a format of "json". Likewise using a ".py" '
558 'file extension will write the notebook as a Python script'
594 'file extension will write the notebook as a Python script'
559 )
595 )
560 @magic_arguments.argument(
596 @magic_arguments.argument(
561 '-f', '--format',
597 '-f', '--format',
562 help='Convert an existing IPython notebook to a new format. This option '
598 help='Convert an existing IPython notebook to a new format. This option '
563 'specifies the new format and can have the values: json, py. '
599 'specifies the new format and can have the values: json, py. '
564 'The target filename is chosen automatically based on the new '
600 'The target filename is chosen automatically based on the new '
565 'format. The filename argument gives the name of the source file.'
601 'format. The filename argument gives the name of the source file.'
566 )
602 )
567 @magic_arguments.argument(
603 @magic_arguments.argument(
568 'filename', type=unicode,
604 'filename', type=unicode,
569 help='Notebook name or filename'
605 help='Notebook name or filename'
570 )
606 )
571 @line_magic
607 @line_magic
572 def notebook(self, s):
608 def notebook(self, s):
573 """Export and convert IPython notebooks.
609 """Export and convert IPython notebooks.
574
610
575 This function can export the current IPython history to a notebook file
611 This function can export the current IPython history to a notebook file
576 or can convert an existing notebook file into a different format. For
612 or can convert an existing notebook file into a different format. For
577 example, to export the history to "foo.ipynb" do "%notebook -e foo.ipynb".
613 example, to export the history to "foo.ipynb" do "%notebook -e foo.ipynb".
578 To export the history to "foo.py" do "%notebook -e foo.py". To convert
614 To export the history to "foo.py" do "%notebook -e foo.py". To convert
579 "foo.ipynb" to "foo.json" do "%notebook -f json foo.ipynb". Possible
615 "foo.ipynb" to "foo.json" do "%notebook -f json foo.ipynb". Possible
580 formats include (json/ipynb, py).
616 formats include (json/ipynb, py).
581 """
617 """
582 args = magic_arguments.parse_argstring(self.notebook, s)
618 args = magic_arguments.parse_argstring(self.notebook, s)
583
619
584 from IPython.nbformat import current
620 from IPython.nbformat import current
585 args.filename = unquote_filename(args.filename)
621 args.filename = unquote_filename(args.filename)
586 if args.export:
622 if args.export:
587 fname, name, format = current.parse_filename(args.filename)
623 fname, name, format = current.parse_filename(args.filename)
588 cells = []
624 cells = []
589 hist = list(self.shell.history_manager.get_range())
625 hist = list(self.shell.history_manager.get_range())
590 for session, prompt_number, input in hist[:-1]:
626 for session, prompt_number, input in hist[:-1]:
591 cells.append(current.new_code_cell(prompt_number=prompt_number,
627 cells.append(current.new_code_cell(prompt_number=prompt_number,
592 input=input))
628 input=input))
593 worksheet = current.new_worksheet(cells=cells)
629 worksheet = current.new_worksheet(cells=cells)
594 nb = current.new_notebook(name=name,worksheets=[worksheet])
630 nb = current.new_notebook(name=name,worksheets=[worksheet])
595 with io.open(fname, 'w', encoding='utf-8') as f:
631 with io.open(fname, 'w', encoding='utf-8') as f:
596 current.write(nb, f, format);
632 current.write(nb, f, format);
597 elif args.format is not None:
633 elif args.format is not None:
598 old_fname, old_name, old_format = current.parse_filename(args.filename)
634 old_fname, old_name, old_format = current.parse_filename(args.filename)
599 new_format = args.format
635 new_format = args.format
600 if new_format == u'xml':
636 if new_format == u'xml':
601 raise ValueError('Notebooks cannot be written as xml.')
637 raise ValueError('Notebooks cannot be written as xml.')
602 elif new_format == u'ipynb' or new_format == u'json':
638 elif new_format == u'ipynb' or new_format == u'json':
603 new_fname = old_name + u'.ipynb'
639 new_fname = old_name + u'.ipynb'
604 new_format = u'json'
640 new_format = u'json'
605 elif new_format == u'py':
641 elif new_format == u'py':
606 new_fname = old_name + u'.py'
642 new_fname = old_name + u'.py'
607 else:
643 else:
608 raise ValueError('Invalid notebook format: %s' % new_format)
644 raise ValueError('Invalid notebook format: %s' % new_format)
609 with io.open(old_fname, 'r', encoding='utf-8') as f:
645 with io.open(old_fname, 'r', encoding='utf-8') as f:
610 nb = current.read(f, old_format)
646 nb = current.read(f, old_format)
611 with io.open(new_fname, 'w', encoding='utf-8') as f:
647 with io.open(new_fname, 'w', encoding='utf-8') as f:
612 current.write(nb, f, new_format)
648 current.write(nb, f, new_format)
@@ -1,992 +1,993 b''
1 """The Qt MainWindow for the QtConsole
1 """The Qt MainWindow for the QtConsole
2
2
3 This is a tabbed pseudo-terminal of IPython sessions, with a menu bar for
3 This is a tabbed pseudo-terminal of IPython sessions, with a menu bar for
4 common actions.
4 common actions.
5
5
6 Authors:
6 Authors:
7
7
8 * Evan Patterson
8 * Evan Patterson
9 * Min RK
9 * Min RK
10 * Erik Tollerud
10 * Erik Tollerud
11 * Fernando Perez
11 * Fernando Perez
12 * Bussonnier Matthias
12 * Bussonnier Matthias
13 * Thomas Kluyver
13 * Thomas Kluyver
14 * Paul Ivanov
14 * Paul Ivanov
15
15
16 """
16 """
17
17
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # Imports
19 # Imports
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 # stdlib imports
22 # stdlib imports
23 import json
23 import json
24 import re
24 import re
25 import sys
25 import sys
26 import webbrowser
26 import webbrowser
27 from threading import Thread
27 from threading import Thread
28
28
29 # System library imports
29 # System library imports
30 from IPython.external.qt import QtGui,QtCore
30 from IPython.external.qt import QtGui,QtCore
31
31
32 from IPython.core.magic import magic_escapes
33
32 def background(f):
34 def background(f):
33 """call a function in a simple thread, to prevent blocking"""
35 """call a function in a simple thread, to prevent blocking"""
34 t = Thread(target=f)
36 t = Thread(target=f)
35 t.start()
37 t.start()
36 return t
38 return t
37
39
38 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
39 # Classes
41 # Classes
40 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
41
43
42 class MainWindow(QtGui.QMainWindow):
44 class MainWindow(QtGui.QMainWindow):
43
45
44 #---------------------------------------------------------------------------
46 #---------------------------------------------------------------------------
45 # 'object' interface
47 # 'object' interface
46 #---------------------------------------------------------------------------
48 #---------------------------------------------------------------------------
47
49
48 _magic_menu_dict = {}
50 _magic_menu_dict = {}
49
51
50 def __init__(self, app,
52 def __init__(self, app,
51 confirm_exit=True,
53 confirm_exit=True,
52 new_frontend_factory=None, slave_frontend_factory=None,
54 new_frontend_factory=None, slave_frontend_factory=None,
53 ):
55 ):
54 """ Create a tabbed MainWindow for managing IPython FrontendWidgets
56 """ Create a tabbed MainWindow for managing IPython FrontendWidgets
55
57
56 Parameters
58 Parameters
57 ----------
59 ----------
58
60
59 app : reference to QApplication parent
61 app : reference to QApplication parent
60 confirm_exit : bool, optional
62 confirm_exit : bool, optional
61 Whether we should prompt on close of tabs
63 Whether we should prompt on close of tabs
62 new_frontend_factory : callable
64 new_frontend_factory : callable
63 A callable that returns a new IPythonWidget instance, attached to
65 A callable that returns a new IPythonWidget instance, attached to
64 its own running kernel.
66 its own running kernel.
65 slave_frontend_factory : callable
67 slave_frontend_factory : callable
66 A callable that takes an existing IPythonWidget, and returns a new
68 A callable that takes an existing IPythonWidget, and returns a new
67 IPythonWidget instance, attached to the same kernel.
69 IPythonWidget instance, attached to the same kernel.
68 """
70 """
69
71
70 super(MainWindow, self).__init__()
72 super(MainWindow, self).__init__()
71 self._kernel_counter = 0
73 self._kernel_counter = 0
72 self._app = app
74 self._app = app
73 self.confirm_exit = confirm_exit
75 self.confirm_exit = confirm_exit
74 self.new_frontend_factory = new_frontend_factory
76 self.new_frontend_factory = new_frontend_factory
75 self.slave_frontend_factory = slave_frontend_factory
77 self.slave_frontend_factory = slave_frontend_factory
76
78
77 self.tab_widget = QtGui.QTabWidget(self)
79 self.tab_widget = QtGui.QTabWidget(self)
78 self.tab_widget.setDocumentMode(True)
80 self.tab_widget.setDocumentMode(True)
79 self.tab_widget.setTabsClosable(True)
81 self.tab_widget.setTabsClosable(True)
80 self.tab_widget.tabCloseRequested[int].connect(self.close_tab)
82 self.tab_widget.tabCloseRequested[int].connect(self.close_tab)
81
83
82 self.setCentralWidget(self.tab_widget)
84 self.setCentralWidget(self.tab_widget)
83 # hide tab bar at first, since we have no tabs:
85 # hide tab bar at first, since we have no tabs:
84 self.tab_widget.tabBar().setVisible(False)
86 self.tab_widget.tabBar().setVisible(False)
85 # prevent focus in tab bar
87 # prevent focus in tab bar
86 self.tab_widget.setFocusPolicy(QtCore.Qt.NoFocus)
88 self.tab_widget.setFocusPolicy(QtCore.Qt.NoFocus)
87
89
88 def update_tab_bar_visibility(self):
90 def update_tab_bar_visibility(self):
89 """ update visibility of the tabBar depending of the number of tab
91 """ update visibility of the tabBar depending of the number of tab
90
92
91 0 or 1 tab, tabBar hidden
93 0 or 1 tab, tabBar hidden
92 2+ tabs, tabBar visible
94 2+ tabs, tabBar visible
93
95
94 send a self.close if number of tab ==0
96 send a self.close if number of tab ==0
95
97
96 need to be called explicitly, or be connected to tabInserted/tabRemoved
98 need to be called explicitly, or be connected to tabInserted/tabRemoved
97 """
99 """
98 if self.tab_widget.count() <= 1:
100 if self.tab_widget.count() <= 1:
99 self.tab_widget.tabBar().setVisible(False)
101 self.tab_widget.tabBar().setVisible(False)
100 else:
102 else:
101 self.tab_widget.tabBar().setVisible(True)
103 self.tab_widget.tabBar().setVisible(True)
102 if self.tab_widget.count()==0 :
104 if self.tab_widget.count()==0 :
103 self.close()
105 self.close()
104
106
105 @property
107 @property
106 def next_kernel_id(self):
108 def next_kernel_id(self):
107 """constantly increasing counter for kernel IDs"""
109 """constantly increasing counter for kernel IDs"""
108 c = self._kernel_counter
110 c = self._kernel_counter
109 self._kernel_counter += 1
111 self._kernel_counter += 1
110 return c
112 return c
111
113
112 @property
114 @property
113 def active_frontend(self):
115 def active_frontend(self):
114 return self.tab_widget.currentWidget()
116 return self.tab_widget.currentWidget()
115
117
116 def create_tab_with_new_frontend(self):
118 def create_tab_with_new_frontend(self):
117 """create a new frontend and attach it to a new tab"""
119 """create a new frontend and attach it to a new tab"""
118 widget = self.new_frontend_factory()
120 widget = self.new_frontend_factory()
119 self.add_tab_with_frontend(widget)
121 self.add_tab_with_frontend(widget)
120
122
121 def create_tab_with_current_kernel(self):
123 def create_tab_with_current_kernel(self):
122 """create a new frontend attached to the same kernel as the current tab"""
124 """create a new frontend attached to the same kernel as the current tab"""
123 current_widget = self.tab_widget.currentWidget()
125 current_widget = self.tab_widget.currentWidget()
124 current_widget_index = self.tab_widget.indexOf(current_widget)
126 current_widget_index = self.tab_widget.indexOf(current_widget)
125 current_widget_name = self.tab_widget.tabText(current_widget_index)
127 current_widget_name = self.tab_widget.tabText(current_widget_index)
126 widget = self.slave_frontend_factory(current_widget)
128 widget = self.slave_frontend_factory(current_widget)
127 if 'slave' in current_widget_name:
129 if 'slave' in current_widget_name:
128 # don't keep stacking slaves
130 # don't keep stacking slaves
129 name = current_widget_name
131 name = current_widget_name
130 else:
132 else:
131 name = '(%s) slave' % current_widget_name
133 name = '(%s) slave' % current_widget_name
132 self.add_tab_with_frontend(widget,name=name)
134 self.add_tab_with_frontend(widget,name=name)
133
135
134 def close_tab(self,current_tab):
136 def close_tab(self,current_tab):
135 """ Called when you need to try to close a tab.
137 """ Called when you need to try to close a tab.
136
138
137 It takes the number of the tab to be closed as argument, or a reference
139 It takes the number of the tab to be closed as argument, or a reference
138 to the widget inside this tab
140 to the widget inside this tab
139 """
141 """
140
142
141 # let's be sure "tab" and "closing widget" are respectively the index
143 # let's be sure "tab" and "closing widget" are respectively the index
142 # of the tab to close and a reference to the frontend to close
144 # of the tab to close and a reference to the frontend to close
143 if type(current_tab) is not int :
145 if type(current_tab) is not int :
144 current_tab = self.tab_widget.indexOf(current_tab)
146 current_tab = self.tab_widget.indexOf(current_tab)
145 closing_widget=self.tab_widget.widget(current_tab)
147 closing_widget=self.tab_widget.widget(current_tab)
146
148
147
149
148 # when trying to be closed, widget might re-send a request to be
150 # when trying to be closed, widget might re-send a request to be
149 # closed again, but will be deleted when event will be processed. So
151 # closed again, but will be deleted when event will be processed. So
150 # need to check that widget still exists and skip if not. One example
152 # need to check that widget still exists and skip if not. One example
151 # of this is when 'exit' is sent in a slave tab. 'exit' will be
153 # of this is when 'exit' is sent in a slave tab. 'exit' will be
152 # re-sent by this function on the master widget, which ask all slave
154 # re-sent by this function on the master widget, which ask all slave
153 # widgets to exit
155 # widgets to exit
154 if closing_widget==None:
156 if closing_widget==None:
155 return
157 return
156
158
157 #get a list of all slave widgets on the same kernel.
159 #get a list of all slave widgets on the same kernel.
158 slave_tabs = self.find_slave_widgets(closing_widget)
160 slave_tabs = self.find_slave_widgets(closing_widget)
159
161
160 keepkernel = None #Use the prompt by default
162 keepkernel = None #Use the prompt by default
161 if hasattr(closing_widget,'_keep_kernel_on_exit'): #set by exit magic
163 if hasattr(closing_widget,'_keep_kernel_on_exit'): #set by exit magic
162 keepkernel = closing_widget._keep_kernel_on_exit
164 keepkernel = closing_widget._keep_kernel_on_exit
163 # If signal sent by exit magic (_keep_kernel_on_exit, exist and not None)
165 # If signal sent by exit magic (_keep_kernel_on_exit, exist and not None)
164 # we set local slave tabs._hidden to True to avoid prompting for kernel
166 # we set local slave tabs._hidden to True to avoid prompting for kernel
165 # restart when they get the signal. and then "forward" the 'exit'
167 # restart when they get the signal. and then "forward" the 'exit'
166 # to the main window
168 # to the main window
167 if keepkernel is not None:
169 if keepkernel is not None:
168 for tab in slave_tabs:
170 for tab in slave_tabs:
169 tab._hidden = True
171 tab._hidden = True
170 if closing_widget in slave_tabs:
172 if closing_widget in slave_tabs:
171 try :
173 try :
172 self.find_master_tab(closing_widget).execute('exit')
174 self.find_master_tab(closing_widget).execute('exit')
173 except AttributeError:
175 except AttributeError:
174 self.log.info("Master already closed or not local, closing only current tab")
176 self.log.info("Master already closed or not local, closing only current tab")
175 self.tab_widget.removeTab(current_tab)
177 self.tab_widget.removeTab(current_tab)
176 self.update_tab_bar_visibility()
178 self.update_tab_bar_visibility()
177 return
179 return
178
180
179 kernel_client = closing_widget.kernel_client
181 kernel_client = closing_widget.kernel_client
180 kernel_manager = closing_widget.kernel_manager
182 kernel_manager = closing_widget.kernel_manager
181
183
182 if keepkernel is None and not closing_widget._confirm_exit:
184 if keepkernel is None and not closing_widget._confirm_exit:
183 # don't prompt, just terminate the kernel if we own it
185 # don't prompt, just terminate the kernel if we own it
184 # or leave it alone if we don't
186 # or leave it alone if we don't
185 keepkernel = closing_widget._existing
187 keepkernel = closing_widget._existing
186 if keepkernel is None: #show prompt
188 if keepkernel is None: #show prompt
187 if kernel_client and kernel_client.channels_running:
189 if kernel_client and kernel_client.channels_running:
188 title = self.window().windowTitle()
190 title = self.window().windowTitle()
189 cancel = QtGui.QMessageBox.Cancel
191 cancel = QtGui.QMessageBox.Cancel
190 okay = QtGui.QMessageBox.Ok
192 okay = QtGui.QMessageBox.Ok
191 if closing_widget._may_close:
193 if closing_widget._may_close:
192 msg = "You are closing the tab : "+'"'+self.tab_widget.tabText(current_tab)+'"'
194 msg = "You are closing the tab : "+'"'+self.tab_widget.tabText(current_tab)+'"'
193 info = "Would you like to quit the Kernel and close all attached Consoles as well?"
195 info = "Would you like to quit the Kernel and close all attached Consoles as well?"
194 justthis = QtGui.QPushButton("&No, just this Tab", self)
196 justthis = QtGui.QPushButton("&No, just this Tab", self)
195 justthis.setShortcut('N')
197 justthis.setShortcut('N')
196 closeall = QtGui.QPushButton("&Yes, close all", self)
198 closeall = QtGui.QPushButton("&Yes, close all", self)
197 closeall.setShortcut('Y')
199 closeall.setShortcut('Y')
198 # allow ctrl-d ctrl-d exit, like in terminal
200 # allow ctrl-d ctrl-d exit, like in terminal
199 closeall.setShortcut('Ctrl+D')
201 closeall.setShortcut('Ctrl+D')
200 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
202 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
201 title, msg)
203 title, msg)
202 box.setInformativeText(info)
204 box.setInformativeText(info)
203 box.addButton(cancel)
205 box.addButton(cancel)
204 box.addButton(justthis, QtGui.QMessageBox.NoRole)
206 box.addButton(justthis, QtGui.QMessageBox.NoRole)
205 box.addButton(closeall, QtGui.QMessageBox.YesRole)
207 box.addButton(closeall, QtGui.QMessageBox.YesRole)
206 box.setDefaultButton(closeall)
208 box.setDefaultButton(closeall)
207 box.setEscapeButton(cancel)
209 box.setEscapeButton(cancel)
208 pixmap = QtGui.QPixmap(self._app.icon.pixmap(QtCore.QSize(64,64)))
210 pixmap = QtGui.QPixmap(self._app.icon.pixmap(QtCore.QSize(64,64)))
209 box.setIconPixmap(pixmap)
211 box.setIconPixmap(pixmap)
210 reply = box.exec_()
212 reply = box.exec_()
211 if reply == 1: # close All
213 if reply == 1: # close All
212 for slave in slave_tabs:
214 for slave in slave_tabs:
213 background(slave.kernel_client.stop_channels)
215 background(slave.kernel_client.stop_channels)
214 self.tab_widget.removeTab(self.tab_widget.indexOf(slave))
216 self.tab_widget.removeTab(self.tab_widget.indexOf(slave))
215 closing_widget.execute("exit")
217 closing_widget.execute("exit")
216 self.tab_widget.removeTab(current_tab)
218 self.tab_widget.removeTab(current_tab)
217 background(kernel_client.stop_channels)
219 background(kernel_client.stop_channels)
218 elif reply == 0: # close Console
220 elif reply == 0: # close Console
219 if not closing_widget._existing:
221 if not closing_widget._existing:
220 # Have kernel: don't quit, just close the tab
222 # Have kernel: don't quit, just close the tab
221 closing_widget.execute("exit True")
223 closing_widget.execute("exit True")
222 self.tab_widget.removeTab(current_tab)
224 self.tab_widget.removeTab(current_tab)
223 background(kernel_client.stop_channels)
225 background(kernel_client.stop_channels)
224 else:
226 else:
225 reply = QtGui.QMessageBox.question(self, title,
227 reply = QtGui.QMessageBox.question(self, title,
226 "Are you sure you want to close this Console?"+
228 "Are you sure you want to close this Console?"+
227 "\nThe Kernel and other Consoles will remain active.",
229 "\nThe Kernel and other Consoles will remain active.",
228 okay|cancel,
230 okay|cancel,
229 defaultButton=okay
231 defaultButton=okay
230 )
232 )
231 if reply == okay:
233 if reply == okay:
232 self.tab_widget.removeTab(current_tab)
234 self.tab_widget.removeTab(current_tab)
233 elif keepkernel: #close console but leave kernel running (no prompt)
235 elif keepkernel: #close console but leave kernel running (no prompt)
234 self.tab_widget.removeTab(current_tab)
236 self.tab_widget.removeTab(current_tab)
235 background(kernel_client.stop_channels)
237 background(kernel_client.stop_channels)
236 else: #close console and kernel (no prompt)
238 else: #close console and kernel (no prompt)
237 self.tab_widget.removeTab(current_tab)
239 self.tab_widget.removeTab(current_tab)
238 if kernel_client and kernel_client.channels_running:
240 if kernel_client and kernel_client.channels_running:
239 for slave in slave_tabs:
241 for slave in slave_tabs:
240 background(slave.kernel_client.stop_channels)
242 background(slave.kernel_client.stop_channels)
241 self.tab_widget.removeTab(self.tab_widget.indexOf(slave))
243 self.tab_widget.removeTab(self.tab_widget.indexOf(slave))
242 if kernel_manager:
244 if kernel_manager:
243 kernel_manager.shutdown_kernel()
245 kernel_manager.shutdown_kernel()
244 background(kernel_client.stop_channels)
246 background(kernel_client.stop_channels)
245
247
246 self.update_tab_bar_visibility()
248 self.update_tab_bar_visibility()
247
249
248 def add_tab_with_frontend(self,frontend,name=None):
250 def add_tab_with_frontend(self,frontend,name=None):
249 """ insert a tab with a given frontend in the tab bar, and give it a name
251 """ insert a tab with a given frontend in the tab bar, and give it a name
250
252
251 """
253 """
252 if not name:
254 if not name:
253 name = 'kernel %i' % self.next_kernel_id
255 name = 'kernel %i' % self.next_kernel_id
254 self.tab_widget.addTab(frontend,name)
256 self.tab_widget.addTab(frontend,name)
255 self.update_tab_bar_visibility()
257 self.update_tab_bar_visibility()
256 self.make_frontend_visible(frontend)
258 self.make_frontend_visible(frontend)
257 frontend.exit_requested.connect(self.close_tab)
259 frontend.exit_requested.connect(self.close_tab)
258
260
259 def next_tab(self):
261 def next_tab(self):
260 self.tab_widget.setCurrentIndex((self.tab_widget.currentIndex()+1))
262 self.tab_widget.setCurrentIndex((self.tab_widget.currentIndex()+1))
261
263
262 def prev_tab(self):
264 def prev_tab(self):
263 self.tab_widget.setCurrentIndex((self.tab_widget.currentIndex()-1))
265 self.tab_widget.setCurrentIndex((self.tab_widget.currentIndex()-1))
264
266
265 def make_frontend_visible(self,frontend):
267 def make_frontend_visible(self,frontend):
266 widget_index=self.tab_widget.indexOf(frontend)
268 widget_index=self.tab_widget.indexOf(frontend)
267 if widget_index > 0 :
269 if widget_index > 0 :
268 self.tab_widget.setCurrentIndex(widget_index)
270 self.tab_widget.setCurrentIndex(widget_index)
269
271
270 def find_master_tab(self,tab,as_list=False):
272 def find_master_tab(self,tab,as_list=False):
271 """
273 """
272 Try to return the frontend that owns the kernel attached to the given widget/tab.
274 Try to return the frontend that owns the kernel attached to the given widget/tab.
273
275
274 Only finds frontend owned by the current application. Selection
276 Only finds frontend owned by the current application. Selection
275 based on port of the kernel might be inaccurate if several kernel
277 based on port of the kernel might be inaccurate if several kernel
276 on different ip use same port number.
278 on different ip use same port number.
277
279
278 This function does the conversion tabNumber/widget if needed.
280 This function does the conversion tabNumber/widget if needed.
279 Might return None if no master widget (non local kernel)
281 Might return None if no master widget (non local kernel)
280 Will crash IPython if more than 1 masterWidget
282 Will crash IPython if more than 1 masterWidget
281
283
282 When asList set to True, always return a list of widget(s) owning
284 When asList set to True, always return a list of widget(s) owning
283 the kernel. The list might be empty or containing several Widget.
285 the kernel. The list might be empty or containing several Widget.
284 """
286 """
285
287
286 #convert from/to int/richIpythonWidget if needed
288 #convert from/to int/richIpythonWidget if needed
287 if isinstance(tab, int):
289 if isinstance(tab, int):
288 tab = self.tab_widget.widget(tab)
290 tab = self.tab_widget.widget(tab)
289 km=tab.kernel_client
291 km=tab.kernel_client
290
292
291 #build list of all widgets
293 #build list of all widgets
292 widget_list = [self.tab_widget.widget(i) for i in range(self.tab_widget.count())]
294 widget_list = [self.tab_widget.widget(i) for i in range(self.tab_widget.count())]
293
295
294 # widget that are candidate to be the owner of the kernel does have all the same port of the curent widget
296 # widget that are candidate to be the owner of the kernel does have all the same port of the curent widget
295 # And should have a _may_close attribute
297 # And should have a _may_close attribute
296 filtered_widget_list = [ widget for widget in widget_list if
298 filtered_widget_list = [ widget for widget in widget_list if
297 widget.kernel_client.connection_file == km.connection_file and
299 widget.kernel_client.connection_file == km.connection_file and
298 hasattr(widget,'_may_close') ]
300 hasattr(widget,'_may_close') ]
299 # the master widget is the one that may close the kernel
301 # the master widget is the one that may close the kernel
300 master_widget= [ widget for widget in filtered_widget_list if widget._may_close]
302 master_widget= [ widget for widget in filtered_widget_list if widget._may_close]
301 if as_list:
303 if as_list:
302 return master_widget
304 return master_widget
303 assert(len(master_widget)<=1 )
305 assert(len(master_widget)<=1 )
304 if len(master_widget)==0:
306 if len(master_widget)==0:
305 return None
307 return None
306
308
307 return master_widget[0]
309 return master_widget[0]
308
310
309 def find_slave_widgets(self,tab):
311 def find_slave_widgets(self,tab):
310 """return all the frontends that do not own the kernel attached to the given widget/tab.
312 """return all the frontends that do not own the kernel attached to the given widget/tab.
311
313
312 Only find frontends owned by the current application. Selection
314 Only find frontends owned by the current application. Selection
313 based on connection file of the kernel.
315 based on connection file of the kernel.
314
316
315 This function does the conversion tabNumber/widget if needed.
317 This function does the conversion tabNumber/widget if needed.
316 """
318 """
317 #convert from/to int/richIpythonWidget if needed
319 #convert from/to int/richIpythonWidget if needed
318 if isinstance(tab, int):
320 if isinstance(tab, int):
319 tab = self.tab_widget.widget(tab)
321 tab = self.tab_widget.widget(tab)
320 km=tab.kernel_client
322 km=tab.kernel_client
321
323
322 #build list of all widgets
324 #build list of all widgets
323 widget_list = [self.tab_widget.widget(i) for i in range(self.tab_widget.count())]
325 widget_list = [self.tab_widget.widget(i) for i in range(self.tab_widget.count())]
324
326
325 # widget that are candidate not to be the owner of the kernel does have all the same port of the curent widget
327 # widget that are candidate not to be the owner of the kernel does have all the same port of the curent widget
326 filtered_widget_list = ( widget for widget in widget_list if
328 filtered_widget_list = ( widget for widget in widget_list if
327 widget.kernel_client.connection_file == km.connection_file)
329 widget.kernel_client.connection_file == km.connection_file)
328 # Get a list of all widget owning the same kernel and removed it from
330 # Get a list of all widget owning the same kernel and removed it from
329 # the previous cadidate. (better using sets ?)
331 # the previous cadidate. (better using sets ?)
330 master_widget_list = self.find_master_tab(tab, as_list=True)
332 master_widget_list = self.find_master_tab(tab, as_list=True)
331 slave_list = [widget for widget in filtered_widget_list if widget not in master_widget_list]
333 slave_list = [widget for widget in filtered_widget_list if widget not in master_widget_list]
332
334
333 return slave_list
335 return slave_list
334
336
335 # Populate the menu bar with common actions and shortcuts
337 # Populate the menu bar with common actions and shortcuts
336 def add_menu_action(self, menu, action, defer_shortcut=False):
338 def add_menu_action(self, menu, action, defer_shortcut=False):
337 """Add action to menu as well as self
339 """Add action to menu as well as self
338
340
339 So that when the menu bar is invisible, its actions are still available.
341 So that when the menu bar is invisible, its actions are still available.
340
342
341 If defer_shortcut is True, set the shortcut context to widget-only,
343 If defer_shortcut is True, set the shortcut context to widget-only,
342 where it will avoid conflict with shortcuts already bound to the
344 where it will avoid conflict with shortcuts already bound to the
343 widgets themselves.
345 widgets themselves.
344 """
346 """
345 menu.addAction(action)
347 menu.addAction(action)
346 self.addAction(action)
348 self.addAction(action)
347
349
348 if defer_shortcut:
350 if defer_shortcut:
349 action.setShortcutContext(QtCore.Qt.WidgetShortcut)
351 action.setShortcutContext(QtCore.Qt.WidgetShortcut)
350
352
351 def init_menu_bar(self):
353 def init_menu_bar(self):
352 #create menu in the order they should appear in the menu bar
354 #create menu in the order they should appear in the menu bar
353 self.init_file_menu()
355 self.init_file_menu()
354 self.init_edit_menu()
356 self.init_edit_menu()
355 self.init_view_menu()
357 self.init_view_menu()
356 self.init_kernel_menu()
358 self.init_kernel_menu()
357 self.init_magic_menu()
359 self.init_magic_menu()
358 self.init_window_menu()
360 self.init_window_menu()
359 self.init_help_menu()
361 self.init_help_menu()
360
362
361 def init_file_menu(self):
363 def init_file_menu(self):
362 self.file_menu = self.menuBar().addMenu("&File")
364 self.file_menu = self.menuBar().addMenu("&File")
363
365
364 self.new_kernel_tab_act = QtGui.QAction("New Tab with &New kernel",
366 self.new_kernel_tab_act = QtGui.QAction("New Tab with &New kernel",
365 self,
367 self,
366 shortcut="Ctrl+T",
368 shortcut="Ctrl+T",
367 triggered=self.create_tab_with_new_frontend)
369 triggered=self.create_tab_with_new_frontend)
368 self.add_menu_action(self.file_menu, self.new_kernel_tab_act)
370 self.add_menu_action(self.file_menu, self.new_kernel_tab_act)
369
371
370 self.slave_kernel_tab_act = QtGui.QAction("New Tab with Sa&me kernel",
372 self.slave_kernel_tab_act = QtGui.QAction("New Tab with Sa&me kernel",
371 self,
373 self,
372 shortcut="Ctrl+Shift+T",
374 shortcut="Ctrl+Shift+T",
373 triggered=self.create_tab_with_current_kernel)
375 triggered=self.create_tab_with_current_kernel)
374 self.add_menu_action(self.file_menu, self.slave_kernel_tab_act)
376 self.add_menu_action(self.file_menu, self.slave_kernel_tab_act)
375
377
376 self.file_menu.addSeparator()
378 self.file_menu.addSeparator()
377
379
378 self.close_action=QtGui.QAction("&Close Tab",
380 self.close_action=QtGui.QAction("&Close Tab",
379 self,
381 self,
380 shortcut=QtGui.QKeySequence.Close,
382 shortcut=QtGui.QKeySequence.Close,
381 triggered=self.close_active_frontend
383 triggered=self.close_active_frontend
382 )
384 )
383 self.add_menu_action(self.file_menu, self.close_action)
385 self.add_menu_action(self.file_menu, self.close_action)
384
386
385 self.export_action=QtGui.QAction("&Save to HTML/XHTML",
387 self.export_action=QtGui.QAction("&Save to HTML/XHTML",
386 self,
388 self,
387 shortcut=QtGui.QKeySequence.Save,
389 shortcut=QtGui.QKeySequence.Save,
388 triggered=self.export_action_active_frontend
390 triggered=self.export_action_active_frontend
389 )
391 )
390 self.add_menu_action(self.file_menu, self.export_action, True)
392 self.add_menu_action(self.file_menu, self.export_action, True)
391
393
392 self.file_menu.addSeparator()
394 self.file_menu.addSeparator()
393
395
394 printkey = QtGui.QKeySequence(QtGui.QKeySequence.Print)
396 printkey = QtGui.QKeySequence(QtGui.QKeySequence.Print)
395 if printkey.matches("Ctrl+P") and sys.platform != 'darwin':
397 if printkey.matches("Ctrl+P") and sys.platform != 'darwin':
396 # Only override the default if there is a collision.
398 # Only override the default if there is a collision.
397 # Qt ctrl = cmd on OSX, so the match gets a false positive on OSX.
399 # Qt ctrl = cmd on OSX, so the match gets a false positive on OSX.
398 printkey = "Ctrl+Shift+P"
400 printkey = "Ctrl+Shift+P"
399 self.print_action = QtGui.QAction("&Print",
401 self.print_action = QtGui.QAction("&Print",
400 self,
402 self,
401 shortcut=printkey,
403 shortcut=printkey,
402 triggered=self.print_action_active_frontend)
404 triggered=self.print_action_active_frontend)
403 self.add_menu_action(self.file_menu, self.print_action, True)
405 self.add_menu_action(self.file_menu, self.print_action, True)
404
406
405 if sys.platform != 'darwin':
407 if sys.platform != 'darwin':
406 # OSX always has Quit in the Application menu, only add it
408 # OSX always has Quit in the Application menu, only add it
407 # to the File menu elsewhere.
409 # to the File menu elsewhere.
408
410
409 self.file_menu.addSeparator()
411 self.file_menu.addSeparator()
410
412
411 self.quit_action = QtGui.QAction("&Quit",
413 self.quit_action = QtGui.QAction("&Quit",
412 self,
414 self,
413 shortcut=QtGui.QKeySequence.Quit,
415 shortcut=QtGui.QKeySequence.Quit,
414 triggered=self.close,
416 triggered=self.close,
415 )
417 )
416 self.add_menu_action(self.file_menu, self.quit_action)
418 self.add_menu_action(self.file_menu, self.quit_action)
417
419
418
420
419 def init_edit_menu(self):
421 def init_edit_menu(self):
420 self.edit_menu = self.menuBar().addMenu("&Edit")
422 self.edit_menu = self.menuBar().addMenu("&Edit")
421
423
422 self.undo_action = QtGui.QAction("&Undo",
424 self.undo_action = QtGui.QAction("&Undo",
423 self,
425 self,
424 shortcut=QtGui.QKeySequence.Undo,
426 shortcut=QtGui.QKeySequence.Undo,
425 statusTip="Undo last action if possible",
427 statusTip="Undo last action if possible",
426 triggered=self.undo_active_frontend
428 triggered=self.undo_active_frontend
427 )
429 )
428 self.add_menu_action(self.edit_menu, self.undo_action)
430 self.add_menu_action(self.edit_menu, self.undo_action)
429
431
430 self.redo_action = QtGui.QAction("&Redo",
432 self.redo_action = QtGui.QAction("&Redo",
431 self,
433 self,
432 shortcut=QtGui.QKeySequence.Redo,
434 shortcut=QtGui.QKeySequence.Redo,
433 statusTip="Redo last action if possible",
435 statusTip="Redo last action if possible",
434 triggered=self.redo_active_frontend)
436 triggered=self.redo_active_frontend)
435 self.add_menu_action(self.edit_menu, self.redo_action)
437 self.add_menu_action(self.edit_menu, self.redo_action)
436
438
437 self.edit_menu.addSeparator()
439 self.edit_menu.addSeparator()
438
440
439 self.cut_action = QtGui.QAction("&Cut",
441 self.cut_action = QtGui.QAction("&Cut",
440 self,
442 self,
441 shortcut=QtGui.QKeySequence.Cut,
443 shortcut=QtGui.QKeySequence.Cut,
442 triggered=self.cut_active_frontend
444 triggered=self.cut_active_frontend
443 )
445 )
444 self.add_menu_action(self.edit_menu, self.cut_action, True)
446 self.add_menu_action(self.edit_menu, self.cut_action, True)
445
447
446 self.copy_action = QtGui.QAction("&Copy",
448 self.copy_action = QtGui.QAction("&Copy",
447 self,
449 self,
448 shortcut=QtGui.QKeySequence.Copy,
450 shortcut=QtGui.QKeySequence.Copy,
449 triggered=self.copy_active_frontend
451 triggered=self.copy_active_frontend
450 )
452 )
451 self.add_menu_action(self.edit_menu, self.copy_action, True)
453 self.add_menu_action(self.edit_menu, self.copy_action, True)
452
454
453 self.copy_raw_action = QtGui.QAction("Copy (&Raw Text)",
455 self.copy_raw_action = QtGui.QAction("Copy (&Raw Text)",
454 self,
456 self,
455 shortcut="Ctrl+Shift+C",
457 shortcut="Ctrl+Shift+C",
456 triggered=self.copy_raw_active_frontend
458 triggered=self.copy_raw_active_frontend
457 )
459 )
458 self.add_menu_action(self.edit_menu, self.copy_raw_action, True)
460 self.add_menu_action(self.edit_menu, self.copy_raw_action, True)
459
461
460 self.paste_action = QtGui.QAction("&Paste",
462 self.paste_action = QtGui.QAction("&Paste",
461 self,
463 self,
462 shortcut=QtGui.QKeySequence.Paste,
464 shortcut=QtGui.QKeySequence.Paste,
463 triggered=self.paste_active_frontend
465 triggered=self.paste_active_frontend
464 )
466 )
465 self.add_menu_action(self.edit_menu, self.paste_action, True)
467 self.add_menu_action(self.edit_menu, self.paste_action, True)
466
468
467 self.edit_menu.addSeparator()
469 self.edit_menu.addSeparator()
468
470
469 selectall = QtGui.QKeySequence(QtGui.QKeySequence.SelectAll)
471 selectall = QtGui.QKeySequence(QtGui.QKeySequence.SelectAll)
470 if selectall.matches("Ctrl+A") and sys.platform != 'darwin':
472 if selectall.matches("Ctrl+A") and sys.platform != 'darwin':
471 # Only override the default if there is a collision.
473 # Only override the default if there is a collision.
472 # Qt ctrl = cmd on OSX, so the match gets a false positive on OSX.
474 # Qt ctrl = cmd on OSX, so the match gets a false positive on OSX.
473 selectall = "Ctrl+Shift+A"
475 selectall = "Ctrl+Shift+A"
474 self.select_all_action = QtGui.QAction("Select &All",
476 self.select_all_action = QtGui.QAction("Select &All",
475 self,
477 self,
476 shortcut=selectall,
478 shortcut=selectall,
477 triggered=self.select_all_active_frontend
479 triggered=self.select_all_active_frontend
478 )
480 )
479 self.add_menu_action(self.edit_menu, self.select_all_action, True)
481 self.add_menu_action(self.edit_menu, self.select_all_action, True)
480
482
481
483
482 def init_view_menu(self):
484 def init_view_menu(self):
483 self.view_menu = self.menuBar().addMenu("&View")
485 self.view_menu = self.menuBar().addMenu("&View")
484
486
485 if sys.platform != 'darwin':
487 if sys.platform != 'darwin':
486 # disable on OSX, where there is always a menu bar
488 # disable on OSX, where there is always a menu bar
487 self.toggle_menu_bar_act = QtGui.QAction("Toggle &Menu Bar",
489 self.toggle_menu_bar_act = QtGui.QAction("Toggle &Menu Bar",
488 self,
490 self,
489 shortcut="Ctrl+Shift+M",
491 shortcut="Ctrl+Shift+M",
490 statusTip="Toggle visibility of menubar",
492 statusTip="Toggle visibility of menubar",
491 triggered=self.toggle_menu_bar)
493 triggered=self.toggle_menu_bar)
492 self.add_menu_action(self.view_menu, self.toggle_menu_bar_act)
494 self.add_menu_action(self.view_menu, self.toggle_menu_bar_act)
493
495
494 fs_key = "Ctrl+Meta+F" if sys.platform == 'darwin' else "F11"
496 fs_key = "Ctrl+Meta+F" if sys.platform == 'darwin' else "F11"
495 self.full_screen_act = QtGui.QAction("&Full Screen",
497 self.full_screen_act = QtGui.QAction("&Full Screen",
496 self,
498 self,
497 shortcut=fs_key,
499 shortcut=fs_key,
498 statusTip="Toggle between Fullscreen and Normal Size",
500 statusTip="Toggle between Fullscreen and Normal Size",
499 triggered=self.toggleFullScreen)
501 triggered=self.toggleFullScreen)
500 self.add_menu_action(self.view_menu, self.full_screen_act)
502 self.add_menu_action(self.view_menu, self.full_screen_act)
501
503
502 self.view_menu.addSeparator()
504 self.view_menu.addSeparator()
503
505
504 self.increase_font_size = QtGui.QAction("Zoom &In",
506 self.increase_font_size = QtGui.QAction("Zoom &In",
505 self,
507 self,
506 shortcut=QtGui.QKeySequence.ZoomIn,
508 shortcut=QtGui.QKeySequence.ZoomIn,
507 triggered=self.increase_font_size_active_frontend
509 triggered=self.increase_font_size_active_frontend
508 )
510 )
509 self.add_menu_action(self.view_menu, self.increase_font_size, True)
511 self.add_menu_action(self.view_menu, self.increase_font_size, True)
510
512
511 self.decrease_font_size = QtGui.QAction("Zoom &Out",
513 self.decrease_font_size = QtGui.QAction("Zoom &Out",
512 self,
514 self,
513 shortcut=QtGui.QKeySequence.ZoomOut,
515 shortcut=QtGui.QKeySequence.ZoomOut,
514 triggered=self.decrease_font_size_active_frontend
516 triggered=self.decrease_font_size_active_frontend
515 )
517 )
516 self.add_menu_action(self.view_menu, self.decrease_font_size, True)
518 self.add_menu_action(self.view_menu, self.decrease_font_size, True)
517
519
518 self.reset_font_size = QtGui.QAction("Zoom &Reset",
520 self.reset_font_size = QtGui.QAction("Zoom &Reset",
519 self,
521 self,
520 shortcut="Ctrl+0",
522 shortcut="Ctrl+0",
521 triggered=self.reset_font_size_active_frontend
523 triggered=self.reset_font_size_active_frontend
522 )
524 )
523 self.add_menu_action(self.view_menu, self.reset_font_size, True)
525 self.add_menu_action(self.view_menu, self.reset_font_size, True)
524
526
525 self.view_menu.addSeparator()
527 self.view_menu.addSeparator()
526
528
527 self.clear_action = QtGui.QAction("&Clear Screen",
529 self.clear_action = QtGui.QAction("&Clear Screen",
528 self,
530 self,
529 shortcut='Ctrl+L',
531 shortcut='Ctrl+L',
530 statusTip="Clear the console",
532 statusTip="Clear the console",
531 triggered=self.clear_magic_active_frontend)
533 triggered=self.clear_magic_active_frontend)
532 self.add_menu_action(self.view_menu, self.clear_action)
534 self.add_menu_action(self.view_menu, self.clear_action)
533
535
534 self.pager_menu = self.view_menu.addMenu("&Pager")
536 self.pager_menu = self.view_menu.addMenu("&Pager")
535
537
536 hsplit_action = QtGui.QAction(".. &Horizontal Split",
538 hsplit_action = QtGui.QAction(".. &Horizontal Split",
537 self,
539 self,
538 triggered=lambda: self.set_paging_active_frontend('hsplit'))
540 triggered=lambda: self.set_paging_active_frontend('hsplit'))
539
541
540 vsplit_action = QtGui.QAction(" : &Vertical Split",
542 vsplit_action = QtGui.QAction(" : &Vertical Split",
541 self,
543 self,
542 triggered=lambda: self.set_paging_active_frontend('vsplit'))
544 triggered=lambda: self.set_paging_active_frontend('vsplit'))
543
545
544 inside_action = QtGui.QAction(" &Inside Pager",
546 inside_action = QtGui.QAction(" &Inside Pager",
545 self,
547 self,
546 triggered=lambda: self.set_paging_active_frontend('inside'))
548 triggered=lambda: self.set_paging_active_frontend('inside'))
547
549
548 self.pager_menu.addAction(hsplit_action)
550 self.pager_menu.addAction(hsplit_action)
549 self.pager_menu.addAction(vsplit_action)
551 self.pager_menu.addAction(vsplit_action)
550 self.pager_menu.addAction(inside_action)
552 self.pager_menu.addAction(inside_action)
551
553
552 def init_kernel_menu(self):
554 def init_kernel_menu(self):
553 self.kernel_menu = self.menuBar().addMenu("&Kernel")
555 self.kernel_menu = self.menuBar().addMenu("&Kernel")
554 # Qt on OSX maps Ctrl to Cmd, and Meta to Ctrl
556 # Qt on OSX maps Ctrl to Cmd, and Meta to Ctrl
555 # keep the signal shortcuts to ctrl, rather than
557 # keep the signal shortcuts to ctrl, rather than
556 # platform-default like we do elsewhere.
558 # platform-default like we do elsewhere.
557
559
558 ctrl = "Meta" if sys.platform == 'darwin' else "Ctrl"
560 ctrl = "Meta" if sys.platform == 'darwin' else "Ctrl"
559
561
560 self.interrupt_kernel_action = QtGui.QAction("&Interrupt current Kernel",
562 self.interrupt_kernel_action = QtGui.QAction("&Interrupt current Kernel",
561 self,
563 self,
562 triggered=self.interrupt_kernel_active_frontend,
564 triggered=self.interrupt_kernel_active_frontend,
563 shortcut=ctrl+"+C",
565 shortcut=ctrl+"+C",
564 )
566 )
565 self.add_menu_action(self.kernel_menu, self.interrupt_kernel_action)
567 self.add_menu_action(self.kernel_menu, self.interrupt_kernel_action)
566
568
567 self.restart_kernel_action = QtGui.QAction("&Restart current Kernel",
569 self.restart_kernel_action = QtGui.QAction("&Restart current Kernel",
568 self,
570 self,
569 triggered=self.restart_kernel_active_frontend,
571 triggered=self.restart_kernel_active_frontend,
570 shortcut=ctrl+"+.",
572 shortcut=ctrl+"+.",
571 )
573 )
572 self.add_menu_action(self.kernel_menu, self.restart_kernel_action)
574 self.add_menu_action(self.kernel_menu, self.restart_kernel_action)
573
575
574 self.kernel_menu.addSeparator()
576 self.kernel_menu.addSeparator()
575
577
576 self.confirm_restart_kernel_action = QtGui.QAction("&Confirm kernel restart",
578 self.confirm_restart_kernel_action = QtGui.QAction("&Confirm kernel restart",
577 self,
579 self,
578 checkable=True,
580 checkable=True,
579 checked=self.active_frontend.confirm_restart,
581 checked=self.active_frontend.confirm_restart,
580 triggered=self.toggle_confirm_restart_active_frontend
582 triggered=self.toggle_confirm_restart_active_frontend
581 )
583 )
582
584
583 self.add_menu_action(self.kernel_menu, self.confirm_restart_kernel_action)
585 self.add_menu_action(self.kernel_menu, self.confirm_restart_kernel_action)
584 self.tab_widget.currentChanged.connect(self.update_restart_checkbox)
586 self.tab_widget.currentChanged.connect(self.update_restart_checkbox)
585
587
586 def _make_dynamic_magic(self,magic):
588 def _make_dynamic_magic(self,magic):
587 """Return a function `fun` that will execute `magic` on active frontend.
589 """Return a function `fun` that will execute `magic` on active frontend.
588
590
589 Parameters
591 Parameters
590 ----------
592 ----------
591 magic : string
593 magic : string
592 string that will be executed as is when the returned function is called
594 string that will be executed as is when the returned function is called
593
595
594 Returns
596 Returns
595 -------
597 -------
596 fun : function
598 fun : function
597 function with no parameters, when called will execute `magic` on the
599 function with no parameters, when called will execute `magic` on the
598 current active frontend at call time
600 current active frontend at call time
599
601
600 See Also
602 See Also
601 --------
603 --------
602 populate_all_magic_menu : generate the "All Magics..." menu
604 populate_all_magic_menu : generate the "All Magics..." menu
603
605
604 Notes
606 Notes
605 -----
607 -----
606 `fun` executes `magic` in active frontend at the moment it is triggered,
608 `fun` executes `magic` in active frontend at the moment it is triggered,
607 not the active frontend at the moment it was created.
609 not the active frontend at the moment it was created.
608
610
609 This function is mostly used to create the "All Magics..." Menu at run time.
611 This function is mostly used to create the "All Magics..." Menu at run time.
610 """
612 """
611 # need two level nested function to be sure to pass magic
613 # need two level nested function to be sure to pass magic
612 # to active frontend **at run time**.
614 # to active frontend **at run time**.
613 def inner_dynamic_magic():
615 def inner_dynamic_magic():
614 self.active_frontend.execute(magic)
616 self.active_frontend.execute(magic)
615 inner_dynamic_magic.__name__ = "dynamics_magic_s"
617 inner_dynamic_magic.__name__ = "dynamics_magic_s"
616 return inner_dynamic_magic
618 return inner_dynamic_magic
617
619
618 def populate_all_magic_menu(self, display_data=None):
620 def populate_all_magic_menu(self, display_data=None):
619 """Clean "All Magics..." menu and repopulate it with `display_data`
621 """Clean "All Magics..." menu and repopulate it with `display_data`
620
622
621 Parameters
623 Parameters
622 ----------
624 ----------
623 display_data : dict,
625 display_data : dict,
624 dict of display_data for the magics list.
626 dict of display_data for the magics dict of a MagicsManager.
625 Expects json data, as the result of MagicsManager.lsmagic_json()
627 Expects json data, as the result of %lsmagic
626
628
627 """
629 """
628 for k,v in self._magic_menu_dict.items():
630 for k,v in self._magic_menu_dict.items():
629 v.clear()
631 v.clear()
630 self.all_magic_menu.clear()
632 self.all_magic_menu.clear()
631
633
632 if not display_data:
634 if not display_data:
633 return
635 return
634
636
635 mlist = json.loads(display_data['data'].get('application/json', []))
637 if display_data['status'] != 'ok':
636
638 self.log.warn("%%lsmagic user-expression failed: %s" % display_data)
637 for magic in mlist:
639 return
638 cell = (magic['type'] == 'cell')
639 name = magic['name']
640 mclass = magic['class']
641 if cell :
642 prefix='%%'
643 else :
644 prefix='%'
645 magic_menu = self._get_magic_menu(mclass)
646
640
647 pmagic = '%s%s'%(prefix,name)
641 mdict = json.loads(display_data['data'].get('application/json', {}))
648
642
643 for mtype in sorted(mdict):
644 subdict = mdict[mtype]
645 prefix = magic_escapes[mtype]
646 for name in sorted(subdict):
647 mclass = subdict[name]
648 magic_menu = self._get_magic_menu(mclass)
649 pmagic = prefix + name
649 xaction = QtGui.QAction(pmagic,
650 xaction = QtGui.QAction(pmagic,
650 self,
651 self,
651 triggered=self._make_dynamic_magic(pmagic)
652 triggered=self._make_dynamic_magic(pmagic)
652 )
653 )
653 magic_menu.addAction(xaction)
654 magic_menu.addAction(xaction)
654 self.all_magic_menu.addAction(xaction)
655 self.all_magic_menu.addAction(xaction)
655
656
656 def update_all_magic_menu(self):
657 def update_all_magic_menu(self):
657 """ Update the list of magics in the "All Magics..." Menu
658 """ Update the list of magics in the "All Magics..." Menu
658
659
659 Request the kernel with the list of available magics and populate the
660 Request the kernel with the list of available magics and populate the
660 menu with the list received back
661 menu with the list received back
661
662
662 """
663 """
663 self.active_frontend._silent_exec_callback('get_ipython().magics_manager.lsmagic_json()',
664 self.active_frontend._silent_exec_callback('get_ipython().magic("lsmagic")',
664 self.populate_all_magic_menu)
665 self.populate_all_magic_menu)
665
666
666 def _get_magic_menu(self,menuidentifier, menulabel=None):
667 def _get_magic_menu(self,menuidentifier, menulabel=None):
667 """return a submagic menu by name, and create it if needed
668 """return a submagic menu by name, and create it if needed
668
669
669 parameters:
670 parameters:
670 -----------
671 -----------
671
672
672 menulabel : str
673 menulabel : str
673 Label for the menu
674 Label for the menu
674
675
675 Will infere the menu name from the identifier at creation if menulabel not given.
676 Will infere the menu name from the identifier at creation if menulabel not given.
676 To do so you have too give menuidentifier as a CamelCassedString
677 To do so you have too give menuidentifier as a CamelCassedString
677 """
678 """
678 menu = self._magic_menu_dict.get(menuidentifier,None)
679 menu = self._magic_menu_dict.get(menuidentifier,None)
679 if not menu :
680 if not menu :
680 if not menulabel:
681 if not menulabel:
681 menulabel = re.sub("([a-zA-Z]+)([A-Z][a-z])","\g<1> \g<2>",menuidentifier)
682 menulabel = re.sub("([a-zA-Z]+)([A-Z][a-z])","\g<1> \g<2>",menuidentifier)
682 menu = QtGui.QMenu(menulabel,self.magic_menu)
683 menu = QtGui.QMenu(menulabel,self.magic_menu)
683 self._magic_menu_dict[menuidentifier]=menu
684 self._magic_menu_dict[menuidentifier]=menu
684 self.magic_menu.insertMenu(self.magic_menu_separator,menu)
685 self.magic_menu.insertMenu(self.magic_menu_separator,menu)
685 return menu
686 return menu
686
687
687
688
688
689
689 def init_magic_menu(self):
690 def init_magic_menu(self):
690 self.magic_menu = self.menuBar().addMenu("&Magic")
691 self.magic_menu = self.menuBar().addMenu("&Magic")
691 self.magic_menu_separator = self.magic_menu.addSeparator()
692 self.magic_menu_separator = self.magic_menu.addSeparator()
692
693
693 self.all_magic_menu = self._get_magic_menu("AllMagics", menulabel="&All Magics...")
694 self.all_magic_menu = self._get_magic_menu("AllMagics", menulabel="&All Magics...")
694
695
695 # This action should usually not appear as it will be cleared when menu
696 # This action should usually not appear as it will be cleared when menu
696 # is updated at first kernel response. Though, it is necessary when
697 # is updated at first kernel response. Though, it is necessary when
697 # connecting through X-forwarding, as in this case, the menu is not
698 # connecting through X-forwarding, as in this case, the menu is not
698 # auto updated, SO DO NOT DELETE.
699 # auto updated, SO DO NOT DELETE.
699 self.pop = QtGui.QAction("&Update All Magic Menu ",
700 self.pop = QtGui.QAction("&Update All Magic Menu ",
700 self, triggered=self.update_all_magic_menu)
701 self, triggered=self.update_all_magic_menu)
701 self.add_menu_action(self.all_magic_menu, self.pop)
702 self.add_menu_action(self.all_magic_menu, self.pop)
702 # we need to populate the 'Magic Menu' once the kernel has answer at
703 # we need to populate the 'Magic Menu' once the kernel has answer at
703 # least once let's do it immediately, but it's assured to works
704 # least once let's do it immediately, but it's assured to works
704 self.pop.trigger()
705 self.pop.trigger()
705
706
706 self.reset_action = QtGui.QAction("&Reset",
707 self.reset_action = QtGui.QAction("&Reset",
707 self,
708 self,
708 statusTip="Clear all variables from workspace",
709 statusTip="Clear all variables from workspace",
709 triggered=self.reset_magic_active_frontend)
710 triggered=self.reset_magic_active_frontend)
710 self.add_menu_action(self.magic_menu, self.reset_action)
711 self.add_menu_action(self.magic_menu, self.reset_action)
711
712
712 self.history_action = QtGui.QAction("&History",
713 self.history_action = QtGui.QAction("&History",
713 self,
714 self,
714 statusTip="show command history",
715 statusTip="show command history",
715 triggered=self.history_magic_active_frontend)
716 triggered=self.history_magic_active_frontend)
716 self.add_menu_action(self.magic_menu, self.history_action)
717 self.add_menu_action(self.magic_menu, self.history_action)
717
718
718 self.save_action = QtGui.QAction("E&xport History ",
719 self.save_action = QtGui.QAction("E&xport History ",
719 self,
720 self,
720 statusTip="Export History as Python File",
721 statusTip="Export History as Python File",
721 triggered=self.save_magic_active_frontend)
722 triggered=self.save_magic_active_frontend)
722 self.add_menu_action(self.magic_menu, self.save_action)
723 self.add_menu_action(self.magic_menu, self.save_action)
723
724
724 self.who_action = QtGui.QAction("&Who",
725 self.who_action = QtGui.QAction("&Who",
725 self,
726 self,
726 statusTip="List interactive variables",
727 statusTip="List interactive variables",
727 triggered=self.who_magic_active_frontend)
728 triggered=self.who_magic_active_frontend)
728 self.add_menu_action(self.magic_menu, self.who_action)
729 self.add_menu_action(self.magic_menu, self.who_action)
729
730
730 self.who_ls_action = QtGui.QAction("Wh&o ls",
731 self.who_ls_action = QtGui.QAction("Wh&o ls",
731 self,
732 self,
732 statusTip="Return a list of interactive variables",
733 statusTip="Return a list of interactive variables",
733 triggered=self.who_ls_magic_active_frontend)
734 triggered=self.who_ls_magic_active_frontend)
734 self.add_menu_action(self.magic_menu, self.who_ls_action)
735 self.add_menu_action(self.magic_menu, self.who_ls_action)
735
736
736 self.whos_action = QtGui.QAction("Who&s",
737 self.whos_action = QtGui.QAction("Who&s",
737 self,
738 self,
738 statusTip="List interactive variables with details",
739 statusTip="List interactive variables with details",
739 triggered=self.whos_magic_active_frontend)
740 triggered=self.whos_magic_active_frontend)
740 self.add_menu_action(self.magic_menu, self.whos_action)
741 self.add_menu_action(self.magic_menu, self.whos_action)
741
742
742 def init_window_menu(self):
743 def init_window_menu(self):
743 self.window_menu = self.menuBar().addMenu("&Window")
744 self.window_menu = self.menuBar().addMenu("&Window")
744 if sys.platform == 'darwin':
745 if sys.platform == 'darwin':
745 # add min/maximize actions to OSX, which lacks default bindings.
746 # add min/maximize actions to OSX, which lacks default bindings.
746 self.minimizeAct = QtGui.QAction("Mini&mize",
747 self.minimizeAct = QtGui.QAction("Mini&mize",
747 self,
748 self,
748 shortcut="Ctrl+m",
749 shortcut="Ctrl+m",
749 statusTip="Minimize the window/Restore Normal Size",
750 statusTip="Minimize the window/Restore Normal Size",
750 triggered=self.toggleMinimized)
751 triggered=self.toggleMinimized)
751 # maximize is called 'Zoom' on OSX for some reason
752 # maximize is called 'Zoom' on OSX for some reason
752 self.maximizeAct = QtGui.QAction("&Zoom",
753 self.maximizeAct = QtGui.QAction("&Zoom",
753 self,
754 self,
754 shortcut="Ctrl+Shift+M",
755 shortcut="Ctrl+Shift+M",
755 statusTip="Maximize the window/Restore Normal Size",
756 statusTip="Maximize the window/Restore Normal Size",
756 triggered=self.toggleMaximized)
757 triggered=self.toggleMaximized)
757
758
758 self.add_menu_action(self.window_menu, self.minimizeAct)
759 self.add_menu_action(self.window_menu, self.minimizeAct)
759 self.add_menu_action(self.window_menu, self.maximizeAct)
760 self.add_menu_action(self.window_menu, self.maximizeAct)
760 self.window_menu.addSeparator()
761 self.window_menu.addSeparator()
761
762
762 prev_key = "Ctrl+Shift+Left" if sys.platform == 'darwin' else "Ctrl+PgUp"
763 prev_key = "Ctrl+Shift+Left" if sys.platform == 'darwin' else "Ctrl+PgUp"
763 self.prev_tab_act = QtGui.QAction("Pre&vious Tab",
764 self.prev_tab_act = QtGui.QAction("Pre&vious Tab",
764 self,
765 self,
765 shortcut=prev_key,
766 shortcut=prev_key,
766 statusTip="Select previous tab",
767 statusTip="Select previous tab",
767 triggered=self.prev_tab)
768 triggered=self.prev_tab)
768 self.add_menu_action(self.window_menu, self.prev_tab_act)
769 self.add_menu_action(self.window_menu, self.prev_tab_act)
769
770
770 next_key = "Ctrl+Shift+Right" if sys.platform == 'darwin' else "Ctrl+PgDown"
771 next_key = "Ctrl+Shift+Right" if sys.platform == 'darwin' else "Ctrl+PgDown"
771 self.next_tab_act = QtGui.QAction("Ne&xt Tab",
772 self.next_tab_act = QtGui.QAction("Ne&xt Tab",
772 self,
773 self,
773 shortcut=next_key,
774 shortcut=next_key,
774 statusTip="Select next tab",
775 statusTip="Select next tab",
775 triggered=self.next_tab)
776 triggered=self.next_tab)
776 self.add_menu_action(self.window_menu, self.next_tab_act)
777 self.add_menu_action(self.window_menu, self.next_tab_act)
777
778
778 def init_help_menu(self):
779 def init_help_menu(self):
779 # please keep the Help menu in Mac Os even if empty. It will
780 # please keep the Help menu in Mac Os even if empty. It will
780 # automatically contain a search field to search inside menus and
781 # automatically contain a search field to search inside menus and
781 # please keep it spelled in English, as long as Qt Doesn't support
782 # please keep it spelled in English, as long as Qt Doesn't support
782 # a QAction.MenuRole like HelpMenuRole otherwise it will lose
783 # a QAction.MenuRole like HelpMenuRole otherwise it will lose
783 # this search field functionality
784 # this search field functionality
784
785
785 self.help_menu = self.menuBar().addMenu("&Help")
786 self.help_menu = self.menuBar().addMenu("&Help")
786
787
787
788
788 # Help Menu
789 # Help Menu
789
790
790 self.intro_active_frontend_action = QtGui.QAction("&Intro to IPython",
791 self.intro_active_frontend_action = QtGui.QAction("&Intro to IPython",
791 self,
792 self,
792 triggered=self.intro_active_frontend
793 triggered=self.intro_active_frontend
793 )
794 )
794 self.add_menu_action(self.help_menu, self.intro_active_frontend_action)
795 self.add_menu_action(self.help_menu, self.intro_active_frontend_action)
795
796
796 self.quickref_active_frontend_action = QtGui.QAction("IPython &Cheat Sheet",
797 self.quickref_active_frontend_action = QtGui.QAction("IPython &Cheat Sheet",
797 self,
798 self,
798 triggered=self.quickref_active_frontend
799 triggered=self.quickref_active_frontend
799 )
800 )
800 self.add_menu_action(self.help_menu, self.quickref_active_frontend_action)
801 self.add_menu_action(self.help_menu, self.quickref_active_frontend_action)
801
802
802 self.guiref_active_frontend_action = QtGui.QAction("&Qt Console",
803 self.guiref_active_frontend_action = QtGui.QAction("&Qt Console",
803 self,
804 self,
804 triggered=self.guiref_active_frontend
805 triggered=self.guiref_active_frontend
805 )
806 )
806 self.add_menu_action(self.help_menu, self.guiref_active_frontend_action)
807 self.add_menu_action(self.help_menu, self.guiref_active_frontend_action)
807
808
808 self.onlineHelpAct = QtGui.QAction("Open Online &Help",
809 self.onlineHelpAct = QtGui.QAction("Open Online &Help",
809 self,
810 self,
810 triggered=self._open_online_help)
811 triggered=self._open_online_help)
811 self.add_menu_action(self.help_menu, self.onlineHelpAct)
812 self.add_menu_action(self.help_menu, self.onlineHelpAct)
812
813
813 # minimize/maximize/fullscreen actions:
814 # minimize/maximize/fullscreen actions:
814
815
815 def toggle_menu_bar(self):
816 def toggle_menu_bar(self):
816 menu_bar = self.menuBar()
817 menu_bar = self.menuBar()
817 if menu_bar.isVisible():
818 if menu_bar.isVisible():
818 menu_bar.setVisible(False)
819 menu_bar.setVisible(False)
819 else:
820 else:
820 menu_bar.setVisible(True)
821 menu_bar.setVisible(True)
821
822
822 def toggleMinimized(self):
823 def toggleMinimized(self):
823 if not self.isMinimized():
824 if not self.isMinimized():
824 self.showMinimized()
825 self.showMinimized()
825 else:
826 else:
826 self.showNormal()
827 self.showNormal()
827
828
828 def _open_online_help(self):
829 def _open_online_help(self):
829 filename="http://ipython.org/ipython-doc/stable/index.html"
830 filename="http://ipython.org/ipython-doc/stable/index.html"
830 webbrowser.open(filename, new=1, autoraise=True)
831 webbrowser.open(filename, new=1, autoraise=True)
831
832
832 def toggleMaximized(self):
833 def toggleMaximized(self):
833 if not self.isMaximized():
834 if not self.isMaximized():
834 self.showMaximized()
835 self.showMaximized()
835 else:
836 else:
836 self.showNormal()
837 self.showNormal()
837
838
838 # Min/Max imizing while in full screen give a bug
839 # Min/Max imizing while in full screen give a bug
839 # when going out of full screen, at least on OSX
840 # when going out of full screen, at least on OSX
840 def toggleFullScreen(self):
841 def toggleFullScreen(self):
841 if not self.isFullScreen():
842 if not self.isFullScreen():
842 self.showFullScreen()
843 self.showFullScreen()
843 if sys.platform == 'darwin':
844 if sys.platform == 'darwin':
844 self.maximizeAct.setEnabled(False)
845 self.maximizeAct.setEnabled(False)
845 self.minimizeAct.setEnabled(False)
846 self.minimizeAct.setEnabled(False)
846 else:
847 else:
847 self.showNormal()
848 self.showNormal()
848 if sys.platform == 'darwin':
849 if sys.platform == 'darwin':
849 self.maximizeAct.setEnabled(True)
850 self.maximizeAct.setEnabled(True)
850 self.minimizeAct.setEnabled(True)
851 self.minimizeAct.setEnabled(True)
851
852
852 def set_paging_active_frontend(self, paging):
853 def set_paging_active_frontend(self, paging):
853 self.active_frontend._set_paging(paging)
854 self.active_frontend._set_paging(paging)
854
855
855 def close_active_frontend(self):
856 def close_active_frontend(self):
856 self.close_tab(self.active_frontend)
857 self.close_tab(self.active_frontend)
857
858
858 def restart_kernel_active_frontend(self):
859 def restart_kernel_active_frontend(self):
859 self.active_frontend.request_restart_kernel()
860 self.active_frontend.request_restart_kernel()
860
861
861 def interrupt_kernel_active_frontend(self):
862 def interrupt_kernel_active_frontend(self):
862 self.active_frontend.request_interrupt_kernel()
863 self.active_frontend.request_interrupt_kernel()
863
864
864 def toggle_confirm_restart_active_frontend(self):
865 def toggle_confirm_restart_active_frontend(self):
865 widget = self.active_frontend
866 widget = self.active_frontend
866 widget.confirm_restart = not widget.confirm_restart
867 widget.confirm_restart = not widget.confirm_restart
867 self.confirm_restart_kernel_action.setChecked(widget.confirm_restart)
868 self.confirm_restart_kernel_action.setChecked(widget.confirm_restart)
868
869
869 def update_restart_checkbox(self):
870 def update_restart_checkbox(self):
870 if self.active_frontend is None:
871 if self.active_frontend is None:
871 return
872 return
872 widget = self.active_frontend
873 widget = self.active_frontend
873 self.confirm_restart_kernel_action.setChecked(widget.confirm_restart)
874 self.confirm_restart_kernel_action.setChecked(widget.confirm_restart)
874
875
875 def cut_active_frontend(self):
876 def cut_active_frontend(self):
876 widget = self.active_frontend
877 widget = self.active_frontend
877 if widget.can_cut():
878 if widget.can_cut():
878 widget.cut()
879 widget.cut()
879
880
880 def copy_active_frontend(self):
881 def copy_active_frontend(self):
881 widget = self.active_frontend
882 widget = self.active_frontend
882 widget.copy()
883 widget.copy()
883
884
884 def copy_raw_active_frontend(self):
885 def copy_raw_active_frontend(self):
885 self.active_frontend._copy_raw_action.trigger()
886 self.active_frontend._copy_raw_action.trigger()
886
887
887 def paste_active_frontend(self):
888 def paste_active_frontend(self):
888 widget = self.active_frontend
889 widget = self.active_frontend
889 if widget.can_paste():
890 if widget.can_paste():
890 widget.paste()
891 widget.paste()
891
892
892 def undo_active_frontend(self):
893 def undo_active_frontend(self):
893 self.active_frontend.undo()
894 self.active_frontend.undo()
894
895
895 def redo_active_frontend(self):
896 def redo_active_frontend(self):
896 self.active_frontend.redo()
897 self.active_frontend.redo()
897
898
898 def reset_magic_active_frontend(self):
899 def reset_magic_active_frontend(self):
899 self.active_frontend.execute("%reset")
900 self.active_frontend.execute("%reset")
900
901
901 def history_magic_active_frontend(self):
902 def history_magic_active_frontend(self):
902 self.active_frontend.execute("%history")
903 self.active_frontend.execute("%history")
903
904
904 def save_magic_active_frontend(self):
905 def save_magic_active_frontend(self):
905 self.active_frontend.save_magic()
906 self.active_frontend.save_magic()
906
907
907 def clear_magic_active_frontend(self):
908 def clear_magic_active_frontend(self):
908 self.active_frontend.execute("%clear")
909 self.active_frontend.execute("%clear")
909
910
910 def who_magic_active_frontend(self):
911 def who_magic_active_frontend(self):
911 self.active_frontend.execute("%who")
912 self.active_frontend.execute("%who")
912
913
913 def who_ls_magic_active_frontend(self):
914 def who_ls_magic_active_frontend(self):
914 self.active_frontend.execute("%who_ls")
915 self.active_frontend.execute("%who_ls")
915
916
916 def whos_magic_active_frontend(self):
917 def whos_magic_active_frontend(self):
917 self.active_frontend.execute("%whos")
918 self.active_frontend.execute("%whos")
918
919
919 def print_action_active_frontend(self):
920 def print_action_active_frontend(self):
920 self.active_frontend.print_action.trigger()
921 self.active_frontend.print_action.trigger()
921
922
922 def export_action_active_frontend(self):
923 def export_action_active_frontend(self):
923 self.active_frontend.export_action.trigger()
924 self.active_frontend.export_action.trigger()
924
925
925 def select_all_active_frontend(self):
926 def select_all_active_frontend(self):
926 self.active_frontend.select_all_action.trigger()
927 self.active_frontend.select_all_action.trigger()
927
928
928 def increase_font_size_active_frontend(self):
929 def increase_font_size_active_frontend(self):
929 self.active_frontend.increase_font_size.trigger()
930 self.active_frontend.increase_font_size.trigger()
930
931
931 def decrease_font_size_active_frontend(self):
932 def decrease_font_size_active_frontend(self):
932 self.active_frontend.decrease_font_size.trigger()
933 self.active_frontend.decrease_font_size.trigger()
933
934
934 def reset_font_size_active_frontend(self):
935 def reset_font_size_active_frontend(self):
935 self.active_frontend.reset_font_size.trigger()
936 self.active_frontend.reset_font_size.trigger()
936
937
937 def guiref_active_frontend(self):
938 def guiref_active_frontend(self):
938 self.active_frontend.execute("%guiref")
939 self.active_frontend.execute("%guiref")
939
940
940 def intro_active_frontend(self):
941 def intro_active_frontend(self):
941 self.active_frontend.execute("?")
942 self.active_frontend.execute("?")
942
943
943 def quickref_active_frontend(self):
944 def quickref_active_frontend(self):
944 self.active_frontend.execute("%quickref")
945 self.active_frontend.execute("%quickref")
945 #---------------------------------------------------------------------------
946 #---------------------------------------------------------------------------
946 # QWidget interface
947 # QWidget interface
947 #---------------------------------------------------------------------------
948 #---------------------------------------------------------------------------
948
949
949 def closeEvent(self, event):
950 def closeEvent(self, event):
950 """ Forward the close event to every tabs contained by the windows
951 """ Forward the close event to every tabs contained by the windows
951 """
952 """
952 if self.tab_widget.count() == 0:
953 if self.tab_widget.count() == 0:
953 # no tabs, just close
954 # no tabs, just close
954 event.accept()
955 event.accept()
955 return
956 return
956 # Do Not loop on the widget count as it change while closing
957 # Do Not loop on the widget count as it change while closing
957 title = self.window().windowTitle()
958 title = self.window().windowTitle()
958 cancel = QtGui.QMessageBox.Cancel
959 cancel = QtGui.QMessageBox.Cancel
959 okay = QtGui.QMessageBox.Ok
960 okay = QtGui.QMessageBox.Ok
960
961
961 if self.confirm_exit:
962 if self.confirm_exit:
962 if self.tab_widget.count() > 1:
963 if self.tab_widget.count() > 1:
963 msg = "Close all tabs, stop all kernels, and Quit?"
964 msg = "Close all tabs, stop all kernels, and Quit?"
964 else:
965 else:
965 msg = "Close console, stop kernel, and Quit?"
966 msg = "Close console, stop kernel, and Quit?"
966 info = "Kernels not started here (e.g. notebooks) will be left alone."
967 info = "Kernels not started here (e.g. notebooks) will be left alone."
967 closeall = QtGui.QPushButton("&Quit", self)
968 closeall = QtGui.QPushButton("&Quit", self)
968 closeall.setShortcut('Q')
969 closeall.setShortcut('Q')
969 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
970 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
970 title, msg)
971 title, msg)
971 box.setInformativeText(info)
972 box.setInformativeText(info)
972 box.addButton(cancel)
973 box.addButton(cancel)
973 box.addButton(closeall, QtGui.QMessageBox.YesRole)
974 box.addButton(closeall, QtGui.QMessageBox.YesRole)
974 box.setDefaultButton(closeall)
975 box.setDefaultButton(closeall)
975 box.setEscapeButton(cancel)
976 box.setEscapeButton(cancel)
976 pixmap = QtGui.QPixmap(self._app.icon.pixmap(QtCore.QSize(64,64)))
977 pixmap = QtGui.QPixmap(self._app.icon.pixmap(QtCore.QSize(64,64)))
977 box.setIconPixmap(pixmap)
978 box.setIconPixmap(pixmap)
978 reply = box.exec_()
979 reply = box.exec_()
979 else:
980 else:
980 reply = okay
981 reply = okay
981
982
982 if reply == cancel:
983 if reply == cancel:
983 event.ignore()
984 event.ignore()
984 return
985 return
985 if reply == okay:
986 if reply == okay:
986 while self.tab_widget.count() >= 1:
987 while self.tab_widget.count() >= 1:
987 # prevent further confirmations:
988 # prevent further confirmations:
988 widget = self.active_frontend
989 widget = self.active_frontend
989 widget._confirm_exit = False
990 widget._confirm_exit = False
990 self.close_tab(widget)
991 self.close_tab(widget)
991 event.accept()
992 event.accept()
992
993
General Comments 0
You need to be logged in to leave comments. Login now