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