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