##// END OF EJS Templates
Faciliate ssh tunnel sharing by announcing ports
Paul Ivanov -
Show More
@@ -1,497 +1,503 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 This is not a complete console app, as subprocess will not be able to receive
3 This is not a complete console app, as subprocess will not be able to receive
4 input, there is no real readline support, among other limitations.
4 input, there is no real readline support, among other limitations.
5
5
6 Authors:
6 Authors:
7
7
8 * Evan Patterson
8 * Evan Patterson
9 * Min RK
9 * Min RK
10 * Erik Tollerud
10 * Erik Tollerud
11 * Fernando Perez
11 * Fernando Perez
12
12
13 """
13 """
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 # stdlib imports
19 # stdlib imports
20 import os
20 import os
21 import signal
21 import signal
22 import sys
22 import sys
23 from getpass import getpass
23 from getpass import getpass
24
24
25 # System library imports
25 # System library imports
26 from IPython.external.qt import QtGui
26 from IPython.external.qt import QtGui
27 from pygments.styles import get_all_styles
27 from pygments.styles import get_all_styles
28
28
29 # external imports
29 # external imports
30 from IPython.external.ssh import tunnel
30 from IPython.external.ssh import tunnel
31
31
32 # Local imports
32 # Local imports
33 from IPython.config.application import boolean_flag
33 from IPython.config.application import boolean_flag
34 from IPython.core.application import BaseIPythonApplication
34 from IPython.core.application import BaseIPythonApplication
35 from IPython.core.profiledir import ProfileDir
35 from IPython.core.profiledir import ProfileDir
36 from IPython.frontend.qt.console.frontend_widget import FrontendWidget
36 from IPython.frontend.qt.console.frontend_widget import FrontendWidget
37 from IPython.frontend.qt.console.ipython_widget import IPythonWidget
37 from IPython.frontend.qt.console.ipython_widget import IPythonWidget
38 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
38 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
39 from IPython.frontend.qt.console import styles
39 from IPython.frontend.qt.console import styles
40 from IPython.frontend.qt.kernelmanager import QtKernelManager
40 from IPython.frontend.qt.kernelmanager import QtKernelManager
41 from IPython.parallel.util import select_random_ports
41 from IPython.parallel.util import select_random_ports
42 from IPython.utils.traitlets import (
42 from IPython.utils.traitlets import (
43 Dict, List, Unicode, Int, CaselessStrEnum, CBool, Any
43 Dict, List, Unicode, Int, CaselessStrEnum, CBool, Any
44 )
44 )
45 from IPython.zmq.ipkernel import (
45 from IPython.zmq.ipkernel import (
46 flags as ipkernel_flags,
46 flags as ipkernel_flags,
47 aliases as ipkernel_aliases,
47 aliases as ipkernel_aliases,
48 IPKernelApp
48 IPKernelApp
49 )
49 )
50 from IPython.zmq.session import Session
50 from IPython.zmq.session import Session
51 from IPython.zmq.zmqshell import ZMQInteractiveShell
51 from IPython.zmq.zmqshell import ZMQInteractiveShell
52
52
53
53
54 #-----------------------------------------------------------------------------
54 #-----------------------------------------------------------------------------
55 # Network Constants
55 # Network Constants
56 #-----------------------------------------------------------------------------
56 #-----------------------------------------------------------------------------
57
57
58 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
58 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
59
59
60 #-----------------------------------------------------------------------------
60 #-----------------------------------------------------------------------------
61 # Globals
61 # Globals
62 #-----------------------------------------------------------------------------
62 #-----------------------------------------------------------------------------
63
63
64 _examples = """
64 _examples = """
65 ipython qtconsole # start the qtconsole
65 ipython qtconsole # start the qtconsole
66 ipython qtconsole --pylab=inline # start with pylab in inline plotting mode
66 ipython qtconsole --pylab=inline # start with pylab in inline plotting mode
67 """
67 """
68
68
69 #-----------------------------------------------------------------------------
69 #-----------------------------------------------------------------------------
70 # Classes
70 # Classes
71 #-----------------------------------------------------------------------------
71 #-----------------------------------------------------------------------------
72
72
73 class MainWindow(QtGui.QMainWindow):
73 class MainWindow(QtGui.QMainWindow):
74
74
75 #---------------------------------------------------------------------------
75 #---------------------------------------------------------------------------
76 # 'object' interface
76 # 'object' interface
77 #---------------------------------------------------------------------------
77 #---------------------------------------------------------------------------
78
78
79 def __init__(self, app, frontend, existing=False, may_close=True,
79 def __init__(self, app, frontend, existing=False, may_close=True,
80 confirm_exit=True):
80 confirm_exit=True):
81 """ Create a MainWindow for the specified FrontendWidget.
81 """ Create a MainWindow for the specified FrontendWidget.
82
82
83 The app is passed as an argument to allow for different
83 The app is passed as an argument to allow for different
84 closing behavior depending on whether we are the Kernel's parent.
84 closing behavior depending on whether we are the Kernel's parent.
85
85
86 If existing is True, then this Console does not own the Kernel.
86 If existing is True, then this Console does not own the Kernel.
87
87
88 If may_close is True, then this Console is permitted to close the kernel
88 If may_close is True, then this Console is permitted to close the kernel
89 """
89 """
90 super(MainWindow, self).__init__()
90 super(MainWindow, self).__init__()
91 self._app = app
91 self._app = app
92 self._frontend = frontend
92 self._frontend = frontend
93 self._existing = existing
93 self._existing = existing
94 if existing:
94 if existing:
95 self._may_close = may_close
95 self._may_close = may_close
96 else:
96 else:
97 self._may_close = True
97 self._may_close = True
98 self._frontend.exit_requested.connect(self.close)
98 self._frontend.exit_requested.connect(self.close)
99 self._confirm_exit = confirm_exit
99 self._confirm_exit = confirm_exit
100 self.setCentralWidget(frontend)
100 self.setCentralWidget(frontend)
101
101
102 #---------------------------------------------------------------------------
102 #---------------------------------------------------------------------------
103 # QWidget interface
103 # QWidget interface
104 #---------------------------------------------------------------------------
104 #---------------------------------------------------------------------------
105
105
106 def closeEvent(self, event):
106 def closeEvent(self, event):
107 """ Close the window and the kernel (if necessary).
107 """ Close the window and the kernel (if necessary).
108
108
109 This will prompt the user if they are finished with the kernel, and if
109 This will prompt the user if they are finished with the kernel, and if
110 so, closes the kernel cleanly. Alternatively, if the exit magic is used,
110 so, closes the kernel cleanly. Alternatively, if the exit magic is used,
111 it closes without prompt.
111 it closes without prompt.
112 """
112 """
113 keepkernel = None #Use the prompt by default
113 keepkernel = None #Use the prompt by default
114 if hasattr(self._frontend,'_keep_kernel_on_exit'): #set by exit magic
114 if hasattr(self._frontend,'_keep_kernel_on_exit'): #set by exit magic
115 keepkernel = self._frontend._keep_kernel_on_exit
115 keepkernel = self._frontend._keep_kernel_on_exit
116
116
117 kernel_manager = self._frontend.kernel_manager
117 kernel_manager = self._frontend.kernel_manager
118
118
119 if keepkernel is None and not self._confirm_exit:
119 if keepkernel is None and not self._confirm_exit:
120 # don't prompt, just terminate the kernel if we own it
120 # don't prompt, just terminate the kernel if we own it
121 # or leave it alone if we don't
121 # or leave it alone if we don't
122 keepkernel = not self._existing
122 keepkernel = not self._existing
123
123
124 if keepkernel is None: #show prompt
124 if keepkernel is None: #show prompt
125 if kernel_manager and kernel_manager.channels_running:
125 if kernel_manager and kernel_manager.channels_running:
126 title = self.window().windowTitle()
126 title = self.window().windowTitle()
127 cancel = QtGui.QMessageBox.Cancel
127 cancel = QtGui.QMessageBox.Cancel
128 okay = QtGui.QMessageBox.Ok
128 okay = QtGui.QMessageBox.Ok
129 if self._may_close:
129 if self._may_close:
130 msg = "You are closing this Console window."
130 msg = "You are closing this Console window."
131 info = "Would you like to quit the Kernel and all attached Consoles as well?"
131 info = "Would you like to quit the Kernel and all attached Consoles as well?"
132 justthis = QtGui.QPushButton("&No, just this Console", self)
132 justthis = QtGui.QPushButton("&No, just this Console", self)
133 justthis.setShortcut('N')
133 justthis.setShortcut('N')
134 closeall = QtGui.QPushButton("&Yes, quit everything", self)
134 closeall = QtGui.QPushButton("&Yes, quit everything", self)
135 closeall.setShortcut('Y')
135 closeall.setShortcut('Y')
136 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
136 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
137 title, msg)
137 title, msg)
138 box.setInformativeText(info)
138 box.setInformativeText(info)
139 box.addButton(cancel)
139 box.addButton(cancel)
140 box.addButton(justthis, QtGui.QMessageBox.NoRole)
140 box.addButton(justthis, QtGui.QMessageBox.NoRole)
141 box.addButton(closeall, QtGui.QMessageBox.YesRole)
141 box.addButton(closeall, QtGui.QMessageBox.YesRole)
142 box.setDefaultButton(closeall)
142 box.setDefaultButton(closeall)
143 box.setEscapeButton(cancel)
143 box.setEscapeButton(cancel)
144 reply = box.exec_()
144 reply = box.exec_()
145 if reply == 1: # close All
145 if reply == 1: # close All
146 kernel_manager.shutdown_kernel()
146 kernel_manager.shutdown_kernel()
147 #kernel_manager.stop_channels()
147 #kernel_manager.stop_channels()
148 event.accept()
148 event.accept()
149 elif reply == 0: # close Console
149 elif reply == 0: # close Console
150 if not self._existing:
150 if not self._existing:
151 # Have kernel: don't quit, just close the window
151 # Have kernel: don't quit, just close the window
152 self._app.setQuitOnLastWindowClosed(False)
152 self._app.setQuitOnLastWindowClosed(False)
153 self.deleteLater()
153 self.deleteLater()
154 event.accept()
154 event.accept()
155 else:
155 else:
156 event.ignore()
156 event.ignore()
157 else:
157 else:
158 reply = QtGui.QMessageBox.question(self, title,
158 reply = QtGui.QMessageBox.question(self, title,
159 "Are you sure you want to close this Console?"+
159 "Are you sure you want to close this Console?"+
160 "\nThe Kernel and other Consoles will remain active.",
160 "\nThe Kernel and other Consoles will remain active.",
161 okay|cancel,
161 okay|cancel,
162 defaultButton=okay
162 defaultButton=okay
163 )
163 )
164 if reply == okay:
164 if reply == okay:
165 event.accept()
165 event.accept()
166 else:
166 else:
167 event.ignore()
167 event.ignore()
168 elif keepkernel: #close console but leave kernel running (no prompt)
168 elif keepkernel: #close console but leave kernel running (no prompt)
169 if kernel_manager and kernel_manager.channels_running:
169 if kernel_manager and kernel_manager.channels_running:
170 if not self._existing:
170 if not self._existing:
171 # I have the kernel: don't quit, just close the window
171 # I have the kernel: don't quit, just close the window
172 self._app.setQuitOnLastWindowClosed(False)
172 self._app.setQuitOnLastWindowClosed(False)
173 event.accept()
173 event.accept()
174 else: #close console and kernel (no prompt)
174 else: #close console and kernel (no prompt)
175 if kernel_manager and kernel_manager.channels_running:
175 if kernel_manager and kernel_manager.channels_running:
176 kernel_manager.shutdown_kernel()
176 kernel_manager.shutdown_kernel()
177 event.accept()
177 event.accept()
178
178
179 #-----------------------------------------------------------------------------
179 #-----------------------------------------------------------------------------
180 # Aliases and Flags
180 # Aliases and Flags
181 #-----------------------------------------------------------------------------
181 #-----------------------------------------------------------------------------
182
182
183 flags = dict(ipkernel_flags)
183 flags = dict(ipkernel_flags)
184 qt_flags = {
184 qt_flags = {
185 'existing' : ({'IPythonQtConsoleApp' : {'existing' : True}},
185 'existing' : ({'IPythonQtConsoleApp' : {'existing' : True}},
186 "Connect to an existing kernel."),
186 "Connect to an existing kernel."),
187 'pure' : ({'IPythonQtConsoleApp' : {'pure' : True}},
187 'pure' : ({'IPythonQtConsoleApp' : {'pure' : True}},
188 "Use a pure Python kernel instead of an IPython kernel."),
188 "Use a pure Python kernel instead of an IPython kernel."),
189 'plain' : ({'ConsoleWidget' : {'kind' : 'plain'}},
189 'plain' : ({'ConsoleWidget' : {'kind' : 'plain'}},
190 "Disable rich text support."),
190 "Disable rich text support."),
191 }
191 }
192 qt_flags.update(boolean_flag(
192 qt_flags.update(boolean_flag(
193 'gui-completion', 'ConsoleWidget.gui_completion',
193 'gui-completion', 'ConsoleWidget.gui_completion',
194 "use a GUI widget for tab completion",
194 "use a GUI widget for tab completion",
195 "use plaintext output for completion"
195 "use plaintext output for completion"
196 ))
196 ))
197 qt_flags.update(boolean_flag(
197 qt_flags.update(boolean_flag(
198 'confirm-exit', 'IPythonQtConsoleApp.confirm_exit',
198 'confirm-exit', 'IPythonQtConsoleApp.confirm_exit',
199 """Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
199 """Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
200 to force a direct exit without any confirmation.
200 to force a direct exit without any confirmation.
201 """,
201 """,
202 """Don't prompt the user when exiting. This will terminate the kernel
202 """Don't prompt the user when exiting. This will terminate the kernel
203 if it is owned by the frontend, and leave it alive if it is external.
203 if it is owned by the frontend, and leave it alive if it is external.
204 """
204 """
205 ))
205 ))
206 flags.update(qt_flags)
206 flags.update(qt_flags)
207 # the flags that are specific to the frontend
207 # the flags that are specific to the frontend
208 # these must be scrubbed before being passed to the kernel,
208 # these must be scrubbed before being passed to the kernel,
209 # or it will raise an error on unrecognized flags
209 # or it will raise an error on unrecognized flags
210 qt_flags = qt_flags.keys()
210 qt_flags = qt_flags.keys()
211
211
212 aliases = dict(ipkernel_aliases)
212 aliases = dict(ipkernel_aliases)
213
213
214 qt_aliases = dict(
214 qt_aliases = dict(
215 hb = 'IPythonQtConsoleApp.hb_port',
215 hb = 'IPythonQtConsoleApp.hb_port',
216 shell = 'IPythonQtConsoleApp.shell_port',
216 shell = 'IPythonQtConsoleApp.shell_port',
217 iopub = 'IPythonQtConsoleApp.iopub_port',
217 iopub = 'IPythonQtConsoleApp.iopub_port',
218 stdin = 'IPythonQtConsoleApp.stdin_port',
218 stdin = 'IPythonQtConsoleApp.stdin_port',
219 ip = 'IPythonQtConsoleApp.ip',
219 ip = 'IPythonQtConsoleApp.ip',
220
220
221 style = 'IPythonWidget.syntax_style',
221 style = 'IPythonWidget.syntax_style',
222 stylesheet = 'IPythonQtConsoleApp.stylesheet',
222 stylesheet = 'IPythonQtConsoleApp.stylesheet',
223 colors = 'ZMQInteractiveShell.colors',
223 colors = 'ZMQInteractiveShell.colors',
224
224
225 editor = 'IPythonWidget.editor',
225 editor = 'IPythonWidget.editor',
226 paging = 'ConsoleWidget.paging',
226 paging = 'ConsoleWidget.paging',
227 ssh = 'IPythonQtConsoleApp.sshserver',
227 ssh = 'IPythonQtConsoleApp.sshserver',
228 )
228 )
229 aliases.update(qt_aliases)
229 aliases.update(qt_aliases)
230 # also scrub aliases from the frontend
230 # also scrub aliases from the frontend
231 qt_flags.extend(qt_aliases.keys())
231 qt_flags.extend(qt_aliases.keys())
232
232
233
233
234 #-----------------------------------------------------------------------------
234 #-----------------------------------------------------------------------------
235 # IPythonQtConsole
235 # IPythonQtConsole
236 #-----------------------------------------------------------------------------
236 #-----------------------------------------------------------------------------
237
237
238
238
239 class IPythonQtConsoleApp(BaseIPythonApplication):
239 class IPythonQtConsoleApp(BaseIPythonApplication):
240 name = 'ipython-qtconsole'
240 name = 'ipython-qtconsole'
241 default_config_file_name='ipython_config.py'
241 default_config_file_name='ipython_config.py'
242
242
243 description = """
243 description = """
244 The IPython QtConsole.
244 The IPython QtConsole.
245
245
246 This launches a Console-style application using Qt. It is not a full
246 This launches a Console-style application using Qt. It is not a full
247 console, in that launched terminal subprocesses will not be able to accept
247 console, in that launched terminal subprocesses will not be able to accept
248 input.
248 input.
249
249
250 The QtConsole supports various extra features beyond the Terminal IPython
250 The QtConsole supports various extra features beyond the Terminal IPython
251 shell, such as inline plotting with matplotlib, via:
251 shell, such as inline plotting with matplotlib, via:
252
252
253 ipython qtconsole --pylab=inline
253 ipython qtconsole --pylab=inline
254
254
255 as well as saving your session as HTML, and printing the output.
255 as well as saving your session as HTML, and printing the output.
256
256
257 """
257 """
258 examples = _examples
258 examples = _examples
259
259
260 classes = [IPKernelApp, IPythonWidget, ZMQInteractiveShell, ProfileDir, Session]
260 classes = [IPKernelApp, IPythonWidget, ZMQInteractiveShell, ProfileDir, Session]
261 flags = Dict(flags)
261 flags = Dict(flags)
262 aliases = Dict(aliases)
262 aliases = Dict(aliases)
263
263
264 kernel_argv = List(Unicode)
264 kernel_argv = List(Unicode)
265
265
266 # create requested profiles by default, if they don't exist:
266 # create requested profiles by default, if they don't exist:
267 auto_create = CBool(True)
267 auto_create = CBool(True)
268 # connection info:
268 # connection info:
269 ip = Unicode(LOCALHOST, config=True,
269 ip = Unicode(LOCALHOST, config=True,
270 help="""Set the kernel\'s IP address [default localhost].
270 help="""Set the kernel\'s IP address [default localhost].
271 If the IP address is something other than localhost, then
271 If the IP address is something other than localhost, then
272 Consoles on other machines will be able to connect
272 Consoles on other machines will be able to connect
273 to the Kernel, so be careful!"""
273 to the Kernel, so be careful!"""
274 )
274 )
275
275
276 sshserver = Unicode('', config=True,
276 sshserver = Unicode('', config=True,
277 help="""The SSH server to use to connect to the kernel.""")
277 help="""The SSH server to use to connect to the kernel.""")
278 sshkey = Unicode('', config=True,
278 sshkey = Unicode('', config=True,
279 help="""Path to the ssh key to use for logging in to the ssh server.""")
279 help="""Path to the ssh key to use for logging in to the ssh server.""")
280
280
281 hb_port = Int(0, config=True,
281 hb_port = Int(0, config=True,
282 help="set the heartbeat port [default: random]")
282 help="set the heartbeat port [default: random]")
283 shell_port = Int(0, config=True,
283 shell_port = Int(0, config=True,
284 help="set the shell (XREP) port [default: random]")
284 help="set the shell (XREP) port [default: random]")
285 iopub_port = Int(0, config=True,
285 iopub_port = Int(0, config=True,
286 help="set the iopub (PUB) port [default: random]")
286 help="set the iopub (PUB) port [default: random]")
287 stdin_port = Int(0, config=True,
287 stdin_port = Int(0, config=True,
288 help="set the stdin (XREQ) port [default: random]")
288 help="set the stdin (XREQ) port [default: random]")
289
289
290 existing = CBool(False, config=True,
290 existing = CBool(False, config=True,
291 help="Whether to connect to an already running Kernel.")
291 help="Whether to connect to an already running Kernel.")
292
292
293 stylesheet = Unicode('', config=True,
293 stylesheet = Unicode('', config=True,
294 help="path to a custom CSS stylesheet")
294 help="path to a custom CSS stylesheet")
295
295
296 pure = CBool(False, config=True,
296 pure = CBool(False, config=True,
297 help="Use a pure Python kernel instead of an IPython kernel.")
297 help="Use a pure Python kernel instead of an IPython kernel.")
298 plain = CBool(False, config=True,
298 plain = CBool(False, config=True,
299 help="Use a plaintext widget instead of rich text (plain can't print/save).")
299 help="Use a plaintext widget instead of rich text (plain can't print/save).")
300
300
301 def _pure_changed(self, name, old, new):
301 def _pure_changed(self, name, old, new):
302 kind = 'plain' if self.plain else 'rich'
302 kind = 'plain' if self.plain else 'rich'
303 self.config.ConsoleWidget.kind = kind
303 self.config.ConsoleWidget.kind = kind
304 if self.pure:
304 if self.pure:
305 self.widget_factory = FrontendWidget
305 self.widget_factory = FrontendWidget
306 elif self.plain:
306 elif self.plain:
307 self.widget_factory = IPythonWidget
307 self.widget_factory = IPythonWidget
308 else:
308 else:
309 self.widget_factory = RichIPythonWidget
309 self.widget_factory = RichIPythonWidget
310
310
311 _plain_changed = _pure_changed
311 _plain_changed = _pure_changed
312
312
313 confirm_exit = CBool(True, config=True,
313 confirm_exit = CBool(True, config=True,
314 help="""
314 help="""
315 Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
315 Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
316 to force a direct exit without any confirmation.""",
316 to force a direct exit without any confirmation.""",
317 )
317 )
318
318
319 # the factory for creating a widget
319 # the factory for creating a widget
320 widget_factory = Any(RichIPythonWidget)
320 widget_factory = Any(RichIPythonWidget)
321
321
322 def parse_command_line(self, argv=None):
322 def parse_command_line(self, argv=None):
323 super(IPythonQtConsoleApp, self).parse_command_line(argv)
323 super(IPythonQtConsoleApp, self).parse_command_line(argv)
324 if argv is None:
324 if argv is None:
325 argv = sys.argv[1:]
325 argv = sys.argv[1:]
326
326
327 self.kernel_argv = list(argv) # copy
327 self.kernel_argv = list(argv) # copy
328 # kernel should inherit default config file from frontend
328 # kernel should inherit default config file from frontend
329 self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
329 self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
330 # scrub frontend-specific flags
330 # scrub frontend-specific flags
331 for a in argv:
331 for a in argv:
332
332
333 if a.startswith('-'):
333 if a.startswith('-'):
334 key = a.lstrip('-').split('=')[0]
334 key = a.lstrip('-').split('=')[0]
335 if key in qt_flags:
335 if key in qt_flags:
336 self.kernel_argv.remove(a)
336 self.kernel_argv.remove(a)
337
337
338 def init_ssh(self):
338 def init_ssh(self):
339 """set up ssh tunnels, if needed."""
339 """set up ssh tunnels, if needed."""
340 if not self.sshserver and not self.sshkey:
340 if not self.sshserver and not self.sshkey:
341 return
341 return
342
342
343 if self.sshkey and not self.sshserver:
343 if self.sshkey and not self.sshserver:
344 self.sshserver = self.ip
344 self.sshserver = self.ip
345 self.ip=LOCALHOST
345 self.ip=LOCALHOST
346
346
347 lports = select_random_ports(4)
347 lports = select_random_ports(4)
348 rports = self.shell_port, self.iopub_port, self.stdin_port, self.hb_port
348 rports = self.shell_port, self.iopub_port, self.stdin_port, self.hb_port
349 self.shell_port, self.iopub_port, self.stdin_port, self.hb_port = lports
349 self.shell_port, self.iopub_port, self.stdin_port, self.hb_port = lports
350
350
351 remote_ip = self.ip
351 remote_ip = self.ip
352 self.ip = LOCALHOST
352 self.ip = LOCALHOST
353 self.log.info("Forwarding connections to %s via %s"%(remote_ip, self.sshserver))
353 self.log.info("Forwarding connections to %s via %s"%(remote_ip, self.sshserver))
354
354
355 if tunnel.try_passwordless_ssh(self.sshserver, self.sshkey):
355 if tunnel.try_passwordless_ssh(self.sshserver, self.sshkey):
356 password=False
356 password=False
357 else:
357 else:
358 password = getpass("SSH Password for %s: "%self.sshserver)
358 password = getpass("SSH Password for %s: "%self.sshserver)
359
359
360 for lp,rp in zip(lports, rports):
360 for lp,rp in zip(lports, rports):
361 tunnel.ssh_tunnel(lp, rp, self.sshserver, remote_ip, self.sshkey, password)
361 tunnel.ssh_tunnel(lp, rp, self.sshserver, remote_ip, self.sshkey, password)
362
362
363 self.log.critical("To connect another client to this tunnel, use:")
364 self.log.critical(
365 "--existing --shell={0} --iopub={1} --stdin={2} --hb={3}".format(
366 self.shell_port, self.iopub_port, self.stdin_port,
367 self.hb_port))
368
363 def init_kernel_manager(self):
369 def init_kernel_manager(self):
364 # Don't let Qt or ZMQ swallow KeyboardInterupts.
370 # Don't let Qt or ZMQ swallow KeyboardInterupts.
365 signal.signal(signal.SIGINT, signal.SIG_DFL)
371 signal.signal(signal.SIGINT, signal.SIG_DFL)
366
372
367 # Create a KernelManager and start a kernel.
373 # Create a KernelManager and start a kernel.
368 self.kernel_manager = QtKernelManager(
374 self.kernel_manager = QtKernelManager(
369 shell_address=(self.ip, self.shell_port),
375 shell_address=(self.ip, self.shell_port),
370 sub_address=(self.ip, self.iopub_port),
376 sub_address=(self.ip, self.iopub_port),
371 stdin_address=(self.ip, self.stdin_port),
377 stdin_address=(self.ip, self.stdin_port),
372 hb_address=(self.ip, self.hb_port),
378 hb_address=(self.ip, self.hb_port),
373 config=self.config
379 config=self.config
374 )
380 )
375 # start the kernel
381 # start the kernel
376 if not self.existing:
382 if not self.existing:
377 kwargs = dict(ip=self.ip, ipython=not self.pure)
383 kwargs = dict(ip=self.ip, ipython=not self.pure)
378 kwargs['extra_arguments'] = self.kernel_argv
384 kwargs['extra_arguments'] = self.kernel_argv
379 self.kernel_manager.start_kernel(**kwargs)
385 self.kernel_manager.start_kernel(**kwargs)
380 self.kernel_manager.start_channels()
386 self.kernel_manager.start_channels()
381
387
382
388
383 def init_qt_elements(self):
389 def init_qt_elements(self):
384 # Create the widget.
390 # Create the widget.
385 self.app = QtGui.QApplication([])
391 self.app = QtGui.QApplication([])
386 local_kernel = (not self.existing) or self.ip in LOCAL_IPS
392 local_kernel = (not self.existing) or self.ip in LOCAL_IPS
387 self.widget = self.widget_factory(config=self.config,
393 self.widget = self.widget_factory(config=self.config,
388 local_kernel=local_kernel)
394 local_kernel=local_kernel)
389 self.widget.kernel_manager = self.kernel_manager
395 self.widget.kernel_manager = self.kernel_manager
390 self.window = MainWindow(self.app, self.widget, self.existing,
396 self.window = MainWindow(self.app, self.widget, self.existing,
391 may_close=local_kernel,
397 may_close=local_kernel,
392 confirm_exit=self.confirm_exit)
398 confirm_exit=self.confirm_exit)
393 self.window.setWindowTitle('Python' if self.pure else 'IPython')
399 self.window.setWindowTitle('Python' if self.pure else 'IPython')
394
400
395 def init_colors(self):
401 def init_colors(self):
396 """Configure the coloring of the widget"""
402 """Configure the coloring of the widget"""
397 # Note: This will be dramatically simplified when colors
403 # Note: This will be dramatically simplified when colors
398 # are removed from the backend.
404 # are removed from the backend.
399
405
400 if self.pure:
406 if self.pure:
401 # only IPythonWidget supports styling
407 # only IPythonWidget supports styling
402 return
408 return
403
409
404 # parse the colors arg down to current known labels
410 # parse the colors arg down to current known labels
405 try:
411 try:
406 colors = self.config.ZMQInteractiveShell.colors
412 colors = self.config.ZMQInteractiveShell.colors
407 except AttributeError:
413 except AttributeError:
408 colors = None
414 colors = None
409 try:
415 try:
410 style = self.config.IPythonWidget.colors
416 style = self.config.IPythonWidget.colors
411 except AttributeError:
417 except AttributeError:
412 style = None
418 style = None
413
419
414 # find the value for colors:
420 # find the value for colors:
415 if colors:
421 if colors:
416 colors=colors.lower()
422 colors=colors.lower()
417 if colors in ('lightbg', 'light'):
423 if colors in ('lightbg', 'light'):
418 colors='lightbg'
424 colors='lightbg'
419 elif colors in ('dark', 'linux'):
425 elif colors in ('dark', 'linux'):
420 colors='linux'
426 colors='linux'
421 else:
427 else:
422 colors='nocolor'
428 colors='nocolor'
423 elif style:
429 elif style:
424 if style=='bw':
430 if style=='bw':
425 colors='nocolor'
431 colors='nocolor'
426 elif styles.dark_style(style):
432 elif styles.dark_style(style):
427 colors='linux'
433 colors='linux'
428 else:
434 else:
429 colors='lightbg'
435 colors='lightbg'
430 else:
436 else:
431 colors=None
437 colors=None
432
438
433 # Configure the style.
439 # Configure the style.
434 widget = self.widget
440 widget = self.widget
435 if style:
441 if style:
436 widget.style_sheet = styles.sheet_from_template(style, colors)
442 widget.style_sheet = styles.sheet_from_template(style, colors)
437 widget.syntax_style = style
443 widget.syntax_style = style
438 widget._syntax_style_changed()
444 widget._syntax_style_changed()
439 widget._style_sheet_changed()
445 widget._style_sheet_changed()
440 elif colors:
446 elif colors:
441 # use a default style
447 # use a default style
442 widget.set_default_style(colors=colors)
448 widget.set_default_style(colors=colors)
443 else:
449 else:
444 # this is redundant for now, but allows the widget's
450 # this is redundant for now, but allows the widget's
445 # defaults to change
451 # defaults to change
446 widget.set_default_style()
452 widget.set_default_style()
447
453
448 if self.stylesheet:
454 if self.stylesheet:
449 # we got an expicit stylesheet
455 # we got an expicit stylesheet
450 if os.path.isfile(self.stylesheet):
456 if os.path.isfile(self.stylesheet):
451 with open(self.stylesheet) as f:
457 with open(self.stylesheet) as f:
452 sheet = f.read()
458 sheet = f.read()
453 widget.style_sheet = sheet
459 widget.style_sheet = sheet
454 widget._style_sheet_changed()
460 widget._style_sheet_changed()
455 else:
461 else:
456 raise IOError("Stylesheet %r not found."%self.stylesheet)
462 raise IOError("Stylesheet %r not found."%self.stylesheet)
457
463
458 def initialize(self, argv=None):
464 def initialize(self, argv=None):
459 super(IPythonQtConsoleApp, self).initialize(argv)
465 super(IPythonQtConsoleApp, self).initialize(argv)
460 self.init_ssh()
466 self.init_ssh()
461 self.init_kernel_manager()
467 self.init_kernel_manager()
462 self.init_qt_elements()
468 self.init_qt_elements()
463 self.init_colors()
469 self.init_colors()
464 self.init_window_shortcut()
470 self.init_window_shortcut()
465
471
466 def init_window_shortcut(self):
472 def init_window_shortcut(self):
467 fullScreenAction = QtGui.QAction('Toggle Full Screen', self.window)
473 fullScreenAction = QtGui.QAction('Toggle Full Screen', self.window)
468 fullScreenAction.setShortcut('Ctrl+Meta+Space')
474 fullScreenAction.setShortcut('Ctrl+Meta+Space')
469 fullScreenAction.triggered.connect(self.toggleFullScreen)
475 fullScreenAction.triggered.connect(self.toggleFullScreen)
470 self.window.addAction(fullScreenAction)
476 self.window.addAction(fullScreenAction)
471
477
472 def toggleFullScreen(self):
478 def toggleFullScreen(self):
473 if not self.window.isFullScreen():
479 if not self.window.isFullScreen():
474 self.window.showFullScreen()
480 self.window.showFullScreen()
475 else:
481 else:
476 self.window.showNormal()
482 self.window.showNormal()
477
483
478 def start(self):
484 def start(self):
479
485
480 # draw the window
486 # draw the window
481 self.window.show()
487 self.window.show()
482
488
483 # Start the application main loop.
489 # Start the application main loop.
484 self.app.exec_()
490 self.app.exec_()
485
491
486 #-----------------------------------------------------------------------------
492 #-----------------------------------------------------------------------------
487 # Main entry point
493 # Main entry point
488 #-----------------------------------------------------------------------------
494 #-----------------------------------------------------------------------------
489
495
490 def main():
496 def main():
491 app = IPythonQtConsoleApp()
497 app = IPythonQtConsoleApp()
492 app.initialize()
498 app.initialize()
493 app.start()
499 app.start()
494
500
495
501
496 if __name__ == '__main__':
502 if __name__ == '__main__':
497 main()
503 main()
General Comments 0
You need to be logged in to leave comments. Login now