diff --git a/IPython/frontend/qt/console/call_tip_widget.py b/IPython/frontend/qt/console/call_tip_widget.py
index ac60710..cfac128 100644
--- a/IPython/frontend/qt/console/call_tip_widget.py
+++ b/IPython/frontend/qt/console/call_tip_widget.py
@@ -34,22 +34,32 @@ class CallTipWidget(QtGui.QLabel):
self.setWindowOpacity(self.style().styleHint(
QtGui.QStyle.SH_ToolTipLabel_Opacity, None, self) / 255.0)
+ def eventFilter(self, obj, event):
+ """ Reimplemented to hide on certain key presses and on parent focus
+ changes.
+ """
+ if obj == self.parent():
+ etype = event.type()
+ if (etype == QtCore.QEvent.KeyPress and
+ event.key() in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return,
+ QtCore.Qt.Key_Escape)):
+ self.hide()
+ elif etype == QtCore.QEvent.FocusOut:
+ self.hide()
+
+ return QtGui.QLabel.eventFilter(self, obj, event)
+
#--------------------------------------------------------------------------
# 'QWidget' interface
#--------------------------------------------------------------------------
def hideEvent(self, event):
- """ Reimplemented to disconnect the cursor movement handler.
+ """ Reimplemented to disconnect signal handlers and event filter.
"""
QtGui.QLabel.hideEvent(self, event)
- self.parent().cursorPositionChanged.disconnect(self._update_tip)
-
- def keyPressEvent(self, event):
- """ Reimplemented to hide on certain key presses.
- """
- if event.key() in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return,
- QtCore.Qt.Key_Escape):
- self.hide()
+ parent = self.parent()
+ parent.cursorPositionChanged.disconnect(self._cursor_position_changed)
+ parent.removeEventFilter(self)
def paintEvent(self, event):
""" Reimplemented to paint the background panel.
@@ -63,10 +73,12 @@ class CallTipWidget(QtGui.QLabel):
QtGui.QLabel.paintEvent(self, event)
def showEvent(self, event):
- """ Reimplemented to connect the cursor movement handler.
+ """ Reimplemented to connect signal handlers and event filter.
"""
QtGui.QLabel.showEvent(self, event)
- self.parent().cursorPositionChanged.connect(self._update_tip)
+ parent = self.parent()
+ parent.cursorPositionChanged.connect(self._cursor_position_changed)
+ parent.installEventFilter(self)
#--------------------------------------------------------------------------
# 'CallTipWidget' interface
@@ -139,38 +151,9 @@ class CallTipWidget(QtGui.QLabel):
position = -1
return position, commas
- def _highlight_tip(self, tip, current_argument):
- """ Highlight the current argument (arguments start at 0), ending at the
- next comma or unmatched closing parenthesis.
-
- FIXME: This is an unreliable way to do things and it isn't being
- used right now. Instead, we should use inspect.getargspec
- metadata for this purpose.
- """
- start = tip.find('(')
- if start != -1:
- for i in xrange(current_argument):
- start = tip.find(',', start)
- if start != -1:
- end = start + 1
- while end < len(tip):
- char = tip[end]
- depth = 0
- if (char == ',' and depth == 0):
- break
- elif char == '(':
- depth += 1
- elif char == ')':
- if depth == 0:
- break
- depth -= 1
- end += 1
- tip = tip[:start+1] + '' + \
- tip[start+1:end] + '' + tip[end:]
- tip = tip.replace('\n', '
')
- return tip
-
- def _update_tip(self):
+ #------ Signal handlers ----------------------------------------------------
+
+ def _cursor_position_changed(self):
""" Updates the tip based on user cursor movement.
"""
cursor = self.parent().textCursor()
diff --git a/IPython/frontend/qt/console/completion_widget.py b/IPython/frontend/qt/console/completion_widget.py
index e2ef3c0..f2e0f49 100644
--- a/IPython/frontend/qt/console/completion_widget.py
+++ b/IPython/frontend/qt/console/completion_widget.py
@@ -7,7 +7,7 @@ class CompletionWidget(QtGui.QListWidget):
"""
#--------------------------------------------------------------------------
- # 'QWidget' interface
+ # 'QObject' interface
#--------------------------------------------------------------------------
def __init__(self, parent):
@@ -28,43 +28,55 @@ class CompletionWidget(QtGui.QListWidget):
self.itemActivated.connect(self._complete_current)
+ def eventFilter(self, obj, event):
+ """ Reimplemented to handle keyboard input and to auto-hide when our
+ parent loses focus.
+ """
+ if obj == self.parent():
+ etype = event.type()
+
+ if etype == QtCore.QEvent.KeyPress:
+ key, text = event.key(), event.text()
+ if key in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter,
+ QtCore.Qt.Key_Tab):
+ self._complete_current()
+ return True
+ elif key == QtCore.Qt.Key_Escape:
+ self.hide()
+ return True
+ elif key in (QtCore.Qt.Key_Up, QtCore.Qt.Key_Down,
+ QtCore.Qt.Key_PageUp, QtCore.Qt.Key_PageDown,
+ QtCore.Qt.Key_Home, QtCore.Qt.Key_End):
+ QtGui.QListWidget.keyPressEvent(self, event)
+ return True
+
+ elif etype == QtCore.QEvent.FocusOut:
+ self.hide()
+
+ return QtGui.QListWidget.eventFilter(self, obj, event)
+
+ #--------------------------------------------------------------------------
+ # 'QWidget' interface
+ #--------------------------------------------------------------------------
+
def hideEvent(self, event):
- """ Reimplemented to disconnect the cursor movement handler.
+ """ Reimplemented to disconnect signal handlers and event filter.
"""
QtGui.QListWidget.hideEvent(self, event)
+ parent = self.parent()
try:
- self.parent().cursorPositionChanged.disconnect(self._update_current)
+ parent.cursorPositionChanged.disconnect(self._update_current)
except TypeError:
pass
-
- def keyPressEvent(self, event):
- """ Reimplemented to update the list.
- """
- key, text = event.key(), event.text()
-
- if key in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter,
- QtCore.Qt.Key_Tab):
- self._complete_current()
- event.accept()
-
- elif key == QtCore.Qt.Key_Escape:
- self.hide()
- event.accept()
-
- elif key in (QtCore.Qt.Key_Up, QtCore.Qt.Key_Down,
- QtCore.Qt.Key_PageUp, QtCore.Qt.Key_PageDown,
- QtCore.Qt.Key_Home, QtCore.Qt.Key_End):
- QtGui.QListWidget.keyPressEvent(self, event)
- event.accept()
-
- else:
- event.ignore()
+ parent.removeEventFilter(self)
def showEvent(self, event):
- """ Reimplemented to connect the cursor movement handler.
+ """ Reimplemented to connect signal handlers and event filter.
"""
QtGui.QListWidget.showEvent(self, event)
- self.parent().cursorPositionChanged.connect(self._update_current)
+ parent = self.parent()
+ parent.cursorPositionChanged.connect(self._update_current)
+ parent.installEventFilter(self)
#--------------------------------------------------------------------------
# 'CompletionWidget' interface
diff --git a/IPython/frontend/qt/console/console_widget.py b/IPython/frontend/qt/console/console_widget.py
index 075ef99..3218845 100644
--- a/IPython/frontend/qt/console/console_widget.py
+++ b/IPython/frontend/qt/console/console_widget.py
@@ -56,8 +56,7 @@ class ConsoleWidget(QtGui.QWidget):
----------
kind : str, optional [default 'plain']
The type of text widget to use. Valid values are 'plain', which
- specifies a QPlainTextEdit, and 'rich', which specifies an
- QTextEdit.
+ specifies a QPlainTextEdit, and 'rich', which specifies a QTextEdit.
parent : QWidget, optional [default None]
The parent for this widget.
@@ -332,6 +331,12 @@ class ConsoleWidget(QtGui.QWidget):
"""
raise NotImplementedError
+ def _execute_interrupt(self):
+ """ Attempts to stop execution. Returns whether this method has an
+ implementation.
+ """
+ return False
+
def _prompt_started_hook(self):
""" Called immediately after a new prompt is displayed.
"""
@@ -469,12 +474,6 @@ class ConsoleWidget(QtGui.QWidget):
QtGui.qApp.sendEvent(self._control, new_event)
return True
- # If the completion widget accepts the key press, return immediately.
- if self._completion_widget.isVisible():
- self._completion_widget.keyPressEvent(event)
- if event.isAccepted():
- return True
-
# Otherwise, proceed normally and do not return early.
intercepted = False
cursor = self._control.textCursor()
@@ -488,7 +487,10 @@ class ConsoleWidget(QtGui.QWidget):
intercepted = True
elif ctrl_down:
- if key == QtCore.Qt.Key_K:
+ if key == QtCore.Qt.Key_C and self._executing:
+ intercepted = self._execute_interrupt()
+
+ elif key == QtCore.Qt.Key_K:
if self._in_buffer(position):
cursor.movePosition(QtGui.QTextCursor.EndOfLine,
QtGui.QTextCursor.KeepAnchor)
diff --git a/IPython/frontend/qt/console/frontend_widget.py b/IPython/frontend/qt/console/frontend_widget.py
index 873a1f0..5199551 100644
--- a/IPython/frontend/qt/console/frontend_widget.py
+++ b/IPython/frontend/qt/console/frontend_widget.py
@@ -91,28 +91,6 @@ class FrontendWidget(HistoryConsoleWidget):
document.contentsChange.connect(self._document_contents_change)
#---------------------------------------------------------------------------
- # 'QWidget' interface
- #---------------------------------------------------------------------------
-
- def focusOutEvent(self, event):
- """ Reimplemented to hide calltips.
- """
- self._call_tip_widget.hide()
- super(FrontendWidget, self).focusOutEvent(event)
-
- def keyPressEvent(self, event):
- """ Reimplemented to allow calltips to process events and to send
- signals to the kernel.
- """
- if self._executing and event.key() == QtCore.Qt.Key_C and \
- self._control_down(event.modifiers()):
- self._interrupt_kernel()
- else:
- if self._call_tip_widget.isVisible():
- self._call_tip_widget.keyPressEvent(event)
- super(FrontendWidget, self).keyPressEvent(event)
-
- #---------------------------------------------------------------------------
# 'ConsoleWidget' abstract interface
#---------------------------------------------------------------------------
@@ -131,6 +109,13 @@ class FrontendWidget(HistoryConsoleWidget):
"""
self.kernel_manager.xreq_channel.execute(source)
self._hidden = hidden
+
+ def _execute_interrupt(self):
+ """ Attempts to stop execution. Returns whether this method has an
+ implementation.
+ """
+ self._interrupt_kernel()
+ return True
def _prompt_started_hook(self):
""" Called immediately after a new prompt is displayed.
@@ -245,8 +230,8 @@ class FrontendWidget(HistoryConsoleWidget):
# Send the metadata request to the kernel
name = '.'.join(context)
- self._calltip_id = self.kernel_manager.xreq_channel.object_info(name)
- self._calltip_pos = self._get_cursor().position()
+ self._call_tip_id = self.kernel_manager.xreq_channel.object_info(name)
+ self._call_tip_pos = self._get_cursor().position()
return True
def _complete(self):
@@ -311,7 +296,7 @@ class FrontendWidget(HistoryConsoleWidget):
pass
def _document_contents_change(self, position, removed, added):
- """ Called whenever the document's content changes. Display a calltip
+ """ Called whenever the document's content changes. Display a call tip
if appropriate.
"""
# Calculate where the cursor should be *after* the change:
@@ -377,8 +362,8 @@ class FrontendWidget(HistoryConsoleWidget):
def _handle_object_info_reply(self, rep):
cursor = self._get_cursor()
- if rep['parent_header']['msg_id'] == self._calltip_id and \
- cursor.position() == self._calltip_pos:
+ if rep['parent_header']['msg_id'] == self._call_tip_id and \
+ cursor.position() == self._call_tip_pos:
doc = rep['content']['docstring']
if doc:
self._call_tip_widget.show_docstring(doc)