##// END OF EJS Templates
update qtconsole with user_expressions changes...
MinRK -
Show More
@@ -1,686 +1,693 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 os
19 import os
19 import re
20 import re
20 import sys
21 import sys
21 import types
22 import types
22 from getopt import getopt, GetoptError
23 from getopt import getopt, GetoptError
23
24
24 # Our own
25 # Our own
25 from IPython.config.configurable import Configurable
26 from IPython.config.configurable import Configurable
26 from IPython.core import oinspect
27 from IPython.core import oinspect
27 from IPython.core.error import UsageError
28 from IPython.core.error import UsageError
28 from IPython.core.inputsplitter import ESC_MAGIC, ESC_MAGIC2
29 from IPython.core.inputsplitter import ESC_MAGIC, ESC_MAGIC2
29 from IPython.external.decorator import decorator
30 from IPython.external.decorator import decorator
30 from IPython.utils.ipstruct import Struct
31 from IPython.utils.ipstruct import Struct
31 from IPython.utils.process import arg_split
32 from IPython.utils.process import arg_split
32 from IPython.utils.text import dedent
33 from IPython.utils.text import dedent
33 from IPython.utils.traitlets import Bool, Dict, Instance, MetaHasTraits
34 from IPython.utils.traitlets import Bool, Dict, Instance, MetaHasTraits
34 from IPython.utils.warn import error
35 from IPython.utils.warn import error
35
36
36 #-----------------------------------------------------------------------------
37 #-----------------------------------------------------------------------------
37 # Globals
38 # Globals
38 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
39
40
40 # 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
41 # pass information between the @line/cell_magic method decorators and the
42 # pass information between the @line/cell_magic method decorators and the
42 # @magics_class class decorator, because the method decorators have no
43 # @magics_class class decorator, because the method decorators have no
43 # access to the class when they run. See for more details:
44 # access to the class when they run. See for more details:
44 # 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
45
46
46 magics = dict(line={}, cell={})
47 magics = dict(line={}, cell={})
47
48
48 magic_kinds = ('line', 'cell')
49 magic_kinds = ('line', 'cell')
49 magic_spec = ('line', 'cell', 'line_cell')
50 magic_spec = ('line', 'cell', 'line_cell')
50 magic_escapes = dict(line=ESC_MAGIC, cell=ESC_MAGIC2)
51 magic_escapes = dict(line=ESC_MAGIC, cell=ESC_MAGIC2)
51
52
52 #-----------------------------------------------------------------------------
53 #-----------------------------------------------------------------------------
53 # Utility classes and functions
54 # Utility classes and functions
54 #-----------------------------------------------------------------------------
55 #-----------------------------------------------------------------------------
55
56
56 class Bunch: pass
57 class Bunch: pass
57
58
58
59
59 def on_off(tag):
60 def on_off(tag):
60 """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."""
61 return ['OFF','ON'][tag]
62 return ['OFF','ON'][tag]
62
63
63
64
64 def compress_dhist(dh):
65 def compress_dhist(dh):
65 """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.
66
67
67 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
68 removal of duplicates.
69 removal of duplicates.
69 """
70 """
70 head, tail = dh[:-10], dh[-10:]
71 head, tail = dh[:-10], dh[-10:]
71
72
72 newhead = []
73 newhead = []
73 done = set()
74 done = set()
74 for h in head:
75 for h in head:
75 if h in done:
76 if h in done:
76 continue
77 continue
77 newhead.append(h)
78 newhead.append(h)
78 done.add(h)
79 done.add(h)
79
80
80 return newhead + tail
81 return newhead + tail
81
82
82
83
83 def needs_local_scope(func):
84 def needs_local_scope(func):
84 """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."""
85 func.needs_local_scope = True
86 func.needs_local_scope = True
86 return func
87 return func
87
88
88 #-----------------------------------------------------------------------------
89 #-----------------------------------------------------------------------------
89 # Class and method decorators for registering magics
90 # Class and method decorators for registering magics
90 #-----------------------------------------------------------------------------
91 #-----------------------------------------------------------------------------
91
92
92 def magics_class(cls):
93 def magics_class(cls):
93 """Class decorator for all subclasses of the main Magics class.
94 """Class decorator for all subclasses of the main Magics class.
94
95
95 Any class that subclasses Magics *must* also apply this decorator, to
96 Any class that subclasses Magics *must* also apply this decorator, to
96 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
97 get correctly registered in the class instance. This is necessary because
98 get correctly registered in the class instance. This is necessary because
98 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
99 temporarily store their information into a module global. Application of
100 temporarily store their information into a module global. Application of
100 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
101 clears the global.
102 clears the global.
102
103
103 Obviously, this mechanism is not thread-safe, which means that the
104 Obviously, this mechanism is not thread-safe, which means that the
104 *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
105 context. Instantiation of the classes has no restrictions. Given that
106 context. Instantiation of the classes has no restrictions. Given that
106 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
107 application code becomes active, in practice this should not pose any
108 application code becomes active, in practice this should not pose any
108 problems.
109 problems.
109 """
110 """
110 cls.registered = True
111 cls.registered = True
111 cls.magics = dict(line = magics['line'],
112 cls.magics = dict(line = magics['line'],
112 cell = magics['cell'])
113 cell = magics['cell'])
113 magics['line'] = {}
114 magics['line'] = {}
114 magics['cell'] = {}
115 magics['cell'] = {}
115 return cls
116 return cls
116
117
117
118
118 def record_magic(dct, magic_kind, magic_name, func):
119 def record_magic(dct, magic_kind, magic_name, func):
119 """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.
120
121
121 Parameters
122 Parameters
122 ----------
123 ----------
123 dct : dict
124 dct : dict
124 A dictionary with 'line' and 'cell' subdicts.
125 A dictionary with 'line' and 'cell' subdicts.
125
126
126 magic_kind : str
127 magic_kind : str
127 Kind of magic to be stored.
128 Kind of magic to be stored.
128
129
129 magic_name : str
130 magic_name : str
130 Key to store the magic as.
131 Key to store the magic as.
131
132
132 func : function
133 func : function
133 Callable object to store.
134 Callable object to store.
134 """
135 """
135 if magic_kind == 'line_cell':
136 if magic_kind == 'line_cell':
136 dct['line'][magic_name] = dct['cell'][magic_name] = func
137 dct['line'][magic_name] = dct['cell'][magic_name] = func
137 else:
138 else:
138 dct[magic_kind][magic_name] = func
139 dct[magic_kind][magic_name] = func
139
140
140
141
141 def validate_type(magic_kind):
142 def validate_type(magic_kind):
142 """Ensure that the given magic_kind is valid.
143 """Ensure that the given magic_kind is valid.
143
144
144 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
145 in the global `magic_spec`), raise ValueError otherwise.
146 in the global `magic_spec`), raise ValueError otherwise.
146 """
147 """
147 if magic_kind not in magic_spec:
148 if magic_kind not in magic_spec:
148 raise ValueError('magic_kind must be one of %s, %s given' %
149 raise ValueError('magic_kind must be one of %s, %s given' %
149 magic_kinds, magic_kind)
150 magic_kinds, magic_kind)
150
151
151
152
152 # 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
153 # 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
154 # templates below.
155 # templates below.
155 _docstring_template = \
156 _docstring_template = \
156 """Decorate the given {0} as {1} magic.
157 """Decorate the given {0} as {1} magic.
157
158
158 The decorator can be used with or without arguments, as follows.
159 The decorator can be used with or without arguments, as follows.
159
160
160 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
161 decorated::
162 decorated::
162
163
163 @deco
164 @deco
164 def foo(...)
165 def foo(...)
165
166
166 will create a {1} magic named `foo`.
167 will create a {1} magic named `foo`.
167
168
168 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
169 resulting magic::
170 resulting magic::
170
171
171 @deco('bar')
172 @deco('bar')
172 def foo(...)
173 def foo(...)
173
174
174 will create a {1} magic named `bar`.
175 will create a {1} magic named `bar`.
175 """
176 """
176
177
177 # These two are decorator factories. While they are conceptually very similar,
178 # These two are decorator factories. While they are conceptually very similar,
178 # 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
179 # written as completely standalone functions rather than trying to share code
180 # written as completely standalone functions rather than trying to share code
180 # and make a single one with convoluted logic.
181 # and make a single one with convoluted logic.
181
182
182 def _method_magic_marker(magic_kind):
183 def _method_magic_marker(magic_kind):
183 """Decorator factory for methods in Magics subclasses.
184 """Decorator factory for methods in Magics subclasses.
184 """
185 """
185
186
186 validate_type(magic_kind)
187 validate_type(magic_kind)
187
188
188 # 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,
189 # but it's overkill for just that one bit of state.
190 # but it's overkill for just that one bit of state.
190 def magic_deco(arg):
191 def magic_deco(arg):
191 call = lambda f, *a, **k: f(*a, **k)
192 call = lambda f, *a, **k: f(*a, **k)
192
193
193 if callable(arg):
194 if callable(arg):
194 # "Naked" decorator call (just @foo, no args)
195 # "Naked" decorator call (just @foo, no args)
195 func = arg
196 func = arg
196 name = func.func_name
197 name = func.func_name
197 retval = decorator(call, func)
198 retval = decorator(call, func)
198 record_magic(magics, magic_kind, name, name)
199 record_magic(magics, magic_kind, name, name)
199 elif isinstance(arg, basestring):
200 elif isinstance(arg, basestring):
200 # Decorator called with arguments (@foo('bar'))
201 # Decorator called with arguments (@foo('bar'))
201 name = arg
202 name = arg
202 def mark(func, *a, **kw):
203 def mark(func, *a, **kw):
203 record_magic(magics, magic_kind, name, func.func_name)
204 record_magic(magics, magic_kind, name, func.func_name)
204 return decorator(call, func)
205 return decorator(call, func)
205 retval = mark
206 retval = mark
206 else:
207 else:
207 raise TypeError("Decorator can only be called with "
208 raise TypeError("Decorator can only be called with "
208 "string or function")
209 "string or function")
209 return retval
210 return retval
210
211
211 # Ensure the resulting decorator has a usable docstring
212 # Ensure the resulting decorator has a usable docstring
212 magic_deco.__doc__ = _docstring_template.format('method', magic_kind)
213 magic_deco.__doc__ = _docstring_template.format('method', magic_kind)
213 return magic_deco
214 return magic_deco
214
215
215
216
216 def _function_magic_marker(magic_kind):
217 def _function_magic_marker(magic_kind):
217 """Decorator factory for standalone functions.
218 """Decorator factory for standalone functions.
218 """
219 """
219 validate_type(magic_kind)
220 validate_type(magic_kind)
220
221
221 # 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,
222 # but it's overkill for just that one bit of state.
223 # but it's overkill for just that one bit of state.
223 def magic_deco(arg):
224 def magic_deco(arg):
224 call = lambda f, *a, **k: f(*a, **k)
225 call = lambda f, *a, **k: f(*a, **k)
225
226
226 # Find get_ipython() in the caller's namespace
227 # Find get_ipython() in the caller's namespace
227 caller = sys._getframe(1)
228 caller = sys._getframe(1)
228 for ns in ['f_locals', 'f_globals', 'f_builtins']:
229 for ns in ['f_locals', 'f_globals', 'f_builtins']:
229 get_ipython = getattr(caller, ns).get('get_ipython')
230 get_ipython = getattr(caller, ns).get('get_ipython')
230 if get_ipython is not None:
231 if get_ipython is not None:
231 break
232 break
232 else:
233 else:
233 raise NameError('Decorator can only run in context where '
234 raise NameError('Decorator can only run in context where '
234 '`get_ipython` exists')
235 '`get_ipython` exists')
235
236
236 ip = get_ipython()
237 ip = get_ipython()
237
238
238 if callable(arg):
239 if callable(arg):
239 # "Naked" decorator call (just @foo, no args)
240 # "Naked" decorator call (just @foo, no args)
240 func = arg
241 func = arg
241 name = func.func_name
242 name = func.func_name
242 ip.register_magic_function(func, magic_kind, name)
243 ip.register_magic_function(func, magic_kind, name)
243 retval = decorator(call, func)
244 retval = decorator(call, func)
244 elif isinstance(arg, basestring):
245 elif isinstance(arg, basestring):
245 # Decorator called with arguments (@foo('bar'))
246 # Decorator called with arguments (@foo('bar'))
246 name = arg
247 name = arg
247 def mark(func, *a, **kw):
248 def mark(func, *a, **kw):
248 ip.register_magic_function(func, magic_kind, name)
249 ip.register_magic_function(func, magic_kind, name)
249 return decorator(call, func)
250 return decorator(call, func)
250 retval = mark
251 retval = mark
251 else:
252 else:
252 raise TypeError("Decorator can only be called with "
253 raise TypeError("Decorator can only be called with "
253 "string or function")
254 "string or function")
254 return retval
255 return retval
255
256
256 # Ensure the resulting decorator has a usable docstring
257 # Ensure the resulting decorator has a usable docstring
257 ds = _docstring_template.format('function', magic_kind)
258 ds = _docstring_template.format('function', magic_kind)
258
259
259 ds += dedent("""
260 ds += dedent("""
260 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
261 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
262 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
263 IPython configuration file itself, which is executed before IPython is
264 IPython configuration file itself, which is executed before IPython is
264 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
265 your configuration profile will be OK in this sense.
266 your configuration profile will be OK in this sense.
266 """)
267 """)
267
268
268 magic_deco.__doc__ = ds
269 magic_deco.__doc__ = ds
269 return magic_deco
270 return magic_deco
270
271
271
272
272 # Create the actual decorators for public use
273 # Create the actual decorators for public use
273
274
274 # These three are used to decorate methods in class definitions
275 # These three are used to decorate methods in class definitions
275 line_magic = _method_magic_marker('line')
276 line_magic = _method_magic_marker('line')
276 cell_magic = _method_magic_marker('cell')
277 cell_magic = _method_magic_marker('cell')
277 line_cell_magic = _method_magic_marker('line_cell')
278 line_cell_magic = _method_magic_marker('line_cell')
278
279
279 # These three decorate standalone functions and perform the decoration
280 # These three decorate standalone functions and perform the decoration
280 # immediately. They can only run where get_ipython() works
281 # immediately. They can only run where get_ipython() works
281 register_line_magic = _function_magic_marker('line')
282 register_line_magic = _function_magic_marker('line')
282 register_cell_magic = _function_magic_marker('cell')
283 register_cell_magic = _function_magic_marker('cell')
283 register_line_cell_magic = _function_magic_marker('line_cell')
284 register_line_cell_magic = _function_magic_marker('line_cell')
284
285
285 #-----------------------------------------------------------------------------
286 #-----------------------------------------------------------------------------
286 # Core Magic classes
287 # Core Magic classes
287 #-----------------------------------------------------------------------------
288 #-----------------------------------------------------------------------------
288
289
289 class MagicsManager(Configurable):
290 class MagicsManager(Configurable):
290 """Object that handles all magic-related functionality for IPython.
291 """Object that handles all magic-related functionality for IPython.
291 """
292 """
292 # Non-configurable class attributes
293 # Non-configurable class attributes
293
294
294 # 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
295 # 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
296 # magic function dispatch
297 # magic function dispatch
297 magics = Dict
298 magics = Dict
298
299
299 # 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.
300 registry = Dict
301 registry = Dict
301
302
302 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
303 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
303
304
304 auto_magic = Bool(True, config=True, help=
305 auto_magic = Bool(True, config=True, help=
305 "Automatically call line magics without requiring explicit % prefix")
306 "Automatically call line magics without requiring explicit % prefix")
306
307
307 def _auto_magic_changed(self, name, value):
308 def _auto_magic_changed(self, name, value):
308 self.shell.automagic = value
309 self.shell.automagic = value
309
310
310 _auto_status = [
311 _auto_status = [
311 'Automagic is OFF, % prefix IS needed for line magics.',
312 'Automagic is OFF, % prefix IS needed for line magics.',
312 'Automagic is ON, % prefix IS NOT needed for line magics.']
313 'Automagic is ON, % prefix IS NOT needed for line magics.']
313
314
314 user_magics = Instance('IPython.core.magics.UserMagics')
315 user_magics = Instance('IPython.core.magics.UserMagics')
315
316
316 def __init__(self, shell=None, config=None, user_magics=None, **traits):
317 def __init__(self, shell=None, config=None, user_magics=None, **traits):
317
318
318 super(MagicsManager, self).__init__(shell=shell, config=config,
319 super(MagicsManager, self).__init__(shell=shell, config=config,
319 user_magics=user_magics, **traits)
320 user_magics=user_magics, **traits)
320 self.magics = dict(line={}, cell={})
321 self.magics = dict(line={}, cell={})
321 # 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*
322 # registered magic containers can be found there.
323 # registered magic containers can be found there.
323 self.registry[user_magics.__class__.__name__] = user_magics
324 self.registry[user_magics.__class__.__name__] = user_magics
324
325
325 def auto_status(self):
326 def auto_status(self):
326 """Return descriptive string with automagic status."""
327 """Return descriptive string with automagic status."""
327 return self._auto_status[self.auto_magic]
328 return self._auto_status[self.auto_magic]
328
329
329 def lsmagic_info(self):
330 def lsmagic_info(self):
331 """Return the magics as a list of dicts"""
330 magic_list = []
332 magic_list = []
331 for m_type in self.magics :
333 for m_type in self.magics:
332 for m_name,mgc in self.magics[m_type].items():
334 for m_name,mgc in self.magics[m_type].items():
333 try :
335 try:
334 magic_list.append({'name':m_name,'type':m_type,'class':mgc.im_class.__name__})
336 magic_list.append({'name':m_name,'type':m_type,'class':mgc.im_class.__name__})
335 except AttributeError :
337 except AttributeError:
336 magic_list.append({'name':m_name,'type':m_type,'class':'Other'})
338 magic_list.append({'name':m_name,'type':m_type,'class':'Other'})
337 return magic_list
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()))
338
345
339 def lsmagic(self):
346 def lsmagic(self):
340 """Return a dict of currently available magic functions.
347 """Return a dict of currently available magic functions.
341
348
342 The return dict has the keys 'line' and 'cell', corresponding to the
349 The return dict has the keys 'line' and 'cell', corresponding to the
343 two types of magics we support. Each value is a list of names.
350 two types of magics we support. Each value is a list of names.
344 """
351 """
345 return self.magics
352 return self.magics
346
353
347 def lsmagic_docs(self, brief=False, missing=''):
354 def lsmagic_docs(self, brief=False, missing=''):
348 """Return dict of documentation of magic functions.
355 """Return dict of documentation of magic functions.
349
356
350 The return dict has the keys 'line' and 'cell', corresponding to the
357 The return dict has the keys 'line' and 'cell', corresponding to the
351 two types of magics we support. Each value is a dict keyed by magic
358 two types of magics we support. Each value is a dict keyed by magic
352 name whose value is the function docstring. If a docstring is
359 name whose value is the function docstring. If a docstring is
353 unavailable, the value of `missing` is used instead.
360 unavailable, the value of `missing` is used instead.
354
361
355 If brief is True, only the first line of each docstring will be returned.
362 If brief is True, only the first line of each docstring will be returned.
356 """
363 """
357 docs = {}
364 docs = {}
358 for m_type in self.magics:
365 for m_type in self.magics:
359 m_docs = {}
366 m_docs = {}
360 for m_name, m_func in self.magics[m_type].iteritems():
367 for m_name, m_func in self.magics[m_type].iteritems():
361 if m_func.__doc__:
368 if m_func.__doc__:
362 if brief:
369 if brief:
363 m_docs[m_name] = m_func.__doc__.split('\n', 1)[0]
370 m_docs[m_name] = m_func.__doc__.split('\n', 1)[0]
364 else:
371 else:
365 m_docs[m_name] = m_func.__doc__.rstrip()
372 m_docs[m_name] = m_func.__doc__.rstrip()
366 else:
373 else:
367 m_docs[m_name] = missing
374 m_docs[m_name] = missing
368 docs[m_type] = m_docs
375 docs[m_type] = m_docs
369 return docs
376 return docs
370
377
371 def register(self, *magic_objects):
378 def register(self, *magic_objects):
372 """Register one or more instances of Magics.
379 """Register one or more instances of Magics.
373
380
374 Take one or more classes or instances of classes that subclass the main
381 Take one or more classes or instances of classes that subclass the main
375 `core.Magic` class, and register them with IPython to use the magic
382 `core.Magic` class, and register them with IPython to use the magic
376 functions they provide. The registration process will then ensure that
383 functions they provide. The registration process will then ensure that
377 any methods that have decorated to provide line and/or cell magics will
384 any methods that have decorated to provide line and/or cell magics will
378 be recognized with the `%x`/`%%x` syntax as a line/cell magic
385 be recognized with the `%x`/`%%x` syntax as a line/cell magic
379 respectively.
386 respectively.
380
387
381 If classes are given, they will be instantiated with the default
388 If classes are given, they will be instantiated with the default
382 constructor. If your classes need a custom constructor, you should
389 constructor. If your classes need a custom constructor, you should
383 instanitate them first and pass the instance.
390 instanitate them first and pass the instance.
384
391
385 The provided arguments can be an arbitrary mix of classes and instances.
392 The provided arguments can be an arbitrary mix of classes and instances.
386
393
387 Parameters
394 Parameters
388 ----------
395 ----------
389 magic_objects : one or more classes or instances
396 magic_objects : one or more classes or instances
390 """
397 """
391 # Start by validating them to ensure they have all had their magic
398 # Start by validating them to ensure they have all had their magic
392 # methods registered at the instance level
399 # methods registered at the instance level
393 for m in magic_objects:
400 for m in magic_objects:
394 if not m.registered:
401 if not m.registered:
395 raise ValueError("Class of magics %r was constructed without "
402 raise ValueError("Class of magics %r was constructed without "
396 "the @register_magics class decorator")
403 "the @register_magics class decorator")
397 if type(m) in (type, MetaHasTraits):
404 if type(m) in (type, MetaHasTraits):
398 # If we're given an uninstantiated class
405 # If we're given an uninstantiated class
399 m = m(shell=self.shell)
406 m = m(shell=self.shell)
400
407
401 # Now that we have an instance, we can register it and update the
408 # Now that we have an instance, we can register it and update the
402 # table of callables
409 # table of callables
403 self.registry[m.__class__.__name__] = m
410 self.registry[m.__class__.__name__] = m
404 for mtype in magic_kinds:
411 for mtype in magic_kinds:
405 self.magics[mtype].update(m.magics[mtype])
412 self.magics[mtype].update(m.magics[mtype])
406
413
407 def register_function(self, func, magic_kind='line', magic_name=None):
414 def register_function(self, func, magic_kind='line', magic_name=None):
408 """Expose a standalone function as magic function for IPython.
415 """Expose a standalone function as magic function for IPython.
409
416
410 This will create an IPython magic (line, cell or both) from a
417 This will create an IPython magic (line, cell or both) from a
411 standalone function. The functions should have the following
418 standalone function. The functions should have the following
412 signatures:
419 signatures:
413
420
414 * For line magics: `def f(line)`
421 * For line magics: `def f(line)`
415 * For cell magics: `def f(line, cell)`
422 * For cell magics: `def f(line, cell)`
416 * For a function that does both: `def f(line, cell=None)`
423 * For a function that does both: `def f(line, cell=None)`
417
424
418 In the latter case, the function will be called with `cell==None` when
425 In the latter case, the function will be called with `cell==None` when
419 invoked as `%f`, and with cell as a string when invoked as `%%f`.
426 invoked as `%f`, and with cell as a string when invoked as `%%f`.
420
427
421 Parameters
428 Parameters
422 ----------
429 ----------
423 func : callable
430 func : callable
424 Function to be registered as a magic.
431 Function to be registered as a magic.
425
432
426 magic_kind : str
433 magic_kind : str
427 Kind of magic, one of 'line', 'cell' or 'line_cell'
434 Kind of magic, one of 'line', 'cell' or 'line_cell'
428
435
429 magic_name : optional str
436 magic_name : optional str
430 If given, the name the magic will have in the IPython namespace. By
437 If given, the name the magic will have in the IPython namespace. By
431 default, the name of the function itself is used.
438 default, the name of the function itself is used.
432 """
439 """
433
440
434 # Create the new method in the user_magics and register it in the
441 # Create the new method in the user_magics and register it in the
435 # global table
442 # global table
436 validate_type(magic_kind)
443 validate_type(magic_kind)
437 magic_name = func.func_name if magic_name is None else magic_name
444 magic_name = func.func_name if magic_name is None else magic_name
438 setattr(self.user_magics, magic_name, func)
445 setattr(self.user_magics, magic_name, func)
439 record_magic(self.magics, magic_kind, magic_name, func)
446 record_magic(self.magics, magic_kind, magic_name, func)
440
447
441 def define_magic(self, name, func):
448 def define_magic(self, name, func):
442 """[Deprecated] Expose own function as magic function for IPython.
449 """[Deprecated] Expose own function as magic function for IPython.
443
450
444 Example::
451 Example::
445
452
446 def foo_impl(self, parameter_s=''):
453 def foo_impl(self, parameter_s=''):
447 'My very own magic!. (Use docstrings, IPython reads them).'
454 'My very own magic!. (Use docstrings, IPython reads them).'
448 print 'Magic function. Passed parameter is between < >:'
455 print 'Magic function. Passed parameter is between < >:'
449 print '<%s>' % parameter_s
456 print '<%s>' % parameter_s
450 print 'The self object is:', self
457 print 'The self object is:', self
451
458
452 ip.define_magic('foo',foo_impl)
459 ip.define_magic('foo',foo_impl)
453 """
460 """
454 meth = types.MethodType(func, self.user_magics)
461 meth = types.MethodType(func, self.user_magics)
455 setattr(self.user_magics, name, meth)
462 setattr(self.user_magics, name, meth)
456 record_magic(self.magics, 'line', name, meth)
463 record_magic(self.magics, 'line', name, meth)
457
464
458 def register_alias(self, alias_name, magic_name, magic_kind='line'):
465 def register_alias(self, alias_name, magic_name, magic_kind='line'):
459 """Register an alias to a magic function.
466 """Register an alias to a magic function.
460
467
461 The alias is an instance of :class:`MagicAlias`, which holds the
468 The alias is an instance of :class:`MagicAlias`, which holds the
462 name and kind of the magic it should call. Binding is done at
469 name and kind of the magic it should call. Binding is done at
463 call time, so if the underlying magic function is changed the alias
470 call time, so if the underlying magic function is changed the alias
464 will call the new function.
471 will call the new function.
465
472
466 Parameters
473 Parameters
467 ----------
474 ----------
468 alias_name : str
475 alias_name : str
469 The name of the magic to be registered.
476 The name of the magic to be registered.
470
477
471 magic_name : str
478 magic_name : str
472 The name of an existing magic.
479 The name of an existing magic.
473
480
474 magic_kind : str
481 magic_kind : str
475 Kind of magic, one of 'line' or 'cell'
482 Kind of magic, one of 'line' or 'cell'
476 """
483 """
477
484
478 # `validate_type` is too permissive, as it allows 'line_cell'
485 # `validate_type` is too permissive, as it allows 'line_cell'
479 # which we do not handle.
486 # which we do not handle.
480 if magic_kind not in magic_kinds:
487 if magic_kind not in magic_kinds:
481 raise ValueError('magic_kind must be one of %s, %s given' %
488 raise ValueError('magic_kind must be one of %s, %s given' %
482 magic_kinds, magic_kind)
489 magic_kinds, magic_kind)
483
490
484 alias = MagicAlias(self.shell, magic_name, magic_kind)
491 alias = MagicAlias(self.shell, magic_name, magic_kind)
485 setattr(self.user_magics, alias_name, alias)
492 setattr(self.user_magics, alias_name, alias)
486 record_magic(self.magics, magic_kind, alias_name, alias)
493 record_magic(self.magics, magic_kind, alias_name, alias)
487
494
488 # Key base class that provides the central functionality for magics.
495 # Key base class that provides the central functionality for magics.
489
496
490 class Magics(object):
497 class Magics(object):
491 """Base class for implementing magic functions.
498 """Base class for implementing magic functions.
492
499
493 Shell functions which can be reached as %function_name. All magic
500 Shell functions which can be reached as %function_name. All magic
494 functions should accept a string, which they can parse for their own
501 functions should accept a string, which they can parse for their own
495 needs. This can make some functions easier to type, eg `%cd ../`
502 needs. This can make some functions easier to type, eg `%cd ../`
496 vs. `%cd("../")`
503 vs. `%cd("../")`
497
504
498 Classes providing magic functions need to subclass this class, and they
505 Classes providing magic functions need to subclass this class, and they
499 MUST:
506 MUST:
500
507
501 - Use the method decorators `@line_magic` and `@cell_magic` to decorate
508 - Use the method decorators `@line_magic` and `@cell_magic` to decorate
502 individual methods as magic functions, AND
509 individual methods as magic functions, AND
503
510
504 - Use the class decorator `@magics_class` to ensure that the magic
511 - Use the class decorator `@magics_class` to ensure that the magic
505 methods are properly registered at the instance level upon instance
512 methods are properly registered at the instance level upon instance
506 initialization.
513 initialization.
507
514
508 See :mod:`magic_functions` for examples of actual implementation classes.
515 See :mod:`magic_functions` for examples of actual implementation classes.
509 """
516 """
510 # Dict holding all command-line options for each magic.
517 # Dict holding all command-line options for each magic.
511 options_table = None
518 options_table = None
512 # Dict for the mapping of magic names to methods, set by class decorator
519 # Dict for the mapping of magic names to methods, set by class decorator
513 magics = None
520 magics = None
514 # Flag to check that the class decorator was properly applied
521 # Flag to check that the class decorator was properly applied
515 registered = False
522 registered = False
516 # Instance of IPython shell
523 # Instance of IPython shell
517 shell = None
524 shell = None
518
525
519 def __init__(self, shell):
526 def __init__(self, shell):
520 if not(self.__class__.registered):
527 if not(self.__class__.registered):
521 raise ValueError('Magics subclass without registration - '
528 raise ValueError('Magics subclass without registration - '
522 'did you forget to apply @magics_class?')
529 'did you forget to apply @magics_class?')
523 self.shell = shell
530 self.shell = shell
524 self.options_table = {}
531 self.options_table = {}
525 # The method decorators are run when the instance doesn't exist yet, so
532 # The method decorators are run when the instance doesn't exist yet, so
526 # they can only record the names of the methods they are supposed to
533 # they can only record the names of the methods they are supposed to
527 # grab. Only now, that the instance exists, can we create the proper
534 # grab. Only now, that the instance exists, can we create the proper
528 # mapping to bound methods. So we read the info off the original names
535 # mapping to bound methods. So we read the info off the original names
529 # table and replace each method name by the actual bound method.
536 # table and replace each method name by the actual bound method.
530 # But we mustn't clobber the *class* mapping, in case of multiple instances.
537 # But we mustn't clobber the *class* mapping, in case of multiple instances.
531 class_magics = self.magics
538 class_magics = self.magics
532 self.magics = {}
539 self.magics = {}
533 for mtype in magic_kinds:
540 for mtype in magic_kinds:
534 tab = self.magics[mtype] = {}
541 tab = self.magics[mtype] = {}
535 cls_tab = class_magics[mtype]
542 cls_tab = class_magics[mtype]
536 for magic_name, meth_name in cls_tab.iteritems():
543 for magic_name, meth_name in cls_tab.iteritems():
537 if isinstance(meth_name, basestring):
544 if isinstance(meth_name, basestring):
538 # it's a method name, grab it
545 # it's a method name, grab it
539 tab[magic_name] = getattr(self, meth_name)
546 tab[magic_name] = getattr(self, meth_name)
540 else:
547 else:
541 # it's the real thing
548 # it's the real thing
542 tab[magic_name] = meth_name
549 tab[magic_name] = meth_name
543
550
544 def arg_err(self,func):
551 def arg_err(self,func):
545 """Print docstring if incorrect arguments were passed"""
552 """Print docstring if incorrect arguments were passed"""
546 print 'Error in arguments:'
553 print 'Error in arguments:'
547 print oinspect.getdoc(func)
554 print oinspect.getdoc(func)
548
555
549 def format_latex(self, strng):
556 def format_latex(self, strng):
550 """Format a string for latex inclusion."""
557 """Format a string for latex inclusion."""
551
558
552 # Characters that need to be escaped for latex:
559 # Characters that need to be escaped for latex:
553 escape_re = re.compile(r'(%|_|\$|#|&)',re.MULTILINE)
560 escape_re = re.compile(r'(%|_|\$|#|&)',re.MULTILINE)
554 # Magic command names as headers:
561 # Magic command names as headers:
555 cmd_name_re = re.compile(r'^(%s.*?):' % ESC_MAGIC,
562 cmd_name_re = re.compile(r'^(%s.*?):' % ESC_MAGIC,
556 re.MULTILINE)
563 re.MULTILINE)
557 # Magic commands
564 # Magic commands
558 cmd_re = re.compile(r'(?P<cmd>%s.+?\b)(?!\}\}:)' % ESC_MAGIC,
565 cmd_re = re.compile(r'(?P<cmd>%s.+?\b)(?!\}\}:)' % ESC_MAGIC,
559 re.MULTILINE)
566 re.MULTILINE)
560 # Paragraph continue
567 # Paragraph continue
561 par_re = re.compile(r'\\$',re.MULTILINE)
568 par_re = re.compile(r'\\$',re.MULTILINE)
562
569
563 # The "\n" symbol
570 # The "\n" symbol
564 newline_re = re.compile(r'\\n')
571 newline_re = re.compile(r'\\n')
565
572
566 # Now build the string for output:
573 # Now build the string for output:
567 #strng = cmd_name_re.sub(r'\n\\texttt{\\textsl{\\large \1}}:',strng)
574 #strng = cmd_name_re.sub(r'\n\\texttt{\\textsl{\\large \1}}:',strng)
568 strng = cmd_name_re.sub(r'\n\\bigskip\n\\texttt{\\textbf{ \1}}:',
575 strng = cmd_name_re.sub(r'\n\\bigskip\n\\texttt{\\textbf{ \1}}:',
569 strng)
576 strng)
570 strng = cmd_re.sub(r'\\texttt{\g<cmd>}',strng)
577 strng = cmd_re.sub(r'\\texttt{\g<cmd>}',strng)
571 strng = par_re.sub(r'\\\\',strng)
578 strng = par_re.sub(r'\\\\',strng)
572 strng = escape_re.sub(r'\\\1',strng)
579 strng = escape_re.sub(r'\\\1',strng)
573 strng = newline_re.sub(r'\\textbackslash{}n',strng)
580 strng = newline_re.sub(r'\\textbackslash{}n',strng)
574 return strng
581 return strng
575
582
576 def parse_options(self, arg_str, opt_str, *long_opts, **kw):
583 def parse_options(self, arg_str, opt_str, *long_opts, **kw):
577 """Parse options passed to an argument string.
584 """Parse options passed to an argument string.
578
585
579 The interface is similar to that of getopt(), but it returns back a
586 The interface is similar to that of getopt(), but it returns back a
580 Struct with the options as keys and the stripped argument string still
587 Struct with the options as keys and the stripped argument string still
581 as a string.
588 as a string.
582
589
583 arg_str is quoted as a true sys.argv vector by using shlex.split.
590 arg_str is quoted as a true sys.argv vector by using shlex.split.
584 This allows us to easily expand variables, glob files, quote
591 This allows us to easily expand variables, glob files, quote
585 arguments, etc.
592 arguments, etc.
586
593
587 Options:
594 Options:
588 -mode: default 'string'. If given as 'list', the argument string is
595 -mode: default 'string'. If given as 'list', the argument string is
589 returned as a list (split on whitespace) instead of a string.
596 returned as a list (split on whitespace) instead of a string.
590
597
591 -list_all: put all option values in lists. Normally only options
598 -list_all: put all option values in lists. Normally only options
592 appearing more than once are put in a list.
599 appearing more than once are put in a list.
593
600
594 -posix (True): whether to split the input line in POSIX mode or not,
601 -posix (True): whether to split the input line in POSIX mode or not,
595 as per the conventions outlined in the shlex module from the
602 as per the conventions outlined in the shlex module from the
596 standard library."""
603 standard library."""
597
604
598 # inject default options at the beginning of the input line
605 # inject default options at the beginning of the input line
599 caller = sys._getframe(1).f_code.co_name
606 caller = sys._getframe(1).f_code.co_name
600 arg_str = '%s %s' % (self.options_table.get(caller,''),arg_str)
607 arg_str = '%s %s' % (self.options_table.get(caller,''),arg_str)
601
608
602 mode = kw.get('mode','string')
609 mode = kw.get('mode','string')
603 if mode not in ['string','list']:
610 if mode not in ['string','list']:
604 raise ValueError('incorrect mode given: %s' % mode)
611 raise ValueError('incorrect mode given: %s' % mode)
605 # Get options
612 # Get options
606 list_all = kw.get('list_all',0)
613 list_all = kw.get('list_all',0)
607 posix = kw.get('posix', os.name == 'posix')
614 posix = kw.get('posix', os.name == 'posix')
608 strict = kw.get('strict', True)
615 strict = kw.get('strict', True)
609
616
610 # Check if we have more than one argument to warrant extra processing:
617 # Check if we have more than one argument to warrant extra processing:
611 odict = {} # Dictionary with options
618 odict = {} # Dictionary with options
612 args = arg_str.split()
619 args = arg_str.split()
613 if len(args) >= 1:
620 if len(args) >= 1:
614 # If the list of inputs only has 0 or 1 thing in it, there's no
621 # If the list of inputs only has 0 or 1 thing in it, there's no
615 # need to look for options
622 # need to look for options
616 argv = arg_split(arg_str, posix, strict)
623 argv = arg_split(arg_str, posix, strict)
617 # Do regular option processing
624 # Do regular option processing
618 try:
625 try:
619 opts,args = getopt(argv, opt_str, long_opts)
626 opts,args = getopt(argv, opt_str, long_opts)
620 except GetoptError as e:
627 except GetoptError as e:
621 raise UsageError('%s ( allowed: "%s" %s)' % (e.msg,opt_str,
628 raise UsageError('%s ( allowed: "%s" %s)' % (e.msg,opt_str,
622 " ".join(long_opts)))
629 " ".join(long_opts)))
623 for o,a in opts:
630 for o,a in opts:
624 if o.startswith('--'):
631 if o.startswith('--'):
625 o = o[2:]
632 o = o[2:]
626 else:
633 else:
627 o = o[1:]
634 o = o[1:]
628 try:
635 try:
629 odict[o].append(a)
636 odict[o].append(a)
630 except AttributeError:
637 except AttributeError:
631 odict[o] = [odict[o],a]
638 odict[o] = [odict[o],a]
632 except KeyError:
639 except KeyError:
633 if list_all:
640 if list_all:
634 odict[o] = [a]
641 odict[o] = [a]
635 else:
642 else:
636 odict[o] = a
643 odict[o] = a
637
644
638 # Prepare opts,args for return
645 # Prepare opts,args for return
639 opts = Struct(odict)
646 opts = Struct(odict)
640 if mode == 'string':
647 if mode == 'string':
641 args = ' '.join(args)
648 args = ' '.join(args)
642
649
643 return opts,args
650 return opts,args
644
651
645 def default_option(self, fn, optstr):
652 def default_option(self, fn, optstr):
646 """Make an entry in the options_table for fn, with value optstr"""
653 """Make an entry in the options_table for fn, with value optstr"""
647
654
648 if fn not in self.lsmagic():
655 if fn not in self.lsmagic():
649 error("%s is not a magic function" % fn)
656 error("%s is not a magic function" % fn)
650 self.options_table[fn] = optstr
657 self.options_table[fn] = optstr
651
658
652 class MagicAlias(object):
659 class MagicAlias(object):
653 """An alias to another magic function.
660 """An alias to another magic function.
654
661
655 An alias is determined by its magic name and magic kind. Lookup
662 An alias is determined by its magic name and magic kind. Lookup
656 is done at call time, so if the underlying magic changes the alias
663 is done at call time, so if the underlying magic changes the alias
657 will call the new function.
664 will call the new function.
658
665
659 Use the :meth:`MagicsManager.register_alias` method or the
666 Use the :meth:`MagicsManager.register_alias` method or the
660 `%alias_magic` magic function to create and register a new alias.
667 `%alias_magic` magic function to create and register a new alias.
661 """
668 """
662 def __init__(self, shell, magic_name, magic_kind):
669 def __init__(self, shell, magic_name, magic_kind):
663 self.shell = shell
670 self.shell = shell
664 self.magic_name = magic_name
671 self.magic_name = magic_name
665 self.magic_kind = magic_kind
672 self.magic_kind = magic_kind
666
673
667 self.pretty_target = '%s%s' % (magic_escapes[self.magic_kind], self.magic_name)
674 self.pretty_target = '%s%s' % (magic_escapes[self.magic_kind], self.magic_name)
668 self.__doc__ = "Alias for `%s`." % self.pretty_target
675 self.__doc__ = "Alias for `%s`." % self.pretty_target
669
676
670 self._in_call = False
677 self._in_call = False
671
678
672 def __call__(self, *args, **kwargs):
679 def __call__(self, *args, **kwargs):
673 """Call the magic alias."""
680 """Call the magic alias."""
674 fn = self.shell.find_magic(self.magic_name, self.magic_kind)
681 fn = self.shell.find_magic(self.magic_name, self.magic_kind)
675 if fn is None:
682 if fn is None:
676 raise UsageError("Magic `%s` not found." % self.pretty_target)
683 raise UsageError("Magic `%s` not found." % self.pretty_target)
677
684
678 # Protect against infinite recursion.
685 # Protect against infinite recursion.
679 if self._in_call:
686 if self._in_call:
680 raise UsageError("Infinite recursion detected; "
687 raise UsageError("Infinite recursion detected; "
681 "magic aliases cannot call themselves.")
688 "magic aliases cannot call themselves.")
682 self._in_call = True
689 self._in_call = True
683 try:
690 try:
684 return fn(*args, **kwargs)
691 return fn(*args, **kwargs)
685 finally:
692 finally:
686 self._in_call = False
693 self._in_call = False
@@ -1,302 +1,304 b''
1 # System library imports
1 # System library imports
2 from IPython.external.qt import QtGui
2 from IPython.external.qt import QtGui
3
3
4 # Local imports
4 # Local imports
5 from IPython.utils.traitlets import Bool
5 from IPython.utils.traitlets import Bool
6 from console_widget import ConsoleWidget
6 from console_widget import ConsoleWidget
7
7
8
8
9 class HistoryConsoleWidget(ConsoleWidget):
9 class HistoryConsoleWidget(ConsoleWidget):
10 """ A ConsoleWidget that keeps a history of the commands that have been
10 """ A ConsoleWidget that keeps a history of the commands that have been
11 executed and provides a readline-esque interface to this history.
11 executed and provides a readline-esque interface to this history.
12 """
12 """
13
13
14 #------ Configuration ------------------------------------------------------
14 #------ Configuration ------------------------------------------------------
15
15
16 # If enabled, the input buffer will become "locked" to history movement when
16 # If enabled, the input buffer will become "locked" to history movement when
17 # an edit is made to a multi-line input buffer. To override the lock, use
17 # an edit is made to a multi-line input buffer. To override the lock, use
18 # Shift in conjunction with the standard history cycling keys.
18 # Shift in conjunction with the standard history cycling keys.
19 history_lock = Bool(False, config=True)
19 history_lock = Bool(False, config=True)
20
20
21 #---------------------------------------------------------------------------
21 #---------------------------------------------------------------------------
22 # 'object' interface
22 # 'object' interface
23 #---------------------------------------------------------------------------
23 #---------------------------------------------------------------------------
24
24
25 def __init__(self, *args, **kw):
25 def __init__(self, *args, **kw):
26 super(HistoryConsoleWidget, self).__init__(*args, **kw)
26 super(HistoryConsoleWidget, self).__init__(*args, **kw)
27
27
28 # HistoryConsoleWidget protected variables.
28 # HistoryConsoleWidget protected variables.
29 self._history = []
29 self._history = []
30 self._history_edits = {}
30 self._history_edits = {}
31 self._history_index = 0
31 self._history_index = 0
32 self._history_prefix = ''
32 self._history_prefix = ''
33
33
34 #---------------------------------------------------------------------------
34 #---------------------------------------------------------------------------
35 # 'ConsoleWidget' public interface
35 # 'ConsoleWidget' public interface
36 #---------------------------------------------------------------------------
36 #---------------------------------------------------------------------------
37
37
38 def execute(self, source=None, hidden=False, interactive=False):
38 def execute(self, source=None, hidden=False, interactive=False):
39 """ Reimplemented to the store history.
39 """ Reimplemented to the store history.
40 """
40 """
41 if not hidden:
41 if not hidden:
42 history = self.input_buffer if source is None else source
42 history = self.input_buffer if source is None else source
43
43
44 executed = super(HistoryConsoleWidget, self).execute(
44 executed = super(HistoryConsoleWidget, self).execute(
45 source, hidden, interactive)
45 source, hidden, interactive)
46
46
47 if executed and not hidden:
47 if executed and not hidden:
48 # Save the command unless it was an empty string or was identical
48 # Save the command unless it was an empty string or was identical
49 # to the previous command.
49 # to the previous command.
50 history = history.rstrip()
50 history = history.rstrip()
51 if history and (not self._history or self._history[-1] != history):
51 if history and (not self._history or self._history[-1] != history):
52 self._history.append(history)
52 self._history.append(history)
53
53
54 # Emulate readline: reset all history edits.
54 # Emulate readline: reset all history edits.
55 self._history_edits = {}
55 self._history_edits = {}
56
56
57 # Move the history index to the most recent item.
57 # Move the history index to the most recent item.
58 self._history_index = len(self._history)
58 self._history_index = len(self._history)
59
59
60 return executed
60 return executed
61
61
62 #---------------------------------------------------------------------------
62 #---------------------------------------------------------------------------
63 # 'ConsoleWidget' abstract interface
63 # 'ConsoleWidget' abstract interface
64 #---------------------------------------------------------------------------
64 #---------------------------------------------------------------------------
65
65
66 def _up_pressed(self, shift_modifier):
66 def _up_pressed(self, shift_modifier):
67 """ Called when the up key is pressed. Returns whether to continue
67 """ Called when the up key is pressed. Returns whether to continue
68 processing the event.
68 processing the event.
69 """
69 """
70 prompt_cursor = self._get_prompt_cursor()
70 prompt_cursor = self._get_prompt_cursor()
71 if self._get_cursor().blockNumber() == prompt_cursor.blockNumber():
71 if self._get_cursor().blockNumber() == prompt_cursor.blockNumber():
72 # Bail out if we're locked.
72 # Bail out if we're locked.
73 if self._history_locked() and not shift_modifier:
73 if self._history_locked() and not shift_modifier:
74 return False
74 return False
75
75
76 # Set a search prefix based on the cursor position.
76 # Set a search prefix based on the cursor position.
77 col = self._get_input_buffer_cursor_column()
77 col = self._get_input_buffer_cursor_column()
78 input_buffer = self.input_buffer
78 input_buffer = self.input_buffer
79 # use the *shortest* of the cursor column and the history prefix
79 # use the *shortest* of the cursor column and the history prefix
80 # to determine if the prefix has changed
80 # to determine if the prefix has changed
81 n = min(col, len(self._history_prefix))
81 n = min(col, len(self._history_prefix))
82
82
83 # prefix changed, restart search from the beginning
83 # prefix changed, restart search from the beginning
84 if (self._history_prefix[:n] != input_buffer[:n]):
84 if (self._history_prefix[:n] != input_buffer[:n]):
85 self._history_index = len(self._history)
85 self._history_index = len(self._history)
86
86
87 # the only time we shouldn't set the history prefix
87 # the only time we shouldn't set the history prefix
88 # to the line up to the cursor is if we are already
88 # to the line up to the cursor is if we are already
89 # in a simple scroll (no prefix),
89 # in a simple scroll (no prefix),
90 # and the cursor is at the end of the first line
90 # and the cursor is at the end of the first line
91
91
92 # check if we are at the end of the first line
92 # check if we are at the end of the first line
93 c = self._get_cursor()
93 c = self._get_cursor()
94 current_pos = c.position()
94 current_pos = c.position()
95 c.movePosition(QtGui.QTextCursor.EndOfLine)
95 c.movePosition(QtGui.QTextCursor.EndOfLine)
96 at_eol = (c.position() == current_pos)
96 at_eol = (c.position() == current_pos)
97
97
98 if self._history_index == len(self._history) or \
98 if self._history_index == len(self._history) or \
99 not (self._history_prefix == '' and at_eol) or \
99 not (self._history_prefix == '' and at_eol) or \
100 not (self._get_edited_history(self._history_index)[:col] == input_buffer[:col]):
100 not (self._get_edited_history(self._history_index)[:col] == input_buffer[:col]):
101 self._history_prefix = input_buffer[:col]
101 self._history_prefix = input_buffer[:col]
102
102
103 # Perform the search.
103 # Perform the search.
104 self.history_previous(self._history_prefix,
104 self.history_previous(self._history_prefix,
105 as_prefix=not shift_modifier)
105 as_prefix=not shift_modifier)
106
106
107 # Go to the first line of the prompt for seemless history scrolling.
107 # Go to the first line of the prompt for seemless history scrolling.
108 # Emulate readline: keep the cursor position fixed for a prefix
108 # Emulate readline: keep the cursor position fixed for a prefix
109 # search.
109 # search.
110 cursor = self._get_prompt_cursor()
110 cursor = self._get_prompt_cursor()
111 if self._history_prefix:
111 if self._history_prefix:
112 cursor.movePosition(QtGui.QTextCursor.Right,
112 cursor.movePosition(QtGui.QTextCursor.Right,
113 n=len(self._history_prefix))
113 n=len(self._history_prefix))
114 else:
114 else:
115 cursor.movePosition(QtGui.QTextCursor.EndOfLine)
115 cursor.movePosition(QtGui.QTextCursor.EndOfLine)
116 self._set_cursor(cursor)
116 self._set_cursor(cursor)
117
117
118 return False
118 return False
119
119
120 return True
120 return True
121
121
122 def _down_pressed(self, shift_modifier):
122 def _down_pressed(self, shift_modifier):
123 """ Called when the down key is pressed. Returns whether to continue
123 """ Called when the down key is pressed. Returns whether to continue
124 processing the event.
124 processing the event.
125 """
125 """
126 end_cursor = self._get_end_cursor()
126 end_cursor = self._get_end_cursor()
127 if self._get_cursor().blockNumber() == end_cursor.blockNumber():
127 if self._get_cursor().blockNumber() == end_cursor.blockNumber():
128 # Bail out if we're locked.
128 # Bail out if we're locked.
129 if self._history_locked() and not shift_modifier:
129 if self._history_locked() and not shift_modifier:
130 return False
130 return False
131
131
132 # Perform the search.
132 # Perform the search.
133 replaced = self.history_next(self._history_prefix,
133 replaced = self.history_next(self._history_prefix,
134 as_prefix=not shift_modifier)
134 as_prefix=not shift_modifier)
135
135
136 # Emulate readline: keep the cursor position fixed for a prefix
136 # Emulate readline: keep the cursor position fixed for a prefix
137 # search. (We don't need to move the cursor to the end of the buffer
137 # search. (We don't need to move the cursor to the end of the buffer
138 # in the other case because this happens automatically when the
138 # in the other case because this happens automatically when the
139 # input buffer is set.)
139 # input buffer is set.)
140 if self._history_prefix and replaced:
140 if self._history_prefix and replaced:
141 cursor = self._get_prompt_cursor()
141 cursor = self._get_prompt_cursor()
142 cursor.movePosition(QtGui.QTextCursor.Right,
142 cursor.movePosition(QtGui.QTextCursor.Right,
143 n=len(self._history_prefix))
143 n=len(self._history_prefix))
144 self._set_cursor(cursor)
144 self._set_cursor(cursor)
145
145
146 return False
146 return False
147
147
148 return True
148 return True
149
149
150 #---------------------------------------------------------------------------
150 #---------------------------------------------------------------------------
151 # 'HistoryConsoleWidget' public interface
151 # 'HistoryConsoleWidget' public interface
152 #---------------------------------------------------------------------------
152 #---------------------------------------------------------------------------
153
153
154 def history_previous(self, substring='', as_prefix=True):
154 def history_previous(self, substring='', as_prefix=True):
155 """ If possible, set the input buffer to a previous history item.
155 """ If possible, set the input buffer to a previous history item.
156
156
157 Parameters:
157 Parameters:
158 -----------
158 -----------
159 substring : str, optional
159 substring : str, optional
160 If specified, search for an item with this substring.
160 If specified, search for an item with this substring.
161 as_prefix : bool, optional
161 as_prefix : bool, optional
162 If True, the substring must match at the beginning (default).
162 If True, the substring must match at the beginning (default).
163
163
164 Returns:
164 Returns:
165 --------
165 --------
166 Whether the input buffer was changed.
166 Whether the input buffer was changed.
167 """
167 """
168 index = self._history_index
168 index = self._history_index
169 replace = False
169 replace = False
170 while index > 0:
170 while index > 0:
171 index -= 1
171 index -= 1
172 history = self._get_edited_history(index)
172 history = self._get_edited_history(index)
173 if (as_prefix and history.startswith(substring)) \
173 if (as_prefix and history.startswith(substring)) \
174 or (not as_prefix and substring in history):
174 or (not as_prefix and substring in history):
175 replace = True
175 replace = True
176 break
176 break
177
177
178 if replace:
178 if replace:
179 self._store_edits()
179 self._store_edits()
180 self._history_index = index
180 self._history_index = index
181 self.input_buffer = history
181 self.input_buffer = history
182
182
183 return replace
183 return replace
184
184
185 def history_next(self, substring='', as_prefix=True):
185 def history_next(self, substring='', as_prefix=True):
186 """ If possible, set the input buffer to a subsequent history item.
186 """ If possible, set the input buffer to a subsequent history item.
187
187
188 Parameters:
188 Parameters:
189 -----------
189 -----------
190 substring : str, optional
190 substring : str, optional
191 If specified, search for an item with this substring.
191 If specified, search for an item with this substring.
192 as_prefix : bool, optional
192 as_prefix : bool, optional
193 If True, the substring must match at the beginning (default).
193 If True, the substring must match at the beginning (default).
194
194
195 Returns:
195 Returns:
196 --------
196 --------
197 Whether the input buffer was changed.
197 Whether the input buffer was changed.
198 """
198 """
199 index = self._history_index
199 index = self._history_index
200 replace = False
200 replace = False
201 while index < len(self._history):
201 while index < len(self._history):
202 index += 1
202 index += 1
203 history = self._get_edited_history(index)
203 history = self._get_edited_history(index)
204 if (as_prefix and history.startswith(substring)) \
204 if (as_prefix and history.startswith(substring)) \
205 or (not as_prefix and substring in history):
205 or (not as_prefix and substring in history):
206 replace = True
206 replace = True
207 break
207 break
208
208
209 if replace:
209 if replace:
210 self._store_edits()
210 self._store_edits()
211 self._history_index = index
211 self._history_index = index
212 self.input_buffer = history
212 self.input_buffer = history
213
213
214 return replace
214 return replace
215
215
216 def history_tail(self, n=10):
216 def history_tail(self, n=10):
217 """ Get the local history list.
217 """ Get the local history list.
218
218
219 Parameters:
219 Parameters:
220 -----------
220 -----------
221 n : int
221 n : int
222 The (maximum) number of history items to get.
222 The (maximum) number of history items to get.
223 """
223 """
224 return self._history[-n:]
224 return self._history[-n:]
225
225
226 def _request_update_session_history_length(self):
226 def _request_update_session_history_length(self):
227 msg_id = self.kernel_client.shell_channel.execute('',
227 msg_id = self.kernel_client.shell_channel.execute('',
228 silent=True,
228 silent=True,
229 user_expressions={
229 user_expressions={
230 'hlen':'len(get_ipython().history_manager.input_hist_raw)',
230 'hlen':'len(get_ipython().history_manager.input_hist_raw)',
231 }
231 }
232 )
232 )
233 self._request_info['execute'][msg_id] = self._ExecutionRequest(msg_id, 'save_magic')
233 self._request_info['execute'][msg_id] = self._ExecutionRequest(msg_id, 'save_magic')
234
234
235 def _handle_execute_reply(self, msg):
235 def _handle_execute_reply(self, msg):
236 """ Handles replies for code execution, here only session history length
236 """ Handles replies for code execution, here only session history length
237 """
237 """
238 msg_id = msg['parent_header']['msg_id']
238 msg_id = msg['parent_header']['msg_id']
239 info = self._request_info['execute'].pop(msg_id,None)
239 info = self._request_info['execute'].pop(msg_id,None)
240 if info and info.kind == 'save_magic' and not self._hidden:
240 if info and info.kind == 'save_magic' and not self._hidden:
241 content = msg['content']
241 content = msg['content']
242 status = content['status']
242 status = content['status']
243 if status == 'ok':
243 if status == 'ok':
244 self._max_session_history=(int(content['user_expressions']['hlen']))
244 self._max_session_history = int(
245 content['user_expressions']['hlen']['data']['text/plain']
246 )
245
247
246 def save_magic(self):
248 def save_magic(self):
247 # update the session history length
249 # update the session history length
248 self._request_update_session_history_length()
250 self._request_update_session_history_length()
249
251
250 file_name,extFilter = QtGui.QFileDialog.getSaveFileName(self,
252 file_name,extFilter = QtGui.QFileDialog.getSaveFileName(self,
251 "Enter A filename",
253 "Enter A filename",
252 filter='Python File (*.py);; All files (*.*)'
254 filter='Python File (*.py);; All files (*.*)'
253 )
255 )
254
256
255 # let's the user search/type for a file name, while the history length
257 # let's the user search/type for a file name, while the history length
256 # is fetched
258 # is fetched
257
259
258 if file_name:
260 if file_name:
259 hist_range, ok = QtGui.QInputDialog.getText(self,
261 hist_range, ok = QtGui.QInputDialog.getText(self,
260 'Please enter an interval of command to save',
262 'Please enter an interval of command to save',
261 'Saving commands:',
263 'Saving commands:',
262 text=str('1-'+str(self._max_session_history))
264 text=str('1-'+str(self._max_session_history))
263 )
265 )
264 if ok:
266 if ok:
265 self.execute("%save"+" "+file_name+" "+str(hist_range))
267 self.execute("%save"+" "+file_name+" "+str(hist_range))
266
268
267 #---------------------------------------------------------------------------
269 #---------------------------------------------------------------------------
268 # 'HistoryConsoleWidget' protected interface
270 # 'HistoryConsoleWidget' protected interface
269 #---------------------------------------------------------------------------
271 #---------------------------------------------------------------------------
270
272
271 def _history_locked(self):
273 def _history_locked(self):
272 """ Returns whether history movement is locked.
274 """ Returns whether history movement is locked.
273 """
275 """
274 return (self.history_lock and
276 return (self.history_lock and
275 (self._get_edited_history(self._history_index) !=
277 (self._get_edited_history(self._history_index) !=
276 self.input_buffer) and
278 self.input_buffer) and
277 (self._get_prompt_cursor().blockNumber() !=
279 (self._get_prompt_cursor().blockNumber() !=
278 self._get_end_cursor().blockNumber()))
280 self._get_end_cursor().blockNumber()))
279
281
280 def _get_edited_history(self, index):
282 def _get_edited_history(self, index):
281 """ Retrieves a history item, possibly with temporary edits.
283 """ Retrieves a history item, possibly with temporary edits.
282 """
284 """
283 if index in self._history_edits:
285 if index in self._history_edits:
284 return self._history_edits[index]
286 return self._history_edits[index]
285 elif index == len(self._history):
287 elif index == len(self._history):
286 return unicode()
288 return unicode()
287 return self._history[index]
289 return self._history[index]
288
290
289 def _set_history(self, history):
291 def _set_history(self, history):
290 """ Replace the current history with a sequence of history items.
292 """ Replace the current history with a sequence of history items.
291 """
293 """
292 self._history = list(history)
294 self._history = list(history)
293 self._history_edits = {}
295 self._history_edits = {}
294 self._history_index = len(self._history)
296 self._history_index = len(self._history)
295
297
296 def _store_edits(self):
298 def _store_edits(self):
297 """ If there are edits to the current input buffer, store them.
299 """ If there are edits to the current input buffer, store them.
298 """
300 """
299 current = self.input_buffer
301 current = self.input_buffer
300 if self._history_index == len(self._history) or \
302 if self._history_index == len(self._history) or \
301 self._history[self._history_index] != current:
303 self._history[self._history_index] != current:
302 self._history_edits[self._history_index] = current
304 self._history_edits[self._history_index] = current
@@ -1,992 +1,992 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 sys
23 import json
24 import re
24 import re
25 import sys
25 import webbrowser
26 import webbrowser
26 import ast
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 def background(f):
32 def background(f):
33 """call a function in a simple thread, to prevent blocking"""
33 """call a function in a simple thread, to prevent blocking"""
34 t = Thread(target=f)
34 t = Thread(target=f)
35 t.start()
35 t.start()
36 return t
36 return t
37
37
38 #-----------------------------------------------------------------------------
38 #-----------------------------------------------------------------------------
39 # Classes
39 # Classes
40 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
41
41
42 class MainWindow(QtGui.QMainWindow):
42 class MainWindow(QtGui.QMainWindow):
43
43
44 #---------------------------------------------------------------------------
44 #---------------------------------------------------------------------------
45 # 'object' interface
45 # 'object' interface
46 #---------------------------------------------------------------------------
46 #---------------------------------------------------------------------------
47
47
48 _magic_menu_dict = {}
48 _magic_menu_dict = {}
49
49
50 def __init__(self, app,
50 def __init__(self, app,
51 confirm_exit=True,
51 confirm_exit=True,
52 new_frontend_factory=None, slave_frontend_factory=None,
52 new_frontend_factory=None, slave_frontend_factory=None,
53 ):
53 ):
54 """ Create a tabbed MainWindow for managing IPython FrontendWidgets
54 """ Create a tabbed MainWindow for managing IPython FrontendWidgets
55
55
56 Parameters
56 Parameters
57 ----------
57 ----------
58
58
59 app : reference to QApplication parent
59 app : reference to QApplication parent
60 confirm_exit : bool, optional
60 confirm_exit : bool, optional
61 Whether we should prompt on close of tabs
61 Whether we should prompt on close of tabs
62 new_frontend_factory : callable
62 new_frontend_factory : callable
63 A callable that returns a new IPythonWidget instance, attached to
63 A callable that returns a new IPythonWidget instance, attached to
64 its own running kernel.
64 its own running kernel.
65 slave_frontend_factory : callable
65 slave_frontend_factory : callable
66 A callable that takes an existing IPythonWidget, and returns a new
66 A callable that takes an existing IPythonWidget, and returns a new
67 IPythonWidget instance, attached to the same kernel.
67 IPythonWidget instance, attached to the same kernel.
68 """
68 """
69
69
70 super(MainWindow, self).__init__()
70 super(MainWindow, self).__init__()
71 self._kernel_counter = 0
71 self._kernel_counter = 0
72 self._app = app
72 self._app = app
73 self.confirm_exit = confirm_exit
73 self.confirm_exit = confirm_exit
74 self.new_frontend_factory = new_frontend_factory
74 self.new_frontend_factory = new_frontend_factory
75 self.slave_frontend_factory = slave_frontend_factory
75 self.slave_frontend_factory = slave_frontend_factory
76
76
77 self.tab_widget = QtGui.QTabWidget(self)
77 self.tab_widget = QtGui.QTabWidget(self)
78 self.tab_widget.setDocumentMode(True)
78 self.tab_widget.setDocumentMode(True)
79 self.tab_widget.setTabsClosable(True)
79 self.tab_widget.setTabsClosable(True)
80 self.tab_widget.tabCloseRequested[int].connect(self.close_tab)
80 self.tab_widget.tabCloseRequested[int].connect(self.close_tab)
81
81
82 self.setCentralWidget(self.tab_widget)
82 self.setCentralWidget(self.tab_widget)
83 # hide tab bar at first, since we have no tabs:
83 # hide tab bar at first, since we have no tabs:
84 self.tab_widget.tabBar().setVisible(False)
84 self.tab_widget.tabBar().setVisible(False)
85 # prevent focus in tab bar
85 # prevent focus in tab bar
86 self.tab_widget.setFocusPolicy(QtCore.Qt.NoFocus)
86 self.tab_widget.setFocusPolicy(QtCore.Qt.NoFocus)
87
87
88 def update_tab_bar_visibility(self):
88 def update_tab_bar_visibility(self):
89 """ update visibility of the tabBar depending of the number of tab
89 """ update visibility of the tabBar depending of the number of tab
90
90
91 0 or 1 tab, tabBar hidden
91 0 or 1 tab, tabBar hidden
92 2+ tabs, tabBar visible
92 2+ tabs, tabBar visible
93
93
94 send a self.close if number of tab ==0
94 send a self.close if number of tab ==0
95
95
96 need to be called explicitly, or be connected to tabInserted/tabRemoved
96 need to be called explicitly, or be connected to tabInserted/tabRemoved
97 """
97 """
98 if self.tab_widget.count() <= 1:
98 if self.tab_widget.count() <= 1:
99 self.tab_widget.tabBar().setVisible(False)
99 self.tab_widget.tabBar().setVisible(False)
100 else:
100 else:
101 self.tab_widget.tabBar().setVisible(True)
101 self.tab_widget.tabBar().setVisible(True)
102 if self.tab_widget.count()==0 :
102 if self.tab_widget.count()==0 :
103 self.close()
103 self.close()
104
104
105 @property
105 @property
106 def next_kernel_id(self):
106 def next_kernel_id(self):
107 """constantly increasing counter for kernel IDs"""
107 """constantly increasing counter for kernel IDs"""
108 c = self._kernel_counter
108 c = self._kernel_counter
109 self._kernel_counter += 1
109 self._kernel_counter += 1
110 return c
110 return c
111
111
112 @property
112 @property
113 def active_frontend(self):
113 def active_frontend(self):
114 return self.tab_widget.currentWidget()
114 return self.tab_widget.currentWidget()
115
115
116 def create_tab_with_new_frontend(self):
116 def create_tab_with_new_frontend(self):
117 """create a new frontend and attach it to a new tab"""
117 """create a new frontend and attach it to a new tab"""
118 widget = self.new_frontend_factory()
118 widget = self.new_frontend_factory()
119 self.add_tab_with_frontend(widget)
119 self.add_tab_with_frontend(widget)
120
120
121 def create_tab_with_current_kernel(self):
121 def create_tab_with_current_kernel(self):
122 """create a new frontend attached to the same kernel as the current tab"""
122 """create a new frontend attached to the same kernel as the current tab"""
123 current_widget = self.tab_widget.currentWidget()
123 current_widget = self.tab_widget.currentWidget()
124 current_widget_index = self.tab_widget.indexOf(current_widget)
124 current_widget_index = self.tab_widget.indexOf(current_widget)
125 current_widget_name = self.tab_widget.tabText(current_widget_index)
125 current_widget_name = self.tab_widget.tabText(current_widget_index)
126 widget = self.slave_frontend_factory(current_widget)
126 widget = self.slave_frontend_factory(current_widget)
127 if 'slave' in current_widget_name:
127 if 'slave' in current_widget_name:
128 # don't keep stacking slaves
128 # don't keep stacking slaves
129 name = current_widget_name
129 name = current_widget_name
130 else:
130 else:
131 name = '(%s) slave' % current_widget_name
131 name = '(%s) slave' % current_widget_name
132 self.add_tab_with_frontend(widget,name=name)
132 self.add_tab_with_frontend(widget,name=name)
133
133
134 def close_tab(self,current_tab):
134 def close_tab(self,current_tab):
135 """ Called when you need to try to close a tab.
135 """ Called when you need to try to close a tab.
136
136
137 It takes the number of the tab to be closed as argument, or a reference
137 It takes the number of the tab to be closed as argument, or a reference
138 to the widget inside this tab
138 to the widget inside this tab
139 """
139 """
140
140
141 # let's be sure "tab" and "closing widget" are respectively the index
141 # 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
142 # of the tab to close and a reference to the frontend to close
143 if type(current_tab) is not int :
143 if type(current_tab) is not int :
144 current_tab = self.tab_widget.indexOf(current_tab)
144 current_tab = self.tab_widget.indexOf(current_tab)
145 closing_widget=self.tab_widget.widget(current_tab)
145 closing_widget=self.tab_widget.widget(current_tab)
146
146
147
147
148 # when trying to be closed, widget might re-send a request to be
148 # 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
149 # 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
150 # 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
151 # 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
152 # re-sent by this function on the master widget, which ask all slave
153 # widgets to exit
153 # widgets to exit
154 if closing_widget==None:
154 if closing_widget==None:
155 return
155 return
156
156
157 #get a list of all slave widgets on the same kernel.
157 #get a list of all slave widgets on the same kernel.
158 slave_tabs = self.find_slave_widgets(closing_widget)
158 slave_tabs = self.find_slave_widgets(closing_widget)
159
159
160 keepkernel = None #Use the prompt by default
160 keepkernel = None #Use the prompt by default
161 if hasattr(closing_widget,'_keep_kernel_on_exit'): #set by exit magic
161 if hasattr(closing_widget,'_keep_kernel_on_exit'): #set by exit magic
162 keepkernel = closing_widget._keep_kernel_on_exit
162 keepkernel = closing_widget._keep_kernel_on_exit
163 # If signal sent by exit magic (_keep_kernel_on_exit, exist and not None)
163 # 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
164 # 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'
165 # restart when they get the signal. and then "forward" the 'exit'
166 # to the main window
166 # to the main window
167 if keepkernel is not None:
167 if keepkernel is not None:
168 for tab in slave_tabs:
168 for tab in slave_tabs:
169 tab._hidden = True
169 tab._hidden = True
170 if closing_widget in slave_tabs:
170 if closing_widget in slave_tabs:
171 try :
171 try :
172 self.find_master_tab(closing_widget).execute('exit')
172 self.find_master_tab(closing_widget).execute('exit')
173 except AttributeError:
173 except AttributeError:
174 self.log.info("Master already closed or not local, closing only current tab")
174 self.log.info("Master already closed or not local, closing only current tab")
175 self.tab_widget.removeTab(current_tab)
175 self.tab_widget.removeTab(current_tab)
176 self.update_tab_bar_visibility()
176 self.update_tab_bar_visibility()
177 return
177 return
178
178
179 kernel_client = closing_widget.kernel_client
179 kernel_client = closing_widget.kernel_client
180 kernel_manager = closing_widget.kernel_manager
180 kernel_manager = closing_widget.kernel_manager
181
181
182 if keepkernel is None and not closing_widget._confirm_exit:
182 if keepkernel is None and not closing_widget._confirm_exit:
183 # don't prompt, just terminate the kernel if we own it
183 # don't prompt, just terminate the kernel if we own it
184 # or leave it alone if we don't
184 # or leave it alone if we don't
185 keepkernel = closing_widget._existing
185 keepkernel = closing_widget._existing
186 if keepkernel is None: #show prompt
186 if keepkernel is None: #show prompt
187 if kernel_client and kernel_client.channels_running:
187 if kernel_client and kernel_client.channels_running:
188 title = self.window().windowTitle()
188 title = self.window().windowTitle()
189 cancel = QtGui.QMessageBox.Cancel
189 cancel = QtGui.QMessageBox.Cancel
190 okay = QtGui.QMessageBox.Ok
190 okay = QtGui.QMessageBox.Ok
191 if closing_widget._may_close:
191 if closing_widget._may_close:
192 msg = "You are closing the tab : "+'"'+self.tab_widget.tabText(current_tab)+'"'
192 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?"
193 info = "Would you like to quit the Kernel and close all attached Consoles as well?"
194 justthis = QtGui.QPushButton("&No, just this Tab", self)
194 justthis = QtGui.QPushButton("&No, just this Tab", self)
195 justthis.setShortcut('N')
195 justthis.setShortcut('N')
196 closeall = QtGui.QPushButton("&Yes, close all", self)
196 closeall = QtGui.QPushButton("&Yes, close all", self)
197 closeall.setShortcut('Y')
197 closeall.setShortcut('Y')
198 # allow ctrl-d ctrl-d exit, like in terminal
198 # allow ctrl-d ctrl-d exit, like in terminal
199 closeall.setShortcut('Ctrl+D')
199 closeall.setShortcut('Ctrl+D')
200 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
200 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
201 title, msg)
201 title, msg)
202 box.setInformativeText(info)
202 box.setInformativeText(info)
203 box.addButton(cancel)
203 box.addButton(cancel)
204 box.addButton(justthis, QtGui.QMessageBox.NoRole)
204 box.addButton(justthis, QtGui.QMessageBox.NoRole)
205 box.addButton(closeall, QtGui.QMessageBox.YesRole)
205 box.addButton(closeall, QtGui.QMessageBox.YesRole)
206 box.setDefaultButton(closeall)
206 box.setDefaultButton(closeall)
207 box.setEscapeButton(cancel)
207 box.setEscapeButton(cancel)
208 pixmap = QtGui.QPixmap(self._app.icon.pixmap(QtCore.QSize(64,64)))
208 pixmap = QtGui.QPixmap(self._app.icon.pixmap(QtCore.QSize(64,64)))
209 box.setIconPixmap(pixmap)
209 box.setIconPixmap(pixmap)
210 reply = box.exec_()
210 reply = box.exec_()
211 if reply == 1: # close All
211 if reply == 1: # close All
212 for slave in slave_tabs:
212 for slave in slave_tabs:
213 background(slave.kernel_client.stop_channels)
213 background(slave.kernel_client.stop_channels)
214 self.tab_widget.removeTab(self.tab_widget.indexOf(slave))
214 self.tab_widget.removeTab(self.tab_widget.indexOf(slave))
215 closing_widget.execute("exit")
215 closing_widget.execute("exit")
216 self.tab_widget.removeTab(current_tab)
216 self.tab_widget.removeTab(current_tab)
217 background(kernel_client.stop_channels)
217 background(kernel_client.stop_channels)
218 elif reply == 0: # close Console
218 elif reply == 0: # close Console
219 if not closing_widget._existing:
219 if not closing_widget._existing:
220 # Have kernel: don't quit, just close the tab
220 # Have kernel: don't quit, just close the tab
221 closing_widget.execute("exit True")
221 closing_widget.execute("exit True")
222 self.tab_widget.removeTab(current_tab)
222 self.tab_widget.removeTab(current_tab)
223 background(kernel_client.stop_channels)
223 background(kernel_client.stop_channels)
224 else:
224 else:
225 reply = QtGui.QMessageBox.question(self, title,
225 reply = QtGui.QMessageBox.question(self, title,
226 "Are you sure you want to close this Console?"+
226 "Are you sure you want to close this Console?"+
227 "\nThe Kernel and other Consoles will remain active.",
227 "\nThe Kernel and other Consoles will remain active.",
228 okay|cancel,
228 okay|cancel,
229 defaultButton=okay
229 defaultButton=okay
230 )
230 )
231 if reply == okay:
231 if reply == okay:
232 self.tab_widget.removeTab(current_tab)
232 self.tab_widget.removeTab(current_tab)
233 elif keepkernel: #close console but leave kernel running (no prompt)
233 elif keepkernel: #close console but leave kernel running (no prompt)
234 self.tab_widget.removeTab(current_tab)
234 self.tab_widget.removeTab(current_tab)
235 background(kernel_client.stop_channels)
235 background(kernel_client.stop_channels)
236 else: #close console and kernel (no prompt)
236 else: #close console and kernel (no prompt)
237 self.tab_widget.removeTab(current_tab)
237 self.tab_widget.removeTab(current_tab)
238 if kernel_client and kernel_client.channels_running:
238 if kernel_client and kernel_client.channels_running:
239 for slave in slave_tabs:
239 for slave in slave_tabs:
240 background(slave.kernel_client.stop_channels)
240 background(slave.kernel_client.stop_channels)
241 self.tab_widget.removeTab(self.tab_widget.indexOf(slave))
241 self.tab_widget.removeTab(self.tab_widget.indexOf(slave))
242 if kernel_manager:
242 if kernel_manager:
243 kernel_manager.shutdown_kernel()
243 kernel_manager.shutdown_kernel()
244 background(kernel_client.stop_channels)
244 background(kernel_client.stop_channels)
245
245
246 self.update_tab_bar_visibility()
246 self.update_tab_bar_visibility()
247
247
248 def add_tab_with_frontend(self,frontend,name=None):
248 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
249 """ insert a tab with a given frontend in the tab bar, and give it a name
250
250
251 """
251 """
252 if not name:
252 if not name:
253 name = 'kernel %i' % self.next_kernel_id
253 name = 'kernel %i' % self.next_kernel_id
254 self.tab_widget.addTab(frontend,name)
254 self.tab_widget.addTab(frontend,name)
255 self.update_tab_bar_visibility()
255 self.update_tab_bar_visibility()
256 self.make_frontend_visible(frontend)
256 self.make_frontend_visible(frontend)
257 frontend.exit_requested.connect(self.close_tab)
257 frontend.exit_requested.connect(self.close_tab)
258
258
259 def next_tab(self):
259 def next_tab(self):
260 self.tab_widget.setCurrentIndex((self.tab_widget.currentIndex()+1))
260 self.tab_widget.setCurrentIndex((self.tab_widget.currentIndex()+1))
261
261
262 def prev_tab(self):
262 def prev_tab(self):
263 self.tab_widget.setCurrentIndex((self.tab_widget.currentIndex()-1))
263 self.tab_widget.setCurrentIndex((self.tab_widget.currentIndex()-1))
264
264
265 def make_frontend_visible(self,frontend):
265 def make_frontend_visible(self,frontend):
266 widget_index=self.tab_widget.indexOf(frontend)
266 widget_index=self.tab_widget.indexOf(frontend)
267 if widget_index > 0 :
267 if widget_index > 0 :
268 self.tab_widget.setCurrentIndex(widget_index)
268 self.tab_widget.setCurrentIndex(widget_index)
269
269
270 def find_master_tab(self,tab,as_list=False):
270 def find_master_tab(self,tab,as_list=False):
271 """
271 """
272 Try to return the frontend that owns the kernel attached to the given widget/tab.
272 Try to return the frontend that owns the kernel attached to the given widget/tab.
273
273
274 Only finds frontend owned by the current application. Selection
274 Only finds frontend owned by the current application. Selection
275 based on port of the kernel might be inaccurate if several kernel
275 based on port of the kernel might be inaccurate if several kernel
276 on different ip use same port number.
276 on different ip use same port number.
277
277
278 This function does the conversion tabNumber/widget if needed.
278 This function does the conversion tabNumber/widget if needed.
279 Might return None if no master widget (non local kernel)
279 Might return None if no master widget (non local kernel)
280 Will crash IPython if more than 1 masterWidget
280 Will crash IPython if more than 1 masterWidget
281
281
282 When asList set to True, always return a list of widget(s) owning
282 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.
283 the kernel. The list might be empty or containing several Widget.
284 """
284 """
285
285
286 #convert from/to int/richIpythonWidget if needed
286 #convert from/to int/richIpythonWidget if needed
287 if isinstance(tab, int):
287 if isinstance(tab, int):
288 tab = self.tab_widget.widget(tab)
288 tab = self.tab_widget.widget(tab)
289 km=tab.kernel_client
289 km=tab.kernel_client
290
290
291 #build list of all widgets
291 #build list of all widgets
292 widget_list = [self.tab_widget.widget(i) for i in range(self.tab_widget.count())]
292 widget_list = [self.tab_widget.widget(i) for i in range(self.tab_widget.count())]
293
293
294 # widget that are candidate to be the owner of the kernel does have all the same port of the curent widget
294 # 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
295 # And should have a _may_close attribute
296 filtered_widget_list = [ widget for widget in widget_list if
296 filtered_widget_list = [ widget for widget in widget_list if
297 widget.kernel_client.connection_file == km.connection_file and
297 widget.kernel_client.connection_file == km.connection_file and
298 hasattr(widget,'_may_close') ]
298 hasattr(widget,'_may_close') ]
299 # the master widget is the one that may close the kernel
299 # 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]
300 master_widget= [ widget for widget in filtered_widget_list if widget._may_close]
301 if as_list:
301 if as_list:
302 return master_widget
302 return master_widget
303 assert(len(master_widget)<=1 )
303 assert(len(master_widget)<=1 )
304 if len(master_widget)==0:
304 if len(master_widget)==0:
305 return None
305 return None
306
306
307 return master_widget[0]
307 return master_widget[0]
308
308
309 def find_slave_widgets(self,tab):
309 def find_slave_widgets(self,tab):
310 """return all the frontends that do not own the kernel attached to the given widget/tab.
310 """return all the frontends that do not own the kernel attached to the given widget/tab.
311
311
312 Only find frontends owned by the current application. Selection
312 Only find frontends owned by the current application. Selection
313 based on connection file of the kernel.
313 based on connection file of the kernel.
314
314
315 This function does the conversion tabNumber/widget if needed.
315 This function does the conversion tabNumber/widget if needed.
316 """
316 """
317 #convert from/to int/richIpythonWidget if needed
317 #convert from/to int/richIpythonWidget if needed
318 if isinstance(tab, int):
318 if isinstance(tab, int):
319 tab = self.tab_widget.widget(tab)
319 tab = self.tab_widget.widget(tab)
320 km=tab.kernel_client
320 km=tab.kernel_client
321
321
322 #build list of all widgets
322 #build list of all widgets
323 widget_list = [self.tab_widget.widget(i) for i in range(self.tab_widget.count())]
323 widget_list = [self.tab_widget.widget(i) for i in range(self.tab_widget.count())]
324
324
325 # widget that are candidate not to be the owner of the kernel does have all the same port of the curent widget
325 # 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
326 filtered_widget_list = ( widget for widget in widget_list if
327 widget.kernel_client.connection_file == km.connection_file)
327 widget.kernel_client.connection_file == km.connection_file)
328 # Get a list of all widget owning the same kernel and removed it from
328 # Get a list of all widget owning the same kernel and removed it from
329 # the previous cadidate. (better using sets ?)
329 # the previous cadidate. (better using sets ?)
330 master_widget_list = self.find_master_tab(tab, as_list=True)
330 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]
331 slave_list = [widget for widget in filtered_widget_list if widget not in master_widget_list]
332
332
333 return slave_list
333 return slave_list
334
334
335 # Populate the menu bar with common actions and shortcuts
335 # Populate the menu bar with common actions and shortcuts
336 def add_menu_action(self, menu, action, defer_shortcut=False):
336 def add_menu_action(self, menu, action, defer_shortcut=False):
337 """Add action to menu as well as self
337 """Add action to menu as well as self
338
338
339 So that when the menu bar is invisible, its actions are still available.
339 So that when the menu bar is invisible, its actions are still available.
340
340
341 If defer_shortcut is True, set the shortcut context to widget-only,
341 If defer_shortcut is True, set the shortcut context to widget-only,
342 where it will avoid conflict with shortcuts already bound to the
342 where it will avoid conflict with shortcuts already bound to the
343 widgets themselves.
343 widgets themselves.
344 """
344 """
345 menu.addAction(action)
345 menu.addAction(action)
346 self.addAction(action)
346 self.addAction(action)
347
347
348 if defer_shortcut:
348 if defer_shortcut:
349 action.setShortcutContext(QtCore.Qt.WidgetShortcut)
349 action.setShortcutContext(QtCore.Qt.WidgetShortcut)
350
350
351 def init_menu_bar(self):
351 def init_menu_bar(self):
352 #create menu in the order they should appear in the menu bar
352 #create menu in the order they should appear in the menu bar
353 self.init_file_menu()
353 self.init_file_menu()
354 self.init_edit_menu()
354 self.init_edit_menu()
355 self.init_view_menu()
355 self.init_view_menu()
356 self.init_kernel_menu()
356 self.init_kernel_menu()
357 self.init_magic_menu()
357 self.init_magic_menu()
358 self.init_window_menu()
358 self.init_window_menu()
359 self.init_help_menu()
359 self.init_help_menu()
360
360
361 def init_file_menu(self):
361 def init_file_menu(self):
362 self.file_menu = self.menuBar().addMenu("&File")
362 self.file_menu = self.menuBar().addMenu("&File")
363
363
364 self.new_kernel_tab_act = QtGui.QAction("New Tab with &New kernel",
364 self.new_kernel_tab_act = QtGui.QAction("New Tab with &New kernel",
365 self,
365 self,
366 shortcut="Ctrl+T",
366 shortcut="Ctrl+T",
367 triggered=self.create_tab_with_new_frontend)
367 triggered=self.create_tab_with_new_frontend)
368 self.add_menu_action(self.file_menu, self.new_kernel_tab_act)
368 self.add_menu_action(self.file_menu, self.new_kernel_tab_act)
369
369
370 self.slave_kernel_tab_act = QtGui.QAction("New Tab with Sa&me kernel",
370 self.slave_kernel_tab_act = QtGui.QAction("New Tab with Sa&me kernel",
371 self,
371 self,
372 shortcut="Ctrl+Shift+T",
372 shortcut="Ctrl+Shift+T",
373 triggered=self.create_tab_with_current_kernel)
373 triggered=self.create_tab_with_current_kernel)
374 self.add_menu_action(self.file_menu, self.slave_kernel_tab_act)
374 self.add_menu_action(self.file_menu, self.slave_kernel_tab_act)
375
375
376 self.file_menu.addSeparator()
376 self.file_menu.addSeparator()
377
377
378 self.close_action=QtGui.QAction("&Close Tab",
378 self.close_action=QtGui.QAction("&Close Tab",
379 self,
379 self,
380 shortcut=QtGui.QKeySequence.Close,
380 shortcut=QtGui.QKeySequence.Close,
381 triggered=self.close_active_frontend
381 triggered=self.close_active_frontend
382 )
382 )
383 self.add_menu_action(self.file_menu, self.close_action)
383 self.add_menu_action(self.file_menu, self.close_action)
384
384
385 self.export_action=QtGui.QAction("&Save to HTML/XHTML",
385 self.export_action=QtGui.QAction("&Save to HTML/XHTML",
386 self,
386 self,
387 shortcut=QtGui.QKeySequence.Save,
387 shortcut=QtGui.QKeySequence.Save,
388 triggered=self.export_action_active_frontend
388 triggered=self.export_action_active_frontend
389 )
389 )
390 self.add_menu_action(self.file_menu, self.export_action, True)
390 self.add_menu_action(self.file_menu, self.export_action, True)
391
391
392 self.file_menu.addSeparator()
392 self.file_menu.addSeparator()
393
393
394 printkey = QtGui.QKeySequence(QtGui.QKeySequence.Print)
394 printkey = QtGui.QKeySequence(QtGui.QKeySequence.Print)
395 if printkey.matches("Ctrl+P") and sys.platform != 'darwin':
395 if printkey.matches("Ctrl+P") and sys.platform != 'darwin':
396 # Only override the default if there is a collision.
396 # Only override the default if there is a collision.
397 # Qt ctrl = cmd on OSX, so the match gets a false positive on OSX.
397 # Qt ctrl = cmd on OSX, so the match gets a false positive on OSX.
398 printkey = "Ctrl+Shift+P"
398 printkey = "Ctrl+Shift+P"
399 self.print_action = QtGui.QAction("&Print",
399 self.print_action = QtGui.QAction("&Print",
400 self,
400 self,
401 shortcut=printkey,
401 shortcut=printkey,
402 triggered=self.print_action_active_frontend)
402 triggered=self.print_action_active_frontend)
403 self.add_menu_action(self.file_menu, self.print_action, True)
403 self.add_menu_action(self.file_menu, self.print_action, True)
404
404
405 if sys.platform != 'darwin':
405 if sys.platform != 'darwin':
406 # OSX always has Quit in the Application menu, only add it
406 # OSX always has Quit in the Application menu, only add it
407 # to the File menu elsewhere.
407 # to the File menu elsewhere.
408
408
409 self.file_menu.addSeparator()
409 self.file_menu.addSeparator()
410
410
411 self.quit_action = QtGui.QAction("&Quit",
411 self.quit_action = QtGui.QAction("&Quit",
412 self,
412 self,
413 shortcut=QtGui.QKeySequence.Quit,
413 shortcut=QtGui.QKeySequence.Quit,
414 triggered=self.close,
414 triggered=self.close,
415 )
415 )
416 self.add_menu_action(self.file_menu, self.quit_action)
416 self.add_menu_action(self.file_menu, self.quit_action)
417
417
418
418
419 def init_edit_menu(self):
419 def init_edit_menu(self):
420 self.edit_menu = self.menuBar().addMenu("&Edit")
420 self.edit_menu = self.menuBar().addMenu("&Edit")
421
421
422 self.undo_action = QtGui.QAction("&Undo",
422 self.undo_action = QtGui.QAction("&Undo",
423 self,
423 self,
424 shortcut=QtGui.QKeySequence.Undo,
424 shortcut=QtGui.QKeySequence.Undo,
425 statusTip="Undo last action if possible",
425 statusTip="Undo last action if possible",
426 triggered=self.undo_active_frontend
426 triggered=self.undo_active_frontend
427 )
427 )
428 self.add_menu_action(self.edit_menu, self.undo_action)
428 self.add_menu_action(self.edit_menu, self.undo_action)
429
429
430 self.redo_action = QtGui.QAction("&Redo",
430 self.redo_action = QtGui.QAction("&Redo",
431 self,
431 self,
432 shortcut=QtGui.QKeySequence.Redo,
432 shortcut=QtGui.QKeySequence.Redo,
433 statusTip="Redo last action if possible",
433 statusTip="Redo last action if possible",
434 triggered=self.redo_active_frontend)
434 triggered=self.redo_active_frontend)
435 self.add_menu_action(self.edit_menu, self.redo_action)
435 self.add_menu_action(self.edit_menu, self.redo_action)
436
436
437 self.edit_menu.addSeparator()
437 self.edit_menu.addSeparator()
438
438
439 self.cut_action = QtGui.QAction("&Cut",
439 self.cut_action = QtGui.QAction("&Cut",
440 self,
440 self,
441 shortcut=QtGui.QKeySequence.Cut,
441 shortcut=QtGui.QKeySequence.Cut,
442 triggered=self.cut_active_frontend
442 triggered=self.cut_active_frontend
443 )
443 )
444 self.add_menu_action(self.edit_menu, self.cut_action, True)
444 self.add_menu_action(self.edit_menu, self.cut_action, True)
445
445
446 self.copy_action = QtGui.QAction("&Copy",
446 self.copy_action = QtGui.QAction("&Copy",
447 self,
447 self,
448 shortcut=QtGui.QKeySequence.Copy,
448 shortcut=QtGui.QKeySequence.Copy,
449 triggered=self.copy_active_frontend
449 triggered=self.copy_active_frontend
450 )
450 )
451 self.add_menu_action(self.edit_menu, self.copy_action, True)
451 self.add_menu_action(self.edit_menu, self.copy_action, True)
452
452
453 self.copy_raw_action = QtGui.QAction("Copy (&Raw Text)",
453 self.copy_raw_action = QtGui.QAction("Copy (&Raw Text)",
454 self,
454 self,
455 shortcut="Ctrl+Shift+C",
455 shortcut="Ctrl+Shift+C",
456 triggered=self.copy_raw_active_frontend
456 triggered=self.copy_raw_active_frontend
457 )
457 )
458 self.add_menu_action(self.edit_menu, self.copy_raw_action, True)
458 self.add_menu_action(self.edit_menu, self.copy_raw_action, True)
459
459
460 self.paste_action = QtGui.QAction("&Paste",
460 self.paste_action = QtGui.QAction("&Paste",
461 self,
461 self,
462 shortcut=QtGui.QKeySequence.Paste,
462 shortcut=QtGui.QKeySequence.Paste,
463 triggered=self.paste_active_frontend
463 triggered=self.paste_active_frontend
464 )
464 )
465 self.add_menu_action(self.edit_menu, self.paste_action, True)
465 self.add_menu_action(self.edit_menu, self.paste_action, True)
466
466
467 self.edit_menu.addSeparator()
467 self.edit_menu.addSeparator()
468
468
469 selectall = QtGui.QKeySequence(QtGui.QKeySequence.SelectAll)
469 selectall = QtGui.QKeySequence(QtGui.QKeySequence.SelectAll)
470 if selectall.matches("Ctrl+A") and sys.platform != 'darwin':
470 if selectall.matches("Ctrl+A") and sys.platform != 'darwin':
471 # Only override the default if there is a collision.
471 # Only override the default if there is a collision.
472 # Qt ctrl = cmd on OSX, so the match gets a false positive on OSX.
472 # Qt ctrl = cmd on OSX, so the match gets a false positive on OSX.
473 selectall = "Ctrl+Shift+A"
473 selectall = "Ctrl+Shift+A"
474 self.select_all_action = QtGui.QAction("Select &All",
474 self.select_all_action = QtGui.QAction("Select &All",
475 self,
475 self,
476 shortcut=selectall,
476 shortcut=selectall,
477 triggered=self.select_all_active_frontend
477 triggered=self.select_all_active_frontend
478 )
478 )
479 self.add_menu_action(self.edit_menu, self.select_all_action, True)
479 self.add_menu_action(self.edit_menu, self.select_all_action, True)
480
480
481
481
482 def init_view_menu(self):
482 def init_view_menu(self):
483 self.view_menu = self.menuBar().addMenu("&View")
483 self.view_menu = self.menuBar().addMenu("&View")
484
484
485 if sys.platform != 'darwin':
485 if sys.platform != 'darwin':
486 # disable on OSX, where there is always a menu bar
486 # disable on OSX, where there is always a menu bar
487 self.toggle_menu_bar_act = QtGui.QAction("Toggle &Menu Bar",
487 self.toggle_menu_bar_act = QtGui.QAction("Toggle &Menu Bar",
488 self,
488 self,
489 shortcut="Ctrl+Shift+M",
489 shortcut="Ctrl+Shift+M",
490 statusTip="Toggle visibility of menubar",
490 statusTip="Toggle visibility of menubar",
491 triggered=self.toggle_menu_bar)
491 triggered=self.toggle_menu_bar)
492 self.add_menu_action(self.view_menu, self.toggle_menu_bar_act)
492 self.add_menu_action(self.view_menu, self.toggle_menu_bar_act)
493
493
494 fs_key = "Ctrl+Meta+F" if sys.platform == 'darwin' else "F11"
494 fs_key = "Ctrl+Meta+F" if sys.platform == 'darwin' else "F11"
495 self.full_screen_act = QtGui.QAction("&Full Screen",
495 self.full_screen_act = QtGui.QAction("&Full Screen",
496 self,
496 self,
497 shortcut=fs_key,
497 shortcut=fs_key,
498 statusTip="Toggle between Fullscreen and Normal Size",
498 statusTip="Toggle between Fullscreen and Normal Size",
499 triggered=self.toggleFullScreen)
499 triggered=self.toggleFullScreen)
500 self.add_menu_action(self.view_menu, self.full_screen_act)
500 self.add_menu_action(self.view_menu, self.full_screen_act)
501
501
502 self.view_menu.addSeparator()
502 self.view_menu.addSeparator()
503
503
504 self.increase_font_size = QtGui.QAction("Zoom &In",
504 self.increase_font_size = QtGui.QAction("Zoom &In",
505 self,
505 self,
506 shortcut=QtGui.QKeySequence.ZoomIn,
506 shortcut=QtGui.QKeySequence.ZoomIn,
507 triggered=self.increase_font_size_active_frontend
507 triggered=self.increase_font_size_active_frontend
508 )
508 )
509 self.add_menu_action(self.view_menu, self.increase_font_size, True)
509 self.add_menu_action(self.view_menu, self.increase_font_size, True)
510
510
511 self.decrease_font_size = QtGui.QAction("Zoom &Out",
511 self.decrease_font_size = QtGui.QAction("Zoom &Out",
512 self,
512 self,
513 shortcut=QtGui.QKeySequence.ZoomOut,
513 shortcut=QtGui.QKeySequence.ZoomOut,
514 triggered=self.decrease_font_size_active_frontend
514 triggered=self.decrease_font_size_active_frontend
515 )
515 )
516 self.add_menu_action(self.view_menu, self.decrease_font_size, True)
516 self.add_menu_action(self.view_menu, self.decrease_font_size, True)
517
517
518 self.reset_font_size = QtGui.QAction("Zoom &Reset",
518 self.reset_font_size = QtGui.QAction("Zoom &Reset",
519 self,
519 self,
520 shortcut="Ctrl+0",
520 shortcut="Ctrl+0",
521 triggered=self.reset_font_size_active_frontend
521 triggered=self.reset_font_size_active_frontend
522 )
522 )
523 self.add_menu_action(self.view_menu, self.reset_font_size, True)
523 self.add_menu_action(self.view_menu, self.reset_font_size, True)
524
524
525 self.view_menu.addSeparator()
525 self.view_menu.addSeparator()
526
526
527 self.clear_action = QtGui.QAction("&Clear Screen",
527 self.clear_action = QtGui.QAction("&Clear Screen",
528 self,
528 self,
529 shortcut='Ctrl+L',
529 shortcut='Ctrl+L',
530 statusTip="Clear the console",
530 statusTip="Clear the console",
531 triggered=self.clear_magic_active_frontend)
531 triggered=self.clear_magic_active_frontend)
532 self.add_menu_action(self.view_menu, self.clear_action)
532 self.add_menu_action(self.view_menu, self.clear_action)
533
533
534 self.pager_menu = self.view_menu.addMenu("&Pager")
534 self.pager_menu = self.view_menu.addMenu("&Pager")
535
535
536 hsplit_action = QtGui.QAction(".. &Horizontal Split",
536 hsplit_action = QtGui.QAction(".. &Horizontal Split",
537 self,
537 self,
538 triggered=lambda: self.set_paging_active_frontend('hsplit'))
538 triggered=lambda: self.set_paging_active_frontend('hsplit'))
539
539
540 vsplit_action = QtGui.QAction(" : &Vertical Split",
540 vsplit_action = QtGui.QAction(" : &Vertical Split",
541 self,
541 self,
542 triggered=lambda: self.set_paging_active_frontend('vsplit'))
542 triggered=lambda: self.set_paging_active_frontend('vsplit'))
543
543
544 inside_action = QtGui.QAction(" &Inside Pager",
544 inside_action = QtGui.QAction(" &Inside Pager",
545 self,
545 self,
546 triggered=lambda: self.set_paging_active_frontend('inside'))
546 triggered=lambda: self.set_paging_active_frontend('inside'))
547
547
548 self.pager_menu.addAction(hsplit_action)
548 self.pager_menu.addAction(hsplit_action)
549 self.pager_menu.addAction(vsplit_action)
549 self.pager_menu.addAction(vsplit_action)
550 self.pager_menu.addAction(inside_action)
550 self.pager_menu.addAction(inside_action)
551
551
552 def init_kernel_menu(self):
552 def init_kernel_menu(self):
553 self.kernel_menu = self.menuBar().addMenu("&Kernel")
553 self.kernel_menu = self.menuBar().addMenu("&Kernel")
554 # Qt on OSX maps Ctrl to Cmd, and Meta to Ctrl
554 # Qt on OSX maps Ctrl to Cmd, and Meta to Ctrl
555 # keep the signal shortcuts to ctrl, rather than
555 # keep the signal shortcuts to ctrl, rather than
556 # platform-default like we do elsewhere.
556 # platform-default like we do elsewhere.
557
557
558 ctrl = "Meta" if sys.platform == 'darwin' else "Ctrl"
558 ctrl = "Meta" if sys.platform == 'darwin' else "Ctrl"
559
559
560 self.interrupt_kernel_action = QtGui.QAction("&Interrupt current Kernel",
560 self.interrupt_kernel_action = QtGui.QAction("&Interrupt current Kernel",
561 self,
561 self,
562 triggered=self.interrupt_kernel_active_frontend,
562 triggered=self.interrupt_kernel_active_frontend,
563 shortcut=ctrl+"+C",
563 shortcut=ctrl+"+C",
564 )
564 )
565 self.add_menu_action(self.kernel_menu, self.interrupt_kernel_action)
565 self.add_menu_action(self.kernel_menu, self.interrupt_kernel_action)
566
566
567 self.restart_kernel_action = QtGui.QAction("&Restart current Kernel",
567 self.restart_kernel_action = QtGui.QAction("&Restart current Kernel",
568 self,
568 self,
569 triggered=self.restart_kernel_active_frontend,
569 triggered=self.restart_kernel_active_frontend,
570 shortcut=ctrl+"+.",
570 shortcut=ctrl+"+.",
571 )
571 )
572 self.add_menu_action(self.kernel_menu, self.restart_kernel_action)
572 self.add_menu_action(self.kernel_menu, self.restart_kernel_action)
573
573
574 self.kernel_menu.addSeparator()
574 self.kernel_menu.addSeparator()
575
575
576 self.confirm_restart_kernel_action = QtGui.QAction("&Confirm kernel restart",
576 self.confirm_restart_kernel_action = QtGui.QAction("&Confirm kernel restart",
577 self,
577 self,
578 checkable=True,
578 checkable=True,
579 checked=self.active_frontend.confirm_restart,
579 checked=self.active_frontend.confirm_restart,
580 triggered=self.toggle_confirm_restart_active_frontend
580 triggered=self.toggle_confirm_restart_active_frontend
581 )
581 )
582
582
583 self.add_menu_action(self.kernel_menu, self.confirm_restart_kernel_action)
583 self.add_menu_action(self.kernel_menu, self.confirm_restart_kernel_action)
584 self.tab_widget.currentChanged.connect(self.update_restart_checkbox)
584 self.tab_widget.currentChanged.connect(self.update_restart_checkbox)
585
585
586 def _make_dynamic_magic(self,magic):
586 def _make_dynamic_magic(self,magic):
587 """Return a function `fun` that will execute `magic` on active frontend.
587 """Return a function `fun` that will execute `magic` on active frontend.
588
588
589 Parameters
589 Parameters
590 ----------
590 ----------
591 magic : string
591 magic : string
592 string that will be executed as is when the returned function is called
592 string that will be executed as is when the returned function is called
593
593
594 Returns
594 Returns
595 -------
595 -------
596 fun : function
596 fun : function
597 function with no parameters, when called will execute `magic` on the
597 function with no parameters, when called will execute `magic` on the
598 current active frontend at call time
598 current active frontend at call time
599
599
600 See Also
600 See Also
601 --------
601 --------
602 populate_all_magic_menu : generate the "All Magics..." menu
602 populate_all_magic_menu : generate the "All Magics..." menu
603
603
604 Notes
604 Notes
605 -----
605 -----
606 `fun` executes `magic` in active frontend at the moment it is triggered,
606 `fun` executes `magic` in active frontend at the moment it is triggered,
607 not the active frontend at the moment it was created.
607 not the active frontend at the moment it was created.
608
608
609 This function is mostly used to create the "All Magics..." Menu at run time.
609 This function is mostly used to create the "All Magics..." Menu at run time.
610 """
610 """
611 # need two level nested function to be sure to pass magic
611 # need two level nested function to be sure to pass magic
612 # to active frontend **at run time**.
612 # to active frontend **at run time**.
613 def inner_dynamic_magic():
613 def inner_dynamic_magic():
614 self.active_frontend.execute(magic)
614 self.active_frontend.execute(magic)
615 inner_dynamic_magic.__name__ = "dynamics_magic_s"
615 inner_dynamic_magic.__name__ = "dynamics_magic_s"
616 return inner_dynamic_magic
616 return inner_dynamic_magic
617
617
618 def populate_all_magic_menu(self, listofmagic=None):
618 def populate_all_magic_menu(self, display_data=None):
619 """Clean "All Magics..." menu and repopulate it with `listofmagic`
619 """Clean "All Magics..." menu and repopulate it with `display_data`
620
620
621 Parameters
621 Parameters
622 ----------
622 ----------
623 listofmagic : string,
623 display_data : dict,
624 repr() of a list of strings, send back by the kernel
624 dict of display_data for the magics list.
625 Expects json data, as the result of MagicsManager.lsmagic_json()
625
626
626 Notes
627 -----
628 `listofmagic`is a repr() of list because it is fed with the result of
629 a 'user_expression'
630 """
627 """
631 for k,v in self._magic_menu_dict.items():
628 for k,v in self._magic_menu_dict.items():
632 v.clear()
629 v.clear()
633 self.all_magic_menu.clear()
630 self.all_magic_menu.clear()
631
632 if not display_data:
633 return
634
634
635 mlist = json.loads(display_data['data'].get('application/json', []))
635
636
636 mlist=ast.literal_eval(listofmagic)
637 for magic in mlist:
637 for magic in mlist:
638 cell = (magic['type'] == 'cell')
638 cell = (magic['type'] == 'cell')
639 name = magic['name']
639 name = magic['name']
640 mclass = magic['class']
640 mclass = magic['class']
641 if cell :
641 if cell :
642 prefix='%%'
642 prefix='%%'
643 else :
643 else :
644 prefix='%'
644 prefix='%'
645 magic_menu = self._get_magic_menu(mclass)
645 magic_menu = self._get_magic_menu(mclass)
646
646
647 pmagic = '%s%s'%(prefix,name)
647 pmagic = '%s%s'%(prefix,name)
648
648
649 xaction = QtGui.QAction(pmagic,
649 xaction = QtGui.QAction(pmagic,
650 self,
650 self,
651 triggered=self._make_dynamic_magic(pmagic)
651 triggered=self._make_dynamic_magic(pmagic)
652 )
652 )
653 magic_menu.addAction(xaction)
653 magic_menu.addAction(xaction)
654 self.all_magic_menu.addAction(xaction)
654 self.all_magic_menu.addAction(xaction)
655
655
656 def update_all_magic_menu(self):
656 def update_all_magic_menu(self):
657 """ Update the list of magics in the "All Magics..." Menu
657 """ Update the list of magics in the "All Magics..." Menu
658
658
659 Request the kernel with the list of available magics and populate the
659 Request the kernel with the list of available magics and populate the
660 menu with the list received back
660 menu with the list received back
661
661
662 """
662 """
663 self.active_frontend._silent_exec_callback('get_ipython().magics_manager.lsmagic_info()',
663 self.active_frontend._silent_exec_callback('get_ipython().magics_manager.lsmagic_json()',
664 self.populate_all_magic_menu)
664 self.populate_all_magic_menu)
665
665
666 def _get_magic_menu(self,menuidentifier, menulabel=None):
666 def _get_magic_menu(self,menuidentifier, menulabel=None):
667 """return a submagic menu by name, and create it if needed
667 """return a submagic menu by name, and create it if needed
668
668
669 parameters:
669 parameters:
670 -----------
670 -----------
671
671
672 menulabel : str
672 menulabel : str
673 Label for the menu
673 Label for the menu
674
674
675 Will infere the menu name from the identifier at creation if menulabel not given.
675 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
676 To do so you have too give menuidentifier as a CamelCassedString
677 """
677 """
678 menu = self._magic_menu_dict.get(menuidentifier,None)
678 menu = self._magic_menu_dict.get(menuidentifier,None)
679 if not menu :
679 if not menu :
680 if not menulabel:
680 if not menulabel:
681 menulabel = re.sub("([a-zA-Z]+)([A-Z][a-z])","\g<1> \g<2>",menuidentifier)
681 menulabel = re.sub("([a-zA-Z]+)([A-Z][a-z])","\g<1> \g<2>",menuidentifier)
682 menu = QtGui.QMenu(menulabel,self.magic_menu)
682 menu = QtGui.QMenu(menulabel,self.magic_menu)
683 self._magic_menu_dict[menuidentifier]=menu
683 self._magic_menu_dict[menuidentifier]=menu
684 self.magic_menu.insertMenu(self.magic_menu_separator,menu)
684 self.magic_menu.insertMenu(self.magic_menu_separator,menu)
685 return menu
685 return menu
686
686
687
687
688
688
689 def init_magic_menu(self):
689 def init_magic_menu(self):
690 self.magic_menu = self.menuBar().addMenu("&Magic")
690 self.magic_menu = self.menuBar().addMenu("&Magic")
691 self.magic_menu_separator = self.magic_menu.addSeparator()
691 self.magic_menu_separator = self.magic_menu.addSeparator()
692
692
693 self.all_magic_menu = self._get_magic_menu("AllMagics", menulabel="&All Magics...")
693 self.all_magic_menu = self._get_magic_menu("AllMagics", menulabel="&All Magics...")
694
694
695 # This action should usually not appear as it will be cleared when menu
695 # 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
696 # is updated at first kernel response. Though, it is necessary when
697 # connecting through X-forwarding, as in this case, the menu is not
697 # connecting through X-forwarding, as in this case, the menu is not
698 # auto updated, SO DO NOT DELETE.
698 # auto updated, SO DO NOT DELETE.
699 self.pop = QtGui.QAction("&Update All Magic Menu ",
699 self.pop = QtGui.QAction("&Update All Magic Menu ",
700 self, triggered=self.update_all_magic_menu)
700 self, triggered=self.update_all_magic_menu)
701 self.add_menu_action(self.all_magic_menu, self.pop)
701 self.add_menu_action(self.all_magic_menu, self.pop)
702 # we need to populate the 'Magic Menu' once the kernel has answer at
702 # 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
703 # least once let's do it immediately, but it's assured to works
704 self.pop.trigger()
704 self.pop.trigger()
705
705
706 self.reset_action = QtGui.QAction("&Reset",
706 self.reset_action = QtGui.QAction("&Reset",
707 self,
707 self,
708 statusTip="Clear all variables from workspace",
708 statusTip="Clear all variables from workspace",
709 triggered=self.reset_magic_active_frontend)
709 triggered=self.reset_magic_active_frontend)
710 self.add_menu_action(self.magic_menu, self.reset_action)
710 self.add_menu_action(self.magic_menu, self.reset_action)
711
711
712 self.history_action = QtGui.QAction("&History",
712 self.history_action = QtGui.QAction("&History",
713 self,
713 self,
714 statusTip="show command history",
714 statusTip="show command history",
715 triggered=self.history_magic_active_frontend)
715 triggered=self.history_magic_active_frontend)
716 self.add_menu_action(self.magic_menu, self.history_action)
716 self.add_menu_action(self.magic_menu, self.history_action)
717
717
718 self.save_action = QtGui.QAction("E&xport History ",
718 self.save_action = QtGui.QAction("E&xport History ",
719 self,
719 self,
720 statusTip="Export History as Python File",
720 statusTip="Export History as Python File",
721 triggered=self.save_magic_active_frontend)
721 triggered=self.save_magic_active_frontend)
722 self.add_menu_action(self.magic_menu, self.save_action)
722 self.add_menu_action(self.magic_menu, self.save_action)
723
723
724 self.who_action = QtGui.QAction("&Who",
724 self.who_action = QtGui.QAction("&Who",
725 self,
725 self,
726 statusTip="List interactive variables",
726 statusTip="List interactive variables",
727 triggered=self.who_magic_active_frontend)
727 triggered=self.who_magic_active_frontend)
728 self.add_menu_action(self.magic_menu, self.who_action)
728 self.add_menu_action(self.magic_menu, self.who_action)
729
729
730 self.who_ls_action = QtGui.QAction("Wh&o ls",
730 self.who_ls_action = QtGui.QAction("Wh&o ls",
731 self,
731 self,
732 statusTip="Return a list of interactive variables",
732 statusTip="Return a list of interactive variables",
733 triggered=self.who_ls_magic_active_frontend)
733 triggered=self.who_ls_magic_active_frontend)
734 self.add_menu_action(self.magic_menu, self.who_ls_action)
734 self.add_menu_action(self.magic_menu, self.who_ls_action)
735
735
736 self.whos_action = QtGui.QAction("Who&s",
736 self.whos_action = QtGui.QAction("Who&s",
737 self,
737 self,
738 statusTip="List interactive variables with details",
738 statusTip="List interactive variables with details",
739 triggered=self.whos_magic_active_frontend)
739 triggered=self.whos_magic_active_frontend)
740 self.add_menu_action(self.magic_menu, self.whos_action)
740 self.add_menu_action(self.magic_menu, self.whos_action)
741
741
742 def init_window_menu(self):
742 def init_window_menu(self):
743 self.window_menu = self.menuBar().addMenu("&Window")
743 self.window_menu = self.menuBar().addMenu("&Window")
744 if sys.platform == 'darwin':
744 if sys.platform == 'darwin':
745 # add min/maximize actions to OSX, which lacks default bindings.
745 # add min/maximize actions to OSX, which lacks default bindings.
746 self.minimizeAct = QtGui.QAction("Mini&mize",
746 self.minimizeAct = QtGui.QAction("Mini&mize",
747 self,
747 self,
748 shortcut="Ctrl+m",
748 shortcut="Ctrl+m",
749 statusTip="Minimize the window/Restore Normal Size",
749 statusTip="Minimize the window/Restore Normal Size",
750 triggered=self.toggleMinimized)
750 triggered=self.toggleMinimized)
751 # maximize is called 'Zoom' on OSX for some reason
751 # maximize is called 'Zoom' on OSX for some reason
752 self.maximizeAct = QtGui.QAction("&Zoom",
752 self.maximizeAct = QtGui.QAction("&Zoom",
753 self,
753 self,
754 shortcut="Ctrl+Shift+M",
754 shortcut="Ctrl+Shift+M",
755 statusTip="Maximize the window/Restore Normal Size",
755 statusTip="Maximize the window/Restore Normal Size",
756 triggered=self.toggleMaximized)
756 triggered=self.toggleMaximized)
757
757
758 self.add_menu_action(self.window_menu, self.minimizeAct)
758 self.add_menu_action(self.window_menu, self.minimizeAct)
759 self.add_menu_action(self.window_menu, self.maximizeAct)
759 self.add_menu_action(self.window_menu, self.maximizeAct)
760 self.window_menu.addSeparator()
760 self.window_menu.addSeparator()
761
761
762 prev_key = "Ctrl+Shift+Left" if sys.platform == 'darwin' else "Ctrl+PgUp"
762 prev_key = "Ctrl+Shift+Left" if sys.platform == 'darwin' else "Ctrl+PgUp"
763 self.prev_tab_act = QtGui.QAction("Pre&vious Tab",
763 self.prev_tab_act = QtGui.QAction("Pre&vious Tab",
764 self,
764 self,
765 shortcut=prev_key,
765 shortcut=prev_key,
766 statusTip="Select previous tab",
766 statusTip="Select previous tab",
767 triggered=self.prev_tab)
767 triggered=self.prev_tab)
768 self.add_menu_action(self.window_menu, self.prev_tab_act)
768 self.add_menu_action(self.window_menu, self.prev_tab_act)
769
769
770 next_key = "Ctrl+Shift+Right" if sys.platform == 'darwin' else "Ctrl+PgDown"
770 next_key = "Ctrl+Shift+Right" if sys.platform == 'darwin' else "Ctrl+PgDown"
771 self.next_tab_act = QtGui.QAction("Ne&xt Tab",
771 self.next_tab_act = QtGui.QAction("Ne&xt Tab",
772 self,
772 self,
773 shortcut=next_key,
773 shortcut=next_key,
774 statusTip="Select next tab",
774 statusTip="Select next tab",
775 triggered=self.next_tab)
775 triggered=self.next_tab)
776 self.add_menu_action(self.window_menu, self.next_tab_act)
776 self.add_menu_action(self.window_menu, self.next_tab_act)
777
777
778 def init_help_menu(self):
778 def init_help_menu(self):
779 # please keep the Help menu in Mac Os even if empty. It will
779 # please keep the Help menu in Mac Os even if empty. It will
780 # automatically contain a search field to search inside menus and
780 # automatically contain a search field to search inside menus and
781 # please keep it spelled in English, as long as Qt Doesn't support
781 # please keep it spelled in English, as long as Qt Doesn't support
782 # a QAction.MenuRole like HelpMenuRole otherwise it will lose
782 # a QAction.MenuRole like HelpMenuRole otherwise it will lose
783 # this search field functionality
783 # this search field functionality
784
784
785 self.help_menu = self.menuBar().addMenu("&Help")
785 self.help_menu = self.menuBar().addMenu("&Help")
786
786
787
787
788 # Help Menu
788 # Help Menu
789
789
790 self.intro_active_frontend_action = QtGui.QAction("&Intro to IPython",
790 self.intro_active_frontend_action = QtGui.QAction("&Intro to IPython",
791 self,
791 self,
792 triggered=self.intro_active_frontend
792 triggered=self.intro_active_frontend
793 )
793 )
794 self.add_menu_action(self.help_menu, self.intro_active_frontend_action)
794 self.add_menu_action(self.help_menu, self.intro_active_frontend_action)
795
795
796 self.quickref_active_frontend_action = QtGui.QAction("IPython &Cheat Sheet",
796 self.quickref_active_frontend_action = QtGui.QAction("IPython &Cheat Sheet",
797 self,
797 self,
798 triggered=self.quickref_active_frontend
798 triggered=self.quickref_active_frontend
799 )
799 )
800 self.add_menu_action(self.help_menu, self.quickref_active_frontend_action)
800 self.add_menu_action(self.help_menu, self.quickref_active_frontend_action)
801
801
802 self.guiref_active_frontend_action = QtGui.QAction("&Qt Console",
802 self.guiref_active_frontend_action = QtGui.QAction("&Qt Console",
803 self,
803 self,
804 triggered=self.guiref_active_frontend
804 triggered=self.guiref_active_frontend
805 )
805 )
806 self.add_menu_action(self.help_menu, self.guiref_active_frontend_action)
806 self.add_menu_action(self.help_menu, self.guiref_active_frontend_action)
807
807
808 self.onlineHelpAct = QtGui.QAction("Open Online &Help",
808 self.onlineHelpAct = QtGui.QAction("Open Online &Help",
809 self,
809 self,
810 triggered=self._open_online_help)
810 triggered=self._open_online_help)
811 self.add_menu_action(self.help_menu, self.onlineHelpAct)
811 self.add_menu_action(self.help_menu, self.onlineHelpAct)
812
812
813 # minimize/maximize/fullscreen actions:
813 # minimize/maximize/fullscreen actions:
814
814
815 def toggle_menu_bar(self):
815 def toggle_menu_bar(self):
816 menu_bar = self.menuBar()
816 menu_bar = self.menuBar()
817 if menu_bar.isVisible():
817 if menu_bar.isVisible():
818 menu_bar.setVisible(False)
818 menu_bar.setVisible(False)
819 else:
819 else:
820 menu_bar.setVisible(True)
820 menu_bar.setVisible(True)
821
821
822 def toggleMinimized(self):
822 def toggleMinimized(self):
823 if not self.isMinimized():
823 if not self.isMinimized():
824 self.showMinimized()
824 self.showMinimized()
825 else:
825 else:
826 self.showNormal()
826 self.showNormal()
827
827
828 def _open_online_help(self):
828 def _open_online_help(self):
829 filename="http://ipython.org/ipython-doc/stable/index.html"
829 filename="http://ipython.org/ipython-doc/stable/index.html"
830 webbrowser.open(filename, new=1, autoraise=True)
830 webbrowser.open(filename, new=1, autoraise=True)
831
831
832 def toggleMaximized(self):
832 def toggleMaximized(self):
833 if not self.isMaximized():
833 if not self.isMaximized():
834 self.showMaximized()
834 self.showMaximized()
835 else:
835 else:
836 self.showNormal()
836 self.showNormal()
837
837
838 # Min/Max imizing while in full screen give a bug
838 # Min/Max imizing while in full screen give a bug
839 # when going out of full screen, at least on OSX
839 # when going out of full screen, at least on OSX
840 def toggleFullScreen(self):
840 def toggleFullScreen(self):
841 if not self.isFullScreen():
841 if not self.isFullScreen():
842 self.showFullScreen()
842 self.showFullScreen()
843 if sys.platform == 'darwin':
843 if sys.platform == 'darwin':
844 self.maximizeAct.setEnabled(False)
844 self.maximizeAct.setEnabled(False)
845 self.minimizeAct.setEnabled(False)
845 self.minimizeAct.setEnabled(False)
846 else:
846 else:
847 self.showNormal()
847 self.showNormal()
848 if sys.platform == 'darwin':
848 if sys.platform == 'darwin':
849 self.maximizeAct.setEnabled(True)
849 self.maximizeAct.setEnabled(True)
850 self.minimizeAct.setEnabled(True)
850 self.minimizeAct.setEnabled(True)
851
851
852 def set_paging_active_frontend(self, paging):
852 def set_paging_active_frontend(self, paging):
853 self.active_frontend._set_paging(paging)
853 self.active_frontend._set_paging(paging)
854
854
855 def close_active_frontend(self):
855 def close_active_frontend(self):
856 self.close_tab(self.active_frontend)
856 self.close_tab(self.active_frontend)
857
857
858 def restart_kernel_active_frontend(self):
858 def restart_kernel_active_frontend(self):
859 self.active_frontend.request_restart_kernel()
859 self.active_frontend.request_restart_kernel()
860
860
861 def interrupt_kernel_active_frontend(self):
861 def interrupt_kernel_active_frontend(self):
862 self.active_frontend.request_interrupt_kernel()
862 self.active_frontend.request_interrupt_kernel()
863
863
864 def toggle_confirm_restart_active_frontend(self):
864 def toggle_confirm_restart_active_frontend(self):
865 widget = self.active_frontend
865 widget = self.active_frontend
866 widget.confirm_restart = not widget.confirm_restart
866 widget.confirm_restart = not widget.confirm_restart
867 self.confirm_restart_kernel_action.setChecked(widget.confirm_restart)
867 self.confirm_restart_kernel_action.setChecked(widget.confirm_restart)
868
868
869 def update_restart_checkbox(self):
869 def update_restart_checkbox(self):
870 if self.active_frontend is None:
870 if self.active_frontend is None:
871 return
871 return
872 widget = self.active_frontend
872 widget = self.active_frontend
873 self.confirm_restart_kernel_action.setChecked(widget.confirm_restart)
873 self.confirm_restart_kernel_action.setChecked(widget.confirm_restart)
874
874
875 def cut_active_frontend(self):
875 def cut_active_frontend(self):
876 widget = self.active_frontend
876 widget = self.active_frontend
877 if widget.can_cut():
877 if widget.can_cut():
878 widget.cut()
878 widget.cut()
879
879
880 def copy_active_frontend(self):
880 def copy_active_frontend(self):
881 widget = self.active_frontend
881 widget = self.active_frontend
882 widget.copy()
882 widget.copy()
883
883
884 def copy_raw_active_frontend(self):
884 def copy_raw_active_frontend(self):
885 self.active_frontend._copy_raw_action.trigger()
885 self.active_frontend._copy_raw_action.trigger()
886
886
887 def paste_active_frontend(self):
887 def paste_active_frontend(self):
888 widget = self.active_frontend
888 widget = self.active_frontend
889 if widget.can_paste():
889 if widget.can_paste():
890 widget.paste()
890 widget.paste()
891
891
892 def undo_active_frontend(self):
892 def undo_active_frontend(self):
893 self.active_frontend.undo()
893 self.active_frontend.undo()
894
894
895 def redo_active_frontend(self):
895 def redo_active_frontend(self):
896 self.active_frontend.redo()
896 self.active_frontend.redo()
897
897
898 def reset_magic_active_frontend(self):
898 def reset_magic_active_frontend(self):
899 self.active_frontend.execute("%reset")
899 self.active_frontend.execute("%reset")
900
900
901 def history_magic_active_frontend(self):
901 def history_magic_active_frontend(self):
902 self.active_frontend.execute("%history")
902 self.active_frontend.execute("%history")
903
903
904 def save_magic_active_frontend(self):
904 def save_magic_active_frontend(self):
905 self.active_frontend.save_magic()
905 self.active_frontend.save_magic()
906
906
907 def clear_magic_active_frontend(self):
907 def clear_magic_active_frontend(self):
908 self.active_frontend.execute("%clear")
908 self.active_frontend.execute("%clear")
909
909
910 def who_magic_active_frontend(self):
910 def who_magic_active_frontend(self):
911 self.active_frontend.execute("%who")
911 self.active_frontend.execute("%who")
912
912
913 def who_ls_magic_active_frontend(self):
913 def who_ls_magic_active_frontend(self):
914 self.active_frontend.execute("%who_ls")
914 self.active_frontend.execute("%who_ls")
915
915
916 def whos_magic_active_frontend(self):
916 def whos_magic_active_frontend(self):
917 self.active_frontend.execute("%whos")
917 self.active_frontend.execute("%whos")
918
918
919 def print_action_active_frontend(self):
919 def print_action_active_frontend(self):
920 self.active_frontend.print_action.trigger()
920 self.active_frontend.print_action.trigger()
921
921
922 def export_action_active_frontend(self):
922 def export_action_active_frontend(self):
923 self.active_frontend.export_action.trigger()
923 self.active_frontend.export_action.trigger()
924
924
925 def select_all_active_frontend(self):
925 def select_all_active_frontend(self):
926 self.active_frontend.select_all_action.trigger()
926 self.active_frontend.select_all_action.trigger()
927
927
928 def increase_font_size_active_frontend(self):
928 def increase_font_size_active_frontend(self):
929 self.active_frontend.increase_font_size.trigger()
929 self.active_frontend.increase_font_size.trigger()
930
930
931 def decrease_font_size_active_frontend(self):
931 def decrease_font_size_active_frontend(self):
932 self.active_frontend.decrease_font_size.trigger()
932 self.active_frontend.decrease_font_size.trigger()
933
933
934 def reset_font_size_active_frontend(self):
934 def reset_font_size_active_frontend(self):
935 self.active_frontend.reset_font_size.trigger()
935 self.active_frontend.reset_font_size.trigger()
936
936
937 def guiref_active_frontend(self):
937 def guiref_active_frontend(self):
938 self.active_frontend.execute("%guiref")
938 self.active_frontend.execute("%guiref")
939
939
940 def intro_active_frontend(self):
940 def intro_active_frontend(self):
941 self.active_frontend.execute("?")
941 self.active_frontend.execute("?")
942
942
943 def quickref_active_frontend(self):
943 def quickref_active_frontend(self):
944 self.active_frontend.execute("%quickref")
944 self.active_frontend.execute("%quickref")
945 #---------------------------------------------------------------------------
945 #---------------------------------------------------------------------------
946 # QWidget interface
946 # QWidget interface
947 #---------------------------------------------------------------------------
947 #---------------------------------------------------------------------------
948
948
949 def closeEvent(self, event):
949 def closeEvent(self, event):
950 """ Forward the close event to every tabs contained by the windows
950 """ Forward the close event to every tabs contained by the windows
951 """
951 """
952 if self.tab_widget.count() == 0:
952 if self.tab_widget.count() == 0:
953 # no tabs, just close
953 # no tabs, just close
954 event.accept()
954 event.accept()
955 return
955 return
956 # Do Not loop on the widget count as it change while closing
956 # Do Not loop on the widget count as it change while closing
957 title = self.window().windowTitle()
957 title = self.window().windowTitle()
958 cancel = QtGui.QMessageBox.Cancel
958 cancel = QtGui.QMessageBox.Cancel
959 okay = QtGui.QMessageBox.Ok
959 okay = QtGui.QMessageBox.Ok
960
960
961 if self.confirm_exit:
961 if self.confirm_exit:
962 if self.tab_widget.count() > 1:
962 if self.tab_widget.count() > 1:
963 msg = "Close all tabs, stop all kernels, and Quit?"
963 msg = "Close all tabs, stop all kernels, and Quit?"
964 else:
964 else:
965 msg = "Close console, stop kernel, and Quit?"
965 msg = "Close console, stop kernel, and Quit?"
966 info = "Kernels not started here (e.g. notebooks) will be left alone."
966 info = "Kernels not started here (e.g. notebooks) will be left alone."
967 closeall = QtGui.QPushButton("&Quit", self)
967 closeall = QtGui.QPushButton("&Quit", self)
968 closeall.setShortcut('Q')
968 closeall.setShortcut('Q')
969 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
969 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
970 title, msg)
970 title, msg)
971 box.setInformativeText(info)
971 box.setInformativeText(info)
972 box.addButton(cancel)
972 box.addButton(cancel)
973 box.addButton(closeall, QtGui.QMessageBox.YesRole)
973 box.addButton(closeall, QtGui.QMessageBox.YesRole)
974 box.setDefaultButton(closeall)
974 box.setDefaultButton(closeall)
975 box.setEscapeButton(cancel)
975 box.setEscapeButton(cancel)
976 pixmap = QtGui.QPixmap(self._app.icon.pixmap(QtCore.QSize(64,64)))
976 pixmap = QtGui.QPixmap(self._app.icon.pixmap(QtCore.QSize(64,64)))
977 box.setIconPixmap(pixmap)
977 box.setIconPixmap(pixmap)
978 reply = box.exec_()
978 reply = box.exec_()
979 else:
979 else:
980 reply = okay
980 reply = okay
981
981
982 if reply == cancel:
982 if reply == cancel:
983 event.ignore()
983 event.ignore()
984 return
984 return
985 if reply == okay:
985 if reply == okay:
986 while self.tab_widget.count() >= 1:
986 while self.tab_widget.count() >= 1:
987 # prevent further confirmations:
987 # prevent further confirmations:
988 widget = self.active_frontend
988 widget = self.active_frontend
989 widget._confirm_exit = False
989 widget._confirm_exit = False
990 self.close_tab(widget)
990 self.close_tab(widget)
991 event.accept()
991 event.accept()
992
992
General Comments 0
You need to be logged in to leave comments. Login now