##// END OF EJS Templates
Use python timers instead of thread + sleep
Use python timers instead of thread + sleep

File last commit:

r9224:43f5c603
r10382:bcdeb79b
Show More
call_tip_widget.py
275 lines | 10.5 KiB | text/x-python | PythonLexer
epatters
Improved docstring formatting in call tips.
r2666 # Standard library imports
import re
Evan Patterson
Paved the way for PySide support....
r3304 from unicodedata import category
epatters
Improved docstring formatting in call tips.
r2666
epatters
Initial checkin of Qt frontend code.
r2602 # System library imports
Evan Patterson
Paved the way for PySide support....
r3304 from IPython.external.qt import QtCore, QtGui
epatters
Initial checkin of Qt frontend code.
r2602
class CallTipWidget(QtGui.QLabel):
""" Shows call tips by parsing the current text of Q[Plain]TextEdit.
"""
#--------------------------------------------------------------------------
epatters
* Added ability to interrupt a kernel to FrontendWidget...
r2687 # 'QObject' interface
epatters
Initial checkin of Qt frontend code.
r2602 #--------------------------------------------------------------------------
epatters
Fixed the segfaults on application exit. The BracketMatcher, CallTipWidget, and CompletionWidget were using the text control as their parents. This should not be a problem, but for some reason it resulted in problems during shutdown. I suspect that PyQt is bugged and was deleting the C++ objects a second time in the garbage collection phase after they had already been deleted automatically by the C++ layer of Qt.
r2982 def __init__(self, text_edit):
epatters
Initial checkin of Qt frontend code.
r2602 """ Create a call tip manager that is attached to the specified Qt
text edit widget.
"""
epatters
Fixed the segfaults on application exit. The BracketMatcher, CallTipWidget, and CompletionWidget were using the text control as their parents. This should not be a problem, but for some reason it resulted in problems during shutdown. I suspect that PyQt is bugged and was deleting the C++ objects a second time in the garbage collection phase after they had already been deleted automatically by the C++ layer of Qt.
r2982 assert isinstance(text_edit, (QtGui.QTextEdit, QtGui.QPlainTextEdit))
super(CallTipWidget, self).__init__(None, QtCore.Qt.ToolTip)
epatters
Initial checkin of Qt frontend code.
r2602
epatters
The CallTipWidget is now smarter about automatically hiding itself.
r2964 self._hide_timer = QtCore.QBasicTimer()
epatters
Fixed the segfaults on application exit. The BracketMatcher, CallTipWidget, and CompletionWidget were using the text control as their parents. This should not be a problem, but for some reason it resulted in problems during shutdown. I suspect that PyQt is bugged and was deleting the C++ objects a second time in the garbage collection phase after they had already been deleted automatically by the C++ layer of Qt.
r2982 self._text_edit = text_edit
epatters
The CallTipWidget is now smarter about automatically hiding itself.
r2964
epatters
Fixed the segfaults on application exit. The BracketMatcher, CallTipWidget, and CompletionWidget were using the text control as their parents. This should not be a problem, but for some reason it resulted in problems during shutdown. I suspect that PyQt is bugged and was deleting the C++ objects a second time in the garbage collection phase after they had already been deleted automatically by the C++ layer of Qt.
r2982 self.setFont(text_edit.document().defaultFont())
epatters
Initial checkin of Qt frontend code.
r2602 self.setForegroundRole(QtGui.QPalette.ToolTipText)
self.setBackgroundRole(QtGui.QPalette.ToolTipBase)
self.setPalette(QtGui.QToolTip.palette())
self.setAlignment(QtCore.Qt.AlignLeft)
self.setIndent(1)
self.setFrameStyle(QtGui.QFrame.NoFrame)
self.setMargin(1 + self.style().pixelMetric(
QtGui.QStyle.PM_ToolTipLabelFrameWidth, None, self))
self.setWindowOpacity(self.style().styleHint(
Evan Patterson
More PySide compatibility fixes.
r3305 QtGui.QStyle.SH_ToolTipLabel_Opacity, None, self, None) / 255.0)
epatters
Initial checkin of Qt frontend code.
r2602
epatters
* CallTipWidget and CompletionWidget no longer need to be fed key presses. This means that can be attached to any Q[Plain]TextEdit with zero hassle....
r2744 def eventFilter(self, obj, event):
epatters
Fixed the segfaults on application exit. The BracketMatcher, CallTipWidget, and CompletionWidget were using the text control as their parents. This should not be a problem, but for some reason it resulted in problems during shutdown. I suspect that PyQt is bugged and was deleting the C++ objects a second time in the garbage collection phase after they had already been deleted automatically by the C++ layer of Qt.
r2982 """ Reimplemented to hide on certain key presses and on text edit focus
epatters
* CallTipWidget and CompletionWidget no longer need to be fed key presses. This means that can be attached to any Q[Plain]TextEdit with zero hassle....
r2744 changes.
"""
epatters
Fixed the segfaults on application exit. The BracketMatcher, CallTipWidget, and CompletionWidget were using the text control as their parents. This should not be a problem, but for some reason it resulted in problems during shutdown. I suspect that PyQt is bugged and was deleting the C++ objects a second time in the garbage collection phase after they had already been deleted automatically by the C++ layer of Qt.
r2982 if obj == self._text_edit:
epatters
* CallTipWidget and CompletionWidget no longer need to be fed key presses. This means that can be attached to any Q[Plain]TextEdit with zero hassle....
r2744 etype = event.type()
epatters
Fixed bug where closing a call tip by pressing Escape also cleared the input buffer.
r2962
if etype == QtCore.QEvent.KeyPress:
key = event.key()
if key in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return):
self.hide()
elif key == QtCore.Qt.Key_Escape:
self.hide()
return True
epatters
* CallTipWidget and CompletionWidget no longer need to be fed key presses. This means that can be attached to any Q[Plain]TextEdit with zero hassle....
r2744 elif etype == QtCore.QEvent.FocusOut:
self.hide()
epatters
The CallTipWidget is now smarter about automatically hiding itself.
r2964 elif etype == QtCore.QEvent.Enter:
self._hide_timer.stop()
elif etype == QtCore.QEvent.Leave:
epatters
Fixed bug where calltips would sometimes inappropriately disappear on OSX.
r3309 self._leave_event_hide()
epatters
The CallTipWidget is now smarter about automatically hiding itself.
r2964
epatters
Fixed the segfaults on application exit. The BracketMatcher, CallTipWidget, and CompletionWidget were using the text control as their parents. This should not be a problem, but for some reason it resulted in problems during shutdown. I suspect that PyQt is bugged and was deleting the C++ objects a second time in the garbage collection phase after they had already been deleted automatically by the C++ layer of Qt.
r2982 return super(CallTipWidget, self).eventFilter(obj, event)
epatters
* CallTipWidget and CompletionWidget no longer need to be fed key presses. This means that can be attached to any Q[Plain]TextEdit with zero hassle....
r2744
epatters
The CallTipWidget is now smarter about automatically hiding itself.
r2964 def timerEvent(self, event):
""" Reimplemented to hide the widget when the hide timer fires.
"""
if event.timerId() == self._hide_timer.timerId():
self._hide_timer.stop()
self.hide()
epatters
* Added ability to interrupt a kernel to FrontendWidget...
r2687 #--------------------------------------------------------------------------
# 'QWidget' interface
#--------------------------------------------------------------------------
epatters
The CallTipWidget is now smarter about automatically hiding itself.
r2964 def enterEvent(self, event):
""" Reimplemented to cancel the hide timer.
"""
epatters
Fixed the segfaults on application exit. The BracketMatcher, CallTipWidget, and CompletionWidget were using the text control as their parents. This should not be a problem, but for some reason it resulted in problems during shutdown. I suspect that PyQt is bugged and was deleting the C++ objects a second time in the garbage collection phase after they had already been deleted automatically by the C++ layer of Qt.
r2982 super(CallTipWidget, self).enterEvent(event)
epatters
The CallTipWidget is now smarter about automatically hiding itself.
r2964 self._hide_timer.stop()
epatters
Initial checkin of Qt frontend code.
r2602 def hideEvent(self, event):
epatters
* CallTipWidget and CompletionWidget no longer need to be fed key presses. This means that can be attached to any Q[Plain]TextEdit with zero hassle....
r2744 """ Reimplemented to disconnect signal handlers and event filter.
epatters
Initial checkin of Qt frontend code.
r2602 """
epatters
Fixed the segfaults on application exit. The BracketMatcher, CallTipWidget, and CompletionWidget were using the text control as their parents. This should not be a problem, but for some reason it resulted in problems during shutdown. I suspect that PyQt is bugged and was deleting the C++ objects a second time in the garbage collection phase after they had already been deleted automatically by the C++ layer of Qt.
r2982 super(CallTipWidget, self).hideEvent(event)
self._text_edit.cursorPositionChanged.disconnect(
self._cursor_position_changed)
self._text_edit.removeEventFilter(self)
epatters
* Added ability to interrupt a kernel to FrontendWidget...
r2687
epatters
The CallTipWidget is now smarter about automatically hiding itself.
r2964 def leaveEvent(self, event):
""" Reimplemented to start the hide timer.
"""
epatters
Fixed the segfaults on application exit. The BracketMatcher, CallTipWidget, and CompletionWidget were using the text control as their parents. This should not be a problem, but for some reason it resulted in problems during shutdown. I suspect that PyQt is bugged and was deleting the C++ objects a second time in the garbage collection phase after they had already been deleted automatically by the C++ layer of Qt.
r2982 super(CallTipWidget, self).leaveEvent(event)
epatters
Fixed bug where calltips would sometimes inappropriately disappear on OSX.
r3309 self._leave_event_hide()
epatters
The CallTipWidget is now smarter about automatically hiding itself.
r2964
epatters
Initial checkin of Qt frontend code.
r2602 def paintEvent(self, event):
""" Reimplemented to paint the background panel.
"""
painter = QtGui.QStylePainter(self)
option = QtGui.QStyleOptionFrame()
epatters
Yet more PySide compatibility fixes.
r3307 option.initFrom(self)
epatters
Initial checkin of Qt frontend code.
r2602 painter.drawPrimitive(QtGui.QStyle.PE_PanelTipLabel, option)
painter.end()
epatters
Fixed the segfaults on application exit. The BracketMatcher, CallTipWidget, and CompletionWidget were using the text control as their parents. This should not be a problem, but for some reason it resulted in problems during shutdown. I suspect that PyQt is bugged and was deleting the C++ objects a second time in the garbage collection phase after they had already been deleted automatically by the C++ layer of Qt.
r2982 super(CallTipWidget, self).paintEvent(event)
epatters
Initial checkin of Qt frontend code.
r2602
epatters
Fixed font changes not being propagated to CallTipWidget.
r3031 def setFont(self, font):
""" Reimplemented to allow use of this method as a slot.
"""
super(CallTipWidget, self).setFont(font)
epatters
Initial checkin of Qt frontend code.
r2602 def showEvent(self, event):
epatters
* CallTipWidget and CompletionWidget no longer need to be fed key presses. This means that can be attached to any Q[Plain]TextEdit with zero hassle....
r2744 """ Reimplemented to connect signal handlers and event filter.
epatters
Initial checkin of Qt frontend code.
r2602 """
epatters
Fixed the segfaults on application exit. The BracketMatcher, CallTipWidget, and CompletionWidget were using the text control as their parents. This should not be a problem, but for some reason it resulted in problems during shutdown. I suspect that PyQt is bugged and was deleting the C++ objects a second time in the garbage collection phase after they had already been deleted automatically by the C++ layer of Qt.
r2982 super(CallTipWidget, self).showEvent(event)
self._text_edit.cursorPositionChanged.connect(
self._cursor_position_changed)
self._text_edit.installEventFilter(self)
epatters
Initial checkin of Qt frontend code.
r2602
#--------------------------------------------------------------------------
# 'CallTipWidget' interface
#--------------------------------------------------------------------------
Fernando Perez
Add function signature info to calltips....
r3051 def show_call_info(self, call_line=None, doc=None, maxlines=20):
""" Attempts to show the specified call line and docstring at the
current cursor location. The docstring is possibly truncated for
epatters
Improved docstring formatting in call tips.
r2666 length.
"""
Fernando Perez
Add function signature info to calltips....
r3051 if doc:
match = re.match("(?:[^\n]*\n){%i}" % maxlines, doc)
if match:
doc = doc[:match.end()] + '\n[Documentation continues...]'
else:
doc = ''
Bernardo B. Marques
remove all trailling spaces
r4872
Fernando Perez
Add function signature info to calltips....
r3051 if call_line:
doc = '\n\n'.join([call_line, doc])
y-p
qtconsole: wrap argument list in tooltip to match width of text body...
r8841 return self.show_tip(self._format_tooltip(doc))
epatters
Improved docstring formatting in call tips.
r2666
epatters
Initial checkin of Qt frontend code.
r2602 def show_tip(self, tip):
""" Attempts to show the specified tip at the current cursor location.
"""
epatters
Call tips are now positioned more intelligently: if a call tip below the current line would be outside the screen, it is positioned above the line instead.
r2881 # Attempt to find the cursor position at which to show the call tip.
epatters
Fixed the segfaults on application exit. The BracketMatcher, CallTipWidget, and CompletionWidget were using the text control as their parents. This should not be a problem, but for some reason it resulted in problems during shutdown. I suspect that PyQt is bugged and was deleting the C++ objects a second time in the garbage collection phase after they had already been deleted automatically by the C++ layer of Qt.
r2982 text_edit = self._text_edit
epatters
Initial checkin of Qt frontend code.
r2602 document = text_edit.document()
cursor = text_edit.textCursor()
search_pos = cursor.position() - 1
Bernardo B. Marques
remove all trailling spaces
r4872 self._start_position, _ = self._find_parenthesis(search_pos,
epatters
Initial checkin of Qt frontend code.
r2602 forward=False)
if self._start_position == -1:
return False
epatters
Call tips are now positioned more intelligently: if a call tip below the current line would be outside the screen, it is positioned above the line instead.
r2881
# Set the text and resize the widget accordingly.
epatters
Initial checkin of Qt frontend code.
r2602 self.setText(tip)
epatters
* The SVG payload matplotlib backend now works....
r2758 self.resize(self.sizeHint())
Bernardo B. Marques
remove all trailling spaces
r4872
epatters
Call tips are now positioned more intelligently: if a call tip below the current line would be outside the screen, it is positioned above the line instead.
r2881 # Locate and show the widget. Place the tip below the current line
Puneeth Chaganti
BUG: Improve placement of CallTipWidget...
r6215 # unless it would be off the screen. In that case, decide the best
# location based trying to minimize the area that goes off-screen.
epatters
Added some padding between the text and the call tip to improve readability.
r2963 padding = 3 # Distance in pixels between cursor bounds and tip box.
epatters
Call tips are now positioned more intelligently: if a call tip below the current line would be outside the screen, it is positioned above the line instead.
r2881 cursor_rect = text_edit.cursorRect(cursor)
screen_rect = QtGui.qApp.desktop().screenGeometry(text_edit)
point = text_edit.mapToGlobal(cursor_rect.bottomRight())
epatters
Added some padding between the text and the call tip to improve readability.
r2963 point.setY(point.y() + padding)
epatters
Call tips are now positioned more intelligently: if a call tip below the current line would be outside the screen, it is positioned above the line instead.
r2881 tip_height = self.size().height()
Puneeth Chaganti
BUG: Improve placement of CallTipWidget...
r6215 tip_width = self.size().width()
vertical = 'bottom'
horizontal = 'Right'
epatters
Call tips are now positioned more intelligently: if a call tip below the current line would be outside the screen, it is positioned above the line instead.
r2881 if point.y() + tip_height > screen_rect.height():
Puneeth Chaganti
BUG: Improve placement of CallTipWidget...
r6215 point_ = text_edit.mapToGlobal(cursor_rect.topRight())
# If tip is still off screen, check if point is in top or bottom
# half of screen.
if point_.y() - tip_height < padding:
# If point is in upper half of screen, show tip below it.
# otherwise above it.
if 2*point.y() < screen_rect.height():
vertical = 'bottom'
else:
vertical = 'top'
else:
vertical = 'top'
if point.x() + tip_width > screen_rect.width():
point_ = text_edit.mapToGlobal(cursor_rect.topRight())
# If tip is still off-screen, check if point is in the right or
# left half of the screen.
if point_.x() - tip_width < padding:
if 2*point.x() < screen_rect.width():
horizontal = 'Right'
else:
horizontal = 'Left'
else:
horizontal = 'Left'
pos = getattr(cursor_rect, '%s%s' %(vertical, horizontal))
point = text_edit.mapToGlobal(pos())
if vertical == 'top':
epatters
Added some padding between the text and the call tip to improve readability.
r2963 point.setY(point.y() - tip_height - padding)
Puneeth Chaganti
BUG: Improve placement of CallTipWidget...
r6215 if horizontal == 'Left':
point.setX(point.x() - tip_width - padding)
epatters
Call tips are now positioned more intelligently: if a call tip below the current line would be outside the screen, it is positioned above the line instead.
r2881 self.move(point)
epatters
* The SVG payload matplotlib backend now works....
r2758 self.show()
epatters
Initial checkin of Qt frontend code.
r2602 return True
Bernardo B. Marques
remove all trailling spaces
r4872
epatters
Initial checkin of Qt frontend code.
r2602 #--------------------------------------------------------------------------
# Protected interface
#--------------------------------------------------------------------------
def _find_parenthesis(self, position, forward=True):
""" If 'forward' is True (resp. False), proceed forwards
(resp. backwards) through the line that contains 'position' until an
unmatched closing (resp. opening) parenthesis is found. Returns a
tuple containing the position of this parenthesis (or -1 if it is
not found) and the number commas (at depth 0) found along the way.
"""
commas = depth = 0
epatters
Fixed the segfaults on application exit. The BracketMatcher, CallTipWidget, and CompletionWidget were using the text control as their parents. This should not be a problem, but for some reason it resulted in problems during shutdown. I suspect that PyQt is bugged and was deleting the C++ objects a second time in the garbage collection phase after they had already been deleted automatically by the C++ layer of Qt.
r2982 document = self._text_edit.document()
Evan Patterson
Paved the way for PySide support....
r3304 char = document.characterAt(position)
# Search until a match is found or a non-printable character is
# encountered.
while category(char) != 'Cc' and position > 0:
epatters
Initial checkin of Qt frontend code.
r2602 if char == ',' and depth == 0:
commas += 1
elif char == ')':
if forward and depth == 0:
break
depth += 1
elif char == '(':
if not forward and depth == 0:
break
depth -= 1
position += 1 if forward else -1
Evan Patterson
Paved the way for PySide support....
r3304 char = document.characterAt(position)
epatters
Initial checkin of Qt frontend code.
r2602 else:
position = -1
return position, commas
epatters
Fixed bug where calltips would sometimes inappropriately disappear on OSX.
r3309 def _leave_event_hide(self):
""" Hides the tooltip after some time has passed (assuming the cursor is
not over the tooltip).
epatters
The CallTipWidget is now smarter about automatically hiding itself.
r2964 """
epatters
Fixed bug where calltips would sometimes inappropriately disappear on OSX.
r3309 if (not self._hide_timer.isActive() and
# If Enter events always came after Leave events, we wouldn't need
# this check. But on Mac OS, it sometimes happens the other way
# around when the tooltip is created.
QtGui.qApp.topLevelAt(QtGui.QCursor.pos()) != self):
epatters
The CallTipWidget is now smarter about automatically hiding itself.
r2964 self._hide_timer.start(300, self)
y-p
qtconsole: wrap argument list in tooltip to match width of text body...
r8841 def _format_tooltip(self,doc):
import textwrap
# make sure a long argument list does not make
# the first row overflow the width of the actual tip body
rows = doc.split("\n")
Puneeth Chaganti
BUG: Opening parenthesis after non-callable raises ValueError...
r9224 # An object which is not a callable has '<no docstring>' as doc
if len(rows) == 1:
return doc
y-p
qtconsole: wrap argument list in tooltip to match width of text body...
r8841 max_text_width = max(80, max([len(x) for x in rows[1:]]))
rows= textwrap.wrap(rows[0],max_text_width) + rows[1:]
doc = "\n".join(rows)
return doc
epatters
* CallTipWidget and CompletionWidget no longer need to be fed key presses. This means that can be attached to any Q[Plain]TextEdit with zero hassle....
r2744 #------ Signal handlers ----------------------------------------------------
def _cursor_position_changed(self):
epatters
Initial checkin of Qt frontend code.
r2602 """ Updates the tip based on user cursor movement.
"""
epatters
Fixed the segfaults on application exit. The BracketMatcher, CallTipWidget, and CompletionWidget were using the text control as their parents. This should not be a problem, but for some reason it resulted in problems during shutdown. I suspect that PyQt is bugged and was deleting the C++ objects a second time in the garbage collection phase after they had already been deleted automatically by the C++ layer of Qt.
r2982 cursor = self._text_edit.textCursor()
epatters
Initial checkin of Qt frontend code.
r2602 if cursor.position() <= self._start_position:
self.hide()
else:
position, commas = self._find_parenthesis(self._start_position + 1)
if position != -1:
self.hide()