Show More
@@ -199,6 +199,7 b' class ConsoleWidget(Configurable, QtGui.QWidget):' | |||
|
199 | 199 | |
|
200 | 200 | # Override shortcuts for all filtered widgets. |
|
201 | 201 | elif etype == QtCore.QEvent.ShortcutOverride and \ |
|
202 | self.override_shortcuts and \ | |
|
202 | 203 | self._control_key_down(event.modifiers()) and \ |
|
203 | 204 | event.key() in self._shortcuts: |
|
204 | 205 | event.accept() |
@@ -2,7 +2,6 b'' | |||
|
2 | 2 | from collections import namedtuple |
|
3 | 3 | import signal |
|
4 | 4 | import sys |
|
5 | import time | |
|
6 | 5 | |
|
7 | 6 | # System library imports |
|
8 | 7 | from pygments.lexers import PythonLexer |
@@ -90,6 +89,9 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):' | |||
|
90 | 89 | # Emitted when an 'execute_reply' has been received from the kernel and |
|
91 | 90 | # processed by the FrontendWidget. |
|
92 | 91 | executed = QtCore.pyqtSignal(object) |
|
92 | ||
|
93 | # Emitted when an exit request has been received from the kernel. | |
|
94 | exit_requested = QtCore.pyqtSignal() | |
|
93 | 95 | |
|
94 | 96 | # Protected class variables. |
|
95 | 97 | _CallTipRequest = namedtuple('_CallTipRequest', ['id', 'pos']) |
@@ -137,27 +139,12 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):' | |||
|
137 | 139 | complete = not self._input_splitter.push_accepts_more() |
|
138 | 140 | return complete |
|
139 | 141 | |
|
140 |
def _execute(self, source, hidden |
|
|
141 | user_expressions=None): | |
|
142 | def _execute(self, source, hidden): | |
|
142 | 143 | """ Execute 'source'. If 'hidden', do not show any output. |
|
143 | 144 | |
|
144 | 145 | See parent class :meth:`execute` docstring for full details. |
|
145 | 146 | """ |
|
146 | # tmp code for testing, disable in real use with 'if 0'. Only delete | |
|
147 | # this code once we have automated tests for these fields. | |
|
148 | if 0: | |
|
149 | user_variables = ['x', 'y', 'z'] | |
|
150 | user_expressions = {'sum' : '1+1', | |
|
151 | 'bad syntax' : 'klsdafj kasd f', | |
|
152 | 'bad call' : 'range("hi")', | |
|
153 | 'time' : 'time.time()', | |
|
154 | } | |
|
155 | # /end tmp code | |
|
156 | ||
|
157 | # FIXME - user_variables/expressions are not visible in API above us. | |
|
158 | msg_id = self.kernel_manager.xreq_channel.execute(source, hidden, | |
|
159 | user_variables, | |
|
160 | user_expressions) | |
|
147 | msg_id = self.kernel_manager.xreq_channel.execute(source, hidden) | |
|
161 | 148 | self._request_info['execute'] = self._ExecutionRequest(msg_id, 'user') |
|
162 | 149 | self._hidden = hidden |
|
163 | 150 | |
@@ -235,7 +222,7 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):' | |||
|
235 | 222 | """ |
|
236 | 223 | info = self._request_info.get('execute') |
|
237 | 224 | if info and info.id == msg['parent_header']['msg_id'] and \ |
|
238 | not self._hidden: | |
|
225 | info.kind == 'user' and not self._hidden: | |
|
239 | 226 | # Make sure that all output from the SUB channel has been processed |
|
240 | 227 | # before writing a new prompt. |
|
241 | 228 | self.kernel_manager.sub_channel.flush() |
@@ -371,38 +358,6 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):' | |||
|
371 | 358 | self._append_plain_text('Kernel process is either remote or ' |
|
372 | 359 | 'unspecified. Cannot restart.\n') |
|
373 | 360 | |
|
374 | def closeEvent(self, event): | |
|
375 | reply = QtGui.QMessageBox.question(self, 'Python', | |
|
376 | 'Close console?', QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) | |
|
377 | if reply == QtGui.QMessageBox.Yes: | |
|
378 | self.shutdown_kernel() | |
|
379 | event.accept() | |
|
380 | else: | |
|
381 | event.ignore() | |
|
382 | ||
|
383 | # Move elsewhere to a better location later, possibly rename | |
|
384 | def shutdown_kernel(self): | |
|
385 | # Send quit message to kernel. Once we implement kernel-side setattr, | |
|
386 | # this should probably be done that way, but for now this will do. | |
|
387 | self.kernel_manager.xreq_channel.execute( | |
|
388 | 'get_ipython().exit_now=True', silent=True) | |
|
389 | ||
|
390 | # Don't send any additional kernel kill messages immediately, to give | |
|
391 | # the kernel a chance to properly execute shutdown actions. | |
|
392 | # Wait for at most 2s, check every 0.1s. | |
|
393 | for i in range(20): | |
|
394 | if self.kernel_manager.is_alive: | |
|
395 | time.sleep(0.1) | |
|
396 | else: | |
|
397 | break | |
|
398 | else: | |
|
399 | # OK, we've waited long enough. | |
|
400 | self.kernel_manager.kill_kernel() | |
|
401 | ||
|
402 | # FIXME: This logic may not be quite right... Perhaps the quit call is | |
|
403 | # made elsewhere by Qt... | |
|
404 | QtGui.QApplication.quit() | |
|
405 | ||
|
406 | 361 | #--------------------------------------------------------------------------- |
|
407 | 362 | # 'FrontendWidget' protected interface |
|
408 | 363 | #--------------------------------------------------------------------------- |
@@ -117,12 +117,11 b' class IPythonWidget(FrontendWidget):' | |||
|
117 | 117 | super(IPythonWidget, self).__init__(*args, **kw) |
|
118 | 118 | |
|
119 | 119 | # IPythonWidget protected variables. |
|
120 | self._previous_prompt_obj = None | |
|
121 | self._payload_handlers = { | |
|
120 | self._payload_handlers = { | |
|
122 | 121 | self._payload_source_edit : self._handle_payload_edit, |
|
123 | 122 | self._payload_source_exit : self._handle_payload_exit, |
|
124 |
self._payload_source_page : self._handle_payload_page |
|
|
125 | } | |
|
123 | self._payload_source_page : self._handle_payload_page } | |
|
124 | self._previous_prompt_obj = None | |
|
126 | 125 | |
|
127 | 126 | # Initialize widget styling. |
|
128 | 127 | if self.style_sheet: |
@@ -253,22 +252,10 b' class IPythonWidget(FrontendWidget):' | |||
|
253 | 252 | self._append_html(traceback) |
|
254 | 253 | else: |
|
255 | 254 | # This is the fallback for now, using plain text with ansi escapes |
|
256 | self._append_plain_text(traceback) | |
|
257 | ||
|
258 | # Payload handlers with generic interface: each takes the opaque payload | |
|
259 | # dict, unpacks it and calls the underlying functions with the necessary | |
|
260 | # arguments | |
|
261 | def _handle_payload_edit(self, item): | |
|
262 | self._edit(item['filename'], item['line_number']) | |
|
263 | ||
|
264 | def _handle_payload_exit(self, item): | |
|
265 | QtCore.QCoreApplication.postEvent(self, QtGui.QCloseEvent()) | |
|
266 | ||
|
267 | def _handle_payload_page(self, item): | |
|
268 | self._page(item['data']) | |
|
255 | self._append_plain_text(traceback) | |
|
269 | 256 | |
|
270 | 257 | def _process_execute_payload(self, item): |
|
271 |
""" Reimplemented to |
|
|
258 | """ Reimplemented to dispatch payloads to handler methods. | |
|
272 | 259 | """ |
|
273 | 260 | handler = self._payload_handlers.get(item['source']) |
|
274 | 261 | if handler is None: |
@@ -413,6 +400,21 b' class IPythonWidget(FrontendWidget):' | |||
|
413 | 400 | body = self.out_prompt % number |
|
414 | 401 | return '<span class="out-prompt">%s</span>' % body |
|
415 | 402 | |
|
403 | #------ Payload handlers -------------------------------------------------- | |
|
404 | ||
|
405 | # Payload handlers with a generic interface: each takes the opaque payload | |
|
406 | # dict, unpacks it and calls the underlying functions with the necessary | |
|
407 | # arguments. | |
|
408 | ||
|
409 | def _handle_payload_edit(self, item): | |
|
410 | self._edit(item['filename'], item['line_number']) | |
|
411 | ||
|
412 | def _handle_payload_exit(self, item): | |
|
413 | self.exit_requested.emit() | |
|
414 | ||
|
415 | def _handle_payload_page(self, item): | |
|
416 | self._page(item['data']) | |
|
417 | ||
|
416 | 418 | #------ Trait change handlers --------------------------------------------- |
|
417 | 419 | |
|
418 | 420 | def _style_sheet_changed(self): |
@@ -1,6 +1,10 b'' | |||
|
1 | 1 | """ A minimal application using the Qt console-style IPython frontend. |
|
2 | 2 | """ |
|
3 | 3 | |
|
4 | #----------------------------------------------------------------------------- | |
|
5 | # Imports | |
|
6 | #----------------------------------------------------------------------------- | |
|
7 | ||
|
4 | 8 | # Systemm library imports |
|
5 | 9 | from PyQt4 import QtGui |
|
6 | 10 | |
@@ -11,9 +15,48 b' from IPython.frontend.qt.console.ipython_widget import IPythonWidget' | |||
|
11 | 15 | from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget |
|
12 | 16 | from IPython.frontend.qt.kernelmanager import QtKernelManager |
|
13 | 17 | |
|
18 | #----------------------------------------------------------------------------- | |
|
14 | 19 | # Constants |
|
20 | #----------------------------------------------------------------------------- | |
|
21 | ||
|
15 | 22 | LOCALHOST = '127.0.0.1' |
|
16 | 23 | |
|
24 | #----------------------------------------------------------------------------- | |
|
25 | # Classes | |
|
26 | #----------------------------------------------------------------------------- | |
|
27 | ||
|
28 | class MainWindow(QtGui.QMainWindow): | |
|
29 | ||
|
30 | #--------------------------------------------------------------------------- | |
|
31 | # 'object' interface | |
|
32 | #--------------------------------------------------------------------------- | |
|
33 | ||
|
34 | def __init__(self, frontend): | |
|
35 | """ Create a MainWindow for the specified FrontendWidget. | |
|
36 | """ | |
|
37 | super(MainWindow, self).__init__() | |
|
38 | self._frontend = frontend | |
|
39 | self._frontend.exit_requested.connect(self.close) | |
|
40 | self.setCentralWidget(frontend) | |
|
41 | ||
|
42 | #--------------------------------------------------------------------------- | |
|
43 | # QWidget interface | |
|
44 | #--------------------------------------------------------------------------- | |
|
45 | ||
|
46 | def closeEvent(self, event): | |
|
47 | """ Reimplemented to prompt the user and close the kernel cleanly. | |
|
48 | """ | |
|
49 | reply = QtGui.QMessageBox.question(self, self.window().windowTitle(), | |
|
50 | 'Close console?', QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) | |
|
51 | if reply == QtGui.QMessageBox.Yes: | |
|
52 | self._frontend.kernel_manager.shutdown_kernel() | |
|
53 | event.accept() | |
|
54 | else: | |
|
55 | event.ignore() | |
|
56 | ||
|
57 | #----------------------------------------------------------------------------- | |
|
58 | # Main entry point | |
|
59 | #----------------------------------------------------------------------------- | |
|
17 | 60 | |
|
18 | 61 | def main(): |
|
19 | 62 | """ Entry point for application. |
@@ -89,8 +132,11 b' def main():' | |||
|
89 | 132 | widget = IPythonWidget(paging=args.paging) |
|
90 | 133 | widget.gui_completion = args.gui_completion |
|
91 | 134 | widget.kernel_manager = kernel_manager |
|
92 | widget.setWindowTitle('Python' if args.pure else 'IPython') | |
|
93 | widget.show() | |
|
135 | ||
|
136 | # Create the main window. | |
|
137 | window = MainWindow(widget) | |
|
138 | window.setWindowTitle('Python' if args.pure else 'IPython') | |
|
139 | window.show() | |
|
94 | 140 | |
|
95 | 141 | # Start the application main loop. |
|
96 | 142 | app.exec_() |
@@ -685,10 +685,10 b' class KernelManager(HasTraits):' | |||
|
685 | 685 | |
|
686 | 686 | self._launch_args = kw.copy() |
|
687 | 687 | if kw.pop('ipython', True): |
|
688 |
from ipkernel import launch_kernel |
|
|
688 | from ipkernel import launch_kernel | |
|
689 | 689 | else: |
|
690 |
from pykernel import launch_kernel |
|
|
691 | self.kernel, xrep, pub, req, hb = launch( | |
|
690 | from pykernel import launch_kernel | |
|
691 | self.kernel, xrep, pub, req, hb = launch_kernel( | |
|
692 | 692 | xrep_port=xreq[1], pub_port=sub[1], |
|
693 | 693 | req_port=rep[1], hb_port=hb[1], **kw) |
|
694 | 694 | self.xreq_address = (LOCALHOST, xrep) |
@@ -696,6 +696,27 b' class KernelManager(HasTraits):' | |||
|
696 | 696 | self.rep_address = (LOCALHOST, req) |
|
697 | 697 | self.hb_address = (LOCALHOST, hb) |
|
698 | 698 | |
|
699 | def shutdown_kernel(self): | |
|
700 | """ Attempts to the stop the kernel process cleanly. If the kernel | |
|
701 | cannot be stopped, it is killed, if possible. | |
|
702 | """ | |
|
703 | # Send quit message to kernel. Once we implement kernel-side setattr, | |
|
704 | # this should probably be done that way, but for now this will do. | |
|
705 | self.xreq_channel.execute('get_ipython().exit_now=True', silent=True) | |
|
706 | ||
|
707 | # Don't send any additional kernel kill messages immediately, to give | |
|
708 | # the kernel a chance to properly execute shutdown actions. Wait for at | |
|
709 | # most 2s, checking every 0.1s. | |
|
710 | for i in range(20): | |
|
711 | if self.is_alive: | |
|
712 | time.sleep(0.1) | |
|
713 | else: | |
|
714 | break | |
|
715 | else: | |
|
716 | # OK, we've waited long enough. | |
|
717 | if self.has_kernel: | |
|
718 | self.kill_kernel() | |
|
719 | ||
|
699 | 720 | def restart_kernel(self): |
|
700 | 721 | """Restarts a kernel with the same arguments that were used to launch |
|
701 | 722 | it. If the old kernel was launched with random ports, the same ports |
General Comments 0
You need to be logged in to leave comments.
Login now