##// END OF EJS Templates
Multiple improvements to tab completion....
Multiple improvements to tab completion. I refactored the API quite a bit, to retain readline compatibility but make it more independent of readline. There's still more to do in cleaning up our init_readline() method, but now the completer objects have separate rlcomplete() and complete() methods. The former uses the quirky readline API with a state flag, while the latter is stateless, takes only text information, and is more suitable for GUIs and other frontends to call programatically. Made other minor fixes to ensure the test suite passes in full. While all this code is a bit messy, we're getting in the direction of the APIs we need in the long run.

File last commit:

r2839:8cff4913
r2839:8cff4913
Show More
frontend_widget.py
384 lines | 14.4 KiB | text/x-python | PythonLexer
epatters
* Added ability to interrupt a kernel to FrontendWidget...
r2687 # Standard library imports
import signal
epatters
Added banners to FrontendWidget and IPythonWidget.
r2714 import sys
epatters
* Added ability to interrupt a kernel to FrontendWidget...
r2687
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
* Moved KernelManager attribute management code in FrontendWidget into a mixin class usable in any Qt frontend. Registering handlers for message types is now trivial....
r2770 from IPython.frontend.qt.base_frontend_mixin import BaseFrontendMixin
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):
epatters
* IPythonWidget now has IPython-style prompts that are futher stylabla via CSS...
r2715 """ A PygmentsHighlighter that can be turned on and off and that ignores
prompts.
epatters
Initial checkin of Qt frontend code.
r2602 """
def __init__(self, frontend):
epatters
Refactored ConsoleWidget to encapsulate, rather than inherit from, QPlainTextEdit. This permits a QTextEdit to be substituted for a QPlainTextEdit if desired. It also makes it more clear what is the public interface of ConsoleWidget.
r2736 super(FrontendHighlighter, self).__init__(frontend._control.document())
epatters
Initial checkin of Qt frontend code.
r2602 self._current_offset = 0
self._frontend = frontend
self.highlighting_on = False
def highlightBlock(self, qstring):
""" Highlight a block of text. Reimplemented to highlight selectively.
"""
epatters
* IPythonWidget now has IPython-style prompts that are futher stylabla via CSS...
r2715 if not self.highlighting_on:
return
# The input to this function is unicode string that may contain
# paragraph break characters, non-breaking spaces, etc. Here we acquire
# the string as plain text so we can compare it.
current_block = self.currentBlock()
string = self._frontend._get_block_plain_text(current_block)
# Decide whether to check for the regular or continuation prompt.
if current_block.contains(self._frontend._prompt_pos):
prompt = self._frontend._prompt
else:
prompt = self._frontend._continuation_prompt
# Don't highlight the part of the string that contains the prompt.
if string.startswith(prompt):
self._current_offset = len(prompt)
qstring.remove(0, len(prompt))
else:
self._current_offset = 0
PygmentsHighlighter.highlightBlock(self, qstring)
epatters
Initial checkin of Qt frontend code.
r2602
epatters
Minor cleanup.
r2825 def rehighlightBlock(self, block):
""" Reimplemented to temporarily enable highlighting if disabled.
"""
old = self.highlighting_on
self.highlighting_on = True
super(FrontendHighlighter, self).rehighlightBlock(block)
self.highlighting_on = old
epatters
Initial checkin of Qt frontend code.
r2602 def setFormat(self, start, count, format):
epatters
* IPythonWidget now has IPython-style prompts that are futher stylabla via CSS...
r2715 """ Reimplemented to highlight selectively.
epatters
Initial checkin of Qt frontend code.
r2602 """
start += self._current_offset
PygmentsHighlighter.setFormat(self, start, count, format)
epatters
* Moved KernelManager attribute management code in FrontendWidget into a mixin class usable in any Qt frontend. Registering handlers for message types is now trivial....
r2770 class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):
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 """
epatters
* ConsoleWidget now has better support for non-GUI tab completion. Multiple matches are formatted into columns....
r2723
epatters
Minor cleanup and bug fix.
r2771 # Emitted when an 'execute_reply' has been received from the kernel and
# processed by the FrontendWidget.
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
* Fixed bug where syntax highlighting was lost after updating a prompt with a bad number....
r2800 # Protected class attributes.
_highlighter_class = FrontendHighlighter
_input_splitter_class = InputSplitter
epatters
Initial checkin of Qt frontend code.
r2602 #---------------------------------------------------------------------------
epatters
Refactored ConsoleWidget to encapsulate, rather than inherit from, QPlainTextEdit. This permits a QTextEdit to be substituted for a QPlainTextEdit if desired. It also makes it more clear what is the public interface of ConsoleWidget.
r2736 # 'object' interface
epatters
Initial checkin of Qt frontend code.
r2602 #---------------------------------------------------------------------------
epatters
Refactored ConsoleWidget to encapsulate, rather than inherit from, QPlainTextEdit. This permits a QTextEdit to be substituted for a QPlainTextEdit if desired. It also makes it more clear what is the public interface of ConsoleWidget.
r2736 def __init__(self, *args, **kw):
super(FrontendWidget, self).__init__(*args, **kw)
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 # FrontendWidget protected variables.
epatters
Refactored ConsoleWidget to encapsulate, rather than inherit from, QPlainTextEdit. This permits a QTextEdit to be substituted for a QPlainTextEdit if desired. It also makes it more clear what is the public interface of ConsoleWidget.
r2736 self._call_tip_widget = CallTipWidget(self._control)
epatters
Initial checkin of Qt frontend code.
r2602 self._completion_lexer = CompletionLexer(PythonLexer())
epatters
* The Qt console frontend now ignores cross chatter from other frontends....
r2824 self._hidden = False
epatters
* Fixed bug where syntax highlighting was lost after updating a prompt with a bad number....
r2800 self._highlighter = self._highlighter_class(self)
self._input_splitter = self._input_splitter_class(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
* IPythonWidget now has IPython-style prompts that are futher stylabla via CSS...
r2715 # Configure the ConsoleWidget.
epatters
* ConsoleWidget now has better support for non-GUI tab completion. Multiple matches are formatted into columns....
r2723 self.tab_width = 4
epatters
* IPythonWidget now has IPython-style prompts that are futher stylabla via CSS...
r2715 self._set_continuation_prompt('... ')
epatters
Refactored ConsoleWidget to encapsulate, rather than inherit from, QPlainTextEdit. This permits a QTextEdit to be substituted for a QPlainTextEdit if desired. It also makes it more clear what is the public interface of ConsoleWidget.
r2736 # Connect signal handlers.
document = self._control.document()
document.contentsChange.connect(self._document_contents_change)
epatters
* Adding object_info_request support to prototype kernel....
r2612
epatters
Minor comment cleanup.
r2669 #---------------------------------------------------------------------------
epatters
Initial checkin of Qt frontend code.
r2602 # 'ConsoleWidget' abstract interface
#---------------------------------------------------------------------------
epatters
* Refactored ConsoleWidget execution API for greater flexibility and clarity....
r2688 def _is_complete(self, source, interactive):
""" Returns whether 'source' can be completely processed and a new
prompt created. When triggered by an Enter/Return key press,
'interactive' is True; otherwise, it is False.
epatters
Initial checkin of Qt frontend code.
r2602 """
epatters
* ConsoleWidget now has better support for non-GUI tab completion. Multiple matches are formatted into columns....
r2723 complete = self._input_splitter.push(source.expandtabs(4))
epatters
* Refactored ConsoleWidget execution API for greater flexibility and clarity....
r2688 if interactive:
complete = not self._input_splitter.push_accepts_more()
return complete
def _execute(self, source, hidden):
""" Execute 'source'. If 'hidden', do not show any output.
"""
self.kernel_manager.xreq_channel.execute(source)
self._hidden = hidden
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 _execute_interrupt(self):
""" Attempts to stop execution. Returns whether this method has an
implementation.
"""
self._interrupt_kernel()
return True
epatters
Initial checkin of Qt frontend code.
r2602
def _prompt_started_hook(self):
""" Called immediately after a new prompt is displayed.
"""
epatters
Fixed bug where syntax highlighting was enabled during raw_input mode.
r2709 if not self._reading:
self._highlighter.highlighting_on = True
epatters
Initial checkin of Qt frontend code.
r2602
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.
"""
epatters
Fixed bug where syntax highlighting was enabled during raw_input mode.
r2709 if not self._reading:
self._highlighter.highlighting_on = False
epatters
Initial checkin of Qt frontend code.
r2602
def _tab_pressed(self):
""" Called when the tab key is pressed. Returns whether to continue
processing the event.
"""
self._keep_cursor_in_buffer()
epatters
Refactored ConsoleWidget to encapsulate, rather than inherit from, QPlainTextEdit. This permits a QTextEdit to be substituted for a QPlainTextEdit if desired. It also makes it more clear what is the public interface of ConsoleWidget.
r2736 cursor = self._get_cursor()
epatters
* Moved AnsiCodeProcessor to separate file, refactored its API, and added unit tests....
r2716 return not self._complete()
epatters
Initial checkin of Qt frontend code.
r2602
#---------------------------------------------------------------------------
epatters
Fixed bug with ConsoleWidget smart paste.
r2787 # 'ConsoleWidget' protected interface
#---------------------------------------------------------------------------
def _show_continuation_prompt(self):
""" Reimplemented for auto-indentation.
"""
super(FrontendWidget, self)._show_continuation_prompt()
spaces = self._input_splitter.indent_spaces
self._append_plain_text('\t' * (spaces / self.tab_width))
self._append_plain_text(' ' * (spaces % self.tab_width))
#---------------------------------------------------------------------------
epatters
* Moved KernelManager attribute management code in FrontendWidget into a mixin class usable in any Qt frontend. Registering handlers for message types is now trivial....
r2770 # 'BaseFrontendMixin' abstract interface
epatters
Initial checkin of Qt frontend code.
r2602 #---------------------------------------------------------------------------
epatters
* Moved KernelManager attribute management code in FrontendWidget into a mixin class usable in any Qt frontend. Registering handlers for message types is now trivial....
r2770 def _handle_complete_reply(self, rep):
""" Handle replies for tab completion.
epatters
Initial checkin of Qt frontend code.
r2602 """
epatters
* Moved KernelManager attribute management code in FrontendWidget into a mixin class usable in any Qt frontend. Registering handlers for message types is now trivial....
r2770 cursor = self._get_cursor()
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
* Moved KernelManager attribute management code in FrontendWidget into a mixin class usable in any Qt frontend. Registering handlers for message types is now trivial....
r2770 def _handle_execute_reply(self, msg):
""" Handles replies for code execution.
epatters
Initial checkin of Qt kernel manager. Began refactor of FrontendWidget.
r2609 """
epatters
* Moved KernelManager attribute management code in FrontendWidget into a mixin class usable in any Qt frontend. Registering handlers for message types is now trivial....
r2770 if not self._hidden:
# Make sure that all output from the SUB channel has been processed
# before writing a new prompt.
self.kernel_manager.sub_channel.flush()
content = msg['content']
status = content['status']
if status == 'ok':
self._process_execute_ok(msg)
elif status == 'error':
self._process_execute_error(msg)
elif status == 'abort':
self._process_execute_abort(msg)
epatters
Updated IPythonWidget to use new prompt information. Initial prompt requests are not implemented.
r2797 self._show_interpreter_prompt_for_reply(msg)
epatters
* Moved KernelManager attribute management code in FrontendWidget into a mixin class usable in any Qt frontend. Registering handlers for message types is now trivial....
r2770 self.executed.emit(msg)
def _handle_input_request(self, msg):
""" Handle requests for raw_input.
"""
epatters
Minor cleanup and bug fix.
r2771 if self._hidden:
raise RuntimeError('Request for raw input during hidden execution.')
epatters
* Moved KernelManager attribute management code in FrontendWidget into a mixin class usable in any Qt frontend. Registering handlers for message types is now trivial....
r2770 # Make sure that all output from the SUB channel has been processed
# before entering readline mode.
self.kernel_manager.sub_channel.flush()
def callback(line):
self.kernel_manager.rep_channel.input(line)
self._readline(msg['content']['prompt'], callback=callback)
epatters
Initial checkin of Qt kernel manager. Began refactor of FrontendWidget.
r2609
epatters
* Moved KernelManager attribute management code in FrontendWidget into a mixin class usable in any Qt frontend. Registering handlers for message types is now trivial....
r2770 def _handle_object_info_reply(self, rep):
""" Handle replies for call tips.
epatters
Initial checkin of Qt kernel manager. Began refactor of FrontendWidget.
r2609 """
epatters
* Moved KernelManager attribute management code in FrontendWidget into a mixin class usable in any Qt frontend. Registering handlers for message types is now trivial....
r2770 cursor = self._get_cursor()
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)
epatters
* Added 'started_listening' and 'stopped_listening' signals to QtKernelManager. The FrontendWidget listens for these signals....
r2643
epatters
* Moved KernelManager attribute management code in FrontendWidget into a mixin class usable in any Qt frontend. Registering handlers for message types is now trivial....
r2770 def _handle_pyout(self, msg):
""" Handle display hook output.
"""
epatters
* The Qt console frontend now ignores cross chatter from other frontends....
r2824 if not self._hidden and self._is_from_this_session(msg):
self._append_plain_text(msg['content']['data'] + '\n')
epatters
Initial checkin of Qt kernel manager. Began refactor of FrontendWidget.
r2609
epatters
* Moved KernelManager attribute management code in FrontendWidget into a mixin class usable in any Qt frontend. Registering handlers for message types is now trivial....
r2770 def _handle_stream(self, msg):
""" Handle stdout, stderr, and stdin.
"""
epatters
* The Qt console frontend now ignores cross chatter from other frontends....
r2824 if not self._hidden and self._is_from_this_session(msg):
self._append_plain_text(msg['content']['data'])
self._control.moveCursor(QtGui.QTextCursor.End)
epatters
* Moved KernelManager attribute management code in FrontendWidget into a mixin class usable in any Qt frontend. Registering handlers for message types is now trivial....
r2770
def _started_channels(self):
""" Called when the KernelManager channels have started listening or
when the frontend is assigned an already listening KernelManager.
"""
self._reset()
self._append_plain_text(self._get_banner())
self._show_interpreter_prompt()
def _stopped_channels(self):
""" Called when the KernelManager channels have stopped listening or
when a listening KernelManager is removed from the frontend.
"""
# FIXME: Print a message here?
pass
#---------------------------------------------------------------------------
# 'FrontendWidget' interface
#---------------------------------------------------------------------------
def execute_file(self, path, hidden=False):
""" Attempts to execute file with 'path'. If 'hidden', no output is
shown.
"""
self.execute('execfile("%s")' % path, hidden=hidden)
epatters
Initial checkin of Qt kernel manager. Began refactor of FrontendWidget.
r2609
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
epatters
Refactored ConsoleWidget to encapsulate, rather than inherit from, QPlainTextEdit. This permits a QTextEdit to be substituted for a QPlainTextEdit if desired. It also makes it more clear what is the public interface of ConsoleWidget.
r2736 cursor = self._get_cursor()
epatters
Initial checkin of Qt frontend code.
r2602 cursor.movePosition(QtGui.QTextCursor.Left)
epatters
Refactored ConsoleWidget to encapsulate, rather than inherit from, QPlainTextEdit. This permits a QTextEdit to be substituted for a QPlainTextEdit if desired. It also makes it more clear what is the public interface of ConsoleWidget.
r2736 document = self._control.document()
epatters
Initial checkin of Qt frontend code.
r2602 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)
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 self._call_tip_id = self.kernel_manager.xreq_channel.object_info(name)
self._call_tip_pos = self._get_cursor().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)
Fernando Perez
Multiple improvements to tab completion....
r2839
# FIXME - Evan: we need the position of the cursor in the current input
# buffer. I tried this line below but the numbers I get are bogus. -
# Not sure what to do. fperez.
cursor_pos = self._get_cursor().position()
epatters
* Adding object_info_request support to prototype kernel....
r2612 self._complete_id = self.kernel_manager.xreq_channel.complete(
Fernando Perez
Multiple improvements to tab completion....
r2839 text, self._get_input_buffer_cursor_line(), cursor_pos,
self.input_buffer)
epatters
Refactored ConsoleWidget to encapsulate, rather than inherit from, QPlainTextEdit. This permits a QTextEdit to be substituted for a QPlainTextEdit if desired. It also makes it more clear what is the public interface of ConsoleWidget.
r2736 self._complete_pos = self._get_cursor().position()
epatters
Initial checkin of Qt frontend code.
r2602 return True
epatters
Added banners to FrontendWidget and IPythonWidget.
r2714 def _get_banner(self):
""" Gets a banner to display at the beginning of a session.
"""
banner = 'Python %s on %s\nType "help", "copyright", "credits" or ' \
'"license" for more information.'
return banner % (sys.version, sys.platform)
epatters
Initial checkin of Qt frontend code.
r2602 def _get_context(self, cursor=None):
""" Gets the context at the current cursor location.
"""
if cursor is None:
epatters
Refactored ConsoleWidget to encapsulate, rather than inherit from, QPlainTextEdit. This permits a QTextEdit to be substituted for a QPlainTextEdit if desired. It also makes it more clear what is the public interface of ConsoleWidget.
r2736 cursor = self._get_cursor()
epatters
Numerous fixes to ConsoleWidget editing region maintenence code. It now impossible (or at least very difficult :) to break the input region.
r2764 cursor.movePosition(QtGui.QTextCursor.StartOfBlock,
epatters
Initial checkin of Qt frontend code.
r2602 QtGui.QTextCursor.KeepAnchor)
epatters
Made use of plain text consistent.
r2720 text = str(cursor.selection().toPlainText())
epatters
Initial checkin of Qt frontend code.
r2602 return self._completion_lexer.get_context(text)
epatters
* Added ability to interrupt a kernel to FrontendWidget...
r2687 def _interrupt_kernel(self):
""" Attempts to the interrupt the kernel.
"""
if self.kernel_manager.has_kernel:
self.kernel_manager.signal_kernel(signal.SIGINT)
else:
epatters
Refactored ConsoleWidget to encapsulate, rather than inherit from, QPlainTextEdit. This permits a QTextEdit to be substituted for a QPlainTextEdit if desired. It also makes it more clear what is the public interface of ConsoleWidget.
r2736 self._append_plain_text('Kernel process is either remote or '
'unspecified. Cannot interrupt.\n')
epatters
* Added ability to interrupt a kernel to FrontendWidget...
r2687
epatters
* Moved KernelManager attribute management code in FrontendWidget into a mixin class usable in any Qt frontend. Registering handlers for message types is now trivial....
r2770 def _process_execute_abort(self, msg):
""" Process a reply for an aborted execution request.
epatters
* IPythonWidget now has IPython-style prompts that are futher stylabla via CSS...
r2715 """
epatters
* Moved KernelManager attribute management code in FrontendWidget into a mixin class usable in any Qt frontend. Registering handlers for message types is now trivial....
r2770 self._append_plain_text("ERROR: execution aborted\n")
epatters
Initial checkin of Qt frontend code.
r2602
epatters
* Moved KernelManager attribute management code in FrontendWidget into a mixin class usable in any Qt frontend. Registering handlers for message types is now trivial....
r2770 def _process_execute_error(self, msg):
""" Process a reply for an execution request that resulted in an error.
epatters
Progress on raw_input.
r2705 """
epatters
* Moved KernelManager attribute management code in FrontendWidget into a mixin class usable in any Qt frontend. Registering handlers for message types is now trivial....
r2770 content = msg['content']
traceback = ''.join(content['traceback'])
self._append_plain_text(traceback)
epatters
Progress on raw_input.
r2705
epatters
* Moved KernelManager attribute management code in FrontendWidget into a mixin class usable in any Qt frontend. Registering handlers for message types is now trivial....
r2770 def _process_execute_ok(self, msg):
""" Process a reply for a successful execution equest.
epatters
Progress on raw_input.
r2705 """
epatters
* Refactored payload handling mechanism....
r2835 payload = msg['content']['payload']
for item in payload:
if not self._process_execute_payload(item):
warning = 'Received unknown payload of type %s\n'
self._append_plain_text(warning % repr(item['source']))
def _process_execute_payload(self, item):
""" Process a single payload item from the list of payload items in an
execution reply. Returns whether the payload was handled.
"""
epatters
* Moved KernelManager attribute management code in FrontendWidget into a mixin class usable in any Qt frontend. Registering handlers for message types is now trivial....
r2770 # The basic FrontendWidget doesn't handle payloads, as they are a
# mechanism for going beyond the standard Python interpreter model.
epatters
* Refactored payload handling mechanism....
r2835 return False
epatters
Progress on raw_input.
r2705
epatters
* Moved KernelManager attribute management code in FrontendWidget into a mixin class usable in any Qt frontend. Registering handlers for message types is now trivial....
r2770 def _show_interpreter_prompt(self):
""" Shows a prompt for the interpreter.
"""
self._show_prompt('>>> ')
epatters
Updated IPythonWidget to use new prompt information. Initial prompt requests are not implemented.
r2797 def _show_interpreter_prompt_for_reply(self, msg):
""" Shows a prompt for the interpreter given an 'execute_reply' message.
"""
self._show_interpreter_prompt()
epatters
* Moved KernelManager attribute management code in FrontendWidget into a mixin class usable in any Qt frontend. Registering handlers for message types is now trivial....
r2770 #------ Signal handlers ----------------------------------------------------
epatters
Initial checkin of Qt frontend code.
r2602 def _document_contents_change(self, position, removed, added):
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 """ Called whenever the document's content changes. Display a call tip
epatters
Initial checkin of Qt frontend code.
r2602 if appropriate.
"""
# Calculate where the cursor should be *after* the change:
position += added
epatters
Refactored ConsoleWidget to encapsulate, rather than inherit from, QPlainTextEdit. This permits a QTextEdit to be substituted for a QPlainTextEdit if desired. It also makes it more clear what is the public interface of ConsoleWidget.
r2736 document = self._control.document()
if position == self._get_cursor().position():
epatters
Initial checkin of Qt frontend code.
r2602 self._call_tip()