##// END OF EJS Templates
prevent remote frontends from closing the kernel
MinRK -
Show More
@@ -1,160 +1,178 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 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 # Imports
5 # Imports
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7
7
8 # Systemm library imports
8 # Systemm library imports
9 from PyQt4 import QtGui
9 from PyQt4 import QtGui
10
10
11 # Local imports
11 # Local imports
12 from IPython.external.argparse import ArgumentParser
12 from IPython.external.argparse import ArgumentParser
13 from IPython.frontend.qt.console.frontend_widget import FrontendWidget
13 from IPython.frontend.qt.console.frontend_widget import FrontendWidget
14 from IPython.frontend.qt.console.ipython_widget import IPythonWidget
14 from IPython.frontend.qt.console.ipython_widget import IPythonWidget
15 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
15 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
16 from IPython.frontend.qt.kernelmanager import QtKernelManager
16 from IPython.frontend.qt.kernelmanager import QtKernelManager
17
17
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # Constants
19 # Constants
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 LOCALHOST = '127.0.0.1'
22 LOCALHOST = '127.0.0.1'
23
23
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25 # Classes
25 # Classes
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27
27
28 class MainWindow(QtGui.QMainWindow):
28 class MainWindow(QtGui.QMainWindow):
29
29
30 #---------------------------------------------------------------------------
30 #---------------------------------------------------------------------------
31 # 'object' interface
31 # 'object' interface
32 #---------------------------------------------------------------------------
32 #---------------------------------------------------------------------------
33
33
34 def __init__(self, app, frontend, existing=False):
34 def __init__(self, app, frontend, existing=False, may_close=True):
35 """ Create a MainWindow for the specified FrontendWidget.
35 """ Create a MainWindow for the specified FrontendWidget.
36
36
37 The app is passed as an argument to allow for different
37 The app is passed as an argument to allow for different
38 closing behavior depending on whether we are the Kernel's parent.
38 closing behavior depending on whether we are the Kernel's parent.
39
39
40 If existing is True, then this Window does not own the Kernel.
40 If existing is True, then this Console does not own the Kernel.
41
42 If may_close is True, then this Console is permitted to close the kernel
41 """
43 """
42 super(MainWindow, self).__init__()
44 super(MainWindow, self).__init__()
43 self._app = app
45 self._app = app
44 self._frontend = frontend
46 self._frontend = frontend
45 self._existing = existing
47 self._existing = existing
48 if not existing:
49 self._may_close = may_close
50 else:
51 self._may_close = True
46 self._frontend.exit_requested.connect(self.close)
52 self._frontend.exit_requested.connect(self.close)
47 self.setCentralWidget(frontend)
53 self.setCentralWidget(frontend)
48
54
49 #---------------------------------------------------------------------------
55 #---------------------------------------------------------------------------
50 # QWidget interface
56 # QWidget interface
51 #---------------------------------------------------------------------------
57 #---------------------------------------------------------------------------
52
58
53 def closeEvent(self, event):
59 def closeEvent(self, event):
54 """ Reimplemented to prompt the user and close the kernel cleanly.
60 """ Reimplemented to prompt the user and close the kernel cleanly.
55 """
61 """
56 kernel_manager = self._frontend.kernel_manager
62 kernel_manager = self._frontend.kernel_manager
57 if kernel_manager and kernel_manager.channels_running:
63 if kernel_manager and kernel_manager.channels_running:
58 title = self.window().windowTitle()
64 title = self.window().windowTitle()
59 reply = QtGui.QMessageBox.question(self, title,
65 if self._may_close:
60 "You are closing this Console window."+
66 reply = QtGui.QMessageBox.question(self, title,
61 "\nWould you like to quit the Kernel and all attached Consoles as well?",
67 "You are closing this Console window."+
62 'Cancel', 'No, just this Console', 'Yes, quit everything')
68 "\nWould you like to quit the Kernel and all attached Consoles as well?",
63 if reply == 2: # close All
69 'Cancel', 'No, just this Console', 'Yes, quit everything')
64 kernel_manager.shutdown_kernel()
70 if reply == 2: # close All
65 #kernel_manager.stop_channels()
71 kernel_manager.shutdown_kernel()
66 event.accept()
72 #kernel_manager.stop_channels()
67 elif reply == 1: # close Console
73 event.accept()
68 if not self._existing:
74 elif reply == 1: # close Console
69 # I have the kernel: don't quit, just close the window
75 if not self._existing:
70 self._app.setQuitOnLastWindowClosed(False)
76 # I have the kernel: don't quit, just close the window
71 self.deleteLater()
77 self._app.setQuitOnLastWindowClosed(False)
72 event.accept()
78 self.deleteLater()
79 event.accept()
80 else:
81 event.ignore()
73 else:
82 else:
74 event.ignore()
83 reply = QtGui.QMessageBox.question(self, title,
84 "Are you sure you want to close this Console?\n"+
85 "The Kernel and other Consoles will remain active.",
86 QtGui.QMessageBox.Yes, QtGui.QMessageBox.No
87 )
88 if reply == QtGui.QMessageBox.Yes:
89 event.accept()
90 else:
91 event.ignore()
92
75
93
76 #-----------------------------------------------------------------------------
94 #-----------------------------------------------------------------------------
77 # Main entry point
95 # Main entry point
78 #-----------------------------------------------------------------------------
96 #-----------------------------------------------------------------------------
79
97
80 def main():
98 def main():
81 """ Entry point for application.
99 """ Entry point for application.
82 """
100 """
83 # Parse command line arguments.
101 # Parse command line arguments.
84 parser = ArgumentParser()
102 parser = ArgumentParser()
85 kgroup = parser.add_argument_group('kernel options')
103 kgroup = parser.add_argument_group('kernel options')
86 kgroup.add_argument('-e', '--existing', action='store_true',
104 kgroup.add_argument('-e', '--existing', action='store_true',
87 help='connect to an existing kernel')
105 help='connect to an existing kernel')
88 kgroup.add_argument('--ip', type=str, default=LOCALHOST,
106 kgroup.add_argument('--ip', type=str, default=LOCALHOST,
89 help='set the kernel\'s IP address [default localhost]')
107 help='set the kernel\'s IP address [default localhost]')
90 kgroup.add_argument('--xreq', type=int, metavar='PORT', default=0,
108 kgroup.add_argument('--xreq', type=int, metavar='PORT', default=0,
91 help='set the XREQ channel port [default random]')
109 help='set the XREQ channel port [default random]')
92 kgroup.add_argument('--sub', type=int, metavar='PORT', default=0,
110 kgroup.add_argument('--sub', type=int, metavar='PORT', default=0,
93 help='set the SUB channel port [default random]')
111 help='set the SUB channel port [default random]')
94 kgroup.add_argument('--rep', type=int, metavar='PORT', default=0,
112 kgroup.add_argument('--rep', type=int, metavar='PORT', default=0,
95 help='set the REP channel port [default random]')
113 help='set the REP channel port [default random]')
96 kgroup.add_argument('--hb', type=int, metavar='PORT', default=0,
114 kgroup.add_argument('--hb', type=int, metavar='PORT', default=0,
97 help='set the heartbeat port [default: random]')
115 help='set the heartbeat port [default: random]')
98
116
99 egroup = kgroup.add_mutually_exclusive_group()
117 egroup = kgroup.add_mutually_exclusive_group()
100 egroup.add_argument('--pure', action='store_true', help = \
118 egroup.add_argument('--pure', action='store_true', help = \
101 'use a pure Python kernel instead of an IPython kernel')
119 'use a pure Python kernel instead of an IPython kernel')
102 egroup.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
120 egroup.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
103 const='auto', help = \
121 const='auto', help = \
104 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
122 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
105 given, the GUI backend is matplotlib's, otherwise use one of: \
123 given, the GUI backend is matplotlib's, otherwise use one of: \
106 ['tk', 'gtk', 'qt', 'wx', 'inline'].")
124 ['tk', 'gtk', 'qt', 'wx', 'inline'].")
107
125
108 wgroup = parser.add_argument_group('widget options')
126 wgroup = parser.add_argument_group('widget options')
109 wgroup.add_argument('--paging', type=str, default='inside',
127 wgroup.add_argument('--paging', type=str, default='inside',
110 choices = ['inside', 'hsplit', 'vsplit', 'none'],
128 choices = ['inside', 'hsplit', 'vsplit', 'none'],
111 help='set the paging style [default inside]')
129 help='set the paging style [default inside]')
112 wgroup.add_argument('--rich', action='store_true',
130 wgroup.add_argument('--rich', action='store_true',
113 help='enable rich text support')
131 help='enable rich text support')
114 wgroup.add_argument('--gui-completion', action='store_true',
132 wgroup.add_argument('--gui-completion', action='store_true',
115 help='use a GUI widget for tab completion')
133 help='use a GUI widget for tab completion')
116
134
117 args = parser.parse_args()
135 args = parser.parse_args()
118
136
119 # Don't let Qt or ZMQ swallow KeyboardInterupts.
137 # Don't let Qt or ZMQ swallow KeyboardInterupts.
120 import signal
138 import signal
121 signal.signal(signal.SIGINT, signal.SIG_DFL)
139 signal.signal(signal.SIGINT, signal.SIG_DFL)
122
140
123 # Create a KernelManager and start a kernel.
141 # Create a KernelManager and start a kernel.
124 kernel_manager = QtKernelManager(xreq_address=(args.ip, args.xreq),
142 kernel_manager = QtKernelManager(xreq_address=(args.ip, args.xreq),
125 sub_address=(args.ip, args.sub),
143 sub_address=(args.ip, args.sub),
126 rep_address=(args.ip, args.rep),
144 rep_address=(args.ip, args.rep),
127 hb_address=(args.ip, args.hb))
145 hb_address=(args.ip, args.hb))
128 if args.ip == LOCALHOST and not args.existing:
146 if not args.existing:
129 if args.pure:
147 if args.pure:
130 kernel_manager.start_kernel(ipython=False)
148 kernel_manager.start_kernel(ipython=False)
131 elif args.pylab:
149 elif args.pylab:
132 kernel_manager.start_kernel(pylab=args.pylab)
150 kernel_manager.start_kernel(pylab=args.pylab)
133 else:
151 else:
134 kernel_manager.start_kernel()
152 kernel_manager.start_kernel()
135 kernel_manager.start_channels()
153 kernel_manager.start_channels()
136
154
137 local_kernel = (args.ip == LOCALHOST)
155 local_kernel = (not args.existing) or args.ip == LOCALHOST
138 # Create the widget.
156 # Create the widget.
139 app = QtGui.QApplication([])
157 app = QtGui.QApplication([])
140 if args.pure:
158 if args.pure:
141 kind = 'rich' if args.rich else 'plain'
159 kind = 'rich' if args.rich else 'plain'
142 widget = FrontendWidget(kind=kind, paging=args.paging, local_kernel=local_kernel)
160 widget = FrontendWidget(kind=kind, paging=args.paging, local_kernel=local_kernel)
143 elif args.rich or args.pylab:
161 elif args.rich or args.pylab:
144 widget = RichIPythonWidget(paging=args.paging, local_kernel=local_kernel)
162 widget = RichIPythonWidget(paging=args.paging, local_kernel=local_kernel)
145 else:
163 else:
146 widget = IPythonWidget(paging=args.paging, local_kernel=local_kernel)
164 widget = IPythonWidget(paging=args.paging, local_kernel=local_kernel)
147 widget.gui_completion = args.gui_completion
165 widget.gui_completion = args.gui_completion
148 widget.kernel_manager = kernel_manager
166 widget.kernel_manager = kernel_manager
149
167
150 # Create the main window.
168 # Create the main window.
151 window = MainWindow(app, widget, args.existing)
169 window = MainWindow(app, widget, args.existing, may_close=local_kernel)
152 window.setWindowTitle('Python' if args.pure else 'IPython')
170 window.setWindowTitle('Python' if args.pure else 'IPython')
153 window.show()
171 window.show()
154
172
155 # Start the application main loop.
173 # Start the application main loop.
156 app.exec_()
174 app.exec_()
157
175
158
176
159 if __name__ == '__main__':
177 if __name__ == '__main__':
160 main()
178 main()
General Comments 0
You need to be logged in to leave comments. Login now