Show More
@@ -3,6 +3,7 b' from __future__ import print_function' | |||
|
3 | 3 | # Standard library imports |
|
4 | 4 | from collections import namedtuple |
|
5 | 5 | import sys |
|
6 | import time | |
|
6 | 7 | |
|
7 | 8 | # System library imports |
|
8 | 9 | from pygments.lexers import PythonLexer |
@@ -361,6 +362,19 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):' | |||
|
361 | 362 | self._append_plain_text(text) |
|
362 | 363 | self._control.moveCursor(QtGui.QTextCursor.End) |
|
363 | 364 | |
|
365 | def _handle_shutdown_reply(self, msg): | |
|
366 | """ Handle shutdown signal, only if from other console. | |
|
367 | """ | |
|
368 | if not self._hidden and not self._is_from_this_session(msg): | |
|
369 | if not msg['content']['restart']: | |
|
370 | sys.exit(0) | |
|
371 | else: | |
|
372 | # we just got notified of a restart! | |
|
373 | time.sleep(0.25) # wait 1/4 sec to reset | |
|
374 | # lest the request for a new prompt | |
|
375 | # goes to the old kernel | |
|
376 | self.reset() | |
|
377 | ||
|
364 | 378 | def _started_channels(self): |
|
365 | 379 | """ Called when the KernelManager channels have started listening or |
|
366 | 380 | when the frontend is assigned an already listening KernelManager. |
@@ -31,11 +31,18 b' class MainWindow(QtGui.QMainWindow):' | |||
|
31 | 31 | # 'object' interface |
|
32 | 32 | #--------------------------------------------------------------------------- |
|
33 | 33 | |
|
34 | def __init__(self, frontend): | |
|
34 | def __init__(self, app, frontend, existing=False): | |
|
35 | 35 | """ Create a MainWindow for the specified FrontendWidget. |
|
36 | ||
|
37 | The app is passed as an argument to allow for different | |
|
38 | closing behavior depending on whether we are the Kernel's parent. | |
|
39 | ||
|
40 | If existing is True, then this Window does not own the Kernel. | |
|
36 | 41 | """ |
|
37 | 42 | super(MainWindow, self).__init__() |
|
43 | self._app = app | |
|
38 | 44 | self._frontend = frontend |
|
45 | self._existing = existing | |
|
39 | 46 | self._frontend.exit_requested.connect(self.close) |
|
40 | 47 | self.setCentralWidget(frontend) |
|
41 | 48 | |
@@ -50,11 +57,18 b' class MainWindow(QtGui.QMainWindow):' | |||
|
50 | 57 | if kernel_manager and kernel_manager.channels_running: |
|
51 | 58 | title = self.window().windowTitle() |
|
52 | 59 | reply = QtGui.QMessageBox.question(self, title, |
|
53 | 'Close console?', QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) | |
|
54 | if reply == QtGui.QMessageBox.Yes: | |
|
60 | "Close just this console, or shutdown the kernel and close "+ | |
|
61 | "all windows attached to it?", | |
|
62 | 'Cancel', 'Close Console', 'Close All') | |
|
63 | if reply == 2: # close All | |
|
55 | 64 | kernel_manager.shutdown_kernel() |
|
56 | 65 | #kernel_manager.stop_channels() |
|
57 | 66 | event.accept() |
|
67 | elif reply == 1: # close Console | |
|
68 | if not self._existing: | |
|
69 | # I have the kernel: don't quit, just close the window | |
|
70 | self._app.setQuitOnLastWindowClosed(False) | |
|
71 | event.accept() | |
|
58 | 72 | else: |
|
59 | 73 | event.ignore() |
|
60 | 74 | |
@@ -132,7 +146,7 b' def main():' | |||
|
132 | 146 | widget.kernel_manager = kernel_manager |
|
133 | 147 | |
|
134 | 148 | # Create the main window. |
|
135 | window = MainWindow(widget) | |
|
149 | window = MainWindow(app, widget, args.existing) | |
|
136 | 150 | window.setWindowTitle('Python' if args.pure else 'IPython') |
|
137 | 151 | window.show() |
|
138 | 152 |
@@ -105,6 +105,9 b' class QtSubSocketChannel(SocketChannelQObject, SubSocketChannel):' | |||
|
105 | 105 | # last-resort sys.excepthook. |
|
106 | 106 | crash_received = QtCore.pyqtSignal(object) |
|
107 | 107 | |
|
108 | # Emitted when a shutdown is noticed. | |
|
109 | shutdown_reply_received = QtCore.pyqtSignal(object) | |
|
110 | ||
|
108 | 111 | #--------------------------------------------------------------------------- |
|
109 | 112 | # 'SubSocketChannel' interface |
|
110 | 113 | #--------------------------------------------------------------------------- |
@@ -330,7 +330,7 b' class Kernel(Configurable):' | |||
|
330 | 330 | |
|
331 | 331 | def shutdown_request(self, ident, parent): |
|
332 | 332 | self.shell.exit_now = True |
|
333 |
self._shutdown_message = self.session.msg(u'shutdown_reply', |
|
|
333 | self._shutdown_message = self.session.msg(u'shutdown_reply', parent['content'], parent) | |
|
334 | 334 | sys.exit(0) |
|
335 | 335 | |
|
336 | 336 | #--------------------------------------------------------------------------- |
@@ -428,6 +428,7 b' class Kernel(Configurable):' | |||
|
428 | 428 | # io.rprint("Kernel at_shutdown") # dbg |
|
429 | 429 | if self._shutdown_message is not None: |
|
430 | 430 | self.reply_socket.send_json(self._shutdown_message) |
|
431 | self.pub_socket.send_json(self._shutdown_message) | |
|
431 | 432 | io.raw_print(self._shutdown_message) |
|
432 | 433 | # A very short sleep to give zmq time to flush its message buffers |
|
433 | 434 | # before Python truly shuts down. |
@@ -305,7 +305,7 b' class XReqSocketChannel(ZmqSocketChannel):' | |||
|
305 | 305 | self._queue_request(msg) |
|
306 | 306 | return msg['header']['msg_id'] |
|
307 | 307 | |
|
308 | def shutdown(self): | |
|
308 | def shutdown(self, restart=False): | |
|
309 | 309 | """Request an immediate kernel shutdown. |
|
310 | 310 | |
|
311 | 311 | Upon receipt of the (empty) reply, client code can safely assume that |
@@ -318,7 +318,7 b' class XReqSocketChannel(ZmqSocketChannel):' | |||
|
318 | 318 | """ |
|
319 | 319 | # Send quit message to kernel. Once we implement kernel-side setattr, |
|
320 | 320 | # this should probably be done that way, but for now this will do. |
|
321 | msg = self.session.msg('shutdown_request', {}) | |
|
321 | msg = self.session.msg('shutdown_request', {'restart':restart}) | |
|
322 | 322 | self._queue_request(msg) |
|
323 | 323 | return msg['header']['msg_id'] |
|
324 | 324 | |
@@ -743,7 +743,7 b' class KernelManager(HasTraits):' | |||
|
743 | 743 | self.rep_address = (LOCALHOST, req) |
|
744 | 744 | self.hb_address = (LOCALHOST, hb) |
|
745 | 745 | |
|
746 | def shutdown_kernel(self): | |
|
746 | def shutdown_kernel(self, restart=False): | |
|
747 | 747 | """ Attempts to the stop the kernel process cleanly. If the kernel |
|
748 | 748 | cannot be stopped, it is killed, if possible. |
|
749 | 749 | """ |
@@ -759,7 +759,7 b' class KernelManager(HasTraits):' | |||
|
759 | 759 | # Don't send any additional kernel kill messages immediately, to give |
|
760 | 760 | # the kernel a chance to properly execute shutdown actions. Wait for at |
|
761 | 761 | # most 1s, checking every 0.1s. |
|
762 | self.xreq_channel.shutdown() | |
|
762 | self.xreq_channel.shutdown(restart=restart) | |
|
763 | 763 | for i in range(10): |
|
764 | 764 | if self.is_alive: |
|
765 | 765 | time.sleep(0.1) |
@@ -793,7 +793,7 b' class KernelManager(HasTraits):' | |||
|
793 | 793 | if now: |
|
794 | 794 | self.kill_kernel() |
|
795 | 795 | else: |
|
796 | self.shutdown_kernel() | |
|
796 | self.shutdown_kernel(restart=True) | |
|
797 | 797 | self.start_kernel(**self._launch_args) |
|
798 | 798 | |
|
799 | 799 | # FIXME: Messages get dropped in Windows due to probable ZMQ bug |
@@ -60,7 +60,7 b' class Kernel(HasTraits):' | |||
|
60 | 60 | |
|
61 | 61 | # Build dict of handlers for message types |
|
62 | 62 | msg_types = [ 'execute_request', 'complete_request', |
|
63 | 'object_info_request' ] | |
|
63 | 'object_info_request', 'shutdown_request' ] | |
|
64 | 64 | self.handlers = {} |
|
65 | 65 | for msg_type in msg_types: |
|
66 | 66 | self.handlers[msg_type] = getattr(self, msg_type) |
@@ -163,6 +163,16 b' class Kernel(HasTraits):' | |||
|
163 | 163 | object_info, parent, ident) |
|
164 | 164 | print >> sys.__stdout__, msg |
|
165 | 165 | |
|
166 | def shutdown_request(self, ident, parent): | |
|
167 | content = dict(parent['content']) | |
|
168 | msg = self.session.send(self.reply_socket, 'shutdown_reply', | |
|
169 | content, parent, ident) | |
|
170 | msg = self.session.send(self.pub_socket, 'shutdown_reply', | |
|
171 | content, parent, ident) | |
|
172 | print >> sys.__stdout__, msg | |
|
173 | time.sleep(0.1) | |
|
174 | sys.exit(0) | |
|
175 | ||
|
166 | 176 | #--------------------------------------------------------------------------- |
|
167 | 177 | # Protected interface |
|
168 | 178 | #--------------------------------------------------------------------------- |
@@ -663,11 +663,13 b' be sent, so the content dict is empty.' | |||
|
663 | 663 | Message type: ``shutdown_request``:: |
|
664 | 664 | |
|
665 | 665 | content = { |
|
666 | 'restart' : bool # whether the shutdown is final, or precedes a restart | |
|
666 | 667 | } |
|
667 | 668 | |
|
668 | 669 | Message type: ``shutdown_reply``:: |
|
669 | 670 | |
|
670 | 671 | content = { |
|
672 | 'restart' : bool # whether the shutdown is final, or precedes a restart | |
|
671 | 673 | } |
|
672 | 674 | |
|
673 | 675 | .. Note:: |
General Comments 0
You need to be logged in to leave comments.
Login now