##// END OF EJS Templates
disable calltips in PySide < 1.0.7 to prevent segfault...
MinRK -
Show More
@@ -1,659 +1,665 b''
1 1 from __future__ import print_function
2 2
3 3 # Standard library imports
4 4 from collections import namedtuple
5 5 import sys
6 6 import time
7 7
8 8 # System library imports
9 9 from pygments.lexers import PythonLexer
10 from IPython.external import qt
10 11 from IPython.external.qt import QtCore, QtGui
11 12
12 13 # Local imports
13 14 from IPython.core.inputsplitter import InputSplitter, transform_classic_prompt
14 15 from IPython.core.oinspect import call_tip
15 16 from IPython.frontend.qt.base_frontend_mixin import BaseFrontendMixin
16 17 from IPython.utils.traitlets import Bool, Instance, Unicode
17 18 from bracket_matcher import BracketMatcher
18 19 from call_tip_widget import CallTipWidget
19 20 from completion_lexer import CompletionLexer
20 21 from history_console_widget import HistoryConsoleWidget
21 22 from pygments_highlighter import PygmentsHighlighter
22 23
23 24
24 25 class FrontendHighlighter(PygmentsHighlighter):
25 26 """ A PygmentsHighlighter that understands and ignores prompts.
26 27 """
27 28
28 29 def __init__(self, frontend):
29 30 super(FrontendHighlighter, self).__init__(frontend._control.document())
30 31 self._current_offset = 0
31 32 self._frontend = frontend
32 33 self.highlighting_on = False
33 34
34 35 def highlightBlock(self, string):
35 36 """ Highlight a block of text. Reimplemented to highlight selectively.
36 37 """
37 38 if not self.highlighting_on:
38 39 return
39 40
40 41 # The input to this function is a unicode string that may contain
41 42 # paragraph break characters, non-breaking spaces, etc. Here we acquire
42 43 # the string as plain text so we can compare it.
43 44 current_block = self.currentBlock()
44 45 string = self._frontend._get_block_plain_text(current_block)
45 46
46 47 # Decide whether to check for the regular or continuation prompt.
47 48 if current_block.contains(self._frontend._prompt_pos):
48 49 prompt = self._frontend._prompt
49 50 else:
50 51 prompt = self._frontend._continuation_prompt
51 52
52 53 # Only highlight if we can identify a prompt, but make sure not to
53 54 # highlight the prompt.
54 55 if string.startswith(prompt):
55 56 self._current_offset = len(prompt)
56 57 string = string[len(prompt):]
57 58 super(FrontendHighlighter, self).highlightBlock(string)
58 59
59 60 def rehighlightBlock(self, block):
60 61 """ Reimplemented to temporarily enable highlighting if disabled.
61 62 """
62 63 old = self.highlighting_on
63 64 self.highlighting_on = True
64 65 super(FrontendHighlighter, self).rehighlightBlock(block)
65 66 self.highlighting_on = old
66 67
67 68 def setFormat(self, start, count, format):
68 69 """ Reimplemented to highlight selectively.
69 70 """
70 71 start += self._current_offset
71 72 super(FrontendHighlighter, self).setFormat(start, count, format)
72 73
73 74
74 75 class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):
75 76 """ A Qt frontend for a generic Python kernel.
76 77 """
77 78
78 79 # The text to show when the kernel is (re)started.
79 80 banner = Unicode()
80 81
81 82 # An option and corresponding signal for overriding the default kernel
82 83 # interrupt behavior.
83 84 custom_interrupt = Bool(False)
84 85 custom_interrupt_requested = QtCore.Signal()
85 86
86 87 # An option and corresponding signals for overriding the default kernel
87 88 # restart behavior.
88 89 custom_restart = Bool(False)
89 90 custom_restart_kernel_died = QtCore.Signal(float)
90 91 custom_restart_requested = QtCore.Signal()
91 92
92 93 # Whether to automatically show calltips on open-parentheses.
93 94 enable_calltips = Bool(True, config=True,
94 95 help="Whether to draw information calltips on open-parentheses.")
95 96
96 97 # Emitted when a user visible 'execute_request' has been submitted to the
97 98 # kernel from the FrontendWidget. Contains the code to be executed.
98 99 executing = QtCore.Signal(object)
99 100
100 101 # Emitted when a user-visible 'execute_reply' has been received from the
101 102 # kernel and processed by the FrontendWidget. Contains the response message.
102 103 executed = QtCore.Signal(object)
103 104
104 105 # Emitted when an exit request has been received from the kernel.
105 106 exit_requested = QtCore.Signal(object)
106 107
107 108 # Protected class variables.
108 109 _CallTipRequest = namedtuple('_CallTipRequest', ['id', 'pos'])
109 110 _CompletionRequest = namedtuple('_CompletionRequest', ['id', 'pos'])
110 111 _ExecutionRequest = namedtuple('_ExecutionRequest', ['id', 'kind'])
111 112 _input_splitter_class = InputSplitter
112 113 _local_kernel = False
113 114 _highlighter = Instance(FrontendHighlighter)
114 115
115 116 #---------------------------------------------------------------------------
116 117 # 'object' interface
117 118 #---------------------------------------------------------------------------
118 119
119 120 def __init__(self, *args, **kw):
120 121 super(FrontendWidget, self).__init__(*args, **kw)
122 # forcefully disable calltips if PySide is < 1.0.7, because they crash
123 if qt.QT_API == qt.QT_API_PYSIDE:
124 import PySide
125 if PySide.__version_info__ < (1,0,7):
126 self.enable_calltips = False
121 127
122 128 # FrontendWidget protected variables.
123 129 self._bracket_matcher = BracketMatcher(self._control)
124 130 self._call_tip_widget = CallTipWidget(self._control)
125 131 self._completion_lexer = CompletionLexer(PythonLexer())
126 132 self._copy_raw_action = QtGui.QAction('Copy (Raw Text)', None)
127 133 self._hidden = False
128 134 self._highlighter = FrontendHighlighter(self)
129 135 self._input_splitter = self._input_splitter_class(input_mode='cell')
130 136 self._kernel_manager = None
131 137 self._request_info = {}
132 138
133 139 # Configure the ConsoleWidget.
134 140 self.tab_width = 4
135 141 self._set_continuation_prompt('... ')
136 142
137 143 # Configure the CallTipWidget.
138 144 self._call_tip_widget.setFont(self.font)
139 145 self.font_changed.connect(self._call_tip_widget.setFont)
140 146
141 147 # Configure actions.
142 148 action = self._copy_raw_action
143 149 key = QtCore.Qt.CTRL | QtCore.Qt.SHIFT | QtCore.Qt.Key_C
144 150 action.setEnabled(False)
145 151 action.setShortcut(QtGui.QKeySequence(key))
146 152 action.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut)
147 153 action.triggered.connect(self.copy_raw)
148 154 self.copy_available.connect(action.setEnabled)
149 155 self.addAction(action)
150 156
151 157 # Connect signal handlers.
152 158 document = self._control.document()
153 159 document.contentsChange.connect(self._document_contents_change)
154 160
155 161 # Set flag for whether we are connected via localhost.
156 162 self._local_kernel = kw.get('local_kernel',
157 163 FrontendWidget._local_kernel)
158 164
159 165 #---------------------------------------------------------------------------
160 166 # 'ConsoleWidget' public interface
161 167 #---------------------------------------------------------------------------
162 168
163 169 def copy(self):
164 170 """ Copy the currently selected text to the clipboard, removing prompts.
165 171 """
166 172 text = self._control.textCursor().selection().toPlainText()
167 173 if text:
168 174 lines = map(transform_classic_prompt, text.splitlines())
169 175 text = '\n'.join(lines)
170 176 QtGui.QApplication.clipboard().setText(text)
171 177
172 178 #---------------------------------------------------------------------------
173 179 # 'ConsoleWidget' abstract interface
174 180 #---------------------------------------------------------------------------
175 181
176 182 def _is_complete(self, source, interactive):
177 183 """ Returns whether 'source' can be completely processed and a new
178 184 prompt created. When triggered by an Enter/Return key press,
179 185 'interactive' is True; otherwise, it is False.
180 186 """
181 187 complete = self._input_splitter.push(source)
182 188 if interactive:
183 189 complete = not self._input_splitter.push_accepts_more()
184 190 return complete
185 191
186 192 def _execute(self, source, hidden):
187 193 """ Execute 'source'. If 'hidden', do not show any output.
188 194
189 195 See parent class :meth:`execute` docstring for full details.
190 196 """
191 197 msg_id = self.kernel_manager.shell_channel.execute(source, hidden)
192 198 self._request_info['execute'] = self._ExecutionRequest(msg_id, 'user')
193 199 self._hidden = hidden
194 200 if not hidden:
195 201 self.executing.emit(source)
196 202
197 203 def _prompt_started_hook(self):
198 204 """ Called immediately after a new prompt is displayed.
199 205 """
200 206 if not self._reading:
201 207 self._highlighter.highlighting_on = True
202 208
203 209 def _prompt_finished_hook(self):
204 210 """ Called immediately after a prompt is finished, i.e. when some input
205 211 will be processed and a new prompt displayed.
206 212 """
207 213 # Flush all state from the input splitter so the next round of
208 214 # reading input starts with a clean buffer.
209 215 self._input_splitter.reset()
210 216
211 217 if not self._reading:
212 218 self._highlighter.highlighting_on = False
213 219
214 220 def _tab_pressed(self):
215 221 """ Called when the tab key is pressed. Returns whether to continue
216 222 processing the event.
217 223 """
218 224 # Perform tab completion if:
219 225 # 1) The cursor is in the input buffer.
220 226 # 2) There is a non-whitespace character before the cursor.
221 227 text = self._get_input_buffer_cursor_line()
222 228 if text is None:
223 229 return False
224 230 complete = bool(text[:self._get_input_buffer_cursor_column()].strip())
225 231 if complete:
226 232 self._complete()
227 233 return not complete
228 234
229 235 #---------------------------------------------------------------------------
230 236 # 'ConsoleWidget' protected interface
231 237 #---------------------------------------------------------------------------
232 238
233 239 def _context_menu_make(self, pos):
234 240 """ Reimplemented to add an action for raw copy.
235 241 """
236 242 menu = super(FrontendWidget, self)._context_menu_make(pos)
237 243 for before_action in menu.actions():
238 244 if before_action.shortcut().matches(QtGui.QKeySequence.Paste) == \
239 245 QtGui.QKeySequence.ExactMatch:
240 246 menu.insertAction(before_action, self._copy_raw_action)
241 247 break
242 248 return menu
243 249
244 250 def request_interrupt_kernel(self):
245 251 if self._executing:
246 252 self.interrupt_kernel()
247 253
248 254 def request_restart_kernel(self):
249 255 message = 'Are you sure you want to restart the kernel?'
250 256 self.restart_kernel(message, now=False)
251 257
252 258 def _event_filter_console_keypress(self, event):
253 259 """ Reimplemented for execution interruption and smart backspace.
254 260 """
255 261 key = event.key()
256 262 if self._control_key_down(event.modifiers(), include_command=False):
257 263
258 264 if key == QtCore.Qt.Key_C and self._executing:
259 265 self.request_interrupt_kernel()
260 266 return True
261 267
262 268 elif key == QtCore.Qt.Key_Period:
263 269 self.request_restart_kernel()
264 270 return True
265 271
266 272 elif not event.modifiers() & QtCore.Qt.AltModifier:
267 273
268 274 # Smart backspace: remove four characters in one backspace if:
269 275 # 1) everything left of the cursor is whitespace
270 276 # 2) the four characters immediately left of the cursor are spaces
271 277 if key == QtCore.Qt.Key_Backspace:
272 278 col = self._get_input_buffer_cursor_column()
273 279 cursor = self._control.textCursor()
274 280 if col > 3 and not cursor.hasSelection():
275 281 text = self._get_input_buffer_cursor_line()[:col]
276 282 if text.endswith(' ') and not text.strip():
277 283 cursor.movePosition(QtGui.QTextCursor.Left,
278 284 QtGui.QTextCursor.KeepAnchor, 4)
279 285 cursor.removeSelectedText()
280 286 return True
281 287
282 288 return super(FrontendWidget, self)._event_filter_console_keypress(event)
283 289
284 290 def _insert_continuation_prompt(self, cursor):
285 291 """ Reimplemented for auto-indentation.
286 292 """
287 293 super(FrontendWidget, self)._insert_continuation_prompt(cursor)
288 294 cursor.insertText(' ' * self._input_splitter.indent_spaces)
289 295
290 296 #---------------------------------------------------------------------------
291 297 # 'BaseFrontendMixin' abstract interface
292 298 #---------------------------------------------------------------------------
293 299
294 300 def _handle_complete_reply(self, rep):
295 301 """ Handle replies for tab completion.
296 302 """
297 303 self.log.debug("complete: %s", rep.get('content', ''))
298 304 cursor = self._get_cursor()
299 305 info = self._request_info.get('complete')
300 306 if info and info.id == rep['parent_header']['msg_id'] and \
301 307 info.pos == cursor.position():
302 308 text = '.'.join(self._get_context())
303 309 cursor.movePosition(QtGui.QTextCursor.Left, n=len(text))
304 310 self._complete_with_items(cursor, rep['content']['matches'])
305 311
306 312 def _handle_execute_reply(self, msg):
307 313 """ Handles replies for code execution.
308 314 """
309 315 self.log.debug("execute: %s", msg.get('content', ''))
310 316 info = self._request_info.get('execute')
311 317 # unset reading flag, because if execute finished, raw_input can't
312 318 # still be pending.
313 319 self._reading = False
314 320 if info and info.id == msg['parent_header']['msg_id'] and \
315 321 info.kind == 'user' and not self._hidden:
316 322 # Make sure that all output from the SUB channel has been processed
317 323 # before writing a new prompt.
318 324 self.kernel_manager.sub_channel.flush()
319 325
320 326 # Reset the ANSI style information to prevent bad text in stdout
321 327 # from messing up our colors. We're not a true terminal so we're
322 328 # allowed to do this.
323 329 if self.ansi_codes:
324 330 self._ansi_processor.reset_sgr()
325 331
326 332 content = msg['content']
327 333 status = content['status']
328 334 if status == 'ok':
329 335 self._process_execute_ok(msg)
330 336 elif status == 'error':
331 337 self._process_execute_error(msg)
332 338 elif status == 'aborted':
333 339 self._process_execute_abort(msg)
334 340
335 341 self._show_interpreter_prompt_for_reply(msg)
336 342 self.executed.emit(msg)
337 343 else:
338 344 super(FrontendWidget, self)._handle_execute_reply(msg)
339 345
340 346 def _handle_input_request(self, msg):
341 347 """ Handle requests for raw_input.
342 348 """
343 349 self.log.debug("input: %s", msg.get('content', ''))
344 350 if self._hidden:
345 351 raise RuntimeError('Request for raw input during hidden execution.')
346 352
347 353 # Make sure that all output from the SUB channel has been processed
348 354 # before entering readline mode.
349 355 self.kernel_manager.sub_channel.flush()
350 356
351 357 def callback(line):
352 358 self.kernel_manager.stdin_channel.input(line)
353 359 if self._reading:
354 360 self.log.debug("Got second input request, assuming first was interrupted.")
355 361 self._reading = False
356 362 self._readline(msg['content']['prompt'], callback=callback)
357 363
358 364 def _handle_kernel_died(self, since_last_heartbeat):
359 365 """ Handle the kernel's death by asking if the user wants to restart.
360 366 """
361 367 self.log.debug("kernel died: %s", since_last_heartbeat)
362 368 if self.custom_restart:
363 369 self.custom_restart_kernel_died.emit(since_last_heartbeat)
364 370 else:
365 371 message = 'The kernel heartbeat has been inactive for %.2f ' \
366 372 'seconds. Do you want to restart the kernel? You may ' \
367 373 'first want to check the network connection.' % \
368 374 since_last_heartbeat
369 375 self.restart_kernel(message, now=True)
370 376
371 377 def _handle_object_info_reply(self, rep):
372 378 """ Handle replies for call tips.
373 379 """
374 380 self.log.debug("oinfo: %s", rep.get('content', ''))
375 381 cursor = self._get_cursor()
376 382 info = self._request_info.get('call_tip')
377 383 if info and info.id == rep['parent_header']['msg_id'] and \
378 384 info.pos == cursor.position():
379 385 # Get the information for a call tip. For now we format the call
380 386 # line as string, later we can pass False to format_call and
381 387 # syntax-highlight it ourselves for nicer formatting in the
382 388 # calltip.
383 389 content = rep['content']
384 390 # if this is from pykernel, 'docstring' will be the only key
385 391 if content.get('ismagic', False):
386 392 # Don't generate a call-tip for magics. Ideally, we should
387 393 # generate a tooltip, but not on ( like we do for actual
388 394 # callables.
389 395 call_info, doc = None, None
390 396 else:
391 397 call_info, doc = call_tip(content, format_call=True)
392 398 if call_info or doc:
393 399 self._call_tip_widget.show_call_info(call_info, doc)
394 400
395 401 def _handle_pyout(self, msg):
396 402 """ Handle display hook output.
397 403 """
398 404 self.log.debug("pyout: %s", msg.get('content', ''))
399 405 if not self._hidden and self._is_from_this_session(msg):
400 406 text = msg['content']['data']
401 407 self._append_plain_text(text + '\n', before_prompt=True)
402 408
403 409 def _handle_stream(self, msg):
404 410 """ Handle stdout, stderr, and stdin.
405 411 """
406 412 self.log.debug("stream: %s", msg.get('content', ''))
407 413 if not self._hidden and self._is_from_this_session(msg):
408 414 # Most consoles treat tabs as being 8 space characters. Convert tabs
409 415 # to spaces so that output looks as expected regardless of this
410 416 # widget's tab width.
411 417 text = msg['content']['data'].expandtabs(8)
412 418
413 419 self._append_plain_text(text, before_prompt=True)
414 420 self._control.moveCursor(QtGui.QTextCursor.End)
415 421
416 422 def _handle_shutdown_reply(self, msg):
417 423 """ Handle shutdown signal, only if from other console.
418 424 """
419 425 self.log.debug("shutdown: %s", msg.get('content', ''))
420 426 if not self._hidden and not self._is_from_this_session(msg):
421 427 if self._local_kernel:
422 428 if not msg['content']['restart']:
423 429 self.exit_requested.emit(self)
424 430 else:
425 431 # we just got notified of a restart!
426 432 time.sleep(0.25) # wait 1/4 sec to reset
427 433 # lest the request for a new prompt
428 434 # goes to the old kernel
429 435 self.reset()
430 436 else: # remote kernel, prompt on Kernel shutdown/reset
431 437 title = self.window().windowTitle()
432 438 if not msg['content']['restart']:
433 439 reply = QtGui.QMessageBox.question(self, title,
434 440 "Kernel has been shutdown permanently. "
435 441 "Close the Console?",
436 442 QtGui.QMessageBox.Yes,QtGui.QMessageBox.No)
437 443 if reply == QtGui.QMessageBox.Yes:
438 444 self.exit_requested.emit(self)
439 445 else:
440 446 reply = QtGui.QMessageBox.question(self, title,
441 447 "Kernel has been reset. Clear the Console?",
442 448 QtGui.QMessageBox.Yes,QtGui.QMessageBox.No)
443 449 if reply == QtGui.QMessageBox.Yes:
444 450 time.sleep(0.25) # wait 1/4 sec to reset
445 451 # lest the request for a new prompt
446 452 # goes to the old kernel
447 453 self.reset()
448 454
449 455 def _started_channels(self):
450 456 """ Called when the KernelManager channels have started listening or
451 457 when the frontend is assigned an already listening KernelManager.
452 458 """
453 459 self.reset()
454 460
455 461 #---------------------------------------------------------------------------
456 462 # 'FrontendWidget' public interface
457 463 #---------------------------------------------------------------------------
458 464
459 465 def copy_raw(self):
460 466 """ Copy the currently selected text to the clipboard without attempting
461 467 to remove prompts or otherwise alter the text.
462 468 """
463 469 self._control.copy()
464 470
465 471 def execute_file(self, path, hidden=False):
466 472 """ Attempts to execute file with 'path'. If 'hidden', no output is
467 473 shown.
468 474 """
469 475 self.execute('execfile(%r)' % path, hidden=hidden)
470 476
471 477 def interrupt_kernel(self):
472 478 """ Attempts to interrupt the running kernel.
473 479
474 480 Also unsets _reading flag, to avoid runtime errors
475 481 if raw_input is called again.
476 482 """
477 483 if self.custom_interrupt:
478 484 self._reading = False
479 485 self.custom_interrupt_requested.emit()
480 486 elif self.kernel_manager.has_kernel:
481 487 self._reading = False
482 488 self.kernel_manager.interrupt_kernel()
483 489 else:
484 490 self._append_plain_text('Kernel process is either remote or '
485 491 'unspecified. Cannot interrupt.\n')
486 492
487 493 def reset(self):
488 494 """ Resets the widget to its initial state. Similar to ``clear``, but
489 495 also re-writes the banner and aborts execution if necessary.
490 496 """
491 497 if self._executing:
492 498 self._executing = False
493 499 self._request_info['execute'] = None
494 500 self._reading = False
495 501 self._highlighter.highlighting_on = False
496 502
497 503 self._control.clear()
498 504 self._append_plain_text(self.banner)
499 505 # update output marker for stdout/stderr, so that startup
500 506 # messages appear after banner:
501 507 self._append_before_prompt_pos = self._get_cursor().position()
502 508 self._show_interpreter_prompt()
503 509
504 510 def restart_kernel(self, message, now=False):
505 511 """ Attempts to restart the running kernel.
506 512 """
507 513 # FIXME: now should be configurable via a checkbox in the dialog. Right
508 514 # now at least the heartbeat path sets it to True and the manual restart
509 515 # to False. But those should just be the pre-selected states of a
510 516 # checkbox that the user could override if so desired. But I don't know
511 517 # enough Qt to go implementing the checkbox now.
512 518
513 519 if self.custom_restart:
514 520 self.custom_restart_requested.emit()
515 521
516 522 elif self.kernel_manager.has_kernel:
517 523 # Pause the heart beat channel to prevent further warnings.
518 524 self.kernel_manager.hb_channel.pause()
519 525
520 526 # Prompt the user to restart the kernel. Un-pause the heartbeat if
521 527 # they decline. (If they accept, the heartbeat will be un-paused
522 528 # automatically when the kernel is restarted.)
523 529 buttons = QtGui.QMessageBox.Yes | QtGui.QMessageBox.No
524 530 result = QtGui.QMessageBox.question(self, 'Restart kernel?',
525 531 message, buttons)
526 532 if result == QtGui.QMessageBox.Yes:
527 533 try:
528 534 self.kernel_manager.restart_kernel(now=now)
529 535 except RuntimeError:
530 536 self._append_plain_text('Kernel started externally. '
531 537 'Cannot restart.\n')
532 538 else:
533 539 self.reset()
534 540 else:
535 541 self.kernel_manager.hb_channel.unpause()
536 542
537 543 else:
538 544 self._append_plain_text('Kernel process is either remote or '
539 545 'unspecified. Cannot restart.\n')
540 546
541 547 #---------------------------------------------------------------------------
542 548 # 'FrontendWidget' protected interface
543 549 #---------------------------------------------------------------------------
544 550
545 551 def _call_tip(self):
546 552 """ Shows a call tip, if appropriate, at the current cursor location.
547 553 """
548 554 # Decide if it makes sense to show a call tip
549 555 if not self.enable_calltips:
550 556 return False
551 557 cursor = self._get_cursor()
552 558 cursor.movePosition(QtGui.QTextCursor.Left)
553 559 if cursor.document().characterAt(cursor.position()) != '(':
554 560 return False
555 561 context = self._get_context(cursor)
556 562 if not context:
557 563 return False
558 564
559 565 # Send the metadata request to the kernel
560 566 name = '.'.join(context)
561 567 msg_id = self.kernel_manager.shell_channel.object_info(name)
562 568 pos = self._get_cursor().position()
563 569 self._request_info['call_tip'] = self._CallTipRequest(msg_id, pos)
564 570 return True
565 571
566 572 def _complete(self):
567 573 """ Performs completion at the current cursor location.
568 574 """
569 575 context = self._get_context()
570 576 if context:
571 577 # Send the completion request to the kernel
572 578 msg_id = self.kernel_manager.shell_channel.complete(
573 579 '.'.join(context), # text
574 580 self._get_input_buffer_cursor_line(), # line
575 581 self._get_input_buffer_cursor_column(), # cursor_pos
576 582 self.input_buffer) # block
577 583 pos = self._get_cursor().position()
578 584 info = self._CompletionRequest(msg_id, pos)
579 585 self._request_info['complete'] = info
580 586
581 587 def _get_context(self, cursor=None):
582 588 """ Gets the context for the specified cursor (or the current cursor
583 589 if none is specified).
584 590 """
585 591 if cursor is None:
586 592 cursor = self._get_cursor()
587 593 cursor.movePosition(QtGui.QTextCursor.StartOfBlock,
588 594 QtGui.QTextCursor.KeepAnchor)
589 595 text = cursor.selection().toPlainText()
590 596 return self._completion_lexer.get_context(text)
591 597
592 598 def _process_execute_abort(self, msg):
593 599 """ Process a reply for an aborted execution request.
594 600 """
595 601 self._append_plain_text("ERROR: execution aborted\n")
596 602
597 603 def _process_execute_error(self, msg):
598 604 """ Process a reply for an execution request that resulted in an error.
599 605 """
600 606 content = msg['content']
601 607 # If a SystemExit is passed along, this means exit() was called - also
602 608 # all the ipython %exit magic syntax of '-k' to be used to keep
603 609 # the kernel running
604 610 if content['ename']=='SystemExit':
605 611 keepkernel = content['evalue']=='-k' or content['evalue']=='True'
606 612 self._keep_kernel_on_exit = keepkernel
607 613 self.exit_requested.emit(self)
608 614 else:
609 615 traceback = ''.join(content['traceback'])
610 616 self._append_plain_text(traceback)
611 617
612 618 def _process_execute_ok(self, msg):
613 619 """ Process a reply for a successful execution equest.
614 620 """
615 621 payload = msg['content']['payload']
616 622 for item in payload:
617 623 if not self._process_execute_payload(item):
618 624 warning = 'Warning: received unknown payload of type %s'
619 625 print(warning % repr(item['source']))
620 626
621 627 def _process_execute_payload(self, item):
622 628 """ Process a single payload item from the list of payload items in an
623 629 execution reply. Returns whether the payload was handled.
624 630 """
625 631 # The basic FrontendWidget doesn't handle payloads, as they are a
626 632 # mechanism for going beyond the standard Python interpreter model.
627 633 return False
628 634
629 635 def _show_interpreter_prompt(self):
630 636 """ Shows a prompt for the interpreter.
631 637 """
632 638 self._show_prompt('>>> ')
633 639
634 640 def _show_interpreter_prompt_for_reply(self, msg):
635 641 """ Shows a prompt for the interpreter given an 'execute_reply' message.
636 642 """
637 643 self._show_interpreter_prompt()
638 644
639 645 #------ Signal handlers ----------------------------------------------------
640 646
641 647 def _document_contents_change(self, position, removed, added):
642 648 """ Called whenever the document's content changes. Display a call tip
643 649 if appropriate.
644 650 """
645 651 # Calculate where the cursor should be *after* the change:
646 652 position += added
647 653
648 654 document = self._control.document()
649 655 if position == self._get_cursor().position():
650 656 self._call_tip()
651 657
652 658 #------ Trait default initializers -----------------------------------------
653 659
654 660 def _banner_default(self):
655 661 """ Returns the standard Python banner.
656 662 """
657 663 banner = 'Python %s on %s\nType "help", "copyright", "credits" or ' \
658 664 '"license" for more information.'
659 665 return banner % (sys.version, sys.platform)
General Comments 0
You need to be logged in to leave comments. Login now