diff --git a/IPython/frontend/qt/console/ipython_widget.py b/IPython/frontend/qt/console/ipython_widget.py index dd2e594..b4741b8 100644 --- a/IPython/frontend/qt/console/ipython_widget.py +++ b/IPython/frontend/qt/console/ipython_widget.py @@ -111,6 +111,7 @@ class IPythonWidget(FrontendWidget): self._payload_source_page : self._handle_payload_page, self._payload_source_loadpy : self._handle_payload_loadpy } self._previous_prompt_obj = None + self._keep_kernel_on_exit = None # Initialize widget styling. if self.style_sheet: @@ -424,6 +425,7 @@ class IPythonWidget(FrontendWidget): self._edit(item['filename'], item['line_number']) def _handle_payload_exit(self, item): + self._keep_kernel_on_exit = item['keepkernel'] self.exit_requested.emit() def _handle_payload_loadpy(self, item): diff --git a/IPython/frontend/qt/console/ipythonqt.py b/IPython/frontend/qt/console/ipythonqt.py index 100404b..b759fd9 100644 --- a/IPython/frontend/qt/console/ipythonqt.py +++ b/IPython/frontend/qt/console/ipythonqt.py @@ -60,50 +60,62 @@ class MainWindow(QtGui.QMainWindow): def closeEvent(self, event): """ Reimplemented to prompt the user and close the kernel cleanly. """ + keepkernel = self._frontend._keep_kernel_on_exit kernel_manager = self._frontend.kernel_manager - if kernel_manager and kernel_manager.channels_running: - title = self.window().windowTitle() - cancel = QtGui.QMessageBox.Cancel - okay = QtGui.QMessageBox.Ok - if self._may_close: - msg = "You are closing this Console window." - info = "Would you like to quit the Kernel and all attached Consoles as well?" - justthis = QtGui.QPushButton("&No, just this Console", self) - justthis.setShortcut('N') - closeall = QtGui.QPushButton("&Yes, quit everything", self) - closeall.setShortcut('Y') - box = QtGui.QMessageBox(QtGui.QMessageBox.Question, title, msg) - box.setInformativeText(info) - box.addButton(cancel) - box.addButton(justthis, QtGui.QMessageBox.NoRole) - box.addButton(closeall, QtGui.QMessageBox.YesRole) - box.setDefaultButton(closeall) - box.setEscapeButton(cancel) - reply = box.exec_() - if reply == 1: # close All - kernel_manager.shutdown_kernel() - #kernel_manager.stop_channels() - event.accept() - elif reply == 0: # close Console - if not self._existing: - # I have the kernel: don't quit, just close the window - self._app.setQuitOnLastWindowClosed(False) - self.deleteLater() - event.accept() - else: - event.ignore() - else: - reply = QtGui.QMessageBox.question(self, title, - "Are you sure you want to close this Console?"+ - "\nThe Kernel and other Consoles will remain active.", - okay|cancel, - defaultButton=okay - ) - if reply == okay: - event.accept() + + if keepkernel is None: + if kernel_manager and kernel_manager.channels_running: + title = self.window().windowTitle() + cancel = QtGui.QMessageBox.Cancel + okay = QtGui.QMessageBox.Ok + if self._may_close: + msg = "You are closing this Console window." + info = "Would you like to quit the Kernel and all attached Consoles as well?" + justthis = QtGui.QPushButton("&No, just this Console", self) + justthis.setShortcut('N') + closeall = QtGui.QPushButton("&Yes, quit everything", self) + closeall.setShortcut('Y') + box = QtGui.QMessageBox(QtGui.QMessageBox.Question, title, msg) + box.setInformativeText(info) + box.addButton(cancel) + box.addButton(justthis, QtGui.QMessageBox.NoRole) + box.addButton(closeall, QtGui.QMessageBox.YesRole) + box.setDefaultButton(closeall) + box.setEscapeButton(cancel) + reply = box.exec_() + if reply == 1: # close All + kernel_manager.shutdown_kernel() + #kernel_manager.stop_channels() + event.accept() + elif reply == 0: # close Console + if not self._existing: + # Have kernel: don't quit, just close the window + self._app.setQuitOnLastWindowClosed(False) + self.deleteLater() + event.accept() + else: + event.ignore() else: - event.ignore() - + reply = QtGui.QMessageBox.question(self, title, + "Are you sure you want to close this Console?"+ + "\nThe Kernel and other Consoles will remain active.", + okay|cancel, + defaultButton=okay + ) + if reply == okay: + event.accept() + else: + event.ignore() + elif keepkernel: #close console but leave kernel running + if kernel_manager and kernel_manager.channels_running: + if not self._existing: + # I have the kernel: don't quit, just close the window + self._app.setQuitOnLastWindowClosed(False) + event.accept() + else: #close console and kernel + if kernel_manager and kernel_manager.channels_running: + kernel_manager.shutdown_kernel() + event.accept() #----------------------------------------------------------------------------- # Main entry point diff --git a/IPython/zmq/zmqshell.py b/IPython/zmq/zmqshell.py index c5785ac..1805622 100644 --- a/IPython/zmq/zmqshell.py +++ b/IPython/zmq/zmqshell.py @@ -78,6 +78,7 @@ class ZMQInteractiveShell(InteractiveShell): """A subclass of InteractiveShell for ZMQ.""" displayhook_class = Type(ZMQDisplayHook) + keepkernel = None def init_environment(self): """Configure the user's environment. @@ -111,6 +112,7 @@ class ZMQInteractiveShell(InteractiveShell): payload = dict( source='IPython.zmq.zmqshell.ZMQInteractiveShell.ask_exit', exit=True, + keepkernel=self.keepkernel, ) self.payload_manager.write_payload(payload) @@ -563,5 +565,16 @@ class ZMQInteractiveShell(InteractiveShell): text=content ) self.payload_manager.write_payload(payload) + + def magic_Exit(self, parameter_s=''): + """Exit IPython. If the -k option is provided, the kernel will be left + running. Otherwise, it will shutdown without prompting. + """ + opts,args = self.parse_options(parameter_s,'k') + self.shell.keepkernel = opts.has_key('k') + self.shell.ask_exit() + + # Add aliases as magics so all common forms work: exit, quit, Exit, Quit. + magic_exit = magic_quit = magic_Quit = magic_Exit InteractiveShellABC.register(ZMQInteractiveShell)