Show More
@@ -65,13 +65,16 b' class ConsoleWidget(Configurable, QtGui.QWidget):' | |||||
65 | """ |
|
65 | """ | |
66 | ) |
|
66 | ) | |
67 | gui_completion = Bool(False, config=True, |
|
67 | gui_completion = Bool(False, config=True, | |
68 | help="Use a list widget instead of plain text output for tab completion." |
|
68 | help=""" | |
|
69 | Use a list widget instead of plain text output for tab completion. | |||
|
70 | """ | |||
69 | ) |
|
71 | ) | |
70 | # NOTE: this value can only be specified during initialization. |
|
72 | # NOTE: this value can only be specified during initialization. | |
71 | kind = Enum(['plain', 'rich'], default_value='plain', config=True, |
|
73 | kind = Enum(['plain', 'rich'], default_value='plain', config=True, | |
72 | help=""" |
|
74 | help=""" | |
73 |
The type of underlying text widget to use. Valid values are 'plain', |
|
75 | The type of underlying text widget to use. Valid values are 'plain', | |
74 |
specifies a QPlainTextEdit, and 'rich', which specifies a |
|
76 | which specifies a QPlainTextEdit, and 'rich', which specifies a | |
|
77 | QTextEdit. | |||
75 | """ |
|
78 | """ | |
76 | ) |
|
79 | ) | |
77 | # NOTE: this value can only be specified during initialization. |
|
80 | # NOTE: this value can only be specified during initialization. | |
@@ -84,7 +87,8 b' class ConsoleWidget(Configurable, QtGui.QWidget):' | |||||
84 | 'hsplit' : When paging is requested, the widget is split |
|
87 | 'hsplit' : When paging is requested, the widget is split | |
85 | horizontally. The top pane contains the console, and the |
|
88 | horizontally. The top pane contains the console, and the | |
86 | bottom pane contains the paged text. |
|
89 | bottom pane contains the paged text. | |
87 |
'vsplit' : Similar to 'hsplit', except that a vertical splitter |
|
90 | 'vsplit' : Similar to 'hsplit', except that a vertical splitter | |
|
91 | used. | |||
88 | 'custom' : No action is taken by the widget beyond emitting a |
|
92 | 'custom' : No action is taken by the widget beyond emitting a | |
89 | 'custom_page_requested(str)' signal. |
|
93 | 'custom_page_requested(str)' signal. | |
90 | 'none' : The text is written directly to the console. |
|
94 | 'none' : The text is written directly to the console. | |
@@ -195,6 +199,7 b' class ConsoleWidget(Configurable, QtGui.QWidget):' | |||||
195 |
|
199 | |||
196 | # Initialize protected variables. Some variables contain useful state |
|
200 | # Initialize protected variables. Some variables contain useful state | |
197 | # information for subclasses; they should be considered read-only. |
|
201 | # information for subclasses; they should be considered read-only. | |
|
202 | self._append_before_prompt_pos = 0 | |||
198 | self._ansi_processor = QtAnsiCodeProcessor() |
|
203 | self._ansi_processor = QtAnsiCodeProcessor() | |
199 | self._completion_widget = CompletionWidget(self._control) |
|
204 | self._completion_widget = CompletionWidget(self._control) | |
200 | self._continuation_prompt = '> ' |
|
205 | self._continuation_prompt = '> ' | |
@@ -507,7 +512,7 b' class ConsoleWidget(Configurable, QtGui.QWidget):' | |||||
507 | """ |
|
512 | """ | |
508 | self._html_exporter.export() |
|
513 | self._html_exporter.export() | |
509 |
|
514 | |||
510 | def _get_input_buffer(self): |
|
515 | def _get_input_buffer(self, force=False): | |
511 | """ The text that the user has entered entered at the current prompt. |
|
516 | """ The text that the user has entered entered at the current prompt. | |
512 |
|
517 | |||
513 | If the console is currently executing, the text that is executing will |
|
518 | If the console is currently executing, the text that is executing will | |
@@ -515,7 +520,7 b' class ConsoleWidget(Configurable, QtGui.QWidget):' | |||||
515 | """ |
|
520 | """ | |
516 | # If we're executing, the input buffer may not even exist anymore due to |
|
521 | # If we're executing, the input buffer may not even exist anymore due to | |
517 | # the limit imposed by 'buffer_size'. Therefore, we store it. |
|
522 | # the limit imposed by 'buffer_size'. Therefore, we store it. | |
518 | if self._executing: |
|
523 | if self._executing and not force: | |
519 | return self._input_buffer_executing |
|
524 | return self._input_buffer_executing | |
520 |
|
525 | |||
521 | cursor = self._get_end_cursor() |
|
526 | cursor = self._get_end_cursor() | |
@@ -718,36 +723,47 b' class ConsoleWidget(Configurable, QtGui.QWidget):' | |||||
718 | # 'ConsoleWidget' protected interface |
|
723 | # 'ConsoleWidget' protected interface | |
719 | #-------------------------------------------------------------------------- |
|
724 | #-------------------------------------------------------------------------- | |
720 |
|
725 | |||
721 |
def _append_ |
|
726 | def _append_custom(self, insert, input, before_prompt=False): | |
722 |
""" A |
|
727 | """ A low-level method for appending content to the end of the buffer. | |
|
728 | ||||
|
729 | If 'before_prompt' is enabled, the content will be inserted before the | |||
|
730 | current prompt, if there is one. | |||
723 | """ |
|
731 | """ | |
724 | cursor = self._get_end_cursor() |
|
732 | # Determine where to insert the content. | |
725 | self._insert_html(cursor, html) |
|
733 | cursor = self._control.textCursor() | |
|
734 | if before_prompt and not self._executing: | |||
|
735 | cursor.setPosition(self._append_before_prompt_pos) | |||
|
736 | else: | |||
|
737 | cursor.movePosition(QtGui.QTextCursor.End) | |||
|
738 | start_pos = cursor.position() | |||
726 |
|
739 | |||
727 | def _append_html_fetching_plain_text(self, html): |
|
740 | # Perform the insertion. | |
728 | """ Appends 'html', then returns the plain text version of it. |
|
741 | result = insert(cursor, input) | |
729 | """ |
|
742 | ||
730 | cursor = self._get_end_cursor() |
|
743 | # Adjust the prompt position if we have inserted before it. This is safe | |
731 | return self._insert_html_fetching_plain_text(cursor, html) |
|
744 | # because buffer truncation is disabled when not executing. | |
|
745 | if before_prompt and not self._executing: | |||
|
746 | diff = cursor.position() - start_pos | |||
|
747 | self._append_before_prompt_pos += diff | |||
|
748 | self._prompt_pos += diff | |||
|
749 | ||||
|
750 | return result | |||
732 |
|
751 | |||
733 |
def _append_ |
|
752 | def _append_html(self, html, before_prompt=False): | |
734 |
""" Appends |
|
753 | """ Appends HTML at the end of the console buffer. | |
735 | ANSI codes if enabled. |
|
|||
736 | """ |
|
754 | """ | |
737 | cursor = self._get_end_cursor() |
|
755 | self._append_custom(self._insert_html, html, before_prompt) | |
738 | self._insert_plain_text(cursor, text) |
|
|||
739 |
|
756 | |||
740 |
def _append_plain_text |
|
757 | def _append_html_fetching_plain_text(self, html, before_prompt=False): | |
741 | """ Writes 'text' after the current prompt, then restores the old prompt |
|
758 | """ Appends HTML, then returns the plain text version of it. | |
742 | with its old input buffer. |
|
|||
743 | """ |
|
759 | """ | |
744 | input_buffer = self.input_buffer |
|
760 | return self._append_custom(self._insert_html_fetching_plain_text, | |
745 | self._append_plain_text('\n') |
|
761 | html, before_prompt) | |
746 | self._prompt_finished() |
|
|||
747 |
|
762 | |||
748 | self._append_plain_text(text) |
|
763 | def _append_plain_text(self, text, before_prompt=False): | |
749 | self._show_prompt() |
|
764 | """ Appends plain text, processing ANSI codes if enabled. | |
750 | self.input_buffer = input_buffer |
|
765 | """ | |
|
766 | self._append_custom(self._insert_plain_text, text, before_prompt) | |||
751 |
|
767 | |||
752 | def _cancel_text_completion(self): |
|
768 | def _cancel_text_completion(self): | |
753 | """ If text completion is progress, cancel it. |
|
769 | """ If text completion is progress, cancel it. | |
@@ -1616,7 +1632,8 b' class ConsoleWidget(Configurable, QtGui.QWidget):' | |||||
1616 | self._control.setReadOnly(False) |
|
1632 | self._control.setReadOnly(False) | |
1617 | self._control.setAttribute(QtCore.Qt.WA_InputMethodEnabled, True) |
|
1633 | self._control.setAttribute(QtCore.Qt.WA_InputMethodEnabled, True) | |
1618 |
|
1634 | |||
1619 |
self._ |
|
1635 | if not self._reading: | |
|
1636 | self._executing = False | |||
1620 | self._prompt_started_hook() |
|
1637 | self._prompt_started_hook() | |
1621 |
|
1638 | |||
1622 | # If the input buffer has changed while executing, load it. |
|
1639 | # If the input buffer has changed while executing, load it. | |
@@ -1659,11 +1676,11 b' class ConsoleWidget(Configurable, QtGui.QWidget):' | |||||
1659 | self._reading_callback = None |
|
1676 | self._reading_callback = None | |
1660 | while self._reading: |
|
1677 | while self._reading: | |
1661 | QtCore.QCoreApplication.processEvents() |
|
1678 | QtCore.QCoreApplication.processEvents() | |
1662 | return self.input_buffer.rstrip('\n') |
|
1679 | return self._get_input_buffer(force=True).rstrip('\n') | |
1663 |
|
1680 | |||
1664 | else: |
|
1681 | else: | |
1665 | self._reading_callback = lambda: \ |
|
1682 | self._reading_callback = lambda: \ | |
1666 | callback(self.input_buffer.rstrip('\n')) |
|
1683 | callback(self._get_input_buffer(force=True).rstrip('\n')) | |
1667 |
|
1684 | |||
1668 | def _set_continuation_prompt(self, prompt, html=False): |
|
1685 | def _set_continuation_prompt(self, prompt, html=False): | |
1669 | """ Sets the continuation prompt. |
|
1686 | """ Sets the continuation prompt. | |
@@ -1716,14 +1733,16 b' class ConsoleWidget(Configurable, QtGui.QWidget):' | |||||
1716 | If set, a new line will be written before showing the prompt if |
|
1733 | If set, a new line will be written before showing the prompt if | |
1717 | there is not already a newline at the end of the buffer. |
|
1734 | there is not already a newline at the end of the buffer. | |
1718 | """ |
|
1735 | """ | |
|
1736 | # Save the current end position to support _append*(before_prompt=True). | |||
|
1737 | cursor = self._get_end_cursor() | |||
|
1738 | self._append_before_prompt_pos = cursor.position() | |||
|
1739 | ||||
1719 | # Insert a preliminary newline, if necessary. |
|
1740 | # Insert a preliminary newline, if necessary. | |
1720 | if newline: |
|
1741 | if newline and cursor.position() > 0: | |
1721 | cursor = self._get_end_cursor() |
|
1742 | cursor.movePosition(QtGui.QTextCursor.Left, | |
1722 | if cursor.position() > 0: |
|
1743 | QtGui.QTextCursor.KeepAnchor) | |
1723 | cursor.movePosition(QtGui.QTextCursor.Left, |
|
1744 | if cursor.selection().toPlainText() != '\n': | |
1724 | QtGui.QTextCursor.KeepAnchor) |
|
1745 | self._append_plain_text('\n') | |
1725 | if cursor.selection().toPlainText() != '\n': |
|
|||
1726 | self._append_plain_text('\n') |
|
|||
1727 |
|
1746 | |||
1728 | # Write the prompt. |
|
1747 | # Write the prompt. | |
1729 | self._append_plain_text(self._prompt_sep) |
|
1748 | self._append_plain_text(self._prompt_sep) |
@@ -22,8 +22,7 b' from pygments_highlighter import PygmentsHighlighter' | |||||
22 |
|
22 | |||
23 |
|
23 | |||
24 | class FrontendHighlighter(PygmentsHighlighter): |
|
24 | class FrontendHighlighter(PygmentsHighlighter): | |
25 |
""" A PygmentsHighlighter that |
|
25 | """ A PygmentsHighlighter that understands and ignores prompts. | |
26 | prompts. |
|
|||
27 | """ |
|
26 | """ | |
28 |
|
27 | |||
29 | def __init__(self, frontend): |
|
28 | def __init__(self, frontend): | |
@@ -50,14 +49,12 b' class FrontendHighlighter(PygmentsHighlighter):' | |||||
50 | else: |
|
49 | else: | |
51 | prompt = self._frontend._continuation_prompt |
|
50 | prompt = self._frontend._continuation_prompt | |
52 |
|
51 | |||
53 | # Don't highlight the part of the string that contains the prompt. |
|
52 | # Only highlight if we can identify a prompt, but make sure not to | |
|
53 | # highlight the prompt. | |||
54 | if string.startswith(prompt): |
|
54 | if string.startswith(prompt): | |
55 | self._current_offset = len(prompt) |
|
55 | self._current_offset = len(prompt) | |
56 | string = string[len(prompt):] |
|
56 | string = string[len(prompt):] | |
57 | else: |
|
57 | super(FrontendHighlighter, self).highlightBlock(string) | |
58 | self._current_offset = 0 |
|
|||
59 |
|
||||
60 | PygmentsHighlighter.highlightBlock(self, string) |
|
|||
61 |
|
58 | |||
62 | def rehighlightBlock(self, block): |
|
59 | def rehighlightBlock(self, block): | |
63 | """ Reimplemented to temporarily enable highlighting if disabled. |
|
60 | """ Reimplemented to temporarily enable highlighting if disabled. | |
@@ -71,7 +68,7 b' class FrontendHighlighter(PygmentsHighlighter):' | |||||
71 | """ Reimplemented to highlight selectively. |
|
68 | """ Reimplemented to highlight selectively. | |
72 | """ |
|
69 | """ | |
73 | start += self._current_offset |
|
70 | start += self._current_offset | |
74 |
|
|
71 | super(FrontendHighlighter, self).setFormat(start, count, format) | |
75 |
|
72 | |||
76 |
|
73 | |||
77 | class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin): |
|
74 | class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin): | |
@@ -372,14 +369,8 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):' | |||||
372 | """ Handle display hook output. |
|
369 | """ Handle display hook output. | |
373 | """ |
|
370 | """ | |
374 | if not self._hidden and self._is_from_this_session(msg): |
|
371 | if not self._hidden and self._is_from_this_session(msg): | |
375 |
|
|
372 | text = msg['content']['data'] | |
376 | if isinstance(data, basestring): |
|
373 | self._append_plain_text(text + '\n', before_prompt=True) | |
377 | # plaintext data from pure Python kernel |
|
|||
378 | text = data |
|
|||
379 | else: |
|
|||
380 | # formatted output from DisplayFormatter (IPython kernel) |
|
|||
381 | text = data.get('text/plain', '') |
|
|||
382 | self._append_plain_text(text + '\n') |
|
|||
383 |
|
374 | |||
384 | def _handle_stream(self, msg): |
|
375 | def _handle_stream(self, msg): | |
385 | """ Handle stdout, stderr, and stdin. |
|
376 | """ Handle stdout, stderr, and stdin. | |
@@ -390,7 +381,7 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):' | |||||
390 | # widget's tab width. |
|
381 | # widget's tab width. | |
391 | text = msg['content']['data'].expandtabs(8) |
|
382 | text = msg['content']['data'].expandtabs(8) | |
392 |
|
383 | |||
393 | self._append_plain_text(text) |
|
384 | self._append_plain_text(text, before_prompt=True) | |
394 | self._control.moveCursor(QtGui.QTextCursor.End) |
|
385 | self._control.moveCursor(QtGui.QTextCursor.End) | |
395 |
|
386 | |||
396 | def _handle_shutdown_reply(self, msg): |
|
387 | def _handle_shutdown_reply(self, msg): |
@@ -62,16 +62,16 b' class IPythonWidget(FrontendWidget):' | |||||
62 | editor = Unicode(default_editor, config=True, |
|
62 | editor = Unicode(default_editor, config=True, | |
63 | help=""" |
|
63 | help=""" | |
64 | A command for invoking a system text editor. If the string contains a |
|
64 | A command for invoking a system text editor. If the string contains a | |
65 |
{filename} format specifier, it will be used. Otherwise, the filename |
|
65 | {filename} format specifier, it will be used. Otherwise, the filename | |
66 | be appended to the end the command. |
|
66 | will be appended to the end the command. | |
67 | """) |
|
67 | """) | |
68 |
|
68 | |||
69 | editor_line = Unicode(config=True, |
|
69 | editor_line = Unicode(config=True, | |
70 | help=""" |
|
70 | help=""" | |
71 | The editor command to use when a specific line number is requested. The |
|
71 | The editor command to use when a specific line number is requested. The | |
72 | string should contain two format specifiers: {line} and {filename}. If |
|
72 | string should contain two format specifiers: {line} and {filename}. If | |
73 |
this parameter is not specified, the line number option to the %edit |
|
73 | this parameter is not specified, the line number option to the %edit | |
74 | will be ignored. |
|
74 | magic will be ignored. | |
75 | """) |
|
75 | """) | |
76 |
|
76 | |||
77 | style_sheet = Unicode(config=True, |
|
77 | style_sheet = Unicode(config=True, | |
@@ -85,8 +85,9 b' class IPythonWidget(FrontendWidget):' | |||||
85 |
|
85 | |||
86 | syntax_style = Str(config=True, |
|
86 | syntax_style = Str(config=True, | |
87 | help=""" |
|
87 | help=""" | |
88 |
If not empty, use this Pygments style for syntax highlighting. |
|
88 | If not empty, use this Pygments style for syntax highlighting. | |
89 |
the style sheet is queried for Pygments style |
|
89 | Otherwise, the style sheet is queried for Pygments style | |
|
90 | information. | |||
90 | """) |
|
91 | """) | |
91 |
|
92 | |||
92 | # Prompts. |
|
93 | # Prompts. | |
@@ -187,20 +188,20 b' class IPythonWidget(FrontendWidget):' | |||||
187 | prompt_number = content['execution_count'] |
|
188 | prompt_number = content['execution_count'] | |
188 | data = content['data'] |
|
189 | data = content['data'] | |
189 | if data.has_key('text/html'): |
|
190 | if data.has_key('text/html'): | |
190 | self._append_plain_text(self.output_sep) |
|
191 | self._append_plain_text(self.output_sep, True) | |
191 | self._append_html(self._make_out_prompt(prompt_number)) |
|
192 | self._append_html(self._make_out_prompt(prompt_number), True) | |
192 | html = data['text/html'] |
|
193 | html = data['text/html'] | |
193 | self._append_plain_text('\n') |
|
194 | self._append_plain_text('\n', True) | |
194 | self._append_html(html + self.output_sep2) |
|
195 | self._append_html(html + self.output_sep2, True) | |
195 | elif data.has_key('text/plain'): |
|
196 | elif data.has_key('text/plain'): | |
196 | self._append_plain_text(self.output_sep) |
|
197 | self._append_plain_text(self.output_sep, True) | |
197 | self._append_html(self._make_out_prompt(prompt_number)) |
|
198 | self._append_html(self._make_out_prompt(prompt_number), True) | |
198 | text = data['text/plain'] |
|
199 | text = data['text/plain'] | |
199 | # If the repr is multiline, make sure we start on a new line, |
|
200 | # If the repr is multiline, make sure we start on a new line, | |
200 | # so that its lines are aligned. |
|
201 | # so that its lines are aligned. | |
201 | if "\n" in text and not self.output_sep.endswith("\n"): |
|
202 | if "\n" in text and not self.output_sep.endswith("\n"): | |
202 | self._append_plain_text('\n') |
|
203 | self._append_plain_text('\n', True) | |
203 | self._append_plain_text(text + self.output_sep2) |
|
204 | self._append_plain_text(text + self.output_sep2, True) | |
204 |
|
205 | |||
205 | def _handle_display_data(self, msg): |
|
206 | def _handle_display_data(self, msg): | |
206 | """ The base handler for the ``display_data`` message. |
|
207 | """ The base handler for the ``display_data`` message. | |
@@ -216,19 +217,19 b' class IPythonWidget(FrontendWidget):' | |||||
216 | # representation. |
|
217 | # representation. | |
217 | if data.has_key('text/html'): |
|
218 | if data.has_key('text/html'): | |
218 | html = data['text/html'] |
|
219 | html = data['text/html'] | |
219 | self._append_html(html) |
|
220 | self._append_html(html, True) | |
220 | elif data.has_key('text/plain'): |
|
221 | elif data.has_key('text/plain'): | |
221 | text = data['text/plain'] |
|
222 | text = data['text/plain'] | |
222 | self._append_plain_text(text) |
|
223 | self._append_plain_text(text, True) | |
223 | # This newline seems to be needed for text and html output. |
|
224 | # This newline seems to be needed for text and html output. | |
224 | self._append_plain_text(u'\n') |
|
225 | self._append_plain_text(u'\n', True) | |
225 |
|
226 | |||
226 | def _started_channels(self): |
|
227 | def _started_channels(self): | |
227 | """ Reimplemented to make a history request. |
|
228 | """ Reimplemented to make a history request. | |
228 | """ |
|
229 | """ | |
229 | super(IPythonWidget, self)._started_channels() |
|
230 | super(IPythonWidget, self)._started_channels() | |
230 |
self.kernel_manager.shell_channel.history(hist_access_type='tail', |
|
231 | self.kernel_manager.shell_channel.history(hist_access_type='tail', | |
231 |
|
232 | n=1000) | ||
232 | #--------------------------------------------------------------------------- |
|
233 | #--------------------------------------------------------------------------- | |
233 | # 'ConsoleWidget' public interface |
|
234 | # 'ConsoleWidget' public interface | |
234 | #--------------------------------------------------------------------------- |
|
235 | #--------------------------------------------------------------------------- | |
@@ -413,8 +414,8 b' class IPythonWidget(FrontendWidget):' | |||||
413 | self.custom_edit_requested.emit(filename, line) |
|
414 | self.custom_edit_requested.emit(filename, line) | |
414 | elif not self.editor: |
|
415 | elif not self.editor: | |
415 | self._append_plain_text('No default editor available.\n' |
|
416 | self._append_plain_text('No default editor available.\n' | |
416 |
'Specify a GUI text editor in the `IPythonWidget.editor` |
|
417 | 'Specify a GUI text editor in the `IPythonWidget.editor` ' | |
417 | 'to enable the %edit magic') |
|
418 | 'configurable to enable the %edit magic') | |
418 | else: |
|
419 | else: | |
419 | try: |
|
420 | try: | |
420 | filename = '"%s"' % filename |
|
421 | filename = '"%s"' % filename |
@@ -74,20 +74,17 b' class RichIPythonWidget(IPythonWidget):' | |||||
74 | prompt_number = content['execution_count'] |
|
74 | prompt_number = content['execution_count'] | |
75 | data = content['data'] |
|
75 | data = content['data'] | |
76 | if data.has_key('image/svg+xml'): |
|
76 | if data.has_key('image/svg+xml'): | |
77 | self._append_plain_text(self.output_sep) |
|
77 | self._append_plain_text(self.output_sep, True) | |
78 | self._append_html(self._make_out_prompt(prompt_number)) |
|
78 | self._append_html(self._make_out_prompt(prompt_number), True) | |
79 | # TODO: try/except this call. |
|
79 | self._append_svg(data['image/svg+xml'], True) | |
80 | self._append_svg(data['image/svg+xml']) |
|
80 | self._append_html(self.output_sep2, True) | |
81 | self._append_html(self.output_sep2) |
|
|||
82 | elif data.has_key('image/png'): |
|
81 | elif data.has_key('image/png'): | |
83 | self._append_plain_text(self.output_sep) |
|
82 | self._append_plain_text(self.output_sep, True) | |
84 | self._append_html(self._make_out_prompt(prompt_number)) |
|
83 | self._append_html(self._make_out_prompt(prompt_number), True) | |
85 | # This helps the output to look nice. |
|
84 | # This helps the output to look nice. | |
86 | self._append_plain_text('\n') |
|
85 | self._append_plain_text('\n', True) | |
87 | # TODO: try/except these calls |
|
86 | self._append_png(decodestring(data['image/png']), True) | |
88 | png = decodestring(data['image/png']) |
|
87 | self._append_html(self.output_sep2, True) | |
89 | self._append_png(png) |
|
|||
90 | self._append_html(self.output_sep2) |
|
|||
91 | else: |
|
88 | else: | |
92 | # Default back to the plain text representation. |
|
89 | # Default back to the plain text representation. | |
93 | return super(RichIPythonWidget, self)._handle_pyout(msg) |
|
90 | return super(RichIPythonWidget, self)._handle_pyout(msg) | |
@@ -103,14 +100,12 b' class RichIPythonWidget(IPythonWidget):' | |||||
103 | # FIXME: Is this the right ordering of things to try? |
|
100 | # FIXME: Is this the right ordering of things to try? | |
104 | if data.has_key('image/svg+xml'): |
|
101 | if data.has_key('image/svg+xml'): | |
105 | svg = data['image/svg+xml'] |
|
102 | svg = data['image/svg+xml'] | |
106 | # TODO: try/except this call. |
|
103 | self._append_svg(svg, True) | |
107 | self._append_svg(svg) |
|
|||
108 | elif data.has_key('image/png'): |
|
104 | elif data.has_key('image/png'): | |
109 | # TODO: try/except these calls |
|
|||
110 | # PNG data is base64 encoded as it passes over the network |
|
105 | # PNG data is base64 encoded as it passes over the network | |
111 | # in a JSON structure so we decode it. |
|
106 | # in a JSON structure so we decode it. | |
112 | png = decodestring(data['image/png']) |
|
107 | png = decodestring(data['image/png']) | |
113 | self._append_png(png) |
|
108 | self._append_png(png, True) | |
114 | else: |
|
109 | else: | |
115 | # Default back to the plain text representation. |
|
110 | # Default back to the plain text representation. | |
116 | return super(RichIPythonWidget, self)._handle_display_data(msg) |
|
111 | return super(RichIPythonWidget, self)._handle_display_data(msg) | |
@@ -119,35 +114,15 b' class RichIPythonWidget(IPythonWidget):' | |||||
119 | # 'RichIPythonWidget' protected interface |
|
114 | # 'RichIPythonWidget' protected interface | |
120 | #--------------------------------------------------------------------------- |
|
115 | #--------------------------------------------------------------------------- | |
121 |
|
116 | |||
122 |
def _append_ |
|
117 | def _append_png(self, png, before_prompt=False): | |
123 |
""" Append raw |
|
118 | """ Append raw PNG data to the widget. | |
124 | """ |
|
119 | """ | |
125 | try: |
|
120 | self._append_custom(self._insert_png, png, before_prompt) | |
126 | image = svg_to_image(svg) |
|
|||
127 | except ValueError: |
|
|||
128 | self._append_plain_text('Received invalid plot data.') |
|
|||
129 | else: |
|
|||
130 | format = self._add_image(image) |
|
|||
131 | self._name_to_svg_map[format.name()] = svg |
|
|||
132 | cursor = self._get_end_cursor() |
|
|||
133 | cursor.insertBlock() |
|
|||
134 | cursor.insertImage(format) |
|
|||
135 | cursor.insertBlock() |
|
|||
136 |
|
121 | |||
137 |
def _append_ |
|
122 | def _append_svg(self, svg, before_prompt=False): | |
138 |
""" Append raw |
|
123 | """ Append raw SVG data to the widget. | |
139 | """ |
|
124 | """ | |
140 | try: |
|
125 | self._append_custom(self._insert_svg, svg, before_prompt) | |
141 | image = QtGui.QImage() |
|
|||
142 | image.loadFromData(png, 'PNG') |
|
|||
143 | except ValueError: |
|
|||
144 | self._append_plain_text('Received invalid plot data.') |
|
|||
145 | else: |
|
|||
146 | format = self._add_image(image) |
|
|||
147 | cursor = self._get_end_cursor() |
|
|||
148 | cursor.insertBlock() |
|
|||
149 | cursor.insertImage(format) |
|
|||
150 | cursor.insertBlock() |
|
|||
151 |
|
126 | |||
152 | def _add_image(self, image): |
|
127 | def _add_image(self, image): | |
153 | """ Adds the specified QImage to the document and returns a |
|
128 | """ Adds the specified QImage to the document and returns a | |
@@ -236,6 +211,34 b' class RichIPythonWidget(IPythonWidget):' | |||||
236 | else: |
|
211 | else: | |
237 | return '<b>Unrecognized image format</b>' |
|
212 | return '<b>Unrecognized image format</b>' | |
238 |
|
213 | |||
|
214 | def _insert_png(self, cursor, png): | |||
|
215 | """ Insert raw PNG data into the widget. | |||
|
216 | """ | |||
|
217 | try: | |||
|
218 | image = QtGui.QImage() | |||
|
219 | image.loadFromData(png, 'PNG') | |||
|
220 | except ValueError: | |||
|
221 | self._insert_plain_text(cursor, 'Received invalid PNG data.') | |||
|
222 | else: | |||
|
223 | format = self._add_image(image) | |||
|
224 | cursor.insertBlock() | |||
|
225 | cursor.insertImage(format) | |||
|
226 | cursor.insertBlock() | |||
|
227 | ||||
|
228 | def _insert_svg(self, cursor, svg): | |||
|
229 | """ Insert raw SVG data into the widet. | |||
|
230 | """ | |||
|
231 | try: | |||
|
232 | image = svg_to_image(svg) | |||
|
233 | except ValueError: | |||
|
234 | self._insert_plain_text(cursor, 'Received invalid SVG data.') | |||
|
235 | else: | |||
|
236 | format = self._add_image(image) | |||
|
237 | self._name_to_svg_map[format.name()] = svg | |||
|
238 | cursor.insertBlock() | |||
|
239 | cursor.insertImage(format) | |||
|
240 | cursor.insertBlock() | |||
|
241 | ||||
239 | def _save_image(self, name, format='PNG'): |
|
242 | def _save_image(self, name, format='PNG'): | |
240 | """ Shows a save dialog for the ImageResource with 'name'. |
|
243 | """ Shows a save dialog for the ImageResource with 'name'. | |
241 | """ |
|
244 | """ |
General Comments 0
You need to be logged in to leave comments.
Login now