##// END OF EJS Templates
tweaked close dialog and added prompts to prevent silent remote close
MinRK -
Show More
@@ -1,568 +1,590 b''
1 from __future__ import print_function
1 from __future__ import print_function
2
2
3 # Standard library imports
3 # Standard library imports
4 from collections import namedtuple
4 from collections import namedtuple
5 import sys
5 import sys
6 import time
6 import time
7
7
8 # System library imports
8 # System library imports
9 from pygments.lexers import PythonLexer
9 from pygments.lexers import PythonLexer
10 from PyQt4 import QtCore, QtGui
10 from PyQt4 import QtCore, QtGui
11
11
12 # Local imports
12 # Local imports
13 from IPython.core.inputsplitter import InputSplitter, transform_classic_prompt
13 from IPython.core.inputsplitter import InputSplitter, transform_classic_prompt
14 from IPython.core.oinspect import call_tip
14 from IPython.core.oinspect import call_tip
15 from IPython.frontend.qt.base_frontend_mixin import BaseFrontendMixin
15 from IPython.frontend.qt.base_frontend_mixin import BaseFrontendMixin
16 from IPython.utils.traitlets import Bool
16 from IPython.utils.traitlets import Bool
17 from bracket_matcher import BracketMatcher
17 from bracket_matcher import BracketMatcher
18 from call_tip_widget import CallTipWidget
18 from call_tip_widget import CallTipWidget
19 from completion_lexer import CompletionLexer
19 from completion_lexer import CompletionLexer
20 from history_console_widget import HistoryConsoleWidget
20 from history_console_widget import HistoryConsoleWidget
21 from pygments_highlighter import PygmentsHighlighter
21 from pygments_highlighter import PygmentsHighlighter
22
22
23
23
24 class FrontendHighlighter(PygmentsHighlighter):
24 class FrontendHighlighter(PygmentsHighlighter):
25 """ A PygmentsHighlighter that can be turned on and off and that ignores
25 """ A PygmentsHighlighter that can be turned on and off and that ignores
26 prompts.
26 prompts.
27 """
27 """
28
28
29 def __init__(self, frontend):
29 def __init__(self, frontend):
30 super(FrontendHighlighter, self).__init__(frontend._control.document())
30 super(FrontendHighlighter, self).__init__(frontend._control.document())
31 self._current_offset = 0
31 self._current_offset = 0
32 self._frontend = frontend
32 self._frontend = frontend
33 self.highlighting_on = False
33 self.highlighting_on = False
34
34
35 def highlightBlock(self, qstring):
35 def highlightBlock(self, qstring):
36 """ Highlight a block of text. Reimplemented to highlight selectively.
36 """ Highlight a block of text. Reimplemented to highlight selectively.
37 """
37 """
38 if not self.highlighting_on:
38 if not self.highlighting_on:
39 return
39 return
40
40
41 # The input to this function is unicode string that may contain
41 # The input to this function is unicode string that may contain
42 # paragraph break characters, non-breaking spaces, etc. Here we acquire
42 # paragraph break characters, non-breaking spaces, etc. Here we acquire
43 # the string as plain text so we can compare it.
43 # the string as plain text so we can compare it.
44 current_block = self.currentBlock()
44 current_block = self.currentBlock()
45 string = self._frontend._get_block_plain_text(current_block)
45 string = self._frontend._get_block_plain_text(current_block)
46
46
47 # Decide whether to check for the regular or continuation prompt.
47 # Decide whether to check for the regular or continuation prompt.
48 if current_block.contains(self._frontend._prompt_pos):
48 if current_block.contains(self._frontend._prompt_pos):
49 prompt = self._frontend._prompt
49 prompt = self._frontend._prompt
50 else:
50 else:
51 prompt = self._frontend._continuation_prompt
51 prompt = self._frontend._continuation_prompt
52
52
53 # Don't highlight the part of the string that contains the prompt.
53 # Don't highlight the part of the string that contains 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 qstring.remove(0, len(prompt))
56 qstring.remove(0, len(prompt))
57 else:
57 else:
58 self._current_offset = 0
58 self._current_offset = 0
59
59
60 PygmentsHighlighter.highlightBlock(self, qstring)
60 PygmentsHighlighter.highlightBlock(self, qstring)
61
61
62 def rehighlightBlock(self, block):
62 def rehighlightBlock(self, block):
63 """ Reimplemented to temporarily enable highlighting if disabled.
63 """ Reimplemented to temporarily enable highlighting if disabled.
64 """
64 """
65 old = self.highlighting_on
65 old = self.highlighting_on
66 self.highlighting_on = True
66 self.highlighting_on = True
67 super(FrontendHighlighter, self).rehighlightBlock(block)
67 super(FrontendHighlighter, self).rehighlightBlock(block)
68 self.highlighting_on = old
68 self.highlighting_on = old
69
69
70 def setFormat(self, start, count, format):
70 def setFormat(self, start, count, format):
71 """ Reimplemented to highlight selectively.
71 """ Reimplemented to highlight selectively.
72 """
72 """
73 start += self._current_offset
73 start += self._current_offset
74 PygmentsHighlighter.setFormat(self, start, count, format)
74 PygmentsHighlighter.setFormat(self, start, count, format)
75
75
76
76
77 class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):
77 class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):
78 """ A Qt frontend for a generic Python kernel.
78 """ A Qt frontend for a generic Python kernel.
79 """
79 """
80
80
81 # An option and corresponding signal for overriding the default kernel
81 # An option and corresponding signal for overriding the default kernel
82 # interrupt behavior.
82 # interrupt behavior.
83 custom_interrupt = Bool(False)
83 custom_interrupt = Bool(False)
84 custom_interrupt_requested = QtCore.pyqtSignal()
84 custom_interrupt_requested = QtCore.pyqtSignal()
85
85
86 # An option and corresponding signals for overriding the default kernel
86 # An option and corresponding signals for overriding the default kernel
87 # restart behavior.
87 # restart behavior.
88 custom_restart = Bool(False)
88 custom_restart = Bool(False)
89 custom_restart_kernel_died = QtCore.pyqtSignal(float)
89 custom_restart_kernel_died = QtCore.pyqtSignal(float)
90 custom_restart_requested = QtCore.pyqtSignal()
90 custom_restart_requested = QtCore.pyqtSignal()
91
91
92 # Emitted when an 'execute_reply' has been received from the kernel and
92 # Emitted when an 'execute_reply' has been received from the kernel and
93 # processed by the FrontendWidget.
93 # processed by the FrontendWidget.
94 executed = QtCore.pyqtSignal(object)
94 executed = QtCore.pyqtSignal(object)
95
95
96 # Emitted when an exit request has been received from the kernel.
96 # Emitted when an exit request has been received from the kernel.
97 exit_requested = QtCore.pyqtSignal()
97 exit_requested = QtCore.pyqtSignal()
98
98
99 # Protected class variables.
99 # Protected class variables.
100 _CallTipRequest = namedtuple('_CallTipRequest', ['id', 'pos'])
100 _CallTipRequest = namedtuple('_CallTipRequest', ['id', 'pos'])
101 _CompletionRequest = namedtuple('_CompletionRequest', ['id', 'pos'])
101 _CompletionRequest = namedtuple('_CompletionRequest', ['id', 'pos'])
102 _ExecutionRequest = namedtuple('_ExecutionRequest', ['id', 'kind'])
102 _ExecutionRequest = namedtuple('_ExecutionRequest', ['id', 'kind'])
103 _input_splitter_class = InputSplitter
103 _input_splitter_class = InputSplitter
104 _local_kernel = False
104
105
105 #---------------------------------------------------------------------------
106 #---------------------------------------------------------------------------
106 # 'object' interface
107 # 'object' interface
107 #---------------------------------------------------------------------------
108 #---------------------------------------------------------------------------
108
109
109 def __init__(self, *args, **kw):
110 def __init__(self, *args, **kw):
110 super(FrontendWidget, self).__init__(*args, **kw)
111 super(FrontendWidget, self).__init__(*args, **kw)
111
112
112 # FrontendWidget protected variables.
113 # FrontendWidget protected variables.
113 self._bracket_matcher = BracketMatcher(self._control)
114 self._bracket_matcher = BracketMatcher(self._control)
114 self._call_tip_widget = CallTipWidget(self._control)
115 self._call_tip_widget = CallTipWidget(self._control)
115 self._completion_lexer = CompletionLexer(PythonLexer())
116 self._completion_lexer = CompletionLexer(PythonLexer())
116 self._copy_raw_action = QtGui.QAction('Copy (Raw Text)', None)
117 self._copy_raw_action = QtGui.QAction('Copy (Raw Text)', None)
117 self._hidden = False
118 self._hidden = False
118 self._highlighter = FrontendHighlighter(self)
119 self._highlighter = FrontendHighlighter(self)
119 self._input_splitter = self._input_splitter_class(input_mode='cell')
120 self._input_splitter = self._input_splitter_class(input_mode='cell')
120 self._kernel_manager = None
121 self._kernel_manager = None
121 self._request_info = {}
122 self._request_info = {}
122
123
123 # Configure the ConsoleWidget.
124 # Configure the ConsoleWidget.
124 self.tab_width = 4
125 self.tab_width = 4
125 self._set_continuation_prompt('... ')
126 self._set_continuation_prompt('... ')
126
127
127 # Configure the CallTipWidget.
128 # Configure the CallTipWidget.
128 self._call_tip_widget.setFont(self.font)
129 self._call_tip_widget.setFont(self.font)
129 self.font_changed.connect(self._call_tip_widget.setFont)
130 self.font_changed.connect(self._call_tip_widget.setFont)
130
131
131 # Configure actions.
132 # Configure actions.
132 action = self._copy_raw_action
133 action = self._copy_raw_action
133 key = QtCore.Qt.CTRL | QtCore.Qt.SHIFT | QtCore.Qt.Key_C
134 key = QtCore.Qt.CTRL | QtCore.Qt.SHIFT | QtCore.Qt.Key_C
134 action.setEnabled(False)
135 action.setEnabled(False)
135 action.setShortcut(QtGui.QKeySequence(key))
136 action.setShortcut(QtGui.QKeySequence(key))
136 action.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut)
137 action.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut)
137 action.triggered.connect(self.copy_raw)
138 action.triggered.connect(self.copy_raw)
138 self.copy_available.connect(action.setEnabled)
139 self.copy_available.connect(action.setEnabled)
139 self.addAction(action)
140 self.addAction(action)
140
141
141 # Connect signal handlers.
142 # Connect signal handlers.
142 document = self._control.document()
143 document = self._control.document()
143 document.contentsChange.connect(self._document_contents_change)
144 document.contentsChange.connect(self._document_contents_change)
145
146 # set flag for whether we are connected via localhost
147 self._local_kernel = kw.get('local_kernel', False)
144
148
145 #---------------------------------------------------------------------------
149 #---------------------------------------------------------------------------
146 # 'ConsoleWidget' public interface
150 # 'ConsoleWidget' public interface
147 #---------------------------------------------------------------------------
151 #---------------------------------------------------------------------------
148
152
149 def copy(self):
153 def copy(self):
150 """ Copy the currently selected text to the clipboard, removing prompts.
154 """ Copy the currently selected text to the clipboard, removing prompts.
151 """
155 """
152 text = unicode(self._control.textCursor().selection().toPlainText())
156 text = unicode(self._control.textCursor().selection().toPlainText())
153 if text:
157 if text:
154 lines = map(transform_classic_prompt, text.splitlines())
158 lines = map(transform_classic_prompt, text.splitlines())
155 text = '\n'.join(lines)
159 text = '\n'.join(lines)
156 QtGui.QApplication.clipboard().setText(text)
160 QtGui.QApplication.clipboard().setText(text)
157
161
158 #---------------------------------------------------------------------------
162 #---------------------------------------------------------------------------
159 # 'ConsoleWidget' abstract interface
163 # 'ConsoleWidget' abstract interface
160 #---------------------------------------------------------------------------
164 #---------------------------------------------------------------------------
161
165
162 def _is_complete(self, source, interactive):
166 def _is_complete(self, source, interactive):
163 """ Returns whether 'source' can be completely processed and a new
167 """ Returns whether 'source' can be completely processed and a new
164 prompt created. When triggered by an Enter/Return key press,
168 prompt created. When triggered by an Enter/Return key press,
165 'interactive' is True; otherwise, it is False.
169 'interactive' is True; otherwise, it is False.
166 """
170 """
167 complete = self._input_splitter.push(source)
171 complete = self._input_splitter.push(source)
168 if interactive:
172 if interactive:
169 complete = not self._input_splitter.push_accepts_more()
173 complete = not self._input_splitter.push_accepts_more()
170 return complete
174 return complete
171
175
172 def _execute(self, source, hidden):
176 def _execute(self, source, hidden):
173 """ Execute 'source'. If 'hidden', do not show any output.
177 """ Execute 'source'. If 'hidden', do not show any output.
174
178
175 See parent class :meth:`execute` docstring for full details.
179 See parent class :meth:`execute` docstring for full details.
176 """
180 """
177 msg_id = self.kernel_manager.xreq_channel.execute(source, hidden)
181 msg_id = self.kernel_manager.xreq_channel.execute(source, hidden)
178 self._request_info['execute'] = self._ExecutionRequest(msg_id, 'user')
182 self._request_info['execute'] = self._ExecutionRequest(msg_id, 'user')
179 self._hidden = hidden
183 self._hidden = hidden
180
184
181 def _prompt_started_hook(self):
185 def _prompt_started_hook(self):
182 """ Called immediately after a new prompt is displayed.
186 """ Called immediately after a new prompt is displayed.
183 """
187 """
184 if not self._reading:
188 if not self._reading:
185 self._highlighter.highlighting_on = True
189 self._highlighter.highlighting_on = True
186
190
187 def _prompt_finished_hook(self):
191 def _prompt_finished_hook(self):
188 """ Called immediately after a prompt is finished, i.e. when some input
192 """ Called immediately after a prompt is finished, i.e. when some input
189 will be processed and a new prompt displayed.
193 will be processed and a new prompt displayed.
190 """
194 """
191 if not self._reading:
195 if not self._reading:
192 self._highlighter.highlighting_on = False
196 self._highlighter.highlighting_on = False
193
197
194 def _tab_pressed(self):
198 def _tab_pressed(self):
195 """ Called when the tab key is pressed. Returns whether to continue
199 """ Called when the tab key is pressed. Returns whether to continue
196 processing the event.
200 processing the event.
197 """
201 """
198 # Perform tab completion if:
202 # Perform tab completion if:
199 # 1) The cursor is in the input buffer.
203 # 1) The cursor is in the input buffer.
200 # 2) There is a non-whitespace character before the cursor.
204 # 2) There is a non-whitespace character before the cursor.
201 text = self._get_input_buffer_cursor_line()
205 text = self._get_input_buffer_cursor_line()
202 if text is None:
206 if text is None:
203 return False
207 return False
204 complete = bool(text[:self._get_input_buffer_cursor_column()].strip())
208 complete = bool(text[:self._get_input_buffer_cursor_column()].strip())
205 if complete:
209 if complete:
206 self._complete()
210 self._complete()
207 return not complete
211 return not complete
208
212
209 #---------------------------------------------------------------------------
213 #---------------------------------------------------------------------------
210 # 'ConsoleWidget' protected interface
214 # 'ConsoleWidget' protected interface
211 #---------------------------------------------------------------------------
215 #---------------------------------------------------------------------------
212
216
213 def _context_menu_make(self, pos):
217 def _context_menu_make(self, pos):
214 """ Reimplemented to add an action for raw copy.
218 """ Reimplemented to add an action for raw copy.
215 """
219 """
216 menu = super(FrontendWidget, self)._context_menu_make(pos)
220 menu = super(FrontendWidget, self)._context_menu_make(pos)
217 for before_action in menu.actions():
221 for before_action in menu.actions():
218 if before_action.shortcut().matches(QtGui.QKeySequence.Paste) == \
222 if before_action.shortcut().matches(QtGui.QKeySequence.Paste) == \
219 QtGui.QKeySequence.ExactMatch:
223 QtGui.QKeySequence.ExactMatch:
220 menu.insertAction(before_action, self._copy_raw_action)
224 menu.insertAction(before_action, self._copy_raw_action)
221 break
225 break
222 return menu
226 return menu
223
227
224 def _event_filter_console_keypress(self, event):
228 def _event_filter_console_keypress(self, event):
225 """ Reimplemented for execution interruption and smart backspace.
229 """ Reimplemented for execution interruption and smart backspace.
226 """
230 """
227 key = event.key()
231 key = event.key()
228 if self._control_key_down(event.modifiers(), include_command=False):
232 if self._control_key_down(event.modifiers(), include_command=False):
229
233
230 if key == QtCore.Qt.Key_C and self._executing:
234 if key == QtCore.Qt.Key_C and self._executing:
231 self.interrupt_kernel()
235 self.interrupt_kernel()
232 return True
236 return True
233
237
234 elif key == QtCore.Qt.Key_Period:
238 elif key == QtCore.Qt.Key_Period:
235 message = 'Are you sure you want to restart the kernel?'
239 message = 'Are you sure you want to restart the kernel?'
236 self.restart_kernel(message, now=False)
240 self.restart_kernel(message, now=False)
237 return True
241 return True
238
242
239 elif not event.modifiers() & QtCore.Qt.AltModifier:
243 elif not event.modifiers() & QtCore.Qt.AltModifier:
240
244
241 # Smart backspace: remove four characters in one backspace if:
245 # Smart backspace: remove four characters in one backspace if:
242 # 1) everything left of the cursor is whitespace
246 # 1) everything left of the cursor is whitespace
243 # 2) the four characters immediately left of the cursor are spaces
247 # 2) the four characters immediately left of the cursor are spaces
244 if key == QtCore.Qt.Key_Backspace:
248 if key == QtCore.Qt.Key_Backspace:
245 col = self._get_input_buffer_cursor_column()
249 col = self._get_input_buffer_cursor_column()
246 cursor = self._control.textCursor()
250 cursor = self._control.textCursor()
247 if col > 3 and not cursor.hasSelection():
251 if col > 3 and not cursor.hasSelection():
248 text = self._get_input_buffer_cursor_line()[:col]
252 text = self._get_input_buffer_cursor_line()[:col]
249 if text.endswith(' ') and not text.strip():
253 if text.endswith(' ') and not text.strip():
250 cursor.movePosition(QtGui.QTextCursor.Left,
254 cursor.movePosition(QtGui.QTextCursor.Left,
251 QtGui.QTextCursor.KeepAnchor, 4)
255 QtGui.QTextCursor.KeepAnchor, 4)
252 cursor.removeSelectedText()
256 cursor.removeSelectedText()
253 return True
257 return True
254
258
255 return super(FrontendWidget, self)._event_filter_console_keypress(event)
259 return super(FrontendWidget, self)._event_filter_console_keypress(event)
256
260
257 def _insert_continuation_prompt(self, cursor):
261 def _insert_continuation_prompt(self, cursor):
258 """ Reimplemented for auto-indentation.
262 """ Reimplemented for auto-indentation.
259 """
263 """
260 super(FrontendWidget, self)._insert_continuation_prompt(cursor)
264 super(FrontendWidget, self)._insert_continuation_prompt(cursor)
261 cursor.insertText(' ' * self._input_splitter.indent_spaces)
265 cursor.insertText(' ' * self._input_splitter.indent_spaces)
262
266
263 #---------------------------------------------------------------------------
267 #---------------------------------------------------------------------------
264 # 'BaseFrontendMixin' abstract interface
268 # 'BaseFrontendMixin' abstract interface
265 #---------------------------------------------------------------------------
269 #---------------------------------------------------------------------------
266
270
267 def _handle_complete_reply(self, rep):
271 def _handle_complete_reply(self, rep):
268 """ Handle replies for tab completion.
272 """ Handle replies for tab completion.
269 """
273 """
270 cursor = self._get_cursor()
274 cursor = self._get_cursor()
271 info = self._request_info.get('complete')
275 info = self._request_info.get('complete')
272 if info and info.id == rep['parent_header']['msg_id'] and \
276 if info and info.id == rep['parent_header']['msg_id'] and \
273 info.pos == cursor.position():
277 info.pos == cursor.position():
274 text = '.'.join(self._get_context())
278 text = '.'.join(self._get_context())
275 cursor.movePosition(QtGui.QTextCursor.Left, n=len(text))
279 cursor.movePosition(QtGui.QTextCursor.Left, n=len(text))
276 self._complete_with_items(cursor, rep['content']['matches'])
280 self._complete_with_items(cursor, rep['content']['matches'])
277
281
278 def _handle_execute_reply(self, msg):
282 def _handle_execute_reply(self, msg):
279 """ Handles replies for code execution.
283 """ Handles replies for code execution.
280 """
284 """
281 info = self._request_info.get('execute')
285 info = self._request_info.get('execute')
282 if info and info.id == msg['parent_header']['msg_id'] and \
286 if info and info.id == msg['parent_header']['msg_id'] and \
283 info.kind == 'user' and not self._hidden:
287 info.kind == 'user' and not self._hidden:
284 # Make sure that all output from the SUB channel has been processed
288 # Make sure that all output from the SUB channel has been processed
285 # before writing a new prompt.
289 # before writing a new prompt.
286 self.kernel_manager.sub_channel.flush()
290 self.kernel_manager.sub_channel.flush()
287
291
288 # Reset the ANSI style information to prevent bad text in stdout
292 # Reset the ANSI style information to prevent bad text in stdout
289 # from messing up our colors. We're not a true terminal so we're
293 # from messing up our colors. We're not a true terminal so we're
290 # allowed to do this.
294 # allowed to do this.
291 if self.ansi_codes:
295 if self.ansi_codes:
292 self._ansi_processor.reset_sgr()
296 self._ansi_processor.reset_sgr()
293
297
294 content = msg['content']
298 content = msg['content']
295 status = content['status']
299 status = content['status']
296 if status == 'ok':
300 if status == 'ok':
297 self._process_execute_ok(msg)
301 self._process_execute_ok(msg)
298 elif status == 'error':
302 elif status == 'error':
299 self._process_execute_error(msg)
303 self._process_execute_error(msg)
300 elif status == 'abort':
304 elif status == 'abort':
301 self._process_execute_abort(msg)
305 self._process_execute_abort(msg)
302
306
303 self._show_interpreter_prompt_for_reply(msg)
307 self._show_interpreter_prompt_for_reply(msg)
304 self.executed.emit(msg)
308 self.executed.emit(msg)
305
309
306 def _handle_input_request(self, msg):
310 def _handle_input_request(self, msg):
307 """ Handle requests for raw_input.
311 """ Handle requests for raw_input.
308 """
312 """
309 if self._hidden:
313 if self._hidden:
310 raise RuntimeError('Request for raw input during hidden execution.')
314 raise RuntimeError('Request for raw input during hidden execution.')
311
315
312 # Make sure that all output from the SUB channel has been processed
316 # Make sure that all output from the SUB channel has been processed
313 # before entering readline mode.
317 # before entering readline mode.
314 self.kernel_manager.sub_channel.flush()
318 self.kernel_manager.sub_channel.flush()
315
319
316 def callback(line):
320 def callback(line):
317 self.kernel_manager.rep_channel.input(line)
321 self.kernel_manager.rep_channel.input(line)
318 self._readline(msg['content']['prompt'], callback=callback)
322 self._readline(msg['content']['prompt'], callback=callback)
319
323
320 def _handle_kernel_died(self, since_last_heartbeat):
324 def _handle_kernel_died(self, since_last_heartbeat):
321 """ Handle the kernel's death by asking if the user wants to restart.
325 """ Handle the kernel's death by asking if the user wants to restart.
322 """
326 """
323 if self.custom_restart:
327 if self.custom_restart:
324 self.custom_restart_kernel_died.emit(since_last_heartbeat)
328 self.custom_restart_kernel_died.emit(since_last_heartbeat)
325 else:
329 else:
326 message = 'The kernel heartbeat has been inactive for %.2f ' \
330 message = 'The kernel heartbeat has been inactive for %.2f ' \
327 'seconds. Do you want to restart the kernel? You may ' \
331 'seconds. Do you want to restart the kernel? You may ' \
328 'first want to check the network connection.' % \
332 'first want to check the network connection.' % \
329 since_last_heartbeat
333 since_last_heartbeat
330 self.restart_kernel(message, now=True)
334 self.restart_kernel(message, now=True)
331
335
332 def _handle_object_info_reply(self, rep):
336 def _handle_object_info_reply(self, rep):
333 """ Handle replies for call tips.
337 """ Handle replies for call tips.
334 """
338 """
335 cursor = self._get_cursor()
339 cursor = self._get_cursor()
336 info = self._request_info.get('call_tip')
340 info = self._request_info.get('call_tip')
337 if info and info.id == rep['parent_header']['msg_id'] and \
341 if info and info.id == rep['parent_header']['msg_id'] and \
338 info.pos == cursor.position():
342 info.pos == cursor.position():
339 # Get the information for a call tip. For now we format the call
343 # Get the information for a call tip. For now we format the call
340 # line as string, later we can pass False to format_call and
344 # line as string, later we can pass False to format_call and
341 # syntax-highlight it ourselves for nicer formatting in the
345 # syntax-highlight it ourselves for nicer formatting in the
342 # calltip.
346 # calltip.
343 call_info, doc = call_tip(rep['content'], format_call=True)
347 call_info, doc = call_tip(rep['content'], format_call=True)
344 if call_info or doc:
348 if call_info or doc:
345 self._call_tip_widget.show_call_info(call_info, doc)
349 self._call_tip_widget.show_call_info(call_info, doc)
346
350
347 def _handle_pyout(self, msg):
351 def _handle_pyout(self, msg):
348 """ Handle display hook output.
352 """ Handle display hook output.
349 """
353 """
350 if not self._hidden and self._is_from_this_session(msg):
354 if not self._hidden and self._is_from_this_session(msg):
351 self._append_plain_text(msg['content']['data'] + '\n')
355 self._append_plain_text(msg['content']['data'] + '\n')
352
356
353 def _handle_stream(self, msg):
357 def _handle_stream(self, msg):
354 """ Handle stdout, stderr, and stdin.
358 """ Handle stdout, stderr, and stdin.
355 """
359 """
356 if not self._hidden and self._is_from_this_session(msg):
360 if not self._hidden and self._is_from_this_session(msg):
357 # Most consoles treat tabs as being 8 space characters. Convert tabs
361 # Most consoles treat tabs as being 8 space characters. Convert tabs
358 # to spaces so that output looks as expected regardless of this
362 # to spaces so that output looks as expected regardless of this
359 # widget's tab width.
363 # widget's tab width.
360 text = msg['content']['data'].expandtabs(8)
364 text = msg['content']['data'].expandtabs(8)
361
365
362 self._append_plain_text(text)
366 self._append_plain_text(text)
363 self._control.moveCursor(QtGui.QTextCursor.End)
367 self._control.moveCursor(QtGui.QTextCursor.End)
364
368
365 def _handle_shutdown_reply(self, msg):
369 def _handle_shutdown_reply(self, msg):
366 """ Handle shutdown signal, only if from other console.
370 """ Handle shutdown signal, only if from other console.
367 """
371 """
368 if not self._hidden and not self._is_from_this_session(msg):
372 if not self._hidden and not self._is_from_this_session(msg):
369 if not msg['content']['restart']:
373 if self._local_kernel:
370 sys.exit(0)
374 if not msg['content']['restart']:
371 else:
375 sys.exit(0)
372 # we just got notified of a restart!
376 else:
373 time.sleep(0.25) # wait 1/4 sec to reset
377 # we just got notified of a restart!
374 # lest the request for a new prompt
378 time.sleep(0.25) # wait 1/4 sec to reset
375 # goes to the old kernel
379 # lest the request for a new prompt
376 self.reset()
380 # goes to the old kernel
381 self.reset()
382 else: # remote kernel, prompt on Kernel shutdown/reset
383 title = self.window().windowTitle()
384 if not msg['content']['restart']:
385 reply = QtGui.QMessageBox.question(self, title,
386 "Kernel has been shutdown permanently. Close the Console?",
387 QtGui.QMessageBox.Yes,QtGui.QMessageBox.No)
388 if reply == QtGui.QMessageBox.Yes:
389 sys.exit(0)
390 else:
391 reply = QtGui.QMessageBox.question(self, title,
392 "Kernel has been reset. Clear the Console?",
393 QtGui.QMessageBox.Yes,QtGui.QMessageBox.No)
394 if reply == QtGui.QMessageBox.Yes:
395 time.sleep(0.25) # wait 1/4 sec to reset
396 # lest the request for a new prompt
397 # goes to the old kernel
398 self.reset()
377
399
378 def _started_channels(self):
400 def _started_channels(self):
379 """ Called when the KernelManager channels have started listening or
401 """ Called when the KernelManager channels have started listening or
380 when the frontend is assigned an already listening KernelManager.
402 when the frontend is assigned an already listening KernelManager.
381 """
403 """
382 self.reset()
404 self.reset()
383
405
384 #---------------------------------------------------------------------------
406 #---------------------------------------------------------------------------
385 # 'FrontendWidget' public interface
407 # 'FrontendWidget' public interface
386 #---------------------------------------------------------------------------
408 #---------------------------------------------------------------------------
387
409
388 def copy_raw(self):
410 def copy_raw(self):
389 """ Copy the currently selected text to the clipboard without attempting
411 """ Copy the currently selected text to the clipboard without attempting
390 to remove prompts or otherwise alter the text.
412 to remove prompts or otherwise alter the text.
391 """
413 """
392 self._control.copy()
414 self._control.copy()
393
415
394 def execute_file(self, path, hidden=False):
416 def execute_file(self, path, hidden=False):
395 """ Attempts to execute file with 'path'. If 'hidden', no output is
417 """ Attempts to execute file with 'path'. If 'hidden', no output is
396 shown.
418 shown.
397 """
419 """
398 self.execute('execfile("%s")' % path, hidden=hidden)
420 self.execute('execfile("%s")' % path, hidden=hidden)
399
421
400 def interrupt_kernel(self):
422 def interrupt_kernel(self):
401 """ Attempts to interrupt the running kernel.
423 """ Attempts to interrupt the running kernel.
402 """
424 """
403 if self.custom_interrupt:
425 if self.custom_interrupt:
404 self.custom_interrupt_requested.emit()
426 self.custom_interrupt_requested.emit()
405 elif self.kernel_manager.has_kernel:
427 elif self.kernel_manager.has_kernel:
406 self.kernel_manager.interrupt_kernel()
428 self.kernel_manager.interrupt_kernel()
407 else:
429 else:
408 self._append_plain_text('Kernel process is either remote or '
430 self._append_plain_text('Kernel process is either remote or '
409 'unspecified. Cannot interrupt.\n')
431 'unspecified. Cannot interrupt.\n')
410
432
411 def reset(self):
433 def reset(self):
412 """ Resets the widget to its initial state. Similar to ``clear``, but
434 """ Resets the widget to its initial state. Similar to ``clear``, but
413 also re-writes the banner and aborts execution if necessary.
435 also re-writes the banner and aborts execution if necessary.
414 """
436 """
415 if self._executing:
437 if self._executing:
416 self._executing = False
438 self._executing = False
417 self._request_info['execute'] = None
439 self._request_info['execute'] = None
418 self._reading = False
440 self._reading = False
419 self._highlighter.highlighting_on = False
441 self._highlighter.highlighting_on = False
420
442
421 self._control.clear()
443 self._control.clear()
422 self._append_plain_text(self._get_banner())
444 self._append_plain_text(self._get_banner())
423 self._show_interpreter_prompt()
445 self._show_interpreter_prompt()
424
446
425 def restart_kernel(self, message, now=False):
447 def restart_kernel(self, message, now=False):
426 """ Attempts to restart the running kernel.
448 """ Attempts to restart the running kernel.
427 """
449 """
428 # FIXME: now should be configurable via a checkbox in the dialog. Right
450 # FIXME: now should be configurable via a checkbox in the dialog. Right
429 # now at least the heartbeat path sets it to True and the manual restart
451 # now at least the heartbeat path sets it to True and the manual restart
430 # to False. But those should just be the pre-selected states of a
452 # to False. But those should just be the pre-selected states of a
431 # checkbox that the user could override if so desired. But I don't know
453 # checkbox that the user could override if so desired. But I don't know
432 # enough Qt to go implementing the checkbox now.
454 # enough Qt to go implementing the checkbox now.
433
455
434 if self.custom_restart:
456 if self.custom_restart:
435 self.custom_restart_requested.emit()
457 self.custom_restart_requested.emit()
436
458
437 elif self.kernel_manager.has_kernel:
459 elif self.kernel_manager.has_kernel:
438 # Pause the heart beat channel to prevent further warnings.
460 # Pause the heart beat channel to prevent further warnings.
439 self.kernel_manager.hb_channel.pause()
461 self.kernel_manager.hb_channel.pause()
440
462
441 # Prompt the user to restart the kernel. Un-pause the heartbeat if
463 # Prompt the user to restart the kernel. Un-pause the heartbeat if
442 # they decline. (If they accept, the heartbeat will be un-paused
464 # they decline. (If they accept, the heartbeat will be un-paused
443 # automatically when the kernel is restarted.)
465 # automatically when the kernel is restarted.)
444 buttons = QtGui.QMessageBox.Yes | QtGui.QMessageBox.No
466 buttons = QtGui.QMessageBox.Yes | QtGui.QMessageBox.No
445 result = QtGui.QMessageBox.question(self, 'Restart kernel?',
467 result = QtGui.QMessageBox.question(self, 'Restart kernel?',
446 message, buttons)
468 message, buttons)
447 if result == QtGui.QMessageBox.Yes:
469 if result == QtGui.QMessageBox.Yes:
448 try:
470 try:
449 self.kernel_manager.restart_kernel(now=now)
471 self.kernel_manager.restart_kernel(now=now)
450 except RuntimeError:
472 except RuntimeError:
451 self._append_plain_text('Kernel started externally. '
473 self._append_plain_text('Kernel started externally. '
452 'Cannot restart.\n')
474 'Cannot restart.\n')
453 else:
475 else:
454 self.reset()
476 self.reset()
455 else:
477 else:
456 self.kernel_manager.hb_channel.unpause()
478 self.kernel_manager.hb_channel.unpause()
457
479
458 else:
480 else:
459 self._append_plain_text('Kernel process is either remote or '
481 self._append_plain_text('Kernel process is either remote or '
460 'unspecified. Cannot restart.\n')
482 'unspecified. Cannot restart.\n')
461
483
462 #---------------------------------------------------------------------------
484 #---------------------------------------------------------------------------
463 # 'FrontendWidget' protected interface
485 # 'FrontendWidget' protected interface
464 #---------------------------------------------------------------------------
486 #---------------------------------------------------------------------------
465
487
466 def _call_tip(self):
488 def _call_tip(self):
467 """ Shows a call tip, if appropriate, at the current cursor location.
489 """ Shows a call tip, if appropriate, at the current cursor location.
468 """
490 """
469 # Decide if it makes sense to show a call tip
491 # Decide if it makes sense to show a call tip
470 cursor = self._get_cursor()
492 cursor = self._get_cursor()
471 cursor.movePosition(QtGui.QTextCursor.Left)
493 cursor.movePosition(QtGui.QTextCursor.Left)
472 if cursor.document().characterAt(cursor.position()).toAscii() != '(':
494 if cursor.document().characterAt(cursor.position()).toAscii() != '(':
473 return False
495 return False
474 context = self._get_context(cursor)
496 context = self._get_context(cursor)
475 if not context:
497 if not context:
476 return False
498 return False
477
499
478 # Send the metadata request to the kernel
500 # Send the metadata request to the kernel
479 name = '.'.join(context)
501 name = '.'.join(context)
480 msg_id = self.kernel_manager.xreq_channel.object_info(name)
502 msg_id = self.kernel_manager.xreq_channel.object_info(name)
481 pos = self._get_cursor().position()
503 pos = self._get_cursor().position()
482 self._request_info['call_tip'] = self._CallTipRequest(msg_id, pos)
504 self._request_info['call_tip'] = self._CallTipRequest(msg_id, pos)
483 return True
505 return True
484
506
485 def _complete(self):
507 def _complete(self):
486 """ Performs completion at the current cursor location.
508 """ Performs completion at the current cursor location.
487 """
509 """
488 context = self._get_context()
510 context = self._get_context()
489 if context:
511 if context:
490 # Send the completion request to the kernel
512 # Send the completion request to the kernel
491 msg_id = self.kernel_manager.xreq_channel.complete(
513 msg_id = self.kernel_manager.xreq_channel.complete(
492 '.'.join(context), # text
514 '.'.join(context), # text
493 self._get_input_buffer_cursor_line(), # line
515 self._get_input_buffer_cursor_line(), # line
494 self._get_input_buffer_cursor_column(), # cursor_pos
516 self._get_input_buffer_cursor_column(), # cursor_pos
495 self.input_buffer) # block
517 self.input_buffer) # block
496 pos = self._get_cursor().position()
518 pos = self._get_cursor().position()
497 info = self._CompletionRequest(msg_id, pos)
519 info = self._CompletionRequest(msg_id, pos)
498 self._request_info['complete'] = info
520 self._request_info['complete'] = info
499
521
500 def _get_banner(self):
522 def _get_banner(self):
501 """ Gets a banner to display at the beginning of a session.
523 """ Gets a banner to display at the beginning of a session.
502 """
524 """
503 banner = 'Python %s on %s\nType "help", "copyright", "credits" or ' \
525 banner = 'Python %s on %s\nType "help", "copyright", "credits" or ' \
504 '"license" for more information.'
526 '"license" for more information.'
505 return banner % (sys.version, sys.platform)
527 return banner % (sys.version, sys.platform)
506
528
507 def _get_context(self, cursor=None):
529 def _get_context(self, cursor=None):
508 """ Gets the context for the specified cursor (or the current cursor
530 """ Gets the context for the specified cursor (or the current cursor
509 if none is specified).
531 if none is specified).
510 """
532 """
511 if cursor is None:
533 if cursor is None:
512 cursor = self._get_cursor()
534 cursor = self._get_cursor()
513 cursor.movePosition(QtGui.QTextCursor.StartOfBlock,
535 cursor.movePosition(QtGui.QTextCursor.StartOfBlock,
514 QtGui.QTextCursor.KeepAnchor)
536 QtGui.QTextCursor.KeepAnchor)
515 text = unicode(cursor.selection().toPlainText())
537 text = unicode(cursor.selection().toPlainText())
516 return self._completion_lexer.get_context(text)
538 return self._completion_lexer.get_context(text)
517
539
518 def _process_execute_abort(self, msg):
540 def _process_execute_abort(self, msg):
519 """ Process a reply for an aborted execution request.
541 """ Process a reply for an aborted execution request.
520 """
542 """
521 self._append_plain_text("ERROR: execution aborted\n")
543 self._append_plain_text("ERROR: execution aborted\n")
522
544
523 def _process_execute_error(self, msg):
545 def _process_execute_error(self, msg):
524 """ Process a reply for an execution request that resulted in an error.
546 """ Process a reply for an execution request that resulted in an error.
525 """
547 """
526 content = msg['content']
548 content = msg['content']
527 traceback = ''.join(content['traceback'])
549 traceback = ''.join(content['traceback'])
528 self._append_plain_text(traceback)
550 self._append_plain_text(traceback)
529
551
530 def _process_execute_ok(self, msg):
552 def _process_execute_ok(self, msg):
531 """ Process a reply for a successful execution equest.
553 """ Process a reply for a successful execution equest.
532 """
554 """
533 payload = msg['content']['payload']
555 payload = msg['content']['payload']
534 for item in payload:
556 for item in payload:
535 if not self._process_execute_payload(item):
557 if not self._process_execute_payload(item):
536 warning = 'Warning: received unknown payload of type %s'
558 warning = 'Warning: received unknown payload of type %s'
537 print(warning % repr(item['source']))
559 print(warning % repr(item['source']))
538
560
539 def _process_execute_payload(self, item):
561 def _process_execute_payload(self, item):
540 """ Process a single payload item from the list of payload items in an
562 """ Process a single payload item from the list of payload items in an
541 execution reply. Returns whether the payload was handled.
563 execution reply. Returns whether the payload was handled.
542 """
564 """
543 # The basic FrontendWidget doesn't handle payloads, as they are a
565 # The basic FrontendWidget doesn't handle payloads, as they are a
544 # mechanism for going beyond the standard Python interpreter model.
566 # mechanism for going beyond the standard Python interpreter model.
545 return False
567 return False
546
568
547 def _show_interpreter_prompt(self):
569 def _show_interpreter_prompt(self):
548 """ Shows a prompt for the interpreter.
570 """ Shows a prompt for the interpreter.
549 """
571 """
550 self._show_prompt('>>> ')
572 self._show_prompt('>>> ')
551
573
552 def _show_interpreter_prompt_for_reply(self, msg):
574 def _show_interpreter_prompt_for_reply(self, msg):
553 """ Shows a prompt for the interpreter given an 'execute_reply' message.
575 """ Shows a prompt for the interpreter given an 'execute_reply' message.
554 """
576 """
555 self._show_interpreter_prompt()
577 self._show_interpreter_prompt()
556
578
557 #------ Signal handlers ----------------------------------------------------
579 #------ Signal handlers ----------------------------------------------------
558
580
559 def _document_contents_change(self, position, removed, added):
581 def _document_contents_change(self, position, removed, added):
560 """ Called whenever the document's content changes. Display a call tip
582 """ Called whenever the document's content changes. Display a call tip
561 if appropriate.
583 if appropriate.
562 """
584 """
563 # Calculate where the cursor should be *after* the change:
585 # Calculate where the cursor should be *after* the change:
564 position += added
586 position += added
565
587
566 document = self._control.document()
588 document = self._control.document()
567 if position == self._get_cursor().position():
589 if position == self._get_cursor().position():
568 self._call_tip()
590 self._call_tip()
@@ -1,158 +1,160 b''
1 """ A minimal application using the Qt console-style IPython frontend.
1 """ A minimal application using the Qt console-style IPython frontend.
2 """
2 """
3
3
4 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 # Imports
5 # Imports
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7
7
8 # Systemm library imports
8 # Systemm library imports
9 from PyQt4 import QtGui
9 from PyQt4 import QtGui
10
10
11 # Local imports
11 # Local imports
12 from IPython.external.argparse import ArgumentParser
12 from IPython.external.argparse import ArgumentParser
13 from IPython.frontend.qt.console.frontend_widget import FrontendWidget
13 from IPython.frontend.qt.console.frontend_widget import FrontendWidget
14 from IPython.frontend.qt.console.ipython_widget import IPythonWidget
14 from IPython.frontend.qt.console.ipython_widget import IPythonWidget
15 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
15 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
16 from IPython.frontend.qt.kernelmanager import QtKernelManager
16 from IPython.frontend.qt.kernelmanager import QtKernelManager
17
17
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # Constants
19 # Constants
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 LOCALHOST = '127.0.0.1'
22 LOCALHOST = '127.0.0.1'
23
23
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25 # Classes
25 # Classes
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27
27
28 class MainWindow(QtGui.QMainWindow):
28 class MainWindow(QtGui.QMainWindow):
29
29
30 #---------------------------------------------------------------------------
30 #---------------------------------------------------------------------------
31 # 'object' interface
31 # 'object' interface
32 #---------------------------------------------------------------------------
32 #---------------------------------------------------------------------------
33
33
34 def __init__(self, app, frontend, existing=False):
34 def __init__(self, app, frontend, existing=False):
35 """ Create a MainWindow for the specified FrontendWidget.
35 """ Create a MainWindow for the specified FrontendWidget.
36
36
37 The app is passed as an argument to allow for different
37 The app is passed as an argument to allow for different
38 closing behavior depending on whether we are the Kernel's parent.
38 closing behavior depending on whether we are the Kernel's parent.
39
39
40 If existing is True, then this Window does not own the Kernel.
40 If existing is True, then this Window does not own the Kernel.
41 """
41 """
42 super(MainWindow, self).__init__()
42 super(MainWindow, self).__init__()
43 self._app = app
43 self._app = app
44 self._frontend = frontend
44 self._frontend = frontend
45 self._existing = existing
45 self._existing = existing
46 self._frontend.exit_requested.connect(self.close)
46 self._frontend.exit_requested.connect(self.close)
47 self.setCentralWidget(frontend)
47 self.setCentralWidget(frontend)
48
48
49 #---------------------------------------------------------------------------
49 #---------------------------------------------------------------------------
50 # QWidget interface
50 # QWidget interface
51 #---------------------------------------------------------------------------
51 #---------------------------------------------------------------------------
52
52
53 def closeEvent(self, event):
53 def closeEvent(self, event):
54 """ Reimplemented to prompt the user and close the kernel cleanly.
54 """ Reimplemented to prompt the user and close the kernel cleanly.
55 """
55 """
56 kernel_manager = self._frontend.kernel_manager
56 kernel_manager = self._frontend.kernel_manager
57 if kernel_manager and kernel_manager.channels_running:
57 if kernel_manager and kernel_manager.channels_running:
58 title = self.window().windowTitle()
58 title = self.window().windowTitle()
59 reply = QtGui.QMessageBox.question(self, title,
59 reply = QtGui.QMessageBox.question(self, title,
60 "Close just this console, or shutdown the kernel and close "+
60 "You are closing this Console window."+
61 "all windows attached to it?",
61 "\nWould you like to quit the Kernel and all attached Consoles as well?",
62 'Cancel', 'Close Console', 'Close All')
62 'Cancel', 'No, just this Console', 'Yes, quit everything')
63 if reply == 2: # close All
63 if reply == 2: # close All
64 kernel_manager.shutdown_kernel()
64 kernel_manager.shutdown_kernel()
65 #kernel_manager.stop_channels()
65 #kernel_manager.stop_channels()
66 event.accept()
66 event.accept()
67 elif reply == 1: # close Console
67 elif reply == 1: # close Console
68 if not self._existing:
68 if not self._existing:
69 # I have the kernel: don't quit, just close the window
69 # I have the kernel: don't quit, just close the window
70 self._app.setQuitOnLastWindowClosed(False)
70 self._app.setQuitOnLastWindowClosed(False)
71 self.deleteLater()
71 event.accept()
72 event.accept()
72 else:
73 else:
73 event.ignore()
74 event.ignore()
74
75
75 #-----------------------------------------------------------------------------
76 #-----------------------------------------------------------------------------
76 # Main entry point
77 # Main entry point
77 #-----------------------------------------------------------------------------
78 #-----------------------------------------------------------------------------
78
79
79 def main():
80 def main():
80 """ Entry point for application.
81 """ Entry point for application.
81 """
82 """
82 # Parse command line arguments.
83 # Parse command line arguments.
83 parser = ArgumentParser()
84 parser = ArgumentParser()
84 kgroup = parser.add_argument_group('kernel options')
85 kgroup = parser.add_argument_group('kernel options')
85 kgroup.add_argument('-e', '--existing', action='store_true',
86 kgroup.add_argument('-e', '--existing', action='store_true',
86 help='connect to an existing kernel')
87 help='connect to an existing kernel')
87 kgroup.add_argument('--ip', type=str, default=LOCALHOST,
88 kgroup.add_argument('--ip', type=str, default=LOCALHOST,
88 help='set the kernel\'s IP address [default localhost]')
89 help='set the kernel\'s IP address [default localhost]')
89 kgroup.add_argument('--xreq', type=int, metavar='PORT', default=0,
90 kgroup.add_argument('--xreq', type=int, metavar='PORT', default=0,
90 help='set the XREQ channel port [default random]')
91 help='set the XREQ channel port [default random]')
91 kgroup.add_argument('--sub', type=int, metavar='PORT', default=0,
92 kgroup.add_argument('--sub', type=int, metavar='PORT', default=0,
92 help='set the SUB channel port [default random]')
93 help='set the SUB channel port [default random]')
93 kgroup.add_argument('--rep', type=int, metavar='PORT', default=0,
94 kgroup.add_argument('--rep', type=int, metavar='PORT', default=0,
94 help='set the REP channel port [default random]')
95 help='set the REP channel port [default random]')
95 kgroup.add_argument('--hb', type=int, metavar='PORT', default=0,
96 kgroup.add_argument('--hb', type=int, metavar='PORT', default=0,
96 help='set the heartbeat port [default: random]')
97 help='set the heartbeat port [default: random]')
97
98
98 egroup = kgroup.add_mutually_exclusive_group()
99 egroup = kgroup.add_mutually_exclusive_group()
99 egroup.add_argument('--pure', action='store_true', help = \
100 egroup.add_argument('--pure', action='store_true', help = \
100 'use a pure Python kernel instead of an IPython kernel')
101 'use a pure Python kernel instead of an IPython kernel')
101 egroup.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
102 egroup.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
102 const='auto', help = \
103 const='auto', help = \
103 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
104 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
104 given, the GUI backend is matplotlib's, otherwise use one of: \
105 given, the GUI backend is matplotlib's, otherwise use one of: \
105 ['tk', 'gtk', 'qt', 'wx', 'inline'].")
106 ['tk', 'gtk', 'qt', 'wx', 'inline'].")
106
107
107 wgroup = parser.add_argument_group('widget options')
108 wgroup = parser.add_argument_group('widget options')
108 wgroup.add_argument('--paging', type=str, default='inside',
109 wgroup.add_argument('--paging', type=str, default='inside',
109 choices = ['inside', 'hsplit', 'vsplit', 'none'],
110 choices = ['inside', 'hsplit', 'vsplit', 'none'],
110 help='set the paging style [default inside]')
111 help='set the paging style [default inside]')
111 wgroup.add_argument('--rich', action='store_true',
112 wgroup.add_argument('--rich', action='store_true',
112 help='enable rich text support')
113 help='enable rich text support')
113 wgroup.add_argument('--gui-completion', action='store_true',
114 wgroup.add_argument('--gui-completion', action='store_true',
114 help='use a GUI widget for tab completion')
115 help='use a GUI widget for tab completion')
115
116
116 args = parser.parse_args()
117 args = parser.parse_args()
117
118
118 # Don't let Qt or ZMQ swallow KeyboardInterupts.
119 # Don't let Qt or ZMQ swallow KeyboardInterupts.
119 import signal
120 import signal
120 signal.signal(signal.SIGINT, signal.SIG_DFL)
121 signal.signal(signal.SIGINT, signal.SIG_DFL)
121
122
122 # Create a KernelManager and start a kernel.
123 # Create a KernelManager and start a kernel.
123 kernel_manager = QtKernelManager(xreq_address=(args.ip, args.xreq),
124 kernel_manager = QtKernelManager(xreq_address=(args.ip, args.xreq),
124 sub_address=(args.ip, args.sub),
125 sub_address=(args.ip, args.sub),
125 rep_address=(args.ip, args.rep),
126 rep_address=(args.ip, args.rep),
126 hb_address=(args.ip, args.hb))
127 hb_address=(args.ip, args.hb))
127 if args.ip == LOCALHOST and not args.existing:
128 if args.ip == LOCALHOST and not args.existing:
128 if args.pure:
129 if args.pure:
129 kernel_manager.start_kernel(ipython=False)
130 kernel_manager.start_kernel(ipython=False)
130 elif args.pylab:
131 elif args.pylab:
131 kernel_manager.start_kernel(pylab=args.pylab)
132 kernel_manager.start_kernel(pylab=args.pylab)
132 else:
133 else:
133 kernel_manager.start_kernel()
134 kernel_manager.start_kernel()
134 kernel_manager.start_channels()
135 kernel_manager.start_channels()
135
136
137 local_kernel = (args.ip == LOCALHOST)
136 # Create the widget.
138 # Create the widget.
137 app = QtGui.QApplication([])
139 app = QtGui.QApplication([])
138 if args.pure:
140 if args.pure:
139 kind = 'rich' if args.rich else 'plain'
141 kind = 'rich' if args.rich else 'plain'
140 widget = FrontendWidget(kind=kind, paging=args.paging)
142 widget = FrontendWidget(kind=kind, paging=args.paging, local_kernel=local_kernel)
141 elif args.rich or args.pylab:
143 elif args.rich or args.pylab:
142 widget = RichIPythonWidget(paging=args.paging)
144 widget = RichIPythonWidget(paging=args.paging, local_kernel=local_kernel)
143 else:
145 else:
144 widget = IPythonWidget(paging=args.paging)
146 widget = IPythonWidget(paging=args.paging, local_kernel=local_kernel)
145 widget.gui_completion = args.gui_completion
147 widget.gui_completion = args.gui_completion
146 widget.kernel_manager = kernel_manager
148 widget.kernel_manager = kernel_manager
147
149
148 # Create the main window.
150 # Create the main window.
149 window = MainWindow(app, widget, args.existing)
151 window = MainWindow(app, widget, args.existing)
150 window.setWindowTitle('Python' if args.pure else 'IPython')
152 window.setWindowTitle('Python' if args.pure else 'IPython')
151 window.show()
153 window.show()
152
154
153 # Start the application main loop.
155 # Start the application main loop.
154 app.exec_()
156 app.exec_()
155
157
156
158
157 if __name__ == '__main__':
159 if __name__ == '__main__':
158 main()
160 main()
General Comments 0
You need to be logged in to leave comments. Login now