##// END OF EJS Templates
* Moved shutdown_kernel method from FrontendWidget to KernelManager....
epatters -
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, user_variables=None,
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 handle %edit and paging payloads.
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 as launch
688 from ipkernel import launch_kernel
689 689 else:
690 from pykernel import launch_kernel as launch
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