##// 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 # Override shortcuts for all filtered widgets.
200 # Override shortcuts for all filtered widgets.
201 elif etype == QtCore.QEvent.ShortcutOverride and \
201 elif etype == QtCore.QEvent.ShortcutOverride and \
202 self.override_shortcuts and \
202 self._control_key_down(event.modifiers()) and \
203 self._control_key_down(event.modifiers()) and \
203 event.key() in self._shortcuts:
204 event.key() in self._shortcuts:
204 event.accept()
205 event.accept()
@@ -2,7 +2,6 b''
2 from collections import namedtuple
2 from collections import namedtuple
3 import signal
3 import signal
4 import sys
4 import sys
5 import time
6
5
7 # System library imports
6 # System library imports
8 from pygments.lexers import PythonLexer
7 from pygments.lexers import PythonLexer
@@ -90,6 +89,9 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
90 # Emitted when an 'execute_reply' has been received from the kernel and
89 # Emitted when an 'execute_reply' has been received from the kernel and
91 # processed by the FrontendWidget.
90 # processed by the FrontendWidget.
92 executed = QtCore.pyqtSignal(object)
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 # Protected class variables.
96 # Protected class variables.
95 _CallTipRequest = namedtuple('_CallTipRequest', ['id', 'pos'])
97 _CallTipRequest = namedtuple('_CallTipRequest', ['id', 'pos'])
@@ -137,27 +139,12 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
137 complete = not self._input_splitter.push_accepts_more()
139 complete = not self._input_splitter.push_accepts_more()
138 return complete
140 return complete
139
141
140 def _execute(self, source, hidden, user_variables=None,
142 def _execute(self, source, hidden):
141 user_expressions=None):
142 """ Execute 'source'. If 'hidden', do not show any output.
143 """ Execute 'source'. If 'hidden', do not show any output.
143
144
144 See parent class :meth:`execute` docstring for full details.
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 msg_id = self.kernel_manager.xreq_channel.execute(source, hidden)
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)
161 self._request_info['execute'] = self._ExecutionRequest(msg_id, 'user')
148 self._request_info['execute'] = self._ExecutionRequest(msg_id, 'user')
162 self._hidden = hidden
149 self._hidden = hidden
163
150
@@ -235,7 +222,7 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
235 """
222 """
236 info = self._request_info.get('execute')
223 info = self._request_info.get('execute')
237 if info and info.id == msg['parent_header']['msg_id'] and \
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 # Make sure that all output from the SUB channel has been processed
226 # Make sure that all output from the SUB channel has been processed
240 # before writing a new prompt.
227 # before writing a new prompt.
241 self.kernel_manager.sub_channel.flush()
228 self.kernel_manager.sub_channel.flush()
@@ -371,38 +358,6 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
371 self._append_plain_text('Kernel process is either remote or '
358 self._append_plain_text('Kernel process is either remote or '
372 'unspecified. Cannot restart.\n')
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 # 'FrontendWidget' protected interface
362 # 'FrontendWidget' protected interface
408 #---------------------------------------------------------------------------
363 #---------------------------------------------------------------------------
@@ -117,12 +117,11 b' class IPythonWidget(FrontendWidget):'
117 super(IPythonWidget, self).__init__(*args, **kw)
117 super(IPythonWidget, self).__init__(*args, **kw)
118
118
119 # IPythonWidget protected variables.
119 # IPythonWidget protected variables.
120 self._previous_prompt_obj = None
120 self._payload_handlers = {
121 self._payload_handlers = {
122 self._payload_source_edit : self._handle_payload_edit,
121 self._payload_source_edit : self._handle_payload_edit,
123 self._payload_source_exit : self._handle_payload_exit,
122 self._payload_source_exit : self._handle_payload_exit,
124 self._payload_source_page : self._handle_payload_page,
123 self._payload_source_page : self._handle_payload_page }
125 }
124 self._previous_prompt_obj = None
126
125
127 # Initialize widget styling.
126 # Initialize widget styling.
128 if self.style_sheet:
127 if self.style_sheet:
@@ -253,22 +252,10 b' class IPythonWidget(FrontendWidget):'
253 self._append_html(traceback)
252 self._append_html(traceback)
254 else:
253 else:
255 # This is the fallback for now, using plain text with ansi escapes
254 # This is the fallback for now, using plain text with ansi escapes
256 self._append_plain_text(traceback)
255 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'])
269
256
270 def _process_execute_payload(self, item):
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 handler = self._payload_handlers.get(item['source'])
260 handler = self._payload_handlers.get(item['source'])
274 if handler is None:
261 if handler is None:
@@ -413,6 +400,21 b' class IPythonWidget(FrontendWidget):'
413 body = self.out_prompt % number
400 body = self.out_prompt % number
414 return '<span class="out-prompt">%s</span>' % body
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 #------ Trait change handlers ---------------------------------------------
418 #------ Trait change handlers ---------------------------------------------
417
419
418 def _style_sheet_changed(self):
420 def _style_sheet_changed(self):
@@ -1,6 +1,10 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 #-----------------------------------------------------------------------------
5 # Imports
6 #-----------------------------------------------------------------------------
7
4 # Systemm library imports
8 # Systemm library imports
5 from PyQt4 import QtGui
9 from PyQt4 import QtGui
6
10
@@ -11,9 +15,48 b' from IPython.frontend.qt.console.ipython_widget import IPythonWidget'
11 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
15 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
12 from IPython.frontend.qt.kernelmanager import QtKernelManager
16 from IPython.frontend.qt.kernelmanager import QtKernelManager
13
17
18 #-----------------------------------------------------------------------------
14 # Constants
19 # Constants
20 #-----------------------------------------------------------------------------
21
15 LOCALHOST = '127.0.0.1'
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 def main():
61 def main():
19 """ Entry point for application.
62 """ Entry point for application.
@@ -89,8 +132,11 b' def main():'
89 widget = IPythonWidget(paging=args.paging)
132 widget = IPythonWidget(paging=args.paging)
90 widget.gui_completion = args.gui_completion
133 widget.gui_completion = args.gui_completion
91 widget.kernel_manager = kernel_manager
134 widget.kernel_manager = kernel_manager
92 widget.setWindowTitle('Python' if args.pure else 'IPython')
135
93 widget.show()
136 # Create the main window.
137 window = MainWindow(widget)
138 window.setWindowTitle('Python' if args.pure else 'IPython')
139 window.show()
94
140
95 # Start the application main loop.
141 # Start the application main loop.
96 app.exec_()
142 app.exec_()
@@ -685,10 +685,10 b' class KernelManager(HasTraits):'
685
685
686 self._launch_args = kw.copy()
686 self._launch_args = kw.copy()
687 if kw.pop('ipython', True):
687 if kw.pop('ipython', True):
688 from ipkernel import launch_kernel as launch
688 from ipkernel import launch_kernel
689 else:
689 else:
690 from pykernel import launch_kernel as launch
690 from pykernel import launch_kernel
691 self.kernel, xrep, pub, req, hb = launch(
691 self.kernel, xrep, pub, req, hb = launch_kernel(
692 xrep_port=xreq[1], pub_port=sub[1],
692 xrep_port=xreq[1], pub_port=sub[1],
693 req_port=rep[1], hb_port=hb[1], **kw)
693 req_port=rep[1], hb_port=hb[1], **kw)
694 self.xreq_address = (LOCALHOST, xrep)
694 self.xreq_address = (LOCALHOST, xrep)
@@ -696,6 +696,27 b' class KernelManager(HasTraits):'
696 self.rep_address = (LOCALHOST, req)
696 self.rep_address = (LOCALHOST, req)
697 self.hb_address = (LOCALHOST, hb)
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 def restart_kernel(self):
720 def restart_kernel(self):
700 """Restarts a kernel with the same arguments that were used to launch
721 """Restarts a kernel with the same arguments that were used to launch
701 it. If the old kernel was launched with random ports, the same ports
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