##// END OF EJS Templates
More PySide compatibility fixes.
Evan Patterson -
Show More
@@ -1,225 +1,225 b''
1 # Standard library imports
1 # Standard library imports
2 import re
2 import re
3 from textwrap import dedent
3 from textwrap import dedent
4 from unicodedata import category
4 from unicodedata import category
5
5
6 # System library imports
6 # System library imports
7 from IPython.external.qt import QtCore, QtGui
7 from IPython.external.qt import QtCore, QtGui
8
8
9
9
10 class CallTipWidget(QtGui.QLabel):
10 class CallTipWidget(QtGui.QLabel):
11 """ Shows call tips by parsing the current text of Q[Plain]TextEdit.
11 """ Shows call tips by parsing the current text of Q[Plain]TextEdit.
12 """
12 """
13
13
14 #--------------------------------------------------------------------------
14 #--------------------------------------------------------------------------
15 # 'QObject' interface
15 # 'QObject' interface
16 #--------------------------------------------------------------------------
16 #--------------------------------------------------------------------------
17
17
18 def __init__(self, text_edit):
18 def __init__(self, text_edit):
19 """ Create a call tip manager that is attached to the specified Qt
19 """ Create a call tip manager that is attached to the specified Qt
20 text edit widget.
20 text edit widget.
21 """
21 """
22 assert isinstance(text_edit, (QtGui.QTextEdit, QtGui.QPlainTextEdit))
22 assert isinstance(text_edit, (QtGui.QTextEdit, QtGui.QPlainTextEdit))
23 super(CallTipWidget, self).__init__(None, QtCore.Qt.ToolTip)
23 super(CallTipWidget, self).__init__(None, QtCore.Qt.ToolTip)
24
24
25 self._hide_timer = QtCore.QBasicTimer()
25 self._hide_timer = QtCore.QBasicTimer()
26 self._text_edit = text_edit
26 self._text_edit = text_edit
27
27
28 self.setFont(text_edit.document().defaultFont())
28 self.setFont(text_edit.document().defaultFont())
29 self.setForegroundRole(QtGui.QPalette.ToolTipText)
29 self.setForegroundRole(QtGui.QPalette.ToolTipText)
30 self.setBackgroundRole(QtGui.QPalette.ToolTipBase)
30 self.setBackgroundRole(QtGui.QPalette.ToolTipBase)
31 self.setPalette(QtGui.QToolTip.palette())
31 self.setPalette(QtGui.QToolTip.palette())
32
32
33 self.setAlignment(QtCore.Qt.AlignLeft)
33 self.setAlignment(QtCore.Qt.AlignLeft)
34 self.setIndent(1)
34 self.setIndent(1)
35 self.setFrameStyle(QtGui.QFrame.NoFrame)
35 self.setFrameStyle(QtGui.QFrame.NoFrame)
36 self.setMargin(1 + self.style().pixelMetric(
36 self.setMargin(1 + self.style().pixelMetric(
37 QtGui.QStyle.PM_ToolTipLabelFrameWidth, None, self))
37 QtGui.QStyle.PM_ToolTipLabelFrameWidth, None, self))
38 self.setWindowOpacity(self.style().styleHint(
38 self.setWindowOpacity(self.style().styleHint(
39 QtGui.QStyle.SH_ToolTipLabel_Opacity, None, self) / 255.0)
39 QtGui.QStyle.SH_ToolTipLabel_Opacity, None, self, None) / 255.0)
40
40
41 def eventFilter(self, obj, event):
41 def eventFilter(self, obj, event):
42 """ Reimplemented to hide on certain key presses and on text edit focus
42 """ Reimplemented to hide on certain key presses and on text edit focus
43 changes.
43 changes.
44 """
44 """
45 if obj == self._text_edit:
45 if obj == self._text_edit:
46 etype = event.type()
46 etype = event.type()
47
47
48 if etype == QtCore.QEvent.KeyPress:
48 if etype == QtCore.QEvent.KeyPress:
49 key = event.key()
49 key = event.key()
50 if key in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return):
50 if key in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return):
51 self.hide()
51 self.hide()
52 elif key == QtCore.Qt.Key_Escape:
52 elif key == QtCore.Qt.Key_Escape:
53 self.hide()
53 self.hide()
54 return True
54 return True
55
55
56 elif etype == QtCore.QEvent.FocusOut:
56 elif etype == QtCore.QEvent.FocusOut:
57 self.hide()
57 self.hide()
58
58
59 elif etype == QtCore.QEvent.Enter:
59 elif etype == QtCore.QEvent.Enter:
60 self._hide_timer.stop()
60 self._hide_timer.stop()
61
61
62 elif etype == QtCore.QEvent.Leave:
62 elif etype == QtCore.QEvent.Leave:
63 self._hide_later()
63 self._hide_later()
64
64
65 return super(CallTipWidget, self).eventFilter(obj, event)
65 return super(CallTipWidget, self).eventFilter(obj, event)
66
66
67 def timerEvent(self, event):
67 def timerEvent(self, event):
68 """ Reimplemented to hide the widget when the hide timer fires.
68 """ Reimplemented to hide the widget when the hide timer fires.
69 """
69 """
70 if event.timerId() == self._hide_timer.timerId():
70 if event.timerId() == self._hide_timer.timerId():
71 self._hide_timer.stop()
71 self._hide_timer.stop()
72 self.hide()
72 self.hide()
73
73
74 #--------------------------------------------------------------------------
74 #--------------------------------------------------------------------------
75 # 'QWidget' interface
75 # 'QWidget' interface
76 #--------------------------------------------------------------------------
76 #--------------------------------------------------------------------------
77
77
78 def enterEvent(self, event):
78 def enterEvent(self, event):
79 """ Reimplemented to cancel the hide timer.
79 """ Reimplemented to cancel the hide timer.
80 """
80 """
81 super(CallTipWidget, self).enterEvent(event)
81 super(CallTipWidget, self).enterEvent(event)
82 self._hide_timer.stop()
82 self._hide_timer.stop()
83
83
84 def hideEvent(self, event):
84 def hideEvent(self, event):
85 """ Reimplemented to disconnect signal handlers and event filter.
85 """ Reimplemented to disconnect signal handlers and event filter.
86 """
86 """
87 super(CallTipWidget, self).hideEvent(event)
87 super(CallTipWidget, self).hideEvent(event)
88 self._text_edit.cursorPositionChanged.disconnect(
88 self._text_edit.cursorPositionChanged.disconnect(
89 self._cursor_position_changed)
89 self._cursor_position_changed)
90 self._text_edit.removeEventFilter(self)
90 self._text_edit.removeEventFilter(self)
91
91
92 def leaveEvent(self, event):
92 def leaveEvent(self, event):
93 """ Reimplemented to start the hide timer.
93 """ Reimplemented to start the hide timer.
94 """
94 """
95 super(CallTipWidget, self).leaveEvent(event)
95 super(CallTipWidget, self).leaveEvent(event)
96 self._hide_later()
96 self._hide_later()
97
97
98 def paintEvent(self, event):
98 def paintEvent(self, event):
99 """ Reimplemented to paint the background panel.
99 """ Reimplemented to paint the background panel.
100 """
100 """
101 painter = QtGui.QStylePainter(self)
101 painter = QtGui.QStylePainter(self)
102 option = QtGui.QStyleOptionFrame()
102 option = QtGui.QStyleOptionFrame()
103 option.init(self)
103 option.init(self)
104 painter.drawPrimitive(QtGui.QStyle.PE_PanelTipLabel, option)
104 painter.drawPrimitive(QtGui.QStyle.PE_PanelTipLabel, option)
105 painter.end()
105 painter.end()
106
106
107 super(CallTipWidget, self).paintEvent(event)
107 super(CallTipWidget, self).paintEvent(event)
108
108
109 def setFont(self, font):
109 def setFont(self, font):
110 """ Reimplemented to allow use of this method as a slot.
110 """ Reimplemented to allow use of this method as a slot.
111 """
111 """
112 super(CallTipWidget, self).setFont(font)
112 super(CallTipWidget, self).setFont(font)
113
113
114 def showEvent(self, event):
114 def showEvent(self, event):
115 """ Reimplemented to connect signal handlers and event filter.
115 """ Reimplemented to connect signal handlers and event filter.
116 """
116 """
117 super(CallTipWidget, self).showEvent(event)
117 super(CallTipWidget, self).showEvent(event)
118 self._text_edit.cursorPositionChanged.connect(
118 self._text_edit.cursorPositionChanged.connect(
119 self._cursor_position_changed)
119 self._cursor_position_changed)
120 self._text_edit.installEventFilter(self)
120 self._text_edit.installEventFilter(self)
121
121
122 #--------------------------------------------------------------------------
122 #--------------------------------------------------------------------------
123 # 'CallTipWidget' interface
123 # 'CallTipWidget' interface
124 #--------------------------------------------------------------------------
124 #--------------------------------------------------------------------------
125
125
126 def show_call_info(self, call_line=None, doc=None, maxlines=20):
126 def show_call_info(self, call_line=None, doc=None, maxlines=20):
127 """ Attempts to show the specified call line and docstring at the
127 """ Attempts to show the specified call line and docstring at the
128 current cursor location. The docstring is possibly truncated for
128 current cursor location. The docstring is possibly truncated for
129 length.
129 length.
130 """
130 """
131 if doc:
131 if doc:
132 match = re.match("(?:[^\n]*\n){%i}" % maxlines, doc)
132 match = re.match("(?:[^\n]*\n){%i}" % maxlines, doc)
133 if match:
133 if match:
134 doc = doc[:match.end()] + '\n[Documentation continues...]'
134 doc = doc[:match.end()] + '\n[Documentation continues...]'
135 else:
135 else:
136 doc = ''
136 doc = ''
137
137
138 if call_line:
138 if call_line:
139 doc = '\n\n'.join([call_line, doc])
139 doc = '\n\n'.join([call_line, doc])
140 return self.show_tip(doc)
140 return self.show_tip(doc)
141
141
142 def show_tip(self, tip):
142 def show_tip(self, tip):
143 """ Attempts to show the specified tip at the current cursor location.
143 """ Attempts to show the specified tip at the current cursor location.
144 """
144 """
145 # Attempt to find the cursor position at which to show the call tip.
145 # Attempt to find the cursor position at which to show the call tip.
146 text_edit = self._text_edit
146 text_edit = self._text_edit
147 document = text_edit.document()
147 document = text_edit.document()
148 cursor = text_edit.textCursor()
148 cursor = text_edit.textCursor()
149 search_pos = cursor.position() - 1
149 search_pos = cursor.position() - 1
150 self._start_position, _ = self._find_parenthesis(search_pos,
150 self._start_position, _ = self._find_parenthesis(search_pos,
151 forward=False)
151 forward=False)
152 if self._start_position == -1:
152 if self._start_position == -1:
153 return False
153 return False
154
154
155 # Set the text and resize the widget accordingly.
155 # Set the text and resize the widget accordingly.
156 self.setText(tip)
156 self.setText(tip)
157 self.resize(self.sizeHint())
157 self.resize(self.sizeHint())
158
158
159 # Locate and show the widget. Place the tip below the current line
159 # Locate and show the widget. Place the tip below the current line
160 # unless it would be off the screen. In that case, place it above
160 # unless it would be off the screen. In that case, place it above
161 # the current line.
161 # the current line.
162 padding = 3 # Distance in pixels between cursor bounds and tip box.
162 padding = 3 # Distance in pixels between cursor bounds and tip box.
163 cursor_rect = text_edit.cursorRect(cursor)
163 cursor_rect = text_edit.cursorRect(cursor)
164 screen_rect = QtGui.qApp.desktop().screenGeometry(text_edit)
164 screen_rect = QtGui.qApp.desktop().screenGeometry(text_edit)
165 point = text_edit.mapToGlobal(cursor_rect.bottomRight())
165 point = text_edit.mapToGlobal(cursor_rect.bottomRight())
166 point.setY(point.y() + padding)
166 point.setY(point.y() + padding)
167 tip_height = self.size().height()
167 tip_height = self.size().height()
168 if point.y() + tip_height > screen_rect.height():
168 if point.y() + tip_height > screen_rect.height():
169 point = text_edit.mapToGlobal(cursor_rect.topRight())
169 point = text_edit.mapToGlobal(cursor_rect.topRight())
170 point.setY(point.y() - tip_height - padding)
170 point.setY(point.y() - tip_height - padding)
171 self.move(point)
171 self.move(point)
172 self.show()
172 self.show()
173 return True
173 return True
174
174
175 #--------------------------------------------------------------------------
175 #--------------------------------------------------------------------------
176 # Protected interface
176 # Protected interface
177 #--------------------------------------------------------------------------
177 #--------------------------------------------------------------------------
178
178
179 def _find_parenthesis(self, position, forward=True):
179 def _find_parenthesis(self, position, forward=True):
180 """ If 'forward' is True (resp. False), proceed forwards
180 """ If 'forward' is True (resp. False), proceed forwards
181 (resp. backwards) through the line that contains 'position' until an
181 (resp. backwards) through the line that contains 'position' until an
182 unmatched closing (resp. opening) parenthesis is found. Returns a
182 unmatched closing (resp. opening) parenthesis is found. Returns a
183 tuple containing the position of this parenthesis (or -1 if it is
183 tuple containing the position of this parenthesis (or -1 if it is
184 not found) and the number commas (at depth 0) found along the way.
184 not found) and the number commas (at depth 0) found along the way.
185 """
185 """
186 commas = depth = 0
186 commas = depth = 0
187 document = self._text_edit.document()
187 document = self._text_edit.document()
188 char = document.characterAt(position)
188 char = document.characterAt(position)
189 # Search until a match is found or a non-printable character is
189 # Search until a match is found or a non-printable character is
190 # encountered.
190 # encountered.
191 while category(char) != 'Cc' and position > 0:
191 while category(char) != 'Cc' and position > 0:
192 if char == ',' and depth == 0:
192 if char == ',' and depth == 0:
193 commas += 1
193 commas += 1
194 elif char == ')':
194 elif char == ')':
195 if forward and depth == 0:
195 if forward and depth == 0:
196 break
196 break
197 depth += 1
197 depth += 1
198 elif char == '(':
198 elif char == '(':
199 if not forward and depth == 0:
199 if not forward and depth == 0:
200 break
200 break
201 depth -= 1
201 depth -= 1
202 position += 1 if forward else -1
202 position += 1 if forward else -1
203 char = document.characterAt(position)
203 char = document.characterAt(position)
204 else:
204 else:
205 position = -1
205 position = -1
206 return position, commas
206 return position, commas
207
207
208 def _hide_later(self):
208 def _hide_later(self):
209 """ Hides the tooltip after some time has passed.
209 """ Hides the tooltip after some time has passed.
210 """
210 """
211 if not self._hide_timer.isActive():
211 if not self._hide_timer.isActive():
212 self._hide_timer.start(300, self)
212 self._hide_timer.start(300, self)
213
213
214 #------ Signal handlers ----------------------------------------------------
214 #------ Signal handlers ----------------------------------------------------
215
215
216 def _cursor_position_changed(self):
216 def _cursor_position_changed(self):
217 """ Updates the tip based on user cursor movement.
217 """ Updates the tip based on user cursor movement.
218 """
218 """
219 cursor = self._text_edit.textCursor()
219 cursor = self._text_edit.textCursor()
220 if cursor.position() <= self._start_position:
220 if cursor.position() <= self._start_position:
221 self.hide()
221 self.hide()
222 else:
222 else:
223 position, commas = self._find_parenthesis(self._start_position + 1)
223 position, commas = self._find_parenthesis(self._start_position + 1)
224 if position != -1:
224 if position != -1:
225 self.hide()
225 self.hide()
@@ -1,497 +1,493 b''
1 """ A FrontendWidget that emulates the interface of the console IPython and
1 """ A FrontendWidget that emulates the interface of the console IPython and
2 supports the additional functionality provided by the IPython kernel.
2 supports the additional functionality provided by the IPython kernel.
3
4 TODO: Add support for retrieving the system default editor. Requires code
5 paths for Windows (use the registry), Mac OS (use LaunchServices), and
6 Linux (use the xdg system).
7 """
3 """
8
4
9 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
10 # Imports
6 # Imports
11 #-----------------------------------------------------------------------------
7 #-----------------------------------------------------------------------------
12
8
13 # Standard library imports
9 # Standard library imports
14 from collections import namedtuple
10 from collections import namedtuple
15 import re
11 import re
16 from subprocess import Popen
12 from subprocess import Popen
17 from textwrap import dedent
13 from textwrap import dedent
18
14
19 # System library imports
15 # System library imports
20 from IPython.external.qt import QtCore, QtGui
16 from IPython.external.qt import QtCore, QtGui
21
17
22 # Local imports
18 # Local imports
23 from IPython.core.inputsplitter import IPythonInputSplitter, \
19 from IPython.core.inputsplitter import IPythonInputSplitter, \
24 transform_ipy_prompt
20 transform_ipy_prompt
25 from IPython.core.usage import default_gui_banner
21 from IPython.core.usage import default_gui_banner
26 from IPython.utils.traitlets import Bool, Str
22 from IPython.utils.traitlets import Bool, Str
27 from frontend_widget import FrontendWidget
23 from frontend_widget import FrontendWidget
28 from styles import (default_light_style_sheet, default_light_syntax_style,
24 from styles import (default_light_style_sheet, default_light_syntax_style,
29 default_dark_style_sheet, default_dark_syntax_style,
25 default_dark_style_sheet, default_dark_syntax_style,
30 default_bw_style_sheet, default_bw_syntax_style)
26 default_bw_style_sheet, default_bw_syntax_style)
31
27
32 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
33 # Constants
29 # Constants
34 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
35
31
36 # Default strings to build and display input and output prompts (and separators
32 # Default strings to build and display input and output prompts (and separators
37 # in between)
33 # in between)
38 default_in_prompt = 'In [<span class="in-prompt-number">%i</span>]: '
34 default_in_prompt = 'In [<span class="in-prompt-number">%i</span>]: '
39 default_out_prompt = 'Out[<span class="out-prompt-number">%i</span>]: '
35 default_out_prompt = 'Out[<span class="out-prompt-number">%i</span>]: '
40 default_input_sep = '\n'
36 default_input_sep = '\n'
41 default_output_sep = ''
37 default_output_sep = ''
42 default_output_sep2 = ''
38 default_output_sep2 = ''
43
39
44 # Base path for most payload sources.
40 # Base path for most payload sources.
45 zmq_shell_source = 'IPython.zmq.zmqshell.ZMQInteractiveShell'
41 zmq_shell_source = 'IPython.zmq.zmqshell.ZMQInteractiveShell'
46
42
47 #-----------------------------------------------------------------------------
43 #-----------------------------------------------------------------------------
48 # IPythonWidget class
44 # IPythonWidget class
49 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
50
46
51 class IPythonWidget(FrontendWidget):
47 class IPythonWidget(FrontendWidget):
52 """ A FrontendWidget for an IPython kernel.
48 """ A FrontendWidget for an IPython kernel.
53 """
49 """
54
50
55 # If set, the 'custom_edit_requested(str, int)' signal will be emitted when
51 # If set, the 'custom_edit_requested(str, int)' signal will be emitted when
56 # an editor is needed for a file. This overrides 'editor' and 'editor_line'
52 # an editor is needed for a file. This overrides 'editor' and 'editor_line'
57 # settings.
53 # settings.
58 custom_edit = Bool(False)
54 custom_edit = Bool(False)
59 custom_edit_requested = QtCore.Signal(object, object)
55 custom_edit_requested = QtCore.Signal(object, object)
60
56
61 # A command for invoking a system text editor. If the string contains a
57 # A command for invoking a system text editor. If the string contains a
62 # {filename} format specifier, it will be used. Otherwise, the filename will
58 # {filename} format specifier, it will be used. Otherwise, the filename will
63 # be appended to the end the command.
59 # be appended to the end the command.
64 editor = Str('default', config=True)
60 editor = Str('default', config=True)
65
61
66 # The editor command to use when a specific line number is requested. The
62 # The editor command to use when a specific line number is requested. The
67 # string should contain two format specifiers: {line} and {filename}. If
63 # string should contain two format specifiers: {line} and {filename}. If
68 # this parameter is not specified, the line number option to the %edit magic
64 # this parameter is not specified, the line number option to the %edit magic
69 # will be ignored.
65 # will be ignored.
70 editor_line = Str(config=True)
66 editor_line = Str(config=True)
71
67
72 # A CSS stylesheet. The stylesheet can contain classes for:
68 # A CSS stylesheet. The stylesheet can contain classes for:
73 # 1. Qt: QPlainTextEdit, QFrame, QWidget, etc
69 # 1. Qt: QPlainTextEdit, QFrame, QWidget, etc
74 # 2. Pygments: .c, .k, .o, etc (see PygmentsHighlighter)
70 # 2. Pygments: .c, .k, .o, etc (see PygmentsHighlighter)
75 # 3. IPython: .error, .in-prompt, .out-prompt, etc
71 # 3. IPython: .error, .in-prompt, .out-prompt, etc
76 style_sheet = Str(config=True)
72 style_sheet = Str(config=True)
77
73
78 # If not empty, use this Pygments style for syntax highlighting. Otherwise,
74 # If not empty, use this Pygments style for syntax highlighting. Otherwise,
79 # the style sheet is queried for Pygments style information.
75 # the style sheet is queried for Pygments style information.
80 syntax_style = Str(config=True)
76 syntax_style = Str(config=True)
81
77
82 # Prompts.
78 # Prompts.
83 in_prompt = Str(default_in_prompt, config=True)
79 in_prompt = Str(default_in_prompt, config=True)
84 out_prompt = Str(default_out_prompt, config=True)
80 out_prompt = Str(default_out_prompt, config=True)
85 input_sep = Str(default_input_sep, config=True)
81 input_sep = Str(default_input_sep, config=True)
86 output_sep = Str(default_output_sep, config=True)
82 output_sep = Str(default_output_sep, config=True)
87 output_sep2 = Str(default_output_sep2, config=True)
83 output_sep2 = Str(default_output_sep2, config=True)
88
84
89 # FrontendWidget protected class variables.
85 # FrontendWidget protected class variables.
90 _input_splitter_class = IPythonInputSplitter
86 _input_splitter_class = IPythonInputSplitter
91
87
92 # IPythonWidget protected class variables.
88 # IPythonWidget protected class variables.
93 _PromptBlock = namedtuple('_PromptBlock', ['block', 'length', 'number'])
89 _PromptBlock = namedtuple('_PromptBlock', ['block', 'length', 'number'])
94 _payload_source_edit = zmq_shell_source + '.edit_magic'
90 _payload_source_edit = zmq_shell_source + '.edit_magic'
95 _payload_source_exit = zmq_shell_source + '.ask_exit'
91 _payload_source_exit = zmq_shell_source + '.ask_exit'
96 _payload_source_loadpy = zmq_shell_source + '.magic_loadpy'
92 _payload_source_loadpy = zmq_shell_source + '.magic_loadpy'
97 _payload_source_page = 'IPython.zmq.page.page'
93 _payload_source_page = 'IPython.zmq.page.page'
98
94
99 #---------------------------------------------------------------------------
95 #---------------------------------------------------------------------------
100 # 'object' interface
96 # 'object' interface
101 #---------------------------------------------------------------------------
97 #---------------------------------------------------------------------------
102
98
103 def __init__(self, *args, **kw):
99 def __init__(self, *args, **kw):
104 super(IPythonWidget, self).__init__(*args, **kw)
100 super(IPythonWidget, self).__init__(*args, **kw)
105
101
106 # IPythonWidget protected variables.
102 # IPythonWidget protected variables.
107 self._code_to_load = None
103 self._code_to_load = None
108 self._payload_handlers = {
104 self._payload_handlers = {
109 self._payload_source_edit : self._handle_payload_edit,
105 self._payload_source_edit : self._handle_payload_edit,
110 self._payload_source_exit : self._handle_payload_exit,
106 self._payload_source_exit : self._handle_payload_exit,
111 self._payload_source_page : self._handle_payload_page,
107 self._payload_source_page : self._handle_payload_page,
112 self._payload_source_loadpy : self._handle_payload_loadpy }
108 self._payload_source_loadpy : self._handle_payload_loadpy }
113 self._previous_prompt_obj = None
109 self._previous_prompt_obj = None
114 self._keep_kernel_on_exit = None
110 self._keep_kernel_on_exit = None
115
111
116 # Initialize widget styling.
112 # Initialize widget styling.
117 if self.style_sheet:
113 if self.style_sheet:
118 self._style_sheet_changed()
114 self._style_sheet_changed()
119 self._syntax_style_changed()
115 self._syntax_style_changed()
120 else:
116 else:
121 self.set_default_style()
117 self.set_default_style()
122
118
123 #---------------------------------------------------------------------------
119 #---------------------------------------------------------------------------
124 # 'BaseFrontendMixin' abstract interface
120 # 'BaseFrontendMixin' abstract interface
125 #---------------------------------------------------------------------------
121 #---------------------------------------------------------------------------
126
122
127 def _handle_complete_reply(self, rep):
123 def _handle_complete_reply(self, rep):
128 """ Reimplemented to support IPython's improved completion machinery.
124 """ Reimplemented to support IPython's improved completion machinery.
129 """
125 """
130 cursor = self._get_cursor()
126 cursor = self._get_cursor()
131 info = self._request_info.get('complete')
127 info = self._request_info.get('complete')
132 if info and info.id == rep['parent_header']['msg_id'] and \
128 if info and info.id == rep['parent_header']['msg_id'] and \
133 info.pos == cursor.position():
129 info.pos == cursor.position():
134 matches = rep['content']['matches']
130 matches = rep['content']['matches']
135 text = rep['content']['matched_text']
131 text = rep['content']['matched_text']
136 offset = len(text)
132 offset = len(text)
137
133
138 # Clean up matches with period and path separators if the matched
134 # Clean up matches with period and path separators if the matched
139 # text has not been transformed. This is done by truncating all
135 # text has not been transformed. This is done by truncating all
140 # but the last component and then suitably decreasing the offset
136 # but the last component and then suitably decreasing the offset
141 # between the current cursor position and the start of completion.
137 # between the current cursor position and the start of completion.
142 if len(matches) > 1 and matches[0][:offset] == text:
138 if len(matches) > 1 and matches[0][:offset] == text:
143 parts = re.split(r'[./\\]', text)
139 parts = re.split(r'[./\\]', text)
144 sep_count = len(parts) - 1
140 sep_count = len(parts) - 1
145 if sep_count:
141 if sep_count:
146 chop_length = sum(map(len, parts[:sep_count])) + sep_count
142 chop_length = sum(map(len, parts[:sep_count])) + sep_count
147 matches = [ match[chop_length:] for match in matches ]
143 matches = [ match[chop_length:] for match in matches ]
148 offset -= chop_length
144 offset -= chop_length
149
145
150 # Move the cursor to the start of the match and complete.
146 # Move the cursor to the start of the match and complete.
151 cursor.movePosition(QtGui.QTextCursor.Left, n=offset)
147 cursor.movePosition(QtGui.QTextCursor.Left, n=offset)
152 self._complete_with_items(cursor, matches)
148 self._complete_with_items(cursor, matches)
153
149
154 def _handle_execute_reply(self, msg):
150 def _handle_execute_reply(self, msg):
155 """ Reimplemented to support prompt requests.
151 """ Reimplemented to support prompt requests.
156 """
152 """
157 info = self._request_info.get('execute')
153 info = self._request_info.get('execute')
158 if info and info.id == msg['parent_header']['msg_id']:
154 if info and info.id == msg['parent_header']['msg_id']:
159 if info.kind == 'prompt':
155 if info.kind == 'prompt':
160 number = msg['content']['execution_count'] + 1
156 number = msg['content']['execution_count'] + 1
161 self._show_interpreter_prompt(number)
157 self._show_interpreter_prompt(number)
162 else:
158 else:
163 super(IPythonWidget, self)._handle_execute_reply(msg)
159 super(IPythonWidget, self)._handle_execute_reply(msg)
164
160
165 def _handle_history_reply(self, msg):
161 def _handle_history_reply(self, msg):
166 """ Implemented to handle history replies, which are only supported by
162 """ Implemented to handle history replies, which are only supported by
167 the IPython kernel.
163 the IPython kernel.
168 """
164 """
169 history_dict = msg['content']['history']
165 history_dict = msg['content']['history']
170 input_history_dict = {}
166 input_history_dict = {}
171 for key,val in history_dict.items():
167 for key,val in history_dict.items():
172 input_history_dict[int(key)] = val
168 input_history_dict[int(key)] = val
173 items = [ val.rstrip() for _, val in sorted(input_history_dict.items()) ]
169 items = [ val.rstrip() for _, val in sorted(input_history_dict.items()) ]
174 self._set_history(items)
170 self._set_history(items)
175
171
176 def _handle_pyout(self, msg):
172 def _handle_pyout(self, msg):
177 """ Reimplemented for IPython-style "display hook".
173 """ Reimplemented for IPython-style "display hook".
178 """
174 """
179 if not self._hidden and self._is_from_this_session(msg):
175 if not self._hidden and self._is_from_this_session(msg):
180 content = msg['content']
176 content = msg['content']
181 prompt_number = content['execution_count']
177 prompt_number = content['execution_count']
182 data = content['data']
178 data = content['data']
183 if data.has_key('text/html'):
179 if data.has_key('text/html'):
184 self._append_plain_text(self.output_sep)
180 self._append_plain_text(self.output_sep)
185 self._append_html(self._make_out_prompt(prompt_number))
181 self._append_html(self._make_out_prompt(prompt_number))
186 html = data['text/html']
182 html = data['text/html']
187 self._append_plain_text('\n')
183 self._append_plain_text('\n')
188 self._append_html(html + self.output_sep2)
184 self._append_html(html + self.output_sep2)
189 elif data.has_key('text/plain'):
185 elif data.has_key('text/plain'):
190 self._append_plain_text(self.output_sep)
186 self._append_plain_text(self.output_sep)
191 self._append_html(self._make_out_prompt(prompt_number))
187 self._append_html(self._make_out_prompt(prompt_number))
192 text = data['text/plain']
188 text = data['text/plain']
193 self._append_plain_text(text + self.output_sep2)
189 self._append_plain_text(text + self.output_sep2)
194
190
195 def _handle_display_data(self, msg):
191 def _handle_display_data(self, msg):
196 """ The base handler for the ``display_data`` message.
192 """ The base handler for the ``display_data`` message.
197 """
193 """
198 # For now, we don't display data from other frontends, but we
194 # For now, we don't display data from other frontends, but we
199 # eventually will as this allows all frontends to monitor the display
195 # eventually will as this allows all frontends to monitor the display
200 # data. But we need to figure out how to handle this in the GUI.
196 # data. But we need to figure out how to handle this in the GUI.
201 if not self._hidden and self._is_from_this_session(msg):
197 if not self._hidden and self._is_from_this_session(msg):
202 source = msg['content']['source']
198 source = msg['content']['source']
203 data = msg['content']['data']
199 data = msg['content']['data']
204 metadata = msg['content']['metadata']
200 metadata = msg['content']['metadata']
205 # In the regular IPythonWidget, we simply print the plain text
201 # In the regular IPythonWidget, we simply print the plain text
206 # representation.
202 # representation.
207 if data.has_key('text/html'):
203 if data.has_key('text/html'):
208 html = data['text/html']
204 html = data['text/html']
209 self._append_html(html)
205 self._append_html(html)
210 elif data.has_key('text/plain'):
206 elif data.has_key('text/plain'):
211 text = data['text/plain']
207 text = data['text/plain']
212 self._append_plain_text(text)
208 self._append_plain_text(text)
213 # This newline seems to be needed for text and html output.
209 # This newline seems to be needed for text and html output.
214 self._append_plain_text(u'\n')
210 self._append_plain_text(u'\n')
215
211
216 def _started_channels(self):
212 def _started_channels(self):
217 """ Reimplemented to make a history request.
213 """ Reimplemented to make a history request.
218 """
214 """
219 super(IPythonWidget, self)._started_channels()
215 super(IPythonWidget, self)._started_channels()
220 self.kernel_manager.xreq_channel.history(raw=True, output=False)
216 self.kernel_manager.xreq_channel.history(raw=True, output=False)
221
217
222 #---------------------------------------------------------------------------
218 #---------------------------------------------------------------------------
223 # 'ConsoleWidget' public interface
219 # 'ConsoleWidget' public interface
224 #---------------------------------------------------------------------------
220 #---------------------------------------------------------------------------
225
221
226 def copy(self):
222 def copy(self):
227 """ Copy the currently selected text to the clipboard, removing prompts
223 """ Copy the currently selected text to the clipboard, removing prompts
228 if possible.
224 if possible.
229 """
225 """
230 text = self._control.textCursor().selection().toPlainText()
226 text = self._control.textCursor().selection().toPlainText()
231 if text:
227 if text:
232 lines = map(transform_ipy_prompt, text.splitlines())
228 lines = map(transform_ipy_prompt, text.splitlines())
233 text = '\n'.join(lines)
229 text = '\n'.join(lines)
234 QtGui.QApplication.clipboard().setText(text)
230 QtGui.QApplication.clipboard().setText(text)
235
231
236 #---------------------------------------------------------------------------
232 #---------------------------------------------------------------------------
237 # 'FrontendWidget' public interface
233 # 'FrontendWidget' public interface
238 #---------------------------------------------------------------------------
234 #---------------------------------------------------------------------------
239
235
240 def execute_file(self, path, hidden=False):
236 def execute_file(self, path, hidden=False):
241 """ Reimplemented to use the 'run' magic.
237 """ Reimplemented to use the 'run' magic.
242 """
238 """
243 self.execute('%%run %s' % path, hidden=hidden)
239 self.execute('%%run %s' % path, hidden=hidden)
244
240
245 #---------------------------------------------------------------------------
241 #---------------------------------------------------------------------------
246 # 'FrontendWidget' protected interface
242 # 'FrontendWidget' protected interface
247 #---------------------------------------------------------------------------
243 #---------------------------------------------------------------------------
248
244
249 def _complete(self):
245 def _complete(self):
250 """ Reimplemented to support IPython's improved completion machinery.
246 """ Reimplemented to support IPython's improved completion machinery.
251 """
247 """
252 # We let the kernel split the input line, so we *always* send an empty
248 # We let the kernel split the input line, so we *always* send an empty
253 # text field. Readline-based frontends do get a real text field which
249 # text field. Readline-based frontends do get a real text field which
254 # they can use.
250 # they can use.
255 text = ''
251 text = ''
256
252
257 # Send the completion request to the kernel
253 # Send the completion request to the kernel
258 msg_id = self.kernel_manager.xreq_channel.complete(
254 msg_id = self.kernel_manager.xreq_channel.complete(
259 text, # text
255 text, # text
260 self._get_input_buffer_cursor_line(), # line
256 self._get_input_buffer_cursor_line(), # line
261 self._get_input_buffer_cursor_column(), # cursor_pos
257 self._get_input_buffer_cursor_column(), # cursor_pos
262 self.input_buffer) # block
258 self.input_buffer) # block
263 pos = self._get_cursor().position()
259 pos = self._get_cursor().position()
264 info = self._CompletionRequest(msg_id, pos)
260 info = self._CompletionRequest(msg_id, pos)
265 self._request_info['complete'] = info
261 self._request_info['complete'] = info
266
262
267 def _get_banner(self):
263 def _get_banner(self):
268 """ Reimplemented to return IPython's default banner.
264 """ Reimplemented to return IPython's default banner.
269 """
265 """
270 return default_gui_banner
266 return default_gui_banner
271
267
272 def _process_execute_error(self, msg):
268 def _process_execute_error(self, msg):
273 """ Reimplemented for IPython-style traceback formatting.
269 """ Reimplemented for IPython-style traceback formatting.
274 """
270 """
275 content = msg['content']
271 content = msg['content']
276 traceback = '\n'.join(content['traceback']) + '\n'
272 traceback = '\n'.join(content['traceback']) + '\n'
277 if False:
273 if False:
278 # FIXME: For now, tracebacks come as plain text, so we can't use
274 # FIXME: For now, tracebacks come as plain text, so we can't use
279 # the html renderer yet. Once we refactor ultratb to produce
275 # the html renderer yet. Once we refactor ultratb to produce
280 # properly styled tracebacks, this branch should be the default
276 # properly styled tracebacks, this branch should be the default
281 traceback = traceback.replace(' ', '&nbsp;')
277 traceback = traceback.replace(' ', '&nbsp;')
282 traceback = traceback.replace('\n', '<br/>')
278 traceback = traceback.replace('\n', '<br/>')
283
279
284 ename = content['ename']
280 ename = content['ename']
285 ename_styled = '<span class="error">%s</span>' % ename
281 ename_styled = '<span class="error">%s</span>' % ename
286 traceback = traceback.replace(ename, ename_styled)
282 traceback = traceback.replace(ename, ename_styled)
287
283
288 self._append_html(traceback)
284 self._append_html(traceback)
289 else:
285 else:
290 # This is the fallback for now, using plain text with ansi escapes
286 # This is the fallback for now, using plain text with ansi escapes
291 self._append_plain_text(traceback)
287 self._append_plain_text(traceback)
292
288
293 def _process_execute_payload(self, item):
289 def _process_execute_payload(self, item):
294 """ Reimplemented to dispatch payloads to handler methods.
290 """ Reimplemented to dispatch payloads to handler methods.
295 """
291 """
296 handler = self._payload_handlers.get(item['source'])
292 handler = self._payload_handlers.get(item['source'])
297 if handler is None:
293 if handler is None:
298 # We have no handler for this type of payload, simply ignore it
294 # We have no handler for this type of payload, simply ignore it
299 return False
295 return False
300 else:
296 else:
301 handler(item)
297 handler(item)
302 return True
298 return True
303
299
304 def _show_interpreter_prompt(self, number=None):
300 def _show_interpreter_prompt(self, number=None):
305 """ Reimplemented for IPython-style prompts.
301 """ Reimplemented for IPython-style prompts.
306 """
302 """
307 # If a number was not specified, make a prompt number request.
303 # If a number was not specified, make a prompt number request.
308 if number is None:
304 if number is None:
309 msg_id = self.kernel_manager.xreq_channel.execute('', silent=True)
305 msg_id = self.kernel_manager.xreq_channel.execute('', silent=True)
310 info = self._ExecutionRequest(msg_id, 'prompt')
306 info = self._ExecutionRequest(msg_id, 'prompt')
311 self._request_info['execute'] = info
307 self._request_info['execute'] = info
312 return
308 return
313
309
314 # Show a new prompt and save information about it so that it can be
310 # Show a new prompt and save information about it so that it can be
315 # updated later if the prompt number turns out to be wrong.
311 # updated later if the prompt number turns out to be wrong.
316 self._prompt_sep = self.input_sep
312 self._prompt_sep = self.input_sep
317 self._show_prompt(self._make_in_prompt(number), html=True)
313 self._show_prompt(self._make_in_prompt(number), html=True)
318 block = self._control.document().lastBlock()
314 block = self._control.document().lastBlock()
319 length = len(self._prompt)
315 length = len(self._prompt)
320 self._previous_prompt_obj = self._PromptBlock(block, length, number)
316 self._previous_prompt_obj = self._PromptBlock(block, length, number)
321
317
322 # Update continuation prompt to reflect (possibly) new prompt length.
318 # Update continuation prompt to reflect (possibly) new prompt length.
323 self._set_continuation_prompt(
319 self._set_continuation_prompt(
324 self._make_continuation_prompt(self._prompt), html=True)
320 self._make_continuation_prompt(self._prompt), html=True)
325
321
326 # Load code from the %loadpy magic, if necessary.
322 # Load code from the %loadpy magic, if necessary.
327 if self._code_to_load is not None:
323 if self._code_to_load is not None:
328 self.input_buffer = dedent(self._code_to_load.rstrip())
324 self.input_buffer = dedent(self._code_to_load.rstrip())
329 self._code_to_load = None
325 self._code_to_load = None
330
326
331 def _show_interpreter_prompt_for_reply(self, msg):
327 def _show_interpreter_prompt_for_reply(self, msg):
332 """ Reimplemented for IPython-style prompts.
328 """ Reimplemented for IPython-style prompts.
333 """
329 """
334 # Update the old prompt number if necessary.
330 # Update the old prompt number if necessary.
335 content = msg['content']
331 content = msg['content']
336 previous_prompt_number = content['execution_count']
332 previous_prompt_number = content['execution_count']
337 if self._previous_prompt_obj and \
333 if self._previous_prompt_obj and \
338 self._previous_prompt_obj.number != previous_prompt_number:
334 self._previous_prompt_obj.number != previous_prompt_number:
339 block = self._previous_prompt_obj.block
335 block = self._previous_prompt_obj.block
340
336
341 # Make sure the prompt block has not been erased.
337 # Make sure the prompt block has not been erased.
342 if block.isValid() and not block.text().isEmpty():
338 if block.isValid() and not block.text().isEmpty():
343
339
344 # Remove the old prompt and insert a new prompt.
340 # Remove the old prompt and insert a new prompt.
345 cursor = QtGui.QTextCursor(block)
341 cursor = QtGui.QTextCursor(block)
346 cursor.movePosition(QtGui.QTextCursor.Right,
342 cursor.movePosition(QtGui.QTextCursor.Right,
347 QtGui.QTextCursor.KeepAnchor,
343 QtGui.QTextCursor.KeepAnchor,
348 self._previous_prompt_obj.length)
344 self._previous_prompt_obj.length)
349 prompt = self._make_in_prompt(previous_prompt_number)
345 prompt = self._make_in_prompt(previous_prompt_number)
350 self._prompt = self._insert_html_fetching_plain_text(
346 self._prompt = self._insert_html_fetching_plain_text(
351 cursor, prompt)
347 cursor, prompt)
352
348
353 # When the HTML is inserted, Qt blows away the syntax
349 # When the HTML is inserted, Qt blows away the syntax
354 # highlighting for the line, so we need to rehighlight it.
350 # highlighting for the line, so we need to rehighlight it.
355 self._highlighter.rehighlightBlock(cursor.block())
351 self._highlighter.rehighlightBlock(cursor.block())
356
352
357 self._previous_prompt_obj = None
353 self._previous_prompt_obj = None
358
354
359 # Show a new prompt with the kernel's estimated prompt number.
355 # Show a new prompt with the kernel's estimated prompt number.
360 self._show_interpreter_prompt(previous_prompt_number + 1)
356 self._show_interpreter_prompt(previous_prompt_number + 1)
361
357
362 #---------------------------------------------------------------------------
358 #---------------------------------------------------------------------------
363 # 'IPythonWidget' interface
359 # 'IPythonWidget' interface
364 #---------------------------------------------------------------------------
360 #---------------------------------------------------------------------------
365
361
366 def set_default_style(self, colors='lightbg'):
362 def set_default_style(self, colors='lightbg'):
367 """ Sets the widget style to the class defaults.
363 """ Sets the widget style to the class defaults.
368
364
369 Parameters:
365 Parameters:
370 -----------
366 -----------
371 colors : str, optional (default lightbg)
367 colors : str, optional (default lightbg)
372 Whether to use the default IPython light background or dark
368 Whether to use the default IPython light background or dark
373 background or B&W style.
369 background or B&W style.
374 """
370 """
375 colors = colors.lower()
371 colors = colors.lower()
376 if colors=='lightbg':
372 if colors=='lightbg':
377 self.style_sheet = default_light_style_sheet
373 self.style_sheet = default_light_style_sheet
378 self.syntax_style = default_light_syntax_style
374 self.syntax_style = default_light_syntax_style
379 elif colors=='linux':
375 elif colors=='linux':
380 self.style_sheet = default_dark_style_sheet
376 self.style_sheet = default_dark_style_sheet
381 self.syntax_style = default_dark_syntax_style
377 self.syntax_style = default_dark_syntax_style
382 elif colors=='nocolor':
378 elif colors=='nocolor':
383 self.style_sheet = default_bw_style_sheet
379 self.style_sheet = default_bw_style_sheet
384 self.syntax_style = default_bw_syntax_style
380 self.syntax_style = default_bw_syntax_style
385 else:
381 else:
386 raise KeyError("No such color scheme: %s"%colors)
382 raise KeyError("No such color scheme: %s"%colors)
387
383
388 #---------------------------------------------------------------------------
384 #---------------------------------------------------------------------------
389 # 'IPythonWidget' protected interface
385 # 'IPythonWidget' protected interface
390 #---------------------------------------------------------------------------
386 #---------------------------------------------------------------------------
391
387
392 def _edit(self, filename, line=None):
388 def _edit(self, filename, line=None):
393 """ Opens a Python script for editing.
389 """ Opens a Python script for editing.
394
390
395 Parameters:
391 Parameters:
396 -----------
392 -----------
397 filename : str
393 filename : str
398 A path to a local system file.
394 A path to a local system file.
399
395
400 line : int, optional
396 line : int, optional
401 A line of interest in the file.
397 A line of interest in the file.
402 """
398 """
403 if self.custom_edit:
399 if self.custom_edit:
404 self.custom_edit_requested.emit(filename, line)
400 self.custom_edit_requested.emit(filename, line)
405 elif self.editor == 'default':
401 elif self.editor == 'default':
406 self._append_plain_text('No default editor available.\n')
402 self._append_plain_text('No default editor available.\n')
407 else:
403 else:
408 try:
404 try:
409 filename = '"%s"' % filename
405 filename = '"%s"' % filename
410 if line and self.editor_line:
406 if line and self.editor_line:
411 command = self.editor_line.format(filename=filename,
407 command = self.editor_line.format(filename=filename,
412 line=line)
408 line=line)
413 else:
409 else:
414 try:
410 try:
415 command = self.editor.format()
411 command = self.editor.format()
416 except KeyError:
412 except KeyError:
417 command = self.editor.format(filename=filename)
413 command = self.editor.format(filename=filename)
418 else:
414 else:
419 command += ' ' + filename
415 command += ' ' + filename
420 except KeyError:
416 except KeyError:
421 self._append_plain_text('Invalid editor command.\n')
417 self._append_plain_text('Invalid editor command.\n')
422 else:
418 else:
423 try:
419 try:
424 Popen(command, shell=True)
420 Popen(command, shell=True)
425 except OSError:
421 except OSError:
426 msg = 'Opening editor with command "%s" failed.\n'
422 msg = 'Opening editor with command "%s" failed.\n'
427 self._append_plain_text(msg % command)
423 self._append_plain_text(msg % command)
428
424
429 def _make_in_prompt(self, number):
425 def _make_in_prompt(self, number):
430 """ Given a prompt number, returns an HTML In prompt.
426 """ Given a prompt number, returns an HTML In prompt.
431 """
427 """
432 body = self.in_prompt % number
428 body = self.in_prompt % number
433 return '<span class="in-prompt">%s</span>' % body
429 return '<span class="in-prompt">%s</span>' % body
434
430
435 def _make_continuation_prompt(self, prompt):
431 def _make_continuation_prompt(self, prompt):
436 """ Given a plain text version of an In prompt, returns an HTML
432 """ Given a plain text version of an In prompt, returns an HTML
437 continuation prompt.
433 continuation prompt.
438 """
434 """
439 end_chars = '...: '
435 end_chars = '...: '
440 space_count = len(prompt.lstrip('\n')) - len(end_chars)
436 space_count = len(prompt.lstrip('\n')) - len(end_chars)
441 body = '&nbsp;' * space_count + end_chars
437 body = '&nbsp;' * space_count + end_chars
442 return '<span class="in-prompt">%s</span>' % body
438 return '<span class="in-prompt">%s</span>' % body
443
439
444 def _make_out_prompt(self, number):
440 def _make_out_prompt(self, number):
445 """ Given a prompt number, returns an HTML Out prompt.
441 """ Given a prompt number, returns an HTML Out prompt.
446 """
442 """
447 body = self.out_prompt % number
443 body = self.out_prompt % number
448 return '<span class="out-prompt">%s</span>' % body
444 return '<span class="out-prompt">%s</span>' % body
449
445
450 #------ Payload handlers --------------------------------------------------
446 #------ Payload handlers --------------------------------------------------
451
447
452 # Payload handlers with a generic interface: each takes the opaque payload
448 # Payload handlers with a generic interface: each takes the opaque payload
453 # dict, unpacks it and calls the underlying functions with the necessary
449 # dict, unpacks it and calls the underlying functions with the necessary
454 # arguments.
450 # arguments.
455
451
456 def _handle_payload_edit(self, item):
452 def _handle_payload_edit(self, item):
457 self._edit(item['filename'], item['line_number'])
453 self._edit(item['filename'], item['line_number'])
458
454
459 def _handle_payload_exit(self, item):
455 def _handle_payload_exit(self, item):
460 self._keep_kernel_on_exit = item['keepkernel']
456 self._keep_kernel_on_exit = item['keepkernel']
461 self.exit_requested.emit()
457 self.exit_requested.emit()
462
458
463 def _handle_payload_loadpy(self, item):
459 def _handle_payload_loadpy(self, item):
464 # Simple save the text of the .py file for later. The text is written
460 # Simple save the text of the .py file for later. The text is written
465 # to the buffer when _prompt_started_hook is called.
461 # to the buffer when _prompt_started_hook is called.
466 self._code_to_load = item['text']
462 self._code_to_load = item['text']
467
463
468 def _handle_payload_page(self, item):
464 def _handle_payload_page(self, item):
469 # Since the plain text widget supports only a very small subset of HTML
465 # Since the plain text widget supports only a very small subset of HTML
470 # and we have no control over the HTML source, we only page HTML
466 # and we have no control over the HTML source, we only page HTML
471 # payloads in the rich text widget.
467 # payloads in the rich text widget.
472 if item['html'] and self.kind == 'rich':
468 if item['html'] and self.kind == 'rich':
473 self._page(item['html'], html=True)
469 self._page(item['html'], html=True)
474 else:
470 else:
475 self._page(item['text'], html=False)
471 self._page(item['text'], html=False)
476
472
477 #------ Trait change handlers --------------------------------------------
473 #------ Trait change handlers --------------------------------------------
478
474
479 def _style_sheet_changed(self):
475 def _style_sheet_changed(self):
480 """ Set the style sheets of the underlying widgets.
476 """ Set the style sheets of the underlying widgets.
481 """
477 """
482 self.setStyleSheet(self.style_sheet)
478 self.setStyleSheet(self.style_sheet)
483 self._control.document().setDefaultStyleSheet(self.style_sheet)
479 self._control.document().setDefaultStyleSheet(self.style_sheet)
484 if self._page_control:
480 if self._page_control:
485 self._page_control.document().setDefaultStyleSheet(self.style_sheet)
481 self._page_control.document().setDefaultStyleSheet(self.style_sheet)
486
482
487 bg_color = self._control.palette().background().color()
483 bg_color = self._control.palette().window().color()
488 self._ansi_processor.set_background_color(bg_color)
484 self._ansi_processor.set_background_color(bg_color)
489
485
490 def _syntax_style_changed(self):
486 def _syntax_style_changed(self):
491 """ Set the style for the syntax highlighter.
487 """ Set the style for the syntax highlighter.
492 """
488 """
493 if self.syntax_style:
489 if self.syntax_style:
494 self._highlighter.set_style(self.syntax_style)
490 self._highlighter.set_style(self.syntax_style)
495 else:
491 else:
496 self._highlighter.set_style_sheet(self.style_sheet)
492 self._highlighter.set_style_sheet(self.style_sheet)
497
493
General Comments 0
You need to be logged in to leave comments. Login now