##// END OF EJS Templates
Updated to FrontendWidget to reflect BlockBreaker API changes.
Updated to FrontendWidget to reflect BlockBreaker API changes.

File last commit:

r2685:babedec4
r2685:babedec4
Show More
frontend_widget.py
306 lines | 11.6 KiB | text/x-python | PythonLexer
epatters
Initial checkin of Qt frontend code.
r2602 # System library imports
from pygments.lexers import PythonLexer
from PyQt4 import QtCore, QtGui
import zmq
# Local imports
epatters
Updated to FrontendWidget to reflect BlockBreaker API changes.
r2685 from IPython.core.inputsplitter import InputSplitter
epatters
Initial checkin of Qt frontend code.
r2602 from call_tip_widget import CallTipWidget
from completion_lexer import CompletionLexer
from console_widget import HistoryConsoleWidget
epatters
Fixed imports and removed references to ETS/EPD
r2603 from pygments_highlighter import PygmentsHighlighter
epatters
Initial checkin of Qt frontend code.
r2602
class FrontendHighlighter(PygmentsHighlighter):
""" A Python PygmentsHighlighter that can be turned on and off and which
knows about continuation prompts.
"""
def __init__(self, frontend):
PygmentsHighlighter.__init__(self, frontend.document(), PythonLexer())
self._current_offset = 0
self._frontend = frontend
self.highlighting_on = False
def highlightBlock(self, qstring):
""" Highlight a block of text. Reimplemented to highlight selectively.
"""
if self.highlighting_on:
epatters
* Added 'started_listening' and 'stopped_listening' signals to QtKernelManager. The FrontendWidget listens for these signals....
r2643 for prompt in (self._frontend._continuation_prompt,
self._frontend._prompt):
epatters
Initial checkin of Qt frontend code.
r2602 if qstring.startsWith(prompt):
qstring.remove(0, len(prompt))
self._current_offset = len(prompt)
break
PygmentsHighlighter.highlightBlock(self, qstring)
def setFormat(self, start, count, format):
""" Reimplemented to avoid highlighting continuation prompts.
"""
start += self._current_offset
PygmentsHighlighter.setFormat(self, start, count, format)
class FrontendWidget(HistoryConsoleWidget):
epatters
* Created an IPythonWidget subclass of FrontendWidget to contain IPython specific functionality....
r2627 """ A Qt frontend for a generic Python kernel.
epatters
Initial checkin of Qt frontend code.
r2602 """
# Emitted when an 'execute_reply' is received from the kernel.
epatters
* Refactored KernelManager to use Traitlets and to have its channels as attributes...
r2611 executed = QtCore.pyqtSignal(object)
epatters
Initial checkin of Qt frontend code.
r2602
#---------------------------------------------------------------------------
epatters
Minor comment cleanup.
r2669 # 'QObject' interface
epatters
Initial checkin of Qt frontend code.
r2602 #---------------------------------------------------------------------------
epatters
* Added 'started_listening' and 'stopped_listening' signals to QtKernelManager. The FrontendWidget listens for these signals....
r2643 def __init__(self, parent=None):
epatters
Initial checkin of Qt frontend code.
r2602 super(FrontendWidget, self).__init__(parent)
epatters
* Added 'started_listening' and 'stopped_listening' signals to QtKernelManager. The FrontendWidget listens for these signals....
r2643 # ConsoleWidget protected variables.
self._continuation_prompt = '... '
self._prompt = '>>> '
# FrontendWidget protected variables.
epatters
Initial checkin of Qt frontend code.
r2602 self._call_tip_widget = CallTipWidget(self)
self._completion_lexer = CompletionLexer(PythonLexer())
epatters
* Created an IPythonWidget subclass of FrontendWidget to contain IPython specific functionality....
r2627 self._hidden = True
epatters
Initial checkin of Qt frontend code.
r2602 self._highlighter = FrontendHighlighter(self)
epatters
Updated to FrontendWidget to reflect BlockBreaker API changes.
r2685 self._input_splitter = InputSplitter(input_mode='replace')
epatters
* Refactored KernelManager to use Traitlets and to have its channels as attributes...
r2611 self._kernel_manager = None
epatters
Initial checkin of Qt frontend code.
r2602
epatters
* Adding object_info_request support to prototype kernel....
r2612 self.document().contentsChange.connect(self._document_contents_change)
epatters
Minor comment cleanup.
r2669 #---------------------------------------------------------------------------
# 'QWidget' interface
#---------------------------------------------------------------------------
epatters
Initial checkin of Qt frontend code.
r2602 def focusOutEvent(self, event):
""" Reimplemented to hide calltips.
"""
self._call_tip_widget.hide()
return super(FrontendWidget, self).focusOutEvent(event)
def keyPressEvent(self, event):
""" Reimplemented to hide calltips.
"""
if event.key() == QtCore.Qt.Key_Escape:
self._call_tip_widget.hide()
return super(FrontendWidget, self).keyPressEvent(event)
#---------------------------------------------------------------------------
# 'ConsoleWidget' abstract interface
#---------------------------------------------------------------------------
def _execute(self, interactive):
""" Called to execute the input buffer. When triggered by an the enter
key press, 'interactive' is True; otherwise, it is False. Returns
whether the input buffer was completely processed and a new prompt
created.
"""
return self.execute_source(self.input_buffer, interactive=interactive)
def _prompt_started_hook(self):
""" Called immediately after a new prompt is displayed.
"""
self._highlighter.highlighting_on = True
def _prompt_finished_hook(self):
""" Called immediately after a prompt is finished, i.e. when some input
will be processed and a new prompt displayed.
"""
self._highlighter.highlighting_on = False
def _tab_pressed(self):
""" Called when the tab key is pressed. Returns whether to continue
processing the event.
"""
self._keep_cursor_in_buffer()
cursor = self.textCursor()
if not self._complete():
cursor.insertText(' ')
return False
#---------------------------------------------------------------------------
# 'FrontendWidget' interface
#---------------------------------------------------------------------------
def execute_source(self, source, hidden=False, interactive=False):
""" Execute a string containing Python code. If 'hidden', no output is
shown. Returns whether the source executed (i.e., returns True only
if no more input is necessary).
"""
epatters
Updated to FrontendWidget to reflect BlockBreaker API changes.
r2685 self._input_splitter.push(source)
executed = not self._input_splitter.push_accepts_more()
epatters
Initial checkin of Qt frontend code.
r2602 if executed:
epatters
* Refactored KernelManager to use Traitlets and to have its channels as attributes...
r2611 self.kernel_manager.xreq_channel.execute(source)
epatters
Setting hidden=True for FrontWidget's 'execute_source' works again.
r2616 self._hidden = hidden
epatters
Initial checkin of Qt frontend code.
r2602 else:
self._show_continuation_prompt()
epatters
Updated to FrontendWidget to reflect BlockBreaker API changes.
r2685 self.appendPlainText(' ' * self._input_splitter.indent_spaces)
epatters
Initial checkin of Qt frontend code.
r2602 return executed
def execute_file(self, path, hidden=False):
""" Attempts to execute file with 'path'. If 'hidden', no output is
shown.
"""
self.execute_source('run %s' % path, hidden=hidden)
epatters
Initial checkin of Qt kernel manager. Began refactor of FrontendWidget.
r2609 def _get_kernel_manager(self):
""" Returns the current kernel manager.
"""
return self._kernel_manager
def _set_kernel_manager(self, kernel_manager):
epatters
* Added 'started_listening' and 'stopped_listening' signals to QtKernelManager. The FrontendWidget listens for these signals....
r2643 """ Disconnect from the current kernel manager (if any) and set a new
kernel manager.
epatters
Initial checkin of Qt kernel manager. Began refactor of FrontendWidget.
r2609 """
epatters
* Added 'started_listening' and 'stopped_listening' signals to QtKernelManager. The FrontendWidget listens for these signals....
r2643 # Disconnect the old kernel manager, if necessary.
epatters
* Refactored KernelManager to use Traitlets and to have its channels as attributes...
r2611 if self._kernel_manager is not None:
epatters
* Added 'started_listening' and 'stopped_listening' signals to QtKernelManager. The FrontendWidget listens for these signals....
r2643 self._kernel_manager.started_listening.disconnect(
self._started_listening)
self._kernel_manager.stopped_listening.disconnect(
self._stopped_listening)
# Disconnect the old kernel manager's channels.
epatters
* Refactored KernelManager to use Traitlets and to have its channels as attributes...
r2611 sub = self._kernel_manager.sub_channel
xreq = self._kernel_manager.xreq_channel
sub.message_received.disconnect(self._handle_sub)
xreq.execute_reply.disconnect(self._handle_execute_reply)
xreq.complete_reply.disconnect(self._handle_complete_reply)
xreq.object_info_reply.disconnect(self._handle_object_info_reply)
epatters
* Added 'started_listening' and 'stopped_listening' signals to QtKernelManager. The FrontendWidget listens for these signals....
r2643 # Set the new kernel manager.
epatters
Initial checkin of Qt kernel manager. Began refactor of FrontendWidget.
r2609 self._kernel_manager = kernel_manager
epatters
* Added 'started_listening' and 'stopped_listening' signals to QtKernelManager. The FrontendWidget listens for these signals....
r2643 if kernel_manager is None:
return
# Connect the new kernel manager.
kernel_manager.started_listening.connect(self._started_listening)
kernel_manager.stopped_listening.connect(self._stopped_listening)
# Connect the new kernel manager's channels.
epatters
* Refactored KernelManager to use Traitlets and to have its channels as attributes...
r2611 sub = kernel_manager.sub_channel
xreq = kernel_manager.xreq_channel
sub.message_received.connect(self._handle_sub)
xreq.execute_reply.connect(self._handle_execute_reply)
epatters
* Adding object_info_request support to prototype kernel....
r2612 xreq.complete_reply.connect(self._handle_complete_reply)
xreq.object_info_reply.connect(self._handle_object_info_reply)
epatters
* Refactored KernelManager to use Traitlets and to have its channels as attributes...
r2611
epatters
* Added 'started_listening' and 'stopped_listening' signals to QtKernelManager. The FrontendWidget listens for these signals....
r2643 # Handle the case where the kernel manager started listening before
# we connected.
if kernel_manager.is_listening:
self._started_listening()
epatters
Initial checkin of Qt kernel manager. Began refactor of FrontendWidget.
r2609
kernel_manager = property(_get_kernel_manager, _set_kernel_manager)
epatters
Initial checkin of Qt frontend code.
r2602 #---------------------------------------------------------------------------
# 'FrontendWidget' protected interface
#---------------------------------------------------------------------------
def _call_tip(self):
""" Shows a call tip, if appropriate, at the current cursor location.
"""
# Decide if it makes sense to show a call tip
cursor = self.textCursor()
cursor.movePosition(QtGui.QTextCursor.Left)
document = self.document()
if document.characterAt(cursor.position()).toAscii() != '(':
return False
context = self._get_context(cursor)
if not context:
return False
# Send the metadata request to the kernel
epatters
* Adding object_info_request support to prototype kernel....
r2612 name = '.'.join(context)
self._calltip_id = self.kernel_manager.xreq_channel.object_info(name)
self._calltip_pos = self.textCursor().position()
epatters
Initial checkin of Qt frontend code.
r2602 return True
def _complete(self):
""" Performs completion at the current cursor location.
"""
# Decide if it makes sense to do completion
context = self._get_context()
if not context:
return False
# Send the completion request to the kernel
text = '.'.join(context)
epatters
* Adding object_info_request support to prototype kernel....
r2612 self._complete_id = self.kernel_manager.xreq_channel.complete(
text, self.input_buffer_cursor_line, self.input_buffer)
self._complete_pos = self.textCursor().position()
epatters
Initial checkin of Qt frontend code.
r2602 return True
def _get_context(self, cursor=None):
""" Gets the context at the current cursor location.
"""
if cursor is None:
cursor = self.textCursor()
cursor.movePosition(QtGui.QTextCursor.StartOfLine,
QtGui.QTextCursor.KeepAnchor)
text = unicode(cursor.selectedText())
return self._completion_lexer.get_context(text)
#------ Signal handlers ----------------------------------------------------
def _document_contents_change(self, position, removed, added):
""" Called whenever the document's content changes. Display a calltip
if appropriate.
"""
# Calculate where the cursor should be *after* the change:
position += added
document = self.document()
if position == self.textCursor().position():
self._call_tip()
epatters
* Refactored KernelManager to use Traitlets and to have its channels as attributes...
r2611 def _handle_sub(self, omsg):
epatters
Setting hidden=True for FrontWidget's 'execute_source' works again.
r2616 if not self._hidden:
handler = getattr(self, '_handle_%s' % omsg['msg_type'], None)
if handler is not None:
handler(omsg)
epatters
Initial checkin of Qt frontend code.
r2602
def _handle_pyout(self, omsg):
epatters
* Refactored KernelManager to use Traitlets and to have its channels as attributes...
r2611 session = omsg['parent_header']['session']
if session == self.kernel_manager.session.session:
self.appendPlainText(omsg['content']['data'] + '\n')
epatters
Initial checkin of Qt frontend code.
r2602
def _handle_stream(self, omsg):
epatters
* Refactored KernelManager to use Traitlets and to have its channels as attributes...
r2611 self.appendPlainText(omsg['content']['data'])
epatters
Initial checkin of Qt frontend code.
r2602
epatters
* Refactored KernelManager to use Traitlets and to have its channels as attributes...
r2611 def _handle_execute_reply(self, rep):
epatters
Added a flush method to the SubSocketChannel. The Qt console frontend now uses this method to ensure that output has been processed before it writes a new prompt.
r2614 # Make sure that all output from the SUB channel has been processed
# before writing a new prompt.
self.kernel_manager.sub_channel.flush()
epatters
* Refactored KernelManager to use Traitlets and to have its channels as attributes...
r2611 content = rep['content']
status = content['status']
if status == 'error':
self.appendPlainText(content['traceback'][-1])
elif status == 'aborted':
text = "ERROR: ABORTED\n"
self.appendPlainText(text)
epatters
* Created an IPythonWidget subclass of FrontendWidget to contain IPython specific functionality....
r2627 self._hidden = True
epatters
* Added 'started_listening' and 'stopped_listening' signals to QtKernelManager. The FrontendWidget listens for these signals....
r2643 self._show_prompt()
epatters
* Refactored KernelManager to use Traitlets and to have its channels as attributes...
r2611 self.executed.emit(rep)
epatters
Initial checkin of Qt frontend code.
r2602
epatters
* Adding object_info_request support to prototype kernel....
r2612 def _handle_complete_reply(self, rep):
cursor = self.textCursor()
if rep['parent_header']['msg_id'] == self._complete_id and \
cursor.position() == self._complete_pos:
text = '.'.join(self._get_context())
cursor.movePosition(QtGui.QTextCursor.Left, n=len(text))
self._complete_with_items(cursor, rep['content']['matches'])
epatters
Initial checkin of Qt frontend code.
r2602
epatters
* Adding object_info_request support to prototype kernel....
r2612 def _handle_object_info_reply(self, rep):
cursor = self.textCursor()
if rep['parent_header']['msg_id'] == self._calltip_id and \
cursor.position() == self._calltip_pos:
doc = rep['content']['docstring']
if doc:
epatters
Improved docstring formatting in call tips.
r2666 self._call_tip_widget.show_docstring(doc)
epatters
* Added 'started_listening' and 'stopped_listening' signals to QtKernelManager. The FrontendWidget listens for these signals....
r2643
def _started_listening(self):
self.clear()
def _stopped_listening(self):
pass