##// END OF EJS Templates
Fixed bug in ConsoleWidget where text could be dragged and dropped.
Fixed bug in ConsoleWidget where text could be dragged and dropped.

File last commit:

r2861:dc3f913a
r2865:dfde2973
Show More
ipython_widget.py
343 lines | 13.6 KiB | text/x-python | PythonLexer
epatters
Reimplemented IPythonWidget's edit magic handling to support line numbers. Also, removed the code path for launching the file with the system default Python application, as this is too dangerous.
r2850 """ A FrontendWidget that emulates the interface of the console IPython and
supports the additional functionality provided by the IPython kernel.
TODO: Add support for retrieving the system default editor. Requires code
paths for Windows (use the registry), Mac OS (use LaunchServices), and
Linux (use the xdg system).
"""
epatters
Added preliminary editor support to IPythonWidget.
r2793 # Standard library imports
from subprocess import Popen
epatters
* Updated FrontendWidget to use BlockBreaker for parsing input...
r2630 # System library imports
from PyQt4 import QtCore, QtGui
# Local imports
epatters
Integrated new IPythonInputSplitter into IPythonWidget.
r2789 from IPython.core.inputsplitter import IPythonInputSplitter
epatters
Added banners to FrontendWidget and IPythonWidget.
r2714 from IPython.core.usage import default_banner
epatters
* Created an IPythonWidget subclass of FrontendWidget to contain IPython specific functionality....
r2627 from frontend_widget import FrontendWidget
epatters
Updated IPythonWidget to use new prompt information. Initial prompt requests are not implemented.
r2797 class IPythonPromptBlock(object):
""" An internal storage object for IPythonWidget.
"""
def __init__(self, block, length, number):
self.block = block
self.length = length
self.number = number
epatters
* Created an IPythonWidget subclass of FrontendWidget to contain IPython specific functionality....
r2627 class IPythonWidget(FrontendWidget):
""" A FrontendWidget for an IPython kernel.
"""
epatters
Added preliminary editor support to IPythonWidget.
r2793 # Signal emitted when an editor is needed for a file and the editor has been
epatters
* Refactored payload handling mechanism....
r2835 # specified as 'custom'. See 'set_editor' for more information.
epatters
Fixed line_number being inserted as a string into the %edit payload.
r2836 custom_edit_requested = QtCore.pyqtSignal(object, object)
epatters
Added preliminary editor support to IPythonWidget.
r2793
epatters
* Fleshed out IPythonWidget's style control....
r2725 # The default stylesheet: black text on a white background.
epatters
* IPythonWidget now has IPython-style prompts that are futher stylabla via CSS...
r2715 default_stylesheet = """
epatters
* Moved AnsiCodeProcessor to separate file, refactored its API, and added unit tests....
r2716 .error { color: red; }
epatters
* IPythonWidget now has IPython-style prompts that are futher stylabla via CSS...
r2715 .in-prompt { color: navy; }
.in-prompt-number { font-weight: bold; }
.out-prompt { color: darkred; }
.out-prompt-number { font-weight: bold; }
"""
epatters
* Fleshed out IPythonWidget's style control....
r2725 # A dark stylesheet: white text on a black background.
dark_stylesheet = """
epatters
Clean up styling information.
r2822 QPlainTextEdit, QTextEdit { background-color: black; color: white }
epatters
* Fleshed out IPythonWidget's style control....
r2725 QFrame { border: 1px solid grey; }
.error { color: red; }
.in-prompt { color: lime; }
.in-prompt-number { color: lime; font-weight: bold; }
.out-prompt { color: red; }
.out-prompt-number { color: red; font-weight: bold; }
"""
epatters
Added machinery to IPythonWidget for updating the previous prompt number.
r2733 # Default prompts.
epatters
IPythonWidget now supports 'input_sep'.
r2806 in_prompt = 'In [<span class="in-prompt-number">%i</span>]: '
epatters
Added machinery to IPythonWidget for updating the previous prompt number.
r2733 out_prompt = 'Out[<span class="out-prompt-number">%i</span>]: '
epatters
* Refactored payload handling mechanism....
r2835 # FrontendWidget protected class variables.
Fernando Perez
Fix bug with IPythonInputSplitter in block input mode.
r2861 _input_splitter_class = IPythonInputSplitter
epatters
* Fixed bug where syntax highlighting was lost after updating a prompt with a bad number....
r2800
epatters
* Refactored payload handling mechanism....
r2835 # IPythonWidget protected class variables.
_payload_source_edit = 'IPython.zmq.zmqshell.ZMQInteractiveShell.edit_magic'
_payload_source_page = 'IPython.zmq.page.page'
epatters
* Created an IPythonWidget subclass of FrontendWidget to contain IPython specific functionality....
r2627 #---------------------------------------------------------------------------
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
* Created an IPythonWidget subclass of FrontendWidget to contain IPython specific functionality....
r2627 #---------------------------------------------------------------------------
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(IPythonWidget, self).__init__(*args, **kw)
epatters
* Created an IPythonWidget subclass of FrontendWidget to contain IPython specific functionality....
r2627
epatters
Integrated new IPythonInputSplitter into IPythonWidget.
r2789 # IPythonWidget protected variables.
epatters
Updated IPythonWidget to use new prompt information. Initial prompt requests are not implemented.
r2797 self._previous_prompt_obj = None
epatters
* IPythonWidget now has IPython-style prompts that are futher stylabla via CSS...
r2715
epatters
Added preliminary editor support to IPythonWidget.
r2793 # Set a default editor and stylesheet.
self.set_editor('default')
epatters
IPythonWidget now supports styling the syntax highlighting.
r2728 self.reset_styling()
epatters
* Refactored ConsoleWidget execution API for greater flexibility and clarity....
r2688
#---------------------------------------------------------------------------
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
* Added support for prompt and history requests to the kernel manager and Qt console frontend....
r2844 def _handle_history_reply(self, msg):
""" Implemented to handle history replies, which are only supported by
the IPython kernel.
"""
history_dict = msg['content']['history']
items = [ history_dict[key] for key in sorted(history_dict.keys()) ]
self._set_history(items)
def _handle_prompt_reply(self, msg):
""" Implemented to handle prompt number replies, which are only
supported by the IPython kernel.
"""
content = msg['content']
self._show_interpreter_prompt(content['prompt_number'],
content['input_sep'])
epatters
IPythonWidget now supports 'input_sep'.
r2806 def _handle_pyout(self, 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 """ Reimplemented for IPython-style "display hook".
"""
epatters
* The Qt console frontend now ignores cross chatter from other frontends....
r2824 if not self._hidden and self._is_from_this_session(msg):
content = msg['content']
prompt_number = content['prompt_number']
self._append_plain_text(content['output_sep'])
self._append_html(self._make_out_prompt(prompt_number))
self._append_plain_text(content['data'] + '\n' +
content['output_sep2'])
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
epatters
* Added support for prompt and history requests to the kernel manager and Qt console frontend....
r2844 def _started_channels(self):
""" Reimplemented to make a history request.
"""
super(IPythonWidget, self)._started_channels()
# FIXME: Disabled until history requests are properly implemented.
#self.kernel_manager.xreq_channel.history(raw=True, output=False)
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 #---------------------------------------------------------------------------
epatters
* Refactored ConsoleWidget execution API for greater flexibility and clarity....
r2688 # 'FrontendWidget' interface
#---------------------------------------------------------------------------
def execute_file(self, path, hidden=False):
""" Reimplemented to use the 'run' magic.
"""
Fernando Perez
Improvements to exception handling to transport structured tracebacks....
r2838 self.execute('%%run %s' % path, hidden=hidden)
epatters
* Created an IPythonWidget subclass of FrontendWidget to contain IPython specific functionality....
r2627
#---------------------------------------------------------------------------
epatters
Added banners to FrontendWidget and IPythonWidget.
r2714 # 'FrontendWidget' protected interface
#---------------------------------------------------------------------------
def _get_banner(self):
epatters
* IPythonWidget now has IPython-style prompts that are futher stylabla via CSS...
r2715 """ Reimplemented to return IPython's default banner.
epatters
Added banners to FrontendWidget and IPythonWidget.
r2714 """
epatters
IPythonWidget now supports 'input_sep'.
r2806 return default_banner + '\n'
epatters
Added banners to FrontendWidget and IPythonWidget.
r2714
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):
""" Reimplemented for IPython-style traceback formatting.
"""
content = msg['content']
epatters
* Tab completion now uses the correct cursor position....
r2841 traceback = '\n'.join(content['traceback']) + '\n'
if False:
# FIXME: For now, tracebacks come as plain text, so we can't use
Fernando Perez
Improvements to exception handling to transport structured tracebacks....
r2838 # the html renderer yet. Once we refactor ultratb to produce
# properly styled tracebacks, this branch should be the default
traceback = traceback.replace(' ', '&nbsp;')
traceback = traceback.replace('\n', '<br/>')
ename = content['ename']
ename_styled = '<span class="error">%s</span>' % ename
traceback = traceback.replace(ename, ename_styled)
self._append_html(traceback)
else:
# This is the fallback for now, using plain text with ansi escapes
self._append_plain_text(traceback)
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
epatters
* Refactored payload handling mechanism....
r2835 def _process_execute_payload(self, item):
""" Reimplemented to handle %edit and paging payloads.
"""
if item['source'] == self._payload_source_edit:
epatters
Reimplemented IPythonWidget's edit magic handling to support line numbers. Also, removed the code path for launching the file with the system default Python application, as this is too dangerous.
r2850 self._edit(item['filename'], item['line_number'])
epatters
* Refactored payload handling mechanism....
r2835 return True
elif item['source'] == self._payload_source_page:
self._page(item['data'])
return True
else:
return False
epatters
IPythonWidget now supports 'input_sep'.
r2806 def _show_interpreter_prompt(self, number=None, input_sep='\n'):
epatters
* IPythonWidget now has IPython-style prompts that are futher stylabla via CSS...
r2715 """ Reimplemented for IPython-style prompts.
"""
epatters
* Added support for prompt and history requests to the kernel manager and Qt console frontend....
r2844 # If a number was not specified, make a prompt number request.
epatters
Updated IPythonWidget to use new prompt information. Initial prompt requests are not implemented.
r2797 if number is None:
epatters
* Added support for prompt and history requests to the kernel manager and Qt console frontend....
r2844 self.kernel_manager.xreq_channel.prompt()
return
epatters
Updated IPythonWidget to use new prompt information. Initial prompt requests are not implemented.
r2797
epatters
IPythonWidget now supports 'input_sep'.
r2806 # Show a new prompt and save information about it so that it can be
# updated later if the prompt number turns out to be wrong.
self._append_plain_text(input_sep)
epatters
Updated IPythonWidget to use new prompt information. Initial prompt requests are not implemented.
r2797 self._show_prompt(self._make_in_prompt(number), html=True)
epatters
IPythonWidget now supports 'input_sep'.
r2806 block = self._control.document().lastBlock()
length = len(self._prompt)
self._previous_prompt_obj = IPythonPromptBlock(block, length, number)
epatters
* IPythonWidget now has IPython-style prompts that are futher stylabla via CSS...
r2715
# Update continuation prompt to reflect (possibly) new prompt length.
epatters
Added machinery to IPythonWidget for updating the previous prompt number.
r2733 self._set_continuation_prompt(
self._make_continuation_prompt(self._prompt), html=True)
epatters
* IPythonWidget now has IPython-style prompts that are futher stylabla via CSS...
r2715
epatters
Updated IPythonWidget to use new prompt information. Initial prompt requests are not implemented.
r2797 def _show_interpreter_prompt_for_reply(self, msg):
""" Reimplemented for IPython-style prompts.
"""
# Update the old prompt number if necessary.
content = msg['content']
previous_prompt_number = content['prompt_number']
if self._previous_prompt_obj and \
self._previous_prompt_obj.number != previous_prompt_number:
block = self._previous_prompt_obj.block
epatters
Fixed bug introduced by the recent payload processing refactor.
r2846
# Make sure the prompt block has not been erased.
epatters
Fixed bug where replacement prompts were occasionally inserted at inappropriate times.
r2845 if block.isValid() and not block.text().isEmpty():
epatters
* Fixed bug where syntax highlighting was lost after updating a prompt with a bad number....
r2800
# Remove the old prompt and insert a new prompt.
epatters
Updated IPythonWidget to use new prompt information. Initial prompt requests are not implemented.
r2797 cursor = QtGui.QTextCursor(block)
cursor.movePosition(QtGui.QTextCursor.Right,
QtGui.QTextCursor.KeepAnchor,
self._previous_prompt_obj.length)
prompt = self._make_in_prompt(previous_prompt_number)
epatters
* Fixed bug where syntax highlighting was lost after updating a prompt with a bad number....
r2800 self._prompt = self._insert_html_fetching_plain_text(
cursor, prompt)
epatters
Minor cleanup.
r2825 # When the HTML is inserted, Qt blows away the syntax
# highlighting for the line, so we need to rehighlight it.
epatters
* Fixed bug where syntax highlighting was lost after updating a prompt with a bad number....
r2800 self._highlighter.rehighlightBlock(cursor.block())
epatters
Updated IPythonWidget to use new prompt information. Initial prompt requests are not implemented.
r2797 self._previous_prompt_obj = None
# Show a new prompt with the kernel's estimated prompt number.
epatters
IPythonWidget now supports 'input_sep'.
r2806 next_prompt = content['next_prompt']
self._show_interpreter_prompt(next_prompt['prompt_number'],
next_prompt['input_sep'])
epatters
Updated IPythonWidget to use new prompt information. Initial prompt requests are not implemented.
r2797
epatters
Added banners to FrontendWidget and IPythonWidget.
r2714 #---------------------------------------------------------------------------
epatters
* Created an IPythonWidget subclass of FrontendWidget to contain IPython specific functionality....
r2627 # 'IPythonWidget' interface
#---------------------------------------------------------------------------
epatters
IPythonWidget now supports styling the syntax highlighting.
r2728 def reset_styling(self):
""" Restores the default IPythonWidget styling.
epatters
* Created an IPythonWidget subclass of FrontendWidget to contain IPython specific functionality....
r2627 """
epatters
IPythonWidget now supports styling the syntax highlighting.
r2728 self.set_styling(self.default_stylesheet, syntax_style='default')
epatters
Clean up styling information.
r2822 #self.set_styling(self.dark_stylesheet, syntax_style='monokai')
epatters
IPythonWidget now supports styling the syntax highlighting.
r2728
epatters
Reimplemented IPythonWidget's edit magic handling to support line numbers. Also, removed the code path for launching the file with the system default Python application, as this is too dangerous.
r2850 def set_editor(self, editor, line_editor=None):
epatters
Added preliminary editor support to IPythonWidget.
r2793 """ Sets the editor to use with the %edit magic.
Parameters:
-----------
epatters
Reimplemented IPythonWidget's edit magic handling to support line numbers. Also, removed the code path for launching the file with the system default Python application, as this is too dangerous.
r2850 editor : str
A command for invoking a system text editor. If the string contains
a {filename} format specifier, it will be used. Otherwise, the
filename will be appended to the end the command.
epatters
Added preliminary editor support to IPythonWidget.
r2793
epatters
Reimplemented IPythonWidget's edit magic handling to support line numbers. Also, removed the code path for launching the file with the system default Python application, as this is too dangerous.
r2850 This parameter also takes a special value:
epatters
* Refactored payload handling mechanism....
r2835 'custom' : Emit a 'custom_edit_requested(str, int)' signal
instead of opening an editor.
epatters
Reimplemented IPythonWidget's edit magic handling to support line numbers. Also, removed the code path for launching the file with the system default Python application, as this is too dangerous.
r2850
line_editor : str, optional
The editor command to use when a specific line number is
requested. The string should contain two format specifiers: {line}
and {filename}. If this parameter is not specified, the line number
option to the %edit magic will be ignored.
epatters
Added preliminary editor support to IPythonWidget.
r2793 """
epatters
Reimplemented IPythonWidget's edit magic handling to support line numbers. Also, removed the code path for launching the file with the system default Python application, as this is too dangerous.
r2850 self._editor = editor
self._editor_line = line_editor
epatters
Added preliminary editor support to IPythonWidget.
r2793
epatters
IPythonWidget now supports styling the syntax highlighting.
r2728 def set_styling(self, stylesheet, syntax_style=None):
""" Sets the IPythonWidget styling.
Parameters:
-----------
stylesheet : str
A CSS stylesheet. The stylesheet can contain classes for:
1. Qt: QPlainTextEdit, QFrame, QWidget, etc
2. Pygments: .c, .k, .o, etc (see PygmentsHighlighter)
3. IPython: .error, .in-prompt, .out-prompt, etc.
syntax_style : str or None [default None]
If specified, use the Pygments style with given name. Otherwise,
the stylesheet is queried for Pygments style information.
epatters
* IPythonWidget now has IPython-style prompts that are futher stylabla via CSS...
r2715 """
epatters
* Fleshed out IPythonWidget's style control....
r2725 self.setStyleSheet(stylesheet)
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._control.document().setDefaultStyleSheet(stylesheet)
epatters
Fix to ensure that the paging widget is styled appropriately.
r2779 if self._page_control:
self._page_control.document().setDefaultStyleSheet(stylesheet)
epatters
* IPythonWidget now has IPython-style prompts that are futher stylabla via CSS...
r2715
epatters
IPythonWidget now supports styling the syntax highlighting.
r2728 if syntax_style is None:
self._highlighter.set_style_sheet(stylesheet)
else:
self._highlighter.set_style(syntax_style)
epatters
Added machinery to IPythonWidget for updating the previous prompt number.
r2733 #---------------------------------------------------------------------------
# 'IPythonWidget' protected interface
#---------------------------------------------------------------------------
epatters
Reimplemented IPythonWidget's edit magic handling to support line numbers. Also, removed the code path for launching the file with the system default Python application, as this is too dangerous.
r2850 def _edit(self, filename, line=None):
""" Opens a Python script for editing.
Parameters:
-----------
filename : str
A path to a local system file.
line : int, optional
A line of interest in the file.
"""
if self._editor == 'custom':
self.custom_edit_requested.emit(filename, line)
elif self._editor == 'default':
self._append_plain_text('No default editor available.\n')
else:
try:
filename = '"%s"' % filename
if line and self._editor_line:
command = self._editor_line.format(filename=filename,
line=line)
else:
try:
command = self._editor.format()
except KeyError:
command = self._editor.format(filename=filename)
else:
command += ' ' + filename
except KeyError:
self._append_plain_text('Invalid editor command.\n')
else:
try:
Popen(command, shell=True)
except OSError:
msg = 'Opening editor with command "%s" failed.\n'
self._append_plain_text(msg % command)
epatters
Added machinery to IPythonWidget for updating the previous prompt number.
r2733 def _make_in_prompt(self, number):
""" Given a prompt number, returns an HTML In prompt.
"""
body = self.in_prompt % number
return '<span class="in-prompt">%s</span>' % body
def _make_continuation_prompt(self, prompt):
""" Given a plain text version of an In prompt, returns an HTML
continuation prompt.
"""
end_chars = '...: '
space_count = len(prompt.lstrip('\n')) - len(end_chars)
body = '&nbsp;' * space_count + end_chars
return '<span class="in-prompt">%s</span>' % body
def _make_out_prompt(self, number):
""" Given a prompt number, returns an HTML Out prompt.
"""
body = self.out_prompt % number
return '<span class="out-prompt">%s</span>' % body